Skip to content

Update of PCI scanning process in fdt #425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
2 changes: 2 additions & 0 deletions src/arch/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions src/arch/x86_64/multiboot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ impl DeviceTree {
fdt = fdt.bootargs(cmdline)?;
}

fdt = fdt.pci()?;

let fdt = fdt.finish()?;

Ok(fdt.leak())
Expand Down
60 changes: 60 additions & 0 deletions src/arch/x86_64/pci.rs
Original file line number Diff line number Diff line change
@@ -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<u32> = Port::new(0xcf8);
const CONFIG_DATA: Port<u32> = 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);
}
}
}
139 changes: 139 additions & 0 deletions src/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -92,6 +99,138 @@ mod x86_64 {

Ok(self)
}

pub fn pci(mut self) -> FdtWriterResult<Self> {
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO still relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the fdt output in the command line, the cells have to be 1 and 1 for now. But this is not standard. If we want a standard device tree, we have to configure the output tool first and change the cells respectively. So the question is do we need a standard device tree?

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<u32> = 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)
}
}
}

Expand Down
Loading