acadia/rust/lib/pci/src/header.rs
Drew Galbraith f26fd73116 [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.
2025-05-05 23:14:01 -07:00

180 lines
4.1 KiB
Rust

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)
}
}
}