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}