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:: collections:: HashMap ;
5+ use std:: fmt:: Debug ;
6+ use std:: sync:: { Arc , Mutex } ;
57
8+ use event_manager:: MutEventSubscriber ;
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 {
1423 /// PCIe segment of the VMM, if PCI is enabled. We currently support a single PCIe segment.
1524 pub pci_segment : Option < PciSegment > ,
25+ /// All VirtIO PCI devices of the system
26+ pub virtio_devices : HashMap < ( u32 , String ) , Arc < Mutex < VirtioPciDevice > > > ,
1627}
1728
1829#[ derive( Debug , thiserror:: Error , displaydoc:: Display ) ]
@@ -21,6 +32,16 @@ pub enum PciManagerError {
2132 ResourceAllocation ( #[ from] vm_allocator:: Error ) ,
2233 /// Bus error: {0}
2334 Bus ( #[ from] BusError ) ,
35+ /// PCI root error: {0}
36+ PciRoot ( #[ from] PciRootError ) ,
37+ /// MSI error: {0}
38+ Msi ( #[ from] InterruptError ) ,
39+ /// VirtIO PCI device error: {0}
40+ VirtioPciDevice ( #[ from] VirtioPciDeviceError ) ,
41+ /// PCI device error: {0}
42+ PciDeviceError ( #[ from] PciDeviceError ) ,
43+ /// KVM error: {0}
44+ Kvm ( #[ from] vmm_sys_util:: errno:: Error ) ,
2445}
2546
2647impl PciDevices {
@@ -61,6 +82,113 @@ impl PciDevices {
6182
6283 Ok ( ( ) )
6384 }
85+
86+ fn register_bars_with_bus (
87+ resource_allocator : & ResourceAllocator ,
88+ virtio_device : & Arc < Mutex < VirtioPciDevice > > ,
89+ ) -> Result < ( ) , PciManagerError > {
90+ for bar in & virtio_device. lock ( ) . expect ( "Poisoned lock" ) . bar_regions {
91+ match bar. region_type ( ) {
92+ PciBarRegionType :: IoRegion => {
93+ debug ! (
94+ "Inserting I/O BAR region: {:#x}:{:#x}" ,
95+ bar. addr( ) ,
96+ bar. size( )
97+ ) ;
98+ #[ cfg( target_arch = "x86_64" ) ]
99+ resource_allocator. pio_bus . insert (
100+ virtio_device. clone ( ) ,
101+ bar. addr ( ) ,
102+ bar. size ( ) ,
103+ ) ?;
104+ #[ cfg( target_arch = "aarch64" ) ]
105+ log:: error!( "pci: We do not support I/O region allocation" )
106+ }
107+ PciBarRegionType :: Memory32BitRegion | PciBarRegionType :: Memory64BitRegion => {
108+ debug ! (
109+ "Inserting MMIO BAR region: {:#x}:{:#x}" ,
110+ bar. addr( ) ,
111+ bar. size( )
112+ ) ;
113+ resource_allocator. mmio_bus . insert (
114+ virtio_device. clone ( ) ,
115+ bar. addr ( ) ,
116+ bar. size ( ) ,
117+ ) ?;
118+ }
119+ }
120+ }
121+
122+ Ok ( ( ) )
123+ }
124+
125+ pub ( crate ) fn attach_pci_virtio_device <
126+ T : ' static + VirtioDevice + MutEventSubscriber + Debug ,
127+ > (
128+ & mut self ,
129+ vm : & Arc < Vm > ,
130+ resource_allocator : & ResourceAllocator ,
131+ id : String ,
132+ device : Arc < Mutex < T > > ,
133+ ) -> Result < ( ) , PciManagerError > {
134+ // We should only be reaching this point if PCI is enabled
135+ let pci_segment = self . pci_segment . as_ref ( ) . unwrap ( ) ;
136+ let pci_device_bdf = pci_segment. next_device_bdf ( ) ?;
137+ debug ! ( "Allocating BDF: {pci_device_bdf:?} for device" ) ;
138+ let mem = vm. guest_memory ( ) . clone ( ) ;
139+
140+ // Allocate one MSI vector per queue, plus one for configuration
141+ let msix_num =
142+ u16:: try_from ( device. lock ( ) . expect ( "Poisoned lock" ) . queues ( ) . len ( ) + 1 ) . unwrap ( ) ;
143+
144+ let msix_vectors = Arc :: new ( Vm :: create_msix_group (
145+ vm. clone ( ) ,
146+ resource_allocator,
147+ 0 ,
148+ msix_num,
149+ ) ?) ;
150+
151+ // Create the transport
152+ let mut virtio_device =
153+ VirtioPciDevice :: new ( id. clone ( ) , mem, device, msix_vectors, pci_device_bdf. into ( ) ) ?;
154+
155+ // Allocate bars
156+ let mut mmio32_allocator = resource_allocator
157+ . mmio32_memory
158+ . lock ( )
159+ . expect ( "Poisoned lock" ) ;
160+ let mut mmio64_allocator = resource_allocator
161+ . mmio64_memory
162+ . lock ( )
163+ . expect ( "Poisoned lock" ) ;
164+
165+ virtio_device. allocate_bars ( & mut mmio32_allocator, & mut mmio64_allocator, None ) ?;
166+
167+ let virtio_device = Arc :: new ( Mutex :: new ( virtio_device) ) ;
168+ pci_segment
169+ . pci_bus
170+ . lock ( )
171+ . expect ( "Poisoned lock" )
172+ . add_device ( pci_device_bdf. device ( ) as u32 , virtio_device. clone ( ) ) ?;
173+
174+ Self :: register_bars_with_bus ( resource_allocator, & virtio_device) ?;
175+ virtio_device
176+ . lock ( )
177+ . expect ( "Poisoned lock" )
178+ . register_notification_ioevent ( vm) ?;
179+
180+ Ok ( ( ) )
181+ }
182+
183+ /// Gets the specified device.
184+ pub fn get_virtio_device (
185+ & self ,
186+ device_type : u32 ,
187+ device_id : & str ,
188+ ) -> Option < & Arc < Mutex < VirtioPciDevice > > > {
189+ self . virtio_devices
190+ . get ( & ( device_type, device_id. to_string ( ) ) )
191+ }
64192}
65193
66194#[ derive( Default , Debug , Clone , Serialize , Deserialize ) ]
0 commit comments