vmm/pci/
bus.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::collections::HashMap;
9use std::fmt::Debug;
10use std::ops::DerefMut;
11use std::sync::{Arc, Barrier, Mutex};
12
13use byteorder::{ByteOrder, LittleEndian};
14use pci::{PciBridgeSubclass, PciClassCode};
15
16use crate::logger::error;
17use crate::pci::configuration::PciConfiguration;
18use crate::pci::{DeviceRelocation, PciDevice};
19use crate::utils::u64_to_usize;
20use crate::vstate::bus::BusDevice;
21
22/// Errors for device manager.
23#[derive(Debug, thiserror::Error, displaydoc::Display)]
24pub enum PciRootError {
25    /// Could not find an available device slot on the PCI bus.
26    NoPciDeviceSlotAvailable,
27}
28
29const VENDOR_ID_INTEL: u16 = 0x8086;
30const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
31const NUM_DEVICE_IDS: usize = 32;
32
33#[derive(Debug)]
34/// Emulates the PCI Root bridge device.
35pub struct PciRoot {
36    /// Configuration space.
37    config: PciConfiguration,
38}
39
40impl PciRoot {
41    /// Create an empty PCI root bridge.
42    pub fn new(config: Option<PciConfiguration>) -> Self {
43        if let Some(config) = config {
44            PciRoot { config }
45        } else {
46            PciRoot {
47                config: PciConfiguration::new_type0(
48                    VENDOR_ID_INTEL,
49                    DEVICE_ID_INTEL_VIRT_PCIE_HOST,
50                    0,
51                    PciClassCode::BridgeDevice,
52                    &PciBridgeSubclass::HostBridge,
53                    0,
54                    0,
55                    None,
56                ),
57            }
58        }
59    }
60}
61
62impl BusDevice for PciRoot {}
63
64impl PciDevice for PciRoot {
65    fn write_config_register(
66        &mut self,
67        reg_idx: usize,
68        offset: u64,
69        data: &[u8],
70    ) -> Option<Arc<Barrier>> {
71        self.config.write_config_register(reg_idx, offset, data);
72        None
73    }
74
75    fn read_config_register(&mut self, reg_idx: usize) -> u32 {
76        self.config.read_reg(reg_idx)
77    }
78}
79
80/// A PCI bus definition
81pub struct PciBus {
82    /// Devices attached to this bus.
83    /// Device 0 is host bridge.
84    pub devices: HashMap<u32, Arc<Mutex<dyn PciDevice>>>,
85    vm: Arc<dyn DeviceRelocation>,
86    device_ids: Vec<bool>,
87}
88
89impl Debug for PciBus {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        f.debug_struct("Root Firecracker PCI Bus")
92            .field("device_ids", &self.device_ids)
93            .finish()
94    }
95}
96
97impl PciBus {
98    /// Create a new PCI bus
99    pub fn new(pci_root: PciRoot, vm: Arc<dyn DeviceRelocation>) -> Self {
100        let mut devices: HashMap<u32, Arc<Mutex<dyn PciDevice>>> = HashMap::new();
101        let mut device_ids: Vec<bool> = vec![false; NUM_DEVICE_IDS];
102
103        devices.insert(0, Arc::new(Mutex::new(pci_root)));
104        device_ids[0] = true;
105
106        PciBus {
107            devices,
108            vm,
109            device_ids,
110        }
111    }
112
113    /// Insert a device in the bus
114    pub fn add_device(&mut self, device_id: u32, device: Arc<Mutex<dyn PciDevice>>) {
115        self.devices.insert(device_id, device);
116    }
117
118    /// Get a new device ID
119    pub fn next_device_id(&mut self) -> Result<u32, PciRootError> {
120        for (idx, device_id) in self.device_ids.iter_mut().enumerate() {
121            if !(*device_id) {
122                *device_id = true;
123                return Ok(idx.try_into().unwrap());
124            }
125        }
126
127        Err(PciRootError::NoPciDeviceSlotAvailable)
128    }
129}
130
131#[cfg(target_arch = "x86_64")]
132/// IO port used for configuring PCI over the legacy bus
133pub const PCI_CONFIG_IO_PORT: u64 = 0xcf8;
134#[cfg(target_arch = "x86_64")]
135/// Size of IO ports we are using to configure PCI over the legacy bus. We have two ports, 0xcf8
136/// and 0xcfc 32bits long.
137pub const PCI_CONFIG_IO_PORT_SIZE: u64 = 0x8;
138
139/// Wrapper that allows handling PCI configuration over the legacy Bus
140#[derive(Debug)]
141pub struct PciConfigIo {
142    /// Config space register.
143    config_address: u32,
144    pci_bus: Arc<Mutex<PciBus>>,
145}
146
147impl PciConfigIo {
148    /// New Port IO configuration handler
149    pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
150        PciConfigIo {
151            config_address: 0,
152            pci_bus,
153        }
154    }
155
156    /// Handle a configuration space read over Port IO
157    pub fn config_space_read(&self) -> u32 {
158        let enabled = (self.config_address & 0x8000_0000) != 0;
159        if !enabled {
160            return 0xffff_ffff;
161        }
162
163        let (bus, device, function, register) =
164            parse_io_config_address(self.config_address & !0x8000_0000);
165
166        // Only support one bus.
167        if bus != 0 {
168            return 0xffff_ffff;
169        }
170
171        // Don't support multi-function devices.
172        if function > 0 {
173            return 0xffff_ffff;
174        }
175
176        // NOTE: Potential contention among vCPU threads on this lock. This should not
177        // be a problem currently, since we mainly access this when we are setting up devices.
178        // We might want to do some profiling to ensure this does not become a bottleneck.
179        self.pci_bus
180            .as_ref()
181            .lock()
182            .unwrap()
183            .devices
184            .get(&(device.try_into().unwrap()))
185            .map_or(0xffff_ffff, |d| {
186                d.lock().unwrap().read_config_register(register)
187            })
188    }
189
190    /// Handle a configuration space write over Port IO
191    pub fn config_space_write(&mut self, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
192        if u64_to_usize(offset) + data.len() > 4 {
193            return None;
194        }
195
196        let enabled = (self.config_address & 0x8000_0000) != 0;
197        if !enabled {
198            return None;
199        }
200
201        let (bus, device, function, register) =
202            parse_io_config_address(self.config_address & !0x8000_0000);
203
204        // Only support one bus.
205        if bus != 0 {
206            return None;
207        }
208
209        // Don't support multi-function devices.
210        if function > 0 {
211            return None;
212        }
213
214        // NOTE: Potential contention among vCPU threads on this lock. This should not
215        // be a problem currently, since we mainly access this when we are setting up devices.
216        // We might want to do some profiling to ensure this does not become a bottleneck.
217        let pci_bus = self.pci_bus.as_ref().lock().unwrap();
218        if let Some(d) = pci_bus.devices.get(&(device.try_into().unwrap())) {
219            let mut device = d.lock().unwrap();
220
221            // Find out if one of the device's BAR is being reprogrammed, and
222            // reprogram it if needed.
223            if let Some(params) = device.detect_bar_reprogramming(register, data)
224                && let Err(e) = pci_bus.vm.move_bar(
225                    params.old_base,
226                    params.new_base,
227                    params.len,
228                    device.deref_mut(),
229                )
230            {
231                error!(
232                    "Failed moving device BAR: {}: 0x{:x}->0x{:x}(0x{:x})",
233                    e, params.old_base, params.new_base, params.len
234                );
235            }
236
237            // Update the register value
238            device.write_config_register(register, offset, data)
239        } else {
240            None
241        }
242    }
243
244    fn set_config_address(&mut self, offset: u64, data: &[u8]) {
245        if u64_to_usize(offset) + data.len() > 4 {
246            return;
247        }
248        let (mask, value): (u32, u32) = match data.len() {
249            1 => (
250                0x0000_00ff << (offset * 8),
251                u32::from(data[0]) << (offset * 8),
252            ),
253            2 => (
254                0x0000_ffff << (offset * 8),
255                ((u32::from(data[1]) << 8) | u32::from(data[0])) << (offset * 8),
256            ),
257            4 => (0xffff_ffff, LittleEndian::read_u32(data)),
258            _ => return,
259        };
260        self.config_address = (self.config_address & !mask) | value;
261    }
262}
263
264impl BusDevice for PciConfigIo {
265    fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
266        // Only allow reads to the register boundary.
267        let start = u64_to_usize(offset) % 4;
268        let end = start + data.len();
269        if end > 4 {
270            for d in data.iter_mut() {
271                *d = 0xff;
272            }
273            return;
274        }
275
276        // `offset` is relative to 0xcf8
277        let value = match offset {
278            0..=3 => self.config_address,
279            4..=7 => self.config_space_read(),
280            _ => 0xffff_ffff,
281        };
282
283        for i in start..end {
284            data[i - start] = ((value >> (i * 8)) & 0xff) as u8;
285        }
286    }
287
288    fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
289        // `offset` is relative to 0xcf8
290        match offset {
291            o @ 0..=3 => {
292                self.set_config_address(o, data);
293                None
294            }
295            o @ 4..=7 => self.config_space_write(o - 4, data),
296            _ => None,
297        }
298    }
299}
300
301#[derive(Debug)]
302/// Emulates PCI memory-mapped configuration access mechanism.
303pub struct PciConfigMmio {
304    pci_bus: Arc<Mutex<PciBus>>,
305}
306
307impl PciConfigMmio {
308    /// New MMIO configuration handler object
309    pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
310        PciConfigMmio { pci_bus }
311    }
312
313    fn config_space_read(&self, config_address: u32) -> u32 {
314        let (bus, device, function, register) = parse_mmio_config_address(config_address);
315
316        // Only support one bus.
317        if bus != 0 {
318            return 0xffff_ffff;
319        }
320
321        // Don't support multi-function devices.
322        if function > 0 {
323            return 0xffff_ffff;
324        }
325
326        self.pci_bus
327            .lock()
328            .unwrap()
329            .devices
330            .get(&(device.try_into().unwrap()))
331            .map_or(0xffff_ffff, |d| {
332                d.lock().unwrap().read_config_register(register)
333            })
334    }
335
336    fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
337        if u64_to_usize(offset) + data.len() > 4 {
338            return;
339        }
340
341        let (bus, device, function, register) = parse_mmio_config_address(config_address);
342
343        // Only support one bus.
344        if bus != 0 {
345            return;
346        }
347
348        // Don't support multi-function devices.
349        if function > 0 {
350            return;
351        }
352
353        let pci_bus = self.pci_bus.lock().unwrap();
354        if let Some(d) = pci_bus.devices.get(&(device.try_into().unwrap())) {
355            let mut device = d.lock().unwrap();
356
357            // Find out if one of the device's BAR is being reprogrammed, and
358            // reprogram it if needed.
359            if let Some(params) = device.detect_bar_reprogramming(register, data)
360                && let Err(e) = pci_bus.vm.move_bar(
361                    params.old_base,
362                    params.new_base,
363                    params.len,
364                    device.deref_mut(),
365                )
366            {
367                error!(
368                    "Failed moving device BAR: {}: 0x{:x}->0x{:x}(0x{:x})",
369                    e, params.old_base, params.new_base, params.len
370                );
371            }
372
373            // Update the register value
374            device.write_config_register(register, offset, data);
375        }
376    }
377}
378
379impl BusDevice for PciConfigMmio {
380    fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
381        // Only allow reads to the register boundary.
382        let start = u64_to_usize(offset) % 4;
383        let end = start + data.len();
384        if end > 4 || offset > u64::from(u32::MAX) {
385            for d in data {
386                *d = 0xff;
387            }
388            return;
389        }
390
391        let value = self.config_space_read(offset.try_into().unwrap());
392        for i in start..end {
393            data[i - start] = ((value >> (i * 8)) & 0xff) as u8;
394        }
395    }
396
397    fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
398        if offset > u64::from(u32::MAX) {
399            return None;
400        }
401        self.config_space_write(offset.try_into().unwrap(), offset % 4, data);
402
403        None
404    }
405}
406
407fn shift_and_mask(value: u32, offset: usize, mask: u32) -> usize {
408    ((value >> offset) & mask) as usize
409}
410
411// Parse the MMIO address offset to a (bus, device, function, register) tuple.
412// See section 7.2.2 PCI Express Enhanced Configuration Access Mechanism (ECAM)
413// from the Pci Express Base Specification Revision 5.0 Version 1.0.
414fn parse_mmio_config_address(config_address: u32) -> (usize, usize, usize, usize) {
415    const BUS_NUMBER_OFFSET: usize = 20;
416    const BUS_NUMBER_MASK: u32 = 0x00ff;
417    const DEVICE_NUMBER_OFFSET: usize = 15;
418    const DEVICE_NUMBER_MASK: u32 = 0x1f;
419    const FUNCTION_NUMBER_OFFSET: usize = 12;
420    const FUNCTION_NUMBER_MASK: u32 = 0x07;
421    const REGISTER_NUMBER_OFFSET: usize = 2;
422    const REGISTER_NUMBER_MASK: u32 = 0x3ff;
423
424    (
425        shift_and_mask(config_address, BUS_NUMBER_OFFSET, BUS_NUMBER_MASK),
426        shift_and_mask(config_address, DEVICE_NUMBER_OFFSET, DEVICE_NUMBER_MASK),
427        shift_and_mask(config_address, FUNCTION_NUMBER_OFFSET, FUNCTION_NUMBER_MASK),
428        shift_and_mask(config_address, REGISTER_NUMBER_OFFSET, REGISTER_NUMBER_MASK),
429    )
430}
431
432// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple.
433fn parse_io_config_address(config_address: u32) -> (usize, usize, usize, usize) {
434    const BUS_NUMBER_OFFSET: usize = 16;
435    const BUS_NUMBER_MASK: u32 = 0x00ff;
436    const DEVICE_NUMBER_OFFSET: usize = 11;
437    const DEVICE_NUMBER_MASK: u32 = 0x1f;
438    const FUNCTION_NUMBER_OFFSET: usize = 8;
439    const FUNCTION_NUMBER_MASK: u32 = 0x07;
440    const REGISTER_NUMBER_OFFSET: usize = 2;
441    const REGISTER_NUMBER_MASK: u32 = 0x3f;
442
443    (
444        shift_and_mask(config_address, BUS_NUMBER_OFFSET, BUS_NUMBER_MASK),
445        shift_and_mask(config_address, DEVICE_NUMBER_OFFSET, DEVICE_NUMBER_MASK),
446        shift_and_mask(config_address, FUNCTION_NUMBER_OFFSET, FUNCTION_NUMBER_MASK),
447        shift_and_mask(config_address, REGISTER_NUMBER_OFFSET, REGISTER_NUMBER_MASK),
448    )
449}
450
451#[cfg(test)]
452mod tests {
453    use std::sync::atomic::AtomicUsize;
454    use std::sync::{Arc, Mutex};
455
456    use pci::{PciClassCode, PciMassStorageSubclass};
457
458    use super::{PciBus, PciConfigIo, PciConfigMmio, PciRoot};
459    use crate::pci::bus::{DEVICE_ID_INTEL_VIRT_PCIE_HOST, VENDOR_ID_INTEL};
460    use crate::pci::configuration::PciConfiguration;
461    use crate::pci::{BarReprogrammingParams, DeviceRelocation, DeviceRelocationError, PciDevice};
462    use crate::vstate::bus::BusDevice;
463
464    #[derive(Debug, Default)]
465    struct RelocationMock {
466        reloc_cnt: AtomicUsize,
467    }
468
469    impl RelocationMock {
470        fn cnt(&self) -> usize {
471            self.reloc_cnt.load(std::sync::atomic::Ordering::SeqCst)
472        }
473    }
474
475    impl DeviceRelocation for RelocationMock {
476        fn move_bar(
477            &self,
478            _old_base: u64,
479            _new_base: u64,
480            _len: u64,
481            _pci_dev: &mut dyn PciDevice,
482        ) -> Result<(), DeviceRelocationError> {
483            self.reloc_cnt
484                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
485            Ok(())
486        }
487    }
488
489    struct PciDevMock(PciConfiguration);
490
491    impl PciDevMock {
492        fn new() -> Self {
493            let mut config = PciConfiguration::new_type0(
494                0x42,
495                0x0,
496                0x0,
497                PciClassCode::MassStorage,
498                &PciMassStorageSubclass::SerialScsiController,
499                0x13,
500                0x12,
501                None,
502            );
503
504            config.add_pci_bar(0, 0x1000, 0x1000);
505
506            PciDevMock(config)
507        }
508    }
509
510    impl PciDevice for PciDevMock {
511        fn write_config_register(
512            &mut self,
513            reg_idx: usize,
514            offset: u64,
515            data: &[u8],
516        ) -> Option<Arc<std::sync::Barrier>> {
517            self.0.write_config_register(reg_idx, offset, data);
518            None
519        }
520
521        fn read_config_register(&mut self, reg_idx: usize) -> u32 {
522            self.0.read_reg(reg_idx)
523        }
524
525        fn detect_bar_reprogramming(
526            &mut self,
527            reg_idx: usize,
528            data: &[u8],
529        ) -> Option<BarReprogrammingParams> {
530            self.0.detect_bar_reprogramming(reg_idx, data)
531        }
532    }
533
534    #[test]
535    fn test_writing_io_config_address() {
536        let mock = Arc::new(RelocationMock::default());
537        let root = PciRoot::new(None);
538        let mut bus = PciConfigIo::new(Arc::new(Mutex::new(PciBus::new(root, mock))));
539
540        assert_eq!(bus.config_address, 0);
541        // Writing more than 32 bits will should fail
542        bus.write(0, 0, &[0x42; 8]);
543        assert_eq!(bus.config_address, 0);
544        // Write all the address at once
545        bus.write(0, 0, &[0x13, 0x12, 0x11, 0x10]);
546        assert_eq!(bus.config_address, 0x10111213);
547        // Not writing 32bits at offset 0 should have no effect
548        bus.write(0, 1, &[0x0; 4]);
549        assert_eq!(bus.config_address, 0x10111213);
550
551        // Write two bytes at a time
552        bus.write(0, 0, &[0x42, 0x42]);
553        assert_eq!(bus.config_address, 0x10114242);
554        bus.write(0, 1, &[0x43, 0x43]);
555        assert_eq!(bus.config_address, 0x10434342);
556        bus.write(0, 2, &[0x44, 0x44]);
557        assert_eq!(bus.config_address, 0x44444342);
558        // Writing two bytes at offset 3 should overflow, so it shouldn't have any effect
559        bus.write(0, 3, &[0x45, 0x45]);
560        assert_eq!(bus.config_address, 0x44444342);
561
562        // Write one byte at a time
563        bus.write(0, 0, &[0x0]);
564        assert_eq!(bus.config_address, 0x44444300);
565        bus.write(0, 1, &[0x0]);
566        assert_eq!(bus.config_address, 0x44440000);
567        bus.write(0, 2, &[0x0]);
568        assert_eq!(bus.config_address, 0x44000000);
569        bus.write(0, 3, &[0x0]);
570        assert_eq!(bus.config_address, 0x00000000);
571        // Writing past 4 bytes should have no effect
572        bus.write(0, 4, &[0x13]);
573        assert_eq!(bus.config_address, 0x0);
574    }
575
576    #[test]
577    fn test_reading_io_config_address() {
578        let mock = Arc::new(RelocationMock::default());
579        let root = PciRoot::new(None);
580        let mut bus = PciConfigIo::new(Arc::new(Mutex::new(PciBus::new(root, mock))));
581
582        let mut buffer = [0u8; 4];
583
584        bus.config_address = 0x13121110;
585
586        // First 4 bytes are the config address
587        // Next 4 bytes are the values read from the configuration space.
588        //
589        // Reading past offset 7 should not return nothing (all 1s)
590        bus.read(0, 8, &mut buffer);
591        assert_eq!(buffer, [0xff; 4]);
592
593        // offset + buffer.len() needs to be smaller or equal than 4
594        bus.read(0, 1, &mut buffer);
595        assert_eq!(buffer, [0xff; 4]);
596        bus.read(0, 2, &mut buffer[..3]);
597        assert_eq!(buffer, [0xff; 4]);
598        bus.read(0, 3, &mut buffer[..2]);
599        assert_eq!(buffer, [0xff; 4]);
600
601        // reading one byte at a time
602        bus.read(0, 0, &mut buffer[0..1]);
603        assert_eq!(buffer, [0x10, 0xff, 0xff, 0xff]);
604        bus.read(0, 1, &mut buffer[1..2]);
605        assert_eq!(buffer, [0x10, 0x11, 0xff, 0xff]);
606        bus.read(0, 2, &mut buffer[2..3]);
607        assert_eq!(buffer, [0x10, 0x11, 0x12, 0xff]);
608        bus.read(0, 3, &mut buffer[3..4]);
609        assert_eq!(buffer, [0x10, 0x11, 0x12, 0x13]);
610
611        // reading two bytes at a time
612        bus.config_address = 0x42434445;
613        bus.read(0, 0, &mut buffer[..2]);
614        assert_eq!(buffer, [0x45, 0x44, 0x12, 0x13]);
615        bus.read(0, 1, &mut buffer[..2]);
616        assert_eq!(buffer, [0x44, 0x43, 0x12, 0x13]);
617        bus.read(0, 2, &mut buffer[..2]);
618        assert_eq!(buffer, [0x43, 0x42, 0x12, 0x13]);
619
620        // reading all of it at once
621        bus.read(0, 0, &mut buffer);
622        assert_eq!(buffer, [0x45, 0x44, 0x43, 0x42]);
623    }
624
625    fn initialize_bus() -> (PciConfigMmio, PciConfigIo, Arc<RelocationMock>) {
626        let mock = Arc::new(RelocationMock::default());
627        let root = PciRoot::new(None);
628        let mut bus = PciBus::new(root, mock.clone());
629        bus.add_device(1, Arc::new(Mutex::new(PciDevMock::new())));
630
631        let bus = Arc::new(Mutex::new(bus));
632        (PciConfigMmio::new(bus.clone()), PciConfigIo::new(bus), mock)
633    }
634
635    #[test]
636    fn test_invalid_register_boundary_reads() {
637        let (mut mmio_config, mut io_config, _) = initialize_bus();
638
639        // Read crossing register boundaries
640        let mut buffer = [0u8; 4];
641        mmio_config.read(0, 1, &mut buffer);
642        assert_eq!(0xffff_ffff, u32::from_le_bytes(buffer));
643
644        let mut buffer = [0u8; 4];
645        io_config.read(0, 1, &mut buffer);
646        assert_eq!(0xffff_ffff, u32::from_le_bytes(buffer));
647
648        // As well in the config space
649        let mut buffer = [0u8; 4];
650        io_config.read(0, 5, &mut buffer);
651        assert_eq!(0xffff_ffff, u32::from_le_bytes(buffer));
652    }
653
654    // MMIO config addresses are of the form
655    //
656    // | Base address upper bits | Bus Number | Device Number | Function Number | Register number | Byte offset |
657    // |         31-28           |    27-20   |     19-15     |      14-12      |      11-2       |     0-1     |
658    //
659    // Meaning that the offset is built using:
660    //
661    // `bus << 20 | device << 15 | function << 12 | register << 2 | byte`
662    fn mmio_offset(bus: u8, device: u8, function: u8, register: u16, byte: u8) -> u32 {
663        assert!(device < 32);
664        assert!(function < 8);
665        assert!(register < 1024);
666        assert!(byte < 4);
667
668        (bus as u32) << 20
669            | (device as u32) << 15
670            | (function as u32) << 12
671            | (register as u32) << 2
672            | (byte as u32)
673    }
674
675    fn read_mmio_config(
676        config: &mut PciConfigMmio,
677        bus: u8,
678        device: u8,
679        function: u8,
680        register: u16,
681        byte: u8,
682        data: &mut [u8],
683    ) {
684        config.read(
685            0,
686            mmio_offset(bus, device, function, register, byte) as u64,
687            data,
688        );
689    }
690
691    fn write_mmio_config(
692        config: &mut PciConfigMmio,
693        bus: u8,
694        device: u8,
695        function: u8,
696        register: u16,
697        byte: u8,
698        data: &[u8],
699    ) {
700        config.write(
701            0,
702            mmio_offset(bus, device, function, register, byte) as u64,
703            data,
704        );
705    }
706
707    // Similarly, when using the IO mechanism the config addresses have the following format
708    //
709    // | Enabled | zeros | Bus Number | Device Number | Function Number | Register number | zeros |
710    // |    31   | 30-24 |   23-16    |     15-11     |      10-8       |       7-2       |  1-0  |
711    //
712    //
713    // Meaning that the address is built using:
714    //
715    // 0x8000_0000 | bus << 16 | device << 11 | function << 8 | register << 2;
716    //
717    // Only 32-bit aligned accesses are allowed here.
718    fn pio_offset(enabled: bool, bus: u8, device: u8, function: u8, register: u8) -> u32 {
719        assert!(device < 32);
720        assert!(function < 8);
721        assert!(register < 64);
722
723        let offset = if enabled { 0x8000_0000 } else { 0u32 };
724
725        offset
726            | (bus as u32) << 16
727            | (device as u32) << 11
728            | (function as u32) << 8
729            | (register as u32) << 2
730    }
731
732    fn set_io_address(
733        config: &mut PciConfigIo,
734        enabled: bool,
735        bus: u8,
736        device: u8,
737        function: u8,
738        register: u8,
739    ) {
740        let address = u32::to_le_bytes(pio_offset(enabled, bus, device, function, register));
741        config.write(0, 0, &address);
742    }
743
744    fn read_io_config(
745        config: &mut PciConfigIo,
746        enabled: bool,
747        bus: u8,
748        device: u8,
749        function: u8,
750        register: u8,
751        data: &mut [u8],
752    ) {
753        set_io_address(config, enabled, bus, device, function, register);
754        config.read(0, 4, data);
755    }
756
757    fn write_io_config(
758        config: &mut PciConfigIo,
759        enabled: bool,
760        bus: u8,
761        device: u8,
762        function: u8,
763        register: u8,
764        data: &[u8],
765    ) {
766        set_io_address(config, enabled, bus, device, function, register);
767        config.write(0, 4, data);
768    }
769
770    #[test]
771    fn test_mmio_invalid_bus_number() {
772        let (mut mmio_config, _, _) = initialize_bus();
773        let mut buffer = [0u8; 4];
774
775        // Asking for Bus 1 should return all 1s
776        read_mmio_config(&mut mmio_config, 1, 0, 0, 0, 0, &mut buffer);
777        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
778        // Writing the same
779        buffer[0] = 0x42;
780        write_mmio_config(&mut mmio_config, 1, 0, 0, 15, 0, &buffer);
781        read_mmio_config(&mut mmio_config, 1, 0, 0, 15, 0, &mut buffer);
782        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
783        read_mmio_config(&mut mmio_config, 0, 0, 0, 15, 0, &mut buffer);
784        assert_eq!(buffer, u32::to_le_bytes(0x0));
785
786        // Asking for Bus 0 should work
787        read_mmio_config(&mut mmio_config, 0, 0, 0, 0, 0, &mut buffer);
788        assert_eq!(&buffer[..2], &u16::to_le_bytes(VENDOR_ID_INTEL));
789        assert_eq!(
790            &buffer[2..],
791            &u16::to_le_bytes(DEVICE_ID_INTEL_VIRT_PCIE_HOST)
792        );
793    }
794
795    #[test]
796    fn test_io_invalid_bus_number() {
797        let (_, mut pio_config, _) = initialize_bus();
798        let mut buffer = [0u8; 4];
799
800        // Asking for Bus 1 should return all 1s
801        read_io_config(&mut pio_config, true, 1, 0, 0, 0, &mut buffer);
802        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
803
804        // Asking for Bus 0 should work
805        read_io_config(&mut pio_config, true, 0, 0, 0, 0, &mut buffer);
806        assert_eq!(&buffer[..2], &u16::to_le_bytes(VENDOR_ID_INTEL));
807        assert_eq!(
808            &buffer[2..],
809            &u16::to_le_bytes(DEVICE_ID_INTEL_VIRT_PCIE_HOST)
810        );
811    }
812
813    #[test]
814    fn test_mmio_invalid_function() {
815        let (mut mmio_config, _, _) = initialize_bus();
816        let mut buffer = [0u8; 4];
817
818        // Asking for Bus 1 should return all 1s
819        read_mmio_config(&mut mmio_config, 0, 0, 1, 0, 0, &mut buffer);
820        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
821        // Writing the same
822        buffer[0] = 0x42;
823        write_mmio_config(&mut mmio_config, 0, 0, 1, 15, 0, &buffer);
824        read_mmio_config(&mut mmio_config, 0, 0, 1, 15, 0, &mut buffer);
825        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
826        read_mmio_config(&mut mmio_config, 0, 0, 0, 15, 0, &mut buffer);
827        assert_eq!(buffer, u32::to_le_bytes(0x0));
828
829        // Asking for Bus 0 should work
830        read_mmio_config(&mut mmio_config, 0, 0, 0, 0, 0, &mut buffer);
831        assert_eq!(&buffer[..2], &u16::to_le_bytes(VENDOR_ID_INTEL));
832        assert_eq!(
833            &buffer[2..],
834            &u16::to_le_bytes(DEVICE_ID_INTEL_VIRT_PCIE_HOST)
835        );
836    }
837
838    #[test]
839    fn test_io_invalid_function() {
840        let (_, mut pio_config, _) = initialize_bus();
841        let mut buffer = [0u8; 4];
842
843        // Asking for Bus 1 should return all 1s
844        read_io_config(&mut pio_config, true, 0, 0, 1, 0, &mut buffer);
845        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
846
847        // Asking for Bus 0 should work
848        read_io_config(&mut pio_config, true, 0, 0, 0, 0, &mut buffer);
849        assert_eq!(&buffer[..2], &u16::to_le_bytes(VENDOR_ID_INTEL));
850        assert_eq!(
851            &buffer[2..],
852            &u16::to_le_bytes(DEVICE_ID_INTEL_VIRT_PCIE_HOST)
853        );
854    }
855
856    #[test]
857    fn test_io_disabled_reads() {
858        let (_, mut pio_config, _) = initialize_bus();
859        let mut buffer = [0u8; 4];
860
861        // Trying to read without enabling should return all 1s
862        read_io_config(&mut pio_config, false, 0, 0, 0, 0, &mut buffer);
863        assert_eq!(buffer, u32::to_le_bytes(0xffff_ffff));
864
865        // Asking for Bus 0 should work
866        read_io_config(&mut pio_config, true, 0, 0, 0, 0, &mut buffer);
867        assert_eq!(&buffer[..2], &u16::to_le_bytes(VENDOR_ID_INTEL));
868        assert_eq!(
869            &buffer[2..],
870            &u16::to_le_bytes(DEVICE_ID_INTEL_VIRT_PCIE_HOST)
871        );
872    }
873
874    #[test]
875    fn test_io_disabled_writes() {
876        let (_, mut pio_config, _) = initialize_bus();
877
878        // Try to write the IRQ line used for the root port.
879        let mut buffer = [0u8; 4];
880
881        // First read the current value (use `enabled` bit)
882        read_io_config(&mut pio_config, true, 0, 0, 0, 15, &mut buffer);
883        let irq_line = buffer[0];
884
885        // Write without setting the `enabled` bit.
886        buffer[0] = 0x42;
887        write_io_config(&mut pio_config, false, 0, 0, 0, 15, &buffer);
888
889        // IRQ line shouldn't have changed
890        read_io_config(&mut pio_config, true, 0, 0, 0, 15, &mut buffer);
891        assert_eq!(buffer[0], irq_line);
892
893        // Write with `enabled` bit set.
894        buffer[0] = 0x42;
895        write_io_config(&mut pio_config, true, 0, 0, 0, 15, &buffer);
896
897        // IRQ line should change
898        read_io_config(&mut pio_config, true, 0, 0, 0, 15, &mut buffer);
899        assert_eq!(buffer[0], 0x42);
900    }
901
902    #[test]
903    fn test_mmio_writes() {
904        let (mut mmio_config, _, _) = initialize_bus();
905        let mut buffer = [0u8; 4];
906
907        read_mmio_config(&mut mmio_config, 0, 0, 0, 15, 0, &mut buffer);
908        assert_eq!(buffer[0], 0x0);
909        write_mmio_config(&mut mmio_config, 0, 0, 0, 15, 0, &[0x42]);
910        read_mmio_config(&mut mmio_config, 0, 0, 0, 15, 0, &mut buffer);
911        assert_eq!(buffer[0], 0x42);
912    }
913
914    #[test]
915    fn test_bar_reprogramming() {
916        let (mut mmio_config, _, mock) = initialize_bus();
917        let mut buffer = [0u8; 4];
918        assert_eq!(mock.cnt(), 0);
919
920        read_mmio_config(&mut mmio_config, 0, 1, 0, 0x4, 0, &mut buffer);
921        let old_addr = u32::from_le_bytes(buffer) & 0xffff_fff0;
922        assert_eq!(old_addr, 0x1000);
923
924        // Writing the lower 32bits first should not trigger any reprogramming
925        write_mmio_config(
926            &mut mmio_config,
927            0,
928            1,
929            0,
930            0x4,
931            0,
932            &u32::to_le_bytes(0x1312_0000),
933        );
934
935        read_mmio_config(&mut mmio_config, 0, 1, 0, 0x4, 0, &mut buffer);
936        let new_addr = u32::from_le_bytes(buffer) & 0xffff_fff0;
937        assert_eq!(new_addr, 0x1312_0000);
938        assert_eq!(mock.cnt(), 0);
939
940        // Writing the upper 32bits first should now trigger the reprogramming logic
941        write_mmio_config(&mut mmio_config, 0, 1, 0, 0x5, 0, &u32::to_le_bytes(0x1110));
942        read_mmio_config(&mut mmio_config, 0, 1, 0, 0x5, 0, &mut buffer);
943        let new_addr = u32::from_le_bytes(buffer);
944        assert_eq!(new_addr, 0x1110);
945        assert_eq!(mock.cnt(), 1);
946
947        // BAR2 should not be used, so reading its address should return all 0s
948        read_mmio_config(&mut mmio_config, 0, 1, 0, 0x6, 0, &mut buffer);
949        assert_eq!(buffer, [0x0, 0x0, 0x0, 0x0]);
950
951        // and reprogramming shouldn't have any effect
952        write_mmio_config(
953            &mut mmio_config,
954            0,
955            1,
956            0,
957            0x5,
958            0,
959            &u32::to_le_bytes(0x1312_1110),
960        );
961
962        read_mmio_config(&mut mmio_config, 0, 1, 0, 0x6, 0, &mut buffer);
963        assert_eq!(buffer, [0x0, 0x0, 0x0, 0x0]);
964    }
965}