pci_types/capability/msix.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
use super::PciCapabilityAddress;
use crate::ConfigRegionAccess;
use bit_field::BitField;
#[derive(Clone, Copy, Debug)]
pub struct MsixCapability {
pub(super) address: PciCapabilityAddress,
table_size: u16,
/// Table BAR in bits 0..3 and offset into that BAR in bits 3..31
table: u32,
/// Pending Bit Array BAR in bits 0..3 and offset into that BAR in bits 3..31
pba: u32,
}
impl MsixCapability {
pub(crate) fn new(
address: PciCapabilityAddress,
control: u16,
access: impl ConfigRegionAccess,
) -> MsixCapability {
let table_size = control.get_bits(0..11) + 1;
let table = unsafe { access.read(address.address, address.offset + 0x04) };
let pba = unsafe { access.read(address.address, address.offset + 0x08) };
MsixCapability { address, table_size, table, pba }
}
/// Enable MSI-X on the specified device feature.
///
/// Unlike with MSI, the MSI message data and delivery address is not contained within the
/// capability, but instead in system memory, and pointed to by the BAR specified by
/// [`MsixCapability::table_bar`] and [`MsixCapability::table_offset`]. The caller is therefore
/// responsible for configuring this separately, as this crate does not have access to
/// arbitrary physical memory.
pub fn set_enabled(&mut self, enabled: bool, access: impl ConfigRegionAccess) {
let mut control = unsafe { access.read(self.address.address, self.address.offset) };
control.set_bit(31, enabled);
unsafe {
access.write(self.address.address, self.address.offset, control);
}
}
pub fn enabled(&self, access: impl ConfigRegionAccess) -> bool {
let control = unsafe { access.read(self.address.address, self.address.offset) };
control.get_bit(31)
}
/// Enable/disable masking of all interrupts for this PCI function.
///
/// Individual interrupt sources can be masked using mask field of the corresponding entry in
/// the MSI-X table.
pub fn set_function_mask(&mut self, mask: bool, access: impl ConfigRegionAccess) {
let mut control = unsafe { access.read(self.address.address, self.address.offset) };
control.set_bit(30, mask);
unsafe {
access.write(self.address.address, self.address.offset, control);
}
}
pub fn function_mask(&self, access: impl ConfigRegionAccess) -> bool {
let control = unsafe { access.read(self.address.address, self.address.offset) };
control.get_bit(30)
}
/// The index of the BAR that contains the MSI-X table.
pub fn table_bar(&self) -> u8 {
self.table.get_bits(0..3) as u8
}
/// The offset, in bytes, of the MSI-X table within its BAR.
pub fn table_offset(&self) -> u32 {
/*
* To form the offset into the BAR, we mask off (set to zero) the lowest 3 bits, but
* they're retained as part of the offset.
*/
self.table & !0b111
}
pub fn table_size(&self) -> u16 {
self.table_size
}
pub fn pba_bar(&self) -> u8 {
self.pba.get_bits(0..3) as u8
}
pub fn pba_offset(&self) -> u32 {
/*
* To form the offset into the BAR, we mask off (set to zero) the lowest 3 bits, but
* they're retained as part of the offset.
*/
self.pba & !0b111
}
}