vmm/pci/
configuration.rs

1// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright 2018 The Chromium OS Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE-BSD-3-Clause file.
5//
6// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
7
8use std::sync::{Arc, Mutex};
9
10use byteorder::{ByteOrder, LittleEndian};
11use pci::{PciCapabilityId, PciClassCode, PciSubclass};
12use serde::{Deserialize, Serialize};
13
14use super::BarReprogrammingParams;
15use super::msix::MsixConfig;
16use crate::logger::{info, warn};
17use crate::utils::u64_to_usize;
18
19// The number of 32bit registers in the config space, 4096 bytes.
20const NUM_CONFIGURATION_REGISTERS: usize = 1024;
21
22const STATUS_REG: usize = 1;
23const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
24const BAR0_REG: usize = 4;
25const ROM_BAR_REG: usize = 12;
26const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
27const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
28const MSI_CAPABILITY_REGISTER_MASK: u32 = 0x0071_0000;
29const MSIX_CAPABILITY_REGISTER_MASK: u32 = 0xc000_0000;
30const NUM_BAR_REGS: usize = 6;
31const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
32const FIRST_CAPABILITY_OFFSET: usize = 0x40;
33const CAPABILITY_MAX_OFFSET: usize = 192;
34
35/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
36pub trait PciCapability {
37    /// Bytes of the PCI capability
38    fn bytes(&self) -> &[u8];
39    /// Id of the PCI capability
40    fn id(&self) -> PciCapabilityId;
41}
42
43// This encodes the BAR size as expected by the software running inside the guest.
44// It assumes that bar_size is not 0
45fn encode_64_bits_bar_size(bar_size: u64) -> (u32, u32) {
46    assert_ne!(bar_size, 0);
47    let result = !(bar_size - 1);
48    let result_hi = (result >> 32) as u32;
49    let result_lo = (result & 0xffff_ffff) as u32;
50    (result_hi, result_lo)
51}
52
53// This decoes the BAR size from the value stored in the BAR registers.
54fn decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> u64 {
55    let bar_size: u64 = ((bar_size_hi as u64) << 32) | (bar_size_lo as u64);
56    let size = !bar_size + 1;
57    assert_ne!(size, 0);
58    size
59}
60
61#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
62struct PciBar {
63    addr: u32,
64    size: u32,
65    used: bool,
66}
67
68/// PCI configuration space state for (de)serialization
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct PciConfigurationState {
71    registers: Vec<u32>,
72    writable_bits: Vec<u32>,
73    bars: Vec<PciBar>,
74    last_capability: Option<(usize, usize)>,
75    msix_cap_reg_idx: Option<usize>,
76}
77
78#[derive(Debug)]
79/// Contains the configuration space of a PCI node.
80///
81/// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space).
82/// The configuration space is accessed with DWORD reads and writes from the guest.
83pub struct PciConfiguration {
84    registers: [u32; NUM_CONFIGURATION_REGISTERS],
85    writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
86    bars: [PciBar; NUM_BAR_REGS],
87    // Contains the byte offset and size of the last capability.
88    last_capability: Option<(usize, usize)>,
89    msix_cap_reg_idx: Option<usize>,
90    msix_config: Option<Arc<Mutex<MsixConfig>>>,
91}
92
93impl PciConfiguration {
94    #[allow(clippy::too_many_arguments)]
95    /// Create a new type 0 PCI configuration
96    pub fn new_type0(
97        vendor_id: u16,
98        device_id: u16,
99        revision_id: u8,
100        class_code: PciClassCode,
101        subclass: &dyn PciSubclass,
102        subsystem_vendor_id: u16,
103        subsystem_id: u16,
104        msix_config: Option<Arc<Mutex<MsixConfig>>>,
105    ) -> Self {
106        let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
107        let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
108        registers[0] = (u32::from(device_id) << 16) | u32::from(vendor_id);
109        // TODO(dverkamp): Status should be write-1-to-clear
110        writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
111        registers[2] = (u32::from(class_code.get_register_value()) << 24)
112            | (u32::from(subclass.get_register_value()) << 16)
113            | u32::from(revision_id);
114        writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
115        registers[3] = 0x0000_0000; // Header type 0 (device)
116        writable_bits[15] = 0x0000_00ff; // IRQ line (r/w)
117        registers[11] = (u32::from(subsystem_id) << 16) | u32::from(subsystem_vendor_id);
118
119        PciConfiguration {
120            registers,
121            writable_bits,
122            bars: [PciBar::default(); NUM_BAR_REGS],
123            last_capability: None,
124            msix_cap_reg_idx: None,
125            msix_config,
126        }
127    }
128
129    /// Create a type 0 PCI configuration from snapshot state
130    pub fn type0_from_state(
131        state: PciConfigurationState,
132        msix_config: Option<Arc<Mutex<MsixConfig>>>,
133    ) -> Self {
134        PciConfiguration {
135            registers: state.registers.try_into().unwrap(),
136            writable_bits: state.writable_bits.try_into().unwrap(),
137            bars: state.bars.try_into().unwrap(),
138            last_capability: state.last_capability,
139            msix_cap_reg_idx: state.msix_cap_reg_idx,
140            msix_config,
141        }
142    }
143
144    /// Create PCI configuration space state
145    pub fn state(&self) -> PciConfigurationState {
146        PciConfigurationState {
147            registers: self.registers.to_vec(),
148            writable_bits: self.writable_bits.to_vec(),
149            bars: self.bars.to_vec(),
150            last_capability: self.last_capability,
151            msix_cap_reg_idx: self.msix_cap_reg_idx,
152        }
153    }
154
155    /// Reads a 32bit register from `reg_idx` in the register map.
156    pub fn read_reg(&self, reg_idx: usize) -> u32 {
157        *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff))
158    }
159
160    /// Writes a 32bit register to `reg_idx` in the register map.
161    pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
162        let mut mask = self.writable_bits[reg_idx];
163
164        if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
165            // Handle very specific case where the BAR is being written with
166            // all 1's to retrieve the BAR size during next BAR reading.
167            if value == 0xffff_ffff {
168                mask &= self.bars[reg_idx - 4].size;
169            }
170        } else if reg_idx == ROM_BAR_REG {
171            // Handle very specific case where the BAR is being written with
172            // all 1's on bits 31-11 to retrieve the BAR size during next BAR
173            // reading.
174            if value & ROM_BAR_ADDR_MASK == ROM_BAR_ADDR_MASK {
175                mask = 0;
176            }
177        }
178
179        if let Some(r) = self.registers.get_mut(reg_idx) {
180            *r = (*r & !self.writable_bits[reg_idx]) | (value & mask);
181        } else {
182            warn!("bad PCI register write {}", reg_idx);
183        }
184    }
185
186    /// Writes a 16bit word to `offset`. `offset` must be 16bit aligned.
187    pub fn write_word(&mut self, offset: usize, value: u16) {
188        let shift = match offset % 4 {
189            0 => 0,
190            2 => 16,
191            _ => {
192                warn!("bad PCI config write offset {}", offset);
193                return;
194            }
195        };
196        let reg_idx = offset / 4;
197
198        if let Some(r) = self.registers.get_mut(reg_idx) {
199            let writable_mask = self.writable_bits[reg_idx];
200            let mask = (0xffffu32 << shift) & writable_mask;
201            let shifted_value = (u32::from(value) << shift) & writable_mask;
202            *r = *r & !mask | shifted_value;
203        } else {
204            warn!("bad PCI config write offset {}", offset);
205        }
206    }
207
208    /// Writes a byte to `offset`.
209    pub fn write_byte(&mut self, offset: usize, value: u8) {
210        self.write_byte_internal(offset, value, true);
211    }
212
213    /// Writes a byte to `offset`, optionally enforcing read-only bits.
214    fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) {
215        let shift = (offset % 4) * 8;
216        let reg_idx = offset / 4;
217
218        if let Some(r) = self.registers.get_mut(reg_idx) {
219            let writable_mask = if apply_writable_mask {
220                self.writable_bits[reg_idx]
221            } else {
222                0xffff_ffff
223            };
224            let mask = (0xffu32 << shift) & writable_mask;
225            let shifted_value = (u32::from(value) << shift) & writable_mask;
226            *r = *r & !mask | shifted_value;
227        } else {
228            warn!("bad PCI config write offset {}", offset);
229        }
230    }
231
232    /// Add the [addr, addr + size) BAR region.
233    ///
234    /// Configures the specified BAR to report this region and size to the guest kernel.
235    /// Enforces a few constraints (i.e, region size must be power of two, register not already
236    /// used).
237    pub fn add_pci_bar(&mut self, bar_idx: usize, addr: u64, size: u64) {
238        let reg_idx = BAR0_REG + bar_idx;
239
240        // These are a few constraints that are imposed due to the fact
241        // that only VirtIO devices are actually allocating a BAR. Moreover, this is
242        // a single 64-bit BAR. Not conforming to these requirements is an internal
243        // Firecracker bug.
244
245        // We are only using BAR 0
246        assert_eq!(bar_idx, 0);
247        // We shouldn't be trying to use the same BAR twice
248        assert!(!self.bars[0].used);
249        assert!(!self.bars[1].used);
250        // We can't have a size of 0
251        assert_ne!(size, 0);
252        // BAR size needs to be a power of two
253        assert!(size.is_power_of_two());
254        // We should not be overflowing the address space
255        addr.checked_add(size - 1).unwrap();
256
257        // Encode the BAR size as expected by the software running in
258        // the guest.
259        let (bar_size_hi, bar_size_lo) = encode_64_bits_bar_size(size);
260
261        self.registers[reg_idx + 1] = (addr >> 32) as u32;
262        self.writable_bits[reg_idx + 1] = 0xffff_ffff;
263        self.bars[bar_idx + 1].addr = self.registers[reg_idx + 1];
264        self.bars[bar_idx].size = bar_size_lo;
265        self.bars[bar_idx + 1].size = bar_size_hi;
266        self.bars[bar_idx + 1].used = true;
267
268        // Addresses of memory BARs are 16-byte aligned so the lower 4 bits are always 0. Within
269        // the register we use this 4 bits to encode extra information about the BAR. The meaning
270        // of these bits is:
271        //
272        // |    Bit 3     | Bits 2-1 |  Bit 0   |
273        // | Prefetchable |   type   | Always 0 |
274        //
275        // Non-prefetchable, 64 bits BAR region
276        self.registers[reg_idx] = (((addr & 0xffff_ffff) as u32) & BAR_MEM_ADDR_MASK) | 4u32;
277        self.writable_bits[reg_idx] = BAR_MEM_ADDR_MASK;
278        self.bars[bar_idx].addr = self.registers[reg_idx];
279        self.bars[bar_idx].used = true;
280    }
281
282    /// Returns the address of the given BAR region.
283    ///
284    /// This assumes that `bar_idx` is a valid BAR register.
285    pub fn get_bar_addr(&self, bar_idx: usize) -> u64 {
286        assert!(bar_idx < NUM_BAR_REGS);
287
288        let reg_idx = BAR0_REG + bar_idx;
289
290        (u64::from(self.bars[bar_idx].addr & self.writable_bits[reg_idx]))
291            | (u64::from(self.bars[bar_idx + 1].addr) << 32)
292    }
293
294    /// Adds the capability `cap_data` to the list of capabilities.
295    ///
296    /// `cap_data` should not include the two-byte PCI capability header (type, next).
297    /// Correct values will be generated automatically based on `cap_data.id()` and
298    /// `cap_data.len()`.
299    pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> usize {
300        let total_len = cap_data.bytes().len() + 2;
301        let (cap_offset, tail_offset) = match self.last_capability {
302            Some((offset, len)) => (Self::next_dword(offset, len), offset + 1),
303            None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET),
304        };
305
306        // We know that the capabilities we are using have a valid size (doesn't overflow) and that
307        // we add capabilities that fit in the available space. If any of these requirements don't
308        // hold, this is due to a Firecracker bug.
309        let end_offset = cap_offset.checked_add(total_len).unwrap();
310        assert!(end_offset <= CAPABILITY_MAX_OFFSET);
311        self.registers[STATUS_REG] |= STATUS_REG_CAPABILITIES_USED_MASK;
312        self.write_byte_internal(tail_offset, cap_offset.try_into().unwrap(), false);
313        self.write_byte_internal(cap_offset, cap_data.id() as u8, false);
314        self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer.
315        for (i, byte) in cap_data.bytes().iter().enumerate() {
316            self.write_byte_internal(cap_offset + i + 2, *byte, false);
317        }
318        self.last_capability = Some((cap_offset, total_len));
319
320        match cap_data.id() {
321            PciCapabilityId::MessageSignalledInterrupts => {
322                self.writable_bits[cap_offset / 4] = MSI_CAPABILITY_REGISTER_MASK;
323            }
324            PciCapabilityId::MsiX => {
325                self.msix_cap_reg_idx = Some(cap_offset / 4);
326                self.writable_bits[self.msix_cap_reg_idx.unwrap()] = MSIX_CAPABILITY_REGISTER_MASK;
327            }
328            _ => {}
329        }
330
331        cap_offset
332    }
333
334    // Find the next aligned offset after the one given.
335    fn next_dword(offset: usize, len: usize) -> usize {
336        let next = offset + len;
337        (next + 3) & !3
338    }
339
340    /// Write a PCI configuration register
341    pub fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
342        if reg_idx >= NUM_CONFIGURATION_REGISTERS {
343            return;
344        }
345
346        if u64_to_usize(offset) + data.len() > 4 {
347            return;
348        }
349
350        // Handle potential write to MSI-X message control register
351        if let Some(msix_cap_reg_idx) = self.msix_cap_reg_idx
352            && let Some(msix_config) = &self.msix_config
353        {
354            if msix_cap_reg_idx == reg_idx && offset == 2 && data.len() == 2 {
355                // 2-bytes write in the Message Control field
356                msix_config
357                    .lock()
358                    .unwrap()
359                    .set_msg_ctl(LittleEndian::read_u16(data));
360            } else if msix_cap_reg_idx == reg_idx && offset == 0 && data.len() == 4 {
361                // 4 bytes write at the beginning. Ignore the first 2 bytes which are the
362                // capability id and next capability pointer
363                msix_config
364                    .lock()
365                    .unwrap()
366                    .set_msg_ctl((LittleEndian::read_u32(data) >> 16) as u16);
367            }
368        }
369
370        match data.len() {
371            1 => self.write_byte(reg_idx * 4 + u64_to_usize(offset), data[0]),
372            2 => self.write_word(
373                reg_idx * 4 + u64_to_usize(offset),
374                u16::from(data[0]) | (u16::from(data[1]) << 8),
375            ),
376            4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)),
377            _ => (),
378        }
379    }
380
381    /// Detect whether the guest wants to reprogram the address of a BAR
382    pub fn detect_bar_reprogramming(
383        &mut self,
384        reg_idx: usize,
385        data: &[u8],
386    ) -> Option<BarReprogrammingParams> {
387        if data.len() != 4 {
388            return None;
389        }
390
391        let value = LittleEndian::read_u32(data);
392
393        let mask = self.writable_bits[reg_idx];
394        if !(BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
395            return None;
396        }
397
398        // Ignore the case where the BAR size is being asked for.
399        if value == 0xffff_ffff {
400            return None;
401        }
402
403        let bar_idx = reg_idx - 4;
404
405        // Do not reprogram BARs we are not using
406        if !self.bars[bar_idx].used {
407            return None;
408        }
409
410        // We are always using 64bit BARs, so two BAR registers. We don't do anything until
411        // the upper BAR is modified, otherwise we would be moving the BAR to a wrong
412        // location in memory.
413        if bar_idx == 0 {
414            return None;
415        }
416
417        // The lower BAR (of this 64bit BAR) has been reprogrammed to a different value
418        // than it used to be
419        if (self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
420                    != (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]) ||
421                    // Or the lower BAR hasn't been changed but the upper one is being reprogrammed
422                    // now to a different value
423                    (value & mask) != (self.bars[bar_idx].addr & mask)
424        {
425            info!(
426                "Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
427                reg_idx, self.registers[reg_idx], value
428            );
429            let old_base = (u64::from(self.bars[bar_idx].addr & mask) << 32)
430                | u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
431            let new_base = (u64::from(value & mask) << 32)
432                | u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
433            let len = decode_64_bits_bar_size(self.bars[bar_idx].size, self.bars[bar_idx - 1].size);
434
435            self.bars[bar_idx].addr = value;
436            self.bars[bar_idx - 1].addr = self.registers[reg_idx - 1];
437
438            return Some(BarReprogrammingParams {
439                old_base,
440                new_base,
441                len,
442            });
443        }
444
445        None
446    }
447}
448
449#[cfg(test)]
450mod tests {
451    use pci::PciMultimediaSubclass;
452    use vm_memory::ByteValued;
453
454    use super::*;
455    use crate::pci::msix::MsixCap;
456
457    #[repr(C, packed)]
458    #[derive(Clone, Copy, Default)]
459    #[allow(dead_code)]
460    struct TestCap {
461        len: u8,
462        foo: u8,
463    }
464
465    // SAFETY: All members are simple numbers and any value is valid.
466    unsafe impl ByteValued for TestCap {}
467
468    impl PciCapability for TestCap {
469        fn bytes(&self) -> &[u8] {
470            self.as_slice()
471        }
472
473        fn id(&self) -> PciCapabilityId {
474            PciCapabilityId::VendorSpecific
475        }
476    }
477
478    struct BadCap {
479        data: Vec<u8>,
480    }
481
482    impl BadCap {
483        fn new(len: u8) -> Self {
484            Self {
485                data: (0..len).collect(),
486            }
487        }
488    }
489
490    impl PciCapability for BadCap {
491        fn bytes(&self) -> &[u8] {
492            &self.data
493        }
494
495        fn id(&self) -> PciCapabilityId {
496            PciCapabilityId::VendorSpecific
497        }
498    }
499
500    #[test]
501    #[should_panic]
502    fn test_too_big_capability() {
503        let mut cfg = default_pci_config();
504        cfg.add_capability(&BadCap::new(127));
505    }
506
507    #[test]
508    #[should_panic]
509    fn test_capability_space_overflow() {
510        let mut cfg = default_pci_config();
511        cfg.add_capability(&BadCap::new(62));
512        cfg.add_capability(&BadCap::new(62));
513        cfg.add_capability(&BadCap::new(0));
514    }
515
516    #[test]
517    fn test_add_capability() {
518        let mut cfg = default_pci_config();
519
520        // Reset capabilities
521        cfg.last_capability = None;
522
523        // Add two capabilities with different contents.
524        let cap1 = TestCap { len: 4, foo: 0xAA };
525        let cap1_offset = cfg.add_capability(&cap1);
526        assert_eq!(cap1_offset % 4, 0);
527
528        let cap2 = TestCap {
529            len: 0x04,
530            foo: 0x55,
531        };
532        let cap2_offset = cfg.add_capability(&cap2);
533        assert_eq!(cap2_offset % 4, 0);
534
535        // The capability list head should be pointing to cap1.
536        let cap_ptr = cfg.read_reg(CAPABILITY_LIST_HEAD_OFFSET / 4) & 0xFF;
537        assert_eq!(cap1_offset, cap_ptr as usize);
538
539        // Verify the contents of the capabilities.
540        let cap1_data = cfg.read_reg(cap1_offset / 4);
541        assert_eq!(cap1_data & 0xFF, 0x09); // capability ID
542        assert_eq!((cap1_data >> 8) & 0xFF, u32::try_from(cap2_offset).unwrap()); // next capability pointer
543        assert_eq!((cap1_data >> 16) & 0xFF, 0x04); // cap1.len
544        assert_eq!((cap1_data >> 24) & 0xFF, 0xAA); // cap1.foo
545
546        let cap2_data = cfg.read_reg(cap2_offset / 4);
547        assert_eq!(cap2_data & 0xFF, 0x09); // capability ID
548        assert_eq!((cap2_data >> 8) & 0xFF, 0x00); // next capability pointer
549        assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len
550        assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
551    }
552
553    #[test]
554    fn test_msix_capability() {
555        let mut cfg = default_pci_config();
556
557        // Information about the MSI-X capability layout: https://wiki.osdev.org/PCI#Enabling_MSI-X
558        let msix_cap = MsixCap::new(
559            3,      // Using BAR3 for message control table
560            1024,   // 1024 MSI-X vectors
561            0x4000, // Offset of message control table inside the BAR
562            4,      // BAR4 used for pending control bit
563            0x420,  // Offset of pending bit array (PBA) inside BAR
564        );
565        cfg.add_capability(&msix_cap);
566
567        let cap_reg = FIRST_CAPABILITY_OFFSET / 4;
568        let reg = cfg.read_reg(cap_reg);
569        // Capability ID is MSI-X
570        assert_eq!(
571            PciCapabilityId::from((reg & 0xff) as u8),
572            PciCapabilityId::MsiX
573        );
574        // We only have one capability, so `next` should be 0
575        assert_eq!(((reg >> 8) & 0xff) as u8, 0);
576        let msg_ctl = (reg >> 16) as u16;
577
578        // MSI-X is enabled
579        assert_eq!(msg_ctl & 0x8000, 0x8000);
580        // Vectors are not masked
581        assert_eq!(msg_ctl & 0x4000, 0x0);
582        // Reserved bits are 0
583        assert_eq!(msg_ctl & 0x3800, 0x0);
584        // We've got 1024 vectors (Table size is N-1 encoded)
585        assert_eq!((msg_ctl & 0x7ff) + 1, 1024);
586
587        let reg = cfg.read_reg(cap_reg + 1);
588        // We are using BAR3
589        assert_eq!(reg & 0x7, 3);
590        // Message Control Table is located in offset 0x4000 inside the BAR
591        // We don't need to shift. Offset needs to be 8-byte aligned - so BIR
592        // is stored in its last 3 bits (which we need to mask out).
593        assert_eq!(reg & 0xffff_fff8, 0x4000);
594
595        let reg = cfg.read_reg(cap_reg + 2);
596        // PBA is 0x420 bytes inside BAR4
597        assert_eq!(reg & 0x7, 4);
598        assert_eq!(reg & 0xffff_fff8, 0x420);
599
600        // Check read/write mask
601        // Capability Id of MSI-X is 0x11
602        cfg.write_config_register(cap_reg, 0, &[0x0]);
603        assert_eq!(
604            PciCapabilityId::from((cfg.read_reg(cap_reg) & 0xff) as u8),
605            PciCapabilityId::MsiX
606        );
607        // Cannot override next capability pointer
608        cfg.write_config_register(cap_reg, 1, &[0x42]);
609        assert_eq!((cfg.read_reg(cap_reg) >> 8) & 0xff, 0);
610
611        // We are writing this:
612        //
613        // meaning: | MSI enabled | Vectors Masked | Reserved | Table size |
614        // bit:     |     15      |       14       |  13 - 11 |   0 - 10   |
615        // R/W:     |     R/W     |       R/W      |     R    |     R      |
616        let msg_ctl = (cfg.read_reg(cap_reg) >> 16) as u16;
617        // Try to flip all bits
618        cfg.write_config_register(cap_reg, 2, &u16::to_le_bytes(!msg_ctl));
619        let msg_ctl = (cfg.read_reg(cap_reg) >> 16) as u16;
620        // MSI enabled and Vectors masked should be flipped (MSI disabled and vectors masked)
621        assert_eq!(msg_ctl & 0xc000, 0x4000);
622        // Reserved bits should still be 0
623        assert_eq!(msg_ctl & 0x3800, 0);
624        // Table size should not have changed
625        assert_eq!((msg_ctl & 0x07ff) + 1, 1024);
626
627        // Table offset is read only
628        let table_offset = cfg.read_reg(cap_reg + 1);
629        // Try to flip all bits
630        cfg.write_config_register(cap_reg + 1, 0, &u32::to_le_bytes(!table_offset));
631        // None should be flipped
632        assert_eq!(cfg.read_reg(cap_reg + 1), table_offset);
633
634        // PBA offset also
635        let pba_offset = cfg.read_reg(cap_reg + 2);
636        // Try to flip all bits
637        cfg.write_config_register(cap_reg + 2, 0, &u32::to_le_bytes(!pba_offset));
638        // None should be flipped
639        assert_eq!(cfg.read_reg(cap_reg + 2), pba_offset);
640    }
641
642    fn default_pci_config() -> PciConfiguration {
643        PciConfiguration::new_type0(
644            0x1234,
645            0x5678,
646            0x1,
647            PciClassCode::MultimediaController,
648            &PciMultimediaSubclass::AudioController,
649            0xABCD,
650            0x2468,
651            None,
652        )
653    }
654
655    #[test]
656    fn class_code() {
657        let cfg = default_pci_config();
658        let class_reg = cfg.read_reg(2);
659        let class_code = (class_reg >> 24) & 0xFF;
660        let subclass = (class_reg >> 16) & 0xFF;
661        let prog_if = (class_reg >> 8) & 0xFF;
662        assert_eq!(class_code, 0x04);
663        assert_eq!(subclass, 0x01);
664        assert_eq!(prog_if, 0x0);
665    }
666
667    #[test]
668    #[should_panic]
669    fn test_encode_zero_sized_bar() {
670        encode_64_bits_bar_size(0);
671    }
672
673    #[test]
674    #[should_panic]
675    fn test_decode_zero_sized_bar() {
676        decode_64_bits_bar_size(0, 0);
677    }
678
679    #[test]
680    fn test_bar_size_encoding() {
681        // According to OSDev wiki (https://wiki.osdev.org/PCI#Address_and_size_of_the_BAR):
682        //
683        // > To determine the amount of address space needed by a PCI device, you must save the
684        // > original value of the BAR, write a value of all 1's to the register, then read it back.
685        // > The amount of memory can then be determined by masking the information bits, performing
686        // > a bitwise NOT ('~' in C), and incrementing the value by 1. The original value of the
687        // BAR > should then be restored. The BAR register is naturally aligned and as such you can
688        // only > modify the bits that are set. For example, if a device utilizes 16 MB it will
689        // have BAR0 > filled with 0xFF000000 (0x1000000 after decoding) and you can only modify
690        // the upper > 8-bits.
691        //
692        // So, we encode a 64 bits size and then store it as a 2 32bit addresses (we use
693        // two BARs).
694        let (hi, lo) = encode_64_bits_bar_size(0xffff_ffff_ffff_fff0);
695        assert_eq!(hi, 0);
696        assert_eq!(lo, 0x0000_0010);
697        assert_eq!(decode_64_bits_bar_size(hi, lo), 0xffff_ffff_ffff_fff0);
698    }
699
700    #[test]
701    #[should_panic]
702    fn test_bar_size_no_power_of_two() {
703        let mut pci_config = default_pci_config();
704        pci_config.add_pci_bar(0, 0x1000, 0x1001);
705    }
706
707    #[test]
708    #[should_panic]
709    fn test_bad_bar_index() {
710        let mut pci_config = default_pci_config();
711        pci_config.add_pci_bar(NUM_BAR_REGS, 0x1000, 0x1000);
712    }
713
714    #[test]
715    #[should_panic]
716    fn test_bad_64bit_bar_index() {
717        let mut pci_config = default_pci_config();
718        pci_config.add_pci_bar(NUM_BAR_REGS - 1, 0x1000, 0x1000);
719    }
720
721    #[test]
722    #[should_panic]
723    fn test_bar_size_overflows() {
724        let mut pci_config = default_pci_config();
725        pci_config.add_pci_bar(0, u64::MAX, 0x2);
726    }
727
728    #[test]
729    #[should_panic]
730    fn test_lower_bar_free_upper_used() {
731        let mut pci_config = default_pci_config();
732        pci_config.add_pci_bar(1, 0x1000, 0x1000);
733        pci_config.add_pci_bar(0, 0x1000, 0x1000);
734    }
735
736    #[test]
737    #[should_panic]
738    fn test_lower_bar_used() {
739        let mut pci_config = default_pci_config();
740        pci_config.add_pci_bar(0, 0x1000, 0x1000);
741        pci_config.add_pci_bar(0, 0x1000, 0x1000);
742    }
743
744    #[test]
745    #[should_panic]
746    fn test_upper_bar_used() {
747        let mut pci_config = default_pci_config();
748        pci_config.add_pci_bar(0, 0x1000, 0x1000);
749        pci_config.add_pci_bar(1, 0x1000, 0x1000);
750    }
751
752    #[test]
753    fn test_add_pci_bar() {
754        let mut pci_config = default_pci_config();
755
756        pci_config.add_pci_bar(0, 0x1_0000_0000, 0x1000);
757
758        assert_eq!(pci_config.get_bar_addr(0), 0x1_0000_0000);
759        assert_eq!(pci_config.read_reg(BAR0_REG) & 0xffff_fff0, 0x0);
760        assert!(pci_config.bars[0].used);
761        assert_eq!(pci_config.read_reg(BAR0_REG + 1), 1);
762        assert!(pci_config.bars[0].used);
763    }
764
765    #[test]
766    fn test_access_invalid_reg() {
767        let mut pci_config = default_pci_config();
768
769        // Can't read past the end of the configuration space
770        assert_eq!(
771            pci_config.read_reg(NUM_CONFIGURATION_REGISTERS),
772            0xffff_ffff
773        );
774
775        // Read out all of configuration space
776        let config_space: Vec<u32> = (0..NUM_CONFIGURATION_REGISTERS)
777            .map(|reg_idx| pci_config.read_reg(reg_idx))
778            .collect();
779
780        // Various invalid write accesses
781
782        // Past the end of config space
783        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 0, &[0x42]);
784        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 0, &[0x42, 0x42]);
785        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 0, &[0x42, 0x42, 0x42, 0x42]);
786
787        // Past register boundaries
788        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 1, &[0x42, 0x42, 0x42, 0x42]);
789        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 2, &[0x42, 0x42, 0x42]);
790        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 3, &[0x42, 0x42]);
791        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 4, &[0x42]);
792        pci_config.write_config_register(NUM_CONFIGURATION_REGISTERS, 5, &[]);
793
794        for (reg_idx, reg) in config_space.iter().enumerate() {
795            assert_eq!(*reg, pci_config.read_reg(reg_idx));
796        }
797    }
798
799    #[test]
800    fn test_detect_bar_reprogramming() {
801        let mut pci_config = default_pci_config();
802
803        // Trying to reprogram with something less than 4 bytes (length of the address) should fail
804        assert!(
805            pci_config
806                .detect_bar_reprogramming(BAR0_REG, &[0x13])
807                .is_none()
808        );
809        assert!(
810            pci_config
811                .detect_bar_reprogramming(BAR0_REG, &[0x13, 0x12])
812                .is_none()
813        );
814        assert!(
815            pci_config
816                .detect_bar_reprogramming(BAR0_REG, &[0x13, 0x12])
817                .is_none()
818        );
819        assert!(
820            pci_config
821                .detect_bar_reprogramming(BAR0_REG, &[0x13, 0x12, 0x16])
822                .is_none()
823        );
824
825        // Writing all 1s is a special case where we're actually asking for the size of the BAR
826        assert!(
827            pci_config
828                .detect_bar_reprogramming(BAR0_REG, &u32::to_le_bytes(0xffff_ffff))
829                .is_none()
830        );
831
832        // Trying to reprogram a BAR that hasn't be initialized does nothing
833        for reg_idx in BAR0_REG..BAR0_REG + NUM_BAR_REGS {
834            assert!(
835                pci_config
836                    .detect_bar_reprogramming(reg_idx, &u32::to_le_bytes(0x1312_4243))
837                    .is_none()
838            );
839        }
840
841        // Reprogramming of a 64bit BAR
842        pci_config.add_pci_bar(0, 0x13_1200_0000, 0x8000);
843
844        // First we write the lower 32 bits and this shouldn't cause any reprogramming
845        assert!(
846            pci_config
847                .detect_bar_reprogramming(BAR0_REG, &u32::to_le_bytes(0x4200_0000))
848                .is_none()
849        );
850        pci_config.write_config_register(BAR0_REG, 0, &u32::to_le_bytes(0x4200_0000));
851
852        // Writing the upper 32 bits should trigger the reprogramming
853        assert_eq!(
854            pci_config.detect_bar_reprogramming(BAR0_REG + 1, &u32::to_le_bytes(0x84)),
855            Some(BarReprogrammingParams {
856                old_base: 0x13_1200_0000,
857                new_base: 0x84_4200_0000,
858                len: 0x8000,
859            })
860        );
861        pci_config.write_config_register(BAR0_REG + 1, 0, &u32::to_le_bytes(0x84));
862
863        // Trying to reprogram the upper bits directly (without first touching the lower bits)
864        // should trigger a reprogramming
865        assert_eq!(
866            pci_config.detect_bar_reprogramming(BAR0_REG + 1, &u32::to_le_bytes(0x1312)),
867            Some(BarReprogrammingParams {
868                old_base: 0x84_4200_0000,
869                new_base: 0x1312_4200_0000,
870                len: 0x8000,
871            })
872        );
873        pci_config.write_config_register(BAR0_REG + 1, 0, &u32::to_le_bytes(0x1312));
874
875        // Attempting to reprogram the BAR with the same address should not have any effect
876        assert!(
877            pci_config
878                .detect_bar_reprogramming(BAR0_REG, &u32::to_le_bytes(0x4200_0000))
879                .is_none()
880        );
881        assert!(
882            pci_config
883                .detect_bar_reprogramming(BAR0_REG + 1, &u32::to_le_bytes(0x1312))
884                .is_none()
885        );
886    }
887
888    #[test]
889    fn test_rom_bar() {
890        let mut pci_config = default_pci_config();
891
892        // ROM BAR address should always be 0 and writes to it shouldn't do anything
893        assert_eq!(pci_config.read_reg(ROM_BAR_REG), 0);
894        pci_config.write_reg(ROM_BAR_REG, 0x42);
895        assert_eq!(pci_config.read_reg(ROM_BAR_REG), 0);
896
897        // Reading the size of the BAR should always return 0 as well
898        pci_config.write_reg(ROM_BAR_REG, 0xffff_ffff);
899        assert_eq!(pci_config.read_reg(ROM_BAR_REG), 0);
900    }
901}