// --------------------------------------------------------------------------- // PCI config space — legacy port-based access (0xCF8 / 0xCFC). // // Enumeration walks buses 0-255 × devices 0-31 × functions 0-7. Port I/O // stays usable after ExitBootServices, so the shell can list devices and // future drivers (AHCI, network, ...) can find their controllers. // --------------------------------------------------------------------------- const CONFIG_ADDRESS: u16 = 0xCF8; const CONFIG_DATA: u16 = 0xCFC; #[derive(Debug, Clone, Copy)] pub struct DeviceInfo { pub bus: u8, pub device: u8, pub func: u8, pub vendor: u16, pub device_id: u16, pub class: u8, pub subclass: u8, pub prog_if: u8, pub header_type: u8, } fn outl(port: u16, val: u32) { unsafe { core::arch::asm!( "out dx, eax", in("dx") port, in("eax") val, options(nomem, nostack, preserves_flags) ); } } fn inl(port: u16) -> u32 { let val: u32; unsafe { core::arch::asm!( "in eax, dx", out("eax") val, in("dx") port, options(nomem, nostack, preserves_flags) ); } val } /// Read a dword from a device's config space (`reg` must be dword-aligned). pub fn config_read(bus: u8, device: u8, func: u8, reg: u8) -> u32 { let addr = 0x8000_0000 | ((bus as u32) << 16) | ((device as u32) << 11) | ((func as u32) << 8) | ((reg as u32) & 0xFC); outl(CONFIG_ADDRESS, addr); inl(CONFIG_DATA) } /// Enumerate every device/function present on all buses. pub fn probe() -> alloc::vec::Vec { let mut out = alloc::vec::Vec::new(); for bus in 0..=255u8 { for dev in 0..32u8 { for func in 0..8u8 { let id = config_read(bus, dev, func, 0); let vendor = (id & 0xFFFF) as u16; if vendor == 0xFFFF { if func == 0 { break; // nothing on this device number } continue; } let hdr = config_read(bus, dev, func, 0x0C); let header_type = (hdr & 0xFF) as u8; let cc = config_read(bus, dev, func, 0x08); out.push(DeviceInfo { bus, device: dev, func, vendor, device_id: (id >> 16) as u16, class: (cc >> 24) as u8, subclass: (cc >> 16) as u8, prog_if: (cc >> 8) as u8, header_type, }); // A non-multifunction device has no functions 1-7. if func == 0 && header_type & 0x80 == 0 { break; } } } } out } /// Short human name for a (class, subclass) pair. pub fn class_name(class: u8, subclass: u8) -> &'static str { match (class, subclass) { (0x00, 0x01) => "VGA-compatible", (0x00, _) => "Unclassified", (0x01, 0x01) => "IDE controller", (0x01, 0x06) => "SATA controller", (0x01, 0x08) => "NVM controller", (0x01, _) => "Mass storage", (0x02, 0x00) => "Ethernet", (0x02, _) => "Network", (0x03, 0x00) => "VGA", (0x03, _) => "Display", (0x04, _) => "Multimedia", (0x05, _) => "Memory controller", (0x06, 0x00) => "Host bridge", (0x06, 0x01) => "ISA bridge", (0x06, 0x04) => "PCI-PCI bridge", (0x06, _) => "Bridge", (0x08, 0x80) => "PIC", (0x0C, 0x03) => "USB controller", (0x0C, 0x05) => "SMBus", (0x0C, _) => "Serial bus", (0x11, _) => "Signal processing", (0xFF, _) => "Unassigned", _ => "Other", } }