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