Implement the PCI bus framework

This commit is contained in:
Yuke Peng
2023-07-23 03:31:43 -07:00
committed by Tate, Hongliang Tian
parent 13c4c614b5
commit a6dcdf6952
10 changed files with 1001 additions and 41 deletions

View File

@ -1 +1,5 @@
pub mod pci;
pub fn init() {
pci::init();
}

View File

@ -0,0 +1,95 @@
use core::fmt::Debug;
use alloc::{collections::VecDeque, sync::Arc, vec::Vec};
use log::{debug, error};
use super::{device_info::PciDeviceId, PciCommonDevice};
pub trait PciDevice: Sync + Send + Debug {
fn device_id(&self) -> PciDeviceId;
}
#[derive(Debug)]
pub enum PciDriverProbeError {
DeviceNotMatch,
ConfigurationSpaceError,
}
/// PCI device driver, PCI bus will pass the device through the `probe` function when a new device is registered.
pub trait PciDriver: Sync + Send + Debug {
/// Probe an unclaimed PCI device.
///
/// If the driver matches and succeeds in initializing the unclaimed device,
/// then the driver will return an claimed instance of the device,
/// signaling that the PCI device is now ready to work.
///
/// Once a device is matched and claimed by a driver,
/// it won't be fed to another driver for probing.
fn probe(
&self,
device: PciCommonDevice,
) -> Result<Arc<dyn PciDevice>, (PciDriverProbeError, PciCommonDevice)>;
}
/// The PCI bus used to register PCI devices. If a component wishes to drive a PCI device, it needs to provide the following:
///
/// 1. The structure that implements the PciDevice trait.
/// 2. PCI driver.
pub struct PciBus {
common_devices: VecDeque<PciCommonDevice>,
devices: Vec<Arc<dyn PciDevice>>,
drivers: Vec<Arc<dyn PciDriver>>,
}
impl PciBus {
pub fn register_driver(&mut self, driver: Arc<dyn PciDriver>) {
debug!("Register driver:{:#x?}", driver);
let length = self.common_devices.len();
for i in (0..length).rev() {
let common_device = self.common_devices.pop_front().unwrap();
let device_id = *common_device.device_id();
let device = match driver.probe(common_device) {
Ok(device) => {
debug_assert!(device_id == device.device_id());
self.devices.push(device);
continue;
}
Err((err, common_device)) => {
error!("PCI device construction failed, reason: {:?}", err);
debug_assert!(device_id == *common_device.device_id());
common_device
}
};
self.common_devices.push_back(device);
}
self.drivers.push(driver);
}
pub(super) fn register_common_device(&mut self, mut common_device: PciCommonDevice) {
debug!("Find pci common devices:{:x?}", common_device);
let device_id = common_device.device_id().clone();
for driver in self.drivers.iter() {
common_device = match driver.probe(common_device) {
Ok(device) => {
debug_assert!(device_id == device.device_id());
self.devices.push(device);
return;
}
Err((err, common_device)) => {
error!("PCI device construction failed, reason: {:?}", err);
debug_assert!(device_id == *common_device.device_id());
common_device
}
};
}
self.common_devices.push_back(common_device);
}
pub(super) const fn new() -> Self {
Self {
common_devices: VecDeque::new(),
devices: Vec::new(),
drivers: Vec::new(),
}
}
}

View File

@ -0,0 +1,135 @@
use alloc::vec::Vec;
use self::{msix::CapabilityMsixData, vendor::CapabilityVndrData};
use super::{
cfg_space::{PciDeviceCommonCfgOffset, Status},
common_device::PciCommonDevice,
PciDeviceLocation,
};
pub mod msix;
pub mod vendor;
#[derive(Debug)]
pub struct Capability {
id: u8,
/// Pointer to the capability.
pos: u16,
/// Next Capability pointer, 0xFC if self is the last one.
next_ptr: u16,
/// The length of this Capability
len: u16,
cap_data: CapabilityData,
}
#[derive(Debug, Clone)]
pub enum CapabilityData {
/// Id:0x01, Power Management
Pm,
/// Id:0x02, Accelerated Graphics Part
Agp,
/// Id:0x03, Vital Product Data
Vpd,
/// Id:0x04, Slot Identification
SlotId,
/// Id:0x05, Message Signalled Interrupts
Msi,
/// Id:0x06, CompactPCI HotSwap
Chswp,
/// Id:0x07, PCI-X
PciX,
/// Id:0x08, HyperTransport
Hp,
/// Id:0x09, Vendor-Specific
Vndr(CapabilityVndrData),
/// Id:0x0A, Debug port
Dbg,
/// Id:0x0B, CompactPCI Central Resource Control
Ccrc,
/// Id:0x0C, PCI Standard Hot-Plug Controller
Shpc,
/// Id:0x0D, Bridge subsystem vendor/device ID
Ssvid,
/// Id:0x0R, AGP Target PCI-PCI bridge
Agp3,
/// Id:0x0F, Secure Device
Secdev,
/// Id:0x10, PCI Express
Exp,
/// Id:0x11, MSI-X
Msix(CapabilityMsixData),
/// Id:0x12, SATA Data/Index Conf
Sata,
/// Id:0x13, PCI Advanced Features
Af,
/// Id:0x14, Enhanced Allocation
Ea,
/// Id:?, Unknown
Unknown(u8),
}
impl Capability {
/// 0xFC, the top of the capability position.
const CAPABILITY_TOP: u16 = 0xFC;
/// get the capabilities of one device
pub fn device_capabilities(dev: &mut PciCommonDevice) -> Vec<Self> {
if !dev.status().contains(Status::CAPABILITIES_LIST) {
return Vec::new();
}
let mut capabilities = Vec::new();
let mut cap_ptr =
dev.location()
.read8(PciDeviceCommonCfgOffset::CapabilitiesPointer as u16) as u16
& PciDeviceLocation::BIT32_ALIGN_MASK;
let mut cap_ptr_vec = Vec::new();
// read all cap_ptr so that it is easy for us to get the length.
while cap_ptr > 0 {
cap_ptr_vec.push(cap_ptr);
cap_ptr = dev.location().read8(cap_ptr + 1) as u16 & PciDeviceLocation::BIT32_ALIGN_MASK;
}
cap_ptr_vec.sort();
// Push here so that we can calculate the length of the last capability.
cap_ptr_vec.push(Self::CAPABILITY_TOP);
let length = cap_ptr_vec.len();
for i in 0..length - 1 {
let cap_ptr = cap_ptr_vec[i];
let next_ptr = cap_ptr_vec[i + 1];
let cap_type = dev.location().read8(cap_ptr);
let data = match cap_type {
0x01 => CapabilityData::Pm,
0x02 => CapabilityData::Agp,
0x03 => CapabilityData::Vpd,
0x04 => CapabilityData::SlotId,
0x05 => CapabilityData::Msi,
0x06 => CapabilityData::Chswp,
0x07 => CapabilityData::PciX,
0x08 => CapabilityData::Hp,
0x09 => {
CapabilityData::Vndr(CapabilityVndrData::new(dev, cap_ptr, next_ptr - cap_ptr))
}
0x0A => CapabilityData::Dbg,
0x0B => CapabilityData::Ccrc,
0x0C => CapabilityData::Shpc,
0x0D => CapabilityData::Ssvid,
0x0E => CapabilityData::Agp3,
0x0F => CapabilityData::Secdev,
0x10 => CapabilityData::Exp,
0x11 => CapabilityData::Msix(CapabilityMsixData::new(dev, cap_ptr)),
0x12 => CapabilityData::Sata,
0x13 => CapabilityData::Af,
0x14 => CapabilityData::Ea,
_ => CapabilityData::Unknown(cap_type),
};
capabilities.push(Self {
id: cap_type,
pos: cap_ptr,
next_ptr,
len: next_ptr - cap_ptr,
cap_data: data,
});
}
capabilities
}
}

View File

@ -0,0 +1,141 @@
use alloc::{sync::Arc, vec::Vec};
use crate::{
bus::pci::{
cfg_space::{Bar, MemoryBar},
common_device::PciCommonDevice,
device_info::PciDeviceLocation,
},
trap::IrqAllocateHandle,
vm::VmIo,
};
/// MSI-X capability. It will set the BAR space it uses to be hidden.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct CapabilityMsixData {
loc: PciDeviceLocation,
ptr: u16,
table_size: u16,
/// MSIX table entry content:
/// | Vector Control: u32 | Msg Data: u32 | Msg Upper Addr: u32 | Msg Addr: u32 |
table_bar: Arc<MemoryBar>,
/// Pending bits table.
pending_table_bar: Arc<MemoryBar>,
irq_allocate_handles: Vec<Option<Arc<IrqAllocateHandle>>>,
}
impl CapabilityMsixData {
pub(super) fn new(dev: &mut PciCommonDevice, cap_ptr: u16) -> Self {
// Get Table and PBA offset, provide functions to modify them
let table_info = dev.location().read32(cap_ptr + 4);
let pba_info = dev.location().read32(cap_ptr + 8);
let table_bar;
let pba_bar;
let bar_manager = dev.bar_manager_mut();
bar_manager.set_invisible((pba_info & 0b111) as u8);
bar_manager.set_invisible((table_info & 0b111) as u8);
match bar_manager
.bar_space_without_invisible((pba_info & 0b111) as u8)
.expect("MSIX cfg:pba BAR is none")
{
Bar::Memory(memory) => {
pba_bar = memory;
}
Bar::Io(_) => {
panic!("MSIX cfg:pba BAR is IO type")
}
};
match bar_manager
.bar_space_without_invisible((table_info & 0b111) as u8)
.expect("MSIX cfg:table BAR is none")
{
Bar::Memory(memory) => {
table_bar = memory;
}
Bar::Io(_) => {
panic!("MSIX cfg:table BAR is IO type")
}
}
let table_size = (dev.location().read16(cap_ptr + 2) & 0b11_1111_1111) + 1;
// TODO: Different architecture seems to have different, so we should set different address here.
let message_address = 0xFEE0_000 as u32;
let message_upper_address = 0 as u32;
// Set message address 0xFEE0_0000
for i in 0..table_size {
// Set message address and disable this msix entry
table_bar
.mmio()
.write_val((16 * i) as usize, &message_address)
.unwrap();
table_bar
.mmio()
.write_val((16 * i + 4) as usize, &message_upper_address)
.unwrap();
table_bar
.mmio()
.write_val((16 * i + 12) as usize, &(1 as u32))
.unwrap();
}
let mut irq_allocate_handles = Vec::with_capacity(table_size as usize);
for i in 0..table_size {
irq_allocate_handles.push(None);
}
Self {
loc: dev.location().clone(),
ptr: cap_ptr,
table_size: (dev.location().read16(cap_ptr + 2) & 0b11_1111_1111) + 1,
table_bar,
pending_table_bar: pba_bar,
irq_allocate_handles,
}
}
pub fn table_size(&self) -> u16 {
// bit 10:0 table size
(self.loc.read16(self.ptr + 2) & 0b11_1111_1111) + 1
}
pub fn set_msix_enable(&self, enable: bool) {
// bit15: msix enable
let value = (enable as u16) << 15;
// message control
self.loc.write16(
self.ptr + 2,
set_bit(self.loc.read16(self.ptr + 2), 15, enable),
)
}
pub fn set_interrupt_enable(&self, enable: bool) {
// bit14: msix enable
let value = (enable as u16) << 14;
// message control
self.loc.write16(
self.ptr + 2,
set_bit(self.loc.read16(self.ptr + 2), 14, enable),
)
}
pub fn set_interrupt_vector(&mut self, vector: Arc<IrqAllocateHandle>, index: u16) {
if index >= self.table_size {
return;
}
let old_handles =
core::mem::replace(&mut self.irq_allocate_handles[index as usize], Some(vector));
// Enable this msix vector
self.table_bar
.mmio()
.write_val((16 * index + 12) as usize, &(0 as u32))
.unwrap();
}
}
fn set_bit(origin_value: u16, offset: usize, set: bool) -> u16 {
(origin_value & (!(1 << offset))) | ((set as u16) << offset)
}

View File

@ -0,0 +1,63 @@
use crate::bus::pci::{common_device::PciCommonDevice, device_info::PciDeviceLocation};
use crate::{Error, Result};
/// Vendor specific capability. Users can access this capability area at will,
/// except for the PCI configuration space which cannot be accessed at will through this structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CapabilityVndrData {
location: PciDeviceLocation,
cap_ptr: u16,
length: u16,
}
impl CapabilityVndrData {
pub(super) fn new(dev: &PciCommonDevice, cap_ptr: u16, length: u16) -> Self {
Self {
location: dev.location().clone(),
cap_ptr,
length,
}
}
pub fn len(&self) -> u16 {
self.length
}
pub fn read8(&self, offset: u16) -> Result<u8> {
self.check_range(offset)?;
Ok(self.location.read8(self.cap_ptr + offset))
}
pub fn write8(&self, offset: u16, value: u8) -> Result<()> {
self.check_range(offset)?;
Ok(self.location.write8(self.cap_ptr + offset, value))
}
pub fn read16(&self, offset: u16) -> Result<u16> {
self.check_range(offset)?;
Ok(self.location.read16(self.cap_ptr + offset))
}
pub fn write16(&self, offset: u16, value: u16) -> Result<()> {
self.check_range(offset)?;
Ok(self.location.write16(self.cap_ptr + offset, value))
}
pub fn read32(&self, offset: u16) -> Result<u32> {
self.check_range(offset)?;
Ok(self.location.read32(self.cap_ptr + offset))
}
pub fn write32(&self, offset: u16, value: u32) -> Result<()> {
self.check_range(offset)?;
Ok(self.location.write32(self.cap_ptr + offset, value))
}
#[inline]
fn check_range(&self, offset: u16) -> Result<()> {
if self.length > offset {
return Err(Error::InvalidArgs);
}
Ok(())
}
}

View File

@ -0,0 +1,201 @@
use alloc::sync::Arc;
use bitflags::bitflags;
use crate::{mmio::Mmio, Error, Result};
use super::PciDeviceLocation;
#[repr(u16)]
pub enum PciDeviceCommonCfgOffset {
VendorId = 0x00,
DeviceId = 0x02,
Command = 0x04,
Status = 0x06,
RevisionId = 0x08,
ClassCode = 0x09,
CacheLineSize = 0x0C,
LatencyTimer = 0x0D,
HeaderType = 0x0E,
Bist = 0x0F,
Bar0 = 0x10,
Bar1 = 0x14,
Bar2 = 0x18,
Bar3 = 0x1C,
Bar4 = 0x20,
Bar5 = 0x24,
CardbusCisPtr = 0x28,
SubsystemVendorId = 0x2C,
SubsystemId = 0x2E,
XromBar = 0x30,
CapabilitiesPointer = 0x34,
InterruptLine = 0x3C,
InterruptPin = 0x3D,
MinGrant = 0x3E,
MaxLatency = 0x3F,
}
bitflags! {
/// PCI device common config space command register.
pub struct Command: u16 {
const IO_SPACE = 1 << 0;
const MEMORY_SPACE = 1 << 1;
const BUS_MASTER = 1 << 2;
const SPECIAL_CYCLES = 1 << 3;
const MWI_ENABLE = 1 << 4;
const VGA_PALETTE_SNOOP = 1 << 5;
const PARITY_ERROR_RESPONSE = 1 << 6;
const STEPPING_CONTROL = 1 << 7;
const SERR_ENABLE = 1 << 8;
const FAST_BACK_TO_BACK_ENABLE = 1 << 9;
const INTERRUPT_DISABLE = 1 << 10;
}
}
bitflags! {
/// PCI device common config space status register.
pub struct Status: u16 {
const INTERRUPT_STATUS = 1 << 3;
const CAPABILITIES_LIST = 1 << 4;
const MHZ66_CAPABLE = 1 << 5;
const FAST_BACK_TO_BACK_CAPABLE = 1 << 7;
const MASTER_DATA_PARITY_ERROR = 1 << 8;
const DEVSEL_MEDIUM_TIMING = 1 << 9;
const DEVSEL_SLOW_TIMING = 1 << 10;
const SIGNALED_TARGET_ABORT = 1 << 11;
const RECEIVED_TARGET_ABORT = 1 << 12;
const RECEIVED_MASTER_ABORT = 1 << 13;
const SIGNALED_SYSTEM_ERROR = 1 << 14;
const DETECTED_PARITY_ERROR = 1 << 15;
}
}
/// BAR space in PCI common config space.
#[derive(Debug, Clone)]
pub enum Bar {
Memory(Arc<MemoryBar>),
Io(Arc<IoBar>),
}
impl Bar {
pub(super) fn new(location: PciDeviceLocation, index: u8) -> Result<Self> {
if index >= 6 {
return Err(Error::InvalidArgs);
}
// Get the original value first, then write all 1 to the register to get the length
let raw = location.read32(index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16);
if raw == 0 {
// no BAR
return Err(Error::InvalidArgs);
}
Ok(if raw & 1 == 0 {
Self::Memory(Arc::new(MemoryBar::new(&location, index)?))
} else {
// IO BAR
Self::Io(Arc::new(IoBar::new(&location, index)?))
})
}
}
#[derive(Debug, Clone)]
pub struct MemoryBar {
base: u64,
size: u32,
/// Whether this bar is prefetchable, allowing the CPU to get the data
/// in advance.
prefetchable: bool,
address_length: AddrLen,
mmio: Mmio,
}
impl MemoryBar {
/// Memory BAR bits type
pub fn address_length(&self) -> AddrLen {
self.address_length
}
pub fn prefetchable(&self) -> bool {
self.prefetchable
}
pub fn base(&self) -> u64 {
self.base
}
pub fn size(&self) -> u32 {
self.size
}
pub fn mmio(&self) -> &Mmio {
&self.mmio
}
/// Create a memory BAR structure.
fn new(location: &PciDeviceLocation, index: u8) -> Result<Self> {
// Get the original value first, then write all 1 to the register to get the length
let offset = index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16;
let raw = location.read32(offset);
location.write32(offset, !0);
let len_encoded = location.read32(offset);
location.write32(offset, raw);
let mut address_length = AddrLen::Bits32;
// base address, it may be bit64 or bit32
let base: u64 = match (raw & 0b110) >> 1 {
// bits32
0 => (raw & !0xF) as u64,
// bits64
2 => {
address_length = AddrLen::Bits64;
((raw & !0xF) as u64) | ((location.read32(offset + 4) as u64) << 32)
}
_ => {
return Err(Error::InvalidArgs);
}
};
// length
let size = !(len_encoded & !0xF).wrapping_add(1);
let prefetchable = if raw & 0b1000 == 0 { false } else { true };
Ok(MemoryBar {
base,
size,
prefetchable,
address_length,
mmio: Mmio::new((base as usize)..((base + size as u64) as usize)).unwrap(),
})
}
}
/// Whether this BAR is 64bit address or 32bit address
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AddrLen {
Bits32,
Bits64,
}
#[derive(Debug, Clone, Copy)]
pub struct IoBar {
base: u32,
size: u32,
}
impl IoBar {
pub fn base(&self) -> u32 {
self.base
}
pub fn size(&self) -> u32 {
self.size
}
fn new(location: &PciDeviceLocation, index: u8) -> Result<Self> {
let offset = index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16;
let raw = location.read32(offset);
location.write32(offset, !0);
let len_encoded = location.read32(offset);
location.write32(offset, raw);
let len = !(len_encoded & !0x3) + 1;
Ok(Self {
base: raw & !0x3,
size: len,
})
}
}

View File

@ -0,0 +1,153 @@
use alloc::vec::Vec;
use super::{
capability::Capability,
cfg_space::{AddrLen, Bar, Command, PciDeviceCommonCfgOffset, Status},
device_info::{PciDeviceId, PciDeviceLocation},
};
/// PCI common device, Contains a range of information and functions common to PCI devices.
#[derive(Debug)]
pub struct PciCommonDevice {
device_id: PciDeviceId,
location: PciDeviceLocation,
bar_manager: BarManager,
capabilities: Vec<Capability>,
}
impl PciCommonDevice {
pub fn device_id(&self) -> &PciDeviceId {
&self.device_id
}
pub fn location(&self) -> &PciDeviceLocation {
&self.location
}
pub fn bar_manager(&self) -> &BarManager {
&self.bar_manager
}
pub fn capabilities(&self) -> &Vec<Capability> {
&self.capabilities
}
pub fn command(&self) -> Command {
Command::from_bits_truncate(
self.location
.read16(PciDeviceCommonCfgOffset::Command as u16),
)
}
pub fn set_command(&self, command: Command) {
self.location
.write16(PciDeviceCommonCfgOffset::Command as u16, command.bits())
}
pub fn status(&self) -> Status {
Status::from_bits_truncate(
self.location
.read16(PciDeviceCommonCfgOffset::Status as u16),
)
}
pub(super) fn new(location: PciDeviceLocation) -> Option<Self> {
if location.read16(0) == 0xFFFF {
// not exists
return None;
}
let capabilities = Vec::new();
let device_id = PciDeviceId::new(location);
let bar_manager = BarManager::new(location);
let mut device = Self {
device_id,
location,
bar_manager,
capabilities,
};
device.capabilities = Capability::device_capabilities(&mut device);
Some(device)
}
pub(super) fn bar_manager_mut(&mut self) -> &mut BarManager {
&mut self.bar_manager
}
pub(super) fn capabilities_mut(&mut self) -> &mut Vec<Capability> {
&mut self.capabilities
}
}
#[derive(Debug)]
pub struct BarManager {
/// BARs, the bool indicate whether this bar should exposed to unprivileged part.
bars: [Option<(Bar, bool)>; 6],
}
impl BarManager {
/// Gain access to the BAR space and return None if that BAR is set to be invisible or absent.
pub fn bar(&self, idx: u8) -> Option<Bar> {
let Some((bar,visible)) = self.bars[idx as usize].clone() else{
return None;
};
if visible {
Some(bar)
} else {
None
}
}
/// Parse the BAR space by PCI device location.
fn new(location: PciDeviceLocation) -> Self {
let header_type = location.read8(PciDeviceCommonCfgOffset::HeaderType as u16) & !(1 << 7);
// Get the max bar amount, header type=0 => end device; header type=1 => PCI bridge.
let max = match header_type {
0 => 6,
1 => 2,
_ => 0,
};
let mut idx = 0;
let mut bars = [None, None, None, None, None, None];
while idx < max {
match Bar::new(location, idx) {
Ok(bar) => {
match &bar {
Bar::Memory(memory_bar) => {
if memory_bar.address_length() == AddrLen::Bits64 {
idx += 1;
}
}
Bar::Io(_) => {}
}
bars[idx as usize] = Some((bar, false));
}
// ignore for now
Err(_) => {}
}
idx += 1;
}
Self { bars }
}
pub(super) fn set_invisible(&mut self, idx: u8) {
if self.bars[idx as usize].is_some() {
let Some((bar,_)) = self.bars[idx as usize].clone() else{
return;
};
self.bars[idx as usize] = Some((bar, false));
}
let Some((_,visible)) = self.bars[idx as usize] else{
return;
};
}
/// Gain access to the BAR space and return None if that BAR is absent.
pub(super) fn bar_space_without_invisible(&self, idx: u8) -> Option<Bar> {
if let Some((bar, _)) = self.bars[idx as usize].clone() {
Some(bar)
} else {
None
}
}
}

View File

@ -0,0 +1,141 @@
use core::iter;
use crate::arch::device::pci::{PCI_ADDRESS_PORT, PCI_DATA_PORT};
use super::cfg_space::PciDeviceCommonCfgOffset;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PciDeviceId {
pub vendor_id: u16,
pub device_id: u16,
pub revision_id: u8,
pub prog_if: u8,
pub subclass: u8,
pub class: u8,
pub subsystem_vendor_id: u16,
pub subsystem_id: u16,
}
impl PciDeviceId {
pub(super) fn new(location: PciDeviceLocation) -> Self {
let vendor_id = location.read16(PciDeviceCommonCfgOffset::VendorId as u16);
let device_id = location.read16(PciDeviceCommonCfgOffset::DeviceId as u16);
let revision_id = location.read8(PciDeviceCommonCfgOffset::RevisionId as u16);
let prog_if = location.read8(PciDeviceCommonCfgOffset::ClassCode as u16);
let subclass = location.read8(PciDeviceCommonCfgOffset::ClassCode as u16 + 1);
let class = location.read8(PciDeviceCommonCfgOffset::ClassCode as u16 + 1);
let subsystem_vendor_id =
location.read16(PciDeviceCommonCfgOffset::SubsystemVendorId as u16);
let subsystem_id = location.read16(PciDeviceCommonCfgOffset::SubsystemId as u16);
Self {
vendor_id,
device_id,
revision_id,
prog_if,
subclass,
class,
subsystem_vendor_id,
subsystem_id,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PciDeviceLocation {
pub bus: u8,
/// Max 31
pub device: u8,
/// Max 7
pub function: u8,
}
impl PciDeviceLocation {
pub const MIN_BUS: u8 = 0;
pub const MAX_BUS: u8 = 255;
pub const MIN_DEVICE: u8 = 0;
pub const MAX_DEVICE: u8 = 31;
pub const MIN_FUNCTION: u8 = 0;
pub const MAX_FUNCTION: u8 = 7;
/// By encoding bus, device, and function into u32, user can access a PCI device in x86 by passing in this value.
#[inline(always)]
pub fn encode_as_x86_address_value(self) -> u32 {
// 1 << 31: Configuration enable
(1 << 31)
| ((self.bus as u32) << 16)
| (((self.device as u32) & 0b11111) << 11)
| (((self.function as u32) & 0b111) << 8)
}
/// Returns an iterator that enumerates all possible PCI device locations.
pub fn all() -> impl Iterator<Item = PciDeviceLocation> {
iter::from_generator(|| {
for bus in Self::MIN_BUS..=Self::MAX_BUS {
for device in Self::MIN_DEVICE..=Self::MAX_DEVICE {
for function in Self::MIN_FUNCTION..=Self::MAX_FUNCTION {
let loc = PciDeviceLocation {
bus,
device,
function,
};
yield loc;
}
}
}
})
}
}
impl PciDeviceLocation {
pub(super) const BIT32_ALIGN_MASK: u16 = 0xFFFC;
pub(super) fn read8(&self, offset: u16) -> u8 {
let val = self.read32(offset & Self::BIT32_ALIGN_MASK);
((val >> ((offset as usize & 0b11) << 3)) & 0xFF) as u8
}
pub(super) fn read16(&self, offset: u16) -> u16 {
let val = self.read32(offset & Self::BIT32_ALIGN_MASK);
((val >> ((offset as usize & 0b10) << 3)) & 0xFFFF) as u16
}
pub(super) fn read32(&self, offset: u16) -> u32 {
debug_assert!(
(offset & 0b11) == 0,
"misaligned PCI configuration dword u32 read"
);
PCI_ADDRESS_PORT
.write(self.encode_as_x86_address_value() | (offset & Self::BIT32_ALIGN_MASK) as u32);
PCI_DATA_PORT.read().to_le()
}
pub(super) fn write8(&self, offset: u16, val: u8) {
let old = self.read32(offset & Self::BIT32_ALIGN_MASK);
let dest = offset as usize & 0b11 << 3;
let mask = (0xFF << dest) as u32;
self.write32(
offset & Self::BIT32_ALIGN_MASK,
((val as u32) << dest | (old & !mask)).to_le(),
);
}
pub(super) fn write16(&self, offset: u16, val: u16) {
let old = self.read32(offset & Self::BIT32_ALIGN_MASK);
let dest = offset as usize & 0b10 << 3;
let mask = (0xFFFF << dest) as u32;
self.write32(
offset & Self::BIT32_ALIGN_MASK,
((val as u32) << dest | (old & !mask)).to_le(),
);
}
pub(super) fn write32(&self, offset: u16, val: u32) {
debug_assert!(
(offset & 0b11) == 0,
"misaligned PCI configuration dword u32 write"
);
PCI_ADDRESS_PORT
.write(self.encode_as_x86_address_value() | (offset & Self::BIT32_ALIGN_MASK) as u32);
PCI_DATA_PORT.write(val.to_le())
}
}

View File

@ -1,46 +1,72 @@
use core::iter;
//! PCI bus
//!
//! Users can implement the bus under the `PciDriver` to the PCI bus to register devices,
//! when the physical device and the driver match successfully, it will be provided through the driver `construct` function
//! to construct a structure that implements the `PciDevice` trait. And in the end,
//! PCI bus will store a reference to the structure and finally call the driver's probe function to remind the driver of a new device access.
//!
//! Use case:
//!
//! ```rust norun
//! #[derive(Debug)]
//! pub struct PciDeviceA {
//! common_device: PciCommonDevice,
//! }
//!
//! impl PciDevice for PciDeviceA {
//! fn device_id(&self) -> PciDeviceId {
//! self.common_device.device_id().clone()
//! }
//! }
//!
//! #[derive(Debug)]
//! pub struct PciDriverA {
//! devices: Mutex<Vec<Arc<PciDeviceA>>>,
//! }
//!
//! impl PciDriver for PciDriverA {
//! fn probe(
//! &self,
//! device: PciCommonDevice,
//! ) -> Result<Arc<dyn PciDevice>, (PciDriverProbeError, PciCommonDevice)> {
//! if device.device_id().vendor_id != 0x1234 {
//! return Err((PciDriverProbeError::DeviceNotMatch, device));
//! }
//! let device = Arc::new(PciDeviceA {
//! common_device: device,
//! });
//! self.devices.lock().push(device.clone());
//! Ok(device)
//! }
//! }
//!
//! pub fn driver_a_init() {
//! let driver_a = Arc::new(PciDriverA {
//! devices: Mutex::new(Vec::new()),
//! });
//! PCI_BUS.lock().register_driver(driver_a);
//! }
//! ```
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PciDeviceLocation {
pub bus: u8,
/// Max 31
pub device: u8,
/// Max 7
pub function: u8,
}
pub mod bus;
mod capability;
mod cfg_space;
mod common_device;
mod device_info;
impl PciDeviceLocation {
pub const MIN_BUS: u8 = 0;
pub const MAX_BUS: u8 = 255;
pub const MIN_DEVICE: u8 = 0;
pub const MAX_DEVICE: u8 = 31;
pub const MIN_FUNCTION: u8 = 0;
pub const MAX_FUNCTION: u8 = 7;
/// By encoding bus, device, and function into u32, user can access a PCI device in x86 by passing in this value.
#[inline(always)]
pub fn encode_as_x86_address_value(self) -> u32 {
// 1 << 31: Configuration enable
(1 << 31)
| ((self.bus as u32) << 16)
| (((self.device as u32) & 0b11111) << 11)
| (((self.function as u32) & 0b111) << 8)
}
pub use device_info::{PciDeviceId, PciDeviceLocation};
use spin::Mutex;
/// Returns an iterator that enumerates all possible PCI device locations.
pub fn all() -> impl Iterator<Item = PciDeviceLocation> {
iter::from_generator(|| {
for bus in Self::MIN_BUS..=Self::MAX_BUS {
for device in Self::MIN_DEVICE..=Self::MAX_DEVICE {
for function in Self::MIN_FUNCTION..=Self::MAX_FUNCTION {
let loc = PciDeviceLocation {
bus,
device,
function,
};
yield loc;
}
}
}
})
use self::{bus::PciBus, common_device::PciCommonDevice};
pub static PCI_BUS: Mutex<PciBus> = Mutex::new(PciBus::new());
pub(crate) fn init() {
let mut lock = PCI_BUS.lock();
for location in PciDeviceLocation::all() {
let Some(device) = PciCommonDevice::new(location)else{
continue;
};
lock.register_common_device(device);
}
}

View File

@ -53,6 +53,7 @@ pub fn init() {
trap::init();
arch::after_all_init();
mmio::init();
bus::init();
register_irq_common_callback();
invoke_c_init_funcs();
}