Skip to main content

virtio/transport/
pci.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! PCI transport for virtio devices
5
6use self::capabilities::*;
7use super::StalledIo;
8use super::core::TransportOps;
9use super::core::VirtioTransportCore;
10use super::task::ConfigReadCompletion;
11use super::task::defer_config_read;
12use super::task::defer_config_write;
13use crate::DynVirtioDevice;
14use crate::MAX_QUEUE_SIZE;
15use crate::spec::VirtioDeviceType;
16use crate::spec::pci::VIRTIO_PCI_COMMON_CFG_SIZE;
17use crate::spec::pci::VIRTIO_PCI_DEVICE_ID_BASE;
18use crate::spec::pci::VIRTIO_VENDOR_ID;
19use crate::spec::pci::VirtioPciCapType;
20use crate::spec::pci::VirtioPciCommonCfg;
21use chipset_device::ChipsetDevice;
22use chipset_device::io::IoError;
23use chipset_device::io::IoResult;
24use chipset_device::io::deferred::defer_read;
25use chipset_device::io::deferred::defer_write;
26use chipset_device::mmio::MmioIntercept;
27use chipset_device::mmio::RegisterMmioIntercept;
28use chipset_device::pci::ByteEnabledDwordRead;
29use chipset_device::pci::ByteEnabledDwordWrite;
30use chipset_device::pci::PciConfigSpace;
31use chipset_device::poll_device::PollDevice;
32use device_emulators::ReadWriteRequestType;
33use device_emulators::read_as_u32_chunks;
34use device_emulators::write_as_u32_chunks;
35use guestmem::DoorbellRegistration;
36use guestmem::GuestMemory;
37use guestmem::MemoryMapper;
38use inspect::Inspect;
39use inspect::InspectMut;
40use pal_async::task::Spawn;
41use parking_lot::Mutex;
42use pci_core::PciInterruptPin;
43use pci_core::capabilities::PciCapability;
44use pci_core::capabilities::ReadOnlyCapability;
45use pci_core::capabilities::msix::MsixEmulator;
46use pci_core::cfg_space_emu::BarMemoryKind;
47use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
48use pci_core::cfg_space_emu::DeviceBars;
49use pci_core::cfg_space_emu::IntxInterrupt;
50use pci_core::msi::MsiTarget;
51use pci_core::spec::caps::COMMON_HEADER_END;
52use pci_core::spec::caps::CapabilityId;
53use pci_core::spec::hwid::ClassCode;
54use pci_core::spec::hwid::HardwareIds;
55use pci_core::spec::hwid::ProgrammingInterface;
56use pci_core::spec::hwid::Subclass;
57use std::io;
58use std::sync::Arc;
59use vmcore::device_state::ChangeDeviceState;
60use vmcore::interrupt::Interrupt;
61use vmcore::line_interrupt::LineInterrupt;
62use vmcore::save_restore::RestoreError;
63use vmcore::save_restore::SaveError;
64
65/// What kind of PCI interrupts [`VirtioPciDevice`] should use.
66pub enum PciInterruptModel<'a> {
67    Msix(&'a MsiTarget),
68    IntX(PciInterruptPin, LineInterrupt),
69}
70
71enum InterruptKind {
72    Msix(MsixEmulator),
73    IntX(Arc<IntxInterrupt>),
74}
75
76/// BAR0 layout: common cfg is at offset 0, followed by notify, ISR, and
77/// device-specific config regions.
78const BAR0_NOTIFY_OFFSET: u16 = VIRTIO_PCI_COMMON_CFG_SIZE;
79const BAR0_NOTIFY_SIZE: u16 = 4;
80const BAR0_ISR_OFFSET: u16 = BAR0_NOTIFY_OFFSET + BAR0_NOTIFY_SIZE;
81const BAR0_ISR_SIZE: u16 = 4;
82const BAR0_DEVICE_CFG_OFFSET: u16 = BAR0_ISR_OFFSET + BAR0_ISR_SIZE;
83
84/// Map a virtio device type to its PCI class/subclass.
85///
86/// The virtio spec does not require a particular class code — drivers bind on
87/// vendor/device ID — but reporting an accurate class lets the guest OS
88/// categorize the device correctly.
89fn virtio_class_code(device_id: VirtioDeviceType) -> (ClassCode, Subclass) {
90    match device_id {
91        VirtioDeviceType::NET => (
92            ClassCode::NETWORK_CONTROLLER,
93            Subclass::NETWORK_CONTROLLER_ETHERNET,
94        ),
95        VirtioDeviceType::BLK => (
96            ClassCode::MASS_STORAGE_CONTROLLER,
97            Subclass::MASS_STORAGE_CONTROLLER_SCSI,
98        ),
99        VirtioDeviceType::CONSOLE => (
100            ClassCode::SIMPLE_COMMUNICATION_CONTROLLER,
101            Subclass::SIMPLE_COMMUNICATION_CONTROLLER_OTHER,
102        ),
103        // These device types have no well-established class code; report a
104        // generic base system peripheral.
105        VirtioDeviceType::RNG
106        | VirtioDeviceType::P9
107        | VirtioDeviceType::VSOCK
108        | VirtioDeviceType::FS
109        | VirtioDeviceType::PMEM => (
110            ClassCode::BASE_SYSTEM_PERIPHERAL,
111            Subclass::BASE_SYSTEM_PERIPHERAL_OTHER,
112        ),
113        _ => {
114            tracelimit::warn_ratelimited!(
115                device_id = device_id.0,
116                "unknown virtio device type; reporting generic class code"
117            );
118            (
119                ClassCode::BASE_SYSTEM_PERIPHERAL,
120                Subclass::BASE_SYSTEM_PERIPHERAL_OTHER,
121            )
122        }
123    }
124}
125
126/// PCI-specific transport state.
127#[derive(Inspect)]
128struct PciTransport {
129    config_space: ConfigSpaceType0Emulator,
130    #[inspect(skip)]
131    interrupt_kind: InterruptKind,
132    #[inspect(skip)]
133    interrupt_status: Arc<Mutex<u32>>,
134    msix_config_vector: u16,
135    #[inspect(hex)]
136    shared_memory_size: u64,
137    /// Shared window state for the `VIRTIO_PCI_CAP_PCI_CFG` capability.
138    #[inspect(skip)]
139    pci_cfg_access: Arc<Mutex<PciCfgAccessState>>,
140    /// Config-space offset of the `pci_cfg_data` window.
141    #[inspect(hex)]
142    pci_cfg_data_offset: u16,
143    /// Length of the device-specific config region within BAR0, used to
144    /// bound `pci_cfg_data` accesses.
145    #[inspect(hex)]
146    device_register_length: u32,
147}
148
149impl TransportOps for PciTransport {
150    fn create_queue_interrupt(&mut self, _idx: usize, msix_vector: u16) -> Interrupt {
151        match &self.interrupt_kind {
152            InterruptKind::Msix(msix) => {
153                if let Some(interrupt) = msix.interrupt(msix_vector) {
154                    interrupt
155                } else {
156                    tracelimit::warn_ratelimited!(msix_vector, "invalid MSIx vector specified");
157                    Interrupt::null()
158                }
159            }
160            InterruptKind::IntX(line) => {
161                let interrupt_status = self.interrupt_status.clone();
162                let line = line.clone();
163                Interrupt::from_fn(move || {
164                    *interrupt_status.lock() |= 1;
165                    line.set_level(true);
166                })
167            }
168        }
169    }
170
171    fn signal_config_change(&mut self) {
172        *self.interrupt_status.lock() |= 2;
173        match &self.interrupt_kind {
174            InterruptKind::Msix(msix) => {
175                if let Some(interrupt) = msix.interrupt(self.msix_config_vector) {
176                    interrupt.deliver();
177                }
178            }
179            InterruptKind::IntX(line) => line.set_level(true),
180        }
181    }
182
183    fn reset_interrupts(&mut self) {
184        *self.interrupt_status.lock() = 0;
185        if let InterruptKind::IntX(line) = &self.interrupt_kind {
186            line.set_level(false);
187        }
188        self.msix_config_vector = 0;
189    }
190
191    fn doorbell_region(&mut self) -> Option<(u64, u32)> {
192        self.config_space
193            .bar_address(0)
194            .map(|base| (base + BAR0_NOTIFY_OFFSET as u64, 2))
195    }
196}
197
198/// Run a virtio device over PCI
199#[derive(InspectMut)]
200pub struct VirtioPciDevice {
201    #[inspect(flatten)]
202    core: VirtioTransportCore,
203    #[inspect(flatten)]
204    pci: PciTransport,
205}
206
207impl VirtioPciDevice {
208    pub fn new(
209        mut device: Box<dyn DynVirtioDevice>,
210        driver: &impl Spawn,
211        guest_memory: GuestMemory,
212        interrupt_model: PciInterruptModel<'_>,
213        doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
214        mmio_registration: &mut dyn RegisterMmioIntercept,
215        shared_mem_mapper: Option<&dyn MemoryMapper>,
216    ) -> io::Result<Self> {
217        let traits = device.traits();
218
219        let (base_class, sub_class) = virtio_class_code(traits.device_id);
220        let hardware_ids = HardwareIds {
221            vendor_id: VIRTIO_VENDOR_ID,
222            device_id: VIRTIO_PCI_DEVICE_ID_BASE + traits.device_id.0,
223            revision_id: 1,
224            prog_if: ProgrammingInterface::NONE,
225            base_class,
226            sub_class,
227            type0_sub_vendor_id: pci_core::microsoft::VENDOR_ID,
228            type0_sub_system_id: pci_core::microsoft::DEFAULT_SUBSYSTEM_ID,
229        };
230
231        let mut caps: Vec<Box<dyn PciCapability>> = vec![
232            Box::new(ReadOnlyCapability::new(
233                "virtio-common",
234                VirtioCapability::new(
235                    VirtioPciCapType::COMMON_CFG.0,
236                    0,
237                    0,
238                    0,
239                    VIRTIO_PCI_COMMON_CFG_SIZE as u32,
240                ),
241            )),
242            Box::new(ReadOnlyCapability::new(
243                "virtio-notify",
244                VirtioNotifyCapability::new(
245                    0,
246                    0,
247                    BAR0_NOTIFY_OFFSET as u32,
248                    BAR0_NOTIFY_SIZE as u32,
249                ),
250            )),
251            Box::new(ReadOnlyCapability::new(
252                "virtio-pci-isr",
253                VirtioCapability::new(
254                    VirtioPciCapType::ISR_CFG.0,
255                    0,
256                    0,
257                    BAR0_ISR_OFFSET as u32,
258                    BAR0_ISR_SIZE as u32,
259                ),
260            )),
261        ];
262
263        // Only advertise a device-specific config capability when the device
264        // actually has device-specific config registers. Per the virtio spec
265        // (v1.2 section 4.1.4.6), a VIRTIO_PCI_CAP_DEVICE_CFG capability is
266        // required only for device types that have a device-specific
267        // configuration; devices without one (e.g. the entropy device) must
268        // not advertise a zero-length capability.
269        if traits.device_register_length > 0 {
270            caps.push(Box::new(ReadOnlyCapability::new(
271                "virtio-pci-device",
272                VirtioCapability::new(
273                    VirtioPciCapType::DEVICE_CFG.0,
274                    0,
275                    0,
276                    BAR0_DEVICE_CFG_OFFSET as u32,
277                    traits.device_register_length,
278                ),
279            )));
280        }
281
282        let mut bars = DeviceBars::new().bar0(
283            BAR0_DEVICE_CFG_OFFSET as u64 + traits.device_register_length as u64,
284            BarMemoryKind::Intercept(mmio_registration.new_io_region(
285                "config",
286                BAR0_DEVICE_CFG_OFFSET as u64 + traits.device_register_length as u64,
287            )),
288        );
289
290        let msix: Option<MsixEmulator> = if let PciInterruptModel::Msix(msi_target) =
291            interrupt_model
292        {
293            let (msix, msix_capability) = MsixEmulator::new(2, 64, msi_target);
294            caps.insert(0, Box::new(msix_capability));
295            bars = bars.bar2(
296                msix.bar_len(),
297                BarMemoryKind::Intercept(mmio_registration.new_io_region("msix", msix.bar_len())),
298            );
299            Some(msix)
300        } else {
301            None
302        };
303
304        let shared_memory_size = traits.shared_memory.size;
305        if shared_memory_size > 0 {
306            let (control, region) = shared_mem_mapper
307                .expect("must provide mapper for shmem")
308                .new_region(
309                    shared_memory_size.try_into().expect("region too big"),
310                    "virtio-pci-shmem".into(),
311                )?;
312
313            caps.push(Box::new(ReadOnlyCapability::new(
314                "virtio-pci-shm",
315                VirtioCapability64::new(
316                    VirtioPciCapType::SHARED_MEMORY_CFG.0,
317                    4, // BAR 4
318                    traits.shared_memory.id,
319                    0,
320                    shared_memory_size,
321                ),
322            )));
323
324            bars = bars.bar4(shared_memory_size, BarMemoryKind::SharedMem(control));
325
326            device
327                .set_shared_memory_region(&region)
328                .map_err(io::Error::other)?;
329        }
330
331        // Add the VIRTIO_PCI_CAP_PCI_CFG capability last. It provides an
332        // alternative access path to the virtio BAR regions through PCI
333        // configuration space. The `pci_cfg_data` window is serviced by
334        // `pci_cfg_read`/`pci_cfg_write` below, using the shared window state.
335        let pci_cfg_access = Arc::new(Mutex::new(PciCfgAccessState::default()));
336        caps.push(Box::new(VirtioPciCfgCapability {
337            state: pci_cfg_access.clone(),
338        }));
339        // Capabilities are laid out consecutively starting at the end of the
340        // common PCI header in the order they appear in `caps`; the pci_cfg
341        // capability is last, so its offset is the header end plus the total
342        // length of all preceding capabilities.
343        let pci_cfg_cap_offset = COMMON_HEADER_END
344            + caps[..caps.len() - 1]
345                .iter()
346                .map(|c| c.len() as u16)
347                .sum::<u16>();
348        let pci_cfg_data_offset = pci_cfg_cap_offset + VIRTIO_PCI_CFG_DATA_OFFSET;
349
350        let mut config_space = ConfigSpaceType0Emulator::new(hardware_ids, caps, Vec::new(), bars);
351        let interrupt_kind = match interrupt_model {
352            PciInterruptModel::Msix(_) => InterruptKind::Msix(msix.unwrap()),
353            PciInterruptModel::IntX(pin, line) => {
354                InterruptKind::IntX(config_space.set_interrupt_pin(pin, line))
355            }
356        };
357
358        let core = VirtioTransportCore::new(device, driver, guest_memory, doorbell_registration)?;
359
360        Ok(VirtioPciDevice {
361            core,
362            pci: PciTransport {
363                config_space,
364                interrupt_kind,
365                interrupt_status: Arc::new(Mutex::new(0)),
366                msix_config_vector: 0,
367                shared_memory_size,
368                pci_cfg_access,
369                pci_cfg_data_offset,
370                device_register_length: traits.device_register_length,
371            },
372        })
373    }
374
375    /// Read a transport register as a u32.
376    fn read_u32_local(&mut self, offset: u16) -> u32 {
377        assert!(offset & 3 == 0);
378        let queue_select = self.core.queue_select as usize;
379        match VirtioPciCommonCfg(offset) {
380            VirtioPciCommonCfg::DEVICE_FEATURE_SELECT => self.core.device_feature_select,
381            VirtioPciCommonCfg::DEVICE_FEATURE => self
382                .core
383                .device_feature
384                .bank(self.core.device_feature_select as usize),
385            VirtioPciCommonCfg::DRIVER_FEATURE_SELECT => self.core.driver_feature_select,
386            VirtioPciCommonCfg::DRIVER_FEATURE => self
387                .core
388                .driver_feature
389                .bank(self.core.driver_feature_select as usize),
390            VirtioPciCommonCfg::MSIX_CONFIG => {
391                (self.core.queues.len() as u32) << 16 | self.pci.msix_config_vector as u32
392            }
393            VirtioPciCommonCfg::DEVICE_STATUS => {
394                self.core.queue_select << 16
395                    | self.core.config_generation << 8
396                    | self.core.device_status.as_u32()
397            }
398            VirtioPciCommonCfg::QUEUE_SIZE => {
399                let size = self
400                    .core
401                    .queues
402                    .get(queue_select)
403                    .map_or(0, |qd| qd.params.size);
404                let msix_vector = self
405                    .core
406                    .queues
407                    .get(queue_select)
408                    .map_or(0, |qd| qd.msix_vector);
409                (msix_vector as u32) << 16 | size as u32
410            }
411            VirtioPciCommonCfg::QUEUE_ENABLE => {
412                self.core
413                    .queues
414                    .get(queue_select)
415                    .is_some_and(|qd| qd.params.enable) as u32
416            }
417            VirtioPciCommonCfg::QUEUE_DESC_LO => self
418                .core
419                .queues
420                .get(queue_select)
421                .map_or(0, |qd| qd.params.desc_addr as u32),
422            VirtioPciCommonCfg::QUEUE_DESC_HI => self
423                .core
424                .queues
425                .get(queue_select)
426                .map_or(0, |qd| (qd.params.desc_addr >> 32) as u32),
427            VirtioPciCommonCfg::QUEUE_AVAIL_LO => self
428                .core
429                .queues
430                .get(queue_select)
431                .map_or(0, |qd| qd.params.avail_addr as u32),
432            VirtioPciCommonCfg::QUEUE_AVAIL_HI => self
433                .core
434                .queues
435                .get(queue_select)
436                .map_or(0, |qd| (qd.params.avail_addr >> 32) as u32),
437            VirtioPciCommonCfg::QUEUE_USED_LO => self
438                .core
439                .queues
440                .get(queue_select)
441                .map_or(0, |qd| qd.params.used_addr as u32),
442            VirtioPciCommonCfg::QUEUE_USED_HI => self
443                .core
444                .queues
445                .get(queue_select)
446                .map_or(0, |qd| (qd.params.used_addr >> 32) as u32),
447            VirtioPciCommonCfg(BAR0_NOTIFY_OFFSET) => 0,
448            VirtioPciCommonCfg(BAR0_ISR_OFFSET) => {
449                let mut interrupt_status = self.pci.interrupt_status.lock();
450                let status = *interrupt_status;
451                *interrupt_status = 0;
452                if let InterruptKind::IntX(line) = &self.pci.interrupt_kind {
453                    line.set_level(false)
454                }
455                status
456            }
457            _ => {
458                tracelimit::warn_ratelimited!(offset, "unknown bar read");
459                0xffffffff
460            }
461        }
462    }
463
464    /// Write a transport register as a u32.
465    fn write_u32_local(&mut self, offset: u16, val: u32) {
466        assert!(offset & 3 == 0);
467        let queues_locked = self.core.device_status.driver_ok();
468        let features_locked = queues_locked || self.core.device_status.features_ok();
469        let queue_select = self.core.queue_select as usize;
470        match VirtioPciCommonCfg(offset) {
471            VirtioPciCommonCfg::DEVICE_FEATURE_SELECT => self.core.device_feature_select = val,
472            VirtioPciCommonCfg::DRIVER_FEATURE_SELECT => self.core.driver_feature_select = val,
473            VirtioPciCommonCfg::DRIVER_FEATURE => {
474                let bank = self.core.driver_feature_select as usize;
475                if !features_locked && bank < 2 {
476                    self.core
477                        .driver_feature
478                        .set_bank(bank, val & self.core.device_feature.bank(bank));
479                }
480            }
481            VirtioPciCommonCfg::MSIX_CONFIG => self.pci.msix_config_vector = val as u16,
482            VirtioPciCommonCfg::DEVICE_STATUS => {
483                self.core.queue_select = val >> 16;
484                self.core.write_device_status(&mut self.pci, val as u8);
485            }
486            VirtioPciCommonCfg::QUEUE_SIZE => {
487                let msix_vector = (val >> 16) as u16;
488                if !queues_locked && queue_select < self.core.queues.len() {
489                    let val = val as u16;
490                    let qd = &mut self.core.queues[queue_select];
491                    if val > MAX_QUEUE_SIZE {
492                        qd.params.size = MAX_QUEUE_SIZE;
493                    } else {
494                        qd.params.size = val;
495                    }
496                    qd.msix_vector = msix_vector;
497                }
498            }
499            VirtioPciCommonCfg::QUEUE_ENABLE => {
500                let val = val & 0xffff;
501                if !queues_locked && queue_select < self.core.queues.len() {
502                    self.core.queues[queue_select].params.enable = val != 0;
503                }
504            }
505            VirtioPciCommonCfg::QUEUE_DESC_LO => {
506                if !queues_locked && queue_select < self.core.queues.len() {
507                    let queue = &mut self.core.queues[queue_select].params;
508                    queue.desc_addr = queue.desc_addr & 0xffffffff00000000 | val as u64;
509                }
510            }
511            VirtioPciCommonCfg::QUEUE_DESC_HI => {
512                if !queues_locked && queue_select < self.core.queues.len() {
513                    let queue = &mut self.core.queues[queue_select].params;
514                    queue.desc_addr = (val as u64) << 32 | queue.desc_addr & 0xffffffff;
515                }
516            }
517            VirtioPciCommonCfg::QUEUE_AVAIL_LO => {
518                if !queues_locked && queue_select < self.core.queues.len() {
519                    let queue = &mut self.core.queues[queue_select].params;
520                    queue.avail_addr = queue.avail_addr & 0xffffffff00000000 | val as u64;
521                }
522            }
523            VirtioPciCommonCfg::QUEUE_AVAIL_HI => {
524                if !queues_locked && queue_select < self.core.queues.len() {
525                    let queue = &mut self.core.queues[queue_select].params;
526                    queue.avail_addr = (val as u64) << 32 | queue.avail_addr & 0xffffffff;
527                }
528            }
529            VirtioPciCommonCfg::QUEUE_USED_LO => {
530                if !queues_locked && queue_select < self.core.queues.len() {
531                    let queue = &mut self.core.queues[queue_select].params;
532                    queue.used_addr = queue.used_addr & 0xffffffff00000000 | val as u64;
533                }
534            }
535            VirtioPciCommonCfg::QUEUE_USED_HI => {
536                if !queues_locked && queue_select < self.core.queues.len() {
537                    let queue = &mut self.core.queues[queue_select].params;
538                    queue.used_addr = (val as u64) << 32 | queue.used_addr & 0xffffffff;
539                }
540            }
541            VirtioPciCommonCfg(BAR0_NOTIFY_OFFSET) => {
542                self.core.notify_queue(val);
543            }
544            _ => {
545                tracelimit::warn_ratelimited!(offset, "unknown bar write at offset");
546            }
547        }
548    }
549
550    /// Read transport registers via sub-word chunk handling.
551    fn read_transport(&mut self, offset: u16, data: &mut [u8]) {
552        read_as_u32_chunks(offset, data, |offset| self.read_u32_local(offset));
553    }
554
555    /// Write transport registers via sub-word chunk handling.
556    fn write_transport(&mut self, offset: u16, data: &[u8]) {
557        write_as_u32_chunks(offset, data, |offset, request_type| match request_type {
558            ReadWriteRequestType::Write(value) => {
559                self.write_u32_local(offset, value);
560                None
561            }
562            ReadWriteRequestType::Read => Some(self.read_u32_local(offset)),
563        });
564    }
565
566    /// Validate a guest-programmed `pci_cfg_data` access against the selected
567    /// BAR, returning the offset as a `u16`, or `None` if `bar`, `offset`, and
568    /// `length` do not address `length` bytes within a reachable BAR.
569    fn pci_cfg_bar_offset(&self, bar: u8, offset: u32, length: u32) -> Option<u16> {
570        let bar_len = match bar {
571            0 => u32::from(BAR0_DEVICE_CFG_OFFSET) + self.pci.device_register_length,
572            2 => match &self.pci.interrupt_kind {
573                InterruptKind::Msix(msix) => msix.bar_len() as u32,
574                _ => return None,
575            },
576            _ => return None,
577        };
578        // The access must fit entirely within the BAR, and the offset must be
579        // addressable as a `u16` (all reachable regions live within the first
580        // 64 KiB of their BARs).
581        if offset.checked_add(length)? > bar_len {
582            return None;
583        }
584        u16::try_from(offset).ok()
585    }
586
587    /// Service a read of the `VIRTIO_PCI_CAP_PCI_CFG` `pci_cfg_data` window.
588    ///
589    /// Per the virtio spec (4.1.4.9.1) the accessed bytes are stored in the
590    /// *first* `cap.length` bytes of `pci_cfg_data`, regardless of how
591    /// `cap.offset` is aligned within its dword. The device-config region is
592    /// owned by the async device task and is therefore deferred; that path
593    /// reads the dword containing the access, so the completed size always
594    /// matches the 4-byte buffer the PCI config bus polls deferred reads with.
595    fn read_pci_cfg_data(&mut self, value: &mut u32) -> IoResult {
596        let (bar, offset, length) = {
597            let state = self.pci.pci_cfg_access.lock();
598            (state.bar, state.offset, state.length)
599        };
600        // Per the virtio spec, length MUST be 1, 2, or 4 and offset MUST be a
601        // multiple of length. Reject anything else without panicking.
602        if !matches!(length, 1 | 2 | 4) {
603            return IoResult::Err(IoError::InvalidAccessSize);
604        }
605        if !offset.is_multiple_of(length) {
606            return IoResult::Err(IoError::UnalignedAccess);
607        }
608        // Reject accesses that do not address `length` bytes within the
609        // selected BAR.
610        let Some(offset) = self.pci_cfg_bar_offset(bar, offset, length) else {
611            return IoResult::Err(IoError::InvalidRegister);
612        };
613        let len = length as usize;
614        if bar == 0 && offset >= BAR0_DEVICE_CFG_OFFSET {
615            // The device-config region is owned by the async device task, so
616            // the access is deferred. The PCI config bus polls the deferred
617            // read with a 4-byte dword buffer, so the completion must be a full
618            // dword with the accessed `cap.length` bytes left-aligned into the
619            // low bytes of `pci_cfg_data`, as required by the spec.
620            let dev_offset = offset - BAR0_DEVICE_CFG_OFFSET;
621            return defer_config_read(
622                &self.core.device_sender,
623                dev_offset,
624                len as u8,
625                ConfigReadCompletion::LeftAlignedDword,
626            );
627        }
628        // Store the accessed bytes in the first `cap.length` bytes of
629        // `pci_cfg_data`, as required by the spec.
630        let mut buf = [0u8; 4];
631        let result = self.read_pci_cfg_bar(bar, offset, &mut buf[..len]);
632        if let IoResult::Ok = result {
633            *value = u32::from_le_bytes(buf);
634        }
635        result
636    }
637
638    /// Service a write of the `VIRTIO_PCI_CAP_PCI_CFG` `pci_cfg_data` window.
639    fn write_pci_cfg_data(&mut self, value: u32) -> IoResult {
640        let (bar, offset, length) = {
641            let state = self.pci.pci_cfg_access.lock();
642            (state.bar, state.offset, state.length)
643        };
644        if !matches!(length, 1 | 2 | 4) {
645            return IoResult::Err(IoError::InvalidAccessSize);
646        }
647        if !offset.is_multiple_of(length) {
648            return IoResult::Err(IoError::UnalignedAccess);
649        }
650        // Reject accesses that do not address `length` bytes within the
651        // selected BAR (see `read_pci_cfg_data`).
652        let Some(offset) = self.pci_cfg_bar_offset(bar, offset, length) else {
653            return IoResult::Err(IoError::InvalidRegister);
654        };
655        let len = length as usize;
656        // Take the value from the first `cap.length` bytes of `pci_cfg_data`,
657        // as required by the spec.
658        let bytes = value.to_le_bytes();
659        let data = &bytes[..len];
660        if bar == 0 && offset >= BAR0_DEVICE_CFG_OFFSET {
661            return defer_config_write(
662                &self.core.device_sender,
663                offset - BAR0_DEVICE_CFG_OFFSET,
664                data,
665            );
666        }
667        self.write_pci_cfg_bar(bar, offset, data)
668    }
669
670    /// Read `data.len()` bytes from a synchronous BAR region (BAR0 transport or
671    /// BAR2 MSI-X) on behalf of a `pci_cfg_data` access. The device-config
672    /// region must be handled separately via deferral by the caller.
673    fn read_pci_cfg_bar(&mut self, bar: u8, offset: u16, data: &mut [u8]) -> IoResult {
674        match bar {
675            0 => self.read_transport(offset, data),
676            2 => read_as_u32_chunks(offset, data, |offset| {
677                if let InterruptKind::Msix(msix) = &self.pci.interrupt_kind {
678                    msix.read_u32(offset as u64)
679                } else {
680                    !0
681                }
682            }),
683            _ => return IoResult::Err(IoError::InvalidRegister),
684        }
685        IoResult::Ok
686    }
687
688    /// Write `data` to a synchronous BAR region (BAR0 transport or BAR2 MSI-X)
689    /// on behalf of a `pci_cfg_data` access. The device-config region must be
690    /// handled separately via deferral by the caller.
691    fn write_pci_cfg_bar(&mut self, bar: u8, offset: u16, data: &[u8]) -> IoResult {
692        match bar {
693            0 => self.write_transport(offset, data),
694            2 => {
695                write_as_u32_chunks(offset, data, |offset, request_type| match request_type {
696                    ReadWriteRequestType::Write(value) => {
697                        if let InterruptKind::Msix(msix) = &mut self.pci.interrupt_kind {
698                            msix.write_u32(offset as u64, value)
699                        }
700                        None
701                    }
702                    ReadWriteRequestType::Read => {
703                        if let InterruptKind::Msix(msix) = &self.pci.interrupt_kind {
704                            Some(msix.read_u32(offset as u64))
705                        } else {
706                            Some(!0)
707                        }
708                    }
709                });
710            }
711            _ => return IoResult::Err(IoError::InvalidRegister),
712        }
713        IoResult::Ok
714    }
715
716    /// Replay MMIO accesses that were stalled while the transport was busy.
717    fn replay_stalled_io(&mut self) {
718        let stalled = std::mem::take(&mut self.core.stalled_io);
719        let mut iter = stalled.into_iter();
720        for io in &mut iter {
721            match io {
722                StalledIo::Read {
723                    address,
724                    len,
725                    deferred,
726                } => {
727                    if let Some((_, offset)) = self.pci.config_space.find_bar(address) {
728                        let mut buf = vec![0u8; len];
729                        self.read_transport(offset as u16, &mut buf);
730                        deferred.complete(&buf);
731                    } else {
732                        // BAR was remapped via PCI config write while
733                        // the IO was stalled.
734                        deferred.complete_error(IoError::InvalidRegister);
735                    }
736                }
737                StalledIo::Write {
738                    address,
739                    data,
740                    len,
741                    deferred,
742                } => {
743                    if let Some((_, offset)) = self.pci.config_space.find_bar(address) {
744                        self.write_transport(offset as u16, &data[..len]);
745                        if self.core.state.is_busy() {
746                            self.core.pending_status_deferred = Some(deferred);
747                            break;
748                        }
749                        deferred.complete();
750                    } else {
751                        deferred.complete_error(IoError::InvalidRegister);
752                    }
753                }
754            }
755        }
756        self.core.stalled_io = iter.collect();
757    }
758
759    #[cfg(test)]
760    pub(crate) fn read_u32(&mut self, offset: u16) -> u32 {
761        self.read_u32_local(offset)
762    }
763
764    #[cfg(test)]
765    pub(crate) fn write_u32(&mut self, offset: u16, val: u32) {
766        self.write_u32_local(offset, val);
767    }
768}
769
770impl ChangeDeviceState for VirtioPciDevice {
771    fn start(&mut self) {
772        self.core.start(&mut self.pci);
773    }
774
775    async fn stop(&mut self) {
776        self.core.stop(&mut self.pci).await;
777    }
778
779    async fn reset(&mut self) {
780        self.core.reset(&mut self.pci).await;
781        self.pci.config_space.reset();
782    }
783}
784
785impl PollDevice for VirtioPciDevice {
786    fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
787        self.core.poll_device(&mut self.pci, cx);
788        // Replay any stalled IO after the state machine advances.
789        if !self.core.stalled_io.is_empty() && !self.core.state.is_busy() {
790            self.replay_stalled_io();
791        }
792    }
793}
794
795impl ChipsetDevice for VirtioPciDevice {
796    fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
797        Some(self)
798    }
799
800    fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
801        Some(self)
802    }
803
804    fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
805        Some(self)
806    }
807}
808
809mod saved_state {
810    mod state {
811        use crate::transport::saved_state::state::CommonQueueState;
812        use crate::transport::saved_state::state::CommonSavedState;
813        use mesh::payload::Protobuf;
814        use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
815        use vmcore::save_restore::SaveRestore;
816        use vmcore::save_restore::SavedStateRoot;
817
818        #[derive(Protobuf)]
819        #[mesh(package = "virtio.transport.pci")]
820        pub struct SavedQueueState {
821            #[mesh(1)]
822            pub common: CommonQueueState,
823            #[mesh(2)]
824            pub msix_vector: u16,
825        }
826
827        #[derive(Protobuf, SavedStateRoot)]
828        #[mesh(package = "virtio.transport.pci")]
829        pub struct SavedState {
830            #[mesh(1)]
831            pub common: CommonSavedState,
832            #[mesh(2)]
833            pub msix_config_vector: u16,
834            #[mesh(3)]
835            pub queues: Vec<SavedQueueState>,
836            #[mesh(4)]
837            pub interrupt_status: u32,
838            /// PCI configuration space, including the BAR base addresses. This
839            /// must be preserved so that BARs pre-assigned by the host (before
840            /// the guest firmware runs) survive a save/restore cycle.
841            #[mesh(5)]
842            pub cfg_space: <ConfigSpaceType0Emulator as SaveRestore>::SavedState,
843        }
844
845        #[derive(Protobuf, SavedStateRoot)]
846        #[mesh(package = "virtio.transport.pci")]
847        pub struct CfgCapSavedState {
848            #[mesh(1)]
849            pub bar: u8,
850            #[mesh(2)]
851            pub offset: u32,
852            #[mesh(3)]
853            pub length: u32,
854        }
855    }
856
857    use super::*;
858    use vmcore::save_restore::SaveRestore;
859
860    impl SaveRestore for VirtioPciDevice {
861        type SavedState = state::SavedState;
862
863        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
864            Ok(state::SavedState {
865                common: self.core.save_common()?,
866                msix_config_vector: self.pci.msix_config_vector,
867                queues: self
868                    .core
869                    .queues
870                    .iter()
871                    .enumerate()
872                    .map(|(i, qd)| state::SavedQueueState {
873                        common: self.core.save_queue_common(i),
874                        msix_vector: qd.msix_vector,
875                    })
876                    .collect(),
877                interrupt_status: *self.pci.interrupt_status.lock(),
878                cfg_space: self.pci.config_space.save()?,
879            })
880        }
881
882        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
883            let saved_queue_count = state.queues.len();
884
885            // Restore the PCI config space (BARs, command register, etc.) so
886            // that host pre-assigned BARs are preserved across the cycle.
887            //
888            // This has to come before `restore_common`, which reinstalls the
889            // queue doorbells: `doorbell_region` reads `bar_address(0)`, and a
890            // BAR address only becomes active once the config space brings it
891            // back. If restored the other way round, `doorbell_region` answers
892            // `None` at that moment and no doorbell is installed. The only other
893            // place they go in is the guest writing DRIVER_OK, which a guest
894            // resumed mid-flight never does again, so the omission lasts for the
895            // life of the VM and every queue kick takes an MMIO exit instead of
896            // the registered doorbell. Nothing in `restore_common` reads the
897            // interrupt state restored below.
898            self.pci.config_space.restore(state.cfg_space)?;
899
900            self.core.restore_common(
901                &mut self.pci,
902                &state.common,
903                state
904                    .queues
905                    .into_iter()
906                    .map(|sq| (sq.common, sq.msix_vector)),
907                saved_queue_count,
908            )?;
909
910            // Restore PCI-specific interrupt state.
911            *self.pci.interrupt_status.lock() = state.interrupt_status;
912            if let InterruptKind::IntX(line) = &self.pci.interrupt_kind {
913                line.set_level(state.interrupt_status != 0);
914            }
915            self.pci.msix_config_vector = state.msix_config_vector;
916
917            Ok(())
918        }
919    }
920
921    impl SaveRestore for VirtioPciCfgCapability {
922        type SavedState = state::CfgCapSavedState;
923
924        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
925            let state = self.state.lock();
926            Ok(state::CfgCapSavedState {
927                bar: state.bar,
928                offset: state.offset,
929                length: state.length,
930            })
931        }
932
933        fn restore(&mut self, saved_state: Self::SavedState) -> Result<(), RestoreError> {
934            let state::CfgCapSavedState {
935                bar,
936                offset,
937                length,
938            } = saved_state;
939            let mut state = self.state.lock();
940            state.bar = bar;
941            state.offset = offset;
942            state.length = length;
943            Ok(())
944        }
945    }
946}
947
948impl MmioIntercept for VirtioPciDevice {
949    fn mmio_read(&mut self, address: u64, data: &mut [u8]) -> IoResult {
950        let Some((bar, offset)) = self.pci.config_space.find_bar(address) else {
951            return IoResult::Err(IoError::InvalidRegister);
952        };
953        let offset = offset as u16;
954        if bar == 0 && offset >= BAR0_DEVICE_CFG_OFFSET {
955            return defer_config_read(
956                &self.core.device_sender,
957                offset - BAR0_DEVICE_CFG_OFFSET,
958                data.len() as u8,
959                ConfigReadCompletion::Exact,
960            );
961        }
962        if bar == 0 && self.core.state.is_busy() {
963            let (deferred, token) = defer_read();
964            self.core.stalled_io.push(StalledIo::Read {
965                address,
966                len: data.len(),
967                deferred,
968            });
969            return IoResult::Defer(token);
970        }
971        match bar {
972            0 => self.read_transport(offset, data),
973            2 => read_as_u32_chunks(offset, data, |offset| {
974                if let InterruptKind::Msix(msix) = &self.pci.interrupt_kind {
975                    msix.read_u32(offset as u64)
976                } else {
977                    !0
978                }
979            }),
980            _ => return IoResult::Err(IoError::InvalidRegister),
981        }
982        IoResult::Ok
983    }
984
985    fn mmio_write(&mut self, address: u64, data: &[u8]) -> IoResult {
986        let Some((bar, offset)) = self.pci.config_space.find_bar(address) else {
987            return IoResult::Err(IoError::InvalidRegister);
988        };
989        let offset = offset as u16;
990        if bar == 0 && offset >= BAR0_DEVICE_CFG_OFFSET {
991            return defer_config_write(
992                &self.core.device_sender,
993                offset - BAR0_DEVICE_CFG_OFFSET,
994                data,
995            );
996        }
997        if bar == 0 && self.core.state.is_busy() {
998            let (deferred, token) = defer_write();
999            let mut buf = [0u8; 8];
1000            buf[..data.len()].copy_from_slice(data);
1001            self.core.stalled_io.push(StalledIo::Write {
1002                address,
1003                data: buf,
1004                len: data.len(),
1005                deferred,
1006            });
1007            return IoResult::Defer(token);
1008        }
1009        match bar {
1010            0 => self.write_transport(offset, data),
1011            2 => {
1012                write_as_u32_chunks(offset, data, |offset, request_type| match request_type {
1013                    ReadWriteRequestType::Write(value) => {
1014                        if let InterruptKind::Msix(msix) = &mut self.pci.interrupt_kind {
1015                            msix.write_u32(offset as u64, value)
1016                        }
1017                        None
1018                    }
1019                    ReadWriteRequestType::Read => {
1020                        if let InterruptKind::Msix(msix) = &self.pci.interrupt_kind {
1021                            Some(msix.read_u32(offset as u64))
1022                        } else {
1023                            Some(!0)
1024                        }
1025                    }
1026                });
1027            }
1028            _ => return IoResult::Err(IoError::InvalidRegister),
1029        }
1030        if bar == 0 && self.core.state.is_busy() {
1031            let (deferred, token) = defer_write();
1032            self.core.pending_status_deferred = Some(deferred);
1033            return IoResult::Defer(token);
1034        }
1035        IoResult::Ok
1036    }
1037}
1038
1039impl PciConfigSpace for VirtioPciDevice {
1040    fn pci_cfg_read(&mut self, offset: u16, mut value: ByteEnabledDwordRead<'_>) -> IoResult {
1041        if offset == self.pci.pci_cfg_data_offset {
1042            let mut dword = 0;
1043            let result = self.read_pci_cfg_data(&mut dword);
1044            if let IoResult::Ok = result {
1045                value.set(dword);
1046            }
1047            return result;
1048        }
1049        self.pci.config_space.read_byte_enabled(offset, value)
1050    }
1051
1052    fn pci_cfg_write(&mut self, offset: u16, value: ByteEnabledDwordWrite) -> IoResult {
1053        if offset == self.pci.pci_cfg_data_offset {
1054            return self.write_pci_cfg_data(value.merge(0));
1055        }
1056        self.pci.config_space.write_byte_enabled(offset, value)
1057    }
1058}
1059
1060/// Length of the `virtio_pci_cfg_cap` structure: the 16-byte `virtio_pci_cap`
1061/// header plus the trailing 4-byte `pci_cfg_data` window.
1062const VIRTIO_PCI_CFG_CAP_LEN: u8 = 20;
1063/// Byte offset of the `pci_cfg_data` window within `virtio_pci_cfg_cap`.
1064const VIRTIO_PCI_CFG_DATA_OFFSET: u16 = 16;
1065
1066/// Shared window state for the `VIRTIO_PCI_CAP_PCI_CFG` capability.
1067///
1068/// The driver programs `bar`, `offset`, and `length` by writing the
1069/// capability's config-space fields, then reads or writes the `pci_cfg_data`
1070/// window to perform an access into the selected BAR region. Both the
1071/// capability (which owns the field writes) and [`VirtioPciDevice`] (which
1072/// services the `pci_cfg_data` window) hold a clone of this state.
1073#[derive(Debug, Default, Inspect)]
1074struct PciCfgAccessState {
1075    bar: u8,
1076    #[inspect(hex)]
1077    offset: u32,
1078    #[inspect(hex)]
1079    length: u32,
1080}
1081
1082/// The `VIRTIO_PCI_CAP_PCI_CFG` capability (`virtio_pci_cfg_cap`).
1083///
1084/// Provides an alternative access path to the virtio BAR regions purely
1085/// through PCI configuration space, for drivers or firmware that cannot map
1086/// the device's memory BARs. See the virtio 1.x spec, "PCI configuration
1087/// access capability". The `pci_cfg_data` window itself is serviced by
1088/// [`VirtioPciDevice`], which has access to the BAR regions; this capability
1089/// only tracks the programmed `bar`/`offset`/`length` fields.
1090struct VirtioPciCfgCapability {
1091    state: Arc<Mutex<PciCfgAccessState>>,
1092}
1093
1094impl Inspect for VirtioPciCfgCapability {
1095    fn inspect(&self, req: inspect::Request<'_>) {
1096        let state = self.state.lock();
1097        req.respond()
1098            .field("label", "virtio-pci-cfg")
1099            .field("bar", state.bar)
1100            .hex("offset", state.offset)
1101            .hex("length", state.length);
1102    }
1103}
1104
1105impl PciCapability for VirtioPciCfgCapability {
1106    fn label(&self) -> &str {
1107        "virtio-pci-cfg"
1108    }
1109
1110    fn capability_id(&self) -> CapabilityId {
1111        CapabilityId::VENDOR_SPECIFIC
1112    }
1113
1114    fn len(&self) -> usize {
1115        VIRTIO_PCI_CFG_CAP_LEN as usize
1116    }
1117
1118    fn read(&self, offset: u16, mut value: ByteEnabledDwordRead<'_>) {
1119        let state = self.state.lock();
1120        let dword = match offset {
1121            // cap_vndr | cap_next (filled in by the config space emulator) |
1122            // cap_len | cfg_type
1123            0 => {
1124                CapabilityId::VENDOR_SPECIFIC.0 as u32
1125                    | (VIRTIO_PCI_CFG_CAP_LEN as u32) << 16
1126                    | (VirtioPciCapType::PCI_CFG.0 as u32) << 24
1127            }
1128            // bar | id | padding. The id field is read-only and unused for
1129            // this capability, so it always reads as zero.
1130            4 => state.bar as u32,
1131            8 => state.offset,
1132            12 => state.length,
1133            // pci_cfg_data is serviced by VirtioPciDevice::pci_cfg_read.
1134            _ => 0,
1135        };
1136        value.set(dword);
1137    }
1138
1139    fn write(&mut self, offset: u16, val: ByteEnabledDwordWrite) {
1140        let mut state = self.state.lock();
1141        match offset {
1142            // The header dword (cap_vndr/cap_next/cap_len/cfg_type) is
1143            // read-only.
1144            0 => {}
1145            // Only bar is writable here; the id byte is read-only and unused
1146            // for this capability.
1147            4 => state.bar = val.merge(state.bar.into()) as u8,
1148            8 => state.offset = val.merge(state.offset),
1149            12 => state.length = val.merge(state.length),
1150            // pci_cfg_data is serviced by VirtioPciDevice::pci_cfg_write.
1151            _ => {}
1152        }
1153    }
1154
1155    fn reset(&mut self) {
1156        *self.state.lock() = PciCfgAccessState::default();
1157    }
1158}
1159
1160pub(crate) mod capabilities {
1161    use crate::spec::pci::VirtioPciCapType;
1162    use pci_core::spec::caps::CapabilityId;
1163
1164    use zerocopy::Immutable;
1165    use zerocopy::IntoBytes;
1166    use zerocopy::KnownLayout;
1167
1168    #[repr(C)]
1169    #[derive(Debug, IntoBytes, Immutable, KnownLayout)]
1170    pub struct VirtioCapabilityCommon {
1171        cap_id: u8,
1172        cap_next: u8,
1173        len: u8,
1174        typ: u8,
1175        bar: u8,
1176        unique_id: u8,
1177        padding: [u8; 2],
1178        offset: u32,
1179        length: u32,
1180    }
1181
1182    impl VirtioCapabilityCommon {
1183        pub fn new(len: u8, typ: u8, bar: u8, unique_id: u8, addr_off: u32, addr_len: u32) -> Self {
1184            Self {
1185                cap_id: CapabilityId::VENDOR_SPECIFIC.0,
1186                cap_next: 0,
1187                len,
1188                typ,
1189                bar,
1190                unique_id,
1191                padding: [0; 2],
1192                offset: addr_off,
1193                length: addr_len,
1194            }
1195        }
1196    }
1197
1198    #[repr(C)]
1199    #[derive(Debug, IntoBytes, Immutable, KnownLayout)]
1200    pub struct VirtioCapability {
1201        common: VirtioCapabilityCommon,
1202    }
1203
1204    impl VirtioCapability {
1205        pub fn new(typ: u8, bar: u8, unique_id: u8, addr_off: u32, addr_len: u32) -> Self {
1206            Self {
1207                common: VirtioCapabilityCommon::new(
1208                    size_of::<Self>() as u8,
1209                    typ,
1210                    bar,
1211                    unique_id,
1212                    addr_off,
1213                    addr_len,
1214                ),
1215            }
1216        }
1217    }
1218
1219    #[repr(C)]
1220    #[derive(Debug, IntoBytes, Immutable, KnownLayout)]
1221    pub struct VirtioCapability64 {
1222        common: VirtioCapabilityCommon,
1223        offset_hi: u32,
1224        length_hi: u32,
1225    }
1226
1227    impl VirtioCapability64 {
1228        pub fn new(typ: u8, bar: u8, unique_id: u8, addr_off: u64, addr_len: u64) -> Self {
1229            Self {
1230                common: VirtioCapabilityCommon::new(
1231                    size_of::<Self>() as u8,
1232                    typ,
1233                    bar,
1234                    unique_id,
1235                    addr_off as u32,
1236                    addr_len as u32,
1237                ),
1238                offset_hi: (addr_off >> 32) as u32,
1239                length_hi: (addr_len >> 32) as u32,
1240            }
1241        }
1242    }
1243
1244    #[repr(C)]
1245    #[derive(Debug, IntoBytes, Immutable, KnownLayout)]
1246    pub struct VirtioNotifyCapability {
1247        common: VirtioCapabilityCommon,
1248        offset_multiplier: u32,
1249    }
1250
1251    impl VirtioNotifyCapability {
1252        pub fn new(offset_multiplier: u32, bar: u8, addr_off: u32, addr_len: u32) -> Self {
1253            Self {
1254                common: VirtioCapabilityCommon::new(
1255                    size_of::<Self>() as u8,
1256                    VirtioPciCapType::NOTIFY_CFG.0,
1257                    bar,
1258                    0,
1259                    addr_off,
1260                    addr_len,
1261                ),
1262                offset_multiplier,
1263            }
1264        }
1265    }
1266
1267    #[cfg(test)]
1268    mod tests {
1269        use super::*;
1270        use pci_core::capabilities::ReadOnlyCapability;
1271        use pci_core::test_helpers::read_cap_u32;
1272
1273        #[test]
1274        fn common_check() {
1275            let common =
1276                ReadOnlyCapability::new("common", VirtioCapability::new(0x13, 2, 0, 0x100, 0x200));
1277            assert_eq!(read_cap_u32(&common, 0), 0x13100009);
1278            assert_eq!(read_cap_u32(&common, 4), 2);
1279            assert_eq!(read_cap_u32(&common, 8), 0x100);
1280            assert_eq!(read_cap_u32(&common, 12), 0x200);
1281        }
1282
1283        #[test]
1284        fn notify_check() {
1285            let notify = ReadOnlyCapability::new(
1286                "notify",
1287                VirtioNotifyCapability::new(0x123, 2, 0x100, 0x200),
1288            );
1289            assert_eq!(read_cap_u32(&notify, 0), 0x2140009);
1290            assert_eq!(read_cap_u32(&notify, 4), 2);
1291            assert_eq!(read_cap_u32(&notify, 8), 0x100);
1292            assert_eq!(read_cap_u32(&notify, 12), 0x200);
1293        }
1294    }
1295}