1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::convert::TryInto;
6 use std::fs::File;
7 use std::fs::OpenOptions;
8 use std::mem::size_of;
9 use std::num::Wrapping;
10 use std::os::unix::fs::OpenOptionsExt;
11 use std::path::Path;
12 use std::str;
13
14 use anyhow::Context;
15 use argh::FromArgs;
16 use base::AsRawDescriptor;
17 use base::Event;
18 use base::RawDescriptor;
19 use base::SafeDescriptor;
20 use cros_async::Executor;
21 use data_model::Le64;
22 use vhost::Vhost;
23 use vhost::Vsock;
24 use vm_memory::GuestMemory;
25 use vmm_vhost::connection::Connection;
26 use vmm_vhost::message::BackendReq;
27 use vmm_vhost::message::VhostSharedMemoryRegion;
28 use vmm_vhost::message::VhostUserConfigFlags;
29 use vmm_vhost::message::VhostUserInflight;
30 use vmm_vhost::message::VhostUserMemoryRegion;
31 use vmm_vhost::message::VhostUserMigrationPhase;
32 use vmm_vhost::message::VhostUserProtocolFeatures;
33 use vmm_vhost::message::VhostUserSingleMemoryRegion;
34 use vmm_vhost::message::VhostUserTransferDirection;
35 use vmm_vhost::message::VhostUserVringAddrFlags;
36 use vmm_vhost::message::VhostUserVringState;
37 use vmm_vhost::Error;
38 use vmm_vhost::Result;
39 use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
40 use zerocopy::AsBytes;
41
42 use super::BackendConnection;
43 use crate::virtio::device_constants::vsock::NUM_QUEUES;
44 use crate::virtio::vhost::user::device::handler::vmm_va_to_gpa;
45 use crate::virtio::vhost::user::device::handler::MappingInfo;
46 use crate::virtio::vhost::user::device::handler::VhostUserRegularOps;
47 use crate::virtio::vhost::user::VhostUserDeviceBuilder;
48 use crate::virtio::Queue;
49 use crate::virtio::QueueConfig;
50
51 const EVENT_QUEUE: usize = NUM_QUEUES - 1;
52
53 struct VsockBackend {
54 queues: [QueueConfig; NUM_QUEUES],
55 vmm_maps: Option<Vec<MappingInfo>>,
56 mem: Option<GuestMemory>,
57
58 handle: Vsock,
59 cid: u64,
60 protocol_features: VhostUserProtocolFeatures,
61 }
62
63 /// A vhost-vsock device which handle is already opened. This allows the parent process to open the
64 /// vhost-vsock device, create this structure, and pass it to the child process so it doesn't need
65 /// the rights to open the vhost-vsock device itself.
66 pub struct VhostUserVsockDevice {
67 cid: u64,
68 handle: Vsock,
69 }
70
71 impl VhostUserVsockDevice {
new<P: AsRef<Path>>(cid: u64, vhost_device: P) -> anyhow::Result<Self>72 pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: P) -> anyhow::Result<Self> {
73 let handle = Vsock::new(
74 OpenOptions::new()
75 .read(true)
76 .write(true)
77 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
78 .open(vhost_device.as_ref())
79 .context(format!(
80 "failed to open vhost-vsock device {}",
81 vhost_device.as_ref().display()
82 ))?,
83 );
84
85 Ok(Self { cid, handle })
86 }
87 }
88
89 impl AsRawDescriptor for VhostUserVsockDevice {
as_raw_descriptor(&self) -> base::RawDescriptor90 fn as_raw_descriptor(&self) -> base::RawDescriptor {
91 self.handle.as_raw_descriptor()
92 }
93 }
94
95 impl VhostUserDeviceBuilder for VhostUserVsockDevice {
build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>>96 fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {
97 let backend = VsockBackend {
98 queues: [
99 QueueConfig::new(Queue::MAX_SIZE, 0),
100 QueueConfig::new(Queue::MAX_SIZE, 0),
101 QueueConfig::new(Queue::MAX_SIZE, 0),
102 ],
103 vmm_maps: None,
104 mem: None,
105 handle: self.handle,
106 cid: self.cid,
107 protocol_features: VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIG,
108 };
109
110 Ok(Box::new(backend))
111 }
112 }
113
convert_vhost_error(err: vhost::Error) -> Error114 fn convert_vhost_error(err: vhost::Error) -> Error {
115 use vhost::Error::*;
116 match err {
117 IoctlError(e) => Error::ReqHandlerError(e),
118 _ => Error::BackendInternalError,
119 }
120 }
121
122 impl vmm_vhost::Backend for VsockBackend {
set_owner(&mut self) -> Result<()>123 fn set_owner(&mut self) -> Result<()> {
124 self.handle.set_owner().map_err(convert_vhost_error)
125 }
126
reset_owner(&mut self) -> Result<()>127 fn reset_owner(&mut self) -> Result<()> {
128 self.handle.reset_owner().map_err(convert_vhost_error)
129 }
130
get_features(&mut self) -> Result<u64>131 fn get_features(&mut self) -> Result<u64> {
132 // Add the vhost-user features that we support.
133 let features = self.handle.get_features().map_err(convert_vhost_error)?
134 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
135 Ok(features)
136 }
137
set_features(&mut self, features: u64) -> Result<()>138 fn set_features(&mut self, features: u64) -> Result<()> {
139 // Unset the vhost-user feature flags as they are not supported by the underlying vhost
140 // device.
141 let features = features & !(1 << VHOST_USER_F_PROTOCOL_FEATURES);
142 self.handle
143 .set_features(features)
144 .map_err(convert_vhost_error)
145 }
146
get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures>147 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
148 Ok(self.protocol_features)
149 }
150
set_protocol_features(&mut self, features: u64) -> Result<()>151 fn set_protocol_features(&mut self, features: u64) -> Result<()> {
152 let unrequested_features = features & !self.protocol_features.bits();
153 if unrequested_features != 0 {
154 Err(Error::InvalidParam)
155 } else {
156 Ok(())
157 }
158 }
159
set_mem_table( &mut self, contexts: &[VhostUserMemoryRegion], files: Vec<File>, ) -> Result<()>160 fn set_mem_table(
161 &mut self,
162 contexts: &[VhostUserMemoryRegion],
163 files: Vec<File>,
164 ) -> Result<()> {
165 let (guest_mem, vmm_maps) = VhostUserRegularOps::set_mem_table(contexts, files)?;
166
167 self.handle
168 .set_mem_table(&guest_mem)
169 .map_err(convert_vhost_error)?;
170
171 self.mem = Some(guest_mem);
172 self.vmm_maps = Some(vmm_maps);
173
174 Ok(())
175 }
176
get_queue_num(&mut self) -> Result<u64>177 fn get_queue_num(&mut self) -> Result<u64> {
178 Ok(NUM_QUEUES as u64)
179 }
180
set_vring_num(&mut self, index: u32, num: u32) -> Result<()>181 fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
182 if index >= NUM_QUEUES as u32 || num == 0 || num > Queue::MAX_SIZE.into() {
183 return Err(Error::InvalidParam);
184 }
185
186 // We checked these values already.
187 let index = index as usize;
188 let num = num as u16;
189 self.queues[index].set_size(num);
190
191 // The last vq is an event-only vq that is not handled by the kernel.
192 if index == EVENT_QUEUE {
193 return Ok(());
194 }
195
196 self.handle
197 .set_vring_num(index, num)
198 .map_err(convert_vhost_error)
199 }
200
set_vring_addr( &mut self, index: u32, flags: VhostUserVringAddrFlags, descriptor: u64, used: u64, available: u64, log: u64, ) -> Result<()>201 fn set_vring_addr(
202 &mut self,
203 index: u32,
204 flags: VhostUserVringAddrFlags,
205 descriptor: u64,
206 used: u64,
207 available: u64,
208 log: u64,
209 ) -> Result<()> {
210 if index >= NUM_QUEUES as u32 {
211 return Err(Error::InvalidParam);
212 }
213
214 let index = index as usize;
215
216 let mem = self.mem.as_ref().ok_or(Error::InvalidParam)?;
217 let maps = self.vmm_maps.as_ref().ok_or(Error::InvalidParam)?;
218
219 let queue = &mut self.queues[index];
220 queue.set_desc_table(vmm_va_to_gpa(maps, descriptor)?);
221 queue.set_avail_ring(vmm_va_to_gpa(maps, available)?);
222 queue.set_used_ring(vmm_va_to_gpa(maps, used)?);
223 let log_addr = if flags.contains(VhostUserVringAddrFlags::VHOST_VRING_F_LOG) {
224 vmm_va_to_gpa(maps, log).map(Some)?
225 } else {
226 None
227 };
228
229 if index == EVENT_QUEUE {
230 return Ok(());
231 }
232
233 self.handle
234 .set_vring_addr(
235 mem,
236 queue.max_size(),
237 queue.size(),
238 index,
239 flags.bits(),
240 queue.desc_table(),
241 queue.used_ring(),
242 queue.avail_ring(),
243 log_addr,
244 )
245 .map_err(convert_vhost_error)
246 }
247
set_vring_base(&mut self, index: u32, base: u32) -> Result<()>248 fn set_vring_base(&mut self, index: u32, base: u32) -> Result<()> {
249 if index >= NUM_QUEUES as u32 || base >= Queue::MAX_SIZE.into() {
250 return Err(Error::InvalidParam);
251 }
252
253 let index = index as usize;
254 let base = base as u16;
255
256 let queue = &mut self.queues[index];
257 queue.set_next_avail(Wrapping(base));
258 queue.set_next_used(Wrapping(base));
259
260 if index == EVENT_QUEUE {
261 return Ok(());
262 }
263
264 self.handle
265 .set_vring_base(index, base)
266 .map_err(convert_vhost_error)
267 }
268
get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState>269 fn get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState> {
270 if index >= NUM_QUEUES as u32 {
271 return Err(Error::InvalidParam);
272 }
273
274 let index = index as usize;
275 let next_avail = if index == EVENT_QUEUE {
276 self.queues[index].next_avail().0
277 } else {
278 self.handle
279 .get_vring_base(index)
280 .map_err(convert_vhost_error)?
281 };
282
283 Ok(VhostUserVringState::new(index as u32, next_avail.into()))
284 }
285
set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()>286 fn set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()> {
287 if index >= NUM_QUEUES as u8 {
288 return Err(Error::InvalidParam);
289 }
290
291 let event = VhostUserRegularOps::set_vring_kick(index, fd)?;
292 let index = usize::from(index);
293 if index != EVENT_QUEUE {
294 self.handle
295 .set_vring_kick(index, &event)
296 .map_err(convert_vhost_error)?;
297 }
298
299 Ok(())
300 }
301
set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()>302 fn set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()> {
303 if index >= NUM_QUEUES as u8 {
304 return Err(Error::InvalidParam);
305 }
306
307 let doorbell = VhostUserRegularOps::set_vring_call(
308 index,
309 fd,
310 Box::new(|| {
311 // `doorbell.signal_config_changed()` is never called, so this shouldn't be
312 // reachable.
313 unreachable!()
314 }),
315 )?;
316 let index = usize::from(index);
317 let event = doorbell.get_interrupt_evt();
318 if index != EVENT_QUEUE {
319 self.handle
320 .set_vring_call(index, event)
321 .map_err(convert_vhost_error)?;
322 }
323
324 Ok(())
325 }
326
set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()>327 fn set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()> {
328 if index >= NUM_QUEUES as u8 {
329 return Err(Error::InvalidParam);
330 }
331
332 let index = usize::from(index);
333 let file = fd.ok_or(Error::InvalidParam)?;
334
335 let event = Event::from(SafeDescriptor::from(file));
336
337 if index == EVENT_QUEUE {
338 return Ok(());
339 }
340
341 self.handle
342 .set_vring_err(index, &event)
343 .map_err(convert_vhost_error)
344 }
345
set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()>346 fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
347 if index >= NUM_QUEUES as u32 {
348 return Err(Error::InvalidParam);
349 }
350
351 self.queues[index as usize].set_ready(enable);
352
353 if index == (EVENT_QUEUE) as u32 {
354 return Ok(());
355 }
356
357 if self.queues[..EVENT_QUEUE].iter().all(|q| q.ready()) {
358 // All queues are ready. Start the device.
359 self.handle.set_cid(self.cid).map_err(convert_vhost_error)?;
360 self.handle.start().map_err(convert_vhost_error)
361 } else if !enable {
362 // If we just disabled a vring then stop the device.
363 self.handle.stop().map_err(convert_vhost_error)
364 } else {
365 Ok(())
366 }
367 }
368
get_config( &mut self, offset: u32, size: u32, _flags: VhostUserConfigFlags, ) -> Result<Vec<u8>>369 fn get_config(
370 &mut self,
371 offset: u32,
372 size: u32,
373 _flags: VhostUserConfigFlags,
374 ) -> Result<Vec<u8>> {
375 let start: usize = offset.try_into().map_err(|_| Error::InvalidParam)?;
376 let end: usize = offset
377 .checked_add(size)
378 .and_then(|e| e.try_into().ok())
379 .ok_or(Error::InvalidParam)?;
380
381 if start >= size_of::<Le64>() || end > size_of::<Le64>() {
382 return Err(Error::InvalidParam);
383 }
384
385 Ok(Le64::from(self.cid).as_bytes()[start..end].to_vec())
386 }
387
set_config( &mut self, _offset: u32, _buf: &[u8], _flags: VhostUserConfigFlags, ) -> Result<()>388 fn set_config(
389 &mut self,
390 _offset: u32,
391 _buf: &[u8],
392 _flags: VhostUserConfigFlags,
393 ) -> Result<()> {
394 Err(Error::InvalidOperation)
395 }
396
set_backend_req_fd(&mut self, _vu_req: Connection<BackendReq>)397 fn set_backend_req_fd(&mut self, _vu_req: Connection<BackendReq>) {
398 // We didn't set VhostUserProtocolFeatures::BACKEND_REQ
399 unreachable!("unexpected set_backend_req_fd");
400 }
401
get_inflight_fd( &mut self, _inflight: &VhostUserInflight, ) -> Result<(VhostUserInflight, File)>402 fn get_inflight_fd(
403 &mut self,
404 _inflight: &VhostUserInflight,
405 ) -> Result<(VhostUserInflight, File)> {
406 Err(Error::InvalidOperation)
407 }
408
set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()>409 fn set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()> {
410 Err(Error::InvalidOperation)
411 }
412
get_max_mem_slots(&mut self) -> Result<u64>413 fn get_max_mem_slots(&mut self) -> Result<u64> {
414 Err(Error::InvalidOperation)
415 }
416
add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()>417 fn add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()> {
418 Err(Error::InvalidOperation)
419 }
420
remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()>421 fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
422 Err(Error::InvalidOperation)
423 }
424
set_device_state_fd( &mut self, _transfer_direction: VhostUserTransferDirection, _migration_phase: VhostUserMigrationPhase, _fd: File, ) -> Result<Option<File>>425 fn set_device_state_fd(
426 &mut self,
427 _transfer_direction: VhostUserTransferDirection,
428 _migration_phase: VhostUserMigrationPhase,
429 _fd: File,
430 ) -> Result<Option<File>> {
431 Err(Error::InvalidOperation)
432 }
433
check_device_state(&mut self) -> Result<()>434 fn check_device_state(&mut self) -> Result<()> {
435 Err(Error::InvalidOperation)
436 }
437
get_shared_memory_regions(&mut self) -> Result<Vec<VhostSharedMemoryRegion>>438 fn get_shared_memory_regions(&mut self) -> Result<Vec<VhostSharedMemoryRegion>> {
439 Ok(vec![])
440 }
441 }
442
443 #[derive(FromArgs)]
444 #[argh(subcommand, name = "vsock")]
445 /// Vsock device
446 pub struct Options {
447 #[argh(option, arg_name = "PATH", hidden_help)]
448 /// deprecated - please use --socket-path instead
449 socket: Option<String>,
450 #[argh(option, arg_name = "PATH")]
451 /// path to the vhost-user socket to bind to.
452 /// If this flag is set, --fd cannot be specified.
453 socket_path: Option<String>,
454 #[argh(option, arg_name = "FD")]
455 /// file descriptor of a connected vhost-user socket.
456 /// If this flag is set, --socket-path cannot be specified.
457 fd: Option<RawDescriptor>,
458
459 #[argh(option, arg_name = "INT")]
460 /// the vsock context id for this device
461 cid: u64,
462 #[argh(
463 option,
464 default = "String::from(\"/dev/vhost-vsock\")",
465 arg_name = "PATH"
466 )]
467 /// path to the vhost-vsock control socket
468 vhost_socket: String,
469 }
470
471 /// Returns an error if the given `args` is invalid or the device fails to run.
run_vsock_device(opts: Options) -> anyhow::Result<()>472 pub fn run_vsock_device(opts: Options) -> anyhow::Result<()> {
473 let ex = Executor::new().context("failed to create executor")?;
474
475 let conn =
476 BackendConnection::from_opts(opts.socket.as_deref(), opts.socket_path.as_deref(), opts.fd)?;
477
478 let vsock_device = Box::new(VhostUserVsockDevice::new(opts.cid, opts.vhost_socket)?);
479
480 conn.run_device(ex, vsock_device)
481 }
482