Skip to content

Commit 3c3ad5c

Browse files
committed
Add basic configuration of MSI-X capabilities
1 parent 88fe6cb commit 3c3ad5c

File tree

2 files changed

+82
-21
lines changed

2 files changed

+82
-21
lines changed

src/capability/mod.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use bit_field::BitField;
33
use core::fmt::Formatter;
44

55
mod msi;
6+
mod msix;
67

78
pub use msi::{MsiCapability, MultipleMessageSupport, TriggerMode};
9+
pub use msix::MsixCapability;
810

911
#[derive(Clone)]
1012
pub struct PciCapabilityAddress {
@@ -52,16 +54,18 @@ pub enum PciCapability {
5254
/// PCI Express capability, Cap ID = `0x10`
5355
PciExpress(PciCapabilityAddress),
5456
/// MSI-X capability, Cap ID = `0x11`
55-
MsiX(PciCapabilityAddress),
57+
MsiX(MsixCapability),
5658
/// Unknown capability
57-
Unknown {
58-
address: PciCapabilityAddress,
59-
id: u8,
60-
},
59+
Unknown { address: PciCapabilityAddress, id: u8 },
6160
}
6261

6362
impl PciCapability {
64-
fn parse(id: u8, address: PciCapabilityAddress, extension: u16) -> Option<PciCapability> {
63+
fn parse(
64+
id: u8,
65+
address: PciCapabilityAddress,
66+
extension: u16,
67+
access: &impl ConfigRegionAccess,
68+
) -> Option<PciCapability> {
6569
match id {
6670
0x00 => None, // null capability
6771
0x01 => Some(PciCapability::PowerManagement(address)),
@@ -79,7 +83,7 @@ impl PciCapability {
7983
0x0D => Some(PciCapability::BridgeSubsystemVendorId(address)),
8084
0x0E => Some(PciCapability::AGP3(address)),
8185
0x10 => Some(PciCapability::PciExpress(address)),
82-
0x11 => Some(PciCapability::MsiX(address)),
86+
0x11 => Some(PciCapability::MsiX(MsixCapability::new(address, extension, access))),
8387
_ => Some(PciCapability::Unknown { address, id }),
8488
}
8589
}
@@ -92,16 +96,8 @@ pub struct CapabilityIterator<'a, T: ConfigRegionAccess> {
9296
}
9397

9498
impl<'a, T: ConfigRegionAccess> CapabilityIterator<'a, T> {
95-
pub(crate) fn new(
96-
address: PciAddress,
97-
offset: u16,
98-
access: &'a T,
99-
) -> CapabilityIterator<'a, T> {
100-
CapabilityIterator {
101-
address,
102-
offset,
103-
access,
104-
}
99+
pub(crate) fn new(address: PciAddress, offset: u16, access: &'a T) -> CapabilityIterator<'a, T> {
100+
CapabilityIterator { address, offset, access }
105101
}
106102
}
107103

@@ -119,11 +115,9 @@ impl<'a, T: ConfigRegionAccess> Iterator for CapabilityIterator<'a, T> {
119115
let extension = data.get_bits(16..32) as u16;
120116
let cap = PciCapability::parse(
121117
id as u8,
122-
PciCapabilityAddress {
123-
address: self.address,
124-
offset: self.offset,
125-
},
118+
PciCapabilityAddress { address: self.address, offset: self.offset },
126119
extension,
120+
self.access,
127121
);
128122
self.offset = next_ptr as u16;
129123
if let Some(cap) = cap {

src/capability/msix.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use super::{PciCapabilityAddress, TriggerMode};
2+
use crate::ConfigRegionAccess;
3+
use bit_field::BitField;
4+
5+
#[derive(Clone, Debug)]
6+
pub struct MsixCapability {
7+
address: PciCapabilityAddress,
8+
table_size: u16,
9+
/// Table BAR in bits 0..3 and offset into that BAR in bits 3..31
10+
table: u32,
11+
/// Pending Bit Array BAR in bits 0..3 and offset into that BAR in bits 3..31
12+
pba: u32,
13+
}
14+
15+
impl MsixCapability {
16+
pub(crate) fn new(
17+
address: PciCapabilityAddress,
18+
control: u16,
19+
access: &impl ConfigRegionAccess,
20+
) -> MsixCapability {
21+
let table_size = control.get_bits(0..11) + 1;
22+
let table = unsafe { access.read(address.address, address.offset + 0x04) };
23+
let pba = unsafe { access.read(address.address, address.offset + 0x08) };
24+
MsixCapability { address, table_size, table, pba }
25+
}
26+
27+
/// Enable MSI-X on the specified device feature.
28+
///
29+
/// Unlike with MSI, the MSI message data and delivery address is not contained within the
30+
/// capability, but instead in system memory, and pointed to by the BAR specified by
31+
/// `[MsixCapability::table_bar]` and `[MsixCapability::table_offset]`. The caller is therefore
32+
/// responsible for configuring this separately, as this crate does not have access to
33+
/// arbitrary physical memory.
34+
pub fn set_enabled(&mut self, enabled: bool, access: &impl ConfigRegionAccess) {
35+
let mut control = unsafe { access.read(self.address.address, self.address.offset) };
36+
control.set_bit(31, enabled);
37+
unsafe {
38+
access.write(self.address.address, self.address.offset, control);
39+
}
40+
}
41+
42+
/// The index of the BAR that contains the MSI-X table.
43+
pub fn table_bar(&self) -> u8 {
44+
self.table.get_bits(0..3) as u8
45+
}
46+
47+
/// The offset, in bytes, of the MSI-X table within its BAR.
48+
pub fn table_offset(&self) -> u32 {
49+
/*
50+
* To form the offset into the BAR, we mask off (set to zero) the lowest 3 bits, but
51+
* they're retained as part of the offset.
52+
*/
53+
self.table & !0b111
54+
}
55+
56+
pub fn pba_bar(&self) -> u8 {
57+
self.pba.get_bits(0..3) as u8
58+
}
59+
60+
pub fn pba_offset(&self) -> u32 {
61+
/*
62+
* To form the offset into the BAR, we mask off (set to zero) the lowest 3 bits, but
63+
* they're retained as part of the offset.
64+
*/
65+
self.pba & !0b111
66+
}
67+
}

0 commit comments

Comments
 (0)