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