@@ -181,6 +181,8 @@ pub struct UniCapsColl {
181181 pub ( crate ) notif_cfg : NotifCfg ,
182182 pub ( crate ) isr_cfg : IsrStatus ,
183183 pub ( crate ) dev_cfg_list : Vec < PciCap > ,
184+ #[ cfg( feature = "msix" ) ]
185+ pub ( crate ) msix_table : Option < VolatileRef < ' static , [ crate :: drivers:: pci:: MsixTableEntry ] > > ,
184186}
185187/// Wraps a [`CommonCfg`] in order to preserve
186188/// the original structure.
@@ -256,6 +258,15 @@ impl VqCfgHandler<'_> {
256258 . write ( addr. as_u64 ( ) . into ( ) ) ;
257259 }
258260
261+ #[ cfg( feature = "msix" ) ]
262+ pub fn set_msix_table_index ( & mut self , index : u16 ) {
263+ self . select_queue ( ) ;
264+ self . raw
265+ . as_mut_ptr ( )
266+ . queue_msix_vector ( )
267+ . write ( index. into ( ) ) ;
268+ }
269+
259270 pub fn notif_off ( & mut self ) -> u16 {
260271 self . select_queue ( ) ;
261272 self . raw . as_mut_ptr ( ) . queue_notify_off ( ) . read ( ) . to_ne ( )
@@ -509,43 +520,6 @@ impl PciBar {
509520 }
510521}
511522
512- /// Reads all PCI capabilities, starting at the capabilities list pointer from the
513- /// PCI device.
514- ///
515- /// Returns ONLY Virtio specific capabilities, which allow to locate the actual capability
516- /// structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
517- fn read_caps ( device : & PciDevice < PciConfigRegion > ) -> Result < Vec < PciCap > , PciError > {
518- let device_id = device. device_id ( ) ;
519-
520- let capabilities = device
521- . capabilities ( )
522- . unwrap ( )
523- . filter_map ( |capability| match capability {
524- PciCapability :: Vendor ( capability) => Some ( capability) ,
525- _ => None ,
526- } )
527- . map ( |addr| CapData :: read ( addr, device. access ( ) ) . unwrap ( ) )
528- . filter ( |cap| cap. cfg_type != CapCfgType :: Pci )
529- . flat_map ( |cap| {
530- let slot = cap. bar ;
531- device
532- . memory_map_bar ( slot, true )
533- . map ( |( addr, size) | PciCap {
534- bar : VirtioPciBar :: new ( slot, addr. as_u64 ( ) , size. try_into ( ) . unwrap ( ) ) ,
535- dev_id : device_id,
536- cap,
537- } )
538- } )
539- . collect :: < Vec < _ > > ( ) ;
540-
541- if capabilities. is_empty ( ) {
542- error ! ( "No virtio capability found for device {device_id:x}" ) ;
543- return Err ( PciError :: NoVirtioCaps ( device_id) ) ;
544- }
545-
546- Ok ( capabilities)
547- }
548-
549523pub ( crate ) fn map_caps ( device : & PciDevice < PciConfigRegion > ) -> Result < UniCapsColl , VirtioError > {
550524 let device_id = device. device_id ( ) ;
551525
@@ -555,58 +529,94 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
555529 return Err ( VirtioError :: FromPci ( PciError :: NoCapPtr ( device_id) ) ) ;
556530 }
557531
558- // Get list of PciCaps pointing to capabilities
559- let cap_list = match read_caps ( device) {
560- Ok ( list) => list,
561- Err ( pci_error) => return Err ( VirtioError :: FromPci ( pci_error) ) ,
562- } ;
563-
564532 let mut com_cfg = None ;
565533 let mut notif_cfg = None ;
566534 let mut isr_cfg = None ;
567535 let mut dev_cfg_list = Vec :: new ( ) ;
568- // Map Caps in virtual memory
569- for pci_cap in cap_list {
570- match pci_cap. cap . cfg_type {
571- CapCfgType :: Common => {
572- if com_cfg. is_none ( ) {
573- match pci_cap. map_common_cfg ( ) {
574- Some ( cap) => com_cfg = Some ( ComCfg :: new ( cap) ) ,
575- None => error ! (
576- "Common config capability of device {device_id:x} could not be mapped!"
577- ) ,
578- }
536+ #[ cfg( feature = "msix" ) ]
537+ let mut msix_table = None ;
538+
539+ // Reads all PCI capabilities, starting at the capabilities list pointer from the
540+ // PCI device.
541+ //
542+ // Maps ONLY Virtio specific capabilities and the MSI-X capability , which allow to locate the actual capability
543+ // structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
544+ for capability in device. capabilities ( ) . unwrap ( ) {
545+ match capability {
546+ PciCapability :: Vendor ( addr) => {
547+ let cap = CapData :: read ( addr, device. access ( ) ) . unwrap ( ) ;
548+ if cap. cfg_type == CapCfgType :: Pci {
549+ continue ;
579550 }
580- }
581- CapCfgType :: Notify => {
582- if notif_cfg. is_none ( ) {
583- match NotifCfg :: new ( & pci_cap) {
584- Some ( notif) => notif_cfg = Some ( notif) ,
585- None => error ! (
586- "Notification config capability of device {device_id:x} could not be used!"
587- ) ,
551+ let slot = cap. bar ;
552+ let Some ( ( addr, size) ) = device. memory_map_bar ( slot, true ) else {
553+ continue ;
554+ } ;
555+ let pci_cap = PciCap {
556+ bar : VirtioPciBar :: new ( slot, addr. as_u64 ( ) , size. try_into ( ) . unwrap ( ) ) ,
557+ dev_id : device_id,
558+ cap,
559+ } ;
560+ match pci_cap. cap . cfg_type {
561+ CapCfgType :: Common => {
562+ if com_cfg. is_none ( ) {
563+ match pci_cap. map_common_cfg ( ) {
564+ Some ( cap) => com_cfg = Some ( ComCfg :: new ( cap) ) ,
565+ None => error ! (
566+ "Common config capability of device {device_id:x} could not be mapped!"
567+ ) ,
568+ }
569+ }
588570 }
589- }
590- }
591- CapCfgType :: Isr => {
592- if isr_cfg. is_none ( ) {
593- match pci_cap. map_isr_status ( ) {
594- Some ( isr_stat) => isr_cfg = Some ( IsrStatus :: new ( isr_stat) ) ,
595- None => error ! (
596- "ISR status config capability of device {device_id:x} could not be used!"
597- ) ,
571+ CapCfgType :: Notify => {
572+ if notif_cfg. is_none ( ) {
573+ match NotifCfg :: new ( & pci_cap) {
574+ Some ( notif) => notif_cfg = Some ( notif) ,
575+ None => error ! (
576+ "Notification config capability of device {device_id:x} could not be used!"
577+ ) ,
578+ }
579+ }
580+ }
581+ CapCfgType :: Isr => {
582+ if isr_cfg. is_none ( ) {
583+ match pci_cap. map_isr_status ( ) {
584+ Some ( isr_stat) => isr_cfg = Some ( IsrStatus :: new ( isr_stat) ) ,
585+ None => error ! (
586+ "ISR status config capability of device {device_id:x} could not be used!"
587+ ) ,
588+ }
589+ }
598590 }
591+ CapCfgType :: SharedMemory => {
592+ let cap_id = pci_cap. cap . id ;
593+ error ! (
594+ "Shared Memory config capability with id {cap_id} of device {device_id:x} could not be used!"
595+ ) ;
596+ }
597+ CapCfgType :: Device => dev_cfg_list. push ( pci_cap) ,
598+ _ => continue ,
599599 }
600600 }
601- CapCfgType :: SharedMemory => {
602- let cap_id = pci_cap. cap . id ;
603- error ! (
604- "Shared Memory config capability with id {cap_id} of device {device_id:x} could not be used!"
601+ #[ cfg( feature = "msix" ) ]
602+ PciCapability :: MsiX ( mut msix_capability) => {
603+ msix_capability. set_enabled ( true , device. access ( ) ) ;
604+ let ( base_addr, _) = device
605+ . memory_map_bar ( msix_capability. table_bar ( ) , true )
606+ . unwrap ( ) ;
607+ let table_ptr = NonNull :: slice_from_raw_parts (
608+ NonNull :: with_exposed_provenance (
609+ core:: num:: NonZero :: new (
610+ base_addr. as_usize ( )
611+ + usize:: try_from ( msix_capability. table_offset ( ) ) . unwrap ( ) ,
612+ )
613+ . unwrap ( ) ,
614+ ) ,
615+ msix_capability. table_size ( ) . into ( ) ,
605616 ) ;
617+ msix_table = Some ( unsafe { VolatileRef :: new ( table_ptr) } ) ;
606618 }
607- CapCfgType :: Device => dev_cfg_list. push ( pci_cap) ,
608-
609- // PCI's configuration space is allowed to hold other structures, which are not virtio specific and are therefore ignored
619+ // PCI's configuration space is allowed to hold other structures, which are not useful for us and are therefore ignored
610620 // in the following
611621 _ => continue ,
612622 }
@@ -617,6 +627,8 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
617627 notif_cfg : notif_cfg. ok_or ( VirtioError :: NoNotifCfg ( device_id) ) ?,
618628 isr_cfg : isr_cfg. ok_or ( VirtioError :: NoIsrCfg ( device_id) ) ?,
619629 dev_cfg_list,
630+ #[ cfg( feature = "msix" ) ]
631+ msix_table,
620632 } )
621633}
622634
@@ -686,9 +698,10 @@ pub(crate) fn init_device(
686698 ) ) ]
687699 virtio:: Id :: Net => match VirtioNetDriver :: init ( device) {
688700 Ok ( virt_net_drv) => {
701+ use crate :: drivers:: Driver ;
689702 info ! ( "Virtio network driver initialized." ) ;
690703
691- let irq = device . get_irq ( ) . unwrap ( ) ;
704+ let irq = virt_net_drv . get_interrupt_number ( ) ;
692705 crate :: arch:: interrupts:: add_irq_name ( irq, "virtio" ) ;
693706 info ! ( "Virtio interrupt handler at line {irq}" ) ;
694707
0 commit comments