⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/
pci.rs

1//! PCI Bus Driver
2//!
3//! Implements PCI bus enumeration and device management.
4
5use alloc::{collections::BTreeMap, format, string::String, vec::Vec};
6use core::mem;
7
8use crate::{
9    error::KernelError,
10    services::driver_framework::{Bus, DeviceClass, DeviceId, DeviceInfo, DeviceStatus},
11    sync::once_lock::OnceLock,
12};
13
14/// PCI configuration space registers (per PCI Local Bus Specification)
15#[repr(u16)]
16#[allow(dead_code)] // Hardware register definitions per PCI specification
17pub enum PciConfigRegister {
18    VendorId = 0x00,
19    DeviceId = 0x02,
20    Command = 0x04,
21    Status = 0x06,
22    RevisionId = 0x08,
23    ProgIf = 0x09,
24    Subclass = 0x0A,
25    ClassCode = 0x0B,
26    CacheLineSize = 0x0C,
27    LatencyTimer = 0x0D,
28    HeaderType = 0x0E,
29    Bist = 0x0F,
30    Bar0 = 0x10,
31    Bar1 = 0x14,
32    Bar2 = 0x18,
33    Bar3 = 0x1C,
34    Bar4 = 0x20,
35    Bar5 = 0x24,
36    CardbusCisPointer = 0x28,
37    SubsystemVendorId = 0x2C,
38    SubsystemId = 0x2E,
39    ExpansionRomBase = 0x30,
40    CapabilitiesPointer = 0x34,
41    InterruptLine = 0x3C,
42    InterruptPin = 0x3D,
43    MinGrant = 0x3E,
44    MaxLatency = 0x3F,
45}
46
47/// PCI class codes (per PCI specification)
48#[allow(dead_code)] // Hardware constants per PCI specification
49pub mod class_codes {
50    pub const UNCLASSIFIED: u8 = 0x00;
51    pub const MASS_STORAGE: u8 = 0x01;
52    pub const NETWORK: u8 = 0x02;
53    pub const DISPLAY: u8 = 0x03;
54    pub const MULTIMEDIA: u8 = 0x04;
55    pub const MEMORY: u8 = 0x05;
56    pub const BRIDGE: u8 = 0x06;
57    pub const COMMUNICATION: u8 = 0x07;
58    pub const SYSTEM: u8 = 0x08;
59    pub const INPUT: u8 = 0x09;
60    pub const DOCKING: u8 = 0x0A;
61    pub const PROCESSOR: u8 = 0x0B;
62    pub const SERIAL_BUS: u8 = 0x0C;
63    pub const WIRELESS: u8 = 0x0D;
64    pub const INTELLIGENT: u8 = 0x0E;
65    pub const SATELLITE: u8 = 0x0F;
66    pub const ENCRYPTION: u8 = 0x10;
67    pub const DATA_ACQUISITION: u8 = 0x11;
68    pub const COPROCESSOR: u8 = 0xFF;
69}
70
71/// PCI command register flags (per PCI specification)
72#[allow(dead_code)] // Hardware constants per PCI specification
73pub mod command_flags {
74    pub const IO_SPACE: u16 = 1 << 0;
75    pub const MEMORY_SPACE: u16 = 1 << 1;
76    pub const BUS_MASTER: u16 = 1 << 2;
77    pub const SPECIAL_CYCLES: u16 = 1 << 3;
78    pub const MEMORY_WRITE_INVALIDATE: u16 = 1 << 4;
79    pub const VGA_PALETTE_SNOOP: u16 = 1 << 5;
80    pub const PARITY_ERROR: u16 = 1 << 6;
81    pub const STEPPING: u16 = 1 << 7;
82    pub const SERR: u16 = 1 << 8;
83    pub const FAST_BACK_TO_BACK: u16 = 1 << 9;
84    pub const INTERRUPT_DISABLE: u16 = 1 << 10;
85}
86
87/// PCI device location
88#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
89pub struct PciLocation {
90    pub bus: u8,
91    pub device: u8,
92    pub function: u8,
93}
94
95impl PciLocation {
96    pub fn new(bus: u8, device: u8, function: u8) -> Self {
97        Self {
98            bus,
99            device,
100            function,
101        }
102    }
103
104    /// Convert to configuration address
105    pub fn to_config_address(&self) -> u32 {
106        0x80000000
107            | ((self.bus as u32) << 16)
108            | ((self.device as u32) << 11)
109            | ((self.function as u32) << 8)
110    }
111}
112
113/// PCI Base Address Register
114#[derive(Debug, Clone)]
115pub enum PciBar {
116    Memory {
117        address: u64,
118        size: u64,
119        prefetchable: bool,
120        is_64bit: bool,
121    },
122    Io {
123        address: u32,
124        size: u32,
125    },
126    None,
127}
128
129impl PciBar {
130    /// Get memory address if this is a memory BAR
131    pub fn get_memory_address(&self) -> Option<u64> {
132        match self {
133            PciBar::Memory { address, .. } => Some(*address),
134            _ => None,
135        }
136    }
137
138    /// Get I/O port address if this is an I/O BAR
139    pub fn get_io_address(&self) -> Option<u32> {
140        match self {
141            PciBar::Io { address, .. } => Some(*address),
142            _ => None,
143        }
144    }
145
146    /// Check if this is a memory BAR
147    pub fn is_memory(&self) -> bool {
148        matches!(self, PciBar::Memory { .. })
149    }
150
151    /// Check if this is an I/O BAR
152    pub fn is_io(&self) -> bool {
153        matches!(self, PciBar::Io { .. })
154    }
155}
156
157/// MSI capability information parsed from the PCI capability chain.
158#[derive(Debug, Clone)]
159pub struct MsiCapability {
160    /// Offset of the MSI capability in config space.
161    pub cap_offset: u16,
162    /// True if 64-bit address is supported.
163    pub is_64bit: bool,
164    /// True if per-vector masking is supported.
165    pub per_vector_mask: bool,
166    /// Maximum number of vectors (log2) the device can request.
167    pub max_vectors_log2: u8,
168}
169
170/// MSI-X capability information parsed from the PCI capability chain.
171#[derive(Debug, Clone)]
172pub struct MsixCapability {
173    /// Offset of the MSI-X capability in config space.
174    pub cap_offset: u16,
175    /// Number of table entries (0-based: actual count = table_size + 1).
176    pub table_size: u16,
177    /// BAR index containing the MSI-X table.
178    pub table_bar: u8,
179    /// Offset within the BAR to the MSI-X table.
180    pub table_offset: u32,
181    /// BAR index containing the Pending Bit Array.
182    pub pba_bar: u8,
183    /// Offset within the BAR to the PBA.
184    pub pba_offset: u32,
185}
186
187/// PCI device representation
188#[derive(Debug, Clone)]
189pub struct PciDevice {
190    pub location: PciLocation,
191    pub vendor_id: u16,
192    pub device_id: u16,
193    pub class_code: u8,
194    pub subclass: u8,
195    pub prog_if: u8,
196    pub revision: u8,
197    pub header_type: u8,
198    pub interrupt_line: u8,
199    pub interrupt_pin: u8,
200    pub bars: Vec<PciBar>,
201    pub enabled: bool,
202    /// MSI capability, if present.
203    pub msi: Option<MsiCapability>,
204    /// MSI-X capability, if present.
205    pub msix: Option<MsixCapability>,
206    /// Secondary bus number (only valid for PCI-to-PCI bridges, header type 1).
207    pub secondary_bus: Option<u8>,
208}
209
210impl PciDevice {
211    /// Create a new PCI device
212    pub fn new(location: PciLocation) -> Self {
213        Self {
214            location,
215            vendor_id: 0,
216            device_id: 0,
217            class_code: 0,
218            subclass: 0,
219            prog_if: 0,
220            revision: 0,
221            header_type: 0,
222            interrupt_line: 0,
223            interrupt_pin: 0,
224            bars: Vec::new(),
225            enabled: false,
226            msi: None,
227            msix: None,
228            secondary_bus: None,
229        }
230    }
231
232    /// Get device class
233    pub fn get_device_class(&self) -> DeviceClass {
234        match self.class_code {
235            class_codes::MASS_STORAGE => DeviceClass::Storage,
236            class_codes::NETWORK => DeviceClass::Network,
237            class_codes::DISPLAY => DeviceClass::Display,
238            class_codes::MULTIMEDIA => DeviceClass::Audio,
239            class_codes::SERIAL_BUS => {
240                match self.subclass {
241                    0x03 => DeviceClass::USB, // USB controller
242                    _ => DeviceClass::Other,
243                }
244            }
245            class_codes::BRIDGE => DeviceClass::PCI,
246            _ => DeviceClass::Other,
247        }
248    }
249
250    /// Check if device is multifunction
251    pub fn is_multifunction(&self) -> bool {
252        self.header_type & 0x80 != 0
253    }
254}
255
256/// PCI bus implementation
257pub struct PciBus {
258    /// Discovered PCI devices
259    devices: spin::RwLock<BTreeMap<PciLocation, PciDevice>>,
260
261    /// Device enumeration complete
262    enumerated: core::sync::atomic::AtomicBool,
263}
264
265impl Default for PciBus {
266    fn default() -> Self {
267        Self::new()
268    }
269}
270
271impl PciBus {
272    /// Create a new PCI bus
273    pub fn new() -> Self {
274        Self {
275            devices: spin::RwLock::new(BTreeMap::new()),
276            enumerated: core::sync::atomic::AtomicBool::new(false),
277        }
278    }
279
280    /// Enumerate all PCI devices
281    #[allow(unused_assignments)]
282    pub fn enumerate_devices(&self) -> Result<(), KernelError> {
283        if self.enumerated.load(core::sync::atomic::Ordering::Acquire) {
284            return Ok(());
285        }
286
287        crate::println!("[PCI] Enumerating PCI devices...");
288        #[allow(unused_variables)]
289        let mut device_count = 0;
290
291        // Scan all buses
292        for bus in 0..=255 {
293            for device in 0..32 {
294                // Check function 0
295                let location = PciLocation::new(bus, device, 0);
296                if let Some(mut pci_device) = self.probe_device(location) {
297                    self.read_device_config(&mut pci_device);
298
299                    crate::println!(
300                        "[PCI] Found device at {}:{}:{} - {:04x}:{:04x} (class {:02x})",
301                        bus,
302                        device,
303                        0,
304                        pci_device.vendor_id,
305                        pci_device.device_id,
306                        pci_device.class_code
307                    );
308
309                    let is_multifunction = pci_device.is_multifunction();
310                    let is_bridge = pci_device.secondary_bus.is_some();
311                    let secondary_bus = pci_device.secondary_bus;
312                    self.devices.write().insert(location, pci_device);
313                    device_count += 1;
314
315                    // Scan behind PCI-to-PCI bridges recursively.
316                    if is_bridge {
317                        if let Some(sec_bus) = secondary_bus {
318                            crate::println!(
319                                "[PCI] Scanning secondary bus {} behind bridge {}:{}:0",
320                                sec_bus,
321                                bus,
322                                device
323                            );
324                            let _ = self.scan_bridge(sec_bus, &mut device_count);
325                        }
326                    }
327
328                    // Check other functions if multifunction
329                    if is_multifunction {
330                        for function in 1..8 {
331                            let func_location = PciLocation::new(bus, device, function);
332                            if let Some(mut func_device) = self.probe_device(func_location) {
333                                self.read_device_config(&mut func_device);
334
335                                crate::println!(
336                                    "[PCI] Found device at {}:{}:{} - {:04x}:{:04x} (class {:02x})",
337                                    bus,
338                                    device,
339                                    function,
340                                    func_device.vendor_id,
341                                    func_device.device_id,
342                                    func_device.class_code
343                                );
344
345                                self.devices.write().insert(func_location, func_device);
346                                device_count += 1;
347                            }
348                        }
349                    }
350                }
351            }
352        }
353
354        self.enumerated
355            .store(true, core::sync::atomic::Ordering::Release);
356        crate::println!("[PCI] Enumeration complete: {} devices found", device_count);
357
358        Ok(())
359    }
360
361    /// Probe for device at location
362    fn probe_device(&self, location: PciLocation) -> Option<PciDevice> {
363        let vendor_id = self.read_config_word(location, PciConfigRegister::VendorId);
364
365        // Check if device exists
366        if vendor_id == 0xFFFF {
367            return None;
368        }
369
370        let mut device = PciDevice::new(location);
371        device.vendor_id = vendor_id;
372        device.device_id = self.read_config_word(location, PciConfigRegister::DeviceId);
373
374        Some(device)
375    }
376
377    /// Read full device configuration including capabilities.
378    fn read_device_config(&self, device: &mut PciDevice) {
379        let location = device.location;
380
381        device.class_code = self.read_config_byte(location, PciConfigRegister::ClassCode);
382        device.subclass = self.read_config_byte(location, PciConfigRegister::Subclass);
383        device.prog_if = self.read_config_byte(location, PciConfigRegister::ProgIf);
384        device.revision = self.read_config_byte(location, PciConfigRegister::RevisionId);
385        device.header_type = self.read_config_byte(location, PciConfigRegister::HeaderType);
386        device.interrupt_line = self.read_config_byte(location, PciConfigRegister::InterruptLine);
387        device.interrupt_pin = self.read_config_byte(location, PciConfigRegister::InterruptPin);
388
389        // Read BARs
390        device.bars = self.read_bars(location, device.header_type & 0x7F);
391
392        // For PCI-to-PCI bridges (header type 1), read the secondary bus number.
393        if device.header_type & 0x7F == 1 {
394            // Secondary bus at offset 0x19 in the bridge header.
395            let buses = self.read_config_dword(location, 0x18);
396            device.secondary_bus = Some(((buses >> 8) & 0xFF) as u8);
397        }
398
399        // Parse PCI capabilities chain (MSI, MSI-X).
400        let status = self.read_config_dword(location, 0x04) >> 16;
401        if status & (1 << 4) != 0 {
402            // Capabilities List bit is set in Status register.
403            self.parse_capabilities(device);
404        }
405    }
406
407    /// Walk the PCI capabilities linked list and parse MSI/MSI-X capabilities.
408    fn parse_capabilities(&self, device: &mut PciDevice) {
409        let location = device.location;
410        // Capabilities pointer is at offset 0x34 (first byte only).
411        let mut cap_ptr = (self
412            .read_config_dword(location, PciConfigRegister::CapabilitiesPointer as u16)
413            & 0xFF) as u16;
414
415        // Walk the linked list (max 48 iterations to prevent infinite loops).
416        let mut iterations = 0;
417        while cap_ptr != 0 && cap_ptr != 0xFF && iterations < 48 {
418            iterations += 1;
419            let cap_header = self.read_config_dword(location, cap_ptr & !3);
420            let offset_in_dword = (cap_ptr & 3) * 8;
421            let cap_id = ((cap_header >> offset_in_dword) & 0xFF) as u8;
422            let next_ptr = ((cap_header >> (offset_in_dword + 8)) & 0xFF) as u16;
423
424            match cap_id {
425                0x05 => {
426                    // MSI Capability
427                    let msg_ctrl = self.read_config_dword(location, (cap_ptr + 2) & !3);
428                    let msg_ctrl_val = ((msg_ctrl >> (((cap_ptr + 2) & 3) * 8)) & 0xFFFF) as u16;
429                    device.msi = Some(MsiCapability {
430                        cap_offset: cap_ptr,
431                        is_64bit: msg_ctrl_val & (1 << 7) != 0,
432                        per_vector_mask: msg_ctrl_val & (1 << 8) != 0,
433                        max_vectors_log2: ((msg_ctrl_val >> 1) & 0x7) as u8,
434                    });
435                }
436                0x11 => {
437                    // MSI-X Capability
438                    let msg_ctrl = self.read_config_dword(location, (cap_ptr + 2) & !3);
439                    let msg_ctrl_val = ((msg_ctrl >> (((cap_ptr + 2) & 3) * 8)) & 0xFFFF) as u16;
440                    let table_offset_dword = self.read_config_dword(location, cap_ptr + 4);
441                    let pba_offset_dword = self.read_config_dword(location, cap_ptr + 8);
442
443                    device.msix = Some(MsixCapability {
444                        cap_offset: cap_ptr,
445                        table_size: msg_ctrl_val & 0x7FF,
446                        table_bar: (table_offset_dword & 0x7) as u8,
447                        table_offset: table_offset_dword & !0x7,
448                        pba_bar: (pba_offset_dword & 0x7) as u8,
449                        pba_offset: pba_offset_dword & !0x7,
450                    });
451                }
452                _ => {} // Skip other capabilities
453            }
454
455            cap_ptr = next_ptr;
456        }
457    }
458
459    /// Scan devices behind a PCI-to-PCI bridge (recursive bus enumeration).
460    fn scan_bridge(&self, secondary_bus: u8, device_count: &mut usize) -> Result<(), KernelError> {
461        for device in 0..32 {
462            let location = PciLocation::new(secondary_bus, device, 0);
463            if let Some(mut pci_device) = self.probe_device(location) {
464                self.read_device_config(&mut pci_device);
465
466                crate::println!(
467                    "[PCI]   Bridge device at {}:{}:{} - {:04x}:{:04x} (class {:02x})",
468                    secondary_bus,
469                    device,
470                    0,
471                    pci_device.vendor_id,
472                    pci_device.device_id,
473                    pci_device.class_code
474                );
475
476                let is_multifunction = pci_device.is_multifunction();
477                let is_bridge = pci_device.secondary_bus.is_some();
478                let sub_bus = pci_device.secondary_bus;
479                self.devices.write().insert(location, pci_device);
480                *device_count += 1;
481
482                // Recurse into sub-bridges.
483                if is_bridge {
484                    if let Some(sub) = sub_bus {
485                        self.scan_bridge(sub, device_count)?;
486                    }
487                }
488
489                // Check other functions if multifunction
490                if is_multifunction {
491                    for function in 1..8 {
492                        let func_location = PciLocation::new(secondary_bus, device, function);
493                        if let Some(mut func_device) = self.probe_device(func_location) {
494                            self.read_device_config(&mut func_device);
495                            self.devices.write().insert(func_location, func_device);
496                            *device_count += 1;
497                        }
498                    }
499                }
500            }
501        }
502        Ok(())
503    }
504
505    /// Read Base Address Registers
506    fn read_bars(&self, location: PciLocation, header_type: u8) -> Vec<PciBar> {
507        let mut bars = Vec::new();
508
509        // Standard header has 6 BARs, bridge header has 2
510        let bar_count = if header_type == 0 { 6 } else { 2 };
511
512        let mut bar_index = 0;
513        while bar_index < bar_count {
514            let bar_offset = PciConfigRegister::Bar0 as u16 + (bar_index * 4) as u16;
515            let bar_value = self.read_config_dword(location, bar_offset);
516
517            if bar_value == 0 {
518                bars.push(PciBar::None);
519                bar_index += 1;
520                continue;
521            }
522
523            if bar_value & 1 == 0 {
524                // Memory BAR
525                let is_64bit = (bar_value >> 1) & 3 == 2;
526                let prefetchable = (bar_value >> 3) & 1 != 0;
527
528                // Write all 1s to determine size
529                self.write_config_dword(location, bar_offset, 0xFFFFFFFF);
530                let size_mask = self.read_config_dword(location, bar_offset);
531                self.write_config_dword(location, bar_offset, bar_value);
532
533                let size = (!size_mask + 1) & 0xFFFFFFF0;
534                let mut address = (bar_value & 0xFFFFFFF0) as u64;
535
536                if is_64bit && bar_index + 1 < bar_count {
537                    // Read upper 32 bits
538                    let upper_bar_offset = bar_offset + 4;
539                    let upper_value = self.read_config_dword(location, upper_bar_offset);
540                    address |= (upper_value as u64) << 32;
541
542                    bars.push(PciBar::Memory {
543                        address,
544                        size: size as u64,
545                        prefetchable,
546                        is_64bit: true,
547                    });
548
549                    bars.push(PciBar::None); // Upper 32 bits
550                    bar_index += 2;
551                } else {
552                    bars.push(PciBar::Memory {
553                        address,
554                        size: size as u64,
555                        prefetchable,
556                        is_64bit: false,
557                    });
558                    bar_index += 1;
559                }
560            } else {
561                // I/O BAR
562                self.write_config_dword(location, bar_offset, 0xFFFFFFFF);
563                let size_mask = self.read_config_dword(location, bar_offset);
564                self.write_config_dword(location, bar_offset, bar_value);
565
566                let size = (!size_mask + 1) & 0xFFFFFFFC;
567                let address = bar_value & 0xFFFFFFFC;
568
569                bars.push(PciBar::Io { address, size });
570                bar_index += 1;
571            }
572        }
573
574        bars
575    }
576
577    /// Read configuration byte
578    fn read_config_byte(&self, location: PciLocation, register: PciConfigRegister) -> u8 {
579        let offset = register as u16;
580        let dword = self.read_config_dword(location, offset & !3);
581        ((dword >> ((offset & 3) * 8)) & 0xFF) as u8
582    }
583
584    /// Read configuration word
585    fn read_config_word(&self, location: PciLocation, register: PciConfigRegister) -> u16 {
586        let offset = register as u16;
587        let dword = self.read_config_dword(location, offset & !3);
588        ((dword >> ((offset & 3) * 8)) & 0xFFFF) as u16
589    }
590
591    /// Read configuration dword
592    fn read_config_dword(&self, location: PciLocation, offset: u16) -> u32 {
593        let address = location.to_config_address() | (offset as u32 & 0xFC);
594
595        // SAFETY: PCI configuration space access requires writing the target address
596        // to I/O port 0xCF8 (CONFIG_ADDRESS) then reading the data from I/O port
597        // 0xCFC (CONFIG_DATA). These are the standard PCI mechanism #1 ports defined
598        // by the PCI specification. We are in kernel mode with full I/O privilege.
599        unsafe {
600            // Write configuration address
601            crate::arch::outl(0xCF8, address);
602            // Read configuration data
603            crate::arch::inl(0xCFC)
604        }
605    }
606
607    /// Write configuration dword
608    fn write_config_dword(&self, location: PciLocation, offset: u16, value: u32) {
609        let address = location.to_config_address() | (offset as u32 & 0xFC);
610
611        // SAFETY: PCI configuration space write via mechanism #1. Writing the target
612        // address to port 0xCF8 then the data to port 0xCFC. Same invariants as
613        // read_config_dword above. We are in kernel mode with full I/O privilege.
614        unsafe {
615            // Write configuration address
616            crate::arch::outl(0xCF8, address);
617            // Write configuration data
618            crate::arch::outl(0xCFC, value);
619        }
620    }
621
622    /// Get device by location
623    pub fn get_device(&self, location: PciLocation) -> Option<PciDevice> {
624        self.devices.read().get(&location).cloned()
625    }
626
627    /// Get all devices
628    pub fn get_all_devices(&self) -> Vec<PciDevice> {
629        self.devices.read().values().cloned().collect()
630    }
631
632    /// Find devices by class
633    pub fn find_devices_by_class(&self, class_code: u8) -> Vec<PciDevice> {
634        self.devices
635            .read()
636            .values()
637            .filter(|dev| dev.class_code == class_code)
638            .cloned()
639            .collect()
640    }
641
642    /// Find devices by vendor and device ID
643    pub fn find_devices_by_id(&self, vendor_id: u16, device_id: u16) -> Vec<PciDevice> {
644        self.devices
645            .read()
646            .values()
647            .filter(|dev| dev.vendor_id == vendor_id && dev.device_id == device_id)
648            .cloned()
649            .collect()
650    }
651}
652
653impl Bus for PciBus {
654    fn name(&self) -> &str {
655        "pci"
656    }
657
658    fn scan(&mut self) -> Vec<DeviceInfo> {
659        // Enumerate devices if not done already
660        self.enumerate_devices().unwrap_or_else(|_e| {
661            crate::println!("[PCI] Enumeration failed: {}", _e);
662        });
663
664        let devices = self.devices.read();
665        let mut device_infos = Vec::new();
666
667        for (location, pci_device) in devices.iter() {
668            let device_id = DeviceId {
669                vendor_id: pci_device.vendor_id,
670                device_id: pci_device.device_id,
671                class_code: pci_device.class_code,
672                subclass: pci_device.subclass,
673                prog_if: pci_device.prog_if,
674                revision: pci_device.revision,
675            };
676
677            let mut io_ports = Vec::new();
678            let mut memory_regions = Vec::new();
679
680            for bar in &pci_device.bars {
681                match bar {
682                    PciBar::Memory { address, size, .. } => {
683                        memory_regions.push((*address, *size));
684                    }
685                    PciBar::Io { address, size } => {
686                        io_ports.push((*address as u16, (*address + *size) as u16));
687                    }
688                    PciBar::None => {}
689                }
690            }
691
692            let device_info = DeviceInfo {
693                id: ((location.bus as u64) << 16)
694                    | ((location.device as u64) << 8)
695                    | (location.function as u64),
696                name: format!(
697                    "PCI Device {:04x}:{:04x}",
698                    pci_device.vendor_id, pci_device.device_id
699                ),
700                class: pci_device.get_device_class(),
701                device_id: Some(device_id),
702                driver: None,
703                bus: String::from("pci"),
704                address: location.to_config_address() as u64,
705                irq: if pci_device.interrupt_line != 0xFF {
706                    Some(pci_device.interrupt_line)
707                } else {
708                    None
709                },
710                dma_channels: Vec::new(), // PCI devices don't use ISA DMA
711                io_ports,
712                memory_regions,
713                status: DeviceStatus::Uninitialized,
714            };
715
716            device_infos.push(device_info);
717        }
718
719        device_infos
720    }
721
722    fn read_config(&self, device: &DeviceInfo, offset: u16, size: u8) -> Result<u32, KernelError> {
723        // Extract location from device address
724        let address = device.address as u32;
725        let bus = ((address >> 16) & 0xFF) as u8;
726        let dev = ((address >> 11) & 0x1F) as u8;
727        let func = ((address >> 8) & 0x07) as u8;
728        let location = PciLocation::new(bus, dev, func);
729
730        match size {
731            // SAFETY: PciConfigRegister is #[repr(u16)] so any u16 value that maps
732            // to a valid variant is safe to transmute. Offsets outside valid register
733            // values will produce a bit pattern that reads from the corresponding PCI
734            // config space offset, which is defined hardware behavior.
735            1 => Ok(self.read_config_byte(location, unsafe {
736                mem::transmute::<u16, PciConfigRegister>(offset)
737            }) as u32),
738            // SAFETY: Same as the 1-byte case above.
739            2 => Ok(self.read_config_word(location, unsafe {
740                mem::transmute::<u16, PciConfigRegister>(offset)
741            }) as u32),
742            4 => Ok(self.read_config_dword(location, offset)),
743            _ => Err(KernelError::InvalidArgument {
744                name: "size",
745                value: "must be 1, 2, or 4",
746            }),
747        }
748    }
749
750    fn write_config(
751        &mut self,
752        device: &DeviceInfo,
753        offset: u16,
754        value: u32,
755        size: u8,
756    ) -> Result<(), KernelError> {
757        // Extract location from device address
758        let address = device.address as u32;
759        let bus = ((address >> 16) & 0xFF) as u8;
760        let dev = ((address >> 11) & 0x1F) as u8;
761        let func = ((address >> 8) & 0x07) as u8;
762        let location = PciLocation::new(bus, dev, func);
763
764        match size {
765            1 => {
766                let current = self.read_config_dword(location, offset & !3);
767                let shift = (offset & 3) * 8;
768                let mask = !(0xFF << shift);
769                let new_value = (current & mask) | ((value & 0xFF) << shift);
770                self.write_config_dword(location, offset & !3, new_value);
771            }
772            2 => {
773                let current = self.read_config_dword(location, offset & !3);
774                let shift = (offset & 3) * 8;
775                let mask = !(0xFFFF << shift);
776                let new_value = (current & mask) | ((value & 0xFFFF) << shift);
777                self.write_config_dword(location, offset & !3, new_value);
778            }
779            4 => self.write_config_dword(location, offset, value),
780            _ => {
781                return Err(KernelError::InvalidArgument {
782                    name: "size",
783                    value: "must be 1, 2, or 4",
784                })
785            }
786        }
787
788        Ok(())
789    }
790
791    fn enable_device(&mut self, device: &DeviceInfo) -> Result<(), KernelError> {
792        // Enable I/O and memory space, bus mastering
793        let current_command = self.read_config(device, PciConfigRegister::Command as u16, 2)?;
794        let new_command = current_command
795            | command_flags::IO_SPACE as u32
796            | command_flags::MEMORY_SPACE as u32
797            | command_flags::BUS_MASTER as u32;
798
799        self.write_config(device, PciConfigRegister::Command as u16, new_command, 2)?;
800
801        crate::println!("[PCI] Enabled device {}", device.name);
802        Ok(())
803    }
804
805    fn disable_device(&mut self, device: &DeviceInfo) -> Result<(), KernelError> {
806        // Disable I/O and memory space, bus mastering
807        let current_command = self.read_config(device, PciConfigRegister::Command as u16, 2)?;
808        let new_command = current_command
809            & !(command_flags::IO_SPACE as u32
810                | command_flags::MEMORY_SPACE as u32
811                | command_flags::BUS_MASTER as u32);
812
813        self.write_config(device, PciConfigRegister::Command as u16, new_command, 2)?;
814
815        crate::println!("[PCI] Disabled device {}", device.name);
816        Ok(())
817    }
818}
819
820/// Global PCI bus instance
821static PCI_BUS: OnceLock<spin::Mutex<PciBus>> = OnceLock::new();
822
823/// Initialize PCI bus
824pub fn init() {
825    let pci_bus = PciBus::new();
826    let _ = PCI_BUS.set(spin::Mutex::new(pci_bus));
827
828    // Register with driver framework
829    let driver_framework = crate::services::driver_framework::get_driver_framework();
830
831    // Create a new PciBus instance for registration (since we can't clone the mutex
832    // guard)
833    let bus_instance = PciBus::new();
834
835    if let Err(_e) = driver_framework.register_bus(alloc::boxed::Box::new(bus_instance)) {
836        crate::println!("[PCI] Failed to register PCI bus: {}", _e);
837    } else {
838        crate::println!("[PCI] PCI bus driver initialized");
839    }
840}
841
842/// Check if PCI bus has been initialized
843pub fn is_pci_initialized() -> bool {
844    PCI_BUS.get().is_some()
845}
846
847/// Get the global PCI bus
848pub fn get_pci_bus() -> &'static spin::Mutex<PciBus> {
849    PCI_BUS.get().expect("PCI bus not initialized")
850}
851
852/// Probe enumerated PCI devices and initialize known VirtIO drivers.
853///
854/// Called after `enumerate_devices()` during bootstrap. Each driver's `init()`
855/// handles the case where no matching device is present, so spurious calls are
856/// harmless.
857pub fn probe_known_drivers() {
858    if !is_pci_initialized() {
859        return;
860    }
861    let devices = get_pci_bus().lock().get_all_devices();
862    for dev in &devices {
863        match (dev.vendor_id, dev.device_id) {
864            // VirtIO GPU (transitional 0x1050, legacy 0x1040)
865            (0x1AF4, 0x1050) | (0x1AF4, 0x1040) => {
866                crate::println!(
867                    "  [pci] Probing VirtIO GPU ({:#06x}:{:#06x})",
868                    dev.vendor_id,
869                    dev.device_id
870                );
871                let _ = crate::drivers::virtio_gpu::init();
872            }
873            // VirtIO Net (transitional 0x1041, legacy 0x1000)
874            (0x1AF4, 0x1041) | (0x1AF4, 0x1000) => {
875                crate::println!(
876                    "  [pci] Probing VirtIO Net ({:#06x}:{:#06x})",
877                    dev.vendor_id,
878                    dev.device_id
879                );
880                let _ = crate::drivers::virtio_net::init();
881            }
882            // VirtIO Sound (0x1059)
883            (0x1AF4, 0x1059) => {
884                crate::println!(
885                    "  [pci] Probing VirtIO Sound ({:#06x}:{:#06x})",
886                    dev.vendor_id,
887                    dev.device_id
888                );
889                let _ = crate::audio::virtio_sound::init();
890            }
891            // VirtIO Block (transitional 0x1042, legacy 0x1001)
892            (0x1AF4, 0x1042) | (0x1AF4, 0x1001) => {
893                // VirtIO-blk is initialized separately via blk::init()
894            }
895            _ => {}
896        }
897    }
898}
899
900// ---------------------------------------------------------------------------
901// MSI / MSI-X configuration
902// ---------------------------------------------------------------------------
903
904/// Configure MSI for a device to deliver a specific vector to a target APIC.
905///
906/// - `location`: PCI device location.
907/// - `msi`: MSI capability previously discovered by `parse_capabilities()`.
908/// - `vector`: IDT vector number to deliver.
909/// - `dest_apic_id`: Target Local APIC ID.
910pub fn configure_msi(location: PciLocation, msi: &MsiCapability, vector: u8, dest_apic_id: u8) {
911    let bus = get_pci_bus().lock();
912
913    // MSI message address (Intel format): bits 31:20 = 0xFEE, bits 19:12 = dest
914    // APIC ID.
915    let msg_addr: u32 = 0xFEE00000 | ((dest_apic_id as u32) << 12);
916
917    // MSI message data: bits 7:0 = vector, delivery mode = fixed (000).
918    let msg_data: u16 = vector as u16;
919
920    // Write message address (offset +4 from capability start).
921    bus.write_config_dword(location, msi.cap_offset + 4, msg_addr);
922
923    if msi.is_64bit {
924        // 64-bit: upper address at +8, data at +12.
925        bus.write_config_dword(location, msi.cap_offset + 8, 0); // upper 32 bits = 0
926        let data_dword = bus.read_config_dword(location, (msi.cap_offset + 12) & !3);
927        let shift = ((msi.cap_offset + 12) & 3) * 8;
928        let masked = data_dword & !(0xFFFF << shift);
929        bus.write_config_dword(
930            location,
931            (msi.cap_offset + 12) & !3,
932            masked | ((msg_data as u32) << shift),
933        );
934    } else {
935        // 32-bit: data at +8.
936        let data_dword = bus.read_config_dword(location, (msi.cap_offset + 8) & !3);
937        let shift = ((msi.cap_offset + 8) & 3) * 8;
938        let masked = data_dword & !(0xFFFF << shift);
939        bus.write_config_dword(
940            location,
941            (msi.cap_offset + 8) & !3,
942            masked | ((msg_data as u32) << shift),
943        );
944    }
945
946    // Enable MSI: set bit 0 of Message Control (offset +2 from cap start).
947    let ctrl_dword = bus.read_config_dword(location, (msi.cap_offset + 2) & !3);
948    let ctrl_shift = ((msi.cap_offset + 2) & 3) * 8;
949    let enabled = ctrl_dword | (1 << ctrl_shift); // Set MSI Enable bit
950    bus.write_config_dword(location, (msi.cap_offset + 2) & !3, enabled);
951
952    crate::println!(
953        "[PCI] MSI configured for {:02x}:{:02x}.{}: vector={}, dest={}",
954        location.bus,
955        location.device,
956        location.function,
957        vector,
958        dest_apic_id
959    );
960}
961
962// ---------------------------------------------------------------------------
963// PCIe ECAM (Enhanced Configuration Access Mechanism) via MCFG
964// ---------------------------------------------------------------------------
965
966/// Read a PCI config register using memory-mapped ECAM access (PCIe).
967///
968/// `ecam_base` is the physical base address of the ECAM region from ACPI MCFG.
969/// Returns `None` if the physical-to-virtual translation is unavailable.
970#[cfg(target_arch = "x86_64")]
971pub fn ecam_read_config(ecam_base: u64, bus: u8, device: u8, func: u8, offset: u16) -> Option<u32> {
972    if offset > 0xFFF {
973        return None;
974    }
975    // ECAM address = base + (bus << 20) + (device << 15) + (function << 12) +
976    // offset
977    let phys = ecam_base as usize
978        + ((bus as usize) << 20)
979        + ((device as usize) << 15)
980        + ((func as usize) << 12)
981        + (offset as usize & 0xFFC);
982
983    let virt = crate::arch::x86_64::msr::phys_to_virt(phys)?;
984
985    // SAFETY: ECAM region is memory-mapped PCI config space. The physical address
986    // is translated to a virtual address via the bootloader's physical memory
987    // mapping. Volatile read ensures the hardware sees the access.
988    Some(unsafe { core::ptr::read_volatile(virt as *const u32) })
989}
990
991/// Write a PCI config register using memory-mapped ECAM access (PCIe).
992#[cfg(target_arch = "x86_64")]
993pub fn ecam_write_config(
994    ecam_base: u64,
995    bus: u8,
996    device: u8,
997    func: u8,
998    offset: u16,
999    value: u32,
1000) -> bool {
1001    if offset > 0xFFF {
1002        return false;
1003    }
1004    let phys = ecam_base as usize
1005        + ((bus as usize) << 20)
1006        + ((device as usize) << 15)
1007        + ((func as usize) << 12)
1008        + (offset as usize & 0xFFC);
1009
1010    if let Some(virt) = crate::arch::x86_64::msr::phys_to_virt(phys) {
1011        // SAFETY: Same as ecam_read_config. Volatile write to ECAM MMIO region.
1012        unsafe {
1013            core::ptr::write_volatile(virt as *mut u32, value);
1014        }
1015        true
1016    } else {
1017        false
1018    }
1019}