1use 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
24struct EcamRegion {
26 start_bus: u8,
27 end_bus: u8,
28 base_virt: usize,
29}
30
31static ECAM_REGIONS: SpinLock<Vec<EcamRegion>> = SpinLock::new(Vec::new());
34
35#[inline]
44unsafe fn ecam_read32(ecam_base: usize, bus: u8, device: u8, function: u8, offset: u16) -> u32 {
45 let addr = ecam_base
46 + ((bus as usize) << 20)
47 + ((device as usize) << 15)
48 + ((function as usize) << 12)
49 + ((offset as usize) & !0x03);
50 core::ptr::read_volatile(addr as *const u32)
51}
52
53#[inline]
55unsafe fn ecam_write32(ecam_base: usize, bus: u8, device: u8, function: u8, offset: u16, val: u32) {
56 let addr = ecam_base
57 + ((bus as usize) << 20)
58 + ((device as usize) << 15)
59 + ((function as usize) << 12)
60 + ((offset as usize) & !0x03);
61 core::ptr::write_volatile(addr as *mut u32, val);
62}
63
64#[inline]
66unsafe fn ecam_read8(ecam_base: usize, bus: u8, device: u8, function: u8, offset: u16) -> u8 {
67 let dword = ecam_read32(ecam_base, bus, device, function, offset & !0x03);
68 ((dword >> ((offset & 0x3) * 8)) & 0xFF) as u8
69}
70
71fn device_name(vendor: u16, device: u16) -> Option<&'static str> {
77 match (vendor, device) {
78 (0x8086, 0x100E) => Some("Intel E1000 (QEMU)"),
80 (0x8086, 0x100F) => Some("Intel E1000 (QEMU)"),
81 (0x8086, 0x10D3) => Some("Intel E1000e (QEMU)"),
82 (0x8086, 0x153A) => Some("Intel I217-LM"),
83 (0x8086, 0x15F9) => Some("Intel I219-LM"),
84 (0x8086, 0x15FA) => Some("Intel I219-V"),
85 (0x8086, 0x15F2) => Some("Intel I225-LM"),
86 (0x8086, 0x15F3) => Some("Intel I225-V"),
87 (0x8086, 0x125B) => Some("Intel I226-LM"),
88 (0x8086, 0x125C) => Some("Intel I226-V"),
89 (0x1AF4, 0x1000) => Some("VirtIO Net"),
91 (0x1AF4, 0x1001) => Some("VirtIO Block"),
92 (0x1AF4, 0x1003) => Some("VirtIO Console"),
93 (0x1AF4, 0x1005) => Some("VirtIO RNG"),
94 (0x1AF4, 0x1050) => Some("VirtIO GPU"),
95 (0x1AF4, 0x1052) => Some("VirtIO Input"),
96 (0x1234, 0x11E9) => Some("QEMU VGA"),
98 _ => None,
99 }
100}
101
102fn class_name(class: u8, subclass: u8) -> Option<&'static str> {
104 match (class, subclass) {
105 (0x00, 0x00) => Some("Legacy Device"),
106 (0x00, 0x01) => Some("VGA-Compatible Device"),
107 (0x01, 0x00) => Some("SCSI Controller"),
108 (0x01, 0x01) => Some("IDE Controller"),
109 (0x01, 0x06) => Some("SATA Controller"),
110 (0x01, 0x08) => Some("NVMe Controller"),
111 (0x02, 0x00) => Some("Ethernet Controller"),
112 (0x02, 0x80) => Some("Network Controller"),
113 (0x03, 0x00) => Some("VGA Controller"),
114 (0x03, 0x02) => Some("3D Controller"),
115 (0x04, 0x00) => Some("Multimedia Video"),
116 (0x04, 0x03) => Some("Audio Device"),
117 (0x06, 0x00) => Some("Host Bridge"),
118 (0x06, 0x01) => Some("ISA Bridge"),
119 (0x06, 0x04) => Some("PCI-to-PCI Bridge"),
120 (0x0C, 0x03) => Some("USB Controller"),
121 (0x08, 0x00) => Some("I20"),
122 _ => None,
123 }
124}
125
126#[derive(Clone, Copy)]
127struct PciLineQuirk {
128 vendor_id: u16,
129 device_id: u16,
130}
131
132const PCI_IRQ_LINE_ZERO_IF_FF: &[PciLineQuirk] = &[
133 PciLineQuirk {
134 vendor_id: vendor::INTEL,
135 device_id: intel_eth::E1000_82540EM,
136 },
137 PciLineQuirk {
138 vendor_id: vendor::INTEL,
139 device_id: intel_eth::E1000_82545EM,
140 },
141 PciLineQuirk {
142 vendor_id: vendor::INTEL,
143 device_id: intel_eth::E1000E_82574L,
144 },
145 PciLineQuirk {
146 vendor_id: vendor::VIRTIO,
147 device_id: device::VIRTIO_NET,
148 },
149 PciLineQuirk {
150 vendor_id: vendor::VIRTIO,
151 device_id: device::VIRTIO_BLOCK,
152 },
153];
154
155pub mod vendor {
157 pub const VIRTIO: u16 = 0x1AF4;
158 pub const QEMU: u16 = 0x1234;
159 pub const INTEL: u16 = 0x8086;
160 pub const AMD: u16 = 0x1022;
161 pub const AMD_DISPLAY: u16 = 0x1002;
163}
164
165pub mod device {
167 pub const VIRTIO_NET: u16 = 0x1000;
168 pub const VIRTIO_BLOCK: u16 = 0x1001;
169 pub const VIRTIO_CONSOLE: u16 = 0x1003;
170 pub const VIRTIO_RNG: u16 = 0x1005;
171 pub const VIRTIO_GPU: u16 = 0x1050;
172 pub const VIRTIO_INPUT: u16 = 0x1052;
173}
174
175pub mod class {
177 pub const MASS_STORAGE: u8 = 0x01;
178 pub const NETWORK: u8 = 0x02;
179}
180
181pub mod storage_subclass {
183 pub const SCSI: u8 = 0x00;
184 pub const IDE: u8 = 0x01;
185 pub const FLOPPY: u8 = 0x02;
186 pub const IPI: u8 = 0x03;
187 pub const RAID: u8 = 0x04;
188 pub const ATA: u8 = 0x05;
189 pub const SATA: u8 = 0x06;
190 pub const SAS: u8 = 0x07;
191 pub const NVM: u8 = 0x08;
192 pub const OTHER: u8 = 0x80;
193}
194
195pub mod sata_progif {
197 pub const AHCI: u8 = 0x01;
199 pub const VENDOR: u8 = 0x00;
201}
202
203pub mod net_subclass {
205 pub const ETHERNET: u8 = 0x00;
206 pub const OTHER: u8 = 0x80;
207}
208
209pub mod intel_eth {
211 pub const E1000_82540EM: u16 = 0x100E; pub const E1000_82545EM: u16 = 0x100F;
213 pub const E1000E_82574L: u16 = 0x10D3; pub const I210_AT: u16 = 0x1533;
215 pub const I350_AM2: u16 = 0x1521;
216 pub const I350_AM4: u16 = 0x1523;
217 pub const I217_LM: u16 = 0x153A;
218 pub const I211_AT: u16 = 0x1539;
219 pub const I219_LM: u16 = 0x15F9;
220 pub const I219_V: u16 = 0x15FA;
221 pub const I225_LM: u16 = 0x15F2;
222 pub const I225_V: u16 = 0x15F3;
223 pub const I226_LM: u16 = 0x125B;
224 pub const I226_V: u16 = 0x125C;
225}
226
227pub mod config {
229 pub const VENDOR_ID: u8 = 0x00;
230 pub const DEVICE_ID: u8 = 0x02;
231 pub const COMMAND: u8 = 0x04;
232 pub const STATUS: u8 = 0x06;
233 pub const REVISION_ID: u8 = 0x08;
234 pub const PROG_IF: u8 = 0x09;
235 pub const SUBCLASS: u8 = 0x0A;
236 pub const CLASS_CODE: u8 = 0x0B;
237 pub const CACHE_LINE_SIZE: u8 = 0x0C;
238 pub const LATENCY_TIMER: u8 = 0x0D;
239 pub const HEADER_TYPE: u8 = 0x0E;
240 pub const BIST: u8 = 0x0F;
241 pub const BAR0: u8 = 0x10;
242 pub const BAR1: u8 = 0x14;
243 pub const BAR2: u8 = 0x18;
244 pub const BAR3: u8 = 0x1C;
245 pub const BAR4: u8 = 0x20;
246 pub const BAR5: u8 = 0x24;
247 pub const CARDBUS_CIS: u8 = 0x28;
248 pub const SUBSYSTEM_VENDOR_ID: u8 = 0x2C;
249 pub const SUBSYSTEM_ID: u8 = 0x2E;
250 pub const ROM_BAR: u8 = 0x30;
251 pub const CAPABILITIES: u8 = 0x34;
252 pub const INTERRUPT_LINE: u8 = 0x3C;
253 pub const INTERRUPT_PIN: u8 = 0x3D;
254 pub const MIN_GNT: u8 = 0x3E;
255 pub const MAX_LAT: u8 = 0x3F;
256 pub const CAPABILITIES_PTR: u8 = 0x34;
257}
258
259pub mod cap_id {
261 pub const MSI: u8 = 0x05;
263 pub const MSIX: u8 = 0x11;
265}
266
267pub mod ext_cap_id {
269 pub const AER: u16 = 0x0001;
271 pub const VC: u16 = 0x0002;
273 pub const DSN: u16 = 0x0003;
275 pub const PB: u16 = 0x0004;
277 pub const RLD: u16 = 0x0005;
279 pub const RCLD: u16 = 0x0006;
281 pub const MRIOV: u16 = 0x0008;
283 pub const SRIOV: u16 = 0x0010;
285 pub const RBAR: u16 = 0x0015;
287 pub const DPA: u16 = 0x0016;
289 pub const TPH: u16 = 0x0017;
291 pub const LTR: u16 = 0x0018;
293 pub const SEC_PCIE: u16 = 0x0019;
295 pub const PMUX: u16 = 0x001A;
297 pub const PASID: u16 = 0x001B;
299 pub const LNR: u16 = 0x001C;
301 pub const ATS: u16 = 0x001D;
303 pub const PRI: u16 = 0x001E;
305 pub const PASID_EXT: u16 = 0x0020;
307 pub const SVM: u16 = 0x0021;
309 pub const L1_PM: u16 = 0x0022;
311 pub const PTM: u16 = 0x0023;
313 pub const TUNNEL: u16 = 0x0024;
315 pub const ACS: u16 = 0x0025;
317 pub const ARI: u16 = 0x0026;
319 pub const ATS_EXT: u16 = 0x0027;
321 pub const SRIOV_EXT: u16 = 0x0028;
323 pub const PF_SRIOV: u16 = 0x0029;
325}
326
327pub mod sriov_cap {
329 pub const CAP: u8 = 0x00;
331 pub const CONTROL: u8 = 0x02;
333 pub const STATUS: u8 = 0x04;
335 pub const PCIE_CAP: u8 = 0x08;
337 pub const ARI_CAP: u8 = 0x0C;
339 pub const INITIAL_VF: u8 = 0x10;
341 pub const TOTAL_VF: u8 = 0x12;
343 pub const NUM_VF: u8 = 0x14;
345 pub const FDL: u8 = 0x16;
347 pub const SYS_PAGE_SIZE: u8 = 0x18;
349 pub const BAR0_OFFSET: u8 = 0x1C;
351 pub const BAR1_OFFSET: u8 = 0x20;
353 pub const BAR2_OFFSET: u8 = 0x24;
355 pub const BAR3_OFFSET: u8 = 0x28;
357 pub const BAR4_OFFSET: u8 = 0x2C;
359 pub const BAR5_OFFSET: u8 = 0x30;
361 pub const VF_MIGRATION_STATE: u8 = 0x3C;
363}
364
365pub mod sriov_ctrl {
367 pub const ENABLE: u16 = 1 << 0;
369 pub const MSE: u16 = 1 << 1;
371 pub const ARI_CAP: u16 = 1 << 2;
373 pub const IMS_ENABLE: u16 = 1 << 4;
375 pub const PFARI_CAP: u16 = 1 << 5;
377 pub const VFARI_CAP: u16 = 1 << 6;
379 pub const VF_MIGRATION: u16 = 1 << 7;
381}
382
383pub mod sriov_status {
385 pub const VF_MEMORY_SPACE: u16 = 1 << 0;
387 pub const VF_ARI_CAP: u16 = 1 << 1;
389 pub const PF_ARI_CAP: u16 = 1 << 2;
391 pub const VF_MIGRATION_STATUS: u16 = 1 << 3;
393}
394
395pub mod aer_cap {
397 pub const CAP: u8 = 0x00;
399 pub const UNCERR_STATUS: u8 = 0x04;
401 pub const UNCERR_MASK: u8 = 0x08;
403 pub const UNCERR_SEVERITY: u8 = 0x0C;
405 pub const CORERR_STATUS: u8 = 0x10;
407 pub const CORERR_MASK: u8 = 0x14;
409 pub const ERR_CAP: u8 = 0x18;
411 pub const HEADER_LOG: u8 = 0x1C;
413}
414
415pub fn walk_ext_capabilities(dev: &PciDevice, cap_ptr: u16) -> Vec<(u16, u16)> {
419 let mut caps = Vec::new();
420 let mut offset = cap_ptr;
421
422 while offset >= 0x100 && offset < 0xFFF {
423 let dword = match dev.read_config_u32_ext(offset) {
424 Some(v) => v,
425 None => break,
426 };
427 let cap_id = (dword & 0xFFFF) as u16;
428 let next_ptr = ((dword >> 20) & 0xFFC) as u16; if cap_id == 0 {
431 break; }
433
434 caps.push((cap_id, offset));
435
436 if next_ptr == 0 || next_ptr == offset {
437 break; }
439 offset = next_ptr;
440 }
441
442 caps
443}
444
445pub fn find_ext_capability(dev: &PciDevice, cap_id: u16) -> Option<u16> {
450 if ECAM_REGIONS.lock().is_empty() {
451 return None; }
453
454 for (found_id, offset) in walk_ext_capabilities(dev, 0x100) {
455 if found_id == cap_id {
456 return Some(offset);
457 }
458 }
459 None
460}
461
462pub fn read_sriov_info(dev: &PciDevice) -> Option<SriovInfo> {
466 let cap_offset = find_ext_capability(dev, ext_cap_id::SRIOV)?;
467 let control = dev.read_config_u32_ext(cap_offset + sriov_cap::CONTROL as u16)? as u16;
468 let status = dev.read_config_u32_ext(cap_offset + sriov_cap::STATUS as u16)? as u16;
469 let initial_vf = dev.read_config_u32_ext(cap_offset + sriov_cap::INITIAL_VF as u16)? as u16;
470 let total_vf = dev.read_config_u32_ext(cap_offset + sriov_cap::TOTAL_VF as u16)? as u16;
471 let num_vf = dev.read_config_u32_ext(cap_offset + sriov_cap::NUM_VF as u16)? as u16;
472
473 Some(SriovInfo {
474 cap_offset,
475 control,
476 status,
477 initial_vf,
478 total_vf,
479 num_vf,
480 enabled: (control & sriov_ctrl::ENABLE) != 0,
481 })
482}
483
484#[derive(Debug, Clone, Copy)]
486pub struct SriovInfo {
487 pub cap_offset: u16,
489 pub control: u16,
491 pub status: u16,
493 pub initial_vf: u16,
495 pub total_vf: u16,
497 pub num_vf: u16,
499 pub enabled: bool,
501}
502
503pub fn read_aer_info(dev: &PciDevice) -> Option<AerInfo> {
505 let cap_offset = find_ext_capability(dev, ext_cap_id::AER)?;
506 let uncerr_status = dev.read_config_u32_ext(cap_offset + aer_cap::UNCERR_STATUS as u16)?;
507 let uncerr_mask = dev.read_config_u32_ext(cap_offset + aer_cap::UNCERR_MASK as u16)?;
508 let corerr_status = dev.read_config_u32_ext(cap_offset + aer_cap::CORERR_STATUS as u16)?;
509 let corerr_mask = dev.read_config_u32_ext(cap_offset + aer_cap::CORERR_MASK as u16)?;
510 let err_cap = dev.read_config_u32_ext(cap_offset + aer_cap::ERR_CAP as u16)?;
511
512 Some(AerInfo {
513 cap_offset,
514 uncerr_status,
515 uncerr_mask,
516 corerr_status,
517 corerr_mask,
518 first_error_pointer: ((err_cap >> 24) & 0x1F) as u8,
519 ecrc_error_capable: (err_cap & 0x01) != 0,
520 ecrc_generate_capable: (err_cap & 0x02) != 0,
521 })
522}
523
524#[derive(Debug, Clone, Copy)]
526pub struct AerInfo {
527 pub cap_offset: u16,
529 pub uncerr_status: u32,
531 pub uncerr_mask: u32,
533 pub corerr_status: u32,
535 pub corerr_mask: u32,
537 pub first_error_pointer: u8,
539 pub ecrc_error_capable: bool,
541 pub ecrc_generate_capable: bool,
543}
544
545pub mod msi_cap {
547 pub const CONTROL: u8 = 0x02;
550 pub const ADDR_LOW: u8 = 0x04;
552 pub const ADDR_HIGH: u8 = 0x08;
554 pub const DATA: u8 = 0x08;
558}
559
560pub mod msi_ctrl {
562 pub const ENABLE: u16 = 1 << 0;
564 pub const MME_MASK: u16 = 0b111 << 4;
566 pub const MMC_MASK: u16 = 0b111 << 1;
568 pub const ADDR64: u16 = 1 << 7;
570 pub const PV_MASK: u16 = 1 << 8;
572}
573
574pub mod msix_cap {
576 pub const CONTROL: u8 = 0x02;
579 pub const TABLE: u8 = 0x04;
581 pub const PBA: u8 = 0x08;
583}
584
585pub mod msix_ctrl {
587 pub const ENABLE: u16 = 1 << 15;
589 pub const FUNC_MASK: u16 = 1 << 14;
591 pub const TABLE_SIZE_MASK: u16 = (1 << 11) - 1;
593}
594
595pub const MSI_ADDR_BASE: u32 = 0xFEE0_0000;
605pub const MSI_ADDR_DEST_SHIFT: u32 = 12;
606
607pub mod command {
609 pub const IO_SPACE: u16 = 1 << 0;
610 pub const MEMORY_SPACE: u16 = 1 << 1;
611 pub const BUS_MASTER: u16 = 1 << 2;
612 pub const SPECIAL_CYCLES: u16 = 1 << 3;
613 pub const MWI_ENABLE: u16 = 1 << 4;
614 pub const VGA_PALETTE_SNOOP: u16 = 1 << 5;
615 pub const PARITY_ERROR_RESPONSE: u16 = 1 << 6;
616 pub const STEPPING_CONTROL: u16 = 1 << 7;
617 pub const SERR_ENABLE: u16 = 1 << 8;
618 pub const FAST_BACK_TO_BACK: u16 = 1 << 9;
619 pub const INTERRUPT_DISABLE: u16 = 1 << 10;
620}
621
622#[derive(Debug, Clone, Copy, PartialEq, Eq)]
624pub enum Bar {
625 Io { port: u16 },
626 Memory32 { addr: u32, prefetchable: bool },
627 Memory64 { addr: u64, prefetchable: bool },
628}
629
630#[derive(Clone, Copy, PartialEq, Eq)]
632pub struct PciAddress {
633 pub bus: u8,
634 pub device: u8,
635 pub function: u8,
636}
637
638impl fmt::Debug for PciAddress {
639 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
641 }
642}
643
644impl PciAddress {
645 pub const fn new(bus: u8, device: u8, function: u8) -> Self {
647 Self {
648 bus,
649 device,
650 function,
651 }
652 }
653
654 fn config_address(&self, offset: u8) -> u32 {
656 let bus = self.bus as u32;
657 let device = (self.device as u32) & 0x1F;
658 let function = (self.function as u32) & 0x07;
659 let offset = (offset as u32) & 0xFC;
660
661 0x8000_0000 | (bus << 16) | (device << 11) | (function << 8) | offset
662 }
663}
664
665#[derive(Clone, Copy)]
667pub struct PciDevice {
668 pub address: PciAddress,
669 pub vendor_id: u16,
670 pub device_id: u16,
671 pub class_code: u8,
672 pub subclass: u8,
673 pub prog_if: u8,
674 pub revision: u8,
675 pub header_type: u8,
676 pub interrupt_line: u8,
677 pub interrupt_pin: u8,
678}
679
680impl fmt::Debug for PciDevice {
681 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682 write!(
683 f,
684 "PciDevice({:?} ID {:04x}:{:04x} Class {:02x}:{:02x})",
685 self.address, self.vendor_id, self.device_id, self.class_code, self.subclass
686 )
687 }
688}
689
690impl PciDevice {
691 pub fn read_config_u8(&self, offset: u8) -> u8 {
693 let addr = self.address.config_address(offset & !0x03);
694 let shift = (offset & 0x03) * 8;
695
696 let _lock = PCI_IO_LOCK.lock();
697 unsafe {
698 outl(CONFIG_ADDRESS, addr);
699 ((inl(CONFIG_DATA) >> shift) & 0xFF) as u8
700 }
701 }
702
703 pub fn read_config_u16(&self, offset: u8) -> u16 {
705 let addr = self.address.config_address(offset & !0x03);
706 let shift = (offset & 0x02) * 8;
707
708 let _lock = PCI_IO_LOCK.lock();
709 unsafe {
710 outl(CONFIG_ADDRESS, addr);
711 ((inl(CONFIG_DATA) >> shift) & 0xFFFF) as u16
712 }
713 }
714
715 pub fn read_config_u32(&self, offset: u8) -> u32 {
717 let addr = self.address.config_address(offset);
718
719 let _lock = PCI_IO_LOCK.lock();
720 unsafe {
721 outl(CONFIG_ADDRESS, addr);
722 inl(CONFIG_DATA)
723 }
724 }
725
726 pub fn read_config_u32_ext(&self, offset: u16) -> Option<u32> {
730 let regions = ECAM_REGIONS.lock();
731 let ecam = regions.iter().find_map(|r| {
732 if self.address.bus >= r.start_bus && self.address.bus <= r.end_bus {
733 Some(r.base_virt)
734 } else {
735 None
736 }
737 })?;
738 Some(unsafe {
739 ecam_read32(
740 ecam,
741 self.address.bus,
742 self.address.device,
743 self.address.function,
744 offset,
745 )
746 })
747 }
748
749 pub fn write_config_u8(&self, offset: u8, value: u8) {
751 let addr = self.address.config_address(offset & !0x03);
752 let shift = (offset & 0x03) * 8;
753
754 let _lock = PCI_IO_LOCK.lock();
755 unsafe {
756 outl(CONFIG_ADDRESS, addr);
757 let old = inl(CONFIG_DATA);
758 let mask = !(0xFF << shift);
759 let new = (old & mask) | ((value as u32) << shift);
760 outl(CONFIG_ADDRESS, addr);
761 outl(CONFIG_DATA, new);
762 }
763 }
764
765 pub fn write_config_u16(&self, offset: u8, value: u16) {
767 let addr = self.address.config_address(offset & !0x03);
768 let shift = (offset & 0x02) * 8;
769
770 let _lock = PCI_IO_LOCK.lock();
771 unsafe {
772 outl(CONFIG_ADDRESS, addr);
773 let old = inl(CONFIG_DATA);
774 let mask = !(0xFFFF << shift);
775 let new = (old & mask) | ((value as u32) << shift);
776 outl(CONFIG_ADDRESS, addr);
777 outl(CONFIG_DATA, new);
778 }
779 }
780
781 pub fn write_config_u32(&self, offset: u8, value: u32) {
783 let addr = self.address.config_address(offset);
784
785 let _lock = PCI_IO_LOCK.lock();
786 unsafe {
787 outl(CONFIG_ADDRESS, addr);
788 outl(CONFIG_DATA, value);
789 }
790 }
791
792 pub fn read_bar(&self, bar_index: u8) -> Option<Bar> {
794 if bar_index > 5 {
795 return None;
796 }
797
798 let offset = config::BAR0 + (bar_index * 4);
799 let bar_low = self.read_config_u32(offset);
800
801 if bar_low == 0 {
802 return None;
803 }
804
805 if bar_low & 0x1 != 0 {
807 let port = (bar_low & 0xFFFF_FFFC) as u16;
808 Some(Bar::Io { port })
809 } else {
810 let bar_type = (bar_low >> 1) & 0x3;
811 let prefetchable = (bar_low >> 3) & 0x1 != 0;
812
813 match bar_type {
814 0 => {
815 let addr = bar_low & 0xFFFF_FFF0;
816 Some(Bar::Memory32 { addr, prefetchable })
817 }
818 2 => {
819 if bar_index >= 5 {
820 return None;
821 }
822 let bar_high = self.read_config_u32(offset + 4);
823 let addr = ((bar_high as u64) << 32) | ((bar_low & 0xFFFF_FFF0) as u64);
824 Some(Bar::Memory64 { addr, prefetchable })
825 }
826 _ => None,
827 }
828 }
829 }
830
831 pub fn read_bar_raw(&self, bar_index: u8) -> Option<u64> {
833 match self.read_bar(bar_index) {
834 Some(Bar::Io { port }) => Some(port as u64),
835 Some(Bar::Memory32 { addr, .. }) => Some(addr as u64),
836 Some(Bar::Memory64 { addr, .. }) => Some(addr),
837 None => None,
838 }
839 }
840
841 pub fn enable_bus_master(&self) {
843 let mut cmd = self.read_config_u16(config::COMMAND);
844 cmd |= command::BUS_MASTER;
845 self.write_config_u16(config::COMMAND, cmd);
846 }
847
848 pub fn enable_memory_space(&self) {
850 let mut cmd = self.read_config_u16(config::COMMAND);
851 cmd |= command::MEMORY_SPACE;
852 self.write_config_u16(config::COMMAND, cmd);
853 }
854
855 pub fn enable_io_space(&self) {
857 let mut cmd = self.read_config_u16(config::COMMAND);
858 cmd |= command::IO_SPACE;
859 self.write_config_u16(config::COMMAND, cmd);
860 }
861}
862
863pub struct PciScanner {
892 bus_queue: [u8; 256],
893 queue_head: usize,
894 queue_tail: usize,
895 seen_buses: [bool; 256],
896 device: u8,
897 function: u8,
898 is_multi_function: bool,
901}
902
903impl PciScanner {
904 pub fn new() -> Self {
905 let mut s = Self {
906 bus_queue: [0u8; 256],
907 queue_head: 0,
908 queue_tail: 1,
909 seen_buses: [false; 256],
910 device: 0,
911 function: 0,
912 is_multi_function: false,
913 };
914 s.seen_buses[0] = true;
915 s
916 }
917
918 fn enqueue_bus(&mut self, bus: u8) {
919 if !self.seen_buses[bus as usize] && self.queue_tail < 256 {
920 self.seen_buses[bus as usize] = true;
921 self.bus_queue[self.queue_tail] = bus;
922 self.queue_tail += 1;
923 }
924 }
925
926 #[inline]
927 fn advance_to_next_device(&mut self) {
928 self.function = 0;
929 self.device += 1;
930 self.is_multi_function = false;
931 }
932}
933
934impl Iterator for PciScanner {
935 type Item = PciDevice;
936
937 fn next(&mut self) -> Option<Self::Item> {
938 loop {
939 if self.queue_head >= self.queue_tail {
941 return None;
942 }
943 let bus = self.bus_queue[self.queue_head];
944
945 if self.device >= 32 {
946 self.queue_head += 1;
947 self.device = 0;
948 self.function = 0;
949 self.is_multi_function = false;
950 continue;
951 }
952
953 let current_function = self.function;
954
955 if current_function == 0 {
957 let word00 = raw_config_read(bus, self.device, 0, 0x00);
960 let vendor_id = (word00 & 0xFFFF) as u16;
961 if is_absent_vendor(vendor_id) {
962 self.advance_to_next_device();
963 continue;
964 }
965
966 let Some(dev) = probe_from_word00(PciAddress::new(bus, self.device, 0), word00)
968 else {
969 self.advance_to_next_device();
970 continue;
971 };
972
973 self.is_multi_function = dev.header_type & 0x80 != 0;
975
976 if dev.header_type & 0x7F == 0x01 {
978 let secondary = raw_config_read_u8(bus, self.device, 0, 0x19);
979 self.enqueue_bus(secondary);
980 }
981
982 if self.is_multi_function {
985 self.function = 1;
986 } else {
987 self.advance_to_next_device();
988 }
989
990 return Some(dev);
991 }
992
993 debug_assert!(self.is_multi_function);
995
996 self.function += 1;
997 if self.function >= 8 {
998 self.advance_to_next_device();
999 }
1000
1001 let address = PciAddress::new(bus, self.device, current_function);
1002 let Some(dev) = probe_device_full(address) else {
1003 continue;
1004 };
1005
1006 if dev.header_type & 0x7F == 0x01 {
1007 let secondary = raw_config_read_u8(bus, self.device, current_function, 0x19);
1008 self.enqueue_bus(secondary);
1009 }
1010
1011 return Some(dev);
1012 }
1013 }
1014}
1015
1016fn is_absent_vendor(vendor_id: u16) -> bool {
1021 vendor_id == 0xFFFF || vendor_id == 0x0000
1022}
1023
1024fn quirk_zero_irq_line(vendor_id: u16, device_id: u16, irq_line: u8) -> u8 {
1025 if irq_line != 0xFF {
1026 return irq_line;
1027 }
1028 if PCI_IRQ_LINE_ZERO_IF_FF
1029 .iter()
1030 .any(|q| q.vendor_id == vendor_id && q.device_id == device_id)
1031 {
1032 return 0;
1033 }
1034 0
1035}
1036
1037fn valid_header_type(header_type: u8) -> bool {
1038 matches!(header_type & 0x7F, 0x00..=0x02)
1039}
1040
1041fn is_ghost_device(class_code: u8, subclass: u8, prog_if: u8) -> bool {
1042 class_code == 0xFF && subclass == 0xFF && prog_if == 0xFF
1043}
1044
1045#[inline]
1048fn raw_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
1049 let addr = 0x8000_0000u32
1050 | ((bus as u32) << 16)
1051 | (((device as u32) & 0x1F) << 11)
1052 | (((function as u32) & 0x07) << 8)
1053 | ((offset as u32) & 0xFC);
1054 let _lock = PCI_IO_LOCK.lock();
1055 unsafe {
1056 outl(CONFIG_ADDRESS, addr);
1057 inl(CONFIG_DATA)
1058 }
1059}
1060
1061#[inline]
1063fn raw_config_read_u8(bus: u8, device: u8, function: u8, offset: u8) -> u8 {
1064 let dword = raw_config_read(bus, device, function, offset & !0x03);
1065 let shift = (offset & 0x03) * 8;
1066 ((dword >> shift) & 0xFF) as u8
1067}
1068
1069fn probe_device_full(address: PciAddress) -> Option<PciDevice> {
1074 let _lock = PCI_IO_LOCK.lock();
1075
1076 let word00 = unsafe {
1077 outl(CONFIG_ADDRESS, address.config_address(0x00));
1078 inl(CONFIG_DATA)
1079 };
1080 let vendor_id = (word00 & 0xFFFF) as u16;
1081 if is_absent_vendor(vendor_id) {
1082 return None;
1083 }
1084 let device_id = (word00 >> 16) as u16;
1085 if device_id == 0xFFFF || device_id == 0x0000 {
1086 return None;
1087 }
1088
1089 let word08 = unsafe {
1090 outl(CONFIG_ADDRESS, address.config_address(0x08));
1091 inl(CONFIG_DATA)
1092 };
1093 let word0c = unsafe {
1094 outl(CONFIG_ADDRESS, address.config_address(0x0C));
1095 inl(CONFIG_DATA)
1096 };
1097
1098 let header_type = ((word0c >> 16) & 0xFF) as u8;
1099 if !valid_header_type(header_type) {
1100 return None;
1101 }
1102
1103 let class_code = ((word08 >> 24) & 0xFF) as u8;
1104 let subclass = ((word08 >> 16) & 0xFF) as u8;
1105 let prog_if = ((word08 >> 8) & 0xFF) as u8;
1106 if is_ghost_device(class_code, subclass, prog_if) {
1107 return None;
1108 }
1109
1110 let word3c = unsafe {
1111 outl(CONFIG_ADDRESS, address.config_address(0x3C));
1112 inl(CONFIG_DATA)
1113 };
1114 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
1115
1116 Some(PciDevice {
1117 address,
1118 vendor_id,
1119 device_id,
1120 class_code,
1121 subclass,
1122 prog_if,
1123 revision: (word08 & 0xFF) as u8,
1124 header_type,
1125 interrupt_line,
1126 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
1127 })
1128}
1129
1130fn probe_from_word00(address: PciAddress, word00: u32) -> Option<PciDevice> {
1133 let vendor_id = (word00 & 0xFFFF) as u16;
1134 let device_id = (word00 >> 16) as u16;
1135 if device_id == 0xFFFF || device_id == 0x0000 {
1136 return None;
1137 }
1138
1139 let _lock = PCI_IO_LOCK.lock();
1140
1141 let word08 = unsafe {
1142 outl(CONFIG_ADDRESS, address.config_address(0x08));
1143 inl(CONFIG_DATA)
1144 };
1145 let word0c = unsafe {
1146 outl(CONFIG_ADDRESS, address.config_address(0x0C));
1147 inl(CONFIG_DATA)
1148 };
1149
1150 let header_type = ((word0c >> 16) & 0xFF) as u8;
1151 if !valid_header_type(header_type) {
1152 return None;
1153 }
1154
1155 let class_code = ((word08 >> 24) & 0xFF) as u8;
1156 let subclass = ((word08 >> 16) & 0xFF) as u8;
1157 let prog_if = ((word08 >> 8) & 0xFF) as u8;
1158 if is_ghost_device(class_code, subclass, prog_if) {
1159 return None;
1160 }
1161
1162 let word3c = unsafe {
1163 outl(CONFIG_ADDRESS, address.config_address(0x3C));
1164 inl(CONFIG_DATA)
1165 };
1166 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
1167
1168 Some(PciDevice {
1169 address,
1170 vendor_id,
1171 device_id,
1172 class_code,
1173 subclass,
1174 prog_if,
1175 revision: (word08 & 0xFF) as u8,
1176 header_type,
1177 interrupt_line,
1178 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
1179 })
1180}
1181
1182static PCI_DEVICE_CACHE: SpinLock<Option<Vec<PciDevice>>> = SpinLock::new(None);
1202
1203fn scan_ecam_devices() -> Vec<PciDevice> {
1213 use crate::{acpi::mcfg, memory};
1214
1215 let mcfg_info = match mcfg::parse_mcfg() {
1216 Some(info) => info,
1217 None => {
1218 log::info!("[PCI-ECAM] No MCFG table found, skipping ECAM scan");
1219 return Vec::new();
1220 }
1221 };
1222
1223 log::info!(
1224 "[PCI-ECAM] Found {} ECAM region(s)",
1225 mcfg_info.entries.len()
1226 );
1227
1228 let mut devices = Vec::new();
1229
1230 for entry in &mcfg_info.entries {
1231 let ecam_phys = entry.base_address;
1232 let start_bus = entry.start_bus;
1233 let end_bus = entry.end_bus;
1234
1235 let region_size = ((end_bus as usize - start_bus as usize + 1) as u64) * (32 * 8 * 4096);
1237 memory::paging::ensure_identity_map_range(ecam_phys, region_size);
1238 let ecam_virt = crate::memory::phys_to_virt(ecam_phys) as usize;
1239
1240 ECAM_REGIONS.lock().push(EcamRegion {
1242 start_bus,
1243 end_bus,
1244 base_virt: ecam_virt,
1245 });
1246
1247 log::info!(
1248 "[PCI-ECAM] Region seg={} ecam={:#x} buses={}..{} virt={:#x}",
1249 entry.segment_group,
1250 ecam_phys,
1251 start_bus,
1252 end_bus,
1253 ecam_virt
1254 );
1255
1256 for bus in start_bus..=end_bus {
1258 for dev in 0..32u8 {
1259 let word00 = unsafe { ecam_read32(ecam_virt, bus, dev, 0, 0x00) };
1261 let vendor_id = (word00 & 0xFFFF) as u16;
1262 if is_absent_vendor(vendor_id) {
1263 continue;
1264 }
1265
1266 if let Some(device) = probe_ecam_device(ecam_virt, bus, dev, 0, word00) {
1268 let is_multi = device.header_type & 0x80 != 0;
1269 devices.push(device);
1270
1271 if is_multi {
1273 for func in 1..8u8 {
1274 if let Some(device) = probe_ecam_device(ecam_virt, bus, dev, func, 0) {
1275 devices.push(device);
1276 }
1277 }
1278 }
1279 }
1280 }
1281 }
1282 }
1283
1284 log::info!("[PCI-ECAM] ECAM scan found {} device(s)", devices.len());
1285 devices
1286}
1287
1288fn probe_ecam_device(
1290 ecam_base: usize,
1291 bus: u8,
1292 device: u8,
1293 function: u8,
1294 word00: u32,
1295) -> Option<PciDevice> {
1296 let vendor_id = (word00 & 0xFFFF) as u16;
1297 let device_id = (word00 >> 16) as u16;
1298 if is_absent_vendor(vendor_id) || device_id == 0xFFFF || device_id == 0x0000 {
1299 return None;
1300 }
1301
1302 let address = PciAddress::new(bus, device, function);
1303
1304 let word08 = unsafe { ecam_read32(ecam_base, bus, device, function, 0x08) };
1305 let word0c = unsafe { ecam_read32(ecam_base, bus, device, function, 0x0C) };
1306
1307 let header_type = ((word0c >> 16) & 0xFF) as u8;
1308 if !valid_header_type(header_type) {
1309 return None;
1310 }
1311
1312 let class_code = ((word08 >> 24) & 0xFF) as u8;
1313 let subclass = ((word08 >> 16) & 0xFF) as u8;
1314 let prog_if = ((word08 >> 8) & 0xFF) as u8;
1315 if is_ghost_device(class_code, subclass, prog_if) {
1316 return None;
1317 }
1318
1319 let word3c = unsafe { ecam_read32(ecam_base, bus, device, function, 0x3C) };
1320 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
1321
1322 Some(PciDevice {
1323 address,
1324 vendor_id,
1325 device_id,
1326 class_code,
1327 subclass,
1328 prog_if,
1329 revision: (word08 & 0xFF) as u8,
1330 header_type,
1331 interrupt_line,
1332 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
1333 })
1334}
1335
1336fn with_cache<R>(f: impl FnOnce(&[PciDevice]) -> R) -> R {
1342 let mut cache = PCI_DEVICE_CACHE.lock();
1343 if cache.is_none() {
1344 let dummy = 0u64;
1345 let rsp = &dummy as *const u64 as u64;
1346 crate::serial_println!("[PCI] Scanning PCI bus (rsp={:#x})...", rsp);
1347
1348 let io_devices: Vec<PciDevice> = PciScanner::new().collect();
1350 crate::serial_println!("[PCI] I/O scan: {} device(s)", io_devices.len());
1351
1352 let ecam_devices: Vec<PciDevice> = scan_ecam_devices();
1354 crate::serial_println!("[PCI] ECAM scan: {} device(s)", ecam_devices.len());
1355
1356 let mut devices = io_devices;
1358 for ecam_dev in &ecam_devices {
1359 let duplicate = devices.iter().any(|d| {
1360 d.address.bus == ecam_dev.address.bus
1361 && d.address.device == ecam_dev.address.device
1362 && d.address.function == ecam_dev.address.function
1363 });
1364 if !duplicate {
1365 devices.push(*ecam_dev);
1366 }
1367 }
1368 crate::serial_println!("[PCI] Total: {} device(s) after merge", devices.len());
1369
1370 for dev in &devices {
1372 let name = device_name(dev.vendor_id, dev.device_id).unwrap_or("Unknown");
1373 let class = class_name(dev.class_code, dev.subclass).unwrap_or("???");
1374 crate::serial_println!(
1375 "[PCI] {:02x}:{:02x}.{:x} {:<24} {} (IRQ={})",
1376 dev.address.bus,
1377 dev.address.device,
1378 dev.address.function,
1379 name,
1380 class,
1381 dev.interrupt_line,
1382 );
1383 }
1384 *cache = Some(devices);
1385 }
1386 f(cache.as_deref().unwrap_or(&[]))
1387}
1388
1389pub fn find_device(vendor_id: u16, device_id: u16) -> Option<PciDevice> {
1391 with_cache(|devs| {
1392 devs.iter()
1393 .copied()
1394 .find(|dev| dev.vendor_id == vendor_id && dev.device_id == device_id)
1395 })
1396}
1397
1398pub fn find_virtio_devices() -> Vec<PciDevice> {
1400 find_devices_by_vendor(vendor::VIRTIO)
1401}
1402
1403pub fn find_virtio_device(device_id: u16) -> Option<PciDevice> {
1405 find_device(vendor::VIRTIO, device_id)
1406}
1407
1408pub fn all_devices() -> Vec<PciDevice> {
1410 with_cache(|devs| devs.to_vec())
1411}
1412
1413pub fn find_devices_by_vendor(vendor_id: u16) -> Vec<PciDevice> {
1415 with_cache(|devs| {
1416 devs.iter()
1417 .copied()
1418 .filter(|dev| dev.vendor_id == vendor_id)
1419 .collect()
1420 })
1421}
1422
1423pub fn find_devices_by_class(class_code: u8, subclass: u8) -> Vec<PciDevice> {
1425 with_cache(|devs| {
1426 devs.iter()
1427 .copied()
1428 .filter(|dev| dev.class_code == class_code && dev.subclass == subclass)
1429 .collect()
1430 })
1431}
1432
1433#[derive(Debug, Clone, Copy, Default)]
1437pub struct ProbeCriteria {
1438 pub vendor_id: Option<u16>,
1439 pub device_id: Option<u16>,
1440 pub class_code: Option<u8>,
1441 pub subclass: Option<u8>,
1442 pub prog_if: Option<u8>,
1443}
1444
1445impl ProbeCriteria {
1446 pub const fn any() -> Self {
1447 Self {
1448 vendor_id: None,
1449 device_id: None,
1450 class_code: None,
1451 subclass: None,
1452 prog_if: None,
1453 }
1454 }
1455
1456 fn matches(&self, dev: &PciDevice) -> bool {
1457 if self.vendor_id.is_some_and(|v| dev.vendor_id != v) {
1458 return false;
1459 }
1460 if self.device_id.is_some_and(|d| dev.device_id != d) {
1461 return false;
1462 }
1463 if self.class_code.is_some_and(|c| dev.class_code != c) {
1464 return false;
1465 }
1466 if self.subclass.is_some_and(|s| dev.subclass != s) {
1467 return false;
1468 }
1469 if self.prog_if.is_some_and(|p| dev.prog_if != p) {
1470 return false;
1471 }
1472 true
1473 }
1474}
1475
1476pub fn probe_all(criteria: ProbeCriteria) -> Vec<PciDevice> {
1478 with_cache(|devs| {
1479 devs.iter()
1480 .copied()
1481 .filter(|dev| criteria.matches(dev))
1482 .collect()
1483 })
1484}
1485
1486pub fn probe_first(criteria: ProbeCriteria) -> Option<PciDevice> {
1488 with_cache(|devs| devs.iter().copied().find(|dev| criteria.matches(dev)))
1489}
1490
1491pub fn invalidate_cache() {
1495 *PCI_DEVICE_CACHE.lock() = None;
1496}