@@ -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 ( )
@@ -515,43 +534,6 @@ impl PciBar {
515534 }
516535}
517536
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-
555537pub ( crate ) fn map_caps ( device : & PciDevice < PciConfigRegion > ) -> Result < UniCapsColl , VirtioError > {
556538 let device_id = device. device_id ( ) ;
557539
@@ -561,58 +543,106 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
561543 return Err ( VirtioError :: FromPci ( PciError :: NoCapPtr ( device_id) ) ) ;
562544 }
563545
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-
570546 let mut com_cfg = None ;
571547 let mut notif_cfg = None ;
572548 let mut isr_cfg = None ;
573549 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- }
550+ #[ cfg( all(
551+ feature = "virtio-net" ,
552+ not( feature = "rtl8139" ) ,
553+ target_arch = "x86_64"
554+ ) ) ]
555+ let mut msix_table = None ;
556+
557+ // Reads all PCI capabilities, starting at the capabilities list pointer from the
558+ // PCI device.
559+ //
560+ // Maps ONLY Virtio specific capabilities and the MSI-X capability , which allow to locate the actual capability
561+ // structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
562+ for capability in device. capabilities ( ) . unwrap ( ) {
563+ match capability {
564+ PciCapability :: Vendor ( addr) => {
565+ let cap = CapData :: read ( addr, device. access ( ) ) . unwrap ( ) ;
566+ if cap. cfg_type == CapCfgType :: Pci {
567+ continue ;
585568 }
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- ) ,
569+ let slot = cap. bar ;
570+ let Some ( ( addr, size) ) = device. memory_map_bar ( slot, true ) else {
571+ continue ;
572+ } ;
573+ let pci_cap = PciCap {
574+ bar : VirtioPciBar :: new ( slot, addr. as_u64 ( ) , size. try_into ( ) . unwrap ( ) ) ,
575+ dev_id : device_id,
576+ cap,
577+ } ;
578+ match pci_cap. cap . cfg_type {
579+ CapCfgType :: Common => {
580+ if com_cfg. is_none ( ) {
581+ match pci_cap. map_common_cfg ( ) {
582+ Some ( cap) => com_cfg = Some ( ComCfg :: new ( cap) ) ,
583+ None => error ! (
584+ "Common config capability of device {device_id:x} could not be mapped!"
585+ ) ,
586+ }
587+ }
594588 }
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- ) ,
589+ CapCfgType :: Notify => {
590+ if notif_cfg. is_none ( ) {
591+ match NotifCfg :: new ( & pci_cap) {
592+ Some ( notif) => notif_cfg = Some ( notif) ,
593+ None => error ! (
594+ "Notification config capability of device {device_id:x} could not be used!"
595+ ) ,
596+ }
597+ }
598+ }
599+ CapCfgType :: Isr => {
600+ if isr_cfg. is_none ( ) {
601+ match pci_cap. map_isr_status ( ) {
602+ Some ( isr_stat) => isr_cfg = Some ( IsrStatus :: new ( isr_stat) ) ,
603+ None => error ! (
604+ "ISR status config capability of device {device_id:x} could not be used!"
605+ ) ,
606+ }
607+ }
604608 }
609+ CapCfgType :: SharedMemory => {
610+ let cap_id = pci_cap. cap . id ;
611+ error ! (
612+ "Shared Memory config capability with id {cap_id} of device {device_id:x} could not be used!"
613+ ) ;
614+ }
615+ CapCfgType :: Device => dev_cfg_list. push ( pci_cap) ,
616+ _ => continue ,
605617 }
606618 }
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!"
619+ #[ cfg( all(
620+ feature = "virtio-net" ,
621+ not( feature = "rtl8139" ) ,
622+ target_arch = "x86_64"
623+ ) ) ]
624+ PciCapability :: MsiX ( mut msix_capability) => {
625+ // We prefer legacy interrupts
626+ if device. get_irq ( ) . is_some ( ) {
627+ continue ;
628+ }
629+ msix_capability. set_enabled ( true , device. access ( ) ) ;
630+ let ( base_addr, _) = device
631+ . memory_map_bar ( msix_capability. table_bar ( ) , true )
632+ . unwrap ( ) ;
633+ let table_ptr = NonNull :: slice_from_raw_parts (
634+ NonNull :: with_exposed_provenance (
635+ core:: num:: NonZero :: new (
636+ base_addr. as_usize ( )
637+ + usize:: try_from ( msix_capability. table_offset ( ) ) . unwrap ( ) ,
638+ )
639+ . unwrap ( ) ,
640+ ) ,
641+ msix_capability. table_size ( ) . into ( ) ,
611642 ) ;
643+ msix_table = Some ( unsafe { VolatileRef :: new ( table_ptr) } ) ;
612644 }
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
645+ // PCI's configuration space is allowed to hold other structures, which are not useful for us and are therefore ignored
616646 // in the following
617647 _ => continue ,
618648 }
@@ -623,6 +653,12 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
623653 notif_cfg : notif_cfg. ok_or ( VirtioError :: NoNotifCfg ( device_id) ) ?,
624654 isr_cfg : isr_cfg. ok_or ( VirtioError :: NoIsrCfg ( device_id) ) ?,
625655 dev_cfg_list,
656+ #[ cfg( all(
657+ feature = "virtio-net" ,
658+ not( feature = "rtl8139" ) ,
659+ target_arch = "x86_64"
660+ ) ) ]
661+ msix_table,
626662 } )
627663}
628664
@@ -690,9 +726,10 @@ pub(crate) fn init_device(
690726 ) ) ]
691727 virtio:: Id :: Net => match VirtioNetDriver :: init ( device) {
692728 Ok ( virt_net_drv) => {
729+ use crate :: drivers:: Driver ;
693730 info ! ( "Virtio network driver initialized." ) ;
694731
695- let irq = device . get_irq ( ) . unwrap ( ) ;
732+ let irq = virt_net_drv . get_interrupt_number ( ) ;
696733 crate :: arch:: interrupts:: add_irq_name ( irq, "virtio" ) ;
697734 info ! ( "Virtio interrupt handler at line {irq}" ) ;
698735
0 commit comments