Skip to content

Commit 0f396a2

Browse files
committed
pci: add unit test for BAR reprogramming detection
Make sure we detect correctly valid intents to reprogram (move) BARs. Also, make sure we correctly ignore buggy ones. Signed-off-by: Babis Chalios <[email protected]>
1 parent c9bdb87 commit 0f396a2

File tree

2 files changed

+140
-4
lines changed

2 files changed

+140
-4
lines changed

src/pci/src/configuration.rs

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,9 +924,15 @@ impl PciConfiguration {
924924
region_type,
925925
});
926926
} else if (reg_idx > BAR0_REG)
927-
&& ((self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
928-
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
929-
|| (value & mask) != (self.bars[bar_idx].addr & mask))
927+
&& (
928+
// The lower BAR (of this 64bit BAR) has been reprogrammed to a different value
929+
// than it used to be
930+
(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
931+
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]) ||
932+
// Or the lower BAR hasn't been changed but the upper one is being reprogrammed
933+
// now to a different value
934+
(value & mask) != (self.bars[bar_idx].addr & mask)
935+
)
930936
{
931937
info!(
932938
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
@@ -1451,4 +1457,134 @@ mod tests {
14511457
assert_eq!(*reg, pci_config.read_reg(reg_idx));
14521458
}
14531459
}
1460+
1461+
#[test]
1462+
fn test_detect_bar_reprogramming() {
1463+
let mut pci_config = PciConfiguration::new(
1464+
0x42,
1465+
0x0,
1466+
0x0,
1467+
PciClassCode::MassStorage,
1468+
&PciMassStorageSubclass::SerialScsiController,
1469+
None,
1470+
PciHeaderType::Device,
1471+
0x13,
1472+
0x12,
1473+
None,
1474+
None,
1475+
);
1476+
1477+
// Trying to reprogram with something less than 4 bytes (length of the address) should fail
1478+
assert!(pci_config
1479+
.detect_bar_reprogramming(BAR0_REG, &[0x13])
1480+
.is_none());
1481+
assert!(pci_config
1482+
.detect_bar_reprogramming(BAR0_REG, &[0x13, 0x12])
1483+
.is_none());
1484+
assert!(pci_config
1485+
.detect_bar_reprogramming(BAR0_REG, &[0x13, 0x12])
1486+
.is_none());
1487+
assert!(pci_config
1488+
.detect_bar_reprogramming(BAR0_REG, &[0x13, 0x12, 0x16])
1489+
.is_none());
1490+
1491+
// Writing all 1s is a special case where we're actually asking for the size of the BAR
1492+
assert!(pci_config
1493+
.detect_bar_reprogramming(BAR0_REG, &u32::to_le_bytes(0xffff_ffff))
1494+
.is_none());
1495+
1496+
// Trying to reprogram a BAR that hasn't be initialized does nothing
1497+
for reg_idx in BAR0_REG..BAR0_REG + NUM_BAR_REGS {
1498+
assert!(pci_config
1499+
.detect_bar_reprogramming(reg_idx, &u32::to_le_bytes(0x1312_4243))
1500+
.is_none());
1501+
}
1502+
1503+
// Reprogramming of a 32bit BAR
1504+
pci_config
1505+
.add_pci_bar(&PciBarConfiguration {
1506+
addr: 0x1000,
1507+
size: 0x1000,
1508+
idx: 0,
1509+
region_type: PciBarRegionType::Memory32BitRegion,
1510+
prefetchable: PciBarPrefetchable::Prefetchable,
1511+
})
1512+
.unwrap();
1513+
1514+
assert_eq!(
1515+
pci_config.detect_bar_reprogramming(BAR0_REG, &u32::to_le_bytes(0x2000)),
1516+
Some(BarReprogrammingParams {
1517+
old_base: 0x1000,
1518+
new_base: 0x2000,
1519+
len: 0x1000,
1520+
region_type: PciBarRegionType::Memory32BitRegion
1521+
})
1522+
);
1523+
1524+
pci_config.write_config_register(BAR0_REG, 0, &u32::to_le_bytes(0x2000));
1525+
assert_eq!(pci_config.read_reg(BAR0_REG) & 0xffff_fff0, 0x2000);
1526+
1527+
// Attempting to reprogram the BAR with the same address should not have any effect
1528+
assert!(pci_config
1529+
.detect_bar_reprogramming(BAR0_REG, &u32::to_le_bytes(0x2000))
1530+
.is_none());
1531+
1532+
// Reprogramming of a 64bit BAR
1533+
pci_config
1534+
.add_pci_bar(&PciBarConfiguration {
1535+
addr: 0x13_1200_0000,
1536+
size: 0x8000,
1537+
idx: 1,
1538+
region_type: PciBarRegionType::Memory64BitRegion,
1539+
prefetchable: PciBarPrefetchable::Prefetchable,
1540+
})
1541+
.unwrap();
1542+
1543+
assert_eq!(pci_config.read_reg(BAR0_REG + 1) & 0xffff_fff0, 0x1200_0000);
1544+
assert_eq!(
1545+
pci_config.bars[1].r#type,
1546+
Some(PciBarRegionType::Memory64BitRegion)
1547+
);
1548+
assert_eq!(pci_config.read_reg(BAR0_REG + 2), 0x13);
1549+
assert!(pci_config.bars[2].r#type.is_none());
1550+
1551+
// First we write the lower 32 bits and this shouldn't cause any reprogramming
1552+
assert!(pci_config
1553+
.detect_bar_reprogramming(BAR0_REG + 1, &u32::to_le_bytes(0x4200_0000))
1554+
.is_none());
1555+
pci_config.write_config_register(BAR0_REG + 1, 0, &u32::to_le_bytes(0x4200_0000));
1556+
1557+
// Writing the upper 32 bits should trigger the reprogramming
1558+
assert_eq!(
1559+
pci_config.detect_bar_reprogramming(BAR0_REG + 2, &u32::to_le_bytes(0x84)),
1560+
Some(BarReprogrammingParams {
1561+
old_base: 0x13_1200_0000,
1562+
new_base: 0x84_4200_0000,
1563+
len: 0x8000,
1564+
region_type: PciBarRegionType::Memory64BitRegion
1565+
})
1566+
);
1567+
pci_config.write_config_register(BAR0_REG + 2, 0, &u32::to_le_bytes(0x84));
1568+
1569+
// Trying to reprogram the upper bits directly (without first touching the lower bits)
1570+
// should trigger a reprogramming
1571+
assert_eq!(
1572+
pci_config.detect_bar_reprogramming(BAR0_REG + 2, &u32::to_le_bytes(0x1312)),
1573+
Some(BarReprogrammingParams {
1574+
old_base: 0x84_4200_0000,
1575+
new_base: 0x1312_4200_0000,
1576+
len: 0x8000,
1577+
region_type: PciBarRegionType::Memory64BitRegion
1578+
})
1579+
);
1580+
pci_config.write_config_register(BAR0_REG + 2, 0, &u32::to_le_bytes(0x1312));
1581+
1582+
// Attempting to reprogram the BAR with the same address should not have any effect
1583+
assert!(pci_config
1584+
.detect_bar_reprogramming(BAR0_REG + 1, &u32::to_le_bytes(0x4200_0000))
1585+
.is_none());
1586+
assert!(pci_config
1587+
.detect_bar_reprogramming(BAR0_REG + 2, &u32::to_le_bytes(0x1312))
1588+
.is_none());
1589+
}
14541590
}

src/pci/src/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub enum Error {
2525
}
2626
pub type Result<T> = std::result::Result<T, Error>;
2727

28-
#[derive(Clone, Copy)]
28+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2929
pub struct BarReprogrammingParams {
3030
pub old_base: u64,
3131
pub new_base: u64,

0 commit comments

Comments
 (0)