11// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22// SPDX-License-Identifier: Apache-2.0
33
4- use std:: sync:: Arc ;
4+ use std:: fmt:: Debug ;
5+ use std:: sync:: { Arc , Mutex } ;
56
7+ use event_manager:: MutEventSubscriber ;
8+ use kvm_ioctls:: { IoEventAddress , NoDatamatch } ;
9+ use log:: debug;
10+ use pci:: { PciBarRegionType , PciDevice , PciDeviceError , PciRootError } ;
611use serde:: { Deserialize , Serialize } ;
712use vm_device:: BusError ;
813
9- use super :: resources:: ResourceAllocator ;
14+ use crate :: Vm ;
15+ use crate :: device_manager:: resources:: ResourceAllocator ;
1016use crate :: devices:: pci:: PciSegment ;
17+ use crate :: devices:: virtio:: device:: VirtioDevice ;
18+ use crate :: devices:: virtio:: transport:: pci:: device:: { VirtioPciDevice , VirtioPciDeviceError } ;
19+ use crate :: vstate:: vm:: InterruptError ;
1120
1221#[ derive( Debug , Default ) ]
1322pub struct PciDevices {
@@ -21,6 +30,16 @@ pub enum PciManagerError {
2130 ResourceAllocation ( #[ from] vm_allocator:: Error ) ,
2231 /// Bus error: {0}
2332 Bus ( #[ from] BusError ) ,
33+ /// PCI root error: {0}
34+ PciRoot ( #[ from] PciRootError ) ,
35+ /// MSI error: {0}
36+ Msi ( #[ from] InterruptError ) ,
37+ /// VirtIO PCI device error: {0}
38+ VirtioPciDevice ( #[ from] VirtioPciDeviceError ) ,
39+ /// PCI device error: {0}
40+ PciDeviceError ( #[ from] PciDeviceError ) ,
41+ /// KVM error: {0}
42+ Kvm ( #[ from] vmm_sys_util:: errno:: Error ) ,
2443}
2544
2645impl PciDevices {
@@ -61,6 +80,106 @@ impl PciDevices {
6180
6281 Ok ( ( ) )
6382 }
83+
84+ pub ( crate ) fn attach_pci_virtio_device <
85+ T : ' static + VirtioDevice + MutEventSubscriber + Debug ,
86+ > (
87+ & mut self ,
88+ vm : & Arc < Vm > ,
89+ resource_allocator : & ResourceAllocator ,
90+ id : String ,
91+ device : Arc < Mutex < T > > ,
92+ ) -> Result < ( ) , PciManagerError > {
93+ // We should only be reaching this point if PCI is enabled
94+ let pci_segment = self . pci_segment . as_ref ( ) . unwrap ( ) ;
95+ let pci_device_bdf = pci_segment. next_device_bdf ( ) ?;
96+ debug ! ( "Allocating BDF: {pci_device_bdf:?} for device" ) ;
97+ let mem = vm. guest_memory ( ) . clone ( ) ;
98+
99+ // Allocate one MSI vector per queue, plus one for configuration
100+ let msix_num =
101+ u16:: try_from ( device. lock ( ) . expect ( "Poisoned lock" ) . queues ( ) . len ( ) + 1 ) . unwrap ( ) ;
102+
103+ let msix_vectors = Arc :: new ( Vm :: create_msix_group (
104+ vm. clone ( ) ,
105+ resource_allocator,
106+ 0 ,
107+ msix_num,
108+ ) ?) ;
109+
110+ // Create the transport
111+ let mut virtio_device = VirtioPciDevice :: new (
112+ id. clone ( ) ,
113+ mem,
114+ device,
115+ msix_vectors,
116+ pci_device_bdf. into ( ) ,
117+ true ,
118+ None ,
119+ ) ?;
120+
121+ // Allocate bars
122+ let mut mmio32_allocator = resource_allocator
123+ . mmio32_memory
124+ . lock ( )
125+ . expect ( "Poisoned lock" ) ;
126+ let mut mmio64_allocator = resource_allocator
127+ . mmio64_memory
128+ . lock ( )
129+ . expect ( "Poisoned lock" ) ;
130+
131+ let bars =
132+ virtio_device. allocate_bars ( & mut mmio32_allocator, & mut mmio64_allocator, None ) ?;
133+
134+ let virtio_device = Arc :: new ( Mutex :: new ( virtio_device) ) ;
135+ pci_segment
136+ . pci_bus
137+ . lock ( )
138+ . expect ( "Poisoned lock" )
139+ . add_device ( pci_device_bdf. device ( ) as u32 , virtio_device. clone ( ) ) ?;
140+
141+ for bar in & bars {
142+ match bar. region_type ( ) {
143+ PciBarRegionType :: IoRegion => {
144+ #[ cfg( target_arch = "x86_64" ) ]
145+ resource_allocator. pio_bus . insert (
146+ virtio_device. clone ( ) ,
147+ bar. addr ( ) ,
148+ bar. size ( ) ,
149+ ) ?;
150+ #[ cfg( target_arch = "aarch64" ) ]
151+ log:: error!( "pci: We do not support I/O region allocation" )
152+ }
153+ PciBarRegionType :: Memory32BitRegion | PciBarRegionType :: Memory64BitRegion => {
154+ resource_allocator. mmio_bus . insert (
155+ virtio_device. clone ( ) ,
156+ bar. addr ( ) ,
157+ bar. size ( ) ,
158+ ) ?;
159+ }
160+ }
161+ }
162+
163+ let locked_device = virtio_device. lock ( ) . expect ( "Poisoned lock" ) ;
164+
165+ let bar_addr = locked_device. config_bar_addr ( ) ;
166+ for ( i, queue_evt) in locked_device
167+ . virtio_device ( )
168+ . lock ( )
169+ . expect ( "Poisoned lock" )
170+ . queue_events ( )
171+ . iter ( )
172+ . enumerate ( )
173+ {
174+ const NOTIFICATION_BAR_OFFSET : u64 = 0x6000 ;
175+ const NOTIFY_OFF_MULTIPLIER : u64 = 4 ;
176+ let notify_base = bar_addr + NOTIFICATION_BAR_OFFSET ;
177+ let io_addr = IoEventAddress :: Mmio ( notify_base + i as u64 * NOTIFY_OFF_MULTIPLIER ) ;
178+ vm. fd ( ) . register_ioevent ( queue_evt, & io_addr, NoDatamatch ) ?;
179+ }
180+
181+ Ok ( ( ) )
182+ }
64183}
65184
66185#[ derive( Default , Debug , Clone , Serialize , Deserialize ) ]
0 commit comments