strat9_kernel/arch/x86_64/
pci.rs1use super::io::{inl, outl};
9use crate::sync::SpinLock;
10use alloc::vec::Vec;
11use core::fmt;
12
13const CONFIG_ADDRESS: u16 = 0xCF8;
15const CONFIG_DATA: u16 = 0xCFC;
17
18static PCI_IO_LOCK: SpinLock<()> = SpinLock::new(());
23
24#[derive(Clone, Copy)]
25struct PciLineQuirk {
26 vendor_id: u16,
27 device_id: u16,
28}
29
30const PCI_IRQ_LINE_ZERO_IF_FF: &[PciLineQuirk] = &[
31 PciLineQuirk {
32 vendor_id: vendor::INTEL,
33 device_id: intel_eth::E1000_82540EM,
34 },
35 PciLineQuirk {
36 vendor_id: vendor::INTEL,
37 device_id: intel_eth::E1000_82545EM,
38 },
39 PciLineQuirk {
40 vendor_id: vendor::INTEL,
41 device_id: intel_eth::E1000E_82574L,
42 },
43 PciLineQuirk {
44 vendor_id: vendor::VIRTIO,
45 device_id: device::VIRTIO_NET,
46 },
47 PciLineQuirk {
48 vendor_id: vendor::VIRTIO,
49 device_id: device::VIRTIO_BLOCK,
50 },
51];
52
53pub mod vendor {
55 pub const VIRTIO: u16 = 0x1AF4;
56 pub const QEMU: u16 = 0x1234;
57 pub const INTEL: u16 = 0x8086;
58 pub const AMD: u16 = 0x1022;
59}
60
61pub mod device {
63 pub const VIRTIO_NET: u16 = 0x1000;
64 pub const VIRTIO_BLOCK: u16 = 0x1001;
65 pub const VIRTIO_CONSOLE: u16 = 0x1003;
66 pub const VIRTIO_RNG: u16 = 0x1005;
67 pub const VIRTIO_GPU: u16 = 0x1050;
68 pub const VIRTIO_INPUT: u16 = 0x1052;
69}
70
71pub mod class {
73 pub const MASS_STORAGE: u8 = 0x01;
74 pub const NETWORK: u8 = 0x02;
75}
76
77pub mod storage_subclass {
79 pub const SCSI: u8 = 0x00;
80 pub const IDE: u8 = 0x01;
81 pub const FLOPPY: u8 = 0x02;
82 pub const IPI: u8 = 0x03;
83 pub const RAID: u8 = 0x04;
84 pub const ATA: u8 = 0x05;
85 pub const SATA: u8 = 0x06;
86 pub const SAS: u8 = 0x07;
87 pub const NVM: u8 = 0x08;
88 pub const OTHER: u8 = 0x80;
89}
90
91pub mod sata_progif {
93 pub const AHCI: u8 = 0x01;
95 pub const VENDOR: u8 = 0x00;
97}
98
99pub mod net_subclass {
101 pub const ETHERNET: u8 = 0x00;
102 pub const OTHER: u8 = 0x80;
103}
104
105pub mod intel_eth {
107 pub const E1000_82540EM: u16 = 0x100E; pub const E1000_82545EM: u16 = 0x100F;
109 pub const E1000E_82574L: u16 = 0x10D3; pub const I210_AT: u16 = 0x1533;
111 pub const I350_AM2: u16 = 0x1521;
112 pub const I350_AM4: u16 = 0x1523;
113 pub const I217_LM: u16 = 0x153A;
114 pub const I211_AT: u16 = 0x1539;
115 pub const I219_LM: u16 = 0x15F9;
116 pub const I219_V: u16 = 0x15FA;
117 pub const I225_LM: u16 = 0x15F2;
118 pub const I225_V: u16 = 0x15F3;
119 pub const I226_LM: u16 = 0x125B;
120 pub const I226_V: u16 = 0x125C;
121}
122
123pub mod config {
125 pub const VENDOR_ID: u8 = 0x00;
126 pub const DEVICE_ID: u8 = 0x02;
127 pub const COMMAND: u8 = 0x04;
128 pub const STATUS: u8 = 0x06;
129 pub const REVISION_ID: u8 = 0x08;
130 pub const PROG_IF: u8 = 0x09;
131 pub const SUBCLASS: u8 = 0x0A;
132 pub const CLASS_CODE: u8 = 0x0B;
133 pub const CACHE_LINE_SIZE: u8 = 0x0C;
134 pub const LATENCY_TIMER: u8 = 0x0D;
135 pub const HEADER_TYPE: u8 = 0x0E;
136 pub const BIST: u8 = 0x0F;
137 pub const BAR0: u8 = 0x10;
138 pub const BAR1: u8 = 0x14;
139 pub const BAR2: u8 = 0x18;
140 pub const BAR3: u8 = 0x1C;
141 pub const BAR4: u8 = 0x20;
142 pub const BAR5: u8 = 0x24;
143 pub const CARDBUS_CIS: u8 = 0x28;
144 pub const SUBSYSTEM_VENDOR_ID: u8 = 0x2C;
145 pub const SUBSYSTEM_ID: u8 = 0x2E;
146 pub const ROM_BAR: u8 = 0x30;
147 pub const CAPABILITIES: u8 = 0x34;
148 pub const INTERRUPT_LINE: u8 = 0x3C;
149 pub const INTERRUPT_PIN: u8 = 0x3D;
150 pub const MIN_GNT: u8 = 0x3E;
151 pub const MAX_LAT: u8 = 0x3F;
152}
153
154pub mod command {
156 pub const IO_SPACE: u16 = 1 << 0;
157 pub const MEMORY_SPACE: u16 = 1 << 1;
158 pub const BUS_MASTER: u16 = 1 << 2;
159 pub const SPECIAL_CYCLES: u16 = 1 << 3;
160 pub const MWI_ENABLE: u16 = 1 << 4;
161 pub const VGA_PALETTE_SNOOP: u16 = 1 << 5;
162 pub const PARITY_ERROR_RESPONSE: u16 = 1 << 6;
163 pub const STEPPING_CONTROL: u16 = 1 << 7;
164 pub const SERR_ENABLE: u16 = 1 << 8;
165 pub const FAST_BACK_TO_BACK: u16 = 1 << 9;
166 pub const INTERRUPT_DISABLE: u16 = 1 << 10;
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum Bar {
172 Io { port: u16 },
173 Memory32 { addr: u32, prefetchable: bool },
174 Memory64 { addr: u64, prefetchable: bool },
175}
176
177#[derive(Clone, Copy, PartialEq, Eq)]
179pub struct PciAddress {
180 pub bus: u8,
181 pub device: u8,
182 pub function: u8,
183}
184
185impl fmt::Debug for PciAddress {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
189 }
190}
191
192impl PciAddress {
193 pub const fn new(bus: u8, device: u8, function: u8) -> Self {
195 Self {
196 bus,
197 device,
198 function,
199 }
200 }
201
202 fn config_address(&self, offset: u8) -> u32 {
204 let bus = self.bus as u32;
205 let device = (self.device as u32) & 0x1F;
206 let function = (self.function as u32) & 0x07;
207 let offset = (offset as u32) & 0xFC;
208
209 0x8000_0000 | (bus << 16) | (device << 11) | (function << 8) | offset
210 }
211}
212
213#[derive(Clone, Copy)]
215pub struct PciDevice {
216 pub address: PciAddress,
217 pub vendor_id: u16,
218 pub device_id: u16,
219 pub class_code: u8,
220 pub subclass: u8,
221 pub prog_if: u8,
222 pub revision: u8,
223 pub header_type: u8,
224 pub interrupt_line: u8,
225 pub interrupt_pin: u8,
226}
227
228impl fmt::Debug for PciDevice {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 write!(
232 f,
233 "PciDevice({:?} ID {:04x}:{:04x} Class {:02x}:{:02x})",
234 self.address, self.vendor_id, self.device_id, self.class_code, self.subclass
235 )
236 }
237}
238
239impl PciDevice {
240 pub fn read_config_u8(&self, offset: u8) -> u8 {
242 let addr = self.address.config_address(offset & !0x03);
243 let shift = (offset & 0x03) * 8;
244
245 let _lock = PCI_IO_LOCK.lock();
246 unsafe {
247 outl(CONFIG_ADDRESS, addr);
248 ((inl(CONFIG_DATA) >> shift) & 0xFF) as u8
249 }
250 }
251
252 pub fn read_config_u16(&self, offset: u8) -> u16 {
254 let addr = self.address.config_address(offset & !0x03);
255 let shift = (offset & 0x02) * 8;
256
257 let _lock = PCI_IO_LOCK.lock();
258 unsafe {
259 outl(CONFIG_ADDRESS, addr);
260 ((inl(CONFIG_DATA) >> shift) & 0xFFFF) as u16
261 }
262 }
263
264 pub fn read_config_u32(&self, offset: u8) -> u32 {
266 let addr = self.address.config_address(offset);
267
268 let _lock = PCI_IO_LOCK.lock();
269 unsafe {
270 outl(CONFIG_ADDRESS, addr);
271 inl(CONFIG_DATA)
272 }
273 }
274
275 pub fn write_config_u8(&self, offset: u8, value: u8) {
277 let addr = self.address.config_address(offset & !0x03);
278 let shift = (offset & 0x03) * 8;
279
280 let _lock = PCI_IO_LOCK.lock();
281 unsafe {
282 outl(CONFIG_ADDRESS, addr);
283 let old = inl(CONFIG_DATA);
284 let mask = !(0xFF << shift);
285 let new = (old & mask) | ((value as u32) << shift);
286 outl(CONFIG_ADDRESS, addr);
287 outl(CONFIG_DATA, new);
288 }
289 }
290
291 pub fn write_config_u16(&self, offset: u8, value: u16) {
293 let addr = self.address.config_address(offset & !0x03);
294 let shift = (offset & 0x02) * 8;
295
296 let _lock = PCI_IO_LOCK.lock();
297 unsafe {
298 outl(CONFIG_ADDRESS, addr);
299 let old = inl(CONFIG_DATA);
300 let mask = !(0xFFFF << shift);
301 let new = (old & mask) | ((value as u32) << shift);
302 outl(CONFIG_ADDRESS, addr);
303 outl(CONFIG_DATA, new);
304 }
305 }
306
307 pub fn write_config_u32(&self, offset: u8, value: u32) {
309 let addr = self.address.config_address(offset);
310
311 let _lock = PCI_IO_LOCK.lock();
312 unsafe {
313 outl(CONFIG_ADDRESS, addr);
314 outl(CONFIG_DATA, value);
315 }
316 }
317
318 pub fn read_bar(&self, bar_index: u8) -> Option<Bar> {
320 if bar_index > 5 {
321 return None;
322 }
323
324 let offset = config::BAR0 + (bar_index * 4);
325 let bar_low = self.read_config_u32(offset);
326
327 if bar_low == 0 {
328 return None;
329 }
330
331 if bar_low & 0x1 != 0 {
333 let port = (bar_low & 0xFFFF_FFFC) as u16;
336 Some(Bar::Io { port })
337 } else {
338 let bar_type = (bar_low >> 1) & 0x3;
340 let prefetchable = (bar_low >> 3) & 0x1 != 0;
341
342 match bar_type {
343 0 => {
344 let addr = bar_low & 0xFFFF_FFF0;
346 Some(Bar::Memory32 { addr, prefetchable })
347 }
348 2 => {
349 if bar_index >= 5 {
351 return None; }
353 let bar_high = self.read_config_u32(offset + 4);
354 let addr = ((bar_high as u64) << 32) | ((bar_low & 0xFFFF_FFF0) as u64);
355 Some(Bar::Memory64 { addr, prefetchable })
356 }
357 _ => None, }
359 }
360 }
361
362 pub fn read_bar_raw(&self, bar_index: u8) -> Option<u64> {
364 match self.read_bar(bar_index) {
365 Some(Bar::Io { port }) => Some(port as u64),
366 Some(Bar::Memory32 { addr, .. }) => Some(addr as u64),
367 Some(Bar::Memory64 { addr, .. }) => Some(addr),
368 None => None,
369 }
370 }
371
372 pub fn enable_bus_master(&self) {
374 let mut cmd = self.read_config_u16(config::COMMAND);
375 cmd |= command::BUS_MASTER;
376 self.write_config_u16(config::COMMAND, cmd);
377 }
378
379 pub fn enable_memory_space(&self) {
381 let mut cmd = self.read_config_u16(config::COMMAND);
382 cmd |= command::MEMORY_SPACE;
383 self.write_config_u16(config::COMMAND, cmd);
384 }
385
386 pub fn enable_io_space(&self) {
388 let mut cmd = self.read_config_u16(config::COMMAND);
389 cmd |= command::IO_SPACE;
390 self.write_config_u16(config::COMMAND, cmd);
391 }
392}
393
394pub struct PciScanner {
396 bus_queue: [u8; 256],
397 queue_head: usize,
398 queue_tail: usize,
399 seen_buses: [bool; 256],
400 device: u8,
401 function: u8,
402}
403
404impl PciScanner {
405 pub fn new() -> Self {
407 let mut s = Self {
408 bus_queue: [0u8; 256],
409 queue_head: 0,
410 queue_tail: 1,
411 seen_buses: [false; 256],
412 device: 0,
413 function: 0,
414 };
415 s.seen_buses[0] = true;
416 s
417 }
418
419 fn enqueue_bus(&mut self, bus: u8) {
420 if !self.seen_buses[bus as usize] && self.queue_tail < 256 {
421 self.seen_buses[bus as usize] = true;
422 self.bus_queue[self.queue_tail] = bus;
423 self.queue_tail += 1;
424 }
425 }
426}
427
428impl Iterator for PciScanner {
429 type Item = PciDevice;
430
431 fn next(&mut self) -> Option<Self::Item> {
432 loop {
433 if self.queue_head >= self.queue_tail {
434 return None;
435 }
436 let bus = self.bus_queue[self.queue_head];
437
438 if self.device >= 32 {
439 self.queue_head += 1;
440 self.device = 0;
441 self.function = 0;
442 continue;
443 }
444
445 let address = PciAddress::new(bus, self.device, self.function);
446 let current_function = self.function;
447
448 self.function += 1;
449 if self.function >= 8 {
450 self.function = 0;
451 self.device += 1;
452 }
453
454 let Some(dev) = probe_device_full(address) else {
455 if current_function == 0 {
456 self.function = 0;
457 self.device += 1;
458 }
459 continue;
460 };
461
462 if dev.header_type & 0x7F == 0x01 {
463 let secondary = {
464 let _lock = PCI_IO_LOCK.lock();
465 unsafe {
466 outl(CONFIG_ADDRESS, address.config_address(0x18));
467 ((inl(CONFIG_DATA) >> 8) & 0xFF) as u8
468 }
469 };
470 self.enqueue_bus(secondary);
471 }
472
473 if current_function == 0 && (dev.header_type & 0x80 == 0) {
474 self.function = 0;
475 self.device += 1;
476 }
477
478 return Some(dev);
479 }
480 }
481}
482
483fn is_absent_vendor(vendor_id: u16) -> bool {
485 vendor_id == 0xFFFF || vendor_id == 0x0000
486}
487
488fn quirk_zero_irq_line(vendor_id: u16, device_id: u16, irq_line: u8) -> u8 {
490 if irq_line != 0xFF {
491 return irq_line;
492 }
493 if PCI_IRQ_LINE_ZERO_IF_FF
494 .iter()
495 .any(|q| q.vendor_id == vendor_id && q.device_id == device_id)
496 {
497 return 0;
498 }
499 0
500}
501
502fn valid_header_type(header_type: u8) -> bool {
504 matches!(header_type & 0x7F, 0x00..=0x02)
505}
506
507fn is_ghost_device(class_code: u8, subclass: u8, prog_if: u8) -> bool {
509 class_code == 0xFF && subclass == 0xFF && prog_if == 0xFF
510}
511
512fn probe_device_full(address: PciAddress) -> Option<PciDevice> {
517 let _lock = PCI_IO_LOCK.lock();
518
519 let word00 = unsafe {
520 outl(CONFIG_ADDRESS, address.config_address(0x00));
521 inl(CONFIG_DATA)
522 };
523 let vendor_id = (word00 & 0xFFFF) as u16;
524 if is_absent_vendor(vendor_id) {
525 return None;
526 }
527 let device_id = (word00 >> 16) as u16;
528 if device_id == 0xFFFF || device_id == 0x0000 {
529 return None;
530 }
531
532 let word08 = unsafe {
533 outl(CONFIG_ADDRESS, address.config_address(0x08));
534 inl(CONFIG_DATA)
535 };
536 let word0c = unsafe {
537 outl(CONFIG_ADDRESS, address.config_address(0x0C));
538 inl(CONFIG_DATA)
539 };
540
541 let header_type = ((word0c >> 16) & 0xFF) as u8;
542 if !valid_header_type(header_type) {
543 return None;
544 }
545
546 let class_code = ((word08 >> 24) & 0xFF) as u8;
547 let subclass = ((word08 >> 16) & 0xFF) as u8;
548 let prog_if = ((word08 >> 8) & 0xFF) as u8;
549 if is_ghost_device(class_code, subclass, prog_if) {
550 return None;
551 }
552
553 let word3c = unsafe {
554 outl(CONFIG_ADDRESS, address.config_address(0x3C));
555 inl(CONFIG_DATA)
556 };
557 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
558
559 Some(PciDevice {
560 address,
561 vendor_id,
562 device_id,
563 class_code,
564 subclass,
565 prog_if,
566 revision: (word08 & 0xFF) as u8,
567 header_type,
568 interrupt_line,
569 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
570 })
571}
572
573pub fn find_device(vendor_id: u16, device_id: u16) -> Option<PciDevice> {
575 let mut cache = PCI_DEVICE_CACHE.lock();
576 if cache.is_none() {
577 *cache = Some(PciScanner::new().collect());
578 }
579 cache.as_ref().and_then(|devs| {
580 devs.iter()
581 .copied()
582 .find(|dev| dev.vendor_id == vendor_id && dev.device_id == device_id)
583 })
584}
585
586pub fn find_virtio_devices() -> Vec<PciDevice> {
588 find_devices_by_vendor(vendor::VIRTIO)
589}
590
591pub fn find_virtio_device(device_id: u16) -> Option<PciDevice> {
593 find_device(vendor::VIRTIO, device_id)
594}
595
596static PCI_DEVICE_CACHE: SpinLock<Option<Vec<PciDevice>>> = SpinLock::new(None);
601
602pub fn all_devices() -> Vec<PciDevice> {
606 let mut cache = PCI_DEVICE_CACHE.lock();
607 if cache.is_none() {
608 *cache = Some(PciScanner::new().collect());
609 }
610 cache.as_ref().cloned().unwrap_or_default()
611}
612
613pub fn find_devices_by_vendor(vendor_id: u16) -> Vec<PciDevice> {
615 all_devices()
616 .into_iter()
617 .filter(|dev| dev.vendor_id == vendor_id)
618 .collect()
619}
620
621pub fn find_devices_by_class(class_code: u8, subclass: u8) -> Vec<PciDevice> {
626 all_devices()
627 .into_iter()
628 .filter(|dev| dev.class_code == class_code && dev.subclass == subclass)
629 .collect()
630}
631
632#[derive(Debug, Clone, Copy, Default)]
636pub struct ProbeCriteria {
637 pub vendor_id: Option<u16>,
638 pub device_id: Option<u16>,
639 pub class_code: Option<u8>,
640 pub subclass: Option<u8>,
641 pub prog_if: Option<u8>,
642}
643
644impl ProbeCriteria {
645 pub const fn any() -> Self {
647 Self {
648 vendor_id: None,
649 device_id: None,
650 class_code: None,
651 subclass: None,
652 prog_if: None,
653 }
654 }
655
656 fn matches(&self, dev: &PciDevice) -> bool {
658 if self.vendor_id.is_some_and(|v| dev.vendor_id != v) {
659 return false;
660 }
661 if self.device_id.is_some_and(|d| dev.device_id != d) {
662 return false;
663 }
664 if self.class_code.is_some_and(|c| dev.class_code != c) {
665 return false;
666 }
667 if self.subclass.is_some_and(|s| dev.subclass != s) {
668 return false;
669 }
670 if self.prog_if.is_some_and(|p| dev.prog_if != p) {
671 return false;
672 }
673 true
674 }
675}
676
677pub fn probe_all(criteria: ProbeCriteria) -> Vec<PciDevice> {
679 all_devices()
680 .into_iter()
681 .filter(|dev| criteria.matches(dev))
682 .collect()
683}
684
685pub fn probe_first(criteria: ProbeCriteria) -> Option<PciDevice> {
687 all_devices().into_iter().find(|dev| criteria.matches(dev))
688}
689
690pub fn invalidate_cache() {
694 *PCI_DEVICE_CACHE.lock() = None;
695}