pci_types/capability/
msi.rs

1use crate::{capability::PciCapabilityAddress, ConfigRegionAccess};
2use bit_field::BitField;
3use core::convert::TryFrom;
4
5/// Specifies how many MSI interrupts one device can have.
6/// Device will modify lower bits of interrupt vector to send multiple messages, so interrupt block
7/// must be aligned accordingly.
8#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
9pub enum MultipleMessageSupport {
10    /// Device can send 1 interrupt. No interrupt vector modification is happening here
11    Int1 = 0b000,
12    /// Device can send 2 interrupts
13    Int2 = 0b001,
14    /// Device can send 4 interrupts
15    Int4 = 0b010,
16    /// Device can send 8 interrupts
17    Int8 = 0b011,
18    /// Device can send 16 interrupts
19    Int16 = 0b100,
20    /// Device can send 32 interrupts
21    Int32 = 0b101,
22}
23
24impl TryFrom<u8> for MultipleMessageSupport {
25    type Error = ();
26
27    fn try_from(value: u8) -> Result<Self, Self::Error> {
28        match value {
29            0b000 => Ok(MultipleMessageSupport::Int1),
30            0b001 => Ok(MultipleMessageSupport::Int2),
31            0b010 => Ok(MultipleMessageSupport::Int4),
32            0b011 => Ok(MultipleMessageSupport::Int8),
33            0b100 => Ok(MultipleMessageSupport::Int16),
34            0b101 => Ok(MultipleMessageSupport::Int32),
35            _ => Err(()),
36        }
37    }
38}
39
40/// When device should trigger the interrupt
41#[derive(Debug)]
42pub enum TriggerMode {
43    Edge = 0b00,
44    LevelAssert = 0b11,
45    LevelDeassert = 0b10,
46}
47
48#[derive(Debug, Clone, Copy)]
49pub struct MsiCapability {
50    pub(super) address: PciCapabilityAddress,
51    per_vector_masking: bool,
52    is_64bit: bool,
53    multiple_message_capable: MultipleMessageSupport,
54}
55
56impl MsiCapability {
57    pub(crate) fn new(address: PciCapabilityAddress, control: u16) -> MsiCapability {
58        MsiCapability {
59            address,
60            per_vector_masking: control.get_bit(8),
61            is_64bit: control.get_bit(7),
62            multiple_message_capable: MultipleMessageSupport::try_from(control.get_bits(1..4) as u8)
63                .unwrap_or(MultipleMessageSupport::Int1),
64        }
65    }
66
67    /// Does device supports masking individual vectors?
68    #[inline]
69    pub fn has_per_vector_masking(&self) -> bool {
70        self.per_vector_masking
71    }
72
73    /// Is device using 64-bit addressing?
74    #[inline]
75    pub fn is_64bit(&self) -> bool {
76        self.is_64bit
77    }
78
79    /// How many interrupts this device has?
80    #[inline]
81    pub fn multiple_message_capable(&self) -> MultipleMessageSupport {
82        self.multiple_message_capable
83    }
84
85    pub fn ctrl(&self, access: impl ConfigRegionAccess) -> u32 {
86        unsafe { access.read(self.address.address, self.address.offset) }
87    }
88
89    /// Is MSI capability enabled?
90    pub fn is_enabled(&self, access: impl ConfigRegionAccess) -> bool {
91        let reg = unsafe { access.read(self.address.address, self.address.offset) };
92        reg.get_bit(16)
93    }
94
95    /// Enable or disable MSI capability
96    pub fn set_enabled(&self, enabled: bool, access: impl ConfigRegionAccess) {
97        let mut reg = unsafe { access.read(self.address.address, self.address.offset) };
98        reg.set_bit(16, enabled);
99        unsafe { access.write(self.address.address, self.address.offset, reg) };
100    }
101
102    /// Set how many interrupts the device will use. If requested count is bigger than supported count,
103    /// the second will be used.
104    pub fn set_multiple_message_enable(&self, data: MultipleMessageSupport, access: impl ConfigRegionAccess) {
105        let mut reg = unsafe { access.read(self.address.address, self.address.offset) };
106        reg.set_bits(4..7, (data.min(self.multiple_message_capable)) as u32);
107        unsafe { access.write(self.address.address, self.address.offset, reg) };
108    }
109
110    /// Return how many interrupts the device is using
111    pub fn multiple_message_enable(&self, access: impl ConfigRegionAccess) -> MultipleMessageSupport {
112        let reg = unsafe { access.read(self.address.address, self.address.offset) };
113        MultipleMessageSupport::try_from(reg.get_bits(4..7) as u8).unwrap_or(MultipleMessageSupport::Int1)
114    }
115
116    /// Set the memory address that will be written to when the interrupt fires, and the data that
117    /// will be written to it.
118    pub fn set_message_info(&self, address: u64, data: u32, access: impl ConfigRegionAccess) {
119        unsafe {
120            access.write(self.address.address, self.address.offset + 0x04, address.get_bits(0..32) as u32);
121            if self.is_64bit {
122                access.write(self.address.address, self.address.offset + 0x08, address.get_bits(32..64) as u32);
123            }
124        }
125        let data_offset = if self.is_64bit { 0x0c } else { 0x08 };
126        unsafe {
127            access.write(self.address.address, self.address.offset + data_offset, data);
128        }
129    }
130
131    /// Set the memory address that will be written to when the interrupt fires, and the data that
132    /// will be written to it, specialised for the message format the LAPIC expects.
133    ///
134    /// # Arguments
135    /// * `address` - Target Local APIC address (if not changed, can be calculated with `0xfee00000 | (processor << 12)`)
136    /// * `vector` - Which interrupt vector should be triggered on LAPIC
137    /// * `trigger_mode` - When interrupt should be triggered
138    /// * `access` - PCI Configuration Space accessor
139    pub fn set_message_info_lapic(
140        &self,
141        address: u64,
142        vector: u8,
143        trigger_mode: TriggerMode,
144        access: impl ConfigRegionAccess,
145    ) {
146        let mut data = 0;
147        data.set_bits(0..8, vector as u32);
148        data.set_bits(14..16, trigger_mode as u32);
149        self.set_message_info(address, data, access);
150    }
151
152    /// Get interrupt mask
153    ///
154    /// # Note
155    /// Only supported on when device supports 64-bit addressing and per-vector masking. Otherwise
156    /// returns `0`
157    pub fn message_mask(&self, access: impl ConfigRegionAccess) -> u32 {
158        if self.is_64bit && self.per_vector_masking {
159            unsafe { access.read(self.address.address, self.address.offset + 0x10) }
160        } else {
161            0
162        }
163    }
164
165    /// Set interrupt mask
166    ///
167    /// # Note
168    /// Only supported on when device supports 64-bit addressing and per-vector masking. Otherwise
169    /// will do nothing
170    pub fn set_message_mask(&self, mask: u32, access: impl ConfigRegionAccess) {
171        if self.is_64bit && self.per_vector_masking {
172            unsafe { access.write(self.address.address, self.address.offset + 0x10, mask) }
173        }
174    }
175
176    /// Get pending interrupts
177    ///
178    /// # Note
179    /// Only supported on when device supports 64-bit addressing. Otherwise will return `0`
180    pub fn is_pending(&self, access: impl ConfigRegionAccess) -> u32 {
181        if self.is_64bit {
182            unsafe { access.read(self.address.address, self.address.offset + 0x14) }
183        } else {
184            0
185        }
186    }
187}