@@ -706,6 +706,10 @@ impl PciConfiguration {
706706 let bar_idx = config. idx ;
707707 let reg_idx = BAR0_REG + bar_idx;
708708
709+ if bar_idx >= NUM_BAR_REGS {
710+ return Err ( Error :: BarInvalid ( bar_idx) ) ;
711+ }
712+
709713 if self . bars [ bar_idx] . used {
710714 return Err ( Error :: BarInUse ( bar_idx) ) ;
711715 }
@@ -714,10 +718,6 @@ impl PciConfiguration {
714718 return Err ( Error :: BarSizeInvalid ( config. size ) ) ;
715719 }
716720
717- if bar_idx >= NUM_BAR_REGS {
718- return Err ( Error :: BarInvalid ( bar_idx) ) ;
719- }
720-
721721 let end_addr = config
722722 . addr
723723 . checked_add ( config. size - 1 )
@@ -739,7 +739,7 @@ impl PciConfiguration {
739739 }
740740
741741 if self . bars [ bar_idx + 1 ] . used {
742- return Err ( Error :: BarInUse64 ( bar_idx) ) ;
742+ return Err ( Error :: BarInUse64 ( bar_idx + 1 ) ) ;
743743 }
744744
745745 // Encode the BAR size as expected by the software running in
@@ -1002,6 +1002,7 @@ impl Default for PciBarConfiguration {
10021002
10031003#[ cfg( test) ]
10041004mod tests {
1005+
10051006 use vm_memory:: ByteValued ;
10061007
10071008 use super :: * ;
@@ -1108,4 +1109,165 @@ mod tests {
11081109 assert_eq ! ( subclass, 0x01 ) ;
11091110 assert_eq ! ( prog_if, 0x5a ) ;
11101111 }
1112+
1113+ #[ test]
1114+ fn test_bar_size_encoding ( ) {
1115+ assert ! ( encode_32_bits_bar_size( 0 ) . is_none( ) ) ;
1116+ assert ! ( decode_32_bits_bar_size( 0 ) . is_none( ) ) ;
1117+ assert ! ( encode_64_bits_bar_size( 0 ) . is_none( ) ) ;
1118+ assert ! ( decode_64_bits_bar_size( 0 , 0 ) . is_none( ) ) ;
1119+
1120+ // According to OSDev wiki (https://wiki.osdev.org/PCI#Address_and_size_of_the_BAR):
1121+ //
1122+ // > To determine the amount of address space needed by a PCI device, you must save the
1123+ // > original value of the BAR, write a value of all 1's to the register, then read it back.
1124+ // > The amount of memory can then be determined by masking the information bits, performing
1125+ // > a bitwise NOT ('~' in C), and incrementing the value by 1. The original value of the
1126+ // BAR > should then be restored. The BAR register is naturally aligned and as such you can
1127+ // only > modify the bits that are set. For example, if a device utilizes 16 MB it will
1128+ // have BAR0 > filled with 0xFF000000 (0x1000000 after decoding) and you can only modify
1129+ // the upper > 8-bits.
1130+ //
1131+ // So we should be encoding an address like this: `addr` -> `!(addr - 1)`
1132+ let encoded = encode_32_bits_bar_size ( 0x0101_0101 ) . unwrap ( ) ;
1133+ assert_eq ! ( encoded, 0xfefe_feff ) ;
1134+ assert_eq ! ( decode_32_bits_bar_size( encoded) , Some ( 0x0101_0101 ) ) ;
1135+
1136+ // Similarly we encode a 64 bits size and then store it as a 2 32bit addresses (we use
1137+ // two BARs).
1138+ let ( hi, lo) = encode_64_bits_bar_size ( 0xffff_ffff_ffff_fff0 ) . unwrap ( ) ;
1139+ assert_eq ! ( hi, 0 ) ;
1140+ assert_eq ! ( lo, 0x0000_0010 ) ;
1141+ assert_eq ! ( decode_64_bits_bar_size( hi, lo) , Some ( 0xffff_ffff_ffff_fff0 ) ) ;
1142+ }
1143+
1144+ #[ test]
1145+ fn test_add_pci_bar ( ) {
1146+ let mut pci_config = PciConfiguration :: new (
1147+ 0x42 ,
1148+ 0x0 ,
1149+ 0x0 ,
1150+ PciClassCode :: MassStorage ,
1151+ & PciMassStorageSubclass :: SerialScsiController ,
1152+ None ,
1153+ PciHeaderType :: Device ,
1154+ 0x13 ,
1155+ 0x12 ,
1156+ None ,
1157+ None ,
1158+ ) ;
1159+
1160+ // BAR size can only be a power of 2
1161+ assert ! ( matches!(
1162+ pci_config. add_pci_bar( & PciBarConfiguration {
1163+ addr: 0x1000 ,
1164+ size: 0x1001 ,
1165+ idx: 0 ,
1166+ region_type: PciBarRegionType :: Memory32BitRegion ,
1167+ prefetchable: PciBarPrefetchable :: Prefetchable ,
1168+ } ) ,
1169+ Err ( Error :: BarSizeInvalid ( 0x1001 ) )
1170+ ) ) ;
1171+
1172+ // Invalid BAR index
1173+ assert ! ( matches!(
1174+ pci_config. add_pci_bar( & PciBarConfiguration {
1175+ addr: 0x1000 ,
1176+ size: 0x1000 ,
1177+ idx: NUM_BAR_REGS ,
1178+ region_type: PciBarRegionType :: Memory32BitRegion ,
1179+ prefetchable: PciBarPrefetchable :: Prefetchable
1180+ } ) ,
1181+ Err ( Error :: BarInvalid ( NUM_BAR_REGS ) )
1182+ ) ) ;
1183+ // 64bit BARs need 2 BAR slots actually
1184+ assert ! ( matches!(
1185+ pci_config. add_pci_bar( & PciBarConfiguration {
1186+ addr: 0x1000 ,
1187+ size: 0x1000 ,
1188+ idx: NUM_BAR_REGS - 1 ,
1189+ region_type: PciBarRegionType :: Memory64BitRegion ,
1190+ prefetchable: PciBarPrefetchable :: Prefetchable
1191+ } ) ,
1192+ Err ( Error :: BarInvalid64 ( _) )
1193+ ) ) ;
1194+
1195+ // Check for valid addresses
1196+ // Can't have an address that exceeds 32 bits for a 32bit BAR
1197+ assert ! ( matches!(
1198+ pci_config. add_pci_bar( & PciBarConfiguration {
1199+ addr: 0x1000_0000_0000_0000 ,
1200+ size: 0x1000 ,
1201+ idx: 0 ,
1202+ region_type: PciBarRegionType :: Memory32BitRegion ,
1203+ prefetchable: PciBarPrefetchable :: Prefetchable
1204+ } ) ,
1205+ Err ( Error :: BarAddressInvalid ( 0x1000_0000_0000_0000 , 0x1000 ) )
1206+ ) ) ;
1207+ // Ensure that we handle properly overflows in 64bit BAR ranges
1208+ assert ! ( matches!(
1209+ pci_config. add_pci_bar( & PciBarConfiguration {
1210+ addr: u64 :: MAX ,
1211+ size: 0x2 ,
1212+ idx: 0 ,
1213+ region_type: PciBarRegionType :: Memory64BitRegion ,
1214+ prefetchable: PciBarPrefetchable :: Prefetchable
1215+ } ) ,
1216+ Err ( Error :: BarAddressInvalid ( u64 :: MAX , 2 ) )
1217+ ) ) ;
1218+
1219+ // We can't reuse a BAR slot
1220+ pci_config
1221+ . add_pci_bar ( & PciBarConfiguration {
1222+ addr : 0x1000 ,
1223+ size : 0x1000 ,
1224+ idx : 0 ,
1225+ region_type : PciBarRegionType :: Memory32BitRegion ,
1226+ prefetchable : PciBarPrefetchable :: Prefetchable ,
1227+ } )
1228+ . unwrap ( ) ;
1229+ assert ! ( matches!(
1230+ pci_config. add_pci_bar( & PciBarConfiguration {
1231+ addr: 0x1000 ,
1232+ size: 0x1000 ,
1233+ idx: 0 ,
1234+ region_type: PciBarRegionType :: Memory32BitRegion ,
1235+ prefetchable: PciBarPrefetchable :: Prefetchable ,
1236+ } ) ,
1237+ Err ( Error :: BarInUse ( 0 ) )
1238+ ) ) ;
1239+ pci_config
1240+ . add_pci_bar ( & PciBarConfiguration {
1241+ addr : 0x0000_0001_0000_0000 ,
1242+ size : 0x2000 ,
1243+ idx : 2 ,
1244+ region_type : PciBarRegionType :: Memory64BitRegion ,
1245+ prefetchable : PciBarPrefetchable :: Prefetchable ,
1246+ } )
1247+ . unwrap ( ) ;
1248+ // For 64bit BARs two BARs are used (in this case BARs 1 and 2)
1249+ assert ! ( matches!(
1250+ pci_config. add_pci_bar( & PciBarConfiguration {
1251+ addr: 0x0000_0001_0000_0000 ,
1252+ size: 0x1000 ,
1253+ idx: 2 ,
1254+ region_type: PciBarRegionType :: Memory64BitRegion ,
1255+ prefetchable: PciBarPrefetchable :: Prefetchable ,
1256+ } ) ,
1257+ Err ( Error :: BarInUse ( 2 ) )
1258+ ) ) ;
1259+ assert ! ( matches!(
1260+ pci_config. add_pci_bar( & PciBarConfiguration {
1261+ addr: 0x0000_0001_0000_0000 ,
1262+ size: 0x1000 ,
1263+ idx: 1 ,
1264+ region_type: PciBarRegionType :: Memory64BitRegion ,
1265+ prefetchable: PciBarPrefetchable :: Prefetchable ,
1266+ } ) ,
1267+ Err ( Error :: BarInUse64 ( 2 ) )
1268+ ) ) ;
1269+
1270+ assert_eq ! ( pci_config. get_bar_addr( 0 ) , 0x1000 ) ;
1271+ assert_eq ! ( pci_config. get_bar_addr( 2 ) , 0x1_0000_0000 ) ;
1272+ }
11111273}
0 commit comments