pci_core/
msi.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Traits for working with MSI interrupts.
5
6use inspect::Inspect;
7use parking_lot::RwLock;
8use std::sync::Arc;
9
10/// An object that can signal MSI interrupts.
11pub trait SignalMsi: Send + Sync {
12    /// Signals a message-signaled interrupt at the specified address with the specified data.
13    ///
14    /// `rid` is the requester ID of the PCI device sending the interrupt.
15    fn signal_msi(&self, rid: u32, address: u64, data: u32);
16}
17
18struct DisconnectedMsiTarget;
19
20impl SignalMsi for DisconnectedMsiTarget {
21    fn signal_msi(&self, _rid: u32, _address: u64, _data: u32) {
22        tracelimit::warn_ratelimited!("dropped MSI interrupt to disconnected target");
23    }
24}
25
26/// A connection between a device and an MSI target.
27#[derive(Debug)]
28pub struct MsiConnection {
29    target: MsiTarget,
30}
31
32/// An MSI target that can be used to signal MSI interrupts.
33#[derive(Inspect, Debug, Clone)]
34#[inspect(skip)]
35pub struct MsiTarget {
36    inner: Arc<RwLock<MsiTargetInner>>,
37}
38
39struct MsiTargetInner {
40    signal_msi: Arc<dyn SignalMsi>,
41}
42
43impl std::fmt::Debug for MsiTargetInner {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        let Self { signal_msi: _ } = self;
46        f.debug_struct("MsiTargetInner").finish()
47    }
48}
49
50impl MsiConnection {
51    /// Creates a new disconnected MSI target connection.
52    pub fn new() -> Self {
53        Self {
54            target: MsiTarget {
55                inner: Arc::new(RwLock::new(MsiTargetInner {
56                    signal_msi: Arc::new(DisconnectedMsiTarget),
57                })),
58            },
59        }
60    }
61
62    /// Updates the MSI target to which this connection signals interrupts.
63    pub fn connect(&self, signal_msi: Arc<dyn SignalMsi>) {
64        let mut inner = self.target.inner.write();
65        inner.signal_msi = signal_msi;
66    }
67
68    /// Returns the MSI target for this connection.
69    pub fn target(&self) -> &MsiTarget {
70        &self.target
71    }
72}
73
74impl MsiTarget {
75    /// Signals an MSI interrupt to this target from the specified RID.
76    ///
77    /// A single-RID device should use `0` as the RID.
78    pub fn signal_msi(&self, rid: u32, address: u64, data: u32) {
79        let inner = self.inner.read();
80        inner.signal_msi.signal_msi(rid, address, data);
81    }
82}