1 // Copyright 2018 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::collections::BTreeMap; 6 use std::sync::Arc; 7 8 #[cfg(target_arch = "x86_64")] 9 use acpi_tables::sdt::SDT; 10 use anyhow::anyhow; 11 use anyhow::Result; 12 use base::Event; 13 use base::Protection; 14 use base::RawDescriptor; 15 use hypervisor::MemCacheType; 16 use sync::Mutex; 17 use vm_control::VmMemorySource; 18 use vm_memory::GuestAddress; 19 use vm_memory::GuestMemory; 20 21 use super::*; 22 use crate::pci::MsixConfig; 23 use crate::pci::MsixStatus; 24 use crate::pci::PciAddress; 25 use crate::pci::PciBarConfiguration; 26 use crate::pci::PciBarIndex; 27 use crate::pci::PciCapability; 28 use crate::virtio::queue::QueueConfig; 29 30 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 31 pub enum VirtioTransportType { 32 Pci, 33 Mmio, 34 } 35 36 #[derive(Clone)] 37 pub struct SharedMemoryRegion { 38 /// The id of the shared memory region. A device may have multiple regions, but each 39 /// must have a unique id. The meaning of a particular region is device-specific. 40 pub id: u8, 41 pub length: u64, 42 } 43 44 /// Trait for mapping memory into the device's shared memory region. 45 pub trait SharedMemoryMapper: Send { 46 /// Maps the given |source| into the shared memory region at |offset|. add_mapping( &mut self, source: VmMemorySource, offset: u64, prot: Protection, cache: MemCacheType, ) -> Result<()>47 fn add_mapping( 48 &mut self, 49 source: VmMemorySource, 50 offset: u64, 51 prot: Protection, 52 cache: MemCacheType, 53 ) -> Result<()>; 54 55 /// Removes the mapping beginning at |offset|. remove_mapping(&mut self, offset: u64) -> Result<()>56 fn remove_mapping(&mut self, offset: u64) -> Result<()>; 57 as_raw_descriptor(&self) -> Option<RawDescriptor>58 fn as_raw_descriptor(&self) -> Option<RawDescriptor> { 59 None 60 } 61 } 62 63 /// Trait for virtio devices to be driven by a virtio transport. 64 /// 65 /// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the 66 /// device. Once the guest driver has configured the device, `VirtioDevice::activate` will be called 67 /// and all the events, memory, and queues for device operation will be moved into the device. 68 /// Optionally, a virtio device can implement device reset in which it returns said resources and 69 /// resets its internal. 70 pub trait VirtioDevice: Send { 71 /// Returns a label suitable for debug output. debug_label(&self) -> String72 fn debug_label(&self) -> String { 73 format!("virtio-{}", self.device_type()) 74 } 75 76 /// A vector of device-specific file descriptors that must be kept open 77 /// after jailing. Must be called before the process is jailed. keep_rds(&self) -> Vec<RawDescriptor>78 fn keep_rds(&self) -> Vec<RawDescriptor>; 79 80 /// The virtio device type. device_type(&self) -> DeviceType81 fn device_type(&self) -> DeviceType; 82 83 /// The maximum size of each queue that this device supports. queue_max_sizes(&self) -> &[u16]84 fn queue_max_sizes(&self) -> &[u16]; 85 86 /// The number of interrupts used by this device. num_interrupts(&self) -> usize87 fn num_interrupts(&self) -> usize { 88 self.queue_max_sizes().len() 89 } 90 91 /// The set of feature bits that this device supports in addition to the base features. features(&self) -> u6492 fn features(&self) -> u64 { 93 0 94 } 95 96 /// Acknowledges that this set of features should be enabled. ack_features(&mut self, value: u64)97 fn ack_features(&mut self, value: u64) { 98 let _ = value; 99 } 100 101 /// Reads this device configuration space at `offset`. read_config(&self, offset: u64, data: &mut [u8])102 fn read_config(&self, offset: u64, data: &mut [u8]) { 103 let _ = offset; 104 let _ = data; 105 } 106 107 /// Writes to this device configuration space at `offset`. write_config(&mut self, offset: u64, data: &[u8])108 fn write_config(&mut self, offset: u64, data: &[u8]) { 109 let _ = offset; 110 let _ = data; 111 } 112 113 /// Activates this device for real usage. activate( &mut self, mem: GuestMemory, interrupt: Interrupt, queues: BTreeMap<usize, Queue>, ) -> Result<()>114 fn activate( 115 &mut self, 116 mem: GuestMemory, 117 interrupt: Interrupt, 118 queues: BTreeMap<usize, Queue>, 119 ) -> Result<()>; 120 121 /// Optionally deactivates this device. If the reset method is 122 /// not able to reset the virtio device, or the virtio device model doesn't 123 /// implement the reset method, an `Err` value is returned to indicate 124 /// the reset is not successful. Otherwise `Ok(())` should be returned. reset(&mut self) -> Result<()>125 fn reset(&mut self) -> Result<()> { 126 Err(anyhow!("reset not implemented for {}", self.debug_label())) 127 } 128 129 /// Returns any additional BAR configuration required by the device. get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration>130 fn get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration> { 131 Vec::new() 132 } 133 134 /// Returns any additional capabiltiies required by the device. get_device_caps(&self) -> Vec<Box<dyn PciCapability>>135 fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> { 136 Vec::new() 137 } 138 139 /// Invoked when the device is sandboxed. on_device_sandboxed(&mut self)140 fn on_device_sandboxed(&mut self) {} 141 control_notify(&self, _behavior: MsixStatus)142 fn control_notify(&self, _behavior: MsixStatus) {} 143 144 #[cfg(target_arch = "x86_64")] generate_acpi( &mut self, _pci_address: &Option<PciAddress>, sdts: Vec<SDT>, ) -> Option<Vec<SDT>>145 fn generate_acpi( 146 &mut self, 147 _pci_address: &Option<PciAddress>, 148 sdts: Vec<SDT>, 149 ) -> Option<Vec<SDT>> { 150 Some(sdts) 151 } 152 153 /// Reads from a BAR region mapped in to the device. 154 /// * `addr` - The guest address inside the BAR. 155 /// * `data` - Filled with the data from `addr`. read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8])156 fn read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8]) {} 157 158 /// Writes to a BAR region mapped in to the device. 159 /// * `addr` - The guest address inside the BAR. 160 /// * `data` - The data to write. write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8])161 fn write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8]) {} 162 163 /// Returns the PCI address where the device will be allocated. 164 /// Returns `None` if any address is good for the device. pci_address(&self) -> Option<PciAddress>165 fn pci_address(&self) -> Option<PciAddress> { 166 None 167 } 168 169 /// Returns the Virtio transport type: PCI (default for crosvm) or MMIO. transport_type(&self) -> VirtioTransportType170 fn transport_type(&self) -> VirtioTransportType { 171 VirtioTransportType::Pci 172 } 173 174 /// Returns the device's shared memory region if present. get_shared_memory_region(&self) -> Option<SharedMemoryRegion>175 fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> { 176 None 177 } 178 179 /// If true, VFIO passthrough devices can access descriptors mapped into 180 /// this region by mapping the corresponding addresses from this device's 181 /// PCI bar into their IO address space with virtio-iommu. 182 /// 183 /// NOTE: Not all vm_control::VmMemorySource types are supported. 184 /// NOTE: Not yet compatible with PrepareSharedMemoryRegion (aka fixed mapping). expose_shmem_descriptors_with_viommu(&self) -> bool185 fn expose_shmem_descriptors_with_viommu(&self) -> bool { 186 false 187 } 188 189 /// Provides the trait object used to map files into the device's shared 190 /// memory region. 191 /// 192 /// If `get_shared_memory_region` returns `Some`, then this will be called 193 /// before `activate`. set_shared_memory_mapper(&mut self, _mapper: Box<dyn SharedMemoryMapper>)194 fn set_shared_memory_mapper(&mut self, _mapper: Box<dyn SharedMemoryMapper>) {} 195 196 /// Provides the base address of the shared memory region, if one is present. Will 197 /// be called before `activate`. 198 /// 199 /// NOTE: Mappings in shared memory regions should be accessed via offset, rather 200 /// than via raw guest physical address. This function is only provided so 201 /// devices can remain backwards compatible with older drivers. set_shared_memory_region_base(&mut self, _addr: GuestAddress)202 fn set_shared_memory_region_base(&mut self, _addr: GuestAddress) {} 203 204 /// Pause all processing. 205 /// 206 /// Gives up the queues so that a higher layer can potentially snapshot them. The 207 /// implementations should also drop the `Interrupt` and queues `Event`s that were given along 208 /// with the queues originally. 209 /// 210 /// Unlike `Suspendable::sleep`, this is not idempotent. Attempting to sleep while already 211 /// asleep is an error. virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>>212 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> { 213 anyhow::bail!("virtio_sleep not implemented for {}", self.debug_label()); 214 } 215 216 /// Resume all processing. 217 /// 218 /// If the device's queues are active, then the queues and associated data will is included. 219 /// 220 /// Unlike `Suspendable::wake`, this is not idempotent. Attempting to wake while already awake 221 /// is an error. virtio_wake( &mut self, _queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()>222 fn virtio_wake( 223 &mut self, 224 _queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, 225 ) -> anyhow::Result<()> { 226 anyhow::bail!("virtio_wake not implemented for {}", self.debug_label()); 227 } 228 229 /// Snapshot current state. Device must be asleep. virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value>230 fn virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value> { 231 anyhow::bail!("virtio_snapshot not implemented for {}", self.debug_label()); 232 } 233 234 /// Restore device state from a snapshot. 235 /// TODO(b/280607404): Vhost user will need fds passed to the device process. virtio_restore(&mut self, _data: serde_json::Value) -> anyhow::Result<()>236 fn virtio_restore(&mut self, _data: serde_json::Value) -> anyhow::Result<()> { 237 anyhow::bail!("virtio_restore not implemented for {}", self.debug_label()); 238 } 239 240 /// Returns true if the device uses the vhost user protocol. is_vhost_user(&self) -> bool241 fn is_vhost_user(&self) -> bool { 242 false 243 } 244 245 /// Vhost user device specific restore to be called instead of `virtio_restore`. This will 246 /// rewire irqfds, queue_evts, start up the worker if needed, and send a RESTORE request to 247 /// the device process. vhost_user_restore( &mut self, _data: serde_json::Value, _queue_configs: &[QueueConfig], _queue_evts: Option<Vec<Event>>, _interrupt: Option<Interrupt>, _mem: GuestMemory, _msix_config: &Arc<Mutex<MsixConfig>>, _device_activated: bool, ) -> anyhow::Result<()>248 fn vhost_user_restore( 249 &mut self, 250 _data: serde_json::Value, 251 _queue_configs: &[QueueConfig], 252 _queue_evts: Option<Vec<Event>>, 253 _interrupt: Option<Interrupt>, 254 _mem: GuestMemory, 255 _msix_config: &Arc<Mutex<MsixConfig>>, 256 _device_activated: bool, 257 ) -> anyhow::Result<()> { 258 anyhow::bail!( 259 "vhost_user_restore not implemented for {}", 260 self.debug_label() 261 ); 262 } 263 264 // Returns a tuple consisting of the non-arch specific part of the OpenFirmware path, 265 // represented as bytes, and the boot index of a device. The non-arch specific part of path for 266 // a virtio-blk device, for example, would consist of everything after the first '/' below: 267 // pci@i0cf8/scsi@6[,3]/disk@0,0 268 // ^ ^ ^ ^ ^ 269 // | | | fixed 270 // | | (PCI function related to disk (optional)) 271 // (x86 specf (PCI slot holding disk) 272 // root at sys 273 // bus port) bootorder_fw_cfg(&self, _pci_address: u8) -> Option<(Vec<u8>, usize)>274 fn bootorder_fw_cfg(&self, _pci_address: u8) -> Option<(Vec<u8>, usize)> { 275 None 276 } 277 } 278 279 // General tests that should pass on all suspendables. 280 // Do implement device-specific tests to validate the functionality of the device. 281 // Those tests are not a replacement for regular tests. Only an extension specific to the trait's 282 // basic functionality. 283 /// `name` is the name of the test grouping. Can be anything unique within the same crate. 284 /// `dev` is a block that returns a created virtio device. 285 /// ``num_queues` is the number of queues to be created. 286 /// `modfun` is the function name of the function that would modify the device. The function call 287 /// should modify the device so that a snapshot taken after the function call would be different 288 /// from a snapshot taken before the function call. 289 #[macro_export] 290 macro_rules! suspendable_virtio_tests { 291 ($name:ident, $dev: expr, $num_queues:literal, $modfun:expr) => { 292 mod $name { 293 use $crate::virtio::QueueConfig; 294 295 use super::*; 296 297 fn memory() -> GuestMemory { 298 GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)]) 299 .expect("Creating guest memory failed.") 300 } 301 302 fn interrupt() -> Interrupt { 303 Interrupt::new_for_test() 304 } 305 306 fn create_queues( 307 num_queues: usize, 308 queue_size: u16, 309 mem: &GuestMemory, 310 ) -> BTreeMap<usize, Queue> { 311 let mut queues = BTreeMap::new(); 312 for i in 0..num_queues { 313 // activate with queues of an arbitrary size. 314 let mut queue = QueueConfig::new(queue_size, 0); 315 queue.set_ready(true); 316 let queue = queue 317 .activate(mem, Event::new().unwrap()) 318 .expect("QueueConfig::activate"); 319 queues.insert(i, queue); 320 } 321 queues 322 } 323 324 #[test] 325 fn test_unactivated_sleep_snapshot_wake() { 326 let (_ctx, mut device) = $dev(); 327 let sleep_result = device.virtio_sleep().expect("failed to sleep"); 328 assert!(sleep_result.is_none()); 329 device.virtio_snapshot().expect("failed to snapshot"); 330 device.virtio_wake(None).expect("failed to wake"); 331 } 332 333 #[test] 334 fn test_sleep_snapshot_wake() { 335 let (_ctx, mut device) = $dev(); 336 let mem = memory(); 337 let interrupt = interrupt(); 338 let queues = create_queues( 339 $num_queues, 340 device 341 .queue_max_sizes() 342 .first() 343 .cloned() 344 .expect("missing queue size"), 345 &mem, 346 ); 347 device 348 .activate(mem.clone(), interrupt.clone(), queues) 349 .expect("failed to activate"); 350 let sleep_result = device 351 .virtio_sleep() 352 .expect("failed to sleep") 353 .expect("missing queues while sleeping"); 354 device.virtio_snapshot().expect("failed to snapshot"); 355 device 356 .virtio_wake(Some((mem.clone(), interrupt.clone(), sleep_result))) 357 .expect("failed to wake"); 358 } 359 360 #[test] 361 fn test_suspend_mod_restore() { 362 let (mut context, mut device) = $dev(); 363 let mem = memory(); 364 let interrupt = interrupt(); 365 let queues = create_queues( 366 $num_queues, 367 device 368 .queue_max_sizes() 369 .first() 370 .cloned() 371 .expect("missing queue size"), 372 &mem, 373 ); 374 device 375 .activate(mem.clone(), interrupt.clone(), queues) 376 .expect("failed to activate"); 377 let sleep_result = device 378 .virtio_sleep() 379 .expect("failed to sleep") 380 .expect("missing queues while sleeping"); 381 // Modify device before snapshotting. 382 $modfun(&mut context, &mut device); 383 let snap = device 384 .virtio_snapshot() 385 .expect("failed to take initial snapshot"); 386 device 387 .virtio_wake(Some((mem.clone(), interrupt.clone(), sleep_result))) 388 .expect("failed to wake"); 389 let (_, device) = &mut $dev(); 390 device 391 .virtio_restore(snap.clone()) 392 .expect("failed to restore"); 393 let snap2 = device 394 .virtio_snapshot() 395 .expect("failed to take snapshot after mod"); 396 assert_eq!(snap, snap2); 397 } 398 } 399 }; 400 } 401