Nothing to see here, move along meow
1use crate::acpi::mcfg::McfgEntry;
2use crate::mem::phys::BitmapFrameAllocator;
3use crate::static_vec::StaticVec;
4use x86_64::structures::paging::OffsetPageTable;
5
6use super::config::{self, EcamAddress};
7
8pub const MAX_PCI_DEVICES: usize = 64;
9
10#[repr(C)]
11#[derive(Debug, Clone, Copy)]
12pub struct BlockedRange {
13 pub start: u16,
14 pub end: u16,
15}
16
17#[allow(dead_code)]
18#[derive(Debug, Clone, Copy)]
19pub struct MsixCapInfo {
20 pub cap_offset: u16,
21 pub table_size: u16,
22 pub table_bir: u8,
23 pub table_offset: u32,
24 pub pba_bir: u8,
25 pub pba_offset: u32,
26}
27
28#[allow(dead_code)]
29#[repr(C)]
30#[derive(Debug, Clone, Copy)]
31pub struct PciDeviceInfo {
32 pub bus: u8,
33 pub device: u8,
34 pub function: u8,
35 pub vendor_id: u16,
36 pub device_id: u16,
37 pub class_code: u8,
38 pub subclass: u8,
39 pub prog_if: u8,
40 pub header_type: u8,
41 pub interrupt_line: u8,
42 pub interrupt_pin: u8,
43 pub bars: [BarInfo; 6],
44 pub blocked_config_ranges: [Option<BlockedRange>; 4],
45 pub msix_cap: Option<MsixCapInfo>,
46}
47
48#[allow(dead_code)]
49#[repr(C)]
50#[derive(Debug, Clone, Copy)]
51pub enum BarInfo {
52 None,
53 Memory {
54 phys_base: u64,
55 size: u64,
56 is_64bit: bool,
57 prefetchable: bool,
58 },
59 Io {
60 port_base: u16,
61 size: u16,
62 },
63}
64
65impl BarInfo {
66 pub fn to_wire(self) -> lancer_core::pci::BarInfoWire {
67 match self {
68 BarInfo::None => lancer_core::pci::BarInfoWire::zeroed(),
69 BarInfo::Memory {
70 phys_base,
71 size,
72 is_64bit,
73 prefetchable,
74 } => {
75 let flags = match is_64bit {
76 true => lancer_core::pci::BAR_FLAG_64BIT,
77 false => 0,
78 } | match prefetchable {
79 true => lancer_core::pci::BAR_FLAG_PREFETCHABLE,
80 false => 0,
81 };
82 lancer_core::pci::BarInfoWire {
83 tag: lancer_core::pci::BAR_TAG_MEMORY,
84 flags,
85 io_port_base: 0,
86 io_size: 0,
87 _reserved: [0; 2],
88 mem_phys_base: phys_base,
89 mem_size: size,
90 }
91 }
92 BarInfo::Io { port_base, size } => lancer_core::pci::BarInfoWire {
93 tag: lancer_core::pci::BAR_TAG_IO,
94 flags: 0,
95 io_port_base: port_base,
96 io_size: size,
97 _reserved: [0; 2],
98 mem_phys_base: 0,
99 mem_size: 0,
100 },
101 }
102 }
103}
104
105impl PciDeviceInfo {
106 pub fn devfn(&self) -> u8 {
107 (self.device & 0x1F) << 3 | (self.function & 0x07)
108 }
109
110 pub fn to_wire(self) -> lancer_core::pci::PciDeviceInfoWire {
111 let bars = [
112 self.bars[0].to_wire(),
113 self.bars[1].to_wire(),
114 self.bars[2].to_wire(),
115 self.bars[3].to_wire(),
116 self.bars[4].to_wire(),
117 self.bars[5].to_wire(),
118 ];
119 let blocked_config_ranges = [
120 self.blocked_config_ranges[0].map_or(
121 lancer_core::pci::BlockedRangeWire::zeroed(),
122 |r| lancer_core::pci::BlockedRangeWire {
123 start: r.start,
124 end: r.end,
125 },
126 ),
127 self.blocked_config_ranges[1].map_or(
128 lancer_core::pci::BlockedRangeWire::zeroed(),
129 |r| lancer_core::pci::BlockedRangeWire {
130 start: r.start,
131 end: r.end,
132 },
133 ),
134 self.blocked_config_ranges[2].map_or(
135 lancer_core::pci::BlockedRangeWire::zeroed(),
136 |r| lancer_core::pci::BlockedRangeWire {
137 start: r.start,
138 end: r.end,
139 },
140 ),
141 self.blocked_config_ranges[3].map_or(
142 lancer_core::pci::BlockedRangeWire::zeroed(),
143 |r| lancer_core::pci::BlockedRangeWire {
144 start: r.start,
145 end: r.end,
146 },
147 ),
148 ];
149 lancer_core::pci::PciDeviceInfoWire {
150 bus: self.bus,
151 device: self.device,
152 function: self.function,
153 class_code: self.class_code,
154 subclass: self.subclass,
155 prog_if: self.prog_if,
156 header_type: self.header_type,
157 interrupt_line: self.interrupt_line,
158 interrupt_pin: self.interrupt_pin,
159 _pad0: 0,
160 vendor_id: self.vendor_id,
161 device_id: self.device_id,
162 _pad1: [0; 2],
163 bars,
164 blocked_config_ranges,
165 }
166 }
167}
168
169pub type DeviceTable = StaticVec<PciDeviceInfo, MAX_PCI_DEVICES>;
170
171pub static DEVICE_TABLE: spin::Mutex<DeviceTable> = spin::Mutex::new(StaticVec::new());
172
173pub fn enumerate_segment(
174 segment: &McfgEntry,
175 mapper: &mut OffsetPageTable,
176 allocator: &mut BitmapFrameAllocator,
177 hhdm_offset: u64,
178 table: &mut DeviceTable,
179) {
180 (segment.start_bus..=segment.end_bus).for_each(|bus| {
181 (0u8..32).for_each(|dev| {
182 let addr = match EcamAddress::new(segment.base_address, bus, dev, 0) {
183 Some(a) => a,
184 None => return,
185 };
186 if config::ensure_ecam_mapped(addr, mapper, allocator, hhdm_offset).is_err() {
187 return;
188 }
189
190 let vendor = config::read_config_u16(addr, 0x00);
191 match vendor {
192 0xFFFF => {}
193 _ => {
194 let header_type = (config::read_config_u32(addr, 0x0C) >> 16) as u8;
195 let multifunction = header_type & 0x80 != 0;
196 let func_limit = match multifunction {
197 true => 8u8,
198 false => 1u8,
199 };
200
201 (0..func_limit).for_each(|func| {
202 let faddr = match EcamAddress::new(segment.base_address, bus, dev, func) {
203 Some(a) => a,
204 None => return,
205 };
206 match func {
207 0 => {}
208 _ => {
209 if config::ensure_ecam_mapped(faddr, mapper, allocator, hhdm_offset)
210 .is_err()
211 {
212 return;
213 }
214 }
215 }
216 probe_function(faddr, table);
217 });
218 }
219 }
220 });
221 });
222}
223
224const PCI_CAP_MSI: u8 = 0x05;
225const PCI_CAP_MSIX: u8 = 0x11;
226
227struct CapScanResult {
228 blocked: [Option<BlockedRange>; 4],
229 msix: Option<MsixCapInfo>,
230}
231
232fn scan_capabilities(addr: EcamAddress) -> CapScanResult {
233 let mut result = CapScanResult {
234 blocked: [None; 4],
235 msix: None,
236 };
237 let range_idx = 0usize;
238
239 let status_cmd = config::read_config_u32(addr, 0x04);
240 let status = (status_cmd >> 16) as u16;
241 if status & 0x10 == 0 {
242 return result;
243 }
244
245 let cap_ptr_word = config::read_config_u32(addr, 0x34);
246 let first_ptr = (cap_ptr_word & 0xFC) as u16;
247
248 (0..48u16).fold((first_ptr, range_idx), |(ptr, ri), _| match ptr {
249 0 => (0, ri),
250 _ if ri >= 4 => (0, ri),
251 _ => {
252 let header = config::read_config_u32(addr, ptr);
253 let cap_id = (header & 0xFF) as u8;
254 let next = ((header >> 8) & 0xFC) as u16;
255 let new_ri = match cap_id {
256 PCI_CAP_MSI => {
257 let msg_ctrl = config::read_config_u16(addr, ptr + 2);
258 let is_64bit = (msg_ctrl >> 7) & 1 != 0;
259 let has_pvmask = (msg_ctrl >> 8) & 1 != 0;
260 let cap_size: u16 = match (is_64bit, has_pvmask) {
261 (false, false) => 10,
262 (false, true) => 14,
263 (true, false) => 14,
264 (true, true) => 20,
265 };
266 result.blocked[ri] = Some(BlockedRange {
267 start: ptr,
268 end: ptr + cap_size,
269 });
270 ri + 1
271 }
272 PCI_CAP_MSIX => {
273 result.blocked[ri] = Some(BlockedRange {
274 start: ptr,
275 end: ptr + 12,
276 });
277
278 let msg_ctrl = config::read_config_u16(addr, ptr + 2);
279 let table_size = (msg_ctrl & 0x7FF) + 1;
280
281 let table_word = config::read_config_u32(addr, ptr + 4);
282 let table_bir = (table_word & 0x7) as u8;
283 let table_offset = table_word & !0x7;
284
285 let pba_word = config::read_config_u32(addr, ptr + 8);
286 let pba_bir = (pba_word & 0x7) as u8;
287 let pba_offset = pba_word & !0x7;
288
289 result.msix = Some(MsixCapInfo {
290 cap_offset: ptr,
291 table_size,
292 table_bir,
293 table_offset,
294 pba_bir,
295 pba_offset,
296 });
297
298 ri + 1
299 }
300 _ => ri,
301 };
302
303 (next, new_ri)
304 }
305 });
306
307 result
308}
309
310fn probe_function(addr: EcamAddress, table: &mut DeviceTable) {
311 let vendor_id = config::read_config_u16(addr, 0x00);
312 match vendor_id {
313 0xFFFF => {}
314 _ => {
315 let device_id = config::read_config_u16(addr, 0x02);
316 let class_rev = config::read_config_u32(addr, 0x08);
317 let class_code = (class_rev >> 24) as u8;
318 let subclass = (class_rev >> 16) as u8;
319 let prog_if = (class_rev >> 8) as u8;
320
321 let header_word = config::read_config_u32(addr, 0x0C);
322 let header_type = ((header_word >> 16) as u8) & 0x7F;
323
324 let (interrupt_line, interrupt_pin) = match header_type {
325 0 => {
326 let irq_word = config::read_config_u32(addr, 0x3C);
327 (irq_word as u8, (irq_word >> 8) as u8)
328 }
329 _ => (0, 0),
330 };
331
332 let bars = match header_type {
333 0 => decode_bars(addr, 6),
334 1 => {
335 let mut b = [BarInfo::None; 6];
336 let partial = decode_bars(addr, 2);
337 b[0] = partial[0];
338 b[1] = partial[1];
339 b
340 }
341 _ => [BarInfo::None; 6],
342 };
343
344 let cap_scan = scan_capabilities(addr);
345
346 crate::kprintln!(
347 " PCI: {:02x}:{:02x}.{} vendor={:04x} device={:04x} class={:02x}/{:02x}",
348 addr.bus,
349 addr.device(),
350 addr.function(),
351 vendor_id,
352 device_id,
353 class_code,
354 subclass,
355 );
356
357 let info = PciDeviceInfo {
358 bus: addr.bus,
359 device: addr.device(),
360 function: addr.function(),
361 vendor_id,
362 device_id,
363 class_code,
364 subclass,
365 prog_if,
366 header_type,
367 interrupt_line,
368 interrupt_pin,
369 bars,
370 blocked_config_ranges: cap_scan.blocked,
371 msix_cap: cap_scan.msix,
372 };
373
374 match table.push(info) {
375 Ok(()) => {}
376 Err(_) => {
377 crate::kprintln!(
378 " PCI: device table full, dropping {:02x}:{:02x}.{}",
379 addr.bus,
380 addr.device(),
381 addr.function(),
382 );
383 }
384 }
385 }
386 }
387}
388
389fn decode_bars(addr: EcamAddress, count: usize) -> [BarInfo; 6] {
390 debug_assert!(count <= 6);
391 let count = count.min(6);
392 let mut bars = [BarInfo::None; 6];
393 let mut idx = 0usize;
394
395 let saved_cmd = config::read_config_u16(addr, 0x04);
396 config::write_config_u16(addr, 0x04, saved_cmd & !0x03u16);
397
398 core::iter::from_fn(|| match idx < count {
399 true => {
400 let current = idx;
401 let offset = 0x10 + (current as u16) * 4;
402
403 let original = config::read_config_u32(addr, offset);
404 match original {
405 0 => {
406 idx += 1;
407 Some((current, BarInfo::None))
408 }
409 _ => {
410 let is_io = original & 1 != 0;
411 match is_io {
412 true => {
413 config::write_config_u32(addr, offset, 0xFFFF_FFFF);
414 let sizing = config::read_config_u32(addr, offset);
415 config::write_config_u32(addr, offset, original);
416
417 let io_mask = sizing & !0x3u32;
418 let size = (!io_mask).wrapping_add(1) as u16;
419 let port_base = (original & !0x3) as u16;
420
421 idx += 1;
422 Some((current, BarInfo::Io { port_base, size }))
423 }
424 false => {
425 let is_64bit = (original >> 1) & 0x3 == 0x2;
426 let prefetchable = (original >> 3) & 1 != 0;
427
428 config::write_config_u32(addr, offset, 0xFFFF_FFFF);
429 let sizing_lo = config::read_config_u32(addr, offset);
430 config::write_config_u32(addr, offset, original);
431
432 let (phys_base, size) = match is_64bit {
433 true if current + 1 < count => {
434 let hi_offset = offset + 4;
435 let original_hi = config::read_config_u32(addr, hi_offset);
436 config::write_config_u32(addr, hi_offset, 0xFFFF_FFFF);
437 let sizing_hi = config::read_config_u32(addr, hi_offset);
438 config::write_config_u32(addr, hi_offset, original_hi);
439
440 let base =
441 ((original_hi as u64) << 32) | ((original & !0xF) as u64);
442 let sizing_full =
443 ((sizing_hi as u64) << 32) | ((sizing_lo & !0xF) as u64);
444 let sz = (!sizing_full).wrapping_add(1);
445
446 idx += 2;
447 (base, sz)
448 }
449 _ => {
450 let base = (original & !0xF) as u64;
451 let mask = sizing_lo & !0xF;
452 let sz = (!(mask as u64)).wrapping_add(1) & 0xFFFF_FFFF;
453
454 idx += match is_64bit {
455 true => 2,
456 false => 1,
457 };
458 (base, sz)
459 }
460 };
461
462 Some((
463 current,
464 BarInfo::Memory {
465 phys_base,
466 size,
467 is_64bit,
468 prefetchable,
469 },
470 ))
471 }
472 }
473 }
474 }
475 }
476 false => None,
477 })
478 .for_each(|(i, bar)| {
479 bars[i] = bar;
480 });
481
482 config::write_config_u16(addr, 0x04, saved_cmd);
483
484 bars
485}