Skip to content

Commit e99639b

Browse files
committed
Scan PCI bus in loader and save the information of network devices in deivcetree
1 parent 7c61b80 commit e99639b

File tree

6 files changed

+211
-2
lines changed

6 files changed

+211
-2
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ cfg-if = "1"
1414
hermit-entry = { version = "0.10", features = ["loader"] }
1515
log = "0.4"
1616
one-shot-mutex = "0.1"
17+
pci_types = "0.6"
1718
sptr = "0.3"
1819
take-static = "0.1"
1920
vm-fdt = { version = "0.3", default-features = false, features = ["alloc"] }

src/arch/x86_64/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ cfg_if::cfg_if! {
1111
mod console;
1212
#[cfg(target_os = "none")]
1313
mod paging;
14+
#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(feature = "fc")))]
15+
pub(crate) mod pci;
1416
#[cfg(target_os = "none")]
1517
mod physicalmem;
1618

src/arch/x86_64/multiboot.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::fdt::Fdt;
2020

2121
unsafe extern "C" {
2222
static mut loader_end: u8;
23-
static mb_info: usize;
23+
pub(crate) static mb_info: usize;
2424
}
2525

2626
#[allow(bad_asm_style)]
@@ -31,7 +31,7 @@ mod entry {
3131
);
3232
}
3333

34-
struct Mem;
34+
pub(crate) struct Mem;
3535

3636
impl MemoryManagement for Mem {
3737
unsafe fn paddr_to_slice<'a>(&self, p: PAddr, sz: usize) -> Option<&'static [u8]> {
@@ -68,6 +68,8 @@ impl DeviceTree {
6868
fdt = fdt.bootargs(cmdline)?;
6969
}
7070

71+
fdt = fdt.pci()?;
72+
7173
let fdt = fdt.finish()?;
7274

7375
Ok(fdt.leak())

src/arch/x86_64/pci.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use pci_types::{ConfigRegionAccess, PciAddress};
2+
use x86_64::instructions::port::Port;
3+
4+
pub(crate) const PCI_MAX_BUS_NUMBER: u8 = 32;
5+
pub(crate) const PCI_MAX_DEVICE_NUMBER: u8 = 32;
6+
7+
const PCI_CONFIG_ADDRESS_ENABLE: u32 = 1 << 31;
8+
9+
const CONFIG_ADDRESS: Port<u32> = Port::new(0xcf8);
10+
const CONFIG_DATA: Port<u32> = Port::new(0xcfc);
11+
12+
#[derive(Debug, Copy, Clone)]
13+
pub(crate) struct PciConfigRegion;
14+
15+
impl PciConfigRegion {
16+
pub const fn new() -> Self {
17+
Self {}
18+
}
19+
}
20+
21+
impl ConfigRegionAccess for PciConfigRegion {
22+
#[inline]
23+
fn function_exists(&self, _address: PciAddress) -> bool {
24+
true
25+
}
26+
27+
#[inline]
28+
unsafe fn read(&self, pci_addr: PciAddress, register: u16) -> u32 {
29+
let mut config_address = CONFIG_ADDRESS;
30+
let mut config_data = CONFIG_DATA;
31+
32+
let address = PCI_CONFIG_ADDRESS_ENABLE
33+
| (u32::from(pci_addr.bus()) << 16)
34+
| (u32::from(pci_addr.device()) << 11)
35+
| (u32::from(pci_addr.function()) << 8)
36+
| u32::from(register);
37+
38+
unsafe {
39+
config_address.write(address);
40+
config_data.read()
41+
}
42+
}
43+
44+
#[inline]
45+
unsafe fn write(&self, pci_addr: PciAddress, register: u16, value: u32) {
46+
let mut config_address = CONFIG_ADDRESS;
47+
let mut config_data = CONFIG_DATA;
48+
49+
let address = PCI_CONFIG_ADDRESS_ENABLE
50+
| (u32::from(pci_addr.bus()) << 16)
51+
| (u32::from(pci_addr.device()) << 11)
52+
| (u32::from(pci_addr.function()) << 8)
53+
| u32::from(register);
54+
55+
unsafe {
56+
config_address.write(address);
57+
config_data.write(value);
58+
}
59+
}
60+
}

src/fdt.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,16 @@ impl<'a> Fdt<'a> {
7272

7373
#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(feature = "fc")))]
7474
mod x86_64 {
75+
use alloc::format;
76+
use alloc::vec::Vec;
77+
78+
use log::info;
7579
use multiboot::information::{MemoryMapIter, MemoryType};
80+
use pci_types::{Bar, EndpointHeader, PciAddress, PciHeader, MAX_BARS};
7681
use vm_fdt::FdtWriterResult;
7782

83+
use crate::arch::pci::{PciConfigRegion, PCI_MAX_BUS_NUMBER, PCI_MAX_DEVICE_NUMBER};
84+
7885
impl super::Fdt<'_> {
7986
pub fn memory_regions(
8087
mut self,
@@ -92,6 +99,132 @@ mod x86_64 {
9299

93100
Ok(self)
94101
}
102+
103+
pub fn pci(mut self) -> FdtWriterResult<Self> {
104+
let fdt = &mut self.writer;
105+
106+
let pci_node = fdt.begin_node("pci")?;
107+
fdt.property_string("device_type", "pci")?;
108+
109+
// TODO: Address cells and size cells should be 3 and 2 respectively. 1 and 1 are only used for compatibility with devicetree output tool.
110+
fdt.property_u32("#address-cells", 0x1)?;
111+
fdt.property_u32("#size-cells", 0x1)?;
112+
113+
info!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1);
114+
115+
// Hermit only uses PCI for network devices.
116+
// Therefore, multifunction devices as well as additional bridges are not scanned.
117+
// We also limit scanning to the first 32 buses.
118+
let pci_config = PciConfigRegion::new();
119+
for bus in 0..PCI_MAX_BUS_NUMBER {
120+
for device in 0..PCI_MAX_DEVICE_NUMBER {
121+
let pci_address = PciAddress::new(0, bus, device, 0);
122+
let header = PciHeader::new(pci_address);
123+
124+
let (vendor_id, device_id) = header.id(&pci_config);
125+
if device_id != u16::MAX && vendor_id != u16::MAX {
126+
let addr = ((pci_address.bus() as u32) << 16)
127+
| ((pci_address.device() as u32) << 11);
128+
info!("Addr: {:#x}", addr);
129+
let endpoint = EndpointHeader::from_header(header, &pci_config).unwrap();
130+
let (_pin, line) = endpoint.interrupt(&pci_config);
131+
132+
info!("Device ID: {:#x} Vendor ID: {:#x}", device_id, vendor_id);
133+
134+
if vendor_id == 0x10ec && (0x8138..=0x8139).contains(&device_id) {
135+
info!("Network card found.");
136+
let net_node =
137+
fdt.begin_node(format!("ethernet@{:x}", addr).as_str())?;
138+
139+
fdt.property_string("compatible", "realtek,rtl8139")?;
140+
fdt.property_u32("vendor-id", vendor_id as u32)?;
141+
fdt.property_u32("device-id", device_id as u32)?;
142+
fdt.property_u32("interrupts", line as u32)?;
143+
144+
// The creation of "reg" and "assigned-addresses" properties is based on the
145+
// PCI Bus Binding to IEEE Std 1275-1994 Revision 2.1 (https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf)
146+
fdt.property_array_u32(
147+
"reg",
148+
&[
149+
addr,
150+
0,
151+
0,
152+
0,
153+
0,
154+
(0x02000010 | addr),
155+
0,
156+
0,
157+
0,
158+
0x100,
159+
(0x01000014 | addr),
160+
0,
161+
0,
162+
0,
163+
0x100,
164+
],
165+
)?;
166+
167+
let mut assigned_addresses: Vec<u32> = Vec::new();
168+
for i in 0..MAX_BARS {
169+
if let Some(bar) = endpoint.bar(i.try_into().unwrap(), &pci_config)
170+
{
171+
match bar {
172+
Bar::Io { port } => {
173+
info!("BAR{:x} IO {{port: {:#X}}}", i, port);
174+
assigned_addresses.extend(alloc::vec![
175+
(0x81000014 | addr),
176+
0,
177+
port,
178+
0,
179+
0x100
180+
]);
181+
}
182+
Bar::Memory32 {
183+
address,
184+
size,
185+
prefetchable,
186+
} => {
187+
info!("BAR{:x} Memory32 {{address: {:#X}, size {:#X}, prefetchable: {:?}}}", i, address, size, prefetchable);
188+
assigned_addresses.extend(alloc::vec![
189+
(0x82000010 | addr),
190+
0,
191+
address,
192+
0,
193+
size
194+
]);
195+
}
196+
Bar::Memory64 {
197+
address,
198+
size,
199+
prefetchable,
200+
} => {
201+
info!("BAR{:x} Memory64 {{address: {:#X}, size {:#X}, prefetchable: {:?}}}", i, address, size, prefetchable);
202+
assigned_addresses.extend(alloc::vec![
203+
(0x82000010 | addr),
204+
(address >> 32) as u32,
205+
address as u32,
206+
(size >> 32) as u32,
207+
size as u32
208+
]);
209+
}
210+
}
211+
}
212+
}
213+
fdt.property_array_u32(
214+
"assigned-addresses",
215+
assigned_addresses.as_slice(),
216+
)?;
217+
218+
fdt.end_node(net_node)?;
219+
}
220+
}
221+
}
222+
}
223+
224+
fdt.end_node(pci_node)?;
225+
226+
Ok(self)
227+
}
95228
}
96229
}
97230

0 commit comments

Comments
 (0)