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