Archived
194 lines
6.4 KiB
Rust
194 lines
6.4 KiB
Rust
//! 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<Vec<PciDevice>> = 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<usize> {
|
|
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::<u32>::new(PCI_CONFIG_ADDRESS);
|
|
let mut port_data = Port::<u32>::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::<u32>::new(PCI_CONFIG_ADDRESS);
|
|
let mut port_data = Port::<u32>::new(PCI_CONFIG_DATA);
|
|
|
|
port_addr.write(address);
|
|
port_data.write(value);
|
|
}
|
|
|
|
/// Найти первое устройство по классу и подклассу.
|
|
pub fn find_by_class(class: u8, subclass: u8) -> Option<PciDevice> {
|
|
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<PciDevice> {
|
|
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;
|
|
}
|