From cc08cdbd836cb280cebcb5f1d737da58aa3eff07 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Thu, 24 May 2018 19:30:53 +0000 Subject: [PATCH] devices: pci - Add PciDevice The PciDevice trait represents any PciDevice. It provides access to configuration registers and BAR space. Change-Id: Ie18cb16b8bd97f9b70af05ebfebbfc612ce18494 Signed-off-by: Dylan Reid Reviewed-on: https://chromium-review.googlesource.com/1072575 Commit-Ready: ChromeOS CL Exonerator Bot Reviewed-by: Sonny Rao --- devices/src/bus.rs | 7 +++ devices/src/lib.rs | 2 +- devices/src/pci/mod.rs | 3 + devices/src/pci/pci_device.rs | 114 ++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 devices/src/pci/pci_device.rs diff --git a/devices/src/bus.rs b/devices/src/bus.rs index 030694a005..7b37c323de 100644 --- a/devices/src/bus.rs +++ b/devices/src/bus.rs @@ -19,6 +19,13 @@ pub trait BusDevice: Send { fn read(&mut self, offset: u64, data: &mut [u8]) {} /// Writes at `offset` into this device fn write(&mut self, offset: u64, data: &[u8]) {} + /// Sets a register in the configuration space. Only used by PCI. + /// * `reg_idx` - The index of the config register to modify. + /// * `offset` - Offset in to the register. + fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {} + /// Gets a register from the configuration space. Only used by PCI. + /// * `reg_idx` - The index of the config register to read. + fn config_register_read(&self, reg_idx: usize) -> u32 { 0 } } #[derive(Debug)] diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 3418baaba5..3a63577ff2 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -30,7 +30,7 @@ pub use self::bus::{Bus, BusDevice}; pub use self::cmos::Cmos; pub use self::pl030::Pl030; pub use self::i8042::I8042Device; -pub use self::pci::PciInterruptPin; +pub use self::pci::{PciDevice, PciInterruptPin}; 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 d997af039e..afcad2c4cf 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -5,6 +5,9 @@ //! Implements pci devices and busses. mod pci_configuration; +mod pci_device; + +pub use self::pci_device::PciDevice; /// PCI has four interrupt pins A->D. #[derive(Copy, Clone)] diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs new file mode 100644 index 0000000000..4af845cf4e --- /dev/null +++ b/devices/src/pci/pci_device.rs @@ -0,0 +1,114 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use byteorder::{ByteOrder, LittleEndian}; + +use std; + +use pci::pci_configuration::PciConfiguration; +use pci::PciInterruptPin; +use sys_util::EventFd; +use resources::SystemAllocator; + +use BusDevice; + +#[derive(Debug)] +pub enum Error { + /// Allocating space for an IO BAR failed. + IoAllocationFailed(u64), + /// Registering an IO BAR failed. + IoRegistrationFailed(u64), +} +pub type Result = std::result::Result; + +pub trait PciDevice: Send + Sync { + /// Assign a legacy PCI IRQ to this device. + fn assign_irq(&mut self, _irq_evt: EventFd, _irq_num: u32, _irq_pin: PciInterruptPin) {} + /// Allocates the needed IO BAR space using the `allocate` function which takes a size and + /// returns an address. Returns a Vec of (address, length) tuples. + fn allocate_io_bars( + &mut self, + _resources: &mut SystemAllocator, + ) -> Result> { + Ok(Vec::new()) + } + /// Gets the configuration registers of the Pci Device. + fn config_registers(&self) -> &PciConfiguration; // TODO - remove these + /// Gets the configuration registers of the Pci Device for modification. + fn config_registers_mut(&mut self) -> &mut PciConfiguration; + /// Reads from a BAR region mapped in to the device. + /// * `addr` - The guest address inside the BAR. + /// * `data` - Filled with the data from `addr`. + fn read_bar(&mut self, addr: u64, data: &mut [u8]); + /// Writes to a BAR region mapped in to the device. + /// * `addr` - The guest address inside the BAR. + /// * `data` - The data to write. + fn write_bar(&mut self, addr: u64, data: &[u8]); +} + +impl BusDevice for T { + fn read(&mut self, offset: u64, data: &mut [u8]) { + self.read_bar(offset, data) + } + + fn write(&mut self, offset: u64, data: &[u8]) { + self.write_bar(offset, data) + } + + fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) { + if offset as usize + data.len() > 4 { + return; + } + + let regs = self.config_registers_mut(); + + match data.len() { + 1 => regs.write_byte(reg_idx * 4 + offset as usize, data[0]), + 2 => regs.write_word( + reg_idx * 4 + offset as usize, + (data[0] as u16) | (data[1] as u16) << 8, + ), + 4 => regs.write_reg(reg_idx, LittleEndian::read_u32(data)), + _ => (), + } + } + + fn config_register_read(&self, reg_idx: usize) -> u32 { + self.config_registers().read_reg(reg_idx) + } +} + +impl PciDevice for Box { + fn assign_irq(&mut self, irq_evt: EventFd, irq_num: u32, irq_pin: PciInterruptPin) { + (**self).assign_irq(irq_evt, irq_num, irq_pin) + } + /// Allocates the needed IO BAR space using the `allocate` function which takes a size and + /// returns an address. Returns a Vec of (address, length) tuples. + fn allocate_io_bars( + &mut self, + resources: &mut SystemAllocator, + ) -> Result> { + (**self).allocate_io_bars(resources) + } + /// Gets the configuration registers of the Pci Device. + fn config_registers(&self) -> &PciConfiguration { + (**self).config_registers() + } + /// Gets the configuration registers of the Pci Device for modification. + fn config_registers_mut(&mut self) -> &mut PciConfiguration { + (**self).config_registers_mut() + } + /// Reads from a BAR region mapped in to the device. + /// * `addr` - The guest address inside the BAR. + /// * `data` - Filled with the data from `addr`. + fn read_bar(&mut self, addr: u64, data: &mut [u8]) { + (**self).read_bar(addr, data) + } + /// Writes to a BAR region mapped in to the device. + /// * `addr` - The guest address inside the BAR. + /// * `data` - The data to write. + fn write_bar(&mut self, addr: u64, data: &[u8]) { + (**self).write_bar(addr, data) + } +}