Skip to content

Commit 511b775

Browse files
committed
Added basic support for PCI bus scanning
This is fairly basic, but it identifies devices and provides the basic functionality necessary to use those devices' PCI APIs. Signed-off-by: SlyMarbo <[email protected]>
1 parent 60463e2 commit 511b775

File tree

4 files changed

+262
-1
lines changed

4 files changed

+262
-1
lines changed

kernel/src/drivers/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//! drivers handles the initialisation of devices, using the
2+
//! driver child modules.
3+
4+
use crate::pci;
5+
6+
/// device_supported is a callback called by the PCI module
7+
/// for each PCI device discovered. If the device is supported
8+
/// by a device driver, device_supported returns true and
9+
/// the device is passed to register_device, so the driver
10+
/// can take ownership.
11+
///
12+
pub fn device_supported(device: &pci::Device) -> bool {
13+
// TODO: once we have device drivers, ask them here.
14+
false
15+
}
16+
17+
/// register_device is a callback called by the PCI module
18+
/// for each PCI device discovered. If the device can be
19+
/// identified as a supported device, the corresponding
20+
/// driver is used to initialise the device.
21+
///
22+
pub fn register_device(device: pci::Device) {
23+
// TODO: once we have device drivers, initialise them here.
24+
}

kernel/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ use lazy_static::lazy_static;
3737
use raw_cpuid::CpuId;
3838
use x86_64::instructions::port::Port;
3939

40+
pub mod drivers;
4041
pub mod gdt;
4142
pub mod interrupts;
4243
pub mod memory;
44+
pub mod pci;
4345
pub mod serial;
4446
pub mod task;
4547
pub mod time;

kernel/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extern crate alloc;
1414

1515
use bootloader::{entry_point, BootInfo};
1616
use core::panic::PanicInfo;
17-
use kernel::{memory, println, time, CPU_ID};
17+
use kernel::{memory, pci, println, time, CPU_ID};
1818

1919
/// This function is called on panic.
2020
#[cfg(not(test))]
@@ -61,6 +61,8 @@ fn kmain() {
6161

6262
unsafe { memory::vmm::debug(memory::kernel_pml4().level_4_table()) };
6363
memory::pmm::debug();
64+
pci::init();
65+
pci::debug();
6466
}
6567

6668
// Testing framework.

kernel/src/pci.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
//! pci is a basic implementation of PCI bus scanning, which will
2+
//! detect and identify the PCI devices available.
3+
4+
use crate::{drivers, println};
5+
use alloc::vec::Vec;
6+
use core::fmt;
7+
use x86_64::instructions::port::Port;
8+
9+
pub const CONFIG_ADDRESS: u16 = 0xcf8;
10+
pub const CONFIG_DATA: u16 = 0xcfc;
11+
12+
pub const VENDOR_ID: u8 = 0x00;
13+
pub const DEVICE_ID: u8 = 0x02;
14+
pub const COMMAND: u8 = 0x04;
15+
pub const SUBCLASS: u8 = 0x0a;
16+
pub const CLASS: u8 = 0x0b;
17+
pub const HEADER_TYPE: u8 = 0x0e;
18+
19+
pub const NONE: u16 = 0xffff;
20+
21+
pub const BAR0: u8 = 0x10;
22+
pub const BAR1: u8 = 0x14;
23+
pub const BAR2: u8 = 0x18;
24+
pub const BAR3: u8 = 0x1c;
25+
pub const BAR4: u8 = 0x20;
26+
pub const BAR5: u8 = 0x24;
27+
28+
pub const SECONDARY_BUS: u8 = 0x19;
29+
30+
pub const TYPE_BRIDGE: u16 = 0x0604;
31+
32+
/// UNKNOWN_DEVICES is the list of PCI devices that have been
33+
/// identified by init but were not claimed by a device driver.
34+
///
35+
static UNKNOWN_DEVICES: spin::Mutex<Vec<Device>> = spin::Mutex::new(Vec::new());
36+
37+
/// init scans PCI busses for devices, populating DEVICES.
38+
///
39+
pub fn init() {
40+
if read_u8(0, 0, 0, HEADER_TYPE) & 0x80 == 0 {
41+
scan_bus(0);
42+
return;
43+
}
44+
45+
let mut found = false;
46+
for func in 0..8 {
47+
if read_u16(0, 0, func, VENDOR_ID) == NONE {
48+
break;
49+
}
50+
51+
found = true;
52+
scan_bus(func);
53+
}
54+
55+
if !found {
56+
return;
57+
}
58+
59+
for bus in 0..=255 {
60+
scan_bus(bus);
61+
}
62+
}
63+
64+
/// debug iterates through the discovered but unsupported
65+
/// PCI devices, printing each device.
66+
///
67+
pub fn debug() {
68+
let devices = UNKNOWN_DEVICES.lock();
69+
for device in devices.iter() {
70+
println!("Found unsupported {}.", device);
71+
}
72+
}
73+
74+
/// Device represents a PCI device.
75+
///
76+
pub struct Device {
77+
bus: u8,
78+
slot: u8,
79+
func: u8,
80+
81+
pub vendor: u16,
82+
pub device: u16,
83+
pub devtype: u16,
84+
}
85+
86+
// set_address sets the PCI slot.
87+
//
88+
fn set_address(bus: u8, slot: u8, func: u8, field: u8) {
89+
let lbus = bus as u32;
90+
let lslot = slot as u32;
91+
let lfunc = func as u32;
92+
let lfield = field as u32;
93+
94+
let address = (lbus << 16) | (lslot << 11) | (lfunc << 8) | (lfield & 0xfc) | 0x80000000;
95+
96+
unsafe {
97+
Port::new(CONFIG_ADDRESS).write(address);
98+
}
99+
}
100+
101+
// The read_X and write_X functions below are fairly
102+
// straightforward. They're all duplicated as methods
103+
// on a device, simply because it would be fiddly and
104+
// tedious to maintain a device as we go along through
105+
// the discovery process.
106+
107+
fn read_u8(bus: u8, slot: u8, func: u8, field: u8) -> u8 {
108+
set_address(bus, slot, func, field);
109+
unsafe { Port::new(CONFIG_DATA + (field as u16 & 3)).read() }
110+
}
111+
112+
fn read_u16(bus: u8, slot: u8, func: u8, field: u8) -> u16 {
113+
set_address(bus, slot, func, field);
114+
unsafe { Port::new(CONFIG_DATA + (field as u16 & 2)).read() }
115+
}
116+
117+
fn read_u32(bus: u8, slot: u8, func: u8, field: u8) -> u32 {
118+
set_address(bus, slot, func, field);
119+
unsafe { Port::new(CONFIG_DATA + (field as u16 & 0)).read() }
120+
}
121+
122+
fn write_u8(bus: u8, slot: u8, func: u8, field: u8, value: u8) {
123+
set_address(bus, slot, func, field);
124+
unsafe { Port::new(CONFIG_DATA).write(value) };
125+
}
126+
127+
fn write_u16(bus: u8, slot: u8, func: u8, field: u8, value: u16) {
128+
set_address(bus, slot, func, field);
129+
unsafe { Port::new(CONFIG_DATA).write(value) };
130+
}
131+
132+
fn write_u32(bus: u8, slot: u8, func: u8, field: u8, value: u32) {
133+
set_address(bus, slot, func, field);
134+
unsafe { Port::new(CONFIG_DATA).write(value) };
135+
}
136+
137+
impl Device {
138+
pub fn read_field_u8(&self, field: u8) -> u8 {
139+
read_u8(self.bus, self.slot, self.func, field)
140+
}
141+
142+
pub fn read_field_u16(&self, field: u8) -> u16 {
143+
read_u16(self.bus, self.slot, self.func, field)
144+
}
145+
146+
pub fn read_field_u32(&self, field: u8) -> u32 {
147+
read_u32(self.bus, self.slot, self.func, field)
148+
}
149+
150+
pub fn write_field_u8(&self, field: u8, value: u8) {
151+
write_u8(self.bus, self.slot, self.func, field, value);
152+
}
153+
154+
pub fn write_field_u16(&self, field: u8, value: u16) {
155+
write_u16(self.bus, self.slot, self.func, field, value);
156+
}
157+
158+
pub fn write_field_u32(&self, field: u8, value: u32) {
159+
write_u32(self.bus, self.slot, self.func, field, value);
160+
}
161+
}
162+
163+
impl fmt::Display for Device {
164+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165+
write!(
166+
f,
167+
"PCI device with vendor={:04x}, device={:04x}, type={:04x}",
168+
self.vendor, self.device, self.devtype
169+
)
170+
}
171+
}
172+
173+
// read_vendor_device returns the vendor and device
174+
// details for the given slot.
175+
//
176+
fn read_vendor_device(bus: u8, slot: u8) -> (u16, u16) {
177+
let data = read_u32(bus, slot, 0, VENDOR_ID);
178+
let vendor = (data & 0xffff) as u16;
179+
let device = (data >> 16) as u16;
180+
181+
(vendor, device)
182+
}
183+
184+
// read_device_type returns the device type of the
185+
// given device.
186+
//
187+
fn read_device_type(bus: u8, slot: u8, func: u8) -> u16 {
188+
let class = read_u8(bus, slot, func, CLASS) as u16;
189+
let subclass = read_u8(bus, slot, func, SUBCLASS) as u16;
190+
(class << 8) | subclass
191+
}
192+
193+
// scan_slot scans a PCI slot for a recognised
194+
// device.
195+
//
196+
fn scan_slot(bus: u8, slot: u8) {
197+
let (vendor, device) = read_vendor_device(bus, slot);
198+
if vendor == NONE {
199+
// Device doesn't exist.
200+
return;
201+
}
202+
203+
let devtype = read_device_type(bus, slot, 0);
204+
205+
let dev = Device {
206+
bus,
207+
slot,
208+
func: 0,
209+
vendor,
210+
device,
211+
devtype,
212+
};
213+
214+
if drivers::device_supported(&dev) {
215+
drivers::register_device(dev);
216+
} else {
217+
UNKNOWN_DEVICES.lock().push(dev);
218+
}
219+
220+
if devtype == TYPE_BRIDGE {
221+
let bus = read_u8(bus, slot, 0, SECONDARY_BUS);
222+
scan_bus(bus);
223+
}
224+
}
225+
226+
// scan_bus scans a PCI bus for a recognised
227+
// device.
228+
//
229+
fn scan_bus(bus: u8) {
230+
for slot in 0..32 {
231+
scan_slot(bus, slot);
232+
}
233+
}

0 commit comments

Comments
 (0)