Skip to content

Commit 67d267d

Browse files
committed
Read PCI devices through devicetree instead of scanning PCI bus in kernel
If devicetree exists, skip scanning the PCI bus
1 parent fcb871c commit 67d267d

File tree

3 files changed

+226
-17
lines changed

3 files changed

+226
-17
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,15 @@ harness = false
4848
default = ["pci", "pci-ids", "acpi", "fsgsbase", "smp", "tcp", "dhcpv4", "fuse", "vsock"]
4949
acpi = []
5050
common-os = []
51-
dhcpv4 = ["smoltcp", "smoltcp/proto-dhcpv4", "smoltcp/socket-dhcpv4"]
51+
dhcpv4 = [
52+
"smoltcp",
53+
"smoltcp/proto-dhcpv4",
54+
"smoltcp/socket-dhcpv4",
55+
]
5256
dns = ["smoltcp", "smoltcp/socket-dns"]
5357
fs = ["fuse"]
54-
fsgsbase = []
5558
fuse = ["pci", "dep:fuse-abi", "fuse-abi/num_enum"]
59+
fsgsbase = []
5660
gem-net = ["tcp", "dep:tock-registers"]
5761
idle-poll = []
5862
mmap = []

src/arch/x86_64/kernel/pci.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,27 @@ impl ConfigRegionAccess for PciConfigRegion {
4949
}
5050

5151
pub(crate) fn init() {
52-
debug!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1);
52+
if let Some(_fdt) = crate::env::fdt() {
53+
info!("Device Tree is available");
5354

54-
// Hermit only uses PCI for network devices.
55-
// Therefore, multifunction devices as well as additional bridges are not scanned.
56-
// We also limit scanning to the first 32 buses.
57-
let pci_config = PciConfigRegion::new();
58-
for bus in 0..PCI_MAX_BUS_NUMBER {
59-
for device in 0..PCI_MAX_DEVICE_NUMBER {
60-
let pci_address = PciAddress::new(0, bus, device, 0);
61-
let header = PciHeader::new(pci_address);
55+
// Do nothing here, as the PCI devices are scanned in the device tree.
56+
} else {
57+
debug!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1);
6258

63-
let (device_id, vendor_id) = header.id(pci_config);
64-
if device_id != u16::MAX && vendor_id != u16::MAX {
65-
let device = PciDevice::new(pci_address, pci_config);
66-
PCI_DEVICES.with(|pci_devices| pci_devices.unwrap().push(device));
59+
// Hermit only uses PCI for network devices.
60+
// Therefore, multifunction devices as well as additional bridges are not scanned.
61+
// We also limit scanning to the first 32 buses.
62+
let pci_config = PciConfigRegion::new();
63+
for bus in 0..PCI_MAX_BUS_NUMBER {
64+
for device in 0..PCI_MAX_DEVICE_NUMBER {
65+
let pci_address = PciAddress::new(0, bus, device, 0);
66+
let header = PciHeader::new(pci_address);
67+
68+
let (device_id, vendor_id) = header.id(pci_config);
69+
if device_id != u16::MAX && vendor_id != u16::MAX {
70+
let device = PciDevice::new(pci_address, pci_config);
71+
PCI_DEVICES.with(|pci_devices| pci_devices.unwrap().push(device));
72+
}
6773
}
6874
}
6975
}

src/drivers/pci.rs

Lines changed: 201 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
use alloc::collections::VecDeque;
44
use alloc::vec::Vec;
5+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
56
use core::fmt;
7+
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
8+
use core::fmt::{self, Write};
69

710
use ahash::RandomState;
811
use hashbrown::HashMap;
@@ -304,14 +307,167 @@ impl<T: ConfigRegionAccess> fmt::Display for PciDevice<T> {
304307
}
305308
}
306309

307-
pub(crate) fn print_information() {
310+
// Currently, this function is only implemented for x86_64
311+
// TODO: Implement reading PCI information from devicetree on aarch64
312+
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
313+
fn print_from_fdt() -> Result<(), ()> {
314+
let mut f = alloc::string::String::new();
315+
316+
let fdt = env::fdt().ok_or(())?;
317+
318+
infoheader!(" PCI BUS INFORMATION ");
319+
320+
if let Some(pci) = fdt.find_node("/pci") {
321+
for node in pci.children() {
322+
let reg = node.property("reg").unwrap().value;
323+
let addr = u32::from_be_bytes(reg[0..4].try_into().unwrap());
324+
325+
let pci_config = PciConfigRegion::new();
326+
327+
let pci_address = PciAddress::new(
328+
0,
329+
((addr >> 16) & 0xff) as u8,
330+
((addr >> 11) & 0x1f) as u8,
331+
0,
332+
);
333+
334+
let vendor_id = u32::from_be_bytes(
335+
node.property("vendor-id").unwrap().value[..]
336+
.try_into()
337+
.unwrap(),
338+
) as u16;
339+
let device_id = u32::from_be_bytes(
340+
node.property("device-id").unwrap().value[..]
341+
.try_into()
342+
.unwrap(),
343+
) as u16;
344+
345+
let header = PciHeader::new(pci_address);
346+
let (_dev_rev, class_id, subclass_id, _interface) =
347+
header.revision_and_class(pci_config);
348+
349+
#[cfg(feature = "pci-ids")]
350+
let (class_name, vendor_name, device_name) = {
351+
use pci_ids::{Class, Device, FromId, Subclass};
352+
353+
let class_name = Class::from_id(class_id).map_or("Unknown Class", |class| {
354+
class
355+
.subclasses()
356+
.find(|s| s.id() == subclass_id)
357+
.map(Subclass::name)
358+
.unwrap_or_else(|| class.name())
359+
});
360+
361+
let (vendor_name, device_name) = Device::from_vid_pid(vendor_id, device_id)
362+
.map(|device| (device.vendor().name(), device.name()))
363+
.unwrap_or(("Unknown Vendor", "Unknown Device"));
364+
365+
(class_name, vendor_name, device_name)
366+
};
367+
368+
#[cfg(not(feature = "pci-ids"))]
369+
let (class_name, vendor_name, device_name) =
370+
("Unknown Class", "Unknown Vendor", "Unknown Device");
371+
372+
// Output detailed readable information about this device.
373+
write!(
374+
&mut f,
375+
"{:02X}:{:02X} {} [{:02X}{:02X}]: {} {} [{:04X}:{:04X}]",
376+
pci_address.bus(),
377+
pci_address.device(),
378+
class_name,
379+
class_id,
380+
subclass_id,
381+
vendor_name,
382+
device_name,
383+
vendor_id,
384+
device_id
385+
)
386+
.unwrap();
387+
388+
// If the devices uses an IRQ, output this one as well.
389+
if let Some(irq_prop) = node.property("interrupts") {
390+
let irq = u32::from_be_bytes(irq_prop.value[..].try_into().unwrap()) as u8;
391+
392+
if irq != 0 && irq != u8::MAX {
393+
write!(&mut f, ", IRQ {irq}").unwrap();
394+
}
395+
}
396+
397+
let mut assigned_addresses = node.property("assigned-addresses").unwrap().value;
398+
let mut value_slice;
399+
400+
let mut slot: u8 = 0;
401+
while !assigned_addresses.is_empty() {
402+
(value_slice, assigned_addresses) =
403+
assigned_addresses.split_at(core::mem::size_of::<u32>());
404+
let bar = u32::from_be_bytes(value_slice.try_into().unwrap());
405+
406+
match bar ^ addr {
407+
0x8100_0014 => {
408+
(value_slice, assigned_addresses) =
409+
assigned_addresses.split_at(core::mem::size_of::<u64>());
410+
let port = u64::from_be_bytes(value_slice.try_into().unwrap());
411+
(value_slice, assigned_addresses) =
412+
assigned_addresses.split_at(core::mem::size_of::<u64>());
413+
let _size = u64::from_be_bytes(value_slice.try_into().unwrap());
414+
write!(&mut f, ", BAR{slot} IO {{ port: {port:#X} }}").unwrap();
415+
}
416+
0x8200_0010 => {
417+
(value_slice, assigned_addresses) =
418+
assigned_addresses.split_at(core::mem::size_of::<u64>());
419+
let address = u64::from_be_bytes(value_slice.try_into().unwrap());
420+
(value_slice, assigned_addresses) =
421+
assigned_addresses.split_at(core::mem::size_of::<u64>());
422+
let size = u64::from_be_bytes(value_slice.try_into().unwrap());
423+
424+
if address.leading_zeros() >= 32 && size.leading_zeros() >= 32 {
425+
write!(
426+
&mut f,
427+
", BAR{slot} Memory32 {{ address: {address:#X}, size: {size:#X} }}"
428+
)
429+
.unwrap();
430+
} else {
431+
write!(
432+
&mut f,
433+
", BAR{slot} Memory64 {{ address: {address:#X}, size: {size:#X} }}"
434+
)
435+
.unwrap();
436+
slot += 1;
437+
}
438+
}
439+
_ => {}
440+
}
441+
slot += 1;
442+
}
443+
}
444+
}
445+
446+
info!("{}", f);
447+
448+
infofooter!();
449+
450+
Ok(())
451+
}
452+
453+
fn print_from_pci() -> Result<(), ()> {
308454
infoheader!(" PCI BUS INFORMATION ");
309455

310456
for adapter in PCI_DEVICES.finalize().iter() {
311457
info!("{}", adapter);
312458
}
313459

314460
infofooter!();
461+
462+
Ok(())
463+
}
464+
465+
pub(crate) fn print_information() {
466+
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
467+
print_from_fdt().or_else(|_e| print_from_pci()).unwrap();
468+
469+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
470+
print_from_pci().unwrap();
315471
}
316472

317473
#[allow(clippy::large_enum_variant)]
@@ -472,7 +628,44 @@ pub(crate) fn get_filesystem_driver() -> Option<&'static InterruptTicketMutex<Vi
472628
.find_map(|drv| drv.get_filesystem_driver())
473629
}
474630

475-
pub(crate) fn init() {
631+
fn init_from_fdt() -> Result<(), ()> {
632+
let fdt = env::fdt().ok_or(())?;
633+
634+
info!(
635+
"Initializing PCI devices from FDT at {:#x}",
636+
core::ptr::from_ref::<fdt::Fdt<'_>>(&fdt) as usize
637+
);
638+
639+
#[cfg(feature = "rtl8139")]
640+
if let Some(node) = fdt.find_compatible(&["realtek,rtl8139"]) {
641+
info!(
642+
"Found Realtek network device with device id {:#x}",
643+
node.property("device-id").unwrap().as_usize().unwrap()
644+
);
645+
646+
let reg = node.property("reg").unwrap().value;
647+
let addr = u32::from_be_bytes(reg[0..4].try_into().unwrap());
648+
649+
let pci_config = PciConfigRegion::new();
650+
651+
let pci_address = PciAddress::new(
652+
0,
653+
((addr >> 16) & 0xff) as u8,
654+
((addr >> 11) & 0x1f) as u8,
655+
0,
656+
);
657+
658+
let adapter = PciDevice::new(pci_address, pci_config);
659+
660+
if let Ok(drv) = rtl8139::init_device(&adapter) {
661+
register_driver(PciDriver::RTL8139Net(InterruptTicketMutex::new(drv)))
662+
}
663+
}
664+
665+
Ok(())
666+
}
667+
668+
fn init_from_pci() -> Result<(), ()> {
476669
// virtio: 4.1.2 PCI Device Discovery
477670
without_interrupts(|| {
478671
for adapter in PCI_DEVICES.finalize().iter().filter(|x| {
@@ -522,6 +715,12 @@ pub(crate) fn init() {
522715
}
523716
}
524717
});
718+
719+
Ok(())
720+
}
721+
722+
pub(crate) fn init() {
723+
init_from_fdt().or_else(|_e| init_from_pci()).unwrap();
525724
}
526725

527726
/// A module containing PCI specific errors

0 commit comments

Comments
 (0)