@@ -181,6 +181,12 @@ 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(
185+ feature = "virtio-net" ,
186+ not( feature = "rtl8139" ) ,
187+ target_arch = "x86_64"
188+ ) ) ]
189+ pub ( crate ) msix_table : Option < VolatileRef < ' static , [ crate :: drivers:: pci:: MsixEntry ] > > ,
184190}
185191/// Wraps a [`CommonCfg`] in order to preserve
186192/// the original structure.
@@ -256,6 +262,19 @@ impl VqCfgHandler<'_> {
256262 . write ( addr. as_u64 ( ) . into ( ) ) ;
257263 }
258264
265+ #[ cfg( all(
266+ feature = "virtio-net" ,
267+ not( feature = "rtl8139" ) ,
268+ target_arch = "x86_64"
269+ ) ) ]
270+ pub fn set_msix_table_index ( & mut self , index : u16 ) {
271+ self . select_queue ( ) ;
272+ self . raw
273+ . as_mut_ptr ( )
274+ . queue_msix_vector ( )
275+ . write ( index. into ( ) ) ;
276+ }
277+
259278 pub fn notif_off ( & mut self ) -> u16 {
260279 self . select_queue ( ) ;
261280 self . raw . as_mut_ptr ( ) . queue_notify_off ( ) . read ( ) . to_ne ( )
@@ -509,43 +528,6 @@ impl PciBar {
509528 }
510529}
511530
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-
549531pub ( crate ) fn map_caps ( device : & PciDevice < PciConfigRegion > ) -> Result < UniCapsColl , VirtioError > {
550532 let device_id = device. device_id ( ) ;
551533
@@ -555,58 +537,106 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
555537 return Err ( VirtioError :: FromPci ( PciError :: NoCapPtr ( device_id) ) ) ;
556538 }
557539
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-
564540 let mut com_cfg = None ;
565541 let mut notif_cfg = None ;
566542 let mut isr_cfg = None ;
567543 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- }
544+ #[ cfg( all(
545+ feature = "virtio-net" ,
546+ not( feature = "rtl8139" ) ,
547+ target_arch = "x86_64"
548+ ) ) ]
549+ let mut msix_table = None ;
550+
551+ // Reads all PCI capabilities, starting at the capabilities list pointer from the
552+ // PCI device.
553+ //
554+ // Maps ONLY Virtio specific capabilities and the MSI-X capability , which allow to locate the actual capability
555+ // structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
556+ for capability in device. capabilities ( ) . unwrap ( ) {
557+ match capability {
558+ PciCapability :: Vendor ( addr) => {
559+ let cap = CapData :: read ( addr, device. access ( ) ) . unwrap ( ) ;
560+ if cap. cfg_type == CapCfgType :: Pci {
561+ continue ;
579562 }
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- ) ,
563+ let slot = cap. bar ;
564+ let Some ( ( addr, size) ) = device. memory_map_bar ( slot, true ) else {
565+ continue ;
566+ } ;
567+ let pci_cap = PciCap {
568+ bar : VirtioPciBar :: new ( slot, addr. as_u64 ( ) , size. try_into ( ) . unwrap ( ) ) ,
569+ dev_id : device_id,
570+ cap,
571+ } ;
572+ match pci_cap. cap . cfg_type {
573+ CapCfgType :: Common => {
574+ if com_cfg. is_none ( ) {
575+ match pci_cap. map_common_cfg ( ) {
576+ Some ( cap) => com_cfg = Some ( ComCfg :: new ( cap) ) ,
577+ None => error ! (
578+ "Common config capability of device {device_id:x} could not be mapped!"
579+ ) ,
580+ }
581+ }
588582 }
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- ) ,
583+ CapCfgType :: Notify => {
584+ if notif_cfg. is_none ( ) {
585+ match NotifCfg :: new ( & pci_cap) {
586+ Some ( notif) => notif_cfg = Some ( notif) ,
587+ None => error ! (
588+ "Notification config capability of device {device_id:x} could not be used!"
589+ ) ,
590+ }
591+ }
592+ }
593+ CapCfgType :: Isr => {
594+ if isr_cfg. is_none ( ) {
595+ match pci_cap. map_isr_status ( ) {
596+ Some ( isr_stat) => isr_cfg = Some ( IsrStatus :: new ( isr_stat) ) ,
597+ None => error ! (
598+ "ISR status config capability of device {device_id:x} could not be used!"
599+ ) ,
600+ }
601+ }
598602 }
603+ CapCfgType :: SharedMemory => {
604+ let cap_id = pci_cap. cap . id ;
605+ error ! (
606+ "Shared Memory config capability with id {cap_id} of device {device_id:x} could not be used!"
607+ ) ;
608+ }
609+ CapCfgType :: Device => dev_cfg_list. push ( pci_cap) ,
610+ _ => continue ,
599611 }
600612 }
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!"
613+ #[ cfg( all(
614+ feature = "virtio-net" ,
615+ not( feature = "rtl8139" ) ,
616+ target_arch = "x86_64"
617+ ) ) ]
618+ PciCapability :: MsiX ( mut msix_capability) => {
619+ // We prefer legacy interrupts
620+ if device. get_irq ( ) . is_some ( ) {
621+ continue ;
622+ }
623+ msix_capability. set_enabled ( true , device. access ( ) ) ;
624+ let ( base_addr, _) = device
625+ . memory_map_bar ( msix_capability. table_bar ( ) , true )
626+ . unwrap ( ) ;
627+ let table_ptr = NonNull :: slice_from_raw_parts (
628+ NonNull :: with_exposed_provenance (
629+ core:: num:: NonZero :: new (
630+ base_addr. as_usize ( )
631+ + usize:: try_from ( msix_capability. table_offset ( ) ) . unwrap ( ) ,
632+ )
633+ . unwrap ( ) ,
634+ ) ,
635+ msix_capability. table_size ( ) . into ( ) ,
605636 ) ;
637+ msix_table = Some ( unsafe { VolatileRef :: new ( table_ptr) } ) ;
606638 }
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
639+ // PCI's configuration space is allowed to hold other structures, which are not useful for us and are therefore ignored
610640 // in the following
611641 _ => continue ,
612642 }
@@ -617,6 +647,12 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
617647 notif_cfg : notif_cfg. ok_or ( VirtioError :: NoNotifCfg ( device_id) ) ?,
618648 isr_cfg : isr_cfg. ok_or ( VirtioError :: NoIsrCfg ( device_id) ) ?,
619649 dev_cfg_list,
650+ #[ cfg( all(
651+ feature = "virtio-net" ,
652+ not( feature = "rtl8139" ) ,
653+ target_arch = "x86_64"
654+ ) ) ]
655+ msix_table,
620656 } )
621657}
622658
@@ -686,9 +722,10 @@ pub(crate) fn init_device(
686722 ) ) ]
687723 virtio:: Id :: Net => match VirtioNetDriver :: init ( device) {
688724 Ok ( virt_net_drv) => {
725+ use crate :: drivers:: Driver ;
689726 info ! ( "Virtio network driver initialized." ) ;
690727
691- let irq = device . get_irq ( ) . unwrap ( ) ;
728+ let irq = virt_net_drv . get_interrupt_number ( ) ;
692729 crate :: arch:: interrupts:: add_irq_name ( irq, "virtio" ) ;
693730 info ! ( "Virtio interrupt handler at line {irq}" ) ;
694731
0 commit comments