blob: 04c6f7a9fadd7a7464c8087c129de482ffd17585 [file] [log] [blame]
// 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<PciAddress, Error> {
// PCI root fixed address.
Ok(PciAddress {
bus: 0,
dev: 0,
func: 0,
})
}
fn keep_rds(&self) -> Vec<RawDescriptor> {
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<PciBarConfiguration> {
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::Value> {
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<Mutex<dyn BusDevice>>),
AddBridge(Arc<Mutex<PciBus>>),
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<Bus>,
/// IO bus (x86 only - for non-x86 platforms, this is just an empty Bus).
#[serde(skip_serializing)]
io_bus: Weak<Bus>,
/// Root pci bus (bus 0)
#[serde(skip_serializing)]
root_bus: Arc<Mutex<PciBus>>,
/// Bus configuration for the root device.
root_configuration: PciRootConfiguration,
/// Devices attached to this bridge.
#[serde(skip_serializing)]
devices: BTreeMap<PciAddress, Arc<Mutex<dyn BusDevice>>>,
/// pcie enhanced configuration access mmio base
pcie_cfg_mmio: Option<u64>,
}
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<u64>,
}
impl PciRoot {
/// Create an empty PCI root bus.
pub fn new(mmio_bus: Weak<Bus>, io_bus: Weak<Bus>, root_bus: Arc<Mutex<PciBus>>) -> 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<Mutex<PciBus>> {
self.root_bus.clone()
}
/// Get the ACPI path to a PCI device
pub fn acpi_path(&self, address: &PciAddress) -> Option<String> {
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::<Vec<String>>()
.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<Mutex<dyn BusDevice>>) {
// 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<Mutex<PciBus>>) {
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::Value> {
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<Mutex<PciRoot>>,
/// 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<Mutex<PciRoot>>, 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 * 16),
u32::from(u16::from_le_bytes(data.try_into().unwrap())) << (offset * 16),
),
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>(&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::Value> {
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<Mutex<PciRoot>>,
/// 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<Mutex<PciRoot>>, 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::Value> {
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<Mutex<PciRoot>>,
/// 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<Mutex<PciRoot>>, 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::Value> {
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 crate::suspendable_tests;
use super::*;
use base::Tube;
fn create_pci_root() -> Arc<Mutex<PciRoot>> {
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<Mutex<PciRoot>>) -> 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<Mutex<PciRoot>>) -> 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<Mutex<PciRoot>>) -> 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
);
}