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}