@@ -924,9 +924,15 @@ impl PciConfiguration {
924924 region_type,
925925 } ) ;
926926 } else if ( reg_idx > BAR0_REG )
927- && ( ( self . registers [ reg_idx - 1 ] & self . writable_bits [ reg_idx - 1 ] )
928- != ( self . bars [ bar_idx - 1 ] . addr & self . writable_bits [ reg_idx - 1 ] )
929- || ( value & mask) != ( self . bars [ bar_idx] . addr & mask) )
927+ && (
928+ // The lower BAR (of this 64bit BAR) has been reprogrammed to a different value
929+ // than it used to be
930+ ( self . registers [ reg_idx - 1 ] & self . writable_bits [ reg_idx - 1 ] )
931+ != ( self . bars [ bar_idx - 1 ] . addr & self . writable_bits [ reg_idx - 1 ] ) ||
932+ // Or the lower BAR hasn't been changed but the upper one is being reprogrammed
933+ // now to a different value
934+ ( value & mask) != ( self . bars [ bar_idx] . addr & mask)
935+ )
930936 {
931937 info ! (
932938 "Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}" ,
@@ -1451,4 +1457,134 @@ mod tests {
14511457 assert_eq ! ( * reg, pci_config. read_reg( reg_idx) ) ;
14521458 }
14531459 }
1460+
1461+ #[ test]
1462+ fn test_detect_bar_reprogramming ( ) {
1463+ let mut pci_config = PciConfiguration :: new (
1464+ 0x42 ,
1465+ 0x0 ,
1466+ 0x0 ,
1467+ PciClassCode :: MassStorage ,
1468+ & PciMassStorageSubclass :: SerialScsiController ,
1469+ None ,
1470+ PciHeaderType :: Device ,
1471+ 0x13 ,
1472+ 0x12 ,
1473+ None ,
1474+ None ,
1475+ ) ;
1476+
1477+ // Trying to reprogram with something less than 4 bytes (length of the address) should fail
1478+ assert ! ( pci_config
1479+ . detect_bar_reprogramming( BAR0_REG , & [ 0x13 ] )
1480+ . is_none( ) ) ;
1481+ assert ! ( pci_config
1482+ . detect_bar_reprogramming( BAR0_REG , & [ 0x13 , 0x12 ] )
1483+ . is_none( ) ) ;
1484+ assert ! ( pci_config
1485+ . detect_bar_reprogramming( BAR0_REG , & [ 0x13 , 0x12 ] )
1486+ . is_none( ) ) ;
1487+ assert ! ( pci_config
1488+ . detect_bar_reprogramming( BAR0_REG , & [ 0x13 , 0x12 , 0x16 ] )
1489+ . is_none( ) ) ;
1490+
1491+ // Writing all 1s is a special case where we're actually asking for the size of the BAR
1492+ assert ! ( pci_config
1493+ . detect_bar_reprogramming( BAR0_REG , & u32 :: to_le_bytes( 0xffff_ffff ) )
1494+ . is_none( ) ) ;
1495+
1496+ // Trying to reprogram a BAR that hasn't be initialized does nothing
1497+ for reg_idx in BAR0_REG ..BAR0_REG + NUM_BAR_REGS {
1498+ assert ! ( pci_config
1499+ . detect_bar_reprogramming( reg_idx, & u32 :: to_le_bytes( 0x1312_4243 ) )
1500+ . is_none( ) ) ;
1501+ }
1502+
1503+ // Reprogramming of a 32bit BAR
1504+ pci_config
1505+ . add_pci_bar ( & PciBarConfiguration {
1506+ addr : 0x1000 ,
1507+ size : 0x1000 ,
1508+ idx : 0 ,
1509+ region_type : PciBarRegionType :: Memory32BitRegion ,
1510+ prefetchable : PciBarPrefetchable :: Prefetchable ,
1511+ } )
1512+ . unwrap ( ) ;
1513+
1514+ assert_eq ! (
1515+ pci_config. detect_bar_reprogramming( BAR0_REG , & u32 :: to_le_bytes( 0x2000 ) ) ,
1516+ Some ( BarReprogrammingParams {
1517+ old_base: 0x1000 ,
1518+ new_base: 0x2000 ,
1519+ len: 0x1000 ,
1520+ region_type: PciBarRegionType :: Memory32BitRegion
1521+ } )
1522+ ) ;
1523+
1524+ pci_config. write_config_register ( BAR0_REG , 0 , & u32:: to_le_bytes ( 0x2000 ) ) ;
1525+ assert_eq ! ( pci_config. read_reg( BAR0_REG ) & 0xffff_fff0 , 0x2000 ) ;
1526+
1527+ // Attempting to reprogram the BAR with the same address should not have any effect
1528+ assert ! ( pci_config
1529+ . detect_bar_reprogramming( BAR0_REG , & u32 :: to_le_bytes( 0x2000 ) )
1530+ . is_none( ) ) ;
1531+
1532+ // Reprogramming of a 64bit BAR
1533+ pci_config
1534+ . add_pci_bar ( & PciBarConfiguration {
1535+ addr : 0x13_1200_0000 ,
1536+ size : 0x8000 ,
1537+ idx : 1 ,
1538+ region_type : PciBarRegionType :: Memory64BitRegion ,
1539+ prefetchable : PciBarPrefetchable :: Prefetchable ,
1540+ } )
1541+ . unwrap ( ) ;
1542+
1543+ assert_eq ! ( pci_config. read_reg( BAR0_REG + 1 ) & 0xffff_fff0 , 0x1200_0000 ) ;
1544+ assert_eq ! (
1545+ pci_config. bars[ 1 ] . r#type,
1546+ Some ( PciBarRegionType :: Memory64BitRegion )
1547+ ) ;
1548+ assert_eq ! ( pci_config. read_reg( BAR0_REG + 2 ) , 0x13 ) ;
1549+ assert ! ( pci_config. bars[ 2 ] . r#type. is_none( ) ) ;
1550+
1551+ // First we write the lower 32 bits and this shouldn't cause any reprogramming
1552+ assert ! ( pci_config
1553+ . detect_bar_reprogramming( BAR0_REG + 1 , & u32 :: to_le_bytes( 0x4200_0000 ) )
1554+ . is_none( ) ) ;
1555+ pci_config. write_config_register ( BAR0_REG + 1 , 0 , & u32:: to_le_bytes ( 0x4200_0000 ) ) ;
1556+
1557+ // Writing the upper 32 bits should trigger the reprogramming
1558+ assert_eq ! (
1559+ pci_config. detect_bar_reprogramming( BAR0_REG + 2 , & u32 :: to_le_bytes( 0x84 ) ) ,
1560+ Some ( BarReprogrammingParams {
1561+ old_base: 0x13_1200_0000 ,
1562+ new_base: 0x84_4200_0000 ,
1563+ len: 0x8000 ,
1564+ region_type: PciBarRegionType :: Memory64BitRegion
1565+ } )
1566+ ) ;
1567+ pci_config. write_config_register ( BAR0_REG + 2 , 0 , & u32:: to_le_bytes ( 0x84 ) ) ;
1568+
1569+ // Trying to reprogram the upper bits directly (without first touching the lower bits)
1570+ // should trigger a reprogramming
1571+ assert_eq ! (
1572+ pci_config. detect_bar_reprogramming( BAR0_REG + 2 , & u32 :: to_le_bytes( 0x1312 ) ) ,
1573+ Some ( BarReprogrammingParams {
1574+ old_base: 0x84_4200_0000 ,
1575+ new_base: 0x1312_4200_0000 ,
1576+ len: 0x8000 ,
1577+ region_type: PciBarRegionType :: Memory64BitRegion
1578+ } )
1579+ ) ;
1580+ pci_config. write_config_register ( BAR0_REG + 2 , 0 , & u32:: to_le_bytes ( 0x1312 ) ) ;
1581+
1582+ // Attempting to reprogram the BAR with the same address should not have any effect
1583+ assert ! ( pci_config
1584+ . detect_bar_reprogramming( BAR0_REG + 1 , & u32 :: to_le_bytes( 0x4200_0000 ) )
1585+ . is_none( ) ) ;
1586+ assert ! ( pci_config
1587+ . detect_bar_reprogramming( BAR0_REG + 2 , & u32 :: to_le_bytes( 0x1312 ) )
1588+ . is_none( ) ) ;
1589+ }
14541590}
0 commit comments