Skip to content

Commit 2540713

Browse files
committed
pci: add unit tests for VirtIO PCI devices
Add more unit tests in for VirtIO PCI capabilities other than the common configuration capability. Signed-off-by: Babis Chalios <[email protected]>
1 parent 8ee3af1 commit 2540713

File tree

1 file changed

+200
-31
lines changed
  • src/vmm/src/devices/virtio/transport/pci

1 file changed

+200
-31
lines changed

src/vmm/src/devices/virtio/transport/pci/device.rs

Lines changed: 200 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ enum PciCapabilityType {
7474
const VIRTIO_PCI_CAP_OFFSET: usize = 2;
7575

7676
#[repr(C, packed)]
77-
#[derive(Debug, Clone, Copy, Default)]
77+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
7878
struct VirtioPciCap {
7979
cap_len: u8, // Generic PCI field: capability length
8080
cfg_type: u8, // Identifies the structure.
@@ -153,7 +153,7 @@ impl VirtioPciNotifyCap {
153153
}
154154

155155
#[repr(C, packed)]
156-
#[derive(Debug, Clone, Copy, Default)]
156+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
157157
struct VirtioPciCfgCap {
158158
cap: VirtioPciCap,
159159
pci_cfg_data: [u8; 4],
@@ -1002,19 +1002,27 @@ mod tests {
10021002

10031003
use event_manager::MutEventSubscriber;
10041004
use linux_loader::loader::Cmdline;
1005-
use pci::{PciBdf, PciClassCode, PciDevice, PciSubclass};
1005+
use pci::{
1006+
MsixCap, PciBdf, PciCapability, PciCapabilityId, PciClassCode, PciDevice, PciSubclass,
1007+
};
1008+
use vm_memory::{ByteValued, Le32};
10061009

1007-
use super::VirtioPciDevice;
1008-
use crate::Vm;
1010+
use super::{PciCapabilityType, VirtioPciDevice};
10091011
use crate::arch::MEM_64BIT_DEVICES_START;
10101012
use crate::builder::tests::default_vmm;
10111013
use crate::devices::virtio::device::VirtioDevice;
1014+
use crate::devices::virtio::generated::virtio_ids;
10121015
use crate::devices::virtio::rng::Entropy;
1013-
use crate::devices::virtio::transport::pci::device::PciVirtioSubclass;
1016+
use crate::devices::virtio::transport::pci::device::{
1017+
COMMON_CONFIG_BAR_OFFSET, COMMON_CONFIG_SIZE, DEVICE_CONFIG_BAR_OFFSET, DEVICE_CONFIG_SIZE,
1018+
ISR_CONFIG_BAR_OFFSET, ISR_CONFIG_SIZE, NOTIFICATION_BAR_OFFSET, NOTIFICATION_SIZE,
1019+
NOTIFY_OFF_MULTIPLIER, PciVirtioSubclass, VirtioPciCap, VirtioPciCfgCap,
1020+
VirtioPciNotifyCap,
1021+
};
10141022
use crate::rate_limiter::RateLimiter;
1023+
use crate::{Vm, Vmm};
10151024

1016-
#[test]
1017-
fn test_pci_device_config() {
1025+
fn create_vmm_with_virtio_pci_device() -> Vmm {
10181026
let mut vmm = default_vmm();
10191027
vmm.device_manager.enable_pci(&vmm.vm);
10201028
let entropy = Arc::new(Mutex::new(Entropy::new(RateLimiter::default()).unwrap()));
@@ -1027,13 +1035,21 @@ mod tests {
10271035
false,
10281036
)
10291037
.unwrap();
1038+
vmm
1039+
}
10301040

1031-
let device = vmm
1032-
.device_manager
1041+
fn get_virtio_device(vmm: &Vmm) -> Arc<Mutex<VirtioPciDevice>> {
1042+
vmm.device_manager
10331043
.pci_devices
1034-
.get_virtio_device(entropy.lock().unwrap().device_type(), "rng")
1035-
.unwrap();
1044+
.get_virtio_device(virtio_ids::VIRTIO_ID_RNG, "rng")
1045+
.unwrap()
1046+
.clone()
1047+
}
10361048

1049+
#[test]
1050+
fn test_pci_device_config() {
1051+
let mut vmm = create_vmm_with_virtio_pci_device();
1052+
let device = get_virtio_device(&vmm);
10371053
let mut locked_virtio_pci_device = device.lock().unwrap();
10381054

10391055
// For more information for the values we are checking here look into the VirtIO spec here:
@@ -1133,25 +1149,8 @@ mod tests {
11331149

11341150
#[test]
11351151
fn test_reading_bars() {
1136-
let mut vmm = default_vmm();
1137-
vmm.device_manager.enable_pci(&vmm.vm);
1138-
let entropy = Arc::new(Mutex::new(Entropy::new(RateLimiter::default()).unwrap()));
1139-
vmm.device_manager
1140-
.attach_virtio_device(
1141-
&vmm.vm,
1142-
"rng".to_string(),
1143-
entropy.clone(),
1144-
&mut Cmdline::new(1024).unwrap(),
1145-
false,
1146-
)
1147-
.unwrap();
1148-
1149-
let device = vmm
1150-
.device_manager
1151-
.pci_devices
1152-
.get_virtio_device(entropy.lock().unwrap().device_type(), "rng")
1153-
.unwrap();
1154-
1152+
let mut vmm = create_vmm_with_virtio_pci_device();
1153+
let device = get_virtio_device(&vmm);
11551154
let mut locked_virtio_pci_device = device.lock().unwrap();
11561155

11571156
// According to OSdev wiki (https://wiki.osdev.org/PCI#Configuration_Space):
@@ -1204,4 +1203,174 @@ mod tests {
12041203
// We create a capabilities BAR region of 0x80000 bytes
12051204
assert_eq!(bar_size, 0x80000);
12061205
}
1206+
1207+
fn read_virtio_pci_cap(
1208+
device: &mut VirtioPciDevice,
1209+
offset: u32,
1210+
) -> (PciCapabilityId, u8, VirtioPciCap) {
1211+
let word1 = device.read_config_register((offset >> 2) as usize);
1212+
let word2 = device.read_config_register((offset >> 2) as usize + 1);
1213+
let word3 = device.read_config_register((offset >> 2) as usize + 2);
1214+
let word4 = device.read_config_register((offset >> 2) as usize + 3);
1215+
1216+
let id = PciCapabilityId::from((word1 & 0xff) as u8);
1217+
let next = ((word1 >> 8) & 0xff) as u8;
1218+
1219+
let cap = VirtioPciCap {
1220+
cap_len: ((word1 >> 16) & 0xff) as u8,
1221+
cfg_type: ((word1 >> 24) & 0xff) as u8,
1222+
pci_bar: (word2 & 0xff) as u8,
1223+
id: ((word2 >> 8) & 0xff) as u8,
1224+
padding: [0u8; 2],
1225+
offset: Le32::from(word3),
1226+
length: Le32::from(word4),
1227+
};
1228+
1229+
// We only ever set a single capability of a type. It's ID is 0.
1230+
assert_eq!(cap.id, 0);
1231+
1232+
(id, next, cap)
1233+
}
1234+
1235+
fn read_virtio_notification_cap(
1236+
device: &mut VirtioPciDevice,
1237+
offset: u32,
1238+
) -> (PciCapabilityId, u8, VirtioPciNotifyCap) {
1239+
let (id, next, cap) = read_virtio_pci_cap(device, offset);
1240+
let word5 = device.read_config_register((offset >> 2) as usize + 4);
1241+
1242+
let notification_cap = VirtioPciNotifyCap {
1243+
cap,
1244+
notify_off_multiplier: Le32::from(word5),
1245+
};
1246+
1247+
(id, next, notification_cap)
1248+
}
1249+
1250+
fn read_virtio_pci_config_cap(
1251+
device: &mut VirtioPciDevice,
1252+
offset: u32,
1253+
) -> (PciCapabilityId, u8, VirtioPciCfgCap) {
1254+
let (id, next, cap) = read_virtio_pci_cap(device, offset);
1255+
let word5 = device.read_config_register((offset >> 2) as usize + 4);
1256+
1257+
let pci_cfg_cap = VirtioPciCfgCap {
1258+
cap,
1259+
pci_cfg_data: word5.as_slice().try_into().unwrap(),
1260+
};
1261+
1262+
(id, next, pci_cfg_cap)
1263+
}
1264+
1265+
fn read_msix_cap(device: &mut VirtioPciDevice, offset: u32) -> (PciCapabilityId, u8, MsixCap) {
1266+
let word1 = device.read_config_register((offset >> 2) as usize);
1267+
let table = device.read_config_register((offset >> 2) as usize + 1);
1268+
let pba = device.read_config_register((offset >> 2) as usize + 2);
1269+
1270+
let id = PciCapabilityId::from((word1 & 0xff) as u8);
1271+
let next = ((word1 >> 8) & 0xff) as u8;
1272+
1273+
let cap = MsixCap {
1274+
msg_ctl: (word1 & 0xffff) as u16,
1275+
table,
1276+
pba,
1277+
};
1278+
1279+
(id, next, cap)
1280+
}
1281+
1282+
fn capabilities_start(device: &mut VirtioPciDevice) -> u32 {
1283+
device.read_config_register(0xd) & 0xfc
1284+
}
1285+
1286+
#[test]
1287+
fn test_capabilities() {
1288+
let mut vmm = create_vmm_with_virtio_pci_device();
1289+
let device = get_virtio_device(&vmm);
1290+
let mut locked_virtio_pci_device = device.lock().unwrap();
1291+
1292+
// VirtIO devices need to expose a set of mandatory capabilities:
1293+
// * Common configuration
1294+
// * Notifications
1295+
// * ISR status
1296+
// * PCI configuration access
1297+
//
1298+
// and, optionally, a device-specific configuration area for those devices that need it.
1299+
//
1300+
// We always expose all 5 capabilities, so check that the capabilities are present
1301+
1302+
// Common config
1303+
let common_config_cap_offset = capabilities_start(&mut locked_virtio_pci_device);
1304+
let (id, next, cap) =
1305+
read_virtio_pci_cap(&mut locked_virtio_pci_device, common_config_cap_offset);
1306+
assert_eq!(id, PciCapabilityId::VendorSpecific);
1307+
assert_eq!(cap.cap_len as usize, size_of::<VirtioPciCap>() + 2);
1308+
assert_eq!(cap.cfg_type, PciCapabilityType::Common as u8);
1309+
assert_eq!(cap.pci_bar, 0);
1310+
assert_eq!(u32::from(cap.offset) as u64, COMMON_CONFIG_BAR_OFFSET);
1311+
assert_eq!(u32::from(cap.length) as u64, COMMON_CONFIG_SIZE);
1312+
assert_eq!(next as u32, common_config_cap_offset + cap.cap_len as u32);
1313+
1314+
// ISR
1315+
let isr_cap_offset = next as u32;
1316+
let (id, next, cap) = read_virtio_pci_cap(&mut locked_virtio_pci_device, isr_cap_offset);
1317+
assert_eq!(id, PciCapabilityId::VendorSpecific);
1318+
assert_eq!(cap.cap_len as usize, size_of::<VirtioPciCap>() + 2);
1319+
assert_eq!(cap.cfg_type, PciCapabilityType::Isr as u8);
1320+
assert_eq!(cap.pci_bar, 0);
1321+
assert_eq!(u32::from(cap.offset) as u64, ISR_CONFIG_BAR_OFFSET);
1322+
assert_eq!(u32::from(cap.length) as u64, ISR_CONFIG_SIZE);
1323+
assert_eq!(next as u32, isr_cap_offset + cap.cap_len as u32);
1324+
1325+
// Device config
1326+
let device_config_cap_offset = next as u32;
1327+
let (id, next, cap) =
1328+
read_virtio_pci_cap(&mut locked_virtio_pci_device, device_config_cap_offset);
1329+
assert_eq!(id, PciCapabilityId::VendorSpecific);
1330+
assert_eq!(cap.cap_len as usize, size_of::<VirtioPciCap>() + 2);
1331+
assert_eq!(cap.cfg_type, PciCapabilityType::Device as u8);
1332+
assert_eq!(cap.pci_bar, 0);
1333+
assert_eq!(u32::from(cap.offset) as u64, DEVICE_CONFIG_BAR_OFFSET);
1334+
assert_eq!(u32::from(cap.length) as u64, DEVICE_CONFIG_SIZE);
1335+
assert_eq!(next as u32, device_config_cap_offset + cap.cap_len as u32);
1336+
1337+
let notification_cap_offset = next as u32;
1338+
let (id, next, cap) =
1339+
read_virtio_notification_cap(&mut locked_virtio_pci_device, notification_cap_offset);
1340+
assert_eq!(id, PciCapabilityId::VendorSpecific);
1341+
assert_eq!(
1342+
cap.cap.cap_len as usize,
1343+
size_of::<VirtioPciNotifyCap>() + 2
1344+
);
1345+
assert_eq!(cap.cap.cfg_type, PciCapabilityType::Notify as u8);
1346+
assert_eq!(cap.cap.pci_bar, 0);
1347+
assert_eq!(u32::from(cap.cap.offset) as u64, NOTIFICATION_BAR_OFFSET);
1348+
assert_eq!(u32::from(cap.cap.length) as u64, NOTIFICATION_SIZE);
1349+
assert_eq!(
1350+
next as u32,
1351+
notification_cap_offset + cap.cap.cap_len as u32
1352+
);
1353+
assert_eq!(u32::from(cap.notify_off_multiplier), NOTIFY_OFF_MULTIPLIER);
1354+
1355+
let pci_config_cap_offset = next as u32;
1356+
let (id, next, cap) =
1357+
read_virtio_pci_config_cap(&mut locked_virtio_pci_device, pci_config_cap_offset);
1358+
assert_eq!(id, PciCapabilityId::VendorSpecific);
1359+
assert_eq!(cap.cap.cap_len as usize, size_of::<VirtioPciCfgCap>() + 2);
1360+
assert_eq!(cap.cap.cfg_type, PciCapabilityType::Pci as u8);
1361+
assert_eq!(cap.cap.pci_bar, 0);
1362+
assert_eq!(u32::from(cap.cap.offset) as u64, 0);
1363+
assert_eq!(u32::from(cap.cap.length) as u64, 0);
1364+
assert_eq!(
1365+
locked_virtio_pci_device.cap_pci_cfg_info.offset,
1366+
pci_config_cap_offset as usize + 2
1367+
);
1368+
assert_eq!(locked_virtio_pci_device.cap_pci_cfg_info.cap, cap);
1369+
assert_eq!(next as u32, pci_config_cap_offset + cap.cap.cap_len as u32);
1370+
1371+
let msix_cap_offset = next as u32;
1372+
let (id, next, cap) = read_msix_cap(&mut locked_virtio_pci_device, msix_cap_offset);
1373+
assert_eq!(id, PciCapabilityId::MsiX);
1374+
assert_eq!(next, 0);
1375+
}
12071376
}

0 commit comments

Comments
 (0)