@@ -571,10 +571,14 @@ impl VirtioPciDevice {
571
571
. unwrap ( ) ;
572
572
}
573
573
} 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
+ }
578
582
}
579
583
}
580
584
@@ -592,10 +596,16 @@ impl VirtioPciDevice {
592
596
right[ ..data_len] . copy_from_slice ( data) ;
593
597
None
594
598
} 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
+ }
599
609
}
600
610
}
601
611
@@ -771,8 +781,13 @@ impl PciDevice for VirtioPciDevice {
771
781
{
772
782
let offset = base - self . cap_pci_cfg_info . offset ;
773
783
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
+ }
776
791
} else {
777
792
self . configuration . read_reg ( reg_idx)
778
793
}
@@ -1373,4 +1388,76 @@ mod tests {
1373
1388
assert_eq ! ( id, PciCapabilityId :: MsiX ) ;
1374
1389
assert_eq ! ( next, 0 ) ;
1375
1390
}
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
+ }
1376
1463
}
0 commit comments