@@ -190,7 +190,7 @@ pub trait PciProgrammingInterface {
190190}
191191
192192/// Types of PCI capabilities.
193- #[ derive( PartialEq , Eq , Copy , Clone ) ]
193+ #[ derive( Debug , PartialEq , Eq , Copy , Clone ) ]
194194#[ allow( dead_code) ]
195195#[ allow( non_camel_case_types) ]
196196#[ repr( u8 ) ]
@@ -474,8 +474,6 @@ pub enum Error {
474474 BarInvalid ( usize ) ,
475475 BarInvalid64 ( usize ) ,
476476 BarSizeInvalid ( u64 ) ,
477- CapabilityEmpty ,
478- CapabilityLengthInvalid ( usize ) ,
479477 CapabilitySpaceFull ( usize ) ,
480478 Decode32BarSize ,
481479 Decode64BarSize ,
@@ -505,8 +503,6 @@ impl Display for Error {
505503 NUM_BAR_REGS - 1
506504 ) ,
507505 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}" ) ,
510506 CapabilitySpaceFull ( s) => write ! ( f, "capability of size {s} doesn't fit" ) ,
511507 Decode32BarSize => write ! ( f, "failed to decode 32 bits BAR size" ) ,
512508 Decode64BarSize => write ! ( f, "failed to decode 64 bits BAR size" ) ,
@@ -789,15 +785,12 @@ impl PciConfiguration {
789785 }
790786
791787 /// 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()`.
795792 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 ;
801794 let ( cap_offset, tail_offset) = match self . last_capability {
802795 Some ( ( offset, len) ) => ( Self :: next_dword ( offset, len) , offset + 1 ) ,
803796 None => ( FIRST_CAPABILITY_OFFSET , CAPABILITY_LIST_HEAD_OFFSET ) ,
@@ -1006,6 +999,7 @@ mod tests {
1006999 use vm_memory:: ByteValued ;
10071000
10081001 use super :: * ;
1002+ use crate :: MsixCap ;
10091003
10101004 #[ repr( C , packed) ]
10111005 #[ derive( Clone , Copy , Default ) ]
@@ -1028,6 +1022,28 @@ mod tests {
10281022 }
10291023 }
10301024
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+
10311047 #[ test]
10321048 fn add_capability ( ) {
10331049 let mut cfg = PciConfiguration :: new (
@@ -1044,6 +1060,20 @@ mod tests {
10441060 None ,
10451061 ) ;
10461062
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+
10471077 // Add two capabilities with different contents.
10481078 let cap1 = TestCap { len : 4 , foo : 0xAA } ;
10491079 let cap1_offset = cfg. add_capability ( & cap1) . unwrap ( ) ;
@@ -1074,6 +1104,107 @@ mod tests {
10741104 assert_eq ! ( ( cap2_data >> 24 ) & 0xFF , 0x55 ) ; // cap2.foo
10751105 }
10761106
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+
10771208 #[ derive( Copy , Clone ) ]
10781209 enum TestPi {
10791210 Test = 0x5a ,
0 commit comments