pci: Add `PciHeaderType` & `PciDeviceType` and refactor `BarManager`

This commit is contained in:
Yuke Peng 2025-12-04 20:19:45 +08:00 committed by Tate, Hongliang Tian
parent 7634a27ba6
commit c825c0d2c8
1 changed files with 118 additions and 29 deletions

View File

@ -8,41 +8,50 @@ use alloc::vec::Vec;
use super::{
capability::Capability,
cfg_space::{AddrLen, Bar, Command, PciCommonCfgOffset, Status},
cfg_space::{AddrLen, Bar, Command, Status},
device_info::PciDeviceId,
};
use crate::device_info::PciDeviceLocation;
use crate::{
cfg_space::{PciBridgeCfgOffset, PciCommonCfgOffset},
device_info::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,
header_type: PciHeaderType,
bar_manager: BarManager,
capabilities: Vec<Capability>,
}
impl PciCommonDevice {
/// PCI device ID
/// Gets the PCI device ID
pub fn device_id(&self) -> &PciDeviceId {
&self.device_id
}
/// PCI device location
/// Gets the PCI device location
pub fn location(&self) -> &PciDeviceLocation {
&self.location
}
/// PCI Base Address Register (BAR) manager
/// Gets the PCI Base Address Register (BAR) manager
pub fn bar_manager(&self) -> &BarManager {
&self.bar_manager
}
/// PCI capabilities
/// Gets the PCI capabilities
pub fn capabilities(&self) -> &Vec<Capability> {
&self.capabilities
}
/// Gets the PCI device type
pub fn device_type(&self) -> PciDeviceType {
self.header_type.device_type()
}
/// Gets the PCI Command
pub fn command(&self) -> Command {
Command::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Command as u16))
@ -70,17 +79,31 @@ impl PciCommonDevice {
let bar_manager = BarManager {
bars: [const { None }; 6],
};
let mut header_type =
PciHeaderType::try_from_raw(location.read8(PciCommonCfgOffset::HeaderType as u16))?;
if let PciDeviceType::PciToPciBridge(primary_bus, secondary_bus, subordinate_bus) =
&mut header_type.device_type
{
*primary_bus = location.read8(PciBridgeCfgOffset::PrimaryBusNumber as u16);
*secondary_bus = location.read8(PciBridgeCfgOffset::SecondaryBusNumber as u16);
*subordinate_bus = location.read8(PciBridgeCfgOffset::SubordinateBusNumber as u16);
}
let mut device = Self {
device_id,
location,
header_type,
bar_manager,
capabilities,
};
device.set_command(
device.command() | Command::MEMORY_SPACE | Command::BUS_MASTER | Command::IO_SPACE,
);
device.bar_manager = BarManager::new(location);
device.bar_manager = BarManager::new(device.header_type.device_type(), location);
device.capabilities = Capability::device_capabilities(&mut device);
Some(device)
}
@ -93,6 +116,70 @@ impl PciCommonDevice {
}
}
/// The header type field of a PCI device struct in the PCI configuration space.
///
/// A header type is comprised of two pieces of information:
/// 1. The device type ([`PciDeviceType`]);
/// 2. Whether the device has multiple functions (`bool`).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct PciHeaderType {
device_type: PciDeviceType,
has_multi_funcs: bool,
}
impl PciHeaderType {
/// Converts a byte into a header type.
///
/// According to the PCI specification, the encoding of a header type is as follows:
/// - Bit 0-6 encodes the raw value of `PciDeviceType`;
/// - Bit 7 indicates whether the PCI device has multiple functions.
pub fn try_from_raw(raw: u8) -> Option<Self> {
let device_type = PciDeviceType::try_from_raw(raw & 0x7F)?;
let has_multi_funcs = (raw & 0x80) != 0;
Some(Self {
device_type,
has_multi_funcs,
})
}
/// Gets the device type.
pub fn device_type(self) -> PciDeviceType {
self.device_type
}
/// Indicates whether the device has multiple functions.
pub fn has_multi_funcs(self) -> bool {
self.has_multi_funcs
}
}
/// Represents the type of PCI device, determined by the device's header type.
/// Used to distinguish between general devices, PCI-to-PCI bridges, and PCI-to-Cardbus bridges.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum PciDeviceType {
/// General PCI device (header type 0x00).
GeneralDevice,
/// PCI-to-PCI bridge (header type 0x01).
/// Contains the primary, secondary, and subordinate bus numbers.
PciToPciBridge(u8, u8, u8),
/// PCI-to-Cardbus bridge (header type 0x02).
PciToCardbusBridge,
}
impl PciDeviceType {
/// Converts a raw header type value into a `PciDeviceType`.
pub fn try_from_raw(raw: u8) -> Option<Self> {
match raw {
0x00 => Some(PciDeviceType::GeneralDevice),
0x01 => Some(PciDeviceType::PciToPciBridge(0, 0, 0)),
0x02 => Some(PciDeviceType::PciToCardbusBridge),
_ => None,
}
}
}
/// Base Address Registers manager.
#[derive(Debug)]
pub struct BarManager {
@ -101,38 +188,40 @@ pub struct BarManager {
}
impl BarManager {
/// Gain access to the BAR space and return None if that BAR is absent.
/// Gains access to the BAR space and returns None if that BAR is absent.
pub fn bar(&self, idx: u8) -> &Option<Bar> {
&self.bars[idx as usize]
}
/// Parse the BAR space by PCI device location.
fn new(location: PciDeviceLocation) -> Self {
let header_type = location.read8(PciCommonCfgOffset::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,
/// Parses the BAR space by PCI device location.
fn new(device_type: PciDeviceType, location: PciDeviceLocation) -> Self {
let mut bars = [None, None, None, None, None, None];
// Determine the maximum number of BARs based on the device type.
let max = match device_type {
PciDeviceType::GeneralDevice => 6,
PciDeviceType::PciToPciBridge(_, _, _) => 2,
PciDeviceType::PciToCardbusBridge => 0,
};
let mut idx = 0;
let mut bars = [None, None, None, None, None, None];
while idx < max {
if let Ok(bar) = Bar::new(location, idx) {
let mut idx_step = 0;
match &bar {
Bar::Memory(memory_bar) => {
if memory_bar.address_length() == AddrLen::Bits64 {
idx_step = 1;
}
}
Bar::Io(_) => {}
}
bars[idx as usize] = Some(bar);
let mut idx_step = 1;
let Ok(bar) = Bar::new(location, idx) else {
idx += idx_step;
continue;
};
if let Bar::Memory(memory_bar) = &bar
&& memory_bar.address_length() == AddrLen::Bits64
{
// 64-bit BAR occupies two BAR slots.
idx_step += 1;
}
idx += 1;
bars[idx as usize] = Some(bar);
idx += idx_step;
}
Self { bars }
}
}