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