Fix minor problems in PCI

This commit is contained in:
Ruihan Li 2026-01-14 19:50:01 +08:00 committed by Tate, Hongliang Tian
parent b11fcdcf0b
commit eefdaf4de9
7 changed files with 85 additions and 73 deletions

View File

@ -10,15 +10,17 @@ use ostd::bus::BusProbeError;
use super::{PciCommonDevice, device_info::PciDeviceId}; use super::{PciCommonDevice, device_info::PciDeviceId};
/// PciDevice trait. /// A trait that represents PCI devices.
pub trait PciDevice: Sync + Send + Debug { pub trait PciDevice: Sync + Send + Debug {
/// Gets device id. /// Returns the device ID.
fn device_id(&self) -> PciDeviceId; fn device_id(&self) -> PciDeviceId;
} }
/// PCI device driver, PCI bus will pass the device through the `probe` function when a new device is registered. /// A trait that represents PCI device drivers.
///
/// The PCI bus will pass the device through the `probe` function when a new device is registered.
pub trait PciDriver: Sync + Send + Debug { pub trait PciDriver: Sync + Send + Debug {
/// Probe an unclaimed PCI device. /// Probes an unclaimed PCI device.
/// ///
/// If the driver matches and succeeds in initializing the unclaimed device, /// If the driver matches and succeeds in initializing the unclaimed device,
/// then the driver will return an claimed instance of the device, /// then the driver will return an claimed instance of the device,
@ -33,10 +35,11 @@ pub trait PciDriver: Sync + Send + Debug {
) -> Result<Arc<dyn PciDevice>, (BusProbeError, PciCommonDevice)>; ) -> Result<Arc<dyn PciDevice>, (BusProbeError, PciCommonDevice)>;
} }
/// The PCI bus used to register PCI devices. If a component wishes to drive a PCI device, it needs to provide the following: /// The PCI bus used to register PCI devices.
/// ///
/// 1. The structure that implements the PciDevice trait. /// If a component wishes to drive a PCI device, it needs to provide the following:
/// 2. PCI driver. /// 1. The structure that implements the [`PciDevice`] trait.
/// 2. A [`PciDriver`] instance.
pub struct PciBus { pub struct PciBus {
common_devices: VecDeque<PciCommonDevice>, common_devices: VecDeque<PciCommonDevice>,
devices: Vec<Arc<dyn PciDevice>>, devices: Vec<Arc<dyn PciDevice>>,
@ -46,7 +49,7 @@ pub struct PciBus {
impl PciBus { impl PciBus {
/// Registers a PCI driver to the PCI bus. /// Registers a PCI driver to the PCI bus.
pub fn register_driver(&mut self, driver: Arc<dyn PciDriver>) { pub fn register_driver(&mut self, driver: Arc<dyn PciDriver>) {
debug!("Register driver:{:#x?}", driver); debug!("Register PCI driver: {:#x?}", driver);
let length = self.common_devices.len(); let length = self.common_devices.len();
for _ in (0..length).rev() { for _ in (0..length).rev() {
let common_device = self.common_devices.pop_front().unwrap(); let common_device = self.common_devices.pop_front().unwrap();
@ -71,7 +74,7 @@ impl PciBus {
} }
pub(super) fn register_common_device(&mut self, mut common_device: PciCommonDevice) { pub(super) fn register_common_device(&mut self, mut common_device: PciCommonDevice) {
debug!("Find pci common devices:{:x?}", common_device); debug!("Find PCI common device: {:#x?}", common_device);
let device_id = *common_device.device_id(); let device_id = *common_device.device_id();
for driver in self.drivers.iter() { for driver in self.drivers.iter() {
common_device = match driver.probe(common_device) { common_device = match driver.probe(common_device) {

View File

@ -67,7 +67,7 @@ pub enum CapabilityData {
} }
impl Capability { impl Capability {
/// 0xFC, the top of the capability position. /// The top of the capability position.
const CAPABILITY_TOP: u16 = 0xFC; const CAPABILITY_TOP: u16 = 0xFC;
/// Gets the capability data /// Gets the capability data
@ -77,26 +77,29 @@ impl Capability {
/// Gets the capabilities of one device /// Gets the capabilities of one device
pub(super) fn device_capabilities(dev: &mut PciCommonDevice) -> Vec<Self> { pub(super) fn device_capabilities(dev: &mut PciCommonDevice) -> Vec<Self> {
if !dev.status().contains(Status::CAPABILITIES_LIST) { if !dev.read_status().contains(Status::CAPABILITIES_LIST) {
return Vec::new(); return Vec::new();
} }
// The offset of the first capability pointer is the same for PCI general devices and PCI bridge devices. // The offset of the first capability pointer is the same for PCI general devices and PCI
// bridge devices.
const CAP_OFFSET: u16 = PciGeneralDeviceCfgOffset::CapabilitiesPointer as u16; const CAP_OFFSET: u16 = PciGeneralDeviceCfgOffset::CapabilitiesPointer as u16;
let mut cap_ptr = let mut cap_ptr =
(dev.location().read8(CAP_OFFSET) as u16).align_down(align_of::<u32>() as _); (dev.location().read8(CAP_OFFSET) as u16).align_down(align_of::<u32>() as _);
let mut cap_ptr_vec = Vec::new(); let mut cap_ptr_vec = Vec::new();
let mut capabilities = Vec::new(); let mut capabilities = Vec::new();
// read all cap_ptr so that it is easy for us to get the length. // Read all capability pointers so that it is easy for us to get the length of each
// capability.
while cap_ptr > 0 { while cap_ptr > 0 {
cap_ptr_vec.push(cap_ptr); cap_ptr_vec.push(cap_ptr);
cap_ptr = (dev.location().read8(cap_ptr + 1) as u16).align_down(align_of::<u32>() as _); cap_ptr = (dev.location().read8(cap_ptr + 1) as u16).align_down(align_of::<u32>() as _);
} }
cap_ptr_vec.sort(); cap_ptr_vec.sort();
// Push here so that we can calculate the length of the last capability. // Push the top position so that we can calculate the length of the last capability.
cap_ptr_vec.push(Self::CAPABILITY_TOP); cap_ptr_vec.push(Self::CAPABILITY_TOP);
let length = cap_ptr_vec.len(); let length = cap_ptr_vec.len();
for i in 0..length - 1 { for i in 0..length - 1 {
let cap_ptr = cap_ptr_vec[i]; let cap_ptr = cap_ptr_vec[i];

View File

@ -15,12 +15,11 @@ use crate::{
/// MSI-X capability. It will set the BAR space it uses to be hidden. /// MSI-X capability. It will set the BAR space it uses to be hidden.
#[derive(Debug)] #[derive(Debug)]
#[repr(C)]
pub struct CapabilityMsixData { pub struct CapabilityMsixData {
loc: PciDeviceLocation, loc: PciDeviceLocation,
ptr: u16, ptr: u16,
table_size: u16, table_size: u16,
/// MSIX table entry content: /// MSI-X table entry content:
/// | Vector Control: u32 | Msg Data: u32 | Msg Upper Addr: u32 | Msg Addr: u32 | /// | Vector Control: u32 | Msg Data: u32 | Msg Upper Addr: u32 | Msg Addr: u32 |
table_bar: Arc<MemoryBar>, table_bar: Arc<MemoryBar>,
/// Pending bits table. /// Pending bits table.
@ -85,13 +84,11 @@ impl CapabilityMsixData {
let table_offset = (table_info & !(0b111u32)) as usize; let table_offset = (table_info & !(0b111u32)) as usize;
let table_size = (dev.location().read16(cap_ptr + 2) & 0b11_1111_1111) + 1; 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.
// Set the message address and disable all MSI-X vectors.
let message_address = MSIX_DEFAULT_MSG_ADDR; let message_address = MSIX_DEFAULT_MSG_ADDR;
let message_upper_address = 0u32; let message_upper_address = 0u32;
// Set message address 0xFEE0_0000
for i in 0..table_size { for i in 0..table_size {
// Set message address and disable this msix entry
table_bar table_bar
.io_mem() .io_mem()
.write_once((16 * i) as usize + table_offset, &message_address) .write_once((16 * i) as usize + table_offset, &message_address)
@ -106,11 +103,11 @@ impl CapabilityMsixData {
.unwrap(); .unwrap();
} }
// enable MSI-X, bit15: MSI-X Enable // Enable MSI-X (bit 15: MSI-X Enable).
dev.location() dev.location()
.write16(cap_ptr + 2, dev.location().read16(cap_ptr + 2) | 0x8000); .write16(cap_ptr + 2, dev.location().read16(cap_ptr + 2) | 0x8000);
// disable INTx, enable Bus master. // Disable INTx. Enable bus master.
dev.set_command(dev.command() | Command::INTERRUPT_DISABLE | Command::BUS_MASTER); dev.write_command(dev.read_command() | Command::INTERRUPT_DISABLE | Command::BUS_MASTER);
let mut irqs = Vec::with_capacity(table_size as usize); let mut irqs = Vec::with_capacity(table_size as usize);
for _ in 0..table_size { for _ in 0..table_size {
@ -129,13 +126,15 @@ impl CapabilityMsixData {
} }
} }
/// MSI-X Table size /// Returns the size of the MSI-X Table.
pub fn table_size(&self) -> u16 { pub fn table_size(&self) -> u16 {
// bit 10:0 table size // bit 10:0 table size
(self.loc.read16(self.ptr + 2) & 0b11_1111_1111) + 1 (self.loc.read16(self.ptr + 2) & 0b11_1111_1111) + 1
} }
/// Enables an interrupt line, it will replace the old handle with the new handle. /// Enables an interrupt line.
///
/// If the interrupt line has already been enabled, the old [`IrqLine`] will be replaced.
pub fn set_interrupt_vector(&mut self, irq: IrqLine, index: u16) { pub fn set_interrupt_vector(&mut self, irq: IrqLine, index: u16) {
if index >= self.table_size { if index >= self.table_size {
return; return;
@ -164,19 +163,21 @@ impl CapabilityMsixData {
} }
let _old_irq = self.irqs[index as usize].replace(irq); let _old_irq = self.irqs[index as usize].replace(irq);
// Enable this msix vector // Enable this MSI-X vector.
self.table_bar self.table_bar
.io_mem() .io_mem()
.write_once((16 * index + 12) as usize + self.table_offset, &0_u32) .write_once((16 * index + 12) as usize + self.table_offset, &0_u32)
.unwrap(); .unwrap();
} }
/// Gets mutable IrqLine. User can register callbacks by using this function. /// Returns a mutable reference to the [`IrqLine`].
///
/// Users can register callbacks using the returned [`IrqLine`] reference.
pub fn irq_mut(&mut self, index: usize) -> Option<&mut IrqLine> { pub fn irq_mut(&mut self, index: usize) -> Option<&mut IrqLine> {
self.irqs[index].as_mut() self.irqs[index].as_mut()
} }
/// Returns true if MSI-X Enable bit is set. /// Returns true if the MSI-X Enable bit is set.
pub fn is_enabled(&self) -> bool { pub fn is_enabled(&self) -> bool {
let msg_ctrl = self.loc.read16(self.ptr + 2); let msg_ctrl = self.loc.read16(self.ptr + 2);
msg_ctrl & 0x8000 != 0 msg_ctrl & 0x8000 != 0

View File

@ -24,55 +24,57 @@ impl CapabilityVndrData {
} }
} }
/// The length of this capability /// Returns the length of this capability.
#[expect(clippy::len_without_is_empty)] #[expect(clippy::len_without_is_empty)]
pub fn len(&self) -> u16 { pub fn len(&self) -> u16 {
self.length self.length
} }
/// Reads u8 from the capability. /// Reads a `u8` from the capability.
pub fn read8(&self, offset: u16) -> Result<u8> { pub fn read8(&self, offset: u16) -> Result<u8> {
self.check_range(offset)?; self.check_range(offset, size_of::<u8>() as u16)?;
Ok(self.location.read8(self.cap_ptr + offset)) Ok(self.location.read8(self.cap_ptr + offset))
} }
/// Writes u8 to the capability. /// Writes a `u8` to the capability.
pub fn write8(&self, offset: u16, value: u8) -> Result<()> { pub fn write8(&self, offset: u16, value: u8) -> Result<()> {
self.check_range(offset)?; self.check_range(offset, size_of::<u8>() as u16)?;
self.location.write8(self.cap_ptr + offset, value); self.location.write8(self.cap_ptr + offset, value);
Ok(()) Ok(())
} }
/// Reads u16 from the capability. /// Reads a `u16` from the capability.
pub fn read16(&self, offset: u16) -> Result<u16> { pub fn read16(&self, offset: u16) -> Result<u16> {
self.check_range(offset)?; self.check_range(offset, size_of::<u16>() as u16)?;
Ok(self.location.read16(self.cap_ptr + offset)) Ok(self.location.read16(self.cap_ptr + offset))
} }
/// Writes u16 to the capability. /// Writes a `u16` to the capability.
pub fn write16(&self, offset: u16, value: u16) -> Result<()> { pub fn write16(&self, offset: u16, value: u16) -> Result<()> {
self.check_range(offset)?; self.check_range(offset, size_of::<u16>() as u16)?;
self.location.write16(self.cap_ptr + offset, value); self.location.write16(self.cap_ptr + offset, value);
Ok(()) Ok(())
} }
/// Reads u32 from the capability. /// Reads a `u32` from the capability.
pub fn read32(&self, offset: u16) -> Result<u32> { pub fn read32(&self, offset: u16) -> Result<u32> {
self.check_range(offset)?; self.check_range(offset, size_of::<u32>() as u16)?;
Ok(self.location.read32(self.cap_ptr + offset)) Ok(self.location.read32(self.cap_ptr + offset))
} }
/// Writes u32 to the capability. /// Writes a `u32` to the capability.
pub fn write32(&self, offset: u16, value: u32) -> Result<()> { pub fn write32(&self, offset: u16, value: u32) -> Result<()> {
self.check_range(offset)?; self.check_range(offset, size_of::<u32>() as u16)?;
self.location.write32(self.cap_ptr + offset, value); self.location.write32(self.cap_ptr + offset, value);
Ok(()) Ok(())
} }
fn check_range(&self, offset: u16) -> Result<()> { fn check_range(&self, offset: u16, size: u16) -> Result<()> {
if self.length < offset { if let Some(end) = offset.checked_add(size)
return Err(Error::InvalidArgs); && end <= self.length
{
return Ok(());
} }
Ok(()) Err(Error::InvalidArgs)
} }
} }

View File

@ -283,7 +283,7 @@ impl Bar {
} }
} }
/// Memory BAR /// Memory BAR.
#[derive(Debug)] #[derive(Debug)]
pub struct MemoryBar { pub struct MemoryBar {
base: u64, base: u64,
@ -294,23 +294,23 @@ pub struct MemoryBar {
} }
impl MemoryBar { impl MemoryBar {
/// Memory BAR bits type /// Returns the BAR's address length (32 bits or 64 bits).
pub fn address_length(&self) -> AddrLen { pub fn address_length(&self) -> AddrLen {
self.address_length self.address_length
} }
/// Whether this bar is prefetchable, allowing the CPU to get the data /// Returns whether the BAR is prefetchable, i.e., whether the CPU is allowed to get the data
/// in advance. /// in advance.
pub fn prefetchable(&self) -> bool { pub fn prefetchable(&self) -> bool {
self.prefetchable self.prefetchable
} }
/// Base address /// Returns the BAR's base address.
pub fn base(&self) -> u64 { pub fn base(&self) -> u64 {
self.base self.base
} }
/// Size of the memory /// Returns the BAR's size.
pub fn size(&self) -> u64 { pub fn size(&self) -> u64 {
self.size self.size
} }
@ -422,7 +422,7 @@ impl MemoryBar {
} }
} }
/// Whether this BAR is 64bit address or 32bit address /// The address length of a memory BAR (32 bits or 64 bits).
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AddrLen { pub enum AddrLen {
/// 32 bits /// 32 bits
@ -439,12 +439,12 @@ pub struct IoBar {
} }
impl IoBar { impl IoBar {
/// Base port /// Returns the BAR's base port.
pub fn base(&self) -> u32 { pub fn base(&self) -> u32 {
self.base self.base
} }
/// Size of the port /// Returns the BAR's size.
pub fn size(&self) -> u32 { pub fn size(&self) -> u32 {
self.size self.size
} }

View File

@ -14,7 +14,9 @@ use crate::{
device_info::PciDeviceLocation, device_info::PciDeviceLocation,
}; };
/// PCI common device, Contains a range of information and functions common to PCI devices. /// PCI common device.
///
/// This type contains a range of information and functions common to PCI devices.
#[derive(Debug)] #[derive(Debug)]
pub struct PciCommonDevice { pub struct PciCommonDevice {
device_id: PciDeviceId, device_id: PciDeviceId,
@ -25,55 +27,55 @@ pub struct PciCommonDevice {
} }
impl PciCommonDevice { impl PciCommonDevice {
/// Gets the PCI device ID /// Returns the PCI device ID.
pub fn device_id(&self) -> &PciDeviceId { pub fn device_id(&self) -> &PciDeviceId {
&self.device_id &self.device_id
} }
/// Gets the PCI device location /// Returns the PCI device location.
pub fn location(&self) -> &PciDeviceLocation { pub fn location(&self) -> &PciDeviceLocation {
&self.location &self.location
} }
/// Gets the PCI Base Address Register (BAR) manager /// Returns the PCI Base Address Register (BAR) manager.
pub fn bar_manager(&self) -> &BarManager { pub fn bar_manager(&self) -> &BarManager {
&self.bar_manager &self.bar_manager
} }
/// Gets the PCI capabilities /// Returns the PCI capabilities.
pub fn capabilities(&self) -> &Vec<Capability> { pub fn capabilities(&self) -> &Vec<Capability> {
&self.capabilities &self.capabilities
} }
/// Gets the PCI device type /// Returns the PCI device type.
pub fn device_type(&self) -> PciDeviceType { pub fn device_type(&self) -> PciDeviceType {
self.header_type.device_type() self.header_type.device_type()
} }
/// Checks whether the device is a multi-function device /// Checks whether the device is a multi-function device.
pub fn has_multi_funcs(&self) -> bool { pub fn has_multi_funcs(&self) -> bool {
self.header_type.has_multi_funcs() self.header_type.has_multi_funcs()
} }
/// Gets the PCI Command /// Reads the PCI command.
pub fn command(&self) -> Command { pub fn read_command(&self) -> Command {
Command::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Command as u16)) Command::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Command as u16))
} }
/// Sets the PCI Command /// Writes the PCI command.
pub fn set_command(&self, command: Command) { pub fn write_command(&self, command: Command) {
self.location self.location
.write16(PciCommonCfgOffset::Command as u16, command.bits()) .write16(PciCommonCfgOffset::Command as u16, command.bits())
} }
/// Gets the PCI status /// Reads the PCI status.
pub fn status(&self) -> Status { pub fn read_status(&self) -> Status {
Status::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Status as u16)) Status::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Status as u16))
} }
pub(super) fn new(location: PciDeviceLocation) -> Option<Self> { pub(super) fn new(location: PciDeviceLocation) -> Option<Self> {
if location.read16(0) == 0xFFFF { if location.read16(0) == 0xFFFF {
// not exists // No device.
return None; return None;
} }
@ -101,8 +103,8 @@ impl PciCommonDevice {
capabilities, capabilities,
}; };
device.set_command( device.write_command(
device.command() | Command::MEMORY_SPACE | Command::BUS_MASTER | Command::IO_SPACE, device.read_command() | Command::MEMORY_SPACE | Command::BUS_MASTER | Command::IO_SPACE,
); );
device.bar_manager = BarManager::new(device.header_type.device_type(), location); device.bar_manager = BarManager::new(device.header_type.device_type(), location);
device.capabilities = Capability::device_capabilities(&mut device); device.capabilities = Capability::device_capabilities(&mut device);
@ -138,18 +140,19 @@ impl PciHeaderType {
}) })
} }
/// Gets the device type. /// Returns the device type.
pub fn device_type(self) -> PciDeviceType { pub fn device_type(self) -> PciDeviceType {
self.device_type self.device_type
} }
/// Indicates whether the device has multiple functions. /// Returns whether the device has multiple functions.
pub fn has_multi_funcs(self) -> bool { pub fn has_multi_funcs(self) -> bool {
self.has_multi_funcs self.has_multi_funcs
} }
} }
/// Represents the type of PCI device, determined by the device's header type. /// 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. /// Used to distinguish between general devices, PCI-to-PCI bridges, and PCI-to-Cardbus bridges.
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)] #[repr(u8)]
@ -175,7 +178,7 @@ impl PciDeviceType {
} }
} }
/// Base Address Registers manager. /// Base Address Register (BAR) manager.
#[derive(Debug)] #[derive(Debug)]
pub struct BarManager { pub struct BarManager {
/// There are at most 6 BARs in PCI device. /// There are at most 6 BARs in PCI device.

View File

@ -4,7 +4,7 @@
use crate::cfg_space::PciCommonCfgOffset; use crate::cfg_space::PciCommonCfgOffset;
/// PCI device Location /// PCI device location.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PciDeviceLocation { pub struct PciDeviceLocation {
/// Bus number /// Bus number