devices: pci: refactor config access mechanism
The current PciRoot is only workable for the legacy I/O port 0xCF8
access mechanism; factor out the config access mechanism part of PciRoot
into PciConfigIo so that we can add a MMIO-based access mechanism for
ARM.
Change-Id: I87756b0ab31070d8717c76d419957bf5ea5d75ad
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1241539
Reviewed-by: Dylan Reid <dgreid@chromium.org>
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index 46664fc..912cd85 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -32,7 +32,7 @@
pub use self::cmos::Cmos;
pub use self::pl030::Pl030;
pub use self::i8042::I8042Device;
-pub use self::pci::{PciDevice, PciDeviceError, PciInterruptPin, PciRoot};
+pub use self::pci::{PciConfigIo, PciDevice, PciDeviceError, PciInterruptPin, PciRoot};
pub use self::proxy::ProxyDevice;
pub use self::proxy::Error as ProxyError;
pub use self::serial::Serial;
diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs
index ce42d62..a0393d3 100644
--- a/devices/src/pci/mod.rs
+++ b/devices/src/pci/mod.rs
@@ -11,7 +11,7 @@
pub use self::pci_configuration::{PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface, PciSubclass};
pub use self::pci_device::Error as PciDeviceError;
pub use self::pci_device::PciDevice;
-pub use self::pci_root::PciRoot;
+pub use self::pci_root::{PciConfigIo, PciRoot};
/// PCI has four interrupt pins A->D.
#[derive(Copy, Clone)]
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index c500f71..b47094b 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -40,8 +40,6 @@
pub struct PciRoot {
/// Bus configuration for the root device.
root_configuration: PciRootConfiguration,
- /// Current address to read/write from (0xcf8 register, litte endian).
- config_address: u32,
/// Devices attached to this bridge.
devices: Vec<Arc<Mutex<ProxyDevice>>>,
}
@@ -62,7 +60,6 @@
0,
),
},
- config_address: 0,
devices: Vec::new(),
}
}
@@ -72,11 +69,15 @@
self.devices.push(device);
}
- fn config_space_read(&self) -> u32 {
- let (enabled, bus, device, _, register) = parse_config_address(self.config_address);
-
+ pub fn config_space_read(
+ &self,
+ bus: usize,
+ device: usize,
+ _function: usize,
+ register: usize,
+ ) -> u32 {
// Only support one bus.
- if !enabled || bus != 0 {
+ if bus != 0 {
return 0xffff_ffff;
}
@@ -94,15 +95,21 @@
}
}
- fn config_space_write(&mut self, offset: u64, data: &[u8]) {
+ pub fn config_space_write(
+ &mut self,
+ bus: usize,
+ device: usize,
+ _function: usize,
+ register: usize,
+ offset: u64,
+ data: &[u8],
+ ) {
if offset as usize + data.len() > 4 {
return;
}
- let (enabled, bus, device, _, register) = parse_config_address(self.config_address);
-
// Only support one bus.
- if !enabled || bus != 0 {
+ if bus != 0 {
return;
}
@@ -120,6 +127,48 @@
}
}
+}
+
+/// Emulates PCI configuration access mechanism #1 (I/O ports 0xcf8 and 0xcfc).
+pub struct PciConfigIo {
+ /// PCI root bridge.
+ pci_root: PciRoot,
+ /// Current address to read/write from (0xcf8 register, litte endian).
+ config_address: u32,
+}
+
+impl PciConfigIo {
+ pub fn new(pci_root: PciRoot) -> Self {
+ PciConfigIo {
+ pci_root,
+ config_address: 0,
+ }
+ }
+
+ fn config_space_read(&self) -> u32 {
+ let enabled = (self.config_address & 0x8000_0000) != 0;
+ if !enabled {
+ return 0xffff_ffff;
+ }
+
+ let (bus, device, function, register) =
+ parse_config_address(self.config_address & !0x8000_0000);
+ self.pci_root
+ .config_space_read(bus, device, function, register)
+ }
+
+ fn config_space_write(&mut self, offset: u64, data: &[u8]) {
+ let enabled = (self.config_address & 0x8000_0000) != 0;
+ if !enabled {
+ return;
+ }
+
+ let (bus, device, function, register) =
+ parse_config_address(self.config_address & !0x8000_0000);
+ self.pci_root
+ .config_space_write(bus, device, function, register, offset, data)
+ }
+
fn set_config_address(&mut self, offset: u64, data: &[u8]) {
if offset as usize + data.len() > 4 {
return;
@@ -140,7 +189,7 @@
}
}
-impl BusDevice for PciRoot {
+impl BusDevice for PciConfigIo {
fn read(&mut self, offset: u64, data: &mut [u8]) {
// `offset` is relative to 0xcf8
let value = match offset {
@@ -173,8 +222,8 @@
}
}
-// Parse the CONFIG_ADDRESS register to a (enabled, bus, device, function, register) tuple.
-fn parse_config_address(config_address: u32) -> (bool, usize, usize, usize, usize) {
+// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple.
+fn parse_config_address(config_address: u32) -> (usize, usize, usize, usize) {
const BUS_NUMBER_OFFSET: usize = 16;
const BUS_NUMBER_MASK: u32 = 0x00ff;
const DEVICE_NUMBER_OFFSET: usize = 11;
@@ -184,7 +233,6 @@
const REGISTER_NUMBER_OFFSET: usize = 2;
const REGISTER_NUMBER_MASK: u32 = 0x3f;
- let enabled = (config_address & 0x8000_0000) != 0;
let bus_number = ((config_address >> BUS_NUMBER_OFFSET) & BUS_NUMBER_MASK) as usize;
let device_number = ((config_address >> DEVICE_NUMBER_OFFSET) & DEVICE_NUMBER_MASK) as usize;
let function_number =
@@ -193,7 +241,6 @@
((config_address >> REGISTER_NUMBER_OFFSET) & REGISTER_NUMBER_MASK) as usize;
(
- enabled,
bus_number,
device_number,
function_number,
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 0d494c0..e8650ec 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -70,7 +70,7 @@
use arch::{RunnableLinuxVm, VirtioDeviceStub, VmComponents};
use bootparam::boot_params;
use bootparam::E820_RAM;
-use devices::PciInterruptPin;
+use devices::{PciConfigIo, PciInterruptPin};
use sys_util::{EventFd, GuestAddress, GuestMemory};
use resources::{AddressRanges, SystemAllocator};
use kvm::*;
@@ -280,7 +280,7 @@
&mut resources,
&mut vm)
.map_err(Error::CreatePciRoot)?;
- let pci_bus = Arc::new(Mutex::new(pci));
+ let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci)));
let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
let (io_bus, stdio_serial) = Self::setup_io_bus(
@@ -429,7 +429,7 @@
///
/// * - `vm` the vm object
/// * - `exit_evt` - the event fd object which should receive exit events
- fn setup_io_bus(vm: &mut Vm, exit_evt: EventFd, pci: Option<Arc<Mutex<devices::PciRoot>>>)
+ fn setup_io_bus(vm: &mut Vm, exit_evt: EventFd, pci: Option<Arc<Mutex<devices::PciConfigIo>>>)
-> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)> {
struct NoDevice;
impl devices::BusDevice for NoDevice {}