Skip to content

Commit 86e972c

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 86e972c

File tree

3 files changed

+224
-18
lines changed

3 files changed

+224
-18
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: 199 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use alloc::collections::VecDeque;
44
use alloc::vec::Vec;
5-
use core::fmt;
5+
use core::fmt::{self, Write};
66

77
use ahash::RandomState;
88
use hashbrown::HashMap;
@@ -304,14 +304,167 @@ impl<T: ConfigRegionAccess> fmt::Display for PciDevice<T> {
304304
}
305305
}
306306

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

310453
for adapter in PCI_DEVICES.finalize().iter() {
311454
info!("{}", adapter);
312455
}
313456

314457
infofooter!();
458+
459+
Ok(())
460+
}
461+
462+
pub(crate) fn print_information() {
463+
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
464+
print_from_fdt().or_else(|_e| print_from_pci()).unwrap();
465+
466+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
467+
print_from_pci().unwrap();
315468
}
316469

317470
#[allow(clippy::large_enum_variant)]
@@ -472,7 +625,44 @@ pub(crate) fn get_filesystem_driver() -> Option<&'static InterruptTicketMutex<Vi
472625
.find_map(|drv| drv.get_filesystem_driver())
473626
}
474627

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

527723
/// A module containing PCI specific errors

0 commit comments

Comments
 (0)