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 <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1072575
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Sonny Rao <sonnyrao@chromium.org>
This commit is contained in:
Dylan Reid 2018-05-24 19:30:53 +00:00 committed by chrome-bot
parent b4e7ea300a
commit cc08cdbd83
4 changed files with 125 additions and 1 deletions

View file

@ -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)]

View file

@ -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;

View file

@ -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)]

View file

@ -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<T> = std::result::Result<T, Error>;
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<Vec<(u64, u64)>> {
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<T: PciDevice> 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<T: PciDevice + ?Sized> PciDevice for Box<T> {
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<Vec<(u64, u64)>> {
(**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)
}
}