@@ -571,10 +571,14 @@ impl VirtioPciDevice {
571571 . unwrap ( ) ;
572572 }
573573 } else {
574- let bar_offset: u32 =
575- // SAFETY: we know self.cap_pci_cfg_info.cap.cap.offset is 32bits long.
576- unsafe { std:: mem:: transmute ( self . cap_pci_cfg_info . cap . cap . offset ) } ;
577- self . read_bar ( 0 , bar_offset as u64 , data)
574+ let bar_offset: u32 = self . cap_pci_cfg_info . cap . cap . offset . into ( ) ;
575+ let len = u32:: from ( self . cap_pci_cfg_info . cap . cap . length ) as usize ;
576+ // BAR reads expect that the buffer has the exact size of the field that
577+ // offset is pointing to. So, do some check that the `length` has a meaningful value
578+ // and only use the part of the buffer we actually need.
579+ if len <= 4 {
580+ self . read_bar ( 0 , bar_offset as u64 , & mut data[ ..len] ) ;
581+ }
578582 }
579583 }
580584
@@ -592,10 +596,16 @@ impl VirtioPciDevice {
592596 right[ ..data_len] . copy_from_slice ( data) ;
593597 None
594598 } else {
595- let bar_offset: u32 =
596- // SAFETY: we know self.cap_pci_cfg_info.cap.cap.offset is 32bits long.
597- unsafe { std:: mem:: transmute ( self . cap_pci_cfg_info . cap . cap . offset ) } ;
598- self . write_bar ( 0 , bar_offset as u64 , data)
599+ let bar_offset: u32 = self . cap_pci_cfg_info . cap . cap . offset . into ( ) ;
600+ let len = u32:: from ( self . cap_pci_cfg_info . cap . cap . length ) as usize ;
601+ // BAR writes expect that the buffer has the exact size of the field that
602+ // offset is pointing to. So, do some check that the `length` has a meaningful value
603+ // and only use the part of the buffer we actually need.
604+ if len <= 4 {
605+ self . write_bar ( 0 , bar_offset as u64 , & data[ ..len] )
606+ } else {
607+ None
608+ }
599609 }
600610 }
601611
@@ -771,8 +781,13 @@ impl PciDevice for VirtioPciDevice {
771781 {
772782 let offset = base - self . cap_pci_cfg_info . offset ;
773783 let mut data = [ 0u8 ; 4 ] ;
774- self . read_cap_pci_cfg ( offset, & mut data) ;
775- u32:: from_le_bytes ( data)
784+ let len = u32:: from ( self . cap_pci_cfg_info . cap . cap . length ) as usize ;
785+ if len <= 4 {
786+ self . read_cap_pci_cfg ( offset, & mut data[ ..len] ) ;
787+ u32:: from_le_bytes ( data)
788+ } else {
789+ 0
790+ }
776791 } else {
777792 self . configuration . read_reg ( reg_idx)
778793 }
@@ -1373,4 +1388,76 @@ mod tests {
13731388 assert_eq ! ( id, PciCapabilityId :: MsiX ) ;
13741389 assert_eq ! ( next, 0 ) ;
13751390 }
1391+
1392+ fn cap_pci_cfg_read ( device : & mut VirtioPciDevice , bar_offset : u32 , length : u32 ) -> u32 {
1393+ let pci_config_cap_offset = capabilities_start ( device) as usize
1394+ + 3 * ( size_of :: < VirtioPciCap > ( ) + 2 )
1395+ + ( size_of :: < VirtioPciNotifyCap > ( ) + 2 ) ;
1396+
1397+ // To program the access through the PCI config capability mechanism, we need to write the
1398+ // bar offset and read length in the `VirtioPciCfgCap::cap.offset` and
1399+ // `VirtioPciCfgCap::length` fields. These are the third and fourth word respectively
1400+ // within the capability. The fifth word of the capability should contain the data
1401+ let offset_register = ( pci_config_cap_offset + 8 ) >> 2 ;
1402+ let length_register = ( pci_config_cap_offset + 12 ) >> 2 ;
1403+ let data_register = ( pci_config_cap_offset + 16 ) >> 2 ;
1404+
1405+ device. write_config_register ( offset_register, 0 , bar_offset. as_slice ( ) ) ;
1406+ device. write_config_register ( length_register, 0 , length. as_slice ( ) ) ;
1407+ device. read_config_register ( data_register)
1408+ }
1409+
1410+ fn cap_pci_cfg_write ( device : & mut VirtioPciDevice , bar_offset : u32 , length : u32 , data : u32 ) {
1411+ let pci_config_cap_offset = capabilities_start ( device) as usize
1412+ + 3 * ( size_of :: < VirtioPciCap > ( ) + 2 )
1413+ + ( size_of :: < VirtioPciNotifyCap > ( ) + 2 ) ;
1414+
1415+ // To program the access through the PCI config capability mechanism, we need to write the
1416+ // bar offset and read length in the `VirtioPciCfgCap::cap.offset` and
1417+ // `VirtioPciCfgCap::length` fields. These are the third and fourth word respectively
1418+ // within the capability. The fifth word of the capability should contain the data
1419+ let offset_register = ( pci_config_cap_offset + 8 ) >> 2 ;
1420+ let length_register = ( pci_config_cap_offset + 12 ) >> 2 ;
1421+ let data_register = ( pci_config_cap_offset + 16 ) >> 2 ;
1422+
1423+ device. write_config_register ( offset_register, 0 , bar_offset. as_slice ( ) ) ;
1424+ device. write_config_register ( length_register, 0 , length. as_slice ( ) ) ;
1425+ device. write_config_register ( data_register, 0 , data. as_slice ( ) ) ;
1426+ }
1427+
1428+ #[ test]
1429+ fn test_pci_configuration_cap ( ) {
1430+ let mut vmm = create_vmm_with_virtio_pci_device ( ) ;
1431+ let device = get_virtio_device ( & vmm) ;
1432+ let mut locked_virtio_pci_device = device. lock ( ) . unwrap ( ) ;
1433+
1434+ // Let's read the number of queues of the entropy device
1435+ // That information is located at offset 0x12 past the BAR region belonging to the common
1436+ // config capability.
1437+ let bar_offset = u32:: try_from ( COMMON_CONFIG_BAR_OFFSET ) . unwrap ( ) + 0x12 ;
1438+ let len = 2u32 ;
1439+ let num_queues = cap_pci_cfg_read ( & mut locked_virtio_pci_device, bar_offset, len) ;
1440+ assert_eq ! ( num_queues, 1 ) ;
1441+
1442+ // Let's update the driver features and see if that takes effect
1443+ let bar_offset = u32:: try_from ( COMMON_CONFIG_BAR_OFFSET ) . unwrap ( ) + 0x14 ;
1444+ let len = 1u32 ;
1445+ let device_status = cap_pci_cfg_read ( & mut locked_virtio_pci_device, bar_offset, len) ;
1446+ assert_eq ! ( device_status, 0 ) ;
1447+ cap_pci_cfg_write ( & mut locked_virtio_pci_device, bar_offset, len, 0x42 ) ;
1448+ let device_status = cap_pci_cfg_read ( & mut locked_virtio_pci_device, bar_offset, len) ;
1449+ assert_eq ! ( device_status, 0x42 ) ;
1450+
1451+ // reads with out-of-bounds lengths should return 0s
1452+ assert_eq ! (
1453+ cap_pci_cfg_read( & mut locked_virtio_pci_device, bar_offset, 8 ) ,
1454+ 0
1455+ ) ;
1456+ // writes out-of-bounds lengths should have no effect
1457+ cap_pci_cfg_write ( & mut locked_virtio_pci_device, bar_offset, 8 , 0x84 ) ;
1458+ assert_eq ! (
1459+ cap_pci_cfg_read( & mut locked_virtio_pci_device, bar_offset, 1 ) ,
1460+ 0x42
1461+ ) ;
1462+ }
13761463}
0 commit comments