Skip to main content

strat9_kernel/syscall/
pci.rs

1//! PCI syscall handlers.
2//!
3//! Provides userspace PCI enumeration and configuration space access.
4
5use super::error::SyscallError;
6use crate::{
7    arch::x86_64::pci,
8    memory::{UserSliceRead, UserSliceWrite},
9};
10
11/// PCI match flags for probe criteria.
12const PCI_MATCH_VENDOR_ID: u32 = 1 << 0;
13const PCI_MATCH_DEVICE_ID: u32 = 1 << 1;
14const PCI_MATCH_CLASS_CODE: u32 = 1 << 2;
15const PCI_MATCH_SUBCLASS: u32 = 1 << 3;
16const PCI_MATCH_PROG_IF: u32 = 1 << 4;
17
18#[derive(Debug, Clone, Copy)]
19#[repr(C)]
20struct PciAddressAbi {
21    bus: u8,
22    device: u8,
23    function: u8,
24    _reserved: u8,
25}
26
27#[derive(Debug, Clone, Copy)]
28#[repr(C)]
29struct PciDeviceInfoAbi {
30    address: PciAddressAbi,
31    vendor_id: u16,
32    device_id: u16,
33    class_code: u8,
34    subclass: u8,
35    prog_if: u8,
36    revision: u8,
37    header_type: u8,
38    interrupt_line: u8,
39    interrupt_pin: u8,
40    _reserved: u16,
41}
42
43#[derive(Debug, Clone, Copy)]
44#[repr(C)]
45struct PciProbeCriteriaAbi {
46    match_flags: u32,
47    vendor_id: u16,
48    device_id: u16,
49    class_code: u8,
50    subclass: u8,
51    prog_if: u8,
52    _reserved: [u8; 5],
53}
54
55fn read_pci_address(addr_ptr: u64) -> Result<pci::PciAddress, SyscallError> {
56    if addr_ptr == 0 {
57        return Err(SyscallError::Fault);
58    }
59    let user = UserSliceRead::new(addr_ptr, core::mem::size_of::<PciAddressAbi>())?;
60    let mut raw = [0u8; core::mem::size_of::<PciAddressAbi>()];
61    user.copy_to(&mut raw);
62    let abi = unsafe { core::ptr::read_unaligned(raw.as_ptr() as *const PciAddressAbi) };
63    if abi.device > 31 || abi.function > 7 {
64        return Err(SyscallError::InvalidArgument);
65    }
66    Ok(pci::PciAddress::new(abi.bus, abi.device, abi.function))
67}
68
69/// SYS_PCI_ENUM: enumerate PCI devices matching a probe criteria.
70pub fn sys_pci_enum(
71    criteria_ptr: u64,
72    out_ptr: u64,
73    max_entries: u64,
74) -> Result<u64, SyscallError> {
75    if criteria_ptr == 0 || out_ptr == 0 {
76        return Err(SyscallError::Fault);
77    }
78    if max_entries == 0 {
79        return Ok(0);
80    }
81    let max_entries = core::cmp::min(max_entries as usize, 4096);
82    let user_criteria =
83        UserSliceRead::new(criteria_ptr, core::mem::size_of::<PciProbeCriteriaAbi>())?;
84    let mut criteria_bytes = [0u8; core::mem::size_of::<PciProbeCriteriaAbi>()];
85    user_criteria.copy_to(&mut criteria_bytes);
86    let criteria_abi =
87        unsafe { core::ptr::read_unaligned(criteria_bytes.as_ptr() as *const PciProbeCriteriaAbi) };
88
89    let criteria = pci::ProbeCriteria {
90        vendor_id: if (criteria_abi.match_flags & PCI_MATCH_VENDOR_ID) != 0 {
91            Some(criteria_abi.vendor_id)
92        } else {
93            None
94        },
95        device_id: if (criteria_abi.match_flags & PCI_MATCH_DEVICE_ID) != 0 {
96            Some(criteria_abi.device_id)
97        } else {
98            None
99        },
100        class_code: if (criteria_abi.match_flags & PCI_MATCH_CLASS_CODE) != 0 {
101            Some(criteria_abi.class_code)
102        } else {
103            None
104        },
105        subclass: if (criteria_abi.match_flags & PCI_MATCH_SUBCLASS) != 0 {
106            Some(criteria_abi.subclass)
107        } else {
108            None
109        },
110        prog_if: if (criteria_abi.match_flags & PCI_MATCH_PROG_IF) != 0 {
111            Some(criteria_abi.prog_if)
112        } else {
113            None
114        },
115    };
116
117    let devices = pci::probe_all(criteria);
118    let count = core::cmp::min(devices.len(), max_entries);
119    let mut out = alloc::vec::Vec::<PciDeviceInfoAbi>::with_capacity(count);
120    for dev in devices.into_iter().take(count) {
121        out.push(PciDeviceInfoAbi {
122            address: PciAddressAbi {
123                bus: dev.address.bus,
124                device: dev.address.device,
125                function: dev.address.function,
126                _reserved: 0,
127            },
128            vendor_id: dev.vendor_id,
129            device_id: dev.device_id,
130            class_code: dev.class_code,
131            subclass: dev.subclass,
132            prog_if: dev.prog_if,
133            revision: dev.revision,
134            header_type: dev.header_type,
135            interrupt_line: dev.interrupt_line,
136            interrupt_pin: dev.interrupt_pin,
137            _reserved: 0,
138        });
139    }
140    let out_bytes_len = out
141        .len()
142        .checked_mul(core::mem::size_of::<PciDeviceInfoAbi>())
143        .ok_or(SyscallError::InvalidArgument)?;
144    let user_out = UserSliceWrite::new(out_ptr, out_bytes_len)?;
145    let out_bytes =
146        unsafe { core::slice::from_raw_parts(out.as_ptr() as *const u8, out_bytes_len) };
147    user_out.copy_from(out_bytes);
148    Ok(out.len() as u64)
149}
150
151/// SYS_PCI_CFG_READ: read from PCI configuration space.
152pub fn sys_pci_cfg_read(addr_ptr: u64, offset: u64, width: u64) -> Result<u64, SyscallError> {
153    let dev_addr = read_pci_address(addr_ptr)?;
154    let off = u8::try_from(offset).map_err(|_| SyscallError::InvalidArgument)?;
155    let w = u8::try_from(width).map_err(|_| SyscallError::InvalidArgument)?;
156    if !matches!(w, 1 | 2 | 4) {
157        return Err(SyscallError::InvalidArgument);
158    }
159    if off > 0xFC || (off as u16 + w as u16) > 0x100 {
160        return Err(SyscallError::InvalidArgument);
161    }
162    if (w == 2 && (off & 1) != 0) || (w == 4 && (off & 3) != 0) {
163        return Err(SyscallError::InvalidArgument);
164    }
165    let dev = pci::all_devices()
166        .into_iter()
167        .find(|d| d.address == dev_addr)
168        .ok_or(SyscallError::NotFound)?;
169    let value = match w {
170        1 => dev.read_config_u8(off) as u32,
171        2 => dev.read_config_u16(off) as u32,
172        _ => dev.read_config_u32(off),
173    };
174    Ok(value as u64)
175}
176
177/// SYS_PCI_CFG_WRITE: write to PCI configuration space.
178pub fn sys_pci_cfg_write(
179    addr_ptr: u64,
180    offset: u64,
181    width: u64,
182    value: u64,
183) -> Result<u64, SyscallError> {
184    let dev_addr = read_pci_address(addr_ptr)?;
185    let off = u8::try_from(offset).map_err(|_| SyscallError::InvalidArgument)?;
186    let w = u8::try_from(width).map_err(|_| SyscallError::InvalidArgument)?;
187    if !matches!(w, 1 | 2 | 4) {
188        return Err(SyscallError::InvalidArgument);
189    }
190    if off > 0xFC || (off as u16 + w as u16) > 0x100 {
191        return Err(SyscallError::InvalidArgument);
192    }
193    if (w == 2 && (off & 1) != 0) || (w == 4 && (off & 3) != 0) {
194        return Err(SyscallError::InvalidArgument);
195    }
196    let dev = pci::all_devices()
197        .into_iter()
198        .find(|d| d.address == dev_addr)
199        .ok_or(SyscallError::NotFound)?;
200    match w {
201        1 => dev.write_config_u8(off, value as u8),
202        2 => dev.write_config_u16(off, value as u16),
203        _ => dev.write_config_u32(off, value as u32),
204    }
205    Ok(0)
206}