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