//! PCI Bus Enumeration — сканирование шины PCI для обнаружения устройств Intel/AMD. use alloc::vec::Vec; use spin::Mutex; use x86_64::instructions::port::Port; const PCI_CONFIG_ADDRESS: u16 = 0xCF8; const PCI_CONFIG_DATA: u16 = 0xCFC; pub static PCI_DEVICES: Mutex> = Mutex::new(Vec::new()); #[derive(Debug, Clone)] pub struct PciDevice { pub bus: u8, pub device: u8, pub function: u8, pub vendor_id: u16, pub device_id: u16, pub class_id: u8, pub subclass_id: u8, pub prog_if: u8, pub header_type: u8, } impl PciDevice { pub fn class_name(&self) -> &'static str { match (self.class_id, self.subclass_id) { (0x00, _) => "Legacy / Unclassified", (0x01, 0x01) => "IDE Controller", (0x01, 0x06) => "SATA / AHCI Controller", (0x01, 0x08) => "NVMe Storage Controller", (0x01, _) => "Mass Storage Controller", (0x02, 0x00) => "Ethernet Controller", (0x02, 0x80) => "Network Controller (Wi-Fi/Other)", (0x02, _) => "Network Controller", (0x03, 0x00) => "VGA Compatible Controller", (0x03, _) => "Display Controller", (0x04, _) => "Multimedia Audio Device", (0x05, _) => "Memory Controller", (0x06, 0x00) => "Host Bridge", (0x06, 0x01) => "ISA Bridge", (0x06, 0x04) => "PCI-to-PCI Bridge", (0x06, _) => "Bridge Device", (0x0C, 0x03) => "USB Controller (xHCI/EHCI/UHCI)", (0x0C, _) => "Serial Bus Controller", _ => "Generic PCI Device", } } pub fn vendor_name(&self) -> &'static str { match self.vendor_id { 0x8086 => "Intel Corporation", 0x1002 => "AMD / ATI", 0x1022 => "AMD (Advanced Micro Devices)", 0x10DE => "NVIDIA Corporation", 0x1AF4 => "Red Hat / VirtIO", 0x1234 => "QEMU / Bochs Virtual Device", 0x10EC => "Realtek Semiconductor", 0x14E4 => "Broadcom", 0x80EE => "VirtualBox", _ => "Unknown Vendor", } } /// Прочитать Base Address Register (BAR 0..5). pub fn read_bar(&self, bar_idx: u8) -> Option { if bar_idx > 5 { return None; } let offset = 0x10 + (bar_idx * 4); let val = unsafe { pci_read_u32(self.bus, self.device, self.function, offset) }; if val == 0 || val == 0xFFFF_FFFF { return None; } // MMIO BAR (bit 0 = 0) vs I/O BAR (bit 0 = 1) if (val & 1) == 0 { Some((val & 0xFFFF_FFF0) as usize) } else { Some((val & 0xFFFF_FFFC) as usize) } } /// Включить Bus Mastering и Memory Space для DMA. pub fn enable_bus_mastering(&self) { let cmd = unsafe { pci_read_u32(self.bus, self.device, self.function, 0x04) }; // Bit 0 = I/O Space, Bit 1 = Memory Space, Bit 2 = Bus Master let new_cmd = cmd | (1 << 1) | (1 << 2); unsafe { pci_write_u32(self.bus, self.device, self.function, 0x04, new_cmd); } } } /// Прочитать 32-битный регистр конфигурационного пространства PCI. pub unsafe fn pci_read_u32(bus: u8, device: u8, function: u8, offset: u8) -> u32 { let address = ((bus as u32) << 16) | ((device as u32) << 11) | ((function as u32) << 8) | ((offset as u32) & 0xFC) | 0x8000_0000; let mut port_addr = Port::::new(PCI_CONFIG_ADDRESS); let mut port_data = Port::::new(PCI_CONFIG_DATA); port_addr.write(address); port_data.read() } /// Записать 32-битный регистр конфигурационного пространства PCI. pub unsafe fn pci_write_u32(bus: u8, device: u8, function: u8, offset: u8, value: u32) { let address = ((bus as u32) << 16) | ((device as u32) << 11) | ((function as u32) << 8) | ((offset as u32) & 0xFC) | 0x8000_0000; let mut port_addr = Port::::new(PCI_CONFIG_ADDRESS); let mut port_data = Port::::new(PCI_CONFIG_DATA); port_addr.write(address); port_data.write(value); } /// Найти первое устройство по классу и подклассу. pub fn find_by_class(class: u8, subclass: u8) -> Option { let devices = PCI_DEVICES.lock(); devices .iter() .find(|d| d.class_id == class && d.subclass_id == subclass) .cloned() } /// Найти первое устройство по Vendor ID и Device ID. pub fn find_by_vendor(vendor: u16) -> Option { let devices = PCI_DEVICES.lock(); devices.iter().find(|d| d.vendor_id == vendor).cloned() } /// Сканировать шину PCI и заполнить список устройств. pub fn scan_bus() { let mut devices = Vec::new(); for bus in 0..=32 { for device in 0..32 { let val0 = unsafe { pci_read_u32(bus, device, 0, 0) }; let vendor_id = (val0 & 0xFFFF) as u16; // 0xFFFF означает, что устройство отсутствует if vendor_id == 0xFFFF || vendor_id == 0 { continue; } let device_id = ((val0 >> 16) & 0xFFFF) as u16; let val8 = unsafe { pci_read_u32(bus, device, 0, 8) }; let class_id = ((val8 >> 24) & 0xFF) as u8; let subclass_id = ((val8 >> 16) & 0xFF) as u8; let prog_if = ((val8 >> 8) & 0xFF) as u8; let val12 = unsafe { pci_read_u32(bus, device, 0, 12) }; let header_type = ((val12 >> 16) & 0xFF) as u8; let dev = PciDevice { bus, device, function: 0, vendor_id, device_id, class_id, subclass_id, prog_if, header_type, }; devices.push(dev); } } log::info!("[pci] Discovered {} PCI devices", devices.len()); for dev in &devices { log::debug!( "[pci] {:02x}:{:02x}.0 [{:04x}:{:04x}] {} — {}", dev.bus, dev.device, dev.vendor_id, dev.device_id, dev.class_name(), dev.vendor_name() ); } *PCI_DEVICES.lock() = devices; }