@@ -190,7 +190,7 @@ pub trait PciProgrammingInterface {
190
190
}
191
191
192
192
/// Types of PCI capabilities.
193
- #[ derive( PartialEq , Eq , Copy , Clone ) ]
193
+ #[ derive( Debug , PartialEq , Eq , Copy , Clone ) ]
194
194
#[ allow( dead_code) ]
195
195
#[ allow( non_camel_case_types) ]
196
196
#[ repr( u8 ) ]
@@ -474,8 +474,6 @@ pub enum Error {
474
474
BarInvalid ( usize ) ,
475
475
BarInvalid64 ( usize ) ,
476
476
BarSizeInvalid ( u64 ) ,
477
- CapabilityEmpty ,
478
- CapabilityLengthInvalid ( usize ) ,
479
477
CapabilitySpaceFull ( usize ) ,
480
478
Decode32BarSize ,
481
479
Decode64BarSize ,
@@ -505,8 +503,6 @@ impl Display for Error {
505
503
NUM_BAR_REGS - 1
506
504
) ,
507
505
BarSizeInvalid ( s) => write ! ( f, "bar address {s} not a power of two" ) ,
508
- CapabilityEmpty => write ! ( f, "empty capabilities are invalid" ) ,
509
- CapabilityLengthInvalid ( l) => write ! ( f, "Invalid capability length {l}" ) ,
510
506
CapabilitySpaceFull ( s) => write ! ( f, "capability of size {s} doesn't fit" ) ,
511
507
Decode32BarSize => write ! ( f, "failed to decode 32 bits BAR size" ) ,
512
508
Decode64BarSize => write ! ( f, "failed to decode 64 bits BAR size" ) ,
@@ -789,15 +785,12 @@ impl PciConfiguration {
789
785
}
790
786
791
787
/// Adds the capability `cap_data` to the list of capabilities.
792
- /// `cap_data` should include the two-byte PCI capability header (type, next),
793
- /// but not populate it. Correct values will be generated automatically based
794
- /// on `cap_data.id()`.
788
+ ///
789
+ /// `cap_data` should not include the two-byte PCI capability header (type, next).
790
+ /// Correct values will be generated automatically based on `cap_data.id()` and
791
+ /// `cap_data.len()`.
795
792
pub fn add_capability ( & mut self , cap_data : & dyn PciCapability ) -> Result < usize > {
796
- let total_len = cap_data. bytes ( ) . len ( ) ;
797
- // Check that the length is valid.
798
- if cap_data. bytes ( ) . is_empty ( ) {
799
- return Err ( Error :: CapabilityEmpty ) ;
800
- }
793
+ let total_len = cap_data. bytes ( ) . len ( ) + 2 ;
801
794
let ( cap_offset, tail_offset) = match self . last_capability {
802
795
Some ( ( offset, len) ) => ( Self :: next_dword ( offset, len) , offset + 1 ) ,
803
796
None => ( FIRST_CAPABILITY_OFFSET , CAPABILITY_LIST_HEAD_OFFSET ) ,
@@ -1006,6 +999,7 @@ mod tests {
1006
999
use vm_memory:: ByteValued ;
1007
1000
1008
1001
use super :: * ;
1002
+ use crate :: MsixCap ;
1009
1003
1010
1004
#[ repr( C , packed) ]
1011
1005
#[ derive( Clone , Copy , Default ) ]
@@ -1028,6 +1022,28 @@ mod tests {
1028
1022
}
1029
1023
}
1030
1024
1025
+ struct BadCap {
1026
+ data : Vec < u8 > ,
1027
+ }
1028
+
1029
+ impl BadCap {
1030
+ fn new ( len : u8 ) -> Self {
1031
+ Self {
1032
+ data : ( 0 ..len) . collect ( ) ,
1033
+ }
1034
+ }
1035
+ }
1036
+
1037
+ impl PciCapability for BadCap {
1038
+ fn bytes ( & self ) -> & [ u8 ] {
1039
+ & self . data
1040
+ }
1041
+
1042
+ fn id ( & self ) -> PciCapabilityId {
1043
+ PciCapabilityId :: VendorSpecific
1044
+ }
1045
+ }
1046
+
1031
1047
#[ test]
1032
1048
fn add_capability ( ) {
1033
1049
let mut cfg = PciConfiguration :: new (
@@ -1044,6 +1060,20 @@ mod tests {
1044
1060
None ,
1045
1061
) ;
1046
1062
1063
+ // Bad size capabilities
1064
+ assert ! ( matches!(
1065
+ cfg. add_capability( & BadCap :: new( 127 ) ) ,
1066
+ Err ( Error :: CapabilitySpaceFull ( 129 ) )
1067
+ ) ) ;
1068
+ cfg. add_capability ( & BadCap :: new ( 62 ) ) . unwrap ( ) ;
1069
+ cfg. add_capability ( & BadCap :: new ( 62 ) ) . unwrap ( ) ;
1070
+ assert ! ( matches!(
1071
+ cfg. add_capability( & BadCap :: new( 0 ) ) ,
1072
+ Err ( Error :: CapabilitySpaceFull ( 2 ) )
1073
+ ) ) ;
1074
+ // Reset capabilities
1075
+ cfg. last_capability = None ;
1076
+
1047
1077
// Add two capabilities with different contents.
1048
1078
let cap1 = TestCap { len : 4 , foo : 0xAA } ;
1049
1079
let cap1_offset = cfg. add_capability ( & cap1) . unwrap ( ) ;
@@ -1074,6 +1104,107 @@ mod tests {
1074
1104
assert_eq ! ( ( cap2_data >> 24 ) & 0xFF , 0x55 ) ; // cap2.foo
1075
1105
}
1076
1106
1107
+ #[ test]
1108
+ fn test_msix_capability ( ) {
1109
+ let mut cfg = PciConfiguration :: new (
1110
+ 0x1234 ,
1111
+ 0x5678 ,
1112
+ 0x1 ,
1113
+ PciClassCode :: MultimediaController ,
1114
+ & PciMultimediaSubclass :: AudioController ,
1115
+ None ,
1116
+ PciHeaderType :: Device ,
1117
+ 0xABCD ,
1118
+ 0x2468 ,
1119
+ None ,
1120
+ None ,
1121
+ ) ;
1122
+
1123
+ // Information about the MSI-X capability layout: https://wiki.osdev.org/PCI#Enabling_MSI-X
1124
+ let msix_cap = MsixCap :: new (
1125
+ 3 , // Using BAR3 for message control table
1126
+ 1024 , // 1024 MSI-X vectors
1127
+ 0x4000 , // Offset of message control table inside the BAR
1128
+ 4 , // BAR4 used for pending control bit
1129
+ 0x420 , // Offset of pending bit array (PBA) inside BAR
1130
+ ) ;
1131
+ cfg. add_capability ( & msix_cap) . unwrap ( ) ;
1132
+
1133
+ let cap_reg = FIRST_CAPABILITY_OFFSET / 4 ;
1134
+ let reg = cfg. read_reg ( cap_reg) ;
1135
+ // Capability ID is MSI-X
1136
+ assert_eq ! (
1137
+ PciCapabilityId :: from( ( reg & 0xff ) as u8 ) ,
1138
+ PciCapabilityId :: MsiX
1139
+ ) ;
1140
+ // We only have one capability, so `next` should be 0
1141
+ assert_eq ! ( ( ( reg >> 8 ) & 0xff ) as u8 , 0 ) ;
1142
+ let msg_ctl = ( reg >> 16 ) as u16 ;
1143
+
1144
+ // MSI-X is enabled
1145
+ assert_eq ! ( msg_ctl & 0x8000 , 0x8000 ) ;
1146
+ // Vectors are not masked
1147
+ assert_eq ! ( msg_ctl & 0x4000 , 0x0 ) ;
1148
+ // Reserved bits are 0
1149
+ assert_eq ! ( msg_ctl & 0x3800 , 0x0 ) ;
1150
+ // We've got 1024 vectors (Table size is N-1 encoded)
1151
+ assert_eq ! ( ( msg_ctl & 0x7ff ) + 1 , 1024 ) ;
1152
+
1153
+ let reg = cfg. read_reg ( cap_reg + 1 ) ;
1154
+ // We are using BAR3
1155
+ assert_eq ! ( reg & 0x7 , 3 ) ;
1156
+ // Message Control Table is located in offset 0x4000 inside the BAR
1157
+ // We don't need to shift. Offset needs to be 8-byte aligned - so BIR
1158
+ // is stored in its last 3 bits (which we need to mask out).
1159
+ assert_eq ! ( reg & 0xffff_fff8 , 0x4000 ) ;
1160
+
1161
+ let reg = cfg. read_reg ( cap_reg + 2 ) ;
1162
+ // PBA is 0x420 bytes inside BAR4
1163
+ assert_eq ! ( reg & 0x7 , 4 ) ;
1164
+ assert_eq ! ( reg & 0xffff_fff8 , 0x420 ) ;
1165
+
1166
+ // Check read/write mask
1167
+ // Capability Id of MSI-X is 0x11
1168
+ cfg. write_config_register ( cap_reg, 0 , & [ 0x0 ] ) ;
1169
+ assert_eq ! (
1170
+ PciCapabilityId :: from( ( cfg. read_reg( cap_reg) & 0xff ) as u8 ) ,
1171
+ PciCapabilityId :: MsiX
1172
+ ) ;
1173
+ // Cannot override next capability pointer
1174
+ cfg. write_config_register ( cap_reg, 1 , & [ 0x42 ] ) ;
1175
+ assert_eq ! ( ( cfg. read_reg( cap_reg) >> 8 ) & 0xff , 0 ) ;
1176
+
1177
+ // We are writing this:
1178
+ //
1179
+ // meaning: | MSI enabled | Vectors Masked | Reserved | Table size |
1180
+ // bit: | 15 | 14 | 13 - 11 | 0 - 10 |
1181
+ // R/W: | R/W | R/W | R | R |
1182
+ let msg_ctl = ( cfg. read_reg ( cap_reg) >> 16 ) as u16 ;
1183
+ // Try to flip all bits
1184
+ cfg. write_config_register ( cap_reg, 2 , & u16:: to_le_bytes ( !msg_ctl) ) ;
1185
+ let msg_ctl = ( cfg. read_reg ( cap_reg) >> 16 ) as u16 ;
1186
+ // MSI enabled and Vectors masked should be flipped (MSI disabled and vectors masked)
1187
+ assert_eq ! ( msg_ctl & 0xc000 , 0x4000 ) ;
1188
+ // Reserved bits should still be 0
1189
+ assert_eq ! ( msg_ctl & 0x3800 , 0 ) ;
1190
+ // Table size should not have changed
1191
+ assert_eq ! ( ( msg_ctl & 0x07ff ) + 1 , 1024 ) ;
1192
+
1193
+ // Table offset is read only
1194
+ let table_offset = cfg. read_reg ( cap_reg + 1 ) ;
1195
+ // Try to flip all bits
1196
+ cfg. write_config_register ( cap_reg + 1 , 0 , & u32:: to_le_bytes ( !table_offset) ) ;
1197
+ // None should be flipped
1198
+ assert_eq ! ( cfg. read_reg( cap_reg + 1 ) , table_offset) ;
1199
+
1200
+ // PBA offset also
1201
+ let pba_offset = cfg. read_reg ( cap_reg + 2 ) ;
1202
+ // Try to flip all bits
1203
+ cfg. write_config_register ( cap_reg + 2 , 0 , & u32:: to_le_bytes ( !pba_offset) ) ;
1204
+ // None should be flipped
1205
+ assert_eq ! ( cfg. read_reg( cap_reg + 2 ) , pba_offset) ;
1206
+ }
1207
+
1077
1208
#[ derive( Copy , Clone ) ]
1078
1209
enum TestPi {
1079
1210
Test = 0x5a ,
0 commit comments