2023-09-05 16:13:15 +03:00
|
|
|
//! PCI/PCIe bus interfaces
|
2023-12-10 21:25:54 +02:00
|
|
|
#![no_std]
|
|
|
|
|
|
|
|
extern crate alloc;
|
|
|
|
|
2023-09-04 11:27:16 +03:00
|
|
|
use core::fmt;
|
|
|
|
|
2023-12-10 21:25:54 +02:00
|
|
|
use acpi::mcfg::McfgEntry;
|
|
|
|
use alloc::{rc::Rc, vec::Vec};
|
2023-09-05 16:13:15 +03:00
|
|
|
use bitflags::bitflags;
|
2023-09-04 11:27:16 +03:00
|
|
|
use device_api::Device;
|
2023-12-10 18:52:33 +02:00
|
|
|
use kernel_util::{
|
|
|
|
mem::address::{FromRaw, PhysicalAddress},
|
|
|
|
sync::IrqSafeSpinlock,
|
|
|
|
};
|
2023-09-04 11:27:16 +03:00
|
|
|
use yggdrasil_abi::error::Error;
|
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
pub mod capability;
|
2023-09-04 11:27:16 +03:00
|
|
|
mod space;
|
|
|
|
|
|
|
|
pub use space::{
|
|
|
|
ecam::PciEcam, PciConfigSpace, PciConfigurationSpace, PciLegacyConfigurationSpace,
|
|
|
|
};
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
bitflags! {
|
|
|
|
/// Command register of the PCI configuration space
|
|
|
|
pub struct PciCommandRegister: u16 {
|
|
|
|
/// If set, I/O access to the device is enabled
|
|
|
|
const ENABLE_IO = 1 << 0;
|
|
|
|
/// If set, memory-mapped access to the device is enabled
|
|
|
|
const ENABLE_MEMORY = 1 << 1;
|
|
|
|
/// If set, the device can generate PCI bus accesses on its own
|
|
|
|
const BUS_MASTER = 1 << 2;
|
|
|
|
/// If set, interrupts are masked from being raised
|
|
|
|
const DISABLE_INTERRUPTS = 1 << 10;
|
|
|
|
}
|
|
|
|
}
|
2023-09-04 11:27:16 +03:00
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
bitflags! {
|
|
|
|
/// Status register of the PCI configuration space
|
|
|
|
pub struct PciStatusRegister: u16 {
|
|
|
|
/// Read-only. If set, the configuration space has a pointer to the capabilities list.
|
|
|
|
const CAPABILITIES_LIST = 1 << 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Represents the address of a single object on a bus (or the bus itself)
|
2023-09-04 11:27:16 +03:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
|
|
pub struct PciAddress {
|
2023-09-05 16:13:15 +03:00
|
|
|
/// PCIe segment group, ignored (?) with PCI
|
2023-09-04 11:27:16 +03:00
|
|
|
pub segment: u8,
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Bus number
|
2023-09-04 11:27:16 +03:00
|
|
|
pub bus: u8,
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Slot/device number
|
2023-09-04 11:27:16 +03:00
|
|
|
pub device: u8,
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Function number
|
2023-09-04 11:27:16 +03:00
|
|
|
pub function: u8,
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Address provided by PCI configuration space Base Address Register
|
2023-09-04 11:27:16 +03:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum PciBaseAddress {
|
2023-09-05 16:13:15 +03:00
|
|
|
/// 32/64-bit memory address
|
2023-09-04 11:27:16 +03:00
|
|
|
Memory(usize),
|
2023-09-05 16:13:15 +03:00
|
|
|
/// I/O space address
|
2023-09-04 11:27:16 +03:00
|
|
|
Io(u16),
|
|
|
|
}
|
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
/// Unique ID assigned to PCI capability structures
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
#[repr(u8)]
|
|
|
|
pub enum PciCapabilityId {
|
|
|
|
/// MSI (32-bit or 64-bit)
|
|
|
|
Msi = 0x05,
|
|
|
|
/// MSI-X
|
|
|
|
MsiX = 0x11,
|
|
|
|
/// Unknown capability missing from this list
|
|
|
|
Unknown,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Interface used for querying PCI capabilities
|
|
|
|
pub trait PciCapability {
|
|
|
|
/// Capability ID
|
|
|
|
const ID: PciCapabilityId;
|
|
|
|
/// Wrapper for accessing the capability data structure
|
|
|
|
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a>;
|
|
|
|
|
|
|
|
/// Constructs an access wrapper for this capability with given offset
|
|
|
|
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
|
|
|
space: &'s S,
|
|
|
|
offset: usize,
|
|
|
|
) -> Self::CapabilityData<'s, S>;
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Describes a PCI device
|
2023-09-04 11:27:16 +03:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PciDeviceInfo {
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Address of the device
|
2023-09-04 11:27:16 +03:00
|
|
|
pub address: PciAddress,
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Configuration space access method
|
2023-09-04 11:27:16 +03:00
|
|
|
pub config_space: PciConfigSpace,
|
|
|
|
}
|
|
|
|
|
2023-12-10 21:25:54 +02:00
|
|
|
pub enum PciMatch {
|
|
|
|
Generic(fn(&PciDeviceInfo) -> bool),
|
|
|
|
Class(u8, Option<u8>, Option<u8>),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PciDriver {
|
|
|
|
name: &'static str,
|
|
|
|
check: PciMatch,
|
|
|
|
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
|
2023-09-04 11:27:16 +03:00
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Used to store PCI bus devices which were enumerated by the kernel
|
2023-09-04 11:27:16 +03:00
|
|
|
pub struct PciBusDevice {
|
|
|
|
info: PciDeviceInfo,
|
|
|
|
driver: Option<Rc<dyn Device>>,
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Represents a single PCIe bus segment
|
2023-09-04 11:27:16 +03:00
|
|
|
pub struct PciBusSegment {
|
|
|
|
segment_number: u8,
|
|
|
|
bus_number_start: u8,
|
|
|
|
bus_number_end: u8,
|
2023-09-15 00:02:14 +03:00
|
|
|
ecam_phys_base: Option<PhysicalAddress>,
|
2023-09-04 11:27:16 +03:00
|
|
|
|
|
|
|
devices: Vec<PciBusDevice>,
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Manager struct to store and control all PCI devices in the system
|
2023-09-04 11:27:16 +03:00
|
|
|
pub struct PciBusManager {
|
|
|
|
segments: Vec<PciBusSegment>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PciBusSegment {
|
|
|
|
fn probe_config_space(&self, address: PciAddress) -> Result<Option<PciConfigSpace>, Error> {
|
|
|
|
match self.ecam_phys_base {
|
|
|
|
Some(ecam_phys_base) => Ok(unsafe {
|
|
|
|
PciEcam::probe_raw_parts(ecam_phys_base, self.bus_number_start, address)?
|
|
|
|
}
|
|
|
|
.map(PciConfigSpace::Ecam)),
|
|
|
|
None => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-07 10:16:44 +02:00
|
|
|
fn enumerate_function(&mut self, address: PciAddress) -> Result<(), Error> {
|
2023-09-04 11:27:16 +03:00
|
|
|
let Some(config) = self.probe_config_space(address)? else {
|
|
|
|
return Ok(());
|
|
|
|
};
|
|
|
|
|
|
|
|
let header_type = config.header_type();
|
|
|
|
|
|
|
|
// Enumerate multi-function devices
|
|
|
|
if address.function == 0 && header_type & 0x80 != 0 {
|
|
|
|
for function in 1..8 {
|
2023-12-07 10:16:44 +02:00
|
|
|
self.enumerate_function(address.with_function(function))?;
|
2023-09-04 11:27:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PCI-to-PCI bridge
|
|
|
|
// if config.class_code() == 0x06 && config.subclass() == 0x04 {
|
|
|
|
// let secondary_bus = config.secondary_bus();
|
|
|
|
// // TODO
|
|
|
|
// }
|
|
|
|
|
|
|
|
let info = PciDeviceInfo {
|
|
|
|
address,
|
|
|
|
config_space: config,
|
|
|
|
};
|
|
|
|
self.devices.push(PciBusDevice { info, driver: None });
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-12-07 10:16:44 +02:00
|
|
|
fn enumerate_bus(&mut self, bus: u8) -> Result<(), Error> {
|
2023-09-04 11:27:16 +03:00
|
|
|
let address = PciAddress::for_bus(self.segment_number, bus);
|
|
|
|
|
|
|
|
for i in 0..32 {
|
|
|
|
let device_address = address.with_device(i);
|
|
|
|
|
2023-12-07 10:16:44 +02:00
|
|
|
self.enumerate_function(device_address)?;
|
2023-09-04 11:27:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Enumerates the bus segment, placing found devices into the manager
|
2023-12-07 10:16:44 +02:00
|
|
|
pub fn enumerate(&mut self) -> Result<(), Error> {
|
2023-09-04 11:27:16 +03:00
|
|
|
for bus in self.bus_number_start..self.bus_number_end {
|
2023-12-07 10:16:44 +02:00
|
|
|
self.enumerate_bus(bus)?;
|
2023-09-04 11:27:16 +03:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PciBusManager {
|
2023-09-05 16:13:15 +03:00
|
|
|
const fn new() -> Self {
|
2023-09-04 11:27:16 +03:00
|
|
|
Self {
|
|
|
|
segments: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
|
|
|
|
/// drivers
|
2023-09-04 11:27:16 +03:00
|
|
|
pub fn setup_bus_devices() -> Result<(), Error> {
|
|
|
|
Self::walk_bus_devices(|device| {
|
|
|
|
setup_bus_device(device)?;
|
|
|
|
Ok(true)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Iterates over the bus devices, calling the function on each of them until either an error
|
|
|
|
/// or `Ok(false)` is returned
|
2023-09-04 11:27:16 +03:00
|
|
|
pub fn walk_bus_devices<F: FnMut(&mut PciBusDevice) -> Result<bool, Error>>(
|
|
|
|
mut f: F,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut this = PCI_MANAGER.lock();
|
|
|
|
|
|
|
|
for segment in this.segments.iter_mut() {
|
|
|
|
for device in segment.devices.iter_mut() {
|
|
|
|
if !f(device)? {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Enumerates a bus segment provided by ACPI MCFG table entry
|
2023-09-04 11:27:16 +03:00
|
|
|
pub fn add_segment_from_mcfg(entry: &McfgEntry) -> Result<(), Error> {
|
|
|
|
let mut bus_segment = PciBusSegment {
|
|
|
|
segment_number: entry.pci_segment_group as u8,
|
|
|
|
bus_number_start: entry.bus_number_start,
|
|
|
|
bus_number_end: entry.bus_number_end,
|
2023-09-15 00:02:14 +03:00
|
|
|
ecam_phys_base: Some(PhysicalAddress::from_raw(entry.base_address)),
|
2023-09-04 11:27:16 +03:00
|
|
|
|
|
|
|
devices: Vec::new(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut this = PCI_MANAGER.lock();
|
2023-12-07 10:16:44 +02:00
|
|
|
bus_segment.enumerate()?;
|
2023-09-04 11:27:16 +03:00
|
|
|
this.segments.push(bus_segment);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for PciAddress {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "{}:{}:{}", self.bus, self.device, self.function)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PciAddress {
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Constructs a [PciAddress] representing a bus
|
2023-09-04 11:27:16 +03:00
|
|
|
pub const fn for_bus(segment: u8, bus: u8) -> Self {
|
|
|
|
Self {
|
|
|
|
segment,
|
|
|
|
bus,
|
|
|
|
device: 0,
|
|
|
|
function: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Constructs a [PciAddress] representing a device on a given bus
|
2023-09-04 11:27:16 +03:00
|
|
|
pub const fn with_device(self, device: u8) -> Self {
|
|
|
|
Self {
|
|
|
|
device,
|
|
|
|
function: 0,
|
|
|
|
..self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:13:15 +03:00
|
|
|
/// Constructs a [PciAddress] representing a function of a given bus device
|
2023-09-04 11:27:16 +03:00
|
|
|
pub const fn with_function(self, function: u8) -> Self {
|
|
|
|
Self { function, ..self }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PciConfigurationSpace for PciConfigSpace {
|
|
|
|
fn read_u32(&self, offset: usize) -> u32 {
|
|
|
|
match self {
|
|
|
|
Self::Ecam(ecam) => ecam.read_u32(offset),
|
|
|
|
_ => todo!(),
|
|
|
|
}
|
|
|
|
}
|
2023-12-08 14:30:49 +02:00
|
|
|
|
|
|
|
fn write_u32(&self, offset: usize, value: u32) {
|
|
|
|
match self {
|
|
|
|
Self::Ecam(ecam) => ecam.write_u32(offset, value),
|
|
|
|
_ => todo!(),
|
|
|
|
}
|
|
|
|
}
|
2023-09-04 11:27:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
|
|
|
if device.driver.is_some() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let config = &device.info.config_space;
|
|
|
|
|
2023-12-10 21:25:54 +02:00
|
|
|
log::debug!(
|
2023-09-04 11:27:16 +03:00
|
|
|
"{}: {:04x}:{:04x}",
|
|
|
|
device.info.address,
|
|
|
|
config.vendor_id(),
|
|
|
|
config.device_id()
|
|
|
|
);
|
|
|
|
|
2023-12-10 21:25:54 +02:00
|
|
|
let class = config.class_code();
|
|
|
|
let subclass = config.subclass();
|
|
|
|
let prog_if = config.prog_if();
|
|
|
|
|
|
|
|
let drivers = PCI_DRIVERS.lock();
|
|
|
|
for driver in drivers.iter() {
|
|
|
|
if driver
|
|
|
|
.check
|
|
|
|
.check_device(&device.info, class, subclass, prog_if)
|
|
|
|
{
|
|
|
|
// TODO add the device to the bus
|
|
|
|
log::debug!(" -> {:?}", driver.name);
|
|
|
|
let device = (driver.probe)(&device.info)?;
|
2023-12-08 14:30:49 +02:00
|
|
|
|
|
|
|
unsafe {
|
2023-12-10 21:25:54 +02:00
|
|
|
device.init()?;
|
2023-12-08 14:30:49 +02:00
|
|
|
}
|
2023-12-10 21:25:54 +02:00
|
|
|
} else {
|
|
|
|
log::debug!(" -> No driver");
|
2023-09-04 11:27:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-12-10 21:25:54 +02:00
|
|
|
impl PciMatch {
|
|
|
|
pub fn check_device(&self, info: &PciDeviceInfo, class: u8, subclass: u8, prog_if: u8) -> bool {
|
|
|
|
match self {
|
|
|
|
Self::Generic(f) => f(info),
|
|
|
|
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
|
|
|
|
class_ == class && subclass_ == subclass && prog_if_ == prog_if
|
|
|
|
}
|
|
|
|
&Self::Class(class_, Some(subclass_), _) => class_ == class && subclass_ == subclass,
|
|
|
|
&Self::Class(class_, _, _) => class_ == class,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn register_class_driver(
|
|
|
|
name: &'static str,
|
|
|
|
class: u8,
|
|
|
|
subclass: Option<u8>,
|
|
|
|
prog_if: Option<u8>,
|
|
|
|
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
|
|
|
|
) {
|
|
|
|
PCI_DRIVERS.lock().push(PciDriver {
|
|
|
|
name,
|
|
|
|
check: PciMatch::Class(class, subclass, prog_if),
|
|
|
|
probe,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn register_generic_driver(
|
|
|
|
name: &'static str,
|
|
|
|
check: fn(&PciDeviceInfo) -> bool,
|
|
|
|
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
|
|
|
|
) {
|
|
|
|
PCI_DRIVERS.lock().push(PciDriver {
|
|
|
|
name,
|
|
|
|
check: PciMatch::Generic(check),
|
|
|
|
probe,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static PCI_DRIVERS: IrqSafeSpinlock<Vec<PciDriver>> = IrqSafeSpinlock::new(Vec::new());
|
2023-09-04 11:27:16 +03:00
|
|
|
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|