From 75107e00eb77fa8bbed3f88ccb4320f230740efa Mon Sep 17 00:00:00 2001 From: Yu Duan Date: Fri, 22 Nov 2024 10:18:48 +0000 Subject: [PATCH] Read PCI devices through devicetree instead of scanning PCI bus in kernel If devicetree exists, skip scanning the PCI bus --- Cargo.toml | 8 +- src/arch/x86_64/kernel/pci.rs | 32 +++--- src/drivers/pci.rs | 203 +++++++++++++++++++++++++++++++++- 3 files changed, 226 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 14fa107be0..ba384e36d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,11 +48,15 @@ harness = false default = ["pci", "pci-ids", "acpi", "fsgsbase", "smp", "tcp", "dhcpv4", "fuse", "vsock"] acpi = [] common-os = [] -dhcpv4 = ["smoltcp", "smoltcp/proto-dhcpv4", "smoltcp/socket-dhcpv4"] +dhcpv4 = [ + "smoltcp", + "smoltcp/proto-dhcpv4", + "smoltcp/socket-dhcpv4", +] dns = ["smoltcp", "smoltcp/socket-dns"] fs = ["fuse"] -fsgsbase = [] fuse = ["pci", "dep:fuse-abi", "fuse-abi/num_enum"] +fsgsbase = [] gem-net = ["tcp", "dep:tock-registers"] idle-poll = [] mmap = [] diff --git a/src/arch/x86_64/kernel/pci.rs b/src/arch/x86_64/kernel/pci.rs index 6763400380..6c8a2c9f41 100644 --- a/src/arch/x86_64/kernel/pci.rs +++ b/src/arch/x86_64/kernel/pci.rs @@ -49,21 +49,27 @@ impl ConfigRegionAccess for PciConfigRegion { } pub(crate) fn init() { - debug!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1); + if let Some(_fdt) = crate::env::fdt() { + info!("Device Tree is available"); - // Hermit only uses PCI for network devices. - // Therefore, multifunction devices as well as additional bridges are not scanned. - // We also limit scanning to the first 32 buses. - let pci_config = PciConfigRegion::new(); - for bus in 0..PCI_MAX_BUS_NUMBER { - for device in 0..PCI_MAX_DEVICE_NUMBER { - let pci_address = PciAddress::new(0, bus, device, 0); - let header = PciHeader::new(pci_address); + // Do nothing here, as the PCI devices are scanned in the device tree. + } else { + debug!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1); - let (device_id, vendor_id) = header.id(pci_config); - if device_id != u16::MAX && vendor_id != u16::MAX { - let device = PciDevice::new(pci_address, pci_config); - PCI_DEVICES.with(|pci_devices| pci_devices.unwrap().push(device)); + // Hermit only uses PCI for network devices. + // Therefore, multifunction devices as well as additional bridges are not scanned. + // We also limit scanning to the first 32 buses. + let pci_config = PciConfigRegion::new(); + for bus in 0..PCI_MAX_BUS_NUMBER { + for device in 0..PCI_MAX_DEVICE_NUMBER { + let pci_address = PciAddress::new(0, bus, device, 0); + let header = PciHeader::new(pci_address); + + let (device_id, vendor_id) = header.id(pci_config); + if device_id != u16::MAX && vendor_id != u16::MAX { + let device = PciDevice::new(pci_address, pci_config); + PCI_DEVICES.with(|pci_devices| pci_devices.unwrap().push(device)); + } } } } diff --git a/src/drivers/pci.rs b/src/drivers/pci.rs index 52621efba7..45adc31065 100644 --- a/src/drivers/pci.rs +++ b/src/drivers/pci.rs @@ -2,6 +2,9 @@ use alloc::collections::VecDeque; use alloc::vec::Vec; +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +use core::fmt::{self, Write}; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use core::fmt; use ahash::RandomState; @@ -304,7 +307,150 @@ impl fmt::Display for PciDevice { } } -pub(crate) fn print_information() { +// Currently, this function is only implemented for x86_64 +// TODO: Implement reading PCI information from devicetree on aarch64 +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +fn print_from_fdt() -> Result<(), ()> { + let mut f = alloc::string::String::new(); + + let fdt = env::fdt().ok_or(())?; + + infoheader!(" PCI BUS INFORMATION "); + + if let Some(pci) = fdt.find_node("/pci") { + for node in pci.children() { + let reg = node.property("reg").unwrap().value; + let addr = u32::from_be_bytes(reg[0..4].try_into().unwrap()); + + let pci_config = PciConfigRegion::new(); + + let pci_address = PciAddress::new( + 0, + ((addr >> 16) & 0xff) as u8, + ((addr >> 11) & 0x1f) as u8, + 0, + ); + + let vendor_id = u32::from_be_bytes( + node.property("vendor-id").unwrap().value[..] + .try_into() + .unwrap(), + ) as u16; + let device_id = u32::from_be_bytes( + node.property("device-id").unwrap().value[..] + .try_into() + .unwrap(), + ) as u16; + + let header = PciHeader::new(pci_address); + let (_dev_rev, class_id, subclass_id, _interface) = + header.revision_and_class(pci_config); + + #[cfg(feature = "pci-ids")] + let (class_name, vendor_name, device_name) = { + use pci_ids::{Class, Device, FromId, Subclass}; + + let class_name = Class::from_id(class_id).map_or("Unknown Class", |class| { + class + .subclasses() + .find(|s| s.id() == subclass_id) + .map(Subclass::name) + .unwrap_or_else(|| class.name()) + }); + + let (vendor_name, device_name) = Device::from_vid_pid(vendor_id, device_id) + .map(|device| (device.vendor().name(), device.name())) + .unwrap_or(("Unknown Vendor", "Unknown Device")); + + (class_name, vendor_name, device_name) + }; + + #[cfg(not(feature = "pci-ids"))] + let (class_name, vendor_name, device_name) = + ("Unknown Class", "Unknown Vendor", "Unknown Device"); + + // Output detailed readable information about this device. + write!( + &mut f, + "{:02X}:{:02X} {} [{:02X}{:02X}]: {} {} [{:04X}:{:04X}]", + pci_address.bus(), + pci_address.device(), + class_name, + class_id, + subclass_id, + vendor_name, + device_name, + vendor_id, + device_id + ) + .unwrap(); + + // If the devices uses an IRQ, output this one as well. + if let Some(irq_prop) = node.property("interrupts") { + let irq = u32::from_be_bytes(irq_prop.value[..].try_into().unwrap()) as u8; + + if irq != 0 && irq != u8::MAX { + write!(&mut f, ", IRQ {irq}").unwrap(); + } + } + + let mut assigned_addresses = node.property("assigned-addresses").unwrap().value; + let mut value_slice; + + let mut slot: u8 = 0; + while !assigned_addresses.is_empty() { + (value_slice, assigned_addresses) = + assigned_addresses.split_at(core::mem::size_of::()); + let bar = u32::from_be_bytes(value_slice.try_into().unwrap()); + + match bar ^ addr { + 0x8100_0014 => { + (value_slice, assigned_addresses) = + assigned_addresses.split_at(core::mem::size_of::()); + let port = u64::from_be_bytes(value_slice.try_into().unwrap()); + (value_slice, assigned_addresses) = + assigned_addresses.split_at(core::mem::size_of::()); + let _size = u64::from_be_bytes(value_slice.try_into().unwrap()); + write!(&mut f, ", BAR{slot} IO {{ port: {port:#X} }}").unwrap(); + } + 0x8200_0010 => { + (value_slice, assigned_addresses) = + assigned_addresses.split_at(core::mem::size_of::()); + let address = u64::from_be_bytes(value_slice.try_into().unwrap()); + (value_slice, assigned_addresses) = + assigned_addresses.split_at(core::mem::size_of::()); + let size = u64::from_be_bytes(value_slice.try_into().unwrap()); + + if address.leading_zeros() >= 32 && size.leading_zeros() >= 32 { + write!( + &mut f, + ", BAR{slot} Memory32 {{ address: {address:#X}, size: {size:#X} }}" + ) + .unwrap(); + } else { + write!( + &mut f, + ", BAR{slot} Memory64 {{ address: {address:#X}, size: {size:#X} }}" + ) + .unwrap(); + slot += 1; + } + } + _ => {} + } + slot += 1; + } + } + } + + info!("{}", f); + + infofooter!(); + + Ok(()) +} + +fn print_from_pci() -> Result<(), ()> { infoheader!(" PCI BUS INFORMATION "); for adapter in PCI_DEVICES.finalize().iter() { @@ -312,6 +458,16 @@ pub(crate) fn print_information() { } infofooter!(); + + Ok(()) +} + +pub(crate) fn print_information() { + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + print_from_fdt().or_else(|_e| print_from_pci()).unwrap(); + + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + print_from_pci().unwrap(); } #[allow(clippy::large_enum_variant)] @@ -472,7 +628,44 @@ pub(crate) fn get_filesystem_driver() -> Option<&'static InterruptTicketMutex Result<(), ()> { + let fdt = env::fdt().ok_or(())?; + + info!( + "Initializing PCI devices from FDT at {:#x}", + core::ptr::from_ref::>(&fdt) as usize + ); + + #[cfg(feature = "rtl8139")] + if let Some(node) = fdt.find_compatible(&["realtek,rtl8139"]) { + info!( + "Found Realtek network device with device id {:#x}", + node.property("device-id").unwrap().as_usize().unwrap() + ); + + let reg = node.property("reg").unwrap().value; + let addr = u32::from_be_bytes(reg[0..4].try_into().unwrap()); + + let pci_config = PciConfigRegion::new(); + + let pci_address = PciAddress::new( + 0, + ((addr >> 16) & 0xff) as u8, + ((addr >> 11) & 0x1f) as u8, + 0, + ); + + let adapter = PciDevice::new(pci_address, pci_config); + + if let Ok(drv) = rtl8139::init_device(&adapter) { + register_driver(PciDriver::RTL8139Net(InterruptTicketMutex::new(drv))) + } + } + + Ok(()) +} + +fn init_from_pci() -> Result<(), ()> { // virtio: 4.1.2 PCI Device Discovery without_interrupts(|| { for adapter in PCI_DEVICES.finalize().iter().filter(|x| { @@ -522,6 +715,12 @@ pub(crate) fn init() { } } }); + + Ok(()) +} + +pub(crate) fn init() { + init_from_fdt().or_else(|_e| init_from_pci()).unwrap(); } /// A module containing PCI specific errors