mycelium_pci/
device.rs

1use crate::{
2    class::{Class, Classes, RawClasses, Subclass},
3    error, register,
4};
5pub use bar::BaseAddress;
6use mycelium_util::fmt;
7pub use pci_ids::{Device as KnownId, ProgIf as KnownProgIf, Vendor};
8
9mod bar;
10
11/// A PCI device header.
12///
13/// This stores data common to all PCI devices, whether they are [standard PCI
14/// devices](StandardDetails), [PCI-to-PCI bridges](PciBridgeDetails), or
15/// [PCI-to-CardBus bridges](CardBusDetails).
16///
17/// The header has the following layout:
18///
19/// | Word | Bits 31-24    | Bits 23-16    | Bits 15-8      | Bits 7-0        |
20/// |------|---------------|---------------|----------------|-----------------|
21/// | 0x0  |[Device ID]    | ...           |[Vendor ID]     | ...             |
22/// | 0x1  |[`Status`]     | ...           |[`Command`]     | ...             |
23/// | 0x2  |[`Class`]      |[`Subclass`]   |[`ProgIf`]      |[Revision ID]    |
24/// | 0x3  |[BIST] register|[`HeaderType`] |[Latency timer] |[Cache line size]|
25///
26/// Much of the documentation for this struct's fields was copied from [the
27/// OSDev Wiki][wiki].
28///
29/// [Device ID]: Id
30/// [Vendor ID]: Vendor
31/// [Revision ID]: #structfield.revision_id
32/// [Latency timer]: #structfield.latency_timer
33/// [Cache line size]: #structfield.cache_line_size
34/// [`Status`]: register::Status
35/// [`Command`]: register::Command
36/// [BIST]: register::Bist
37/// [wiki]: https://wiki.osdev.org/Pci#Common_Header_Fields
38#[derive(Debug, Copy, Clone)]
39#[repr(C)]
40pub struct Header {
41    /// The device's raw [vendor ID](Vendor) and [device ID](Id).
42    ///
43    /// Use the [`Header::id`] method to attempt to resolve the raw numeric IDs
44    /// to a known vendor and device in the PCI IDs database.
45    pub id: RawIds,
46    /// The device's [`Command`] register.
47    ///
48    /// This register provides control over a device's ability to generate and
49    /// respond to PCI cycles. When a 0 is written to this register, the device
50    /// is disconnected from the PCI bus. Other values may be written to this
51    /// register to send other commands, depending on the device.
52    ///
53    /// [`Command`]: register::Command
54    pub command: register::Command,
55    /// The device's [`Status`] register.
56    ///
57    /// This register can be read from to access status information about PCI
58    /// events.
59    ///
60    /// [`Status`]: register::Command
61    pub status: register::Status,
62    /// Specifies a revision identifier for a particular device.
63    ///
64    /// Revision IDs are allocated by the device's vendor.
65    pub revision_id: u8,
66    /// Programming interface.
67    ///
68    /// A read-only register that specifies a register-level programming
69    /// interface the device has, if it has any at all.
70    ///
71    /// This is often used alongside the device's class and subclass to
72    /// determine how to interact with the device.
73    pub(crate) prog_if: u8,
74    /// The device's class and subclass.
75    ///
76    /// See the [`class`](crate::class) module for details.
77    pub(crate) class: RawClasses,
78    /// Specifies the system cache line size in 32-bit units.
79    ///
80    /// A device can limit the number of cacheline sizes it can support, if a
81    /// unsupported value is written to this field, the device will behave as if
82    /// a value of 0 was written.
83    pub cache_line_size: u8,
84    /// Specifies the latency timer in units of PCI bus clocks.
85    pub latency_timer: u8,
86    /// Identifies the [device kind] and the layout of the rest of the
87    /// device's PCI configuration space header.
88    ///
89    /// A device is one of the following:
90    ///
91    /// - A standard PCI device ([`StandardDetails`])
92    /// - A PCI-to-PCI bridge ([`PciBridgeDetails`])
93    /// - A PCI-to-CardBus bridge ([`CardBusDetails`])
94    ///
95    /// [device kind]: Kind
96    pub(crate) header_type: HeaderTypeReg,
97    /// A read-write register for running the device's Built-In Self Test
98    /// (BIST).
99    pub bist: register::Bist,
100}
101
102#[derive(Debug)]
103pub struct Device {
104    pub header: Header,
105    pub details: Kind,
106}
107
108#[derive(Debug, Copy, Clone, Eq, PartialEq)]
109#[repr(C)]
110pub struct RawIds {
111    /// Identifies the manufacturer of the device.
112    ///
113    /// PCI vendor IDs are allocated by PCI-SIG to ensure uniqueness; a complete
114    /// list is available [here]. Vendor ID `0xFFFF` is reserved to indicate
115    /// that a device is not present.
116    ///
117    /// [here]: https://pcisig.com/membership/member-companies
118    pub vendor_id: u16,
119    /// Identifies the specific device.
120    ///
121    /// Device IDs are allocated by the device's vendor.
122    pub device_id: u16,
123}
124
125/// Represents a device's [vendor ID] and [device ID].
126///
127/// [vendor ID]: Vendor
128/// [known ID]: KnownId
129#[derive(Copy, Clone, Eq, PartialEq)]
130pub enum Id {
131    /// A known ID.
132    ///
133    /// This variant is returned if the device's vendor ID and device ID exist
134    /// in the PCI ID database, and information such as a string representing
135    /// the name of the vendor or device can be returned.
136    Known(&'static KnownId),
137
138    /// An unknown ID.
139    ///
140    /// This variant is returned if the device's vendor ID or device ID were not
141    /// found in the PCI ID database.
142    ///
143    /// This is not necessarily an error, as it may indicate that the database
144    /// has not been updated to include a newer device, the device is a
145    /// prototype that has not yet been registered with PCI-SIG, or the device
146    /// is not a "real" device but an emulated device provided by a virtual
147    /// machine. An unknown device may still be used, provided that its
148    /// [`Class`], [`Subclass`], and [prog IF] provide sufficient information to
149    /// program that device.
150    ///
151    /// [prog IF]: Header#structfield.prog_if
152    Unknown(RawIds),
153}
154
155mycelium_bitfield::bitfield! {
156    #[derive(PartialEq, Eq)]
157    pub(crate) struct HeaderTypeReg<u8> {
158        /// Indicates the type of device and the layout of the header.
159        pub(crate) const TYPE: HeaderType;
160        const _RESERVED = 5;
161        /// Indicates that this device has multiple functions.
162        pub(crate) const MULTIFUNCTION: bool;
163    }
164}
165
166/// Describes the type of device header.
167///
168/// This indicates whether the device is a [standard PCI
169/// device](StandardDetails), a [PCI-to-PCI bridge](PciBridgeDetails), or a
170/// [PCI-to-CardBus bridge](CardBusDetails).
171
172#[derive(Copy, Clone, Debug, Eq, PartialEq)]
173#[repr(u8)]
174pub enum HeaderType {
175    /// This is a [standard PCI device](StandardDetails).
176    Standard = 0x00,
177    /// This is a [PCI-to-PCI bridge device](PciBridgeDetails).
178    PciBridge = 0x01,
179    /// This is a [PCI-to-CardBus bridge device](CardBusDetails).
180    CardBusBridge = 0x02,
181}
182
183impl mycelium_bitfield::FromBits<u8> for HeaderType {
184    type Error = error::UnexpectedValue<u8>;
185    const BITS: u32 = 2;
186
187    fn try_from_bits(bits: u8) -> Result<Self, Self::Error> {
188        match bits {
189            bits if bits == Self::Standard as u8 => Ok(Self::Standard),
190            bits if bits == Self::PciBridge as u8 => Ok(Self::PciBridge),
191            bits if bits == Self::CardBusBridge as u8 => Ok(Self::CardBusBridge),
192            bits => Err(error::unexpected(bits)),
193        }
194    }
195
196    fn into_bits(self) -> u8 {
197        self as u8
198    }
199}
200
201/// Details describing specific types of device.
202#[derive(Debug)]
203pub enum Kind {
204    /// A standard PCI device (not a bridge)
205    Standard(StandardDetails),
206
207    /// A PCI-to-PCI bridge device.
208    PciBridge(PciBridgeDetails),
209
210    /// A PCI-to-CardBus bridge device.
211    CardBus(CardBusDetails),
212}
213
214/// Describes a device's register-level programming interface.
215#[derive(Debug, Copy, Clone)]
216pub enum ProgIf {
217    /// The device's prog IF code was found in the PCI IDs database, and is
218    /// associated with a named interface.
219    Known(&'static KnownProgIf),
220
221    /// The device's prog IF code was not found in the PCI IDs database.
222    Unknown(u8),
223}
224
225/// A header describing a standard PCI device (not a bridge).
226///
227/// Much of the documentation for this struct's fields was copied from [the
228/// OSDev Wiki][1].
229///
230/// [1]: https://wiki.osdev.org/Pci#Header_Type_0x0
231#[derive(Debug)]
232#[repr(C)]
233pub struct StandardDetails {
234    pub(crate) base_addrs: [u32; 6],
235    /// Points to the Card Information Structure and is used by devices that
236    /// share silicon between CardBus and PCI.
237    pub cardbus_cis_ptr: u32,
238    pub subsystem: SubsystemId,
239    /// Expansion ROM base address.
240    pub exp_rom_base_addr: u32,
241    /// Points (i.e. an offset into this function's configuration space) to a
242    /// linked list of new capabilities implemented by the device. Used if bit 4
243    /// of the status register (Capabilities List bit) is set to 1. The bottom
244    /// two bits are reserved and should be masked before the Pointer is used to
245    /// access the Configuration Space.
246    pub cap_ptr: u8,
247    pub(crate) _res0: [u8; 7],
248    /// Specifies which input of the system interrupt controllers the device's
249    /// interrupt pin is connected to and is implemented by any device that
250    /// makes use of an interrupt pin.
251    ///
252    /// For the x86 architectures, this register corresponds to the PIC IRQ
253    /// numbers 0-15 (and not I/O APIC IRQ numbers) and a value of 0xFF defines
254    /// no connection.
255    pub irq_line: u8,
256    /// Specifies which interrupt pin the device uses.
257    ///
258    /// A value of `0x1` is `INTA#`, `0x2` is `INTB#`, `0x3` is `INTC#`, `0x4`
259    /// is `INTD#`, and `0x0` means the device does not use an interrupt pin.
260    pub(crate) irq_pin: u8,
261    /// A read-only register that specifies the burst period length,
262    /// in 1/4 microsecond units, that the device needs (assuming a 33 MHz clock
263    /// rate).
264    pub min_grant: u8,
265    /// A read-only register that specifies how often the device needs access to
266    /// the PCI bus (in 1/4 microsecond units).
267    pub max_latency: u8,
268}
269
270#[derive(Debug)]
271#[repr(C)]
272pub struct PciBridgeDetails {
273    pub(crate) base_addrs: [u32; 2],
274    // WIP
275}
276
277#[derive(Debug)]
278#[repr(C)]
279pub struct CardBusDetails {
280    // WIP
281}
282
283#[derive(Debug)]
284#[repr(C)]
285pub struct SubsystemId {
286    pub(crate) vendor_id: u16,
287    pub(crate) subsystem: u16,
288}
289
290/// Specifies which interrupt pin a standard PCI device uses.
291///
292/// A value of `0x1` is `INTA#`, `0x2` is `INTB#`, `0x3` is `INTC#`, `0x4` is
293/// `INTD#`, and `0x0` means the device does not use an interrupt pin.
294#[derive(Copy, Clone, Debug, Eq, PartialEq)]
295#[repr(u8)]
296pub enum IrqPin {
297    IntA = 0x1,
298    IntB = 0x2,
299    IntC = 0x3,
300    IntD = 0x4,
301}
302
303impl Header {
304    /// Returns the device's header type, or an error if the header type code is
305    /// invalid.
306    ///
307    /// This value identifies the [device kind], and the layout of the rest of
308    /// the device's PCI configuration space header.
309    ///
310    /// A device is one of the following:
311    ///
312    /// - A standard PCI device ([`StandardDetails`])
313    /// - A PCI-to-PCI bridge ([`PciBridgeDetails`])
314    /// - A PCI-to-CardBus bridge ([`CardBusDetails`])
315    ///
316    /// [device kind]: Kind
317    #[inline]
318    pub fn header_type(&self) -> Result<HeaderType, error::UnexpectedValue<u8>> {
319        self.header_type
320            .try_get(HeaderTypeReg::TYPE)
321            .map_err(|e| e.named("header type"))
322    }
323
324    /// Returns `true` if this device has multiple functions.
325    #[inline]
326    #[must_use]
327    pub fn is_multifunction(&self) -> bool {
328        self.header_type.get(HeaderTypeReg::MULTIFUNCTION)
329    }
330
331    /// Returns the device's [`Id`].
332    ///
333    /// This will attempt to resolve the device's [vendor ID](Vendor) and
334    /// [device ID](KnownId) in the PCI IDs database. If the device ID can be
335    /// resolved, an [`Id::Known`] is returned. Otherwise, if the device's
336    /// vendor ID or device ID does not exist in the PCI ID database, an
337    /// [`Id::Unknown`] is returned.
338    #[inline]
339    #[must_use]
340    pub fn id(&self) -> Id {
341        self.id.resolve()
342    }
343
344    /// Returns the device's [`Classes`] (its [`Class`] and [`Subclass`]), or an
345    /// error if this header's class or subclass code does not exist in the PCI
346    /// ID database.
347    #[inline]
348    pub fn classes(&self) -> Result<Classes, error::UnexpectedValue<RawClasses>> {
349        self.class.resolve()
350    }
351
352    /// Returns the device's [`Class`].
353    ///
354    /// The class indicates what general category of device (e.g. a display
355    /// controller, network controller, etc) this header describes. The class
356    /// value is used to determine the meaning of the [`Subclass`] code, which
357    /// describes a subcategory of device within that class.
358    ///
359    /// # Returns
360    ///
361    /// - [`Ok`]`(`[`Class`]`)` if the header's class code exists in the PCI ID
362    ///   database.
363    /// - [`Err`]`(`[`error::UnexpectedValue`]`)` if the header's class code was
364    ///   not present in the PCI ID database. This generally indicates that the
365    ///   device header was read incorrectly or the device is malfunctioning, as
366    ///   all devices should have a known class code.
367    #[inline]
368    pub fn class(&self) -> Result<Class, error::UnexpectedValue<u8>> {
369        self.class.resolve_class()
370    }
371
372    /// Returns the device's [`Subclass`].
373    ///
374    /// The subclass describes a more specific category of device within a
375    /// [`Class`].
376    ///
377    /// # Returns
378    ///
379    /// - [`Ok`]`(`[`Subclass`]`)` if the header's class and subclass codes
380    ///   exist in the PCI ID database.
381    /// - [`Err`]`(`[`error::UnexpectedValue`]`)` if the header's class or
382    ///   subclass code was not present in the PCI ID database. This generally
383    ///   indicates that the device header was read incorrectly or the device is
384    ///   malfunctioning, as all devices should have known class and subclass
385    ///   codes.
386    #[inline]
387    pub fn subclass(&self) -> Result<Subclass, error::UnexpectedValue<u8>> {
388        self.class.resolve_subclass()
389    }
390
391    /// Returns the [register-level programming interface](ProgIf) ("prog IF")
392    /// for this device, or an error if the device's class or subclass code
393    /// (which are necessary for determining the programming interface) could
394    /// not be resolved.
395    pub fn prog_if(&self) -> Result<ProgIf, error::UnexpectedValue<u8>> {
396        let prog_if = self.class.resolve_subclass()?.prog_if(self.prog_if);
397        Ok(prog_if)
398    }
399
400    /// Returns the raw programming interface code of this device.
401    ///
402    /// This can be used with the [`Subclass::prog_if`] method.
403    #[inline]
404    #[must_use]
405    pub fn raw_prog_if(&self) -> u8 {
406        self.prog_if
407    }
408}
409
410impl StandardDetails {
411    /// Returns this device's base address registers (BARs).
412    ///
413    /// # Returns
414    ///
415    /// - [`Ok`] containing an array of [`Option`]`<`[`BaseAddress`]`>` if the
416    ///   BARs were successfully decoded.
417    /// - [`Err`]`(`[`error::UnexpectedValue`]`)` if a BAR value is invalid.
418    ///
419    /// [`BaseAddress`]: bar::BaseAddress
420    #[inline]
421    pub fn base_addrs(&self) -> Result<[Option<bar::BaseAddress>; 6], error::UnexpectedValue<u32>> {
422        bar::BaseAddress::decode_bars(&self.base_addrs)
423    }
424
425    /// Returns which IRQ pin this device uses.
426    ///
427    /// # Returns
428    ///
429    /// - [`Err`]`(`[`error::UnexpectedValue`]`)` if the value is not a valid IRQ
430    ///   pin.
431    /// - [`None`] if this device does not use an IRQ pin.
432    /// - [`Some`]`(`[`IrqPin`]`)` if this device specifies a valid IRQ pin.
433    pub fn irq_pin(&self) -> Result<Option<IrqPin>, error::UnexpectedValue<u8>> {
434        match self.irq_pin {
435            0x00 => Ok(None),
436            0x01 => Ok(Some(IrqPin::IntA)),
437            0x02 => Ok(Some(IrqPin::IntB)),
438            0x03 => Ok(Some(IrqPin::IntC)),
439            0x04 => Ok(Some(IrqPin::IntD)),
440            bits => Err(error::unexpected(bits).named("IRQ pin")),
441        }
442    }
443}
444
445impl PciBridgeDetails {
446    /// Returns this device's base address registers (BARs).
447    pub fn base_addrs(&self) -> Result<[Option<bar::BaseAddress>; 2], error::UnexpectedValue<u32>> {
448        bar::BaseAddress::decode_bars(&self.base_addrs)
449    }
450}
451
452// === impl RawIds ===
453
454impl RawIds {
455    pub fn resolve(self) -> Id {
456        match KnownId::from_vid_pid(self.vendor_id, self.device_id) {
457            Some(known) => Id::Known(known),
458            None => Id::Unknown(self),
459        }
460    }
461}
462
463impl fmt::LowerHex for RawIds {
464    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465        let Self {
466            device_id,
467            vendor_id,
468        } = self;
469        // should the formatted ID be prefaced with a leading `0x`?
470        let leading = if f.alternate() { "0x" } else { "" };
471        write!(f, "{leading}{device_id:x}:{vendor_id:x}")
472    }
473}
474
475// === impl Id ===
476
477impl Id {
478    #[inline]
479    #[must_use]
480    pub fn name(&self) -> Option<&'static str> {
481        match self {
482            Self::Known(known) => Some(known.name()),
483            _ => None,
484        }
485    }
486
487    #[inline]
488    #[must_use]
489    pub fn vendor(&self) -> Option<&'static Vendor> {
490        match self {
491            Self::Known(known) => Some(known.vendor()),
492            _ => None,
493        }
494    }
495
496    #[inline]
497    #[must_use]
498    pub fn device_id(&self) -> u16 {
499        match self {
500            Self::Known(known) => known.id(),
501            Self::Unknown(unknown) => unknown.device_id,
502        }
503    }
504
505    #[inline]
506    #[must_use]
507    pub fn vendor_id(&self) -> u16 {
508        match self {
509            Self::Known(known) => known.vendor().id(),
510            Self::Unknown(unknown) => unknown.vendor_id,
511        }
512    }
513}
514
515impl fmt::Display for Id {
516    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517        match self {
518            Self::Known(known) => write!(f, "{} {}", known.vendor().name(), known.name()),
519
520            Self::Unknown(RawIds {
521                vendor_id,
522                device_id,
523            }) => write!(f, "{vendor_id:#x}:{device_id:x}"),
524        }
525    }
526}
527
528impl fmt::Debug for Id {
529    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530        let mut s = f.debug_struct("Id");
531        match self {
532            Self::Known(known) => s
533                .field("vendor", known.vendor())
534                .field("device", known)
535                .finish(),
536
537            Self::Unknown(RawIds {
538                vendor_id,
539                device_id,
540            }) => s
541                .field("vendor", &fmt::hex(vendor_id))
542                .field("device", &fmt::hex(device_id))
543                .finish(),
544        }
545    }
546}
547
548// === impl ProgIf ===
549
550impl ProgIf {
551    /// Returns the name of the device's register-level programming interface,
552    /// if it is known.
553    #[inline]
554    #[must_use]
555    pub fn name(&self) -> Option<&'static str> {
556        match self {
557            Self::Known(known) => Some(known.name()),
558            _ => None,
559        }
560    }
561
562    /// Returns the raw ID of the device's register-level programming interface.
563    #[inline]
564    #[must_use]
565    pub fn id(&self) -> u8 {
566        match *self {
567            Self::Known(known) => known.id(),
568            Self::Unknown(id) => id,
569        }
570    }
571}
572
573impl fmt::Display for ProgIf {
574    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575        match self {
576            Self::Known(known) => write!(f, "{} ({:#x})", known.name(), known.id()),
577
578            Self::Unknown(id) => write!(f, "unknown ({id:#x})"),
579        }
580    }
581}
582
583impl From<ProgIf> for u8 {
584    #[inline]
585    fn from(value: ProgIf) -> Self {
586        match value {
587            ProgIf::Known(known) => known.id(),
588            ProgIf::Unknown(code) => code,
589        }
590    }
591}