// Copyright 2018 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::collections::BTreeMap;56#[cfg(target_arch = "x86_64")]7use acpi_tables::sdt::SDT;8use anyhow::anyhow;9use anyhow::Result;10use base::Protection;11use base::RawDescriptor;12use hypervisor::MemCacheType;13use resources::AddressRange;14use snapshot::AnySnapshot;15use vm_control::VmMemorySource;16use vm_memory::GuestMemory;1718use super::*;19use crate::pci::MsixStatus;20use crate::pci::PciAddress;21use crate::pci::PciBarConfiguration;22use crate::pci::PciCapability;2324/// Type of Virtio device memory mapping to use.25pub enum SharedMemoryPrepareType {26/// On first attempted mapping, the entire SharedMemoryRegion is configured with declared27/// MemCacheType.28SingleMappingOnFirst(MemCacheType),29/// No mapping preparation is performed. each mapping is handled individually30DynamicPerMapping,31}3233/// Trait for mapping memory into the device's shared memory region.34pub trait SharedMemoryMapper: Send {35/// Maps the given |source| into the shared memory region at |offset|.36fn add_mapping(37&mut self,38source: VmMemorySource,39offset: u64,40prot: Protection,41cache: MemCacheType,42) -> Result<()>;4344/// Removes the mapping beginning at |offset|.45fn remove_mapping(&mut self, offset: u64) -> Result<()>;4647fn as_raw_descriptor(&self) -> Option<RawDescriptor> {48None49}50}5152/// Trait for virtio devices to be driven by a virtio transport.53///54/// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the55/// device. Once the guest driver has configured the device, `VirtioDevice::activate` will be called56/// and all the events, memory, and queues for device operation will be moved into the device.57/// Optionally, a virtio device can implement device reset in which it returns said resources and58/// resets its internal.59///60/// Virtio device state machine61/// ```none62/// restore (inactive)63/// ----------------------------------------------------64/// | |65/// | V66/// | ------------ --------------67/// ------------- restore(active) | asleep | | asleep | // States in this row68/// |asleep(new)|---------------> | (active) | | (inactive) | // can be snapshotted69/// ------------- ------------ --------------70/// ^ | ^ | ^ |71/// | | | | | |72/// sleep wake sleep wake sleep wake73/// | | | | | |74/// | V | V | V75/// ------------ activate ---------- reset ------------76/// | new | ---------------> | active | ------> | inactive |77/// ------------ ---------- <------ ------------78/// activate79/// ```80pub trait VirtioDevice: Send {81/// Returns a label suitable for debug output.82fn debug_label(&self) -> String {83format!("virtio-{}", self.device_type())84}8586/// A vector of device-specific file descriptors that must be kept open87/// after jailing. Must be called before the process is jailed.88fn keep_rds(&self) -> Vec<RawDescriptor>;8990/// The virtio device type.91fn device_type(&self) -> DeviceType;9293/// The maximum size of each queue that this device supports.94fn queue_max_sizes(&self) -> &[u16];9596/// The number of interrupts used by this device.97fn num_interrupts(&self) -> usize {98self.queue_max_sizes().len()99}100101/// The set of feature bits that this device supports in addition to the base features.102fn features(&self) -> u64 {1030104}105106/// Acknowledges that this set of features should be enabled.107fn ack_features(&mut self, value: u64) {108let _ = value;109}110111/// Reads this device configuration space at `offset`.112fn read_config(&self, offset: u64, data: &mut [u8]) {113let _ = offset;114let _ = data;115}116117/// Writes to this device configuration space at `offset`.118fn write_config(&mut self, offset: u64, data: &[u8]) {119let _ = offset;120let _ = data;121}122123/// Activates this device for real usage.124fn activate(125&mut self,126mem: GuestMemory,127interrupt: Interrupt,128queues: BTreeMap<usize, Queue>,129) -> Result<()>;130131/// Optionally deactivates this device. If the reset method is132/// not able to reset the virtio device, or the virtio device model doesn't133/// implement the reset method, an `Err` value is returned to indicate134/// the reset is not successful. Otherwise `Ok(())` should be returned.135fn reset(&mut self) -> Result<()> {136Err(anyhow!("reset not implemented for {}", self.debug_label()))137}138139/// Returns any additional BAR configuration required by the device.140fn get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration> {141Vec::new()142}143144/// Returns any additional capabiltiies required by the device.145fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {146Vec::new()147}148149/// Invoked when the device is sandboxed.150fn on_device_sandboxed(&mut self) {}151152fn control_notify(&self, _behavior: MsixStatus) {}153154#[cfg(target_arch = "x86_64")]155fn generate_acpi(156&mut self,157pci_address: PciAddress,158sdts: &mut Vec<SDT>,159) -> anyhow::Result<()> {160let _ = pci_address;161let _ = sdts;162Ok(())163}164165/// Returns the PCI address where the device will be allocated.166/// Returns `None` if any address is good for the device.167fn pci_address(&self) -> Option<PciAddress> {168None169}170171/// Returns the device's shared memory region if present.172fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {173None174}175176/// If true, VFIO passthrough devices can access descriptors mapped into177/// this region by mapping the corresponding addresses from this device's178/// PCI bar into their IO address space with virtio-iommu.179///180/// NOTE: Not all vm_control::VmMemorySource types are supported.181/// NOTE: Not yet compatible with PrepareSharedMemoryRegion (aka fixed mapping).182fn expose_shmem_descriptors_with_viommu(&self) -> bool {183false184}185186/// Provides the trait object used to map files into the device's shared187/// memory region.188///189/// If `get_shared_memory_region` returns `Some`, then this will be called190/// before `activate`.191fn set_shared_memory_mapper(&mut self, _mapper: Box<dyn SharedMemoryMapper>) {}192193/// Provides the guest address range of the shared memory region, if one is present. Will194/// be called before `activate`.195fn set_shared_memory_region(&mut self, shmem_region: AddressRange) {196let _ = shmem_region;197}198199/// Queries the implementation whether a single prepared hypervisor memory mapping with explicit200/// caching type should be setup lazily on first mapping request, or whether to dynamically201/// setup a hypervisor mapping with every request's caching type.202fn get_shared_memory_prepare_type(&mut self) -> SharedMemoryPrepareType {203// default to lazy-prepare of a single memslot with explicit caching type204SharedMemoryPrepareType::SingleMappingOnFirst(MemCacheType::CacheCoherent)205}206207/// Pause all processing.208///209/// Gives up the queues so that a higher layer can potentially snapshot them. The210/// implementations should also drop the `Interrupt` and queues `Event`s that were given along211/// with the queues originally.212///213/// Unlike `Suspendable::sleep`, this is not idempotent. Attempting to sleep while already214/// asleep is an error.215fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {216anyhow::bail!("virtio_sleep not implemented for {}", self.debug_label());217}218219/// Resume all processing.220///221/// If the device's queues are active, then the queues and associated data will is included.222///223/// Unlike `Suspendable::wake`, this is not idempotent. Attempting to wake while already awake224/// is an error.225fn virtio_wake(226&mut self,227_queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,228) -> anyhow::Result<()> {229anyhow::bail!("virtio_wake not implemented for {}", self.debug_label());230}231232/// Snapshot current state. Device must be asleep.233fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {234anyhow::bail!("virtio_snapshot not implemented for {}", self.debug_label());235}236237/// Restore device state from a snapshot.238/// TODO(b/280607404): Vhost user will need fds passed to the device process.239fn virtio_restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {240anyhow::bail!("virtio_restore not implemented for {}", self.debug_label());241}242243// Returns a tuple consisting of the non-arch specific part of the OpenFirmware path,244// represented as bytes, and the boot index of a device. The non-arch specific part of path for245// a virtio-blk device, for example, would consist of everything after the first '/' below:246// pci@i0cf8/scsi@6[,3]/disk@0,0247// ^ ^ ^ ^ ^248// | | | fixed249// | | (PCI function related to disk (optional))250// (x86 specf (PCI slot holding disk)251// root at sys252// bus port)253fn bootorder_fw_cfg(&self, _pci_address: u8) -> Option<(Vec<u8>, usize)> {254None255}256}257258// General tests that should pass on all suspendables.259// Do implement device-specific tests to validate the functionality of the device.260// Those tests are not a replacement for regular tests. Only an extension specific to the trait's261// basic functionality.262/// `name` is the name of the test grouping. Can be anything unique within the same crate.263/// `dev` is a block that returns a created virtio device.264/// ``num_queues` is the number of queues to be created.265/// `modfun` is the function name of the function that would modify the device. The function call266/// should modify the device so that a snapshot taken after the function call would be different267/// from a snapshot taken before the function call.268#[macro_export]269macro_rules! suspendable_virtio_tests {270($name:ident, $dev: expr, $num_queues:literal, $modfun:expr) => {271mod $name {272use $crate::virtio::QueueConfig;273274use super::*;275276fn memory() -> GuestMemory {277use vm_memory::GuestAddress;278GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])279.expect("Creating guest memory failed.")280}281282fn interrupt() -> Interrupt {283Interrupt::new_for_test()284}285286fn create_queues(287num_queues: usize,288queue_size: u16,289mem: &GuestMemory,290interrupt: Interrupt,291) -> BTreeMap<usize, Queue> {292let mut queues = BTreeMap::new();293for i in 0..num_queues {294// activate with queues of an arbitrary size.295let mut queue = QueueConfig::new(queue_size, 0);296queue.set_ready(true);297let queue = queue298.activate(mem, base::Event::new().unwrap(), interrupt.clone())299.expect("QueueConfig::activate");300queues.insert(i, queue);301}302queues303}304305#[test]306fn test_unactivated_sleep_snapshot_wake() {307let (_ctx, mut device) = $dev();308let sleep_result = device.virtio_sleep().expect("failed to sleep");309assert!(sleep_result.is_none());310device.virtio_snapshot().expect("failed to snapshot");311device.virtio_wake(None).expect("failed to wake");312}313314#[test]315fn test_sleep_snapshot_wake() {316let (_ctx, mut device) = $dev();317let mem = memory();318let interrupt = interrupt();319let queues = create_queues(320$num_queues,321device322.queue_max_sizes()323.first()324.cloned()325.expect("missing queue size"),326&mem,327interrupt.clone(),328);329device330.activate(mem.clone(), interrupt.clone(), queues)331.expect("failed to activate");332let sleep_result = device333.virtio_sleep()334.expect("failed to sleep")335.expect("missing queues while sleeping");336device.virtio_snapshot().expect("failed to snapshot");337device338.virtio_wake(Some((mem.clone(), interrupt.clone(), sleep_result)))339.expect("failed to wake");340}341342#[test]343fn test_suspend_mod_restore() {344let (mut context, mut device) = $dev();345let mem = memory();346let interrupt = interrupt();347let queues = create_queues(348$num_queues,349device350.queue_max_sizes()351.first()352.cloned()353.expect("missing queue size"),354&mem,355interrupt.clone(),356);357device358.activate(mem.clone(), interrupt.clone(), queues)359.expect("failed to activate");360let sleep_result = device361.virtio_sleep()362.expect("failed to sleep")363.expect("missing queues while sleeping");364// Modify device before snapshotting.365$modfun(&mut context, &mut device);366let snap = device367.virtio_snapshot()368.expect("failed to take initial snapshot");369device370.virtio_wake(Some((mem.clone(), interrupt.clone(), sleep_result)))371.expect("failed to wake");372373// Create a new device to restore the previously taken snapshot374let (_ctx2, mut device) = $dev();375// Sleep the device before restore376assert!(device.virtio_sleep().expect("failed to sleep").is_none());377device378.virtio_restore(snap.clone())379.expect("failed to restore");380let snap2 = device381.virtio_snapshot()382.expect("failed to take snapshot after mod");383assert_eq!(snap, snap2);384}385}386};387}388389390