mycelium_pci/
config.rs

1//! PCI Bus Configuration Space
2//!
3//! This module implements the PCI "configuration space access mechanism #1" as
4//! described [on the OSDev Wiki here][wiki].
5//!
6//! [wiki]: https://wiki.osdev.org/Pci#Configuration_Space_Access_Mechanism_.231
7use 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            // if the device is multifunction, enumerate up to 8 additional functions.
30            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
44/// Enumerate all PCI buses
45pub fn enumerate_all() -> impl Iterator<Item = (Address, Device)> {
46    (0..=255u8).flat_map(enumerate_bus)
47}
48
49bitfield! {
50    /// The PCI bus `CONFIG_ADDRESS` register (port `0xCF8`).
51    ///
52    /// |Bit 31    |Bits 30-24|Bits 23-16|Bits 15-11   |Bits 10-8      |Bits 7-0       |
53    /// |:---------|----------|----------|:------------|:--------------|:--------------|
54    /// |Enable Bit|Reserved  |Bus Number|Device Number|Function Number|Register Offset|
55    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                // tfw no cardbus trans pointer T___T
91                let cardbus_cis_ptr = self.read_offset(0x28);
92                let subsystem = {
93                    let word = self.read_offset(0x2C);
94                    device::SubsystemId {
95                        // XXX(eliza): if the subsystem vendor ID is 0xFFFF,
96                        // does this mean "no subsystem"?
97                        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                // the rest of this word is ~~garbage~~ reserved
103                let cap_ptr = self.read_offset(0x34) as u8;
104                // 0x38 is reserved
105                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        // Off | Bits 31-24    | Bits 23-16    | Bits 15-8     | Bits 7-0      |
151        // 0x0 | Device ID                     | Vendor ID                     |
152        // 0x4 | Status                        | Command                       |
153        // 0x8 | Class code   | Subclass       | Prog IF        | Revision ID  |
154        // 0xC | BIST         | Header type    | Latency Timer  | Cacheline Sz |
155        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        // vendor ID 0xFFFF is reserved for nonexistent devices.
184        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}