@@ -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( all( feature = "virtio-net" , target_arch = "x86_64" ) ) ]
185+ pub ( crate ) msix_table : Option < VolatileRef < ' static , [ crate :: drivers:: pci:: MsixEntry ] > > ,
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( all( feature = "virtio-net" , target_arch = "x86_64" ) ) ]
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 ( )
@@ -515,43 +526,6 @@ impl PciBar {
515526 }
516527}
517528
518- /// Reads all PCI capabilities, starting at the capabilities list pointer from the
519- /// PCI device.
520- ///
521- /// Returns ONLY Virtio specific capabilities, which allow to locate the actual capability
522- /// structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
523- fn read_caps ( device : & PciDevice < PciConfigRegion > ) -> Result < Vec < PciCap > , PciError > {
524- let device_id = device. device_id ( ) ;
525-
526- let capabilities = device
527- . capabilities ( )
528- . unwrap ( )
529- . filter_map ( |capability| match capability {
530- PciCapability :: Vendor ( capability) => Some ( capability) ,
531- _ => None ,
532- } )
533- . map ( |addr| CapData :: read ( addr, device. access ( ) ) . unwrap ( ) )
534- . filter ( |cap| cap. cfg_type != CapCfgType :: Pci )
535- . flat_map ( |cap| {
536- let slot = cap. bar ;
537- device
538- . memory_map_bar ( slot, true )
539- . map ( |( addr, size) | PciCap {
540- bar : VirtioPciBar :: new ( slot, addr. as_u64 ( ) , size. try_into ( ) . unwrap ( ) ) ,
541- dev_id : device_id,
542- cap,
543- } )
544- } )
545- . collect :: < Vec < _ > > ( ) ;
546-
547- if capabilities. is_empty ( ) {
548- error ! ( "No virtio capability found for device {device_id:x}" ) ;
549- Err ( PciError :: NoVirtioCaps ( device_id) )
550- } else {
551- Ok ( capabilities)
552- }
553- }
554-
555529pub ( crate ) fn map_caps ( device : & PciDevice < PciConfigRegion > ) -> Result < UniCapsColl , VirtioError > {
556530 let device_id = device. device_id ( ) ;
557531
@@ -561,58 +535,98 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
561535 return Err ( VirtioError :: FromPci ( PciError :: NoCapPtr ( device_id) ) ) ;
562536 }
563537
564- // Get list of PciCaps pointing to capabilities
565- let cap_list = match read_caps ( device) {
566- Ok ( list) => list,
567- Err ( pci_error) => return Err ( VirtioError :: FromPci ( pci_error) ) ,
568- } ;
569-
570538 let mut com_cfg = None ;
571539 let mut notif_cfg = None ;
572540 let mut isr_cfg = None ;
573541 let mut dev_cfg_list = Vec :: new ( ) ;
574- // Map Caps in virtual memory
575- for pci_cap in cap_list {
576- match pci_cap. cap . cfg_type {
577- CapCfgType :: Common => {
578- if com_cfg. is_none ( ) {
579- match pci_cap. map_common_cfg ( ) {
580- Some ( cap) => com_cfg = Some ( ComCfg :: new ( cap) ) ,
581- None => error ! (
582- "Common config capability of device {device_id:x} could not be mapped!"
583- ) ,
584- }
542+ #[ cfg( all( feature = "virtio-net" , target_arch = "x86_64" ) ) ]
543+ let mut msix_table = None ;
544+
545+ // Reads all PCI capabilities, starting at the capabilities list pointer from the
546+ // PCI device.
547+ //
548+ // Maps ONLY Virtio specific capabilities and the MSI-X capability , which allow to locate the actual capability
549+ // structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
550+ for capability in device. capabilities ( ) . unwrap ( ) {
551+ match capability {
552+ PciCapability :: Vendor ( addr) => {
553+ let cap = CapData :: read ( addr, device. access ( ) ) . unwrap ( ) ;
554+ if cap. cfg_type == CapCfgType :: Pci {
555+ continue ;
585556 }
586- }
587- CapCfgType :: Notify => {
588- if notif_cfg. is_none ( ) {
589- match NotifCfg :: new ( & pci_cap) {
590- Some ( notif) => notif_cfg = Some ( notif) ,
591- None => error ! (
592- "Notification config capability of device {device_id:x} could not be used!"
593- ) ,
557+ let slot = cap. bar ;
558+ let Some ( ( addr, size) ) = device. memory_map_bar ( slot, true ) else {
559+ continue ;
560+ } ;
561+ let pci_cap = PciCap {
562+ bar : VirtioPciBar :: new ( slot, addr. as_u64 ( ) , size. try_into ( ) . unwrap ( ) ) ,
563+ dev_id : device_id,
564+ cap,
565+ } ;
566+ match pci_cap. cap . cfg_type {
567+ CapCfgType :: Common => {
568+ if com_cfg. is_none ( ) {
569+ match pci_cap. map_common_cfg ( ) {
570+ Some ( cap) => com_cfg = Some ( ComCfg :: new ( cap) ) ,
571+ None => error ! (
572+ "Common config capability of device {device_id:x} could not be mapped!"
573+ ) ,
574+ }
575+ }
594576 }
595- }
596- }
597- CapCfgType :: Isr => {
598- if isr_cfg. is_none ( ) {
599- match pci_cap. map_isr_status ( ) {
600- Some ( isr_stat) => isr_cfg = Some ( IsrStatus :: new ( isr_stat) ) ,
601- None => error ! (
602- "ISR status config capability of device {device_id:x} could not be used!"
603- ) ,
577+ CapCfgType :: Notify => {
578+ if notif_cfg. is_none ( ) {
579+ match NotifCfg :: new ( & pci_cap) {
580+ Some ( notif) => notif_cfg = Some ( notif) ,
581+ None => error ! (
582+ "Notification config capability of device {device_id:x} could not be used!"
583+ ) ,
584+ }
585+ }
586+ }
587+ CapCfgType :: Isr => {
588+ if isr_cfg. is_none ( ) {
589+ match pci_cap. map_isr_status ( ) {
590+ Some ( isr_stat) => isr_cfg = Some ( IsrStatus :: new ( isr_stat) ) ,
591+ None => error ! (
592+ "ISR status config capability of device {device_id:x} could not be used!"
593+ ) ,
594+ }
595+ }
596+ }
597+ CapCfgType :: SharedMemory => {
598+ let cap_id = pci_cap. cap . id ;
599+ error ! (
600+ "Shared Memory config capability with id {cap_id} of device {device_id:x} could not be used!"
601+ ) ;
604602 }
603+ CapCfgType :: Device => dev_cfg_list. push ( pci_cap) ,
604+ _ => continue ,
605605 }
606606 }
607- CapCfgType :: SharedMemory => {
608- let cap_id = pci_cap. cap . id ;
609- error ! (
610- "Shared Memory config capability with id {cap_id} of device {device_id:x} could not be used!"
607+ #[ cfg( all( feature = "virtio-net" , target_arch = "x86_64" ) ) ]
608+ PciCapability :: MsiX ( mut msix_capability) => {
609+ // We prefer legacy interrupts
610+ if device. get_irq ( ) . is_some ( ) {
611+ continue ;
612+ }
613+ msix_capability. set_enabled ( true , device. access ( ) ) ;
614+ let ( base_addr, _) = device
615+ . memory_map_bar ( msix_capability. table_bar ( ) , true )
616+ . unwrap ( ) ;
617+ let table_ptr = NonNull :: slice_from_raw_parts (
618+ NonNull :: with_exposed_provenance (
619+ core:: num:: NonZero :: new (
620+ base_addr. as_usize ( )
621+ + usize:: try_from ( msix_capability. table_offset ( ) ) . unwrap ( ) ,
622+ )
623+ . unwrap ( ) ,
624+ ) ,
625+ msix_capability. table_size ( ) . into ( ) ,
611626 ) ;
627+ msix_table = Some ( unsafe { VolatileRef :: new ( table_ptr) } ) ;
612628 }
613- CapCfgType :: Device => dev_cfg_list. push ( pci_cap) ,
614-
615- // PCI's configuration space is allowed to hold other structures, which are not virtio specific and are therefore ignored
629+ // PCI's configuration space is allowed to hold other structures, which are not useful for us and are therefore ignored
616630 // in the following
617631 _ => continue ,
618632 }
@@ -623,6 +637,8 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
623637 notif_cfg : notif_cfg. ok_or ( VirtioError :: NoNotifCfg ( device_id) ) ?,
624638 isr_cfg : isr_cfg. ok_or ( VirtioError :: NoIsrCfg ( device_id) ) ?,
625639 dev_cfg_list,
640+ #[ cfg( all( feature = "virtio-net" , target_arch = "x86_64" ) ) ]
641+ msix_table,
626642 } )
627643}
628644
@@ -690,9 +706,10 @@ pub(crate) fn init_device(
690706 ) ) ]
691707 virtio:: Id :: Net => match VirtioNetDriver :: init ( device) {
692708 Ok ( virt_net_drv) => {
709+ use crate :: drivers:: Driver ;
693710 info ! ( "Virtio network driver initialized." ) ;
694711
695- let irq = device . get_irq ( ) . unwrap ( ) ;
712+ let irq = virt_net_drv . get_interrupt_number ( ) ;
696713 crate :: arch:: interrupts:: add_irq_name ( irq, "virtio" ) ;
697714 info ! ( "Virtio interrupt handler at line {irq}" ) ;
698715
0 commit comments