@@ -74,7 +74,7 @@ enum PciCapabilityType {
7474const 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 ) ]
7878struct 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 ) ]
157157struct 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