@@ -74,7 +74,7 @@ enum PciCapabilityType {
74
74
const VIRTIO_PCI_CAP_OFFSET : usize = 2 ;
75
75
76
76
#[ repr( C , packed) ]
77
- #[ derive( Debug , Clone , Copy , Default ) ]
77
+ #[ derive( Debug , Clone , Copy , Default , PartialEq , Eq ) ]
78
78
struct VirtioPciCap {
79
79
cap_len : u8 , // Generic PCI field: capability length
80
80
cfg_type : u8 , // Identifies the structure.
@@ -153,7 +153,7 @@ impl VirtioPciNotifyCap {
153
153
}
154
154
155
155
#[ repr( C , packed) ]
156
- #[ derive( Debug , Clone , Copy , Default ) ]
156
+ #[ derive( Debug , Clone , Copy , Default , PartialEq , Eq ) ]
157
157
struct VirtioPciCfgCap {
158
158
cap : VirtioPciCap ,
159
159
pci_cfg_data : [ u8 ; 4 ] ,
@@ -1002,19 +1002,27 @@ mod tests {
1002
1002
1003
1003
use event_manager:: MutEventSubscriber ;
1004
1004
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 } ;
1006
1009
1007
- use super :: VirtioPciDevice ;
1008
- use crate :: Vm ;
1010
+ use super :: { PciCapabilityType , VirtioPciDevice } ;
1009
1011
use crate :: arch:: MEM_64BIT_DEVICES_START ;
1010
1012
use crate :: builder:: tests:: default_vmm;
1011
1013
use crate :: devices:: virtio:: device:: VirtioDevice ;
1014
+ use crate :: devices:: virtio:: generated:: virtio_ids;
1012
1015
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
+ } ;
1014
1022
use crate :: rate_limiter:: RateLimiter ;
1023
+ use crate :: { Vm , Vmm } ;
1015
1024
1016
- #[ test]
1017
- fn test_pci_device_config ( ) {
1025
+ fn create_vmm_with_virtio_pci_device ( ) -> Vmm {
1018
1026
let mut vmm = default_vmm ( ) ;
1019
1027
vmm. device_manager . enable_pci ( & vmm. vm ) ;
1020
1028
let entropy = Arc :: new ( Mutex :: new ( Entropy :: new ( RateLimiter :: default ( ) ) . unwrap ( ) ) ) ;
@@ -1027,13 +1035,21 @@ mod tests {
1027
1035
false ,
1028
1036
)
1029
1037
. unwrap ( ) ;
1038
+ vmm
1039
+ }
1030
1040
1031
- let device = vmm
1032
- . device_manager
1041
+ fn get_virtio_device ( vmm : & Vmm ) -> Arc < Mutex < VirtioPciDevice > > {
1042
+ vmm . device_manager
1033
1043
. 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
+ }
1036
1048
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) ;
1037
1053
let mut locked_virtio_pci_device = device. lock ( ) . unwrap ( ) ;
1038
1054
1039
1055
// For more information for the values we are checking here look into the VirtIO spec here:
@@ -1133,25 +1149,8 @@ mod tests {
1133
1149
1134
1150
#[ test]
1135
1151
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) ;
1155
1154
let mut locked_virtio_pci_device = device. lock ( ) . unwrap ( ) ;
1156
1155
1157
1156
// According to OSdev wiki (https://wiki.osdev.org/PCI#Configuration_Space):
@@ -1204,4 +1203,174 @@ mod tests {
1204
1203
// We create a capabilities BAR region of 0x80000 bytes
1205
1204
assert_eq ! ( bar_size, 0x80000 ) ;
1206
1205
}
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
+ }
1207
1376
}
0 commit comments