1
1
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
// SPDX-License-Identifier: Apache-2.0
3
3
4
- use std:: sync:: Arc ;
4
+ use std:: fmt:: Debug ;
5
+ use std:: sync:: { Arc , Mutex } ;
5
6
7
+ use event_manager:: MutEventSubscriber ;
8
+ use kvm_ioctls:: { IoEventAddress , NoDatamatch } ;
9
+ use log:: debug;
10
+ use pci:: { PciBarRegionType , PciDevice , PciDeviceError , PciRootError } ;
6
11
use serde:: { Deserialize , Serialize } ;
7
12
use vm_device:: BusError ;
8
13
9
- use super :: resources:: ResourceAllocator ;
14
+ use crate :: Vm ;
15
+ use crate :: device_manager:: resources:: ResourceAllocator ;
10
16
use 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 ;
11
20
12
21
#[ derive( Debug , Default ) ]
13
22
pub struct PciDevices {
@@ -21,6 +30,16 @@ pub enum PciManagerError {
21
30
ResourceAllocation ( #[ from] vm_allocator:: Error ) ,
22
31
/// Bus error: {0}
23
32
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 ) ,
24
43
}
25
44
26
45
impl PciDevices {
@@ -61,6 +80,106 @@ impl PciDevices {
61
80
62
81
Ok ( ( ) )
63
82
}
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
+ }
64
183
}
65
184
66
185
#[ derive( Default , Debug , Clone , Serialize , Deserialize ) ]
0 commit comments