Skip to main content

pcie/
its.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! GICv3 ITS interrupt wrappers for PCIe.
5//!
6//! The ITS routes MSIs using a 32-bit device ID. For PCIe, this is
7//! `(segment << 16) | bdf`. The BDF is resolved by `MsiTarget` from
8//! the port's bus range (for single-function devices) or passed
9//! explicitly by multi-function devices and root ports.
10//!
11//! The wrappers here handle only the segment-to-ITS-devid mapping:
12//! prepending the PCI segment number to the BDF. One instance per
13//! root complex or switch connection.
14
15use pal_event::Event;
16use pci_core::msi::SignalMsi;
17use std::sync::Arc;
18use vmcore::irqfd::IrqFd;
19use vmcore::irqfd::IrqFdRoute;
20
21/// A [`SignalMsi`] wrapper that prepends the PCI segment to the BDF,
22/// producing the ITS device ID.
23///
24/// When `devid` is `Some(bdf)`, the ITS device ID is
25/// `(segment << 16) | (bdf & 0xFFFF)`.
26///
27/// In the normal flow this wrapper is invoked via
28/// [`MsiTarget::signal_msi`](pci_core::msi::MsiTarget::signal_msi) or
29/// [`MsiTarget::signal_msi_with_rid`](pci_core::msi::MsiTarget::signal_msi_with_rid),
30/// both of which always pass `Some(bdf)`, so the `None` arm is
31/// unreachable. It is retained as a defensive guard for direct trait
32/// callers that have no BDF to provide.
33pub struct ItsSignalMsi {
34    inner: Arc<dyn SignalMsi>,
35    segment: u16,
36}
37
38impl ItsSignalMsi {
39    /// Creates a new wrapper for the given segment.
40    pub fn new(inner: Arc<dyn SignalMsi>, segment: u16) -> Self {
41        Self { inner, segment }
42    }
43}
44
45impl SignalMsi for ItsSignalMsi {
46    fn signal_msi(&self, devid: Option<u32>, address: u64, data: u32) {
47        let Some(bdf) = devid else {
48            return;
49        };
50        let its_devid = (self.segment as u32) << 16 | (bdf & 0xFFFF);
51        self.inner.signal_msi(Some(its_devid), address, data);
52    }
53}
54
55/// An [`IrqFd`] wrapper that produces ITS-aware irqfd routes.
56pub struct ItsIrqFd {
57    inner: Arc<dyn IrqFd>,
58    segment: u16,
59}
60
61impl ItsIrqFd {
62    /// Creates a new wrapper for the given segment.
63    pub fn new(inner: Arc<dyn IrqFd>, segment: u16) -> Self {
64        Self { inner, segment }
65    }
66}
67
68impl IrqFd for ItsIrqFd {
69    fn new_irqfd_route(&self) -> anyhow::Result<Box<dyn IrqFdRoute>> {
70        let inner_route = self.inner.new_irqfd_route()?;
71        Ok(Box::new(ItsIrqFdRoute {
72            inner: inner_route,
73            segment: self.segment,
74        }))
75    }
76}
77
78/// An [`IrqFdRoute`] wrapper that prepends the PCI segment to the BDF
79/// on `enable`.
80struct ItsIrqFdRoute {
81    inner: Box<dyn IrqFdRoute>,
82    segment: u16,
83}
84
85impl IrqFdRoute for ItsIrqFdRoute {
86    fn event(&self) -> &Event {
87        self.inner.event()
88    }
89
90    fn enable(&self, address: u64, data: u32, devid: Option<u32>) {
91        let Some(bdf) = devid else {
92            return;
93        };
94        let its_devid = (self.segment as u32) << 16 | (bdf & 0xFFFF);
95        self.inner.enable(address, data, Some(its_devid));
96    }
97
98    fn disable(&self) {
99        self.inner.disable();
100    }
101}