2024-01-03 03:22:36 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
2024-06-27 09:47:20 +00:00
|
|
|
//! PCI device common definitions or functions.
|
|
|
|
|
|
2025-01-24 10:06:53 +00:00
|
|
|
#![expect(dead_code)]
|
2024-06-03 18:34:33 +00:00
|
|
|
|
2023-07-23 10:31:43 +00:00
|
|
|
use alloc::vec::Vec;
|
|
|
|
|
|
|
|
|
|
use super::{
|
|
|
|
|
capability::Capability,
|
2025-12-04 12:19:45 +00:00
|
|
|
cfg_space::{AddrLen, Bar, Command, Status},
|
2025-07-25 07:51:39 +00:00
|
|
|
device_info::PciDeviceId,
|
2023-07-23 10:31:43 +00:00
|
|
|
};
|
2025-12-04 12:19:45 +00:00
|
|
|
use crate::{
|
|
|
|
|
cfg_space::{PciBridgeCfgOffset, PciCommonCfgOffset},
|
|
|
|
|
device_info::PciDeviceLocation,
|
|
|
|
|
};
|
2023-07-23 10:31:43 +00:00
|
|
|
|
|
|
|
|
/// PCI common device, Contains a range of information and functions common to PCI devices.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct PciCommonDevice {
|
|
|
|
|
device_id: PciDeviceId,
|
|
|
|
|
location: PciDeviceLocation,
|
2025-12-04 12:19:45 +00:00
|
|
|
header_type: PciHeaderType,
|
2023-07-23 10:31:43 +00:00
|
|
|
bar_manager: BarManager,
|
|
|
|
|
capabilities: Vec<Capability>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PciCommonDevice {
|
2025-12-04 12:19:45 +00:00
|
|
|
/// Gets the PCI device ID
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn device_id(&self) -> &PciDeviceId {
|
|
|
|
|
&self.device_id
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 12:19:45 +00:00
|
|
|
/// Gets the PCI device location
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn location(&self) -> &PciDeviceLocation {
|
|
|
|
|
&self.location
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 12:19:45 +00:00
|
|
|
/// Gets the PCI Base Address Register (BAR) manager
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn bar_manager(&self) -> &BarManager {
|
|
|
|
|
&self.bar_manager
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 12:19:45 +00:00
|
|
|
/// Gets the PCI capabilities
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn capabilities(&self) -> &Vec<Capability> {
|
|
|
|
|
&self.capabilities
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 12:19:45 +00:00
|
|
|
/// Gets the PCI device type
|
|
|
|
|
pub fn device_type(&self) -> PciDeviceType {
|
|
|
|
|
self.header_type.device_type()
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 09:47:20 +00:00
|
|
|
/// Gets the PCI Command
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn command(&self) -> Command {
|
2025-12-04 11:58:09 +00:00
|
|
|
Command::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Command as u16))
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-27 09:47:20 +00:00
|
|
|
/// Sets the PCI Command
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn set_command(&self, command: Command) {
|
|
|
|
|
self.location
|
2025-12-04 11:58:09 +00:00
|
|
|
.write16(PciCommonCfgOffset::Command as u16, command.bits())
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-27 09:47:20 +00:00
|
|
|
/// Gets the PCI status
|
2023-07-23 10:31:43 +00:00
|
|
|
pub fn status(&self) -> Status {
|
2025-12-04 11:58:09 +00:00
|
|
|
Status::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Status as u16))
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2025-07-13 16:57:08 +00:00
|
|
|
let bar_manager = BarManager {
|
|
|
|
|
bars: [const { None }; 6],
|
|
|
|
|
};
|
2025-12-04 12:19:45 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-23 10:31:43 +00:00
|
|
|
let mut device = Self {
|
|
|
|
|
device_id,
|
|
|
|
|
location,
|
2025-12-04 12:19:45 +00:00
|
|
|
header_type,
|
2023-07-23 10:31:43 +00:00
|
|
|
bar_manager,
|
|
|
|
|
capabilities,
|
|
|
|
|
};
|
2025-12-04 12:19:45 +00:00
|
|
|
|
2025-07-13 16:57:08 +00:00
|
|
|
device.set_command(
|
|
|
|
|
device.command() | Command::MEMORY_SPACE | Command::BUS_MASTER | Command::IO_SPACE,
|
|
|
|
|
);
|
2025-12-04 12:19:45 +00:00
|
|
|
device.bar_manager = BarManager::new(device.header_type.device_type(), location);
|
2023-07-23 10:31:43 +00:00
|
|
|
device.capabilities = Capability::device_capabilities(&mut device);
|
2025-12-04 12:19:45 +00:00
|
|
|
|
2023-07-23 10:31:43 +00:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 12:19:45 +00:00
|
|
|
/// 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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 09:47:20 +00:00
|
|
|
/// Base Address Registers manager.
|
2023-07-23 10:31:43 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct BarManager {
|
2025-03-25 11:37:41 +00:00
|
|
|
/// There are at most 6 BARs in PCI device.
|
|
|
|
|
bars: [Option<Bar>; 6],
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BarManager {
|
2025-12-04 12:19:45 +00:00
|
|
|
/// Gains access to the BAR space and returns None if that BAR is absent.
|
2025-03-25 11:37:41 +00:00
|
|
|
pub fn bar(&self, idx: u8) -> &Option<Bar> {
|
|
|
|
|
&self.bars[idx as usize]
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-04 12:19:45 +00:00
|
|
|
/// 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,
|
2023-07-23 10:31:43 +00:00
|
|
|
};
|
|
|
|
|
let mut idx = 0;
|
|
|
|
|
while idx < max {
|
2025-12-04 12:19:45 +00:00
|
|
|
let mut idx_step = 1;
|
|
|
|
|
let Ok(bar) = Bar::new(location, idx) else {
|
2023-09-04 03:04:42 +00:00
|
|
|
idx += idx_step;
|
2025-12-04 12:19:45 +00:00
|
|
|
continue;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Bar::Memory(memory_bar) = &bar
|
|
|
|
|
&& memory_bar.address_length() == AddrLen::Bits64
|
|
|
|
|
{
|
|
|
|
|
// 64-bit BAR occupies two BAR slots.
|
|
|
|
|
idx_step += 1;
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
2025-12-04 12:19:45 +00:00
|
|
|
|
|
|
|
|
bars[idx as usize] = Some(bar);
|
|
|
|
|
idx += idx_step;
|
2023-07-23 10:31:43 +00:00
|
|
|
}
|
2025-12-04 12:19:45 +00:00
|
|
|
|
2023-07-23 10:31:43 +00:00
|
|
|
Self { bars }
|
|
|
|
|
}
|
|
|
|
|
}
|