From 959bd8b88fb8393a674ff3c434a3680c84a2c1b2 Mon Sep 17 00:00:00 2001 From: Yu Duan Date: Tue, 26 Nov 2024 16:11:59 +0000 Subject: [PATCH] Scan PCI bus in loader and save the information of network devices in deivcetree --- Cargo.lock | 11 +++ Cargo.toml | 1 + src/arch/x86_64/mod.rs | 2 + src/arch/x86_64/multiboot.rs | 2 + src/arch/x86_64/pci.rs | 60 +++++++++++++++ src/fdt.rs | 139 +++++++++++++++++++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 src/arch/x86_64/pci.rs diff --git a/Cargo.lock b/Cargo.lock index a2f42b24..cbaf4848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,6 +456,7 @@ dependencies = [ "multiboot", "naked-function", "one-shot-mutex", + "pci_types", "sbi-rt", "sptr", "take-static", @@ -765,6 +766,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pci_types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebac2b2ee11791f721a51184b632a916b3044f2ee7b2374e7fdcfdf3eaf29c79" +dependencies = [ + "bit_field", + "bitflags 2.8.0", +] + [[package]] name = "percent-encoding" version = "2.3.1" diff --git a/Cargo.toml b/Cargo.toml index cbc43967..1ec351da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ cfg-if = "1" hermit-entry = { version = "0.10", features = ["loader"] } log = "0.4" one-shot-mutex = "0.1" +pci_types = "0.6" sptr = "0.3" take-static = "0.1" vm-fdt = { version = "0.3", default-features = false, features = ["alloc"] } diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 7adca01b..e9d51513 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -11,6 +11,8 @@ cfg_if::cfg_if! { mod console; #[cfg(target_os = "none")] mod paging; +#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(feature = "fc")))] +pub(crate) mod pci; #[cfg(target_os = "none")] mod physicalmem; diff --git a/src/arch/x86_64/multiboot.rs b/src/arch/x86_64/multiboot.rs index 73abe9c2..9aaefc36 100644 --- a/src/arch/x86_64/multiboot.rs +++ b/src/arch/x86_64/multiboot.rs @@ -68,6 +68,8 @@ impl DeviceTree { fdt = fdt.bootargs(cmdline)?; } + fdt = fdt.pci()?; + let fdt = fdt.finish()?; Ok(fdt.leak()) diff --git a/src/arch/x86_64/pci.rs b/src/arch/x86_64/pci.rs new file mode 100644 index 00000000..c7406a49 --- /dev/null +++ b/src/arch/x86_64/pci.rs @@ -0,0 +1,60 @@ +use pci_types::{ConfigRegionAccess, PciAddress}; +use x86_64::instructions::port::Port; + +pub(crate) const PCI_MAX_BUS_NUMBER: u8 = 32; +pub(crate) const PCI_MAX_DEVICE_NUMBER: u8 = 32; + +const PCI_CONFIG_ADDRESS_ENABLE: u32 = 1 << 31; + +const CONFIG_ADDRESS: Port = Port::new(0xcf8); +const CONFIG_DATA: Port = Port::new(0xcfc); + +#[derive(Debug, Copy, Clone)] +pub(crate) struct PciConfigRegion; + +impl PciConfigRegion { + pub const fn new() -> Self { + Self {} + } +} + +impl ConfigRegionAccess for PciConfigRegion { + #[inline] + fn function_exists(&self, _address: PciAddress) -> bool { + true + } + + #[inline] + unsafe fn read(&self, pci_addr: PciAddress, register: u16) -> u32 { + let mut config_address = CONFIG_ADDRESS; + let mut config_data = CONFIG_DATA; + + let address = PCI_CONFIG_ADDRESS_ENABLE + | (u32::from(pci_addr.bus()) << 16) + | (u32::from(pci_addr.device()) << 11) + | (u32::from(pci_addr.function()) << 8) + | u32::from(register); + + unsafe { + config_address.write(address); + config_data.read() + } + } + + #[inline] + unsafe fn write(&self, pci_addr: PciAddress, register: u16, value: u32) { + let mut config_address = CONFIG_ADDRESS; + let mut config_data = CONFIG_DATA; + + let address = PCI_CONFIG_ADDRESS_ENABLE + | (u32::from(pci_addr.bus()) << 16) + | (u32::from(pci_addr.device()) << 11) + | (u32::from(pci_addr.function()) << 8) + | u32::from(register); + + unsafe { + config_address.write(address); + config_data.write(value); + } + } +} diff --git a/src/fdt.rs b/src/fdt.rs index 320e8c14..d3371aad 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -72,9 +72,16 @@ impl<'a> Fdt<'a> { #[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(feature = "fc")))] mod x86_64 { + use alloc::format; + use alloc::vec::Vec; + + use log::info; use multiboot::information::{MemoryMapIter, MemoryType}; + use pci_types::{Bar, EndpointHeader, MAX_BARS, PciAddress, PciHeader}; use vm_fdt::FdtWriterResult; + use crate::arch::pci::{PCI_MAX_BUS_NUMBER, PCI_MAX_DEVICE_NUMBER, PciConfigRegion}; + impl super::Fdt<'_> { pub fn memory_regions( mut self, @@ -92,6 +99,138 @@ mod x86_64 { Ok(self) } + + pub fn pci(mut self) -> FdtWriterResult { + let fdt = &mut self.writer; + + let pci_node = fdt.begin_node("pci")?; + fdt.property_string("device_type", "pci")?; + + // TODO: Address cells and size cells should be 3 and 2 respectively. 1 and 1 are only used for compatibility with devicetree output tool. + fdt.property_u32("#address-cells", 0x1)?; + fdt.property_u32("#size-cells", 0x1)?; + + info!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1); + + // 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 (vendor_id, device_id) = header.id(&pci_config); + if device_id != u16::MAX && vendor_id != u16::MAX { + let addr = ((pci_address.bus() as u32) << 16) + | ((pci_address.device() as u32) << 11); + info!("Addr: {:#x}", addr); + let endpoint = EndpointHeader::from_header(header, &pci_config).unwrap(); + let (_pin, line) = endpoint.interrupt(&pci_config); + + info!("Device ID: {:#x} Vendor ID: {:#x}", device_id, vendor_id); + + if vendor_id == 0x10ec && (0x8138..=0x8139).contains(&device_id) { + info!("Network card found."); + let net_node = + fdt.begin_node(format!("ethernet@{:x}", addr).as_str())?; + + fdt.property_string("compatible", "realtek,rtl8139")?; + fdt.property_u32("vendor-id", vendor_id as u32)?; + fdt.property_u32("device-id", device_id as u32)?; + fdt.property_u32("interrupts", line as u32)?; + + // The creation of "reg" and "assigned-addresses" properties is based on the + // PCI Bus Binding to IEEE Std 1275-1994 Revision 2.1 (https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf) + fdt.property_array_u32( + "reg", + &[ + addr, + 0, + 0, + 0, + 0, + (0x02000010 | addr), + 0, + 0, + 0, + 0x100, + (0x01000014 | addr), + 0, + 0, + 0, + 0x100, + ], + )?; + + let mut assigned_addresses: Vec = Vec::new(); + for i in 0..MAX_BARS { + if let Some(bar) = endpoint.bar(i.try_into().unwrap(), &pci_config) + { + match bar { + Bar::Io { port } => { + info!("BAR{:x} IO {{port: {:#X}}}", i, port); + assigned_addresses.extend(alloc::vec![ + (0x81000014 | addr), + 0, + port, + 0, + 0x100 + ]); + } + Bar::Memory32 { + address, + size, + prefetchable, + } => { + info!( + "BAR{:x} Memory32 {{address: {:#X}, size {:#X}, prefetchable: {:?}}}", + i, address, size, prefetchable + ); + assigned_addresses.extend(alloc::vec![ + (0x82000010 | addr), + 0, + address, + 0, + size + ]); + } + Bar::Memory64 { + address, + size, + prefetchable, + } => { + info!( + "BAR{:x} Memory64 {{address: {:#X}, size {:#X}, prefetchable: {:?}}}", + i, address, size, prefetchable + ); + assigned_addresses.extend(alloc::vec![ + (0x82000010 | addr), + (address >> 32) as u32, + address as u32, + (size >> 32) as u32, + size as u32 + ]); + } + } + } + } + fdt.property_array_u32( + "assigned-addresses", + assigned_addresses.as_slice(), + )?; + + fdt.end_node(net_node)?; + } + } + } + } + + fdt.end_node(pci_node)?; + + Ok(self) + } } }