1use crate::{
8 addr::AddressBits,
9 class, device,
10 error::{self, unexpected, UnexpectedValue},
11 register, Address, Device,
12};
13use hal_x86_64::cpu::Port;
14use mycelium_bitfield::{bitfield, pack};
15
16#[derive(Debug)]
17pub struct ConfigReg {
18 data_port: Port,
19 addr_port: Port,
20 addr: ConfigAddress,
21}
22
23pub fn enumerate_bus(bus: u8) -> impl Iterator<Item = (Address, Device)> {
24 let addrs = (0..32u8).map(move |device| Address::new().with_bus(bus).with_device(device));
25
26 addrs
27 .filter_map(|addr| Some((addr, ConfigReg::new(addr).read_device()?)))
28 .flat_map(|(addr, device)| {
29 let function_nums = device
31 .header
32 .is_multifunction()
33 .then_some(1..8)
34 .into_iter()
35 .flatten();
36 let functions = function_nums.filter_map(move |function| {
37 let addr = addr.with_function(function);
38 Some((addr, ConfigReg::new(addr).read_device()?))
39 });
40 core::iter::once((addr, device)).chain(functions)
41 })
42}
43
44pub fn enumerate_all() -> impl Iterator<Item = (Address, Device)> {
46 (0..=255u8).flat_map(enumerate_bus)
47}
48
49bitfield! {
50 struct ConfigAddress<u32> {
56 const REGISTER_OFFSET: u8;
57 const FUNCTION = 3;
58 const DEVICE = 5;
59 const BUS: u8;
60 const _RESERVED = 7;
61 const ENABLE: bool;
62 }
63}
64
65impl ConfigReg {
66 pub fn new(addr: Address) -> Self {
67 Self::try_from(addr).expect("invalid config register address")
68 }
69
70 pub fn try_from(addr: Address) -> Result<Self, UnexpectedValue<Address>> {
71 Ok(Self {
72 data_port: Port::at(DATA_PORT),
73 addr_port: Port::at(ADDRESS_PORT),
74 addr: ConfigAddress::from_address(addr)?,
75 })
76 }
77
78 pub fn read_device(&self) -> Option<device::Device> {
79 let header = self.read_header()?;
80 let details = match header.header_type() {
81 Ok(device::HeaderType::Standard) => {
82 let base_addrs = [
83 self.read_offset(0x10),
84 self.read_offset(0x14),
85 self.read_offset(0x18),
86 self.read_offset(0x1C),
87 self.read_offset(0x20),
88 self.read_offset(0x24),
89 ];
90 let cardbus_cis_ptr = self.read_offset(0x28);
92 let subsystem = {
93 let word = self.read_offset(0x2C);
94 device::SubsystemId {
95 vendor_id: (word & 0xffff) as u16,
98 subsystem: (word >> 16) as u16,
99 }
100 };
101 let exp_rom_base_addr = self.read_offset(0x30);
102 let cap_ptr = self.read_offset(0x34) as u8;
104 let [max_latency, min_grant, irq_pin, irq_line] =
106 self.read_offset(0x3c).to_le_bytes();
107 device::Kind::Standard(device::StandardDetails {
108 base_addrs,
109 cardbus_cis_ptr,
110 subsystem,
111 exp_rom_base_addr,
112 cap_ptr,
113 _res0: [0; 7],
114 irq_line,
115 irq_pin,
116 min_grant,
117 max_latency,
118 })
119 }
120 Ok(device::HeaderType::CardBusBridge) => {
121 tracing::debug!(device = ?self.addr, "skipping CardBus-to-PCI bridge; not yet implemented");
122 return None;
123 }
124 Ok(device::HeaderType::PciBridge) => {
125 tracing::debug!(device = ?self.addr, "skipping PCI-to-PCI bridge; not yet implemented");
126 return None;
127 }
128 Err(err) => {
129 tracing::warn!(device = ?self.addr, %err, "invalid header type! skipping device");
130 return None;
131 }
132 };
133
134 Some(device::Device { header, details })
135 }
136
137 pub fn read_command_status(&self) -> (register::Command, register::Status) {
138 let word = self.read_command_status_reg();
139 let command = word.get(register::RegisterWord::COMMAND);
140 let status = word.get(register::RegisterWord::STATUS);
141 (command, status)
142 }
143
144 fn read_command_status_reg(&self) -> register::RegisterWord {
145 let word = self.read_offset(offsets::COMMAND_STATUS);
146 register::RegisterWord::from_bits(word)
147 }
148
149 pub fn read_header(&self) -> Option<device::Header> {
150 let id = self.read_device_id()?;
156 let (command, status) = self.read_command_status();
157 let [revision_id, prog_if, subclass, class] = self.read_offset(0x8).to_le_bytes();
158 let class = class::RawClasses { class, subclass };
159
160 let [cache_line_size, latency_timer, header_type, bist] =
161 self.read_offset(0xC).to_le_bytes();
162 let header_type = device::HeaderTypeReg::from_bits(header_type);
163 let bist = register::Bist::from_bits(bist);
164
165 Some(device::Header {
166 id,
167 command,
168 status,
169 revision_id,
170 prog_if,
171 class,
172 cache_line_size,
173 latency_timer,
174 header_type,
175 bist,
176 })
177 }
178
179 pub fn read_device_id(&self) -> Option<device::RawIds> {
180 let id = self.read_offset(0);
181 let vendor_id = (id & 0xFFFF) as u16;
182
183 if vendor_id == 0xFFFF {
185 return None;
186 }
187
188 Some(device::RawIds {
189 vendor_id,
190 device_id: (id >> 16) as u16,
191 })
192 }
193
194 pub fn send_command(
195 &self,
196 f: impl FnOnce(register::Status, register::Command) -> register::Command,
197 ) {
198 use register::RegisterWord;
199 let word = self.read_command_status_reg();
200 let command = f(
201 word.get(RegisterWord::STATUS),
202 word.get(RegisterWord::COMMAND),
203 );
204 self.write_offset(
205 offsets::COMMAND_STATUS,
206 word.with(RegisterWord::COMMAND, command).bits(),
207 );
208 }
209
210 fn read_offset(&self, offset: u8) -> u32 {
211 let addr = self.addr.with(ConfigAddress::REGISTER_OFFSET, offset);
212 unsafe {
213 self.addr_port.writel(addr.bits());
214 self.data_port.readl()
215 }
216 }
217
218 fn write_offset(&self, offset: u8, word: u32) {
219 let addr = self.addr.with(ConfigAddress::REGISTER_OFFSET, offset);
220 unsafe {
221 self.addr_port.writel(addr.bits());
222 self.data_port.writel(word)
223 }
224 }
225}
226
227const ADDRESS_PORT: u16 = 0xCF8;
228const DATA_PORT: u16 = 0xCFC;
229
230impl ConfigAddress {
231 const BUS_PAIR: pack::Pair32 = Self::BUS
232 .typed::<u32, ()>()
233 .pair_with(AddressBits::BUS.typed::<_, AddressBits>());
234 const DEVICE_PAIR: pack::Pair32 = Self::DEVICE.pair_with(AddressBits::DEVICE);
235 const FUNCTION_PAIR: pack::Pair32 = Self::FUNCTION.pair_with(AddressBits::FUNCTION);
236
237 fn from_address(addr: Address) -> Result<Self, error::UnexpectedValue<Address>> {
238 if addr.group().is_some() {
239 return Err(unexpected(addr)
240 .named("only PCI Express addresses may contain extended segment groups"));
241 }
242 let addr_bits = addr.bitfield().bits();
243 let bits = pack::Pack32::pack_in(0)
244 .pack_from_src(addr_bits, &Self::BUS_PAIR)
245 .pack_from_src(addr_bits, &Self::DEVICE_PAIR)
246 .pack_from_src(addr_bits, &Self::FUNCTION_PAIR)
247 .pack(true, &Self::ENABLE)
248 .bits();
249 Ok(Self::from_bits(bits))
250 }
251}
252
253mod offsets {
254 pub(super) const COMMAND_STATUS: u8 = 0x4;
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use proptest::{prop_assert, prop_assert_eq, proptest};
261
262 #[test]
263 fn config_addr_is_valid() {
264 ConfigAddress::assert_valid();
265 assert_eq!(ConfigAddress::ENABLE.least_significant_index(), 31);
266 }
267
268 #[test]
269 fn config_addr_pairs_valid() {
270 ConfigAddress::BUS_PAIR.assert_valid();
271 ConfigAddress::DEVICE_PAIR.assert_valid();
272 ConfigAddress::FUNCTION_PAIR.assert_valid();
273
274 println!("BUS_PAIR: {:#?}", ConfigAddress::BUS_PAIR);
275 println!("DEVICE_PAIR: {:#?}", ConfigAddress::DEVICE_PAIR);
276 println!("FUNCTION_PAIR: {:#?}", ConfigAddress::FUNCTION_PAIR);
277 }
278
279 #[test]
280 fn config_addr_fn_pair() {
281 let addr = Address::new().with_function(3);
282 let addr_bits = addr.bitfield().bits();
283 let bits = pack::Pack32::pack_in(0)
284 .pack_from_src(addr_bits, &ConfigAddress::FUNCTION_PAIR)
285 .bits();
286 let config_addr = ConfigAddress::from_bits(bits);
287 assert_eq!(
288 config_addr.get(ConfigAddress::FUNCTION),
289 addr.bitfield().get(AddressBits::FUNCTION),
290 "\n{config_addr}"
291 );
292 }
293
294 proptest! {
295 #[test]
296 fn config_address_from_address(bus in 0u8..255u8, device in 0u8..32u8, function in 0u8..8u8) {
297 let addr = Address::new().with_bus(bus).with_device(device).with_function(function);
298
299 let config_addr = ConfigAddress::from_address(addr);
300 prop_assert!(config_addr.is_ok(), "converting address {addr} to ConfigAddress failed unexpectedly");
301 let config_addr = config_addr.unwrap();
302
303 prop_assert_eq!(
304 config_addr.get(ConfigAddress::BUS),
305 bus,
306 "\n addr: {:?}\n cfg:\n{}", addr, config_addr
307 );
308 prop_assert_eq!(
309 config_addr.get(ConfigAddress::DEVICE) as u8,
310 device,
311 "\n addr: {:?}\n cfg:\n{}", addr, config_addr
312 );
313 prop_assert_eq!(
314 config_addr.get(ConfigAddress::FUNCTION) as u8,
315 function,
316 "\n addr: {:?}\n cfg:\n{}", addr, config_addr
317 );
318
319 }
320 }
321}