[Zion][Denali] Move to MSI for AHCI devices.

This will allow us to properly do interrupts for XHCI devices in the
future.

Also move PCI device header parsing to a shared library.

Get rid of the old irq register format which supplied an irq number and
instead pass the appropriate irq number back out to the caller.
This commit is contained in:
Drew Galbraith 2025-05-05 23:13:59 -07:00
parent c645405ca8
commit f26fd73116
21 changed files with 371 additions and 124 deletions

180
rust/lib/pci/src/header.rs Normal file
View file

@ -0,0 +1,180 @@
use bitfield_struct::bitfield;
use mammoth::{mem::MemoryRegion, zion::ZError};
#[bitfield(u16)]
pub struct PciCommand {
io_space_enable: bool,
memory_space_enable: bool,
bus_master_enable: bool,
#[bits(access=RO)]
special_cycles_enable: bool,
#[bits(access=RO)]
memory_write_and_invalidate_enable: bool,
#[bits(access=RO)]
vga_pallette_snoop_enable: bool,
parity_error_response_enable: bool,
#[bits(access=RO)]
wait_cycle_enable: bool,
serr_enable: bool,
fast_back_to_back_enable: bool,
/// Parity is reversed here, set to true to disable.
/// Does not affect MSI.
pub interrupt_disable: bool,
#[bits(5)]
__: u8,
}
#[bitfield(u16)]
pub struct PciStatus {
#[bits(3)]
__: u8,
#[bits(access=RO)]
pub interrupt_status: bool,
#[bits(access=RO)]
pub capability_list: bool,
#[bits(access=RO)]
pub capable_of_66mhz: bool,
___: bool,
#[bits(access=RO)]
pub fast_back_to_back_capabale: bool,
/// Write 1 to clear
pub master_data_parity_error: bool,
#[bits(2, access=RO)]
pub devsel_timing: u8,
/// Write 1 to clear
pub signaled_target_abort: bool,
/// Write 1 to clear
pub received_target_abort: bool,
/// Write 1 to clear
pub received_master_abort: bool,
/// Write 1 to clear
pub signaled_system_erro: bool,
/// Write 1 to clear
pub detected_parity_error: bool,
}
/// Header definitions from https://wiki.osdev.org/PCI
#[repr(C, packed)]
#[derive(Debug)]
pub struct HeaderShared {
pub vendor_id: u16,
pub device_id: u16,
pub command: PciCommand,
pub status: PciStatus,
pub revision_id: u8,
pub prog_if: u8,
pub subclass: u8,
pub class_code: u8,
pub cache_line_size: u8,
pub latency_timer: u8,
pub header_type: u8,
bist: u8,
}
const _: () = assert!(size_of::<HeaderShared>() == 16);
#[repr(C, packed)]
#[derive(Debug)]
pub struct PciDeviceHeader {
pub vendor_id: u16,
pub device_id: u16,
pub command: PciCommand,
pub status: PciStatus,
pub revision_id: u8,
pub prog_if: u8,
pub subclass: u8,
pub class_code: u8,
pub cache_line_size: u8,
pub latency_timer: u8,
pub header_type: u8,
bist: u8,
pub bars: [u32; 6],
pub cardbus_cis_ptr: u32,
pub subsystem_vendor_id: u16,
pub subsystem_id: u16,
pub expansion_rom_address: u32,
pub capability_ptr: u8,
__: [u8; 7],
pub interrupt_line: u8,
pub interrupt_pin: u8,
pub min_grant: u8,
pub max_latency: u8,
}
const _: () = assert!(size_of::<PciDeviceHeader>() == 0x40);
#[repr(C, packed)]
#[derive(Debug)]
pub struct PciCapabilityPointer {
pub cap_id: u8,
pub next_cap_offset: u8,
}
#[bitfield(u16)]
pub struct PciMsiControl {
pub msi_enable: bool,
#[bits(3, access=RO)]
pub multi_message_capable: u8,
#[bits(3)]
pub multi_message_enable: u8,
#[bits(access=RO)]
pub capable_address_64: bool,
#[bits(access=RO)]
pub per_vector_masking: bool,
#[bits(7)]
__: u8,
}
#[repr(C, packed)]
#[derive(Debug)]
pub struct PciMsiCapability {
pub cap_id: u8,
pub next_cap_offset: u8,
pub msi_control: PciMsiControl,
pub msi_addr_lower: u32,
pub msi_addr_upper_or_data: u32,
pub msi_data_if_64: u32,
pub mask: u32,
pub pending: u32,
}
#[derive(Debug)]
pub enum PciHeaderType {
Device,
PciBridge,
CardBusBridge,
}
pub fn get_header_type(memory_region: &MemoryRegion) -> Result<PciHeaderType, ZError> {
let shared: &HeaderShared = memory_region.as_ref();
// The only reference I can find to the high bit here is at
// https://www.khoury.northeastern.edu/~pjd/cs7680/homework/pci-enumeration.html
// > Header Type: bit 7 (0x80) indicates whether it is a multi-function device,
match shared.header_type & (!0x80) {
0x0 => Ok(PciHeaderType::Device),
0x1 => Ok(PciHeaderType::PciBridge),
0x2 => Ok(PciHeaderType::CardBusBridge),
_ => {
mammoth::debug!("Unknown pci header type: {:#x}", shared.header_type);
Err(ZError::INVALID_ARGUMENT)
}
}
}