@@ -706,6 +706,10 @@ impl PciConfiguration {
706
706
let bar_idx = config. idx ;
707
707
let reg_idx = BAR0_REG + bar_idx;
708
708
709
+ if bar_idx >= NUM_BAR_REGS {
710
+ return Err ( Error :: BarInvalid ( bar_idx) ) ;
711
+ }
712
+
709
713
if self . bars [ bar_idx] . used {
710
714
return Err ( Error :: BarInUse ( bar_idx) ) ;
711
715
}
@@ -714,10 +718,6 @@ impl PciConfiguration {
714
718
return Err ( Error :: BarSizeInvalid ( config. size ) ) ;
715
719
}
716
720
717
- if bar_idx >= NUM_BAR_REGS {
718
- return Err ( Error :: BarInvalid ( bar_idx) ) ;
719
- }
720
-
721
721
let end_addr = config
722
722
. addr
723
723
. checked_add ( config. size - 1 )
@@ -739,7 +739,7 @@ impl PciConfiguration {
739
739
}
740
740
741
741
if self . bars [ bar_idx + 1 ] . used {
742
- return Err ( Error :: BarInUse64 ( bar_idx) ) ;
742
+ return Err ( Error :: BarInUse64 ( bar_idx + 1 ) ) ;
743
743
}
744
744
745
745
// Encode the BAR size as expected by the software running in
@@ -1002,6 +1002,7 @@ impl Default for PciBarConfiguration {
1002
1002
1003
1003
#[ cfg( test) ]
1004
1004
mod tests {
1005
+
1005
1006
use vm_memory:: ByteValued ;
1006
1007
1007
1008
use super :: * ;
@@ -1108,4 +1109,165 @@ mod tests {
1108
1109
assert_eq ! ( subclass, 0x01 ) ;
1109
1110
assert_eq ! ( prog_if, 0x5a ) ;
1110
1111
}
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
+ }
1111
1273
}
0 commit comments