This repository has been archived on 2026-09-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
opencoreRS/kernel/src/arch/x86_64/pci.rs
T

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