xref: /aosp_15_r20/external/crosvm/devices/src/virtio/vhost/user/device/vsock.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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