// Copyright 2018 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::collections::BTreeMap; use std::convert::TryInto; use std::ops::Bound::Included; use std::sync::Arc; use std::sync::Weak; use anyhow::Context; use base::custom_serde::serialize_arc_mutex; use base::error; use base::RawDescriptor; use base::SendTube; use base::VmEventType; use resources::SystemAllocator; use serde::Deserialize; use serde::Serialize; use sync::Mutex; use crate::pci::pci_configuration::PciBarConfiguration; use crate::pci::pci_configuration::PciBridgeSubclass; use crate::pci::pci_configuration::PciClassCode; use crate::pci::pci_configuration::PciConfiguration; use crate::pci::pci_configuration::PciHeaderType; use crate::pci::pci_configuration::HEADER_TYPE_MULTIFUNCTION_MASK; use crate::pci::pci_configuration::HEADER_TYPE_REG; use crate::pci::pci_device::Error; use crate::pci::pci_device::PciBus; use crate::pci::pci_device::PciDevice; use crate::pci::PciAddress; use crate::pci::PciId; use crate::pci::PCI_VENDOR_ID_INTEL; use crate::Bus; use crate::BusAccessInfo; use crate::BusDevice; use crate::BusType; use crate::DeviceId; use crate::Suspendable; // A PciDevice that holds the root hub's configuration. #[derive(Serialize)] struct PciRootConfiguration { config: PciConfiguration, } impl PciDevice for PciRootConfiguration { fn debug_label(&self) -> String { "pci root device".to_owned() } fn allocate_address(&mut self, _resources: &mut SystemAllocator) -> Result { // PCI root fixed address. Ok(PciAddress { bus: 0, dev: 0, func: 0, }) } fn keep_rds(&self) -> Vec { Vec::new() } fn read_config_register(&self, reg_idx: usize) -> u32 { self.config.read_reg(reg_idx) } fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) { self.config.write_reg(reg_idx, offset, data) } fn read_bar(&mut self, _addr: u64, _data: &mut [u8]) {} fn write_bar(&mut self, _addr: u64, _data: &[u8]) {} fn get_bar_configuration(&self, bar_num: usize) -> Option { self.config.get_bar_configuration(bar_num) } } impl Suspendable for PciRootConfiguration { // no thread to sleep, no change required. fn sleep(&mut self) -> anyhow::Result<()> { Ok(()) } fn wake(&mut self) -> anyhow::Result<()> { Ok(()) } fn snapshot(&self) -> anyhow::Result { serde_json::to_value(self) .with_context(|| format!("failed to serialize {}", PciDevice::debug_label(self))) } fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { #[derive(Deserialize)] struct PciRootConfigurationSerializable { config: serde_json::Value, } let deser: PciRootConfigurationSerializable = serde_json::from_value(data).context("failed to deserialize PciRootConfiguration")?; self.config.restore(deser.config)?; Ok(()) } } // Command send to pci root worker thread to add/remove device from pci root pub enum PciRootCommand { Add(PciAddress, Arc>), AddBridge(Arc>), Remove(PciAddress), Kill, } /// Emulates the PCI Root bridge. #[allow(dead_code)] // TODO(b/174705596): remove once mmio_bus and io_bus are used #[derive(Serialize)] pub struct PciRoot { /// Memory (MMIO) bus. #[serde(skip_serializing)] mmio_bus: Weak, /// IO bus (x86 only - for non-x86 platforms, this is just an empty Bus). #[serde(skip_serializing)] io_bus: Weak, /// Root pci bus (bus 0) #[serde(skip_serializing)] root_bus: Arc>, /// Bus configuration for the root device. root_configuration: PciRootConfiguration, /// Devices attached to this bridge. #[serde(skip_serializing)] devices: BTreeMap>>, /// pcie enhanced configuration access mmio base pcie_cfg_mmio: Option, } const PCI_DEVICE_ID_INTEL_82441: u16 = 0x1237; const PCIE_XBAR_BASE_ADDR: usize = 24; /// Used to serialize relevant information to PciRoot #[derive(Deserialize)] struct PciRootSerializable { root_configuration: serde_json::Value, pcie_cfg_mmio: Option, } impl PciRoot { /// Create an empty PCI root bus. pub fn new(mmio_bus: Weak, io_bus: Weak, root_bus: Arc>) -> Self { PciRoot { mmio_bus, io_bus, root_bus, root_configuration: PciRootConfiguration { config: PciConfiguration::new( PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, PciClassCode::BridgeDevice, &PciBridgeSubclass::HostBridge, None, PciHeaderType::Device, 0, 0, 0, ), }, devices: BTreeMap::new(), pcie_cfg_mmio: None, } } /// Get the root pci bus pub fn get_root_bus(&self) -> Arc> { self.root_bus.clone() } /// Get the ACPI path to a PCI device pub fn acpi_path(&self, address: &PciAddress) -> Option { if let Some(device) = self.devices.get(address) { let path = self.root_bus.lock().path_to(address.bus); if path.is_empty() { None } else { Some(format!( "_SB_.{}.{}", path.iter() .map(|x| format!("PC{:02X}", x)) .collect::>() .join("."), match device.lock().is_bridge() { Some(bus_no) => format!("PC{:02X}", bus_no), None => format!("PE{:02X}", address.devfn()), } )) } } else { None } } /// enable pcie enhanced configuration access and set base mmio pub fn enable_pcie_cfg_mmio(&mut self, pcie_cfg_mmio: u64) { self.pcie_cfg_mmio = Some(pcie_cfg_mmio); } /// Add a `device` to this root PCI bus. pub fn add_device(&mut self, address: PciAddress, device: Arc>) { // Ignore attempt to replace PCI Root host bridge. if !address.is_root() { self.devices.insert(address, device); } if let Err(e) = self.root_bus.lock().add_child_device(address) { error!("add device error: {}", e); } } pub fn add_bridge(&mut self, bridge_bus: Arc>) { if let Err(e) = self.root_bus.lock().add_child_bus(bridge_bus) { error!("add bridge error: {}", e); } } pub fn remove_device(&mut self, address: PciAddress) { if let Some(d) = self.devices.remove(&address) { for (range, bus_type) in d.lock().get_ranges() { let bus_ptr = if bus_type == BusType::Mmio { match self.mmio_bus.upgrade() { Some(m) => m, None => continue, } } else { match self.io_bus.upgrade() { Some(i) => i, None => continue, } }; let _ = bus_ptr.remove(range.base, range.len); } // Remove the pci bus if this device is a pci bridge. if let Some(bus_no) = d.lock().is_bridge() { let _ = self.root_bus.lock().remove_child_bus(bus_no); } d.lock().destroy_device(); let _ = self.root_bus.lock().remove_child_device(address); } } pub fn config_space_read(&self, address: PciAddress, register: usize) -> u32 { if address.is_root() { if register == PCIE_XBAR_BASE_ADDR && self.pcie_cfg_mmio.is_some() { let pcie_mmio = self.pcie_cfg_mmio.unwrap() as u32; pcie_mmio | 0x1 } else if register == (PCIE_XBAR_BASE_ADDR + 1) && self.pcie_cfg_mmio.is_some() { (self.pcie_cfg_mmio.unwrap() >> 32) as u32 } else { self.root_configuration.config_register_read(register) } } else { let mut data = self .devices .get(&address) .map_or(0xffff_ffff, |d| d.lock().config_register_read(register)); if register == HEADER_TYPE_REG { // Set multifunction bit in header type if there are devices at non-zero functions // in this slot. if self .devices .range(( Included(&PciAddress { bus: address.bus, dev: address.dev, func: 1, }), Included(&PciAddress { bus: address.bus, dev: address.dev, func: 7, }), )) .next() .is_some() { data |= HEADER_TYPE_MULTIFUNCTION_MASK; } } data } } pub fn config_space_write( &mut self, address: PciAddress, register: usize, offset: u64, data: &[u8], ) { if offset as usize + data.len() > 4 { return; } if address.is_root() { self.root_configuration .config_register_write(register, offset, data); } else if let Some(d) = self.devices.get(&address) { let res = d.lock().config_register_write(register, offset, data); if !res.mmio_add.is_empty() || !res.mmio_remove.is_empty() { let mmio_bus = match self.mmio_bus.upgrade() { Some(m) => m, None => return, }; for range in &res.mmio_remove { let _ = mmio_bus.remove(range.base, range.len); } for range in &res.mmio_add { let _ = mmio_bus.insert(d.clone(), range.base, range.len); } } if !res.io_add.is_empty() || !res.io_remove.is_empty() { let io_bus = match self.io_bus.upgrade() { Some(i) => i, None => return, }; for range in &res.io_remove { let _ = io_bus.remove(range.base, range.len); } for range in &res.io_add { let _ = io_bus.insert(d.clone(), range.base, range.len); } } for remove_pci_device in res.removed_pci_devices.iter() { self.remove_device(*remove_pci_device); } } } pub fn virtual_config_space_read(&self, address: PciAddress, register: usize) -> u32 { if address.is_root() { 0u32 } else { self.devices .get(&address) .map_or(0u32, |d| d.lock().virtual_config_register_read(register)) } } pub fn virtual_config_space_write(&mut self, address: PciAddress, register: usize, value: u32) { if !address.is_root() { if let Some(d) = self.devices.get(&address) { d.lock().virtual_config_register_write(register, value); } } } pub fn snapshot(&self) -> anyhow::Result { serde_json::to_value(self).context("failed to serialize PciRoot") } pub fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { let deser: PciRootSerializable = serde_json::from_value(data).context("failed to deserialize PciRoot")?; self.root_configuration.restore(deser.root_configuration)?; self.pcie_cfg_mmio = deser.pcie_cfg_mmio; Ok(()) } } /// Emulates PCI configuration access mechanism #1 (I/O ports 0xcf8 and 0xcfc). #[derive(Serialize)] pub struct PciConfigIo { /// PCI root bridge. #[serde(serialize_with = "serialize_arc_mutex")] pci_root: Arc>, /// Current address to read/write from (0xcf8 register, litte endian). config_address: u32, /// Tube to signal that the guest requested reset via writing to 0xcf9 register. #[serde(skip_serializing)] reset_evt_wrtube: SendTube, } #[derive(Deserialize)] struct PciConfigIoSerializable { pci_root: serde_json::Value, config_address: u32, } impl PciConfigIo { const REGISTER_BITS_NUM: usize = 8; pub fn new(pci_root: Arc>, reset_evt_wrtube: SendTube) -> Self { PciConfigIo { pci_root, config_address: 0, reset_evt_wrtube, } } fn config_space_read(&self) -> u32 { let enabled = (self.config_address & 0x8000_0000) != 0; if !enabled { return 0xffff_ffff; } let (address, register) = PciAddress::from_config_address(self.config_address, Self::REGISTER_BITS_NUM); self.pci_root.lock().config_space_read(address, register) } fn config_space_write(&mut self, offset: u64, data: &[u8]) { let enabled = (self.config_address & 0x8000_0000) != 0; if !enabled { return; } let (address, register) = PciAddress::from_config_address(self.config_address, Self::REGISTER_BITS_NUM); self.pci_root .lock() .config_space_write(address, register, offset, data) } fn set_config_address(&mut self, offset: u64, data: &[u8]) { if offset as usize + data.len() > 4 { return; } let (mask, value): (u32, u32) = match data.len() { 1 => ( 0x0000_00ff << (offset * 8), (data[0] as u32) << (offset * 8), ), 2 => ( 0x0000_ffff << (offset * 8), u32::from(u16::from_le_bytes(data.try_into().unwrap())) << (offset * 8), ), 4 => (0xffff_ffff, u32::from_le_bytes(data.try_into().unwrap())), _ => return, }; self.config_address = (self.config_address & !mask) | value; } } const PCI_RESET_CPU_BIT: u8 = 1 << 2; impl BusDevice for PciConfigIo { fn debug_label(&self) -> String { format!("pci config io-port 0x{:03x}", self.config_address) } fn device_id(&self) -> DeviceId { PciId::new(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441).into() } fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) { // `offset` is relative to 0xcf8 let value = match info.offset { 0..=3 => self.config_address, 4..=7 => self.config_space_read(), _ => 0xffff_ffff, }; // Only allow reads to the register boundary. let start = info.offset as usize % 4; let end = start + data.len(); if end <= 4 { for i in start..end { data[i - start] = (value >> (i * 8)) as u8; } } else { for d in data { *d = 0xff; } } } fn write(&mut self, info: BusAccessInfo, data: &[u8]) { // `offset` is relative to 0xcf8 match info.offset { _o @ 1 if data.len() == 1 && data[0] & PCI_RESET_CPU_BIT != 0 => { if let Err(e) = self .reset_evt_wrtube .send::(&VmEventType::Reset) { error!("failed to trigger PCI 0xcf9 reset event: {}", e); } } o @ 0..=3 => self.set_config_address(o, data), o @ 4..=7 => self.config_space_write(o - 4, data), _ => (), }; } } impl Suspendable for PciConfigIo { // no thread to sleep, no change required. fn sleep(&mut self) -> anyhow::Result<()> { Ok(()) } fn wake(&mut self) -> anyhow::Result<()> { Ok(()) } fn snapshot(&self) -> anyhow::Result { serde_json::to_value(self) .with_context(|| format!("failed to serialize {}", self.debug_label())) } fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { let mut root = self.pci_root.lock(); let deser: PciConfigIoSerializable = serde_json::from_value(data) .context(format!("failed to deserialize {}", self.debug_label()))?; root.restore(deser.pci_root)?; self.config_address = deser.config_address; Ok(()) } } /// Emulates PCI memory-mapped configuration access mechanism. #[derive(Serialize)] pub struct PciConfigMmio { /// PCI root bridge. #[serde(serialize_with = "serialize_arc_mutex")] pci_root: Arc>, /// Register bit number in config address. register_bit_num: usize, } #[derive(Deserialize)] struct PciConfigMmioSerializable { pci_root: serde_json::Value, register_bit_num: usize, } impl PciConfigMmio { pub fn new(pci_root: Arc>, register_bit_num: usize) -> Self { PciConfigMmio { pci_root, register_bit_num, } } fn config_space_read(&self, config_address: u32) -> u32 { let (address, register) = PciAddress::from_config_address(config_address, self.register_bit_num); self.pci_root.lock().config_space_read(address, register) } fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) { let (address, register) = PciAddress::from_config_address(config_address, self.register_bit_num); self.pci_root .lock() .config_space_write(address, register, offset, data) } } impl BusDevice for PciConfigMmio { fn debug_label(&self) -> String { "pci config mmio".to_owned() } fn device_id(&self) -> DeviceId { PciId::new(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441).into() } fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) { // Only allow reads to the register boundary. let start = info.offset as usize % 4; let end = start + data.len(); if end > 4 || info.offset > u32::max_value() as u64 { for d in data { *d = 0xff; } return; } let value = self.config_space_read(info.offset as u32); for i in start..end { data[i - start] = (value >> (i * 8)) as u8; } } fn write(&mut self, info: BusAccessInfo, data: &[u8]) { if info.offset > u32::max_value() as u64 { return; } self.config_space_write(info.offset as u32, info.offset % 4, data) } } impl Suspendable for PciConfigMmio { // no thread to sleep, no change required. fn sleep(&mut self) -> anyhow::Result<()> { Ok(()) } fn wake(&mut self) -> anyhow::Result<()> { Ok(()) } fn snapshot(&self) -> anyhow::Result { serde_json::to_value(self) .with_context(|| format!("failed to serialize {}", self.debug_label())) } fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { let mut root = self.pci_root.lock(); let deser: PciConfigMmioSerializable = serde_json::from_value(data) .context(format!("failed to deserialize {}", self.debug_label()))?; root.restore(deser.pci_root)?; self.register_bit_num = deser.register_bit_num; Ok(()) } } /// Inspired by PCI configuration space, CrosVM provides 2048 dword virtual registers (8KiB in /// total) for each PCI device. The guest can use these registers to exchange device-specific /// information with crosvm. The first 4kB is trapped by crosvm and crosvm supplies these /// register's emulation. The second 4KB is mapped into guest directly as shared memory, so /// when guest access this 4KB, vm exit doesn't happen. /// All these virtual registers from all PCI devices locate in a contiguous memory region. /// The base address of this memory region is provided by an IntObj named VCFG in the ACPI DSDT. /// Bit 12 is used to select the first trapped page or the second directly mapped page /// The offset of each register is calculated in the same way as PCIe ECAM; /// i.e. offset = (bus << 21) | (device << 16) | (function << 13) | (page_select << 12) | /// (register_index << 2) #[derive(Serialize)] pub struct PciVirtualConfigMmio { /// PCI root bridge. #[serde(serialize_with = "serialize_arc_mutex")] pci_root: Arc>, /// Register bit number in config address. register_bit_num: usize, } #[derive(Deserialize)] struct PciVirtualConfigMmioSerializable { pci_root: serde_json::Value, register_bit_num: usize, } impl PciVirtualConfigMmio { pub fn new(pci_root: Arc>, register_bit_num: usize) -> Self { PciVirtualConfigMmio { pci_root, register_bit_num, } } } impl BusDevice for PciVirtualConfigMmio { fn debug_label(&self) -> String { "pci virtual config mmio".to_owned() } fn device_id(&self) -> DeviceId { PciId::new(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441).into() } fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) { let value = if info.offset % 4 != 0 || data.len() != 4 { error!( "{} unexpected read at offset = {}, len = {}", self.debug_label(), info.offset, data.len() ); 0u32 } else { let (address, register) = PciAddress::from_config_address(info.offset as u32, self.register_bit_num); self.pci_root .lock() .virtual_config_space_read(address, register) }; data[0..4].copy_from_slice(&value.to_le_bytes()[..]); } fn write(&mut self, info: BusAccessInfo, data: &[u8]) { if info.offset % 4 != 0 || data.len() != 4 { error!( "{} unexpected write at offset = {}, len = {}", self.debug_label(), info.offset, data.len() ); return; } // Unwrap is safe as we verified length above let value = u32::from_le_bytes(data.try_into().unwrap()); let (address, register) = PciAddress::from_config_address(info.offset as u32, self.register_bit_num); self.pci_root .lock() .virtual_config_space_write(address, register, value) } } impl Suspendable for PciVirtualConfigMmio { // no thread to sleep, no change required. fn sleep(&mut self) -> anyhow::Result<()> { Ok(()) } fn wake(&mut self) -> anyhow::Result<()> { Ok(()) } fn snapshot(&self) -> anyhow::Result { serde_json::to_value(self) .with_context(|| format!("failed to serialize {}", self.debug_label())) } fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { let mut root = self.pci_root.lock(); let deser: PciVirtualConfigMmioSerializable = serde_json::from_value(data) .context(format!("failed to deserialize {}", self.debug_label()))?; root.restore(deser.pci_root)?; self.register_bit_num = deser.register_bit_num; Ok(()) } } #[cfg(test)] mod tests { use base::Tube; use super::*; use crate::suspendable_tests; fn create_pci_root() -> Arc> { let io_bus = Arc::new(Bus::new()); let mmio_bus = Arc::new(Bus::new()); let root_bus = Arc::new(Mutex::new(PciBus::new(0, 0, false))); Arc::new(Mutex::new(PciRoot::new( Arc::downgrade(&mmio_bus), Arc::downgrade(&io_bus), root_bus, ))) } fn create_pci_io_config(pci_root: Arc>) -> PciConfigIo { let (reset_evt_wrtube, _) = Tube::directional_pair().unwrap(); PciConfigIo::new(pci_root, reset_evt_wrtube) } fn modify_pci_io_config(pci_config: &mut PciConfigIo) { pci_config.config_address += 1; } fn create_pci_mmio_config(pci_root: Arc>) -> PciConfigMmio { PciConfigMmio::new(pci_root, 0) } fn modify_pci_mmio_config(pci_config: &mut PciConfigMmio) { pci_config.register_bit_num += 1; } fn create_pci_virtual_config_mmio(pci_root: Arc>) -> PciVirtualConfigMmio { PciVirtualConfigMmio::new(pci_root, 0) } fn modify_pci_virtual_config_mmio(pci_config: &mut PciVirtualConfigMmio) { pci_config.register_bit_num += 1; } suspendable_tests!( pci_io_config, create_pci_io_config(create_pci_root()), modify_pci_io_config ); suspendable_tests!( pcie_mmio_config, create_pci_mmio_config(create_pci_root()), modify_pci_mmio_config ); suspendable_tests!( pci_virtual_config_mmio, create_pci_virtual_config_mmio(create_pci_root()), modify_pci_virtual_config_mmio ); #[test] fn pci_set_config_address_word() { let mut pci_io_config = create_pci_io_config(create_pci_root()); // Set the full 32-bit config_address to a known value (0x11223344). pci_io_config.write( BusAccessInfo { offset: 0, address: 0xCF8, id: 0, }, &[0x44, 0x33, 0x22, 0x11], ); // Overwrite the high 16 bits of config_address with 0x55AA (test for b/274366589). pci_io_config.write( BusAccessInfo { offset: 2, address: 0xCFA, id: 0, }, &[0xAA, 0x55], ); // Verify config_address has the expected value (0x55AA3344). let mut config_address = [0u8; 4]; pci_io_config.read( BusAccessInfo { offset: 0, address: 0xCF8, id: 0, }, &mut config_address, ); assert_eq!(config_address, [0x44, 0x33, 0xAA, 0x55]); } }