1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
// ---------------------------------------------------------------------------
// PCI config space — legacy port-based access (0xCF8 / 0xCFC).
//
// Enumeration walks buses 0-255 × devices 0-31 × functions 0-7. Port I/O
// stays usable after ExitBootServices, so the shell can list devices and
// future drivers (AHCI, network, ...) can find their controllers.
// ---------------------------------------------------------------------------
const CONFIG_ADDRESS: u16 = 0xCF8;
const CONFIG_DATA: u16 = 0xCFC;
#[derive(Debug, Clone, Copy)]
pub struct DeviceInfo {
pub bus: u8,
pub device: u8,
pub func: u8,
pub vendor: u16,
pub device_id: u16,
pub class: u8,
pub subclass: u8,
pub prog_if: u8,
pub header_type: u8,
}
fn outl(port: u16, val: u32) {
unsafe {
core::arch::asm!(
"out dx, eax",
in("dx") port,
in("eax") val,
options(nomem, nostack, preserves_flags)
);
}
}
fn inl(port: u16) -> u32 {
let val: u32;
unsafe {
core::arch::asm!(
"in eax, dx",
out("eax") val,
in("dx") port,
options(nomem, nostack, preserves_flags)
);
}
val
}
/// Read a dword from a device's config space (`reg` must be dword-aligned).
pub fn config_read(bus: u8, device: u8, func: u8, reg: u8) -> u32 {
let addr = 0x8000_0000
| ((bus as u32) << 16)
| ((device as u32) << 11)
| ((func as u32) << 8)
| ((reg as u32) & 0xFC);
outl(CONFIG_ADDRESS, addr);
inl(CONFIG_DATA)
}
/// Enumerate every device/function present on all buses.
pub fn probe() -> alloc::vec::Vec<DeviceInfo> {
let mut out = alloc::vec::Vec::new();
for bus in 0..=255u8 {
for dev in 0..32u8 {
for func in 0..8u8 {
let id = config_read(bus, dev, func, 0);
let vendor = (id & 0xFFFF) as u16;
if vendor == 0xFFFF {
if func == 0 {
break; // nothing on this device number
}
continue;
}
let hdr = config_read(bus, dev, func, 0x0C);
let header_type = (hdr & 0xFF) as u8;
let cc = config_read(bus, dev, func, 0x08);
out.push(DeviceInfo {
bus,
device: dev,
func,
vendor,
device_id: (id >> 16) as u16,
class: (cc >> 24) as u8,
subclass: (cc >> 16) as u8,
prog_if: (cc >> 8) as u8,
header_type,
});
// A non-multifunction device has no functions 1-7.
if func == 0 && header_type & 0x80 == 0 {
break;
}
}
}
}
out
}
/// Short human name for a (class, subclass) pair.
pub fn class_name(class: u8, subclass: u8) -> &'static str {
match (class, subclass) {
(0x00, 0x01) => "VGA-compatible",
(0x00, _) => "Unclassified",
(0x01, 0x01) => "IDE controller",
(0x01, 0x06) => "SATA controller",
(0x01, 0x08) => "NVM controller",
(0x01, _) => "Mass storage",
(0x02, 0x00) => "Ethernet",
(0x02, _) => "Network",
(0x03, 0x00) => "VGA",
(0x03, _) => "Display",
(0x04, _) => "Multimedia",
(0x05, _) => "Memory controller",
(0x06, 0x00) => "Host bridge",
(0x06, 0x01) => "ISA bridge",
(0x06, 0x04) => "PCI-PCI bridge",
(0x06, _) => "Bridge",
(0x08, 0x80) => "PIC",
(0x0C, 0x03) => "USB controller",
(0x0C, 0x05) => "SMBus",
(0x0C, _) => "Serial bus",
(0x11, _) => "Signal processing",
(0xFF, _) => "Unassigned",
_ => "Other",
}
}
|