diff --git a/kernel/comps/pci/src/common_device.rs b/kernel/comps/pci/src/common_device.rs index 4f218aaa0..3aad94cbf 100644 --- a/kernel/comps/pci/src/common_device.rs +++ b/kernel/comps/pci/src/common_device.rs @@ -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, } 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 { &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 { + 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 { + 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 { &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 } } }