diff options
-rw-r--r-- | devices/src/lib.rs | 2 | ||||
-rw-r--r-- | devices/src/pci/mod.rs | 2 | ||||
-rw-r--r-- | devices/src/pci/pci_root.rs | 79 | ||||
-rw-r--r-- | x86_64/src/lib.rs | 6 |
4 files changed, 68 insertions, 21 deletions
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::bus::Error as BusError; 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 @@ mod pci_root; 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 @@ impl PciDevice for PciRootConfiguration { 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 @@ impl PciRoot { 0, ), }, - config_address: 0, devices: Vec::new(), } } @@ -72,11 +69,15 @@ impl PciRoot { 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 @@ impl PciRoot { } } - 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 @@ impl PciRoot { } } +} + +/// 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 PciRoot { } } -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 @@ impl BusDevice for PciRoot { } } -// 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 @@ fn parse_config_address(config_address: u32) -> (bool, usize, usize, usize, usiz 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 @@ fn parse_config_address(config_address: u32) -> (bool, usize, usize, usize, usiz ((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 std::sync::{Arc, Mutex}; 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 @@ impl arch::LinuxArch for X8664arch { &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 @@ impl X8664arch { /// /// * - `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 {} |