Skip to content

Commit 51a1aa8

Browse files
committed
pci: add unit test for configuring MSI-X capability
Make sure that configuring the MSI-X capability in PCI configuration space works properly (configuration space is initialized as expected). Also, make sure that the implementation respects the read/write properties of the respective bits. Finally, fix logic in the code that adds capabilities to take into account properly the size of capabilities. Signed-off-by: Babis Chalios <[email protected]>
1 parent 57aab34 commit 51a1aa8

File tree

1 file changed

+144
-13
lines changed

1 file changed

+144
-13
lines changed

src/pci/src/configuration.rs

Lines changed: 144 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub trait PciProgrammingInterface {
190190
}
191191

192192
/// Types of PCI capabilities.
193-
#[derive(PartialEq, Eq, Copy, Clone)]
193+
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
194194
#[allow(dead_code)]
195195
#[allow(non_camel_case_types)]
196196
#[repr(u8)]
@@ -474,8 +474,6 @@ pub enum Error {
474474
BarInvalid(usize),
475475
BarInvalid64(usize),
476476
BarSizeInvalid(u64),
477-
CapabilityEmpty,
478-
CapabilityLengthInvalid(usize),
479477
CapabilitySpaceFull(usize),
480478
Decode32BarSize,
481479
Decode64BarSize,
@@ -505,8 +503,6 @@ impl Display for Error {
505503
NUM_BAR_REGS - 1
506504
),
507505
BarSizeInvalid(s) => write!(f, "bar address {s} not a power of two"),
508-
CapabilityEmpty => write!(f, "empty capabilities are invalid"),
509-
CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {l}"),
510506
CapabilitySpaceFull(s) => write!(f, "capability of size {s} doesn't fit"),
511507
Decode32BarSize => write!(f, "failed to decode 32 bits BAR size"),
512508
Decode64BarSize => write!(f, "failed to decode 64 bits BAR size"),
@@ -789,15 +785,12 @@ impl PciConfiguration {
789785
}
790786

791787
/// Adds the capability `cap_data` to the list of capabilities.
792-
/// `cap_data` should include the two-byte PCI capability header (type, next),
793-
/// but not populate it. Correct values will be generated automatically based
794-
/// on `cap_data.id()`.
788+
///
789+
/// `cap_data` should not include the two-byte PCI capability header (type, next).
790+
/// Correct values will be generated automatically based on `cap_data.id()` and
791+
/// `cap_data.len()`.
795792
pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> Result<usize> {
796-
let total_len = cap_data.bytes().len();
797-
// Check that the length is valid.
798-
if cap_data.bytes().is_empty() {
799-
return Err(Error::CapabilityEmpty);
800-
}
793+
let total_len = cap_data.bytes().len() + 2;
801794
let (cap_offset, tail_offset) = match self.last_capability {
802795
Some((offset, len)) => (Self::next_dword(offset, len), offset + 1),
803796
None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET),
@@ -1006,6 +999,7 @@ mod tests {
1006999
use vm_memory::ByteValued;
10071000

10081001
use super::*;
1002+
use crate::MsixCap;
10091003

10101004
#[repr(C, packed)]
10111005
#[derive(Clone, Copy, Default)]
@@ -1028,6 +1022,28 @@ mod tests {
10281022
}
10291023
}
10301024

1025+
struct BadCap {
1026+
data: Vec<u8>,
1027+
}
1028+
1029+
impl BadCap {
1030+
fn new(len: u8) -> Self {
1031+
Self {
1032+
data: (0..len).collect(),
1033+
}
1034+
}
1035+
}
1036+
1037+
impl PciCapability for BadCap {
1038+
fn bytes(&self) -> &[u8] {
1039+
&self.data
1040+
}
1041+
1042+
fn id(&self) -> PciCapabilityId {
1043+
PciCapabilityId::VendorSpecific
1044+
}
1045+
}
1046+
10311047
#[test]
10321048
fn add_capability() {
10331049
let mut cfg = PciConfiguration::new(
@@ -1044,6 +1060,20 @@ mod tests {
10441060
None,
10451061
);
10461062

1063+
// Bad size capabilities
1064+
assert!(matches!(
1065+
cfg.add_capability(&BadCap::new(127)),
1066+
Err(Error::CapabilitySpaceFull(129))
1067+
));
1068+
cfg.add_capability(&BadCap::new(62)).unwrap();
1069+
cfg.add_capability(&BadCap::new(62)).unwrap();
1070+
assert!(matches!(
1071+
cfg.add_capability(&BadCap::new(0)),
1072+
Err(Error::CapabilitySpaceFull(2))
1073+
));
1074+
// Reset capabilities
1075+
cfg.last_capability = None;
1076+
10471077
// Add two capabilities with different contents.
10481078
let cap1 = TestCap { len: 4, foo: 0xAA };
10491079
let cap1_offset = cfg.add_capability(&cap1).unwrap();
@@ -1074,6 +1104,107 @@ mod tests {
10741104
assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
10751105
}
10761106

1107+
#[test]
1108+
fn test_msix_capability() {
1109+
let mut cfg = PciConfiguration::new(
1110+
0x1234,
1111+
0x5678,
1112+
0x1,
1113+
PciClassCode::MultimediaController,
1114+
&PciMultimediaSubclass::AudioController,
1115+
None,
1116+
PciHeaderType::Device,
1117+
0xABCD,
1118+
0x2468,
1119+
None,
1120+
None,
1121+
);
1122+
1123+
// Information about the MSI-X capability layout: https://wiki.osdev.org/PCI#Enabling_MSI-X
1124+
let msix_cap = MsixCap::new(
1125+
3, // Using BAR3 for message control table
1126+
1024, // 1024 MSI-X vectors
1127+
0x4000, // Offset of message control table inside the BAR
1128+
4, // BAR4 used for pending control bit
1129+
0x420, // Offset of pending bit array (PBA) inside BAR
1130+
);
1131+
cfg.add_capability(&msix_cap).unwrap();
1132+
1133+
let cap_reg = FIRST_CAPABILITY_OFFSET / 4;
1134+
let reg = cfg.read_reg(cap_reg);
1135+
// Capability ID is MSI-X
1136+
assert_eq!(
1137+
PciCapabilityId::from((reg & 0xff) as u8),
1138+
PciCapabilityId::MsiX
1139+
);
1140+
// We only have one capability, so `next` should be 0
1141+
assert_eq!(((reg >> 8) & 0xff) as u8, 0);
1142+
let msg_ctl = (reg >> 16) as u16;
1143+
1144+
// MSI-X is enabled
1145+
assert_eq!(msg_ctl & 0x8000, 0x8000);
1146+
// Vectors are not masked
1147+
assert_eq!(msg_ctl & 0x4000, 0x0);
1148+
// Reserved bits are 0
1149+
assert_eq!(msg_ctl & 0x3800, 0x0);
1150+
// We've got 1024 vectors (Table size is N-1 encoded)
1151+
assert_eq!((msg_ctl & 0x7ff) + 1, 1024);
1152+
1153+
let reg = cfg.read_reg(cap_reg + 1);
1154+
// We are using BAR3
1155+
assert_eq!(reg & 0x7, 3);
1156+
// Message Control Table is located in offset 0x4000 inside the BAR
1157+
// We don't need to shift. Offset needs to be 8-byte aligned - so BIR
1158+
// is stored in its last 3 bits (which we need to mask out).
1159+
assert_eq!(reg & 0xffff_fff8, 0x4000);
1160+
1161+
let reg = cfg.read_reg(cap_reg + 2);
1162+
// PBA is 0x420 bytes inside BAR4
1163+
assert_eq!(reg & 0x7, 4);
1164+
assert_eq!(reg & 0xffff_fff8, 0x420);
1165+
1166+
// Check read/write mask
1167+
// Capability Id of MSI-X is 0x11
1168+
cfg.write_config_register(cap_reg, 0, &[0x0]);
1169+
assert_eq!(
1170+
PciCapabilityId::from((cfg.read_reg(cap_reg) & 0xff) as u8),
1171+
PciCapabilityId::MsiX
1172+
);
1173+
// Cannot override next capability pointer
1174+
cfg.write_config_register(cap_reg, 1, &[0x42]);
1175+
assert_eq!((cfg.read_reg(cap_reg) >> 8) & 0xff, 0);
1176+
1177+
// We are writing this:
1178+
//
1179+
// meaning: | MSI enabled | Vectors Masked | Reserved | Table size |
1180+
// bit: | 15 | 14 | 13 - 11 | 0 - 10 |
1181+
// R/W: | R/W | R/W | R | R |
1182+
let msg_ctl = (cfg.read_reg(cap_reg) >> 16) as u16;
1183+
// Try to flip all bits
1184+
cfg.write_config_register(cap_reg, 2, &u16::to_le_bytes(!msg_ctl));
1185+
let msg_ctl = (cfg.read_reg(cap_reg) >> 16) as u16;
1186+
// MSI enabled and Vectors masked should be flipped (MSI disabled and vectors masked)
1187+
assert_eq!(msg_ctl & 0xc000, 0x4000);
1188+
// Reserved bits should still be 0
1189+
assert_eq!(msg_ctl & 0x3800, 0);
1190+
// Table size should not have changed
1191+
assert_eq!((msg_ctl & 0x07ff) + 1, 1024);
1192+
1193+
// Table offset is read only
1194+
let table_offset = cfg.read_reg(cap_reg + 1);
1195+
// Try to flip all bits
1196+
cfg.write_config_register(cap_reg + 1, 0, &u32::to_le_bytes(!table_offset));
1197+
// None should be flipped
1198+
assert_eq!(cfg.read_reg(cap_reg + 1), table_offset);
1199+
1200+
// PBA offset also
1201+
let pba_offset = cfg.read_reg(cap_reg + 2);
1202+
// Try to flip all bits
1203+
cfg.write_config_register(cap_reg + 2, 0, &u32::to_le_bytes(!pba_offset));
1204+
// None should be flipped
1205+
assert_eq!(cfg.read_reg(cap_reg + 2), pba_offset);
1206+
}
1207+
10771208
#[derive(Copy, Clone)]
10781209
enum TestPi {
10791210
Test = 0x5a,

0 commit comments

Comments
 (0)