use sunrise_libutils::io::{Io, Pio};
use spin::Mutex;
use alloc::vec::Vec;
pub const CONFIG_ADDRESS: u16 = 0xCF8;
pub const CONFIG_DATA: u16 = 0xCFC;
struct PciConfigPortsPair {
address: Pio<u32>,
data: Pio<u32>
}
static PCI_CONFIG_PORTS: Mutex<PciConfigPortsPair> = Mutex::new(PciConfigPortsPair {
address: Pio::new(CONFIG_ADDRESS),
data: Pio::new(CONFIG_DATA)
});
const MAX_BUS: u8 = 255;
const MAX_SLOT: u8 = 31;
const MAX_FUNC: u8 = 15;
const MAX_REGISTER: u8 = 63;
#[derive(Debug, Copy, Clone)]
#[allow(clippy::missing_docs_in_private_items)]
struct PciDevice {
bus: u8,
slot: u8,
function: u8,
did: u16,
vid: u16,
class: u8,
subclass: u8,
prog_if: u8,
rev_id: u8,
header_type: u8,
latency_timer: u8,
cache_line_size: u8,
header: PciHeader
}
#[derive(Copy, Clone, Debug)]
#[allow(clippy::missing_docs_in_private_items)]
struct PciHeader00 {
bar0: BAR,
bar1: BAR,
bar2: BAR,
bar3: BAR,
bar4: BAR,
bar5: BAR,
cardbus_cis_ptr: u32,
subsystem_id: u16,
subsystem_vendor_id: u16,
expansion_rom_base_address: u32,
capabilities_ptr: u8,
max_latency: u8,
min_grant: u8,
interrupt_pin: u8,
interrupt_line: u8,
}
#[derive(Copy, Clone, Debug)]
enum PciHeader {
GeneralDevice(PciHeader00),
PCItoPCIBridge,
CardBus,
UnknownHeaderType(u8)
}
#[derive(Copy, Clone, Debug)]
enum BAR {
Memory(u32, u32),
Io(u32, u32)
}
impl PciDevice {
#[allow(clippy::absurd_extreme_comparisons)]
fn probe(bus: u8, slot: u8, function: u8) -> Option<Self> {
debug_assert!(bus <= MAX_BUS);
debug_assert!(slot <= MAX_SLOT);
debug_assert!(function <= MAX_FUNC);
let did_vid = pci_config_read_word(bus, slot, function, 0);
return if did_vid == 0xFFFF_FFFF {
None
} else {
Some(Self {
bus,
slot,
function,
did: (did_vid >> 16) as u16,
vid: did_vid as u16,
class: (pci_config_read_word(bus, slot, function, 2) >> 24) as u8,
subclass: (pci_config_read_word(bus, slot, function, 2) >> 16) as u8,
prog_if: (pci_config_read_word(bus, slot, function, 2) >> 8) as u8,
rev_id: pci_config_read_word(bus, slot, function, 2) as u8,
header_type: (pci_config_read_word(bus, slot, function, 3) >> 16) as u8,
latency_timer: (pci_config_read_word(bus, slot, function, 3) >> 8) as u8,
cache_line_size: pci_config_read_word(bus, slot, function, 3) as u8,
header: read_header(bus, slot, function)
})
};
fn read_header(bus: u8, slot: u8, function: u8) -> PciHeader {
let header_type = (pci_config_read_word(bus, slot, function, 3) >> 16) as u8;
return match header_type & 0x7f {
0x00 => PciHeader::GeneralDevice(PciHeader00 {
bar0: decode_bar(bus, slot, function, 4),
bar1: decode_bar(bus, slot, function, 5),
bar2: decode_bar(bus, slot, function, 6),
bar3: decode_bar(bus, slot, function, 7),
bar4: decode_bar(bus, slot, function, 8),
bar5: decode_bar(bus, slot, function, 9),
cardbus_cis_ptr: pci_config_read_word(bus, slot, function, 0xa),
subsystem_id: (pci_config_read_word(bus, slot, function, 0xb) >> 16) as u16,
subsystem_vendor_id: pci_config_read_word(bus, slot, function, 0xb) as u16,
expansion_rom_base_address: pci_config_read_word(bus, slot, function, 0xc),
capabilities_ptr: pci_config_read_word(bus, slot, function, 0xd) as u8,
max_latency: (pci_config_read_word(bus, slot, function, 0xf) >> 24) as u8,
min_grant: (pci_config_read_word(bus, slot, function, 0xf) >> 16) as u8,
interrupt_pin: (pci_config_read_word(bus, slot, function, 0xf) >> 8) as u8,
interrupt_line: pci_config_read_word(bus, slot, function, 0xf) as u8,
}),
0x01 => PciHeader::PCItoPCIBridge,
0x02 => PciHeader::CardBus,
other => PciHeader::UnknownHeaderType(other)
};
fn decode_bar(bus: u8, slot: u8, function: u8, register: u8) -> BAR {
let addr = pci_config_read_word(bus, slot, function, register);
pci_config_write_word(bus, slot, function, register, 0xFFFF_FFFF);
let length = pci_config_read_word(bus, slot, function, register);
pci_config_write_word(bus, slot, function, register, addr);
match addr & 0x01 {
0 => {
BAR::Memory(addr & 0xFFFF_FFF0, (!(length & 0xFFFF_FFF0)).wrapping_add(1))
},
_ => {
BAR::Io(addr & 0xFFFF_FFFC, (!(length & 0xFFFF_FFFC)).wrapping_add(1))
}
}
}
}
}
fn read_config_register(&self, register: u8) -> u32 {
pci_config_read_word(self.bus, self.slot, self.function, register)
}
fn write_config_register(&self, register: u8, value: u32) {
pci_config_write_word(self.bus, self.slot, self.function, register, value)
}
fn status(&self) -> u16 {
(self.read_config_register(1) >> 16) as u16
}
fn command(&self) -> u16 {
(self.read_config_register(1) >> 0) as u16
}
}
#[allow(clippy::absurd_extreme_comparisons)]
fn pci_config_read_word(bus: u8, slot: u8, func: u8, register: u8) -> u32 {
debug_assert!(bus <= MAX_BUS);
debug_assert!(slot <= MAX_SLOT);
debug_assert!(func <= MAX_FUNC);
debug_assert!(register <= MAX_REGISTER);
let lbus = u32::from(bus);
let lslot = u32::from(slot);
let lfunc = u32::from(func);
let lregister = u32::from(register);
let mut ports = PCI_CONFIG_PORTS.lock();
let address: u32 = (lbus << 16) | (lslot << 11) |
(lfunc << 8) | (lregister << 2) | 0x80000000;
ports.address.write(address);
ports.data.read()
}
#[allow(clippy::absurd_extreme_comparisons)]
fn pci_config_write_word(bus: u8, slot: u8, func: u8, register: u8, value: u32) {
debug_assert!(bus <= MAX_BUS);
debug_assert!(slot <= MAX_SLOT);
debug_assert!(func <= MAX_FUNC);
debug_assert!(register <= MAX_REGISTER);
let lbus = u32::from(bus);
let lslot = u32::from(slot);
let lfunc = u32::from(func);
let lregister = u32::from(register);
let mut ports = PCI_CONFIG_PORTS.lock();
let address: u32 = (lbus << 16) | (lslot << 11) |
(lfunc << 8) | (lregister << 2) | 0x80000000;
ports.address.write(address);
ports.data.write(value)
}
fn discover() -> Vec<PciDevice> {
let mut devices = vec![];
for bus in 0..MAX_BUS {
for slot in 0..MAX_SLOT {
if let Some(device) = PciDevice::probe(bus, slot, 0) {
let is_multifunction = device.header_type & 0x80 != 0;
devices.push(device);
if is_multifunction {
for function in 1..MAX_FUNC {
if let Some(device) = PciDevice::probe(bus, slot, function) {
devices.push(device);
}
}
}
}
}
}
devices
}
pub fn get_ahci_controllers() -> Vec<(u32, u32)> {
discover().iter()
.filter(|device| device.class == 0x01 && device.subclass == 0x06 && device.prog_if == 0x01)
.map(|device| {
match device.header {
PciHeader::GeneralDevice(header00) => {
match header00.bar5 {
BAR::Memory(addr, size) => (addr, size),
_ => panic!("PCI device with unexpected BAR 5")
}
},
_ => panic!("PCI device with unexpected header")
}
})
.collect()
}