Skip to content

Commit c44f5a1

Browse files
committed
pci: add unit test for adding BARs in config space
Add a unit test for the logic that handles adding BARs for a device. Also, ensure that we check that the BAR index we are adding is within range before we use it to index the BARs vector. Signed-off-by: Babis Chalios <bchalios@amazon.es>
1 parent 9993b35 commit c44f5a1

File tree

1 file changed

+167
-5
lines changed

1 file changed

+167
-5
lines changed

src/pci/src/configuration.rs

Lines changed: 167 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ impl PciConfiguration {
706706
let bar_idx = config.idx;
707707
let reg_idx = BAR0_REG + bar_idx;
708708

709+
if bar_idx >= NUM_BAR_REGS {
710+
return Err(Error::BarInvalid(bar_idx));
711+
}
712+
709713
if self.bars[bar_idx].used {
710714
return Err(Error::BarInUse(bar_idx));
711715
}
@@ -714,10 +718,6 @@ impl PciConfiguration {
714718
return Err(Error::BarSizeInvalid(config.size));
715719
}
716720

717-
if bar_idx >= NUM_BAR_REGS {
718-
return Err(Error::BarInvalid(bar_idx));
719-
}
720-
721721
let end_addr = config
722722
.addr
723723
.checked_add(config.size - 1)
@@ -739,7 +739,7 @@ impl PciConfiguration {
739739
}
740740

741741
if self.bars[bar_idx + 1].used {
742-
return Err(Error::BarInUse64(bar_idx));
742+
return Err(Error::BarInUse64(bar_idx + 1));
743743
}
744744

745745
// Encode the BAR size as expected by the software running in
@@ -1002,6 +1002,7 @@ impl Default for PciBarConfiguration {
10021002

10031003
#[cfg(test)]
10041004
mod tests {
1005+
10051006
use vm_memory::ByteValued;
10061007

10071008
use super::*;
@@ -1108,4 +1109,165 @@ mod tests {
11081109
assert_eq!(subclass, 0x01);
11091110
assert_eq!(prog_if, 0x5a);
11101111
}
1112+
1113+
#[test]
1114+
fn test_bar_size_encoding() {
1115+
assert!(encode_32_bits_bar_size(0).is_none());
1116+
assert!(decode_32_bits_bar_size(0).is_none());
1117+
assert!(encode_64_bits_bar_size(0).is_none());
1118+
assert!(decode_64_bits_bar_size(0, 0).is_none());
1119+
1120+
// According to OSDev wiki (https://wiki.osdev.org/PCI#Address_and_size_of_the_BAR):
1121+
//
1122+
// > To determine the amount of address space needed by a PCI device, you must save the
1123+
// > original value of the BAR, write a value of all 1's to the register, then read it back.
1124+
// > The amount of memory can then be determined by masking the information bits, performing
1125+
// > a bitwise NOT ('~' in C), and incrementing the value by 1. The original value of the
1126+
// BAR > should then be restored. The BAR register is naturally aligned and as such you can
1127+
// only > modify the bits that are set. For example, if a device utilizes 16 MB it will
1128+
// have BAR0 > filled with 0xFF000000 (0x1000000 after decoding) and you can only modify
1129+
// the upper > 8-bits.
1130+
//
1131+
// So we should be encoding an address like this: `addr` -> `!(addr - 1)`
1132+
let encoded = encode_32_bits_bar_size(0x0101_0101).unwrap();
1133+
assert_eq!(encoded, 0xfefe_feff);
1134+
assert_eq!(decode_32_bits_bar_size(encoded), Some(0x0101_0101));
1135+
1136+
// Similarly we encode a 64 bits size and then store it as a 2 32bit addresses (we use
1137+
// two BARs).
1138+
let (hi, lo) = encode_64_bits_bar_size(0xffff_ffff_ffff_fff0).unwrap();
1139+
assert_eq!(hi, 0);
1140+
assert_eq!(lo, 0x0000_0010);
1141+
assert_eq!(decode_64_bits_bar_size(hi, lo), Some(0xffff_ffff_ffff_fff0));
1142+
}
1143+
1144+
#[test]
1145+
fn test_add_pci_bar() {
1146+
let mut pci_config = PciConfiguration::new(
1147+
0x42,
1148+
0x0,
1149+
0x0,
1150+
PciClassCode::MassStorage,
1151+
&PciMassStorageSubclass::SerialScsiController,
1152+
None,
1153+
PciHeaderType::Device,
1154+
0x13,
1155+
0x12,
1156+
None,
1157+
None,
1158+
);
1159+
1160+
// BAR size can only be a power of 2
1161+
assert!(matches!(
1162+
pci_config.add_pci_bar(&PciBarConfiguration {
1163+
addr: 0x1000,
1164+
size: 0x1001,
1165+
idx: 0,
1166+
region_type: PciBarRegionType::Memory32BitRegion,
1167+
prefetchable: PciBarPrefetchable::Prefetchable,
1168+
}),
1169+
Err(Error::BarSizeInvalid(0x1001))
1170+
));
1171+
1172+
// Invalid BAR index
1173+
assert!(matches!(
1174+
pci_config.add_pci_bar(&PciBarConfiguration {
1175+
addr: 0x1000,
1176+
size: 0x1000,
1177+
idx: NUM_BAR_REGS,
1178+
region_type: PciBarRegionType::Memory32BitRegion,
1179+
prefetchable: PciBarPrefetchable::Prefetchable
1180+
}),
1181+
Err(Error::BarInvalid(NUM_BAR_REGS))
1182+
));
1183+
// 64bit BARs need 2 BAR slots actually
1184+
assert!(matches!(
1185+
pci_config.add_pci_bar(&PciBarConfiguration {
1186+
addr: 0x1000,
1187+
size: 0x1000,
1188+
idx: NUM_BAR_REGS - 1,
1189+
region_type: PciBarRegionType::Memory64BitRegion,
1190+
prefetchable: PciBarPrefetchable::Prefetchable
1191+
}),
1192+
Err(Error::BarInvalid64(_))
1193+
));
1194+
1195+
// Check for valid addresses
1196+
// Can't have an address that exceeds 32 bits for a 32bit BAR
1197+
assert!(matches!(
1198+
pci_config.add_pci_bar(&PciBarConfiguration {
1199+
addr: 0x1000_0000_0000_0000,
1200+
size: 0x1000,
1201+
idx: 0,
1202+
region_type: PciBarRegionType::Memory32BitRegion,
1203+
prefetchable: PciBarPrefetchable::Prefetchable
1204+
}),
1205+
Err(Error::BarAddressInvalid(0x1000_0000_0000_0000, 0x1000))
1206+
));
1207+
// Ensure that we handle properly overflows in 64bit BAR ranges
1208+
assert!(matches!(
1209+
pci_config.add_pci_bar(&PciBarConfiguration {
1210+
addr: u64::MAX,
1211+
size: 0x2,
1212+
idx: 0,
1213+
region_type: PciBarRegionType::Memory64BitRegion,
1214+
prefetchable: PciBarPrefetchable::Prefetchable
1215+
}),
1216+
Err(Error::BarAddressInvalid(u64::MAX, 2))
1217+
));
1218+
1219+
// We can't reuse a BAR slot
1220+
pci_config
1221+
.add_pci_bar(&PciBarConfiguration {
1222+
addr: 0x1000,
1223+
size: 0x1000,
1224+
idx: 0,
1225+
region_type: PciBarRegionType::Memory32BitRegion,
1226+
prefetchable: PciBarPrefetchable::Prefetchable,
1227+
})
1228+
.unwrap();
1229+
assert!(matches!(
1230+
pci_config.add_pci_bar(&PciBarConfiguration {
1231+
addr: 0x1000,
1232+
size: 0x1000,
1233+
idx: 0,
1234+
region_type: PciBarRegionType::Memory32BitRegion,
1235+
prefetchable: PciBarPrefetchable::Prefetchable,
1236+
}),
1237+
Err(Error::BarInUse(0))
1238+
));
1239+
pci_config
1240+
.add_pci_bar(&PciBarConfiguration {
1241+
addr: 0x0000_0001_0000_0000,
1242+
size: 0x2000,
1243+
idx: 2,
1244+
region_type: PciBarRegionType::Memory64BitRegion,
1245+
prefetchable: PciBarPrefetchable::Prefetchable,
1246+
})
1247+
.unwrap();
1248+
// For 64bit BARs two BARs are used (in this case BARs 1 and 2)
1249+
assert!(matches!(
1250+
pci_config.add_pci_bar(&PciBarConfiguration {
1251+
addr: 0x0000_0001_0000_0000,
1252+
size: 0x1000,
1253+
idx: 2,
1254+
region_type: PciBarRegionType::Memory64BitRegion,
1255+
prefetchable: PciBarPrefetchable::Prefetchable,
1256+
}),
1257+
Err(Error::BarInUse(2))
1258+
));
1259+
assert!(matches!(
1260+
pci_config.add_pci_bar(&PciBarConfiguration {
1261+
addr: 0x0000_0001_0000_0000,
1262+
size: 0x1000,
1263+
idx: 1,
1264+
region_type: PciBarRegionType::Memory64BitRegion,
1265+
prefetchable: PciBarPrefetchable::Prefetchable,
1266+
}),
1267+
Err(Error::BarInUse64(2))
1268+
));
1269+
1270+
assert_eq!(pci_config.get_bar_addr(0), 0x1000);
1271+
assert_eq!(pci_config.get_bar_addr(2), 0x1_0000_0000);
1272+
}
11111273
}

0 commit comments

Comments
 (0)