Skip to main content

chipset_device/
pci.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! PCI configuration space access
5
6use crate::ChipsetDevice;
7use crate::io::IoError;
8use crate::io::IoResult;
9use inspect::Inspect;
10use inspect::InspectMut;
11use mesh::MeshPayload;
12use zerocopy::IntoBytes;
13
14/// Byte enables for the four lanes of a PCI configuration DWORD.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Inspect, MeshPayload)]
16pub struct PciConfigByteEnable(u8);
17
18impl PciConfigByteEnable {
19    /// All byte lanes enabled.
20    pub const FULL: Self = Self(0b1111);
21
22    /// Byte lane 0 enabled.
23    pub const BYTE0: Self = Self(0b0001);
24    /// Byte lane 1 enabled.
25    pub const BYTE1: Self = Self(0b0010);
26    /// Byte lane 2 enabled.
27    pub const BYTE2: Self = Self(0b0100);
28    /// Byte lane 3 enabled.
29    pub const BYTE3: Self = Self(0b1000);
30    /// Low word byte lanes enabled.
31    pub const LOW_WORD: Self = Self(0b0011);
32    /// High word byte lanes enabled.
33    pub const HIGH_WORD: Self = Self(0b1100);
34
35    /// Create byte enables from raw lane bits.
36    pub const fn new(bits: u8) -> Option<Self> {
37        if bits != 0 && bits <= 0xf {
38            Some(Self(bits))
39        } else {
40            None
41        }
42    }
43
44    /// Create byte enables for an access at `offset` with byte length `len`.
45    pub const fn from_offset_len(offset: u16, len: usize) -> Result<Self, IoError> {
46        let lane = (offset & 0x3) as u8;
47        match len {
48            1 => Ok(Self(1 << lane)),
49            2 if lane & 1 == 0 && lane <= 2 => Ok(Self(0x3 << lane)),
50            4 if lane == 0 => Ok(Self::FULL),
51            2 | 4 => Err(IoError::UnalignedAccess),
52            _ => Err(IoError::InvalidAccessSize),
53        }
54    }
55
56    /// Returns the byte offset of the first enabled byte in the DWORD and the number of enabled bytes.
57    pub const fn to_byte_offset_len(self) -> (u16, usize) {
58        (self.0.trailing_zeros() as u16, self.0.count_ones() as usize)
59    }
60
61    /// Raw byte-lane bits.
62    pub const fn bits(self) -> u8 {
63        self.0
64    }
65
66    /// Returns true if all byte lanes are enabled.
67    pub const fn is_full(self) -> bool {
68        self.0 == 0xf
69    }
70
71    /// `u32` mask corresponding to the enabled byte lanes.
72    pub const fn mask(self) -> u32 {
73        let mut mask = 0;
74        let mut lane = 0;
75        while lane < 4 {
76            if self.0 & (1 << lane) != 0 {
77                mask |= 0xff << (lane * 8);
78            }
79            lane += 1;
80        }
81        mask
82    }
83
84    /// Merge enabled byte lanes from `write_value` into `current_value`.
85    pub const fn merge(self, current_value: u32, write_value: u32) -> u32 {
86        let mask = self.mask();
87        (current_value & !mask) | (write_value & mask)
88    }
89
90    /// Keep only enabled byte lanes from `value`.
91    pub const fn extract(self, value: u32) -> u32 {
92        value & self.mask()
93    }
94
95    /// Restrict the underlying byte enable to only include the provided bytes, or None
96    /// when no bytes would be left enabled.
97    pub const fn restrict(self, byte_enable: PciConfigByteEnable) -> Option<Self> {
98        Self::new(self.0 & byte_enable.0)
99    }
100
101    /// Exclude the provided bytes from the underlying byte enable, returning None
102    /// when no bytes would be left enabled.
103    pub const fn exclude(self, byte_enable: PciConfigByteEnable) -> Option<Self> {
104        Self::new(self.0 & !byte_enable.0)
105    }
106}
107
108/// A DWORD value with byte enables for PCI configuration space write.
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Inspect)]
110pub struct ByteEnabledDwordWrite {
111    value: u32,
112    byte_enable: PciConfigByteEnable,
113}
114
115impl ByteEnabledDwordWrite {
116    /// Create a byte-enabled DWORD value.
117    pub const fn new(value: u32, byte_enable: PciConfigByteEnable) -> Self {
118        Self {
119            value: byte_enable.merge(0, value),
120            byte_enable,
121        }
122    }
123
124    /// Create a full-DWORD value with all byte lanes enabled.
125    pub const fn with_all_bytes_enabled(value: u32) -> Self {
126        Self::new(value, PciConfigByteEnable::FULL)
127    }
128
129    /// Create a byte-enabled DWORD value from a slice of bytes.
130    pub fn from_intercept_buffer(byte_enable: PciConfigByteEnable, buffer: &[u8]) -> Self {
131        let mut temp: u32 = 0;
132        let (byte_offset, len) = byte_enable.to_byte_offset_len();
133        assert!(len <= buffer.len());
134        let byte_offset = byte_offset as usize;
135        temp.as_mut_bytes()[byte_offset..byte_offset + len].copy_from_slice(buffer);
136        Self::new(temp, byte_enable)
137    }
138
139    /// Retrieve a mutable slice of the enabled byte lanes of the DWORD.
140    pub fn as_valid_byte_slice(&self) -> &[u8] {
141        let (byte_offset, len) = self.byte_enable.to_byte_offset_len();
142        let byte_offset = byte_offset as usize;
143        &self.value.as_bytes()[byte_offset..byte_offset + len]
144    }
145
146    /// Returns true if all byte lanes are enabled.
147    pub const fn is_full(self) -> bool {
148        self.byte_enable.is_full()
149    }
150
151    /// Retrieve the underlying byte enable.
152    pub const fn byte_enable(&self) -> PciConfigByteEnable {
153        self.byte_enable
154    }
155
156    /// Get the mask of valid bytes.
157    pub const fn valid_mask(self) -> u32 {
158        self.byte_enable.mask()
159    }
160
161    /// Merge enabled byte lanes from this value into `current_value`.
162    pub const fn merge(self, current_value: u32) -> u32 {
163        self.byte_enable.merge(current_value, self.value)
164    }
165
166    /// Merge enabled byte lanes from this value into `current_value` (read-modify-write).
167    pub const fn merge_into(self, current_value: &mut u32) {
168        *current_value = self.merge(*current_value);
169    }
170
171    /// Merge enabled byte lanes from the low WORD of this value into `current_value`.
172    pub const fn merge_low(self, current_value: u16) -> u16 {
173        self.byte_enable.merge(current_value as u32, self.value) as u16
174    }
175
176    /// Merge enabled byte lanes from the high WORD of this value into `current_value`.
177    pub const fn merge_high(self, current_value: u16) -> u16 {
178        let shifted = (current_value as u32) << 16;
179        let merged = self.byte_enable.merge(shifted, self.value);
180        (merged >> 16) as u16
181    }
182
183    /// Keep only enabled byte lanes from this value.
184    pub const fn extract(self) -> u32 {
185        self.byte_enable.extract(self.value)
186    }
187
188    /// Keep only enabled byte lanes from the low WORD this value.
189    pub const fn extract_low(self) -> u16 {
190        self.extract() as u16
191    }
192
193    /// Keep only enabled byte lanes from the high WORD this value.
194    pub const fn extract_high(self) -> u16 {
195        (self.extract() >> 16) as u16
196    }
197}
198
199/// A DWORD value with byte enables for PCI configuration space read.
200#[derive(Debug, InspectMut)]
201pub struct ByteEnabledDwordRead<'a> {
202    value: &'a mut u32,
203    byte_enable: PciConfigByteEnable,
204}
205
206impl<'a> ByteEnabledDwordRead<'a> {
207    /// Create a byte-enabled DWORD value.
208    pub const fn new(value: &'a mut u32, byte_enable: PciConfigByteEnable) -> Self {
209        Self { value, byte_enable }
210    }
211
212    /// Create a full-DWORD value with all byte lanes enabled.
213    pub const fn with_all_bytes_enabled(value: &'a mut u32) -> Self {
214        Self::new(value, PciConfigByteEnable::FULL)
215    }
216
217    /// Retrieve the underlying byte enable.
218    pub const fn byte_enable(&self) -> PciConfigByteEnable {
219        self.byte_enable
220    }
221
222    /// Fill the intercept buffer with the enabled byte lanes of the DWORD.
223    pub fn fill_intercept_buffer(self, buffer: &mut [u8]) {
224        let src = self.into_valid_byte_slice();
225        buffer.copy_from_slice(src);
226    }
227
228    /// Retrieve a mutable slice of the enabled byte lanes of the DWORD.
229    pub fn into_valid_byte_slice(self) -> &'a mut [u8] {
230        let (byte_offset, len) = self.byte_enable.to_byte_offset_len();
231        let byte_offset = byte_offset as usize;
232        &mut self.value.as_mut_bytes()[byte_offset..byte_offset + len]
233    }
234
235    /// Get the mask of valid bytes.
236    pub const fn valid_mask(&self) -> u32 {
237        self.byte_enable.mask()
238    }
239
240    /// Update the value of the DWORD, honoring byte enables.
241    pub fn set(&mut self, value: u32) {
242        *self.value = self.byte_enable.merge(*self.value, value);
243    }
244
245    /// Update the value of the DWORD, honoring byte enables.
246    pub fn set_low_high(&mut self, low: u16, high: u16) {
247        *self.value = self
248            .byte_enable
249            .merge(*self.value, (high as u32) << 16 | (low as u32));
250    }
251
252    /// Update the value of the DWORD, honoring byte enables.
253    pub fn set_bytes(&mut self, byte0: u8, byte1: u8, byte2: u8, byte3: u8) {
254        *self.value = self.byte_enable.merge(
255            *self.value,
256            (byte3 as u32) << 24 | (byte2 as u32) << 16 | (byte1 as u32) << 8 | (byte0 as u32),
257        );
258    }
259
260    /// Keep only enabled byte lanes from this value.
261    pub const fn extract(&self) -> u32 {
262        self.byte_enable.extract(*self.value)
263    }
264
265    /// Keep only enabled byte lanes from the low WORD this value.
266    pub const fn extract_low(self) -> u16 {
267        self.extract() as u16
268    }
269
270    /// Keep only enabled byte lanes from the high WORD this value.
271    pub const fn extract_high(self) -> u16 {
272        (self.extract() >> 16) as u16
273    }
274
275    /// Reborrow the underlying value.
276    pub fn reborrow(&mut self) -> ByteEnabledDwordRead<'_> {
277        self.restrict(PciConfigByteEnable::FULL).unwrap()
278    }
279
280    /// Restrict the read to only the provided bytes.
281    pub fn restrict(
282        &mut self,
283        byte_enable: PciConfigByteEnable,
284    ) -> Option<ByteEnabledDwordRead<'_>> {
285        let byte_enable = self.byte_enable.restrict(byte_enable)?;
286        Some(ByteEnabledDwordRead::new(&mut *self.value, byte_enable))
287    }
288
289    /// Exclude the provided bytes from the read.
290    pub fn exclude(
291        &mut self,
292        byte_enable: PciConfigByteEnable,
293    ) -> Option<ByteEnabledDwordRead<'_>> {
294        let byte_enable = self.byte_enable.exclude(byte_enable)?;
295        Some(ByteEnabledDwordRead::new(&mut *self.value, byte_enable))
296    }
297}
298
299/// A PCI configuration space request.
300#[derive(Debug, Clone, Copy, PartialEq, Eq, Inspect)]
301pub struct PciConfigAddress {
302    /// Target bus number.
303    pub bus: u8,
304    /// Target packed device/function number (`device << 3 | function`).
305    pub devfn: u8,
306    /// Aligned DWORD register number in configuration space.
307    dword_number: u16,
308}
309
310impl PciConfigAddress {
311    /// Create a new PCI configuration-space request.
312    pub const fn new(bus: u8, devfn: u8, dword_number: u16) -> Option<Self> {
313        if dword_number >= 1024 {
314            return None;
315        }
316        Some(Self {
317            bus,
318            devfn,
319            dword_number,
320        })
321    }
322
323    /// Target device number.
324    pub const fn device(self) -> u8 {
325        self.devfn >> 3
326    }
327
328    /// Target function number.
329    pub const fn function(self) -> u8 {
330        self.devfn & 0x7
331    }
332
333    /// Aligned byte offset of the addressed DWORD in configuration space.
334    pub const fn byte_offset(self) -> u16 {
335        self.dword_number * 4
336    }
337}
338
339/// Represents the type of PCI configuration space access.
340#[derive(Debug, Clone, Copy, PartialEq, Eq, Inspect)]
341pub enum PciConfigAccessType {
342    /// Type 0 PCI configuration space access. Type 0 accesses have
343    /// been fully routed to the target bus number.
344    Type0,
345    /// Type 1 PCI configuration space access. Type 1 accesses have
346    /// not been fully routed to the target bus number.
347    Type1,
348}
349
350/// Implemented by devices which have a PCI config space.
351pub trait PciConfigSpace: ChipsetDevice {
352    /// Dispatch a PCI config space read to the device with the given address.
353    ///
354    /// This function serves as a shorthand that single-function endpoint devices
355    /// can implement directly. More advanced routing components (switches, bridges)
356    /// and multi-function devices should instead implement
357    /// [`pci_cfg_read_with_routing`](Self::pci_cfg_read_with_routing) for full
358    /// routing context.
359    ///
360    /// `byte_offset` is guaranteed to be aligned to a 4-byte boundary.
361    fn pci_cfg_read(&mut self, byte_offset: u16, value: ByteEnabledDwordRead<'_>) -> IoResult;
362
363    /// Dispatch a PCI config space write to the device with the given address.
364    ///
365    /// This function serves as a shorthand that single-function endpoint devices
366    /// can implement directly. More advanced routing components (switches, bridges)
367    /// and multi-function devices should instead implement
368    /// [`pci_cfg_write_with_routing`](Self::pci_cfg_write_with_routing) for full
369    /// routing context.
370    ///
371    /// `byte_offset` is guaranteed to be aligned to a 4-byte boundary.
372    fn pci_cfg_write(&mut self, byte_offset: u16, value: ByteEnabledDwordWrite) -> IoResult;
373
374    /// Dispatch a PCI configuration space read with full routing context.
375    ///
376    /// This method receives configuration space read with the access type,
377    /// target bus, target device/function number, and DWORD offset.
378    ///
379    /// The default implementation dispatches type 0 access to function 0 to
380    /// [`pci_cfg_read`](Self::pci_cfg_read) and returns all-1s for other
381    /// functions (the standard "no device present" response). Routing
382    /// components (switches, bridges) and multi-function devices should
383    /// override this method.
384    ///
385    /// # Parameters
386    /// - `access_type`: The type of PCI configuration space access (Type 0 or Type 1)
387    /// - `address`: The target address (BDF + offset) being accessed
388    /// - `value`: Byte-enabled DWORD value to receive the read
389    fn pci_cfg_read_with_routing(
390        &mut self,
391        access_type: PciConfigAccessType,
392        address: PciConfigAddress,
393        mut value: ByteEnabledDwordRead<'_>,
394    ) -> IoResult {
395        match (access_type, address.devfn) {
396            (PciConfigAccessType::Type0, 0) => self.pci_cfg_read(address.byte_offset(), value),
397            _ => {
398                value.set(!0);
399                IoResult::Ok
400            }
401        }
402    }
403
404    /// Dispatch a PCI configuration space write with full routing context.
405    ///
406    /// This method receives configuration space write with the access type,
407    /// target bus, target device/function number, and DWORD offset.
408    ///
409    /// The default implementation dispatches type 0 access to function 0 to
410    /// [`pci_cfg_write`](Self::pci_cfg_write) and silently drops writes to
411    /// other functions. Routing components (switches, bridges) and
412    /// multi-function devices should override this method.
413    ///
414    /// # Parameters
415    /// - `access_type`: The type of PCI configuration space access (Type 0 or Type 1)
416    /// - `address`: The target address (BDF + offset) being accessed
417    /// - `value`: Byte-enabled DWORD value to write
418    fn pci_cfg_write_with_routing(
419        &mut self,
420        access_type: PciConfigAccessType,
421        address: PciConfigAddress,
422        value: ByteEnabledDwordWrite,
423    ) -> IoResult {
424        match (access_type, address.devfn) {
425            (PciConfigAccessType::Type0, 0) => self.pci_cfg_write(address.byte_offset(), value),
426            _ => IoResult::Ok,
427        }
428    }
429
430    /// Check if the device has a suggested (bus, device, function) it expects
431    /// to be located at.
432    ///
433    /// The term "suggested" is important here, as it's important to note that
434    /// one of the major selling points of PCI was that PCI devices _shouldn't_
435    /// need to care about about what PCI address they are initialized at. i.e:
436    /// on a physical machine, it shouldn't matter that your fancy GTX 4090 is
437    /// plugged into the first vs. second PCI slot.
438    ///
439    /// ..that said, there are some instances where it makes sense for an
440    /// emulated device to declare its suggested PCI address:
441    ///
442    /// 1. Devices that emulate bespoke PCI devices part of a particular
443    ///    system's chipset.
444    ///   - e.g: the PIIX4 chipset includes several bespoke PCI devices that are
445    ///     required to have specific PCI addresses. While it _would_ be
446    ///     possible to relocate them to a different address, it may break OSes
447    ///     that assume they exist at those spec-declared addresses.
448    /// 2. Multi-function PCI devices
449    ///   - In an unfortunate case of inverted responsibilities, there is a
450    ///     single bit in the PCI configuration space's `Header` register that
451    ///     denotes if a particular PCI card includes multiple functions.
452    ///   - Since multi-function devices are pretty rare, `ChipsetDevice` opted
453    ///     to model each function as its own device, which in turn implies that
454    ///     in order to correctly init a multi-function PCI card, the
455    ///     `ChipsetDevice` with function 0 _must_ report if there are other
456    ///     functions at the same bus and device.
457    fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
458        None
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465
466    #[test]
467    fn byte_enable_from_offset_len_rejects_invalid_accesses() {
468        assert_eq!(
469            PciConfigByteEnable::from_offset_len(0, 1).unwrap().bits(),
470            0b0001
471        );
472        assert_eq!(
473            PciConfigByteEnable::from_offset_len(1, 1).unwrap().bits(),
474            0b0010
475        );
476        assert_eq!(
477            PciConfigByteEnable::from_offset_len(2, 1).unwrap().bits(),
478            0b0100
479        );
480        assert_eq!(
481            PciConfigByteEnable::from_offset_len(3, 1).unwrap().bits(),
482            0b1000
483        );
484        assert_eq!(
485            PciConfigByteEnable::from_offset_len(0, 2).unwrap().bits(),
486            0b0011
487        );
488        assert_eq!(
489            PciConfigByteEnable::from_offset_len(2, 2).unwrap().bits(),
490            0b1100
491        );
492        assert_eq!(
493            PciConfigByteEnable::from_offset_len(0, 4).unwrap().bits(),
494            0b1111
495        );
496
497        assert!(matches!(
498            PciConfigByteEnable::from_offset_len(1, 2),
499            Err(IoError::UnalignedAccess)
500        ));
501        assert!(matches!(
502            PciConfigByteEnable::from_offset_len(3, 2),
503            Err(IoError::UnalignedAccess)
504        ));
505        assert!(matches!(
506            PciConfigByteEnable::from_offset_len(1, 4),
507            Err(IoError::UnalignedAccess)
508        ));
509        assert!(matches!(
510            PciConfigByteEnable::from_offset_len(0, 3),
511            Err(IoError::InvalidAccessSize)
512        ));
513    }
514
515    #[test]
516    fn byte_enable_masks_and_merges_lanes() {
517        let byte_enable = PciConfigByteEnable::from_offset_len(1, 1).unwrap();
518        assert_eq!(byte_enable.bits(), 0b0010);
519        assert_eq!(byte_enable.mask(), 0x0000_ff00);
520        assert_eq!(byte_enable.extract(0x1234_5678), 0x0000_5600);
521        assert_eq!(byte_enable.merge(0xaaaa_aaaa, 0x1234_5678), 0xaaaa_56aa);
522
523        let byte_enable = PciConfigByteEnable::from_offset_len(2, 2).unwrap();
524        assert_eq!(byte_enable.bits(), 0b1100);
525        assert_eq!(byte_enable.mask(), 0xffff_0000);
526        assert_eq!(byte_enable.extract(0x1234_5678), 0x1234_0000);
527        assert_eq!(byte_enable.merge(0xaaaa_aaaa, 0x1234_5678), 0x1234_aaaa);
528
529        let byte_enable = PciConfigByteEnable::from_offset_len(0, 4).unwrap();
530        assert_eq!(byte_enable.bits(), 0b1111);
531        assert_eq!(byte_enable.mask(), 0xffff_ffff);
532        assert_eq!(byte_enable.extract(0x1234_5678), 0x1234_5678);
533        assert_eq!(byte_enable.merge(0xaaaa_aaaa, 0x1234_5678), 0x1234_5678);
534    }
535
536    #[test]
537    fn byte_enabled_intercept_buffers_copy_selected_lanes() {
538        let write = ByteEnabledDwordWrite::from_intercept_buffer(
539            PciConfigByteEnable::HIGH_WORD,
540            &[0x22, 0x11],
541        );
542        assert_eq!(write.extract(), 0x1122_0000);
543        assert_eq!(write.merge(0xaabb_ccdd), 0x1122_ccdd);
544
545        let mut value = 0x5566_7788;
546        let read = ByteEnabledDwordRead::new(&mut value, PciConfigByteEnable::HIGH_WORD);
547        let mut buffer = [0; 2];
548        read.fill_intercept_buffer(&mut buffer);
549        assert_eq!(buffer, [0x66, 0x55]);
550    }
551
552    #[test]
553    fn config_request_decodes_bdf() {
554        let address = PciConfigAddress::new(0x12, 0x1d, 0x40).unwrap();
555
556        assert_eq!(address.bus, 0x12);
557        assert_eq!(address.devfn, 0x1d);
558        assert_eq!(address.device(), 3);
559        assert_eq!(address.function(), 5);
560        assert_eq!(address.byte_offset(), 0x100);
561    }
562}