[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

View file

@ -258,6 +258,7 @@ pub const kZionPortSend: u64 = 81;
pub const kZionPortRecv: u64 = 82;
pub const kZionPortPoll: u64 = 83;
pub const kZionIrqRegister: u64 = 88;
pub const kZionMsiIrqRegister: u64 = 89;
pub const kZionEndpointCreate: u64 = 96;
pub const kZionEndpointSend: u64 = 97;
pub const kZionEndpointRecv: u64 = 98;
@ -478,6 +479,12 @@ pub struct ZIrqRegisterReq {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ZMsiIrqRegisterReq {
pub irq_num: *mut u64,
pub port_cap: *mut z_cap_t,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ZEndpointCreateReq {
pub endpoint_cap: *mut z_cap_t,
}

View file

@ -120,6 +120,12 @@ impl<T> AsRef<T> for MemoryRegion {
}
}
impl<T> AsMut<T> for MemoryRegion {
fn as_mut(&mut self) -> &mut T {
unsafe { (self.virt_addr as *mut T).as_mut().unwrap() }
}
}
impl Drop for MemoryRegion {
fn drop(&mut self) {
// FIXME: We shouldn't have to do this manual adjustment.

View file

@ -310,16 +310,17 @@ pub fn port_poll(
Ok((num_bytes, num_caps))
}
pub fn register_irq(irq_num: u64) -> Result<Capability, ZError> {
pub fn register_msi_irq() -> Result<(Capability, u64), ZError> {
let mut irq_num: u64 = 0;
let mut port_cap: z_cap_t = 0;
syscall(
zion::kZionIrqRegister,
&zion::ZIrqRegisterReq {
irq_num,
zion::kZionMsiIrqRegister,
&zion::ZMsiIrqRegisterReq {
irq_num: &mut irq_num as *mut u64,
port_cap: &mut port_cap,
},
)?;
Ok(Capability::take(port_cap))
Ok((Capability::take(port_cap), irq_num))
}
pub fn endpoint_create() -> Result<Capability, ZError> {

8
rust/lib/pci/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "pci"
version = "0.1.0"
edition = "2024"
[dependencies]
bitfield-struct = "0.8.0"
mammoth = {path = "../mammoth/"}

View file

@ -0,0 +1,89 @@
use alloc::vec::Vec;
use mammoth::{cap::Capability, mem::MemoryRegion, syscall, zion::ZError};
use crate::header::{
PciCapabilityPointer, PciDeviceHeader, PciHeaderType, PciMsiCapability, PciMsiControl,
get_header_type,
};
pub struct PciDevice {
memory_region: MemoryRegion,
}
impl PciDevice {
pub fn from(mut memory_region: MemoryRegion) -> Result<Self, ZError> {
match get_header_type(&memory_region)? {
PciHeaderType::Device => {}
t => {
mammoth::debug!("Invalid header type: {:?}", t);
return Err(ZError::INVALID_ARGUMENT);
}
}
Ok(Self { memory_region })
}
pub fn from_cap(capability: Capability) -> Result<Self, ZError> {
Self::from(MemoryRegion::from_cap(capability)?)
}
pub fn header(&self) -> &PciDeviceHeader {
self.memory_region.as_ref()
}
pub fn get_capability_list(&self) -> Result<Vec<&PciCapabilityPointer>, ZError> {
let status = self.header().status;
if !status.capability_list() {
return Err(ZError::NOT_FOUND);
}
let mut cap_offset = self.header().capability_ptr;
let mut cap_vec = Vec::new();
while cap_offset != 0 {
let cap_ptr: &PciCapabilityPointer = unsafe {
self.memory_region
.raw_ptr_at_offset::<PciCapabilityPointer>(cap_offset as u64)
.as_ref()
.unwrap()
};
cap_vec.push(cap_ptr);
cap_offset = cap_ptr.next_cap_offset;
}
Ok(cap_vec)
}
pub fn register_msi(&mut self) -> Result<Capability, ZError> {
let caps = self.get_capability_list()?;
const MSI_CAP_ID: u8 = 0x05;
let msi_cap: &PciCapabilityPointer = caps
.iter()
.find(|cp| cp.cap_id == MSI_CAP_ID)
.ok_or(ZError::NOT_FOUND)?;
let msi_cap = unsafe {
((msi_cap as *const PciCapabilityPointer) as *mut PciMsiCapability)
.as_mut()
.unwrap()
};
mammoth::debug!("MSI Cap: {:#x?}", msi_cap);
let control = msi_cap.msi_control;
assert!(control.capable_address_64());
assert!(control.multi_message_capable() == 0);
// FIXME: These probably need to be volatile writes.
let header: &mut PciDeviceHeader = self.memory_region.as_mut();
header.command = header.command.with_interrupt_disable(true);
msi_cap.msi_control = control.with_msi_enable(true);
msi_cap.msi_addr_lower = 0xFEE00000;
msi_cap.msi_addr_upper_or_data = 0x0;
let (cap, irq_num) = syscall::register_msi_irq()?;
msi_cap.msi_data_if_64 = irq_num as u32;
Ok(cap)
}
}

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

8
rust/lib/pci/src/lib.rs Normal file
View file

@ -0,0 +1,8 @@
#![no_std]
extern crate alloc;
mod device;
mod header;
pub use device::PciDevice;