strat9_kernel/syscall/
pci.rs1use super::error::SyscallError;
6use crate::{
7 arch::x86_64::pci,
8 memory::{UserSliceRead, UserSliceWrite},
9};
10
11const 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
69pub 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
151pub 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
177pub 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}