kernel/device/pci/
mod.rs

1//! PCI (Peripheral Component Interconnect) bus module.
2//!
3//! This module provides support for PCI device discovery and management.
4//! It implements PCI configuration space access, device enumeration, and
5//! integration with the device manager.
6//!
7//! # Overview
8//!
9//! PCI is a standard bus for connecting peripheral devices to a computer system.
10//! This implementation focuses on PCIe (PCI Express) using ECAM (Enhanced Configuration
11//! Access Mechanism) which is commonly used on RISC-V and ARM platforms.
12//!
13//! # Architecture
14//!
15//! The PCI subsystem consists of several components:
16//! - **Configuration Space Access**: Reading and writing PCI configuration registers
17//! - **Device Enumeration**: Scanning the PCI bus tree to discover devices
18//! - **Device Information**: Representing PCI device properties (vendor, device ID, etc.)
19//! - **Driver Matching**: Matching PCI devices with appropriate drivers
20//!
21//! # Integration with DeviceManager
22//!
23//! PCI devices are discovered and registered with the DeviceManager, similar to
24//! platform devices. The PCI subsystem provides:
25//! - `PciDeviceInfo`: Device information structure implementing `DeviceInfo` trait
26//! - `PciDeviceDriver`: Driver structure implementing `DeviceDriver` trait
27//!
28//! # Usage
29//!
30//! ```rust,no_run
31//! use crate::device::pci::PciBus;
32//! use crate::device::manager::DeviceManager;
33//!
34//! // Initialize PCI bus with ECAM base address from device tree
35//! let pci_bus = PciBus::new(ecam_base_addr, ecam_size);
36//!
37//! // Scan for devices and register with DeviceManager
38//! pci_bus.scan_and_register();
39//! ```
40//!
41//! # Configuration Space Layout
42//!
43//! PCI configuration space is 256 bytes (4KB for PCIe) per function:
44//! - 0x00-0x3F: Standard PCI configuration header
45//! - 0x40-0xFF: Device-specific configuration
46//! - 0x100-0xFFF: PCIe extended configuration (PCIe only)
47//!
48
49pub mod config;
50pub mod device;
51pub mod driver;
52pub mod scan;
53
54extern crate alloc;
55
56use alloc::vec::Vec;
57use spin::mutex::Mutex;
58
59/// PCI device address components
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct PciAddress {
62    /// PCI segment/domain (usually 0)
63    pub segment: u16,
64    /// Bus number (0-255)
65    pub bus: u8,
66    /// Device number (0-31)
67    pub device: u8,
68    /// Function number (0-7)
69    pub function: u8,
70}
71
72impl PciAddress {
73    /// Create a new PCI address
74    pub const fn new(segment: u16, bus: u8, device: u8, function: u8) -> Self {
75        Self {
76            segment,
77            bus,
78            device,
79            function,
80        }
81    }
82
83    /// Calculate ECAM offset for this address
84    ///
85    /// ECAM uses a flat memory mapping where each function gets 4KB:
86    /// offset = (bus << 20) | (device << 15) | (function << 12)
87    pub const fn ecam_offset(&self) -> usize {
88        ((self.bus as usize) << 20)
89            | ((self.device as usize) << 15)
90            | ((self.function as usize) << 12)
91    }
92}
93
94/// PCI bus manager
95///
96/// Manages PCI device discovery and configuration space access.
97pub struct PciBus {
98    /// ECAM (Enhanced Configuration Access Mechanism) base address
99    ecam_base: usize,
100    /// ECAM region size in bytes
101    ecam_size: usize,
102    /// List of discovered PCI devices
103    devices: Mutex<Vec<device::PciDeviceInfo>>,
104}
105
106impl PciBus {
107    /// Create a new PCI bus manager
108    ///
109    /// # Arguments
110    ///
111    /// * `ecam_base` - Physical address of the ECAM region
112    /// * `ecam_size` - Size of the ECAM region in bytes
113    ///
114    /// # Returns
115    ///
116    /// A new `PciBus` instance
117    pub const fn new(ecam_base: usize, ecam_size: usize) -> Self {
118        Self {
119            ecam_base,
120            ecam_size,
121            devices: Mutex::new(Vec::new()),
122        }
123    }
124
125    /// Get the ECAM base address
126    pub const fn ecam_base(&self) -> usize {
127        self.ecam_base
128    }
129
130    /// Get the ECAM size
131    pub const fn ecam_size(&self) -> usize {
132        self.ecam_size
133    }
134
135    /// Check if a PCI address is within the ECAM region
136    pub fn is_valid_address(&self, addr: &PciAddress) -> bool {
137        let offset = addr.ecam_offset();
138        offset + 0x1000 <= self.ecam_size // Each function needs 4KB
139    }
140
141    /// Get the list of discovered devices
142    pub fn devices(&self) -> Vec<device::PciDeviceInfo> {
143        let devices = self.devices.lock();
144        devices.clone()
145    }
146
147    /// Add a device to the list of discovered devices
148    pub fn add_device(&self, device: device::PciDeviceInfo) {
149        let mut devices = self.devices.lock();
150        devices.push(device);
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test_case]
159    fn test_pci_address_ecam_offset() {
160        let addr = PciAddress::new(0, 0, 0, 0);
161        assert_eq!(addr.ecam_offset(), 0);
162
163        let addr = PciAddress::new(0, 1, 0, 0);
164        assert_eq!(addr.ecam_offset(), 1 << 20);
165
166        let addr = PciAddress::new(0, 0, 1, 0);
167        assert_eq!(addr.ecam_offset(), 1 << 15);
168
169        let addr = PciAddress::new(0, 0, 0, 1);
170        assert_eq!(addr.ecam_offset(), 1 << 12);
171
172        let addr = PciAddress::new(0, 1, 2, 3);
173        assert_eq!(addr.ecam_offset(), (1 << 20) | (2 << 15) | (3 << 12));
174    }
175
176    #[test_case]
177    fn test_pci_bus_creation() {
178        let pci_bus = PciBus::new(0x3000_0000, 0x1000_0000);
179        assert_eq!(pci_bus.ecam_base(), 0x3000_0000);
180        assert_eq!(pci_bus.ecam_size(), 0x1000_0000);
181    }
182
183    #[test_case]
184    fn test_pci_address_validity() {
185        let pci_bus = PciBus::new(0x3000_0000, 0x100000); // 1MB ECAM region
186
187        // Valid addresses
188        let addr = PciAddress::new(0, 0, 0, 0);
189        assert!(pci_bus.is_valid_address(&addr));
190
191        // Address at the edge (offset + 4KB <= size)
192        let max_offset = 0x100000 - 0x1000; // 1MB - 4KB
193        let max_bus = (max_offset >> 20) as u8;
194        let addr = PciAddress::new(0, max_bus, 0, 0);
195        assert!(pci_bus.is_valid_address(&addr));
196
197        // Invalid address (beyond ECAM region)
198        let addr = PciAddress::new(0, 255, 31, 7);
199        assert!(!pci_bus.is_valid_address(&addr));
200    }
201
202    #[test_case]
203    fn test_pci_bus_virtio_integration() {
204        use crate::device::DeviceDriver;
205        use crate::device::pci::device::PciDeviceInfo;
206        use crate::device::pci::driver::{PciDeviceDriver, PciDeviceId};
207
208        // Simulate a PCI bus with virtio devices
209        let pci_bus = PciBus::new(0x3000_0000, 0x1000_0000);
210
211        static mut VIRTIO_PROBED_COUNT: usize = 0;
212
213        // Create stub virtio-pci driver
214        let id_table = alloc::vec![
215            PciDeviceId::new(0x1AF4, 0x1000), // VirtIO net
216            PciDeviceId::new(0x1AF4, 0x1001), // VirtIO block
217        ];
218
219        let driver = PciDeviceDriver::new(
220            "virtio-pci-integration",
221            id_table,
222            |device| {
223                unsafe {
224                    VIRTIO_PROBED_COUNT += 1;
225                }
226                // Verify this is a virtio device
227                assert_eq!(device.vendor_id(), 0x1AF4);
228                assert!(device.device_id() == 0x1000 || device.device_id() == 0x1001);
229                Ok(())
230            },
231            |_device| Ok(()),
232        );
233
234        // Simulate discovered virtio-net device
235        let addr1 = PciAddress::new(0, 0, 1, 0);
236        let virtio_net = PciDeviceInfo::new(
237            addr1,
238            0x1AF4,
239            0x1000,
240            0x020000,
241            0x00,
242            0x1AF4,
243            0x0001,
244            0x0B,
245            0x01,
246            "virtio_net",
247            1,
248        );
249        pci_bus.add_device(virtio_net.clone());
250
251        // Simulate discovered virtio-blk device
252        let addr2 = PciAddress::new(0, 0, 2, 0);
253        let virtio_blk = PciDeviceInfo::new(
254            addr2,
255            0x1AF4,
256            0x1001,
257            0x010000,
258            0x00,
259            0x1AF4,
260            0x0002,
261            0x0B,
262            0x01,
263            "virtio_blk",
264            2,
265        );
266        pci_bus.add_device(virtio_blk.clone());
267
268        // Verify devices were added
269        let devices = pci_bus.devices();
270        assert_eq!(devices.len(), 2);
271
272        // Probe devices with driver using DeviceDriver trait
273        for device in devices {
274            if driver.matches_device(&device) {
275                // Use the DeviceDriver trait's probe method
276                let result = DeviceDriver::probe(&driver, &device);
277                assert!(result.is_ok());
278            }
279        }
280
281        // Verify both devices were probed
282        assert_eq!(unsafe { VIRTIO_PROBED_COUNT }, 2);
283    }
284}