1use std::fs::File;
6use std::sync::Arc;
7
8use anyhow::bail;
9use anyhow::Context;
10use anyhow::Result;
11use base::error;
12use base::pagesize;
13use base::AsRawDescriptor;
14use base::AsRawDescriptors;
15use base::Event;
16use base::MappedRegion;
17use base::MemoryMapping;
18use base::MemoryMappingBuilder;
19use base::Protection;
20use base::RawDescriptor;
21use hypervisor::MemCacheType;
22use hypervisor::Vm;
23use resources::SystemAllocator;
24use vfio_sys::*;
25use vm_control::api::VmMemoryClient;
26use vm_control::DeviceId;
27use vm_control::PlatformDeviceId;
28use vm_control::VmMemoryDestination;
29use vm_control::VmMemorySource;
30use vm_memory::GuestAddress;
31
32use crate::vfio::VfioDevice;
33use crate::vfio::VfioError;
34use crate::vfio::VfioIrq;
35use crate::BusAccessInfo;
36use crate::BusDevice;
37use crate::BusDeviceObj;
38use crate::IommuDevType;
39use crate::IrqEdgeEvent;
40use crate::IrqLevelEvent;
41use crate::Suspendable;
42
43struct MmioInfo {
44 index: usize,
45 start: u64,
46 length: u64,
47}
48
49pub struct VfioPlatformDevice {
50 device: Arc<VfioDevice>,
51 interrupt_edge_evt: Vec<IrqEdgeEvent>,
52 interrupt_level_evt: Vec<IrqLevelEvent>,
53 mmio_regions: Vec<MmioInfo>,
54 vm_memory_client: VmMemoryClient,
55 mem: Vec<MemoryMapping>,
57}
58
59impl BusDevice for VfioPlatformDevice {
60 fn device_id(&self) -> DeviceId {
61 PlatformDeviceId::VfioPlatformDevice.into()
62 }
63
64 fn debug_label(&self) -> String {
65 format!("vfio {} device", self.device.device_name())
66 }
67
68 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
69 self.read_mmio(info.address, data)
70 }
71
72 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
73 self.write_mmio(info.address, data)
74 }
75
76 fn supports_power_management(&self) -> anyhow::Result<bool> {
77 Ok(self.device.supports_pm_low_power())
78 }
79
80 fn power_on(&mut self) -> anyhow::Result<()> {
81 self.device.pm_low_power_exit()?;
82 Ok(())
83 }
84
85 fn power_off(&mut self) -> anyhow::Result<()> {
86 self.device.pm_low_power_enter()?;
87 Ok(())
88 }
89}
90
91impl Suspendable for VfioPlatformDevice {}
92
93impl BusDeviceObj for VfioPlatformDevice {
94 fn as_platform_device(&self) -> Option<&VfioPlatformDevice> {
95 Some(self)
96 }
97 fn as_platform_device_mut(&mut self) -> Option<&mut VfioPlatformDevice> {
98 Some(self)
99 }
100 fn into_platform_device(self: Box<Self>) -> Option<Box<VfioPlatformDevice>> {
101 Some(self)
102 }
103}
104
105impl VfioPlatformDevice {
106 pub fn new(device: VfioDevice, vm_memory_client: VmMemoryClient) -> Self {
108 let dev = Arc::new(device);
109 VfioPlatformDevice {
110 device: dev,
111 interrupt_edge_evt: Vec::new(),
112 interrupt_level_evt: Vec::new(),
113 mmio_regions: Vec::new(),
114 vm_memory_client,
115 mem: Vec::new(),
116 }
117 }
118
119 pub fn get_platform_irqs(&self) -> Result<Vec<VfioIrq>, VfioError> {
120 self.device.get_irqs()
121 }
122
123 pub fn irq_is_automask(&self, irq: &VfioIrq) -> bool {
124 irq.flags & VFIO_IRQ_INFO_AUTOMASKED != 0
125 }
126
127 fn setup_irq_resample(&mut self, resample_evt: &Event, index: u32) -> Result<()> {
128 self.device.irq_mask(index).context("Intx mask failed")?;
129 self.device
130 .resample_virq_enable(resample_evt, index)
131 .context("resample enable failed")?;
132 self.device
133 .irq_unmask(index)
134 .context("Intx unmask failed")?;
135 Ok(())
136 }
137
138 pub fn assign_edge_platform_irq(&mut self, irq_evt: &IrqEdgeEvent, index: u32) -> Result<()> {
139 let interrupt_evt = irq_evt.try_clone().context("failed to clone irq event")?;
140 self.device
141 .irq_enable(&[Some(interrupt_evt.get_trigger())], index, 0)
142 .context("platform irq enable failed")?;
143 self.interrupt_edge_evt.push(interrupt_evt);
144 Ok(())
145 }
146
147 pub fn assign_level_platform_irq(&mut self, irq_evt: &IrqLevelEvent, index: u32) -> Result<()> {
148 let interrupt_evt = irq_evt.try_clone().context("failed to clone irq event")?;
149 self.device
150 .irq_enable(&[Some(interrupt_evt.get_trigger())], index, 0)
151 .context("platform irq enable failed")?;
152 if let Err(e) = self.setup_irq_resample(interrupt_evt.get_resample(), index) {
153 self.disable_irqs(index);
154 bail!("failed to set up irq resampling: {}", e);
155 }
156 self.interrupt_level_evt.push(interrupt_evt);
157 Ok(())
158 }
159
160 fn find_region(&self, addr: u64) -> Option<MmioInfo> {
161 for mmio_info in self.mmio_regions.iter() {
162 if addr >= mmio_info.start && addr < mmio_info.start + mmio_info.length {
163 return Some(MmioInfo {
164 index: mmio_info.index,
165 start: mmio_info.start,
166 length: mmio_info.length,
167 });
168 }
169 }
170 None
171 }
172
173 pub fn allocate_regions(
174 &mut self,
175 resources: &mut SystemAllocator,
176 ) -> Result<Vec<(u64, u64)>, resources::Error> {
177 let mut ranges = Vec::new();
178 for i in 0..self.device.get_region_count() {
179 let size = self.device.get_region_size(i);
180 let alloc_id = resources.get_anon_alloc();
181 let allocator = resources
182 .mmio_platform_allocator()
183 .ok_or(resources::Error::MissingPlatformMMIOAddresses)?;
184 let start_addr = allocator.allocate_with_align(
185 size,
186 alloc_id,
187 "vfio_mmio".to_string(),
188 pagesize() as u64,
189 )?;
190 ranges.push((start_addr, size));
191
192 self.mmio_regions.push(MmioInfo {
193 index: i,
194 start: start_addr,
195 length: size,
196 });
197 }
198 Ok(ranges)
199 }
200
201 fn region_mmap_early(&self, vm: &mut impl Vm, index: usize, start_addr: u64) {
202 if self.device.get_region_flags(index) & VFIO_REGION_INFO_FLAG_MMAP == 0 {
203 return;
204 }
205
206 for mmap in &self.device.get_region_mmap(index) {
207 let mmap_offset = mmap.offset;
208 let mmap_size = mmap.size;
209 let guest_map_start = start_addr + mmap_offset;
210 let region_offset = self.device.get_region_offset(index);
211 let offset = region_offset + mmap_offset;
212
213 let mmap = match MemoryMappingBuilder::new(mmap_size as usize)
214 .from_file(self.device.device_file())
215 .offset(offset)
216 .build()
217 {
218 Ok(v) => v,
219 Err(e) => {
220 error!("{e}, index: {index}, start_addr:{start_addr:#x}, offset:{offset:#x}");
221 break;
222 }
223 };
224
225 let host = mmap.as_ptr();
226 let guest_addr = GuestAddress(guest_map_start);
227 if let Err(e) = vm.add_memory_region(
228 guest_addr,
229 Box::new(mmap),
230 false,
231 false,
232 MemCacheType::CacheCoherent,
233 ) {
234 error!("{e}, index: {index}, guest_addr:{guest_addr}, host:{host:?}");
235 break;
236 }
237 }
238 }
239
240 pub fn regions_mmap_early(&mut self, vm: &mut impl Vm) {
246 for mmio_info in self.mmio_regions.iter() {
247 self.region_mmap_early(vm, mmio_info.index, mmio_info.start);
248 }
249 }
250
251 fn region_mmap(&self, index: usize, start_addr: u64) -> Vec<MemoryMapping> {
252 let mut mem_map: Vec<MemoryMapping> = Vec::new();
253 if self.device.get_region_flags(index) & VFIO_REGION_INFO_FLAG_MMAP != 0 {
254 let mmaps = self.device.get_region_mmap(index);
255 if mmaps.is_empty() {
256 return mem_map;
257 }
258
259 for mmap in mmaps.iter() {
260 let mmap_offset = mmap.offset;
261 let mmap_size = mmap.size;
262 let guest_map_start = start_addr + mmap_offset;
263 let region_offset = self.device.get_region_offset(index);
264 let offset = region_offset + mmap_offset;
265 let descriptor = match self.device.device_file().try_clone() {
266 Ok(device_file) => device_file.into(),
267 Err(_) => break,
268 };
269 match self.vm_memory_client.register_memory(
270 VmMemorySource::Descriptor {
271 descriptor,
272 offset,
273 size: mmap_size,
274 },
275 VmMemoryDestination::GuestPhysicalAddress(guest_map_start),
276 Protection::read_write(),
277 MemCacheType::CacheCoherent,
278 ) {
279 Ok(_region) => {
280 let mmap = match MemoryMappingBuilder::new(mmap_size as usize)
284 .from_file(self.device.device_file())
285 .offset(offset)
286 .build()
287 {
288 Ok(v) => v,
289 Err(_e) => break,
290 };
291 let host = mmap.as_ptr() as u64;
292 match unsafe {
297 self.device
298 .vfio_dma_map(guest_map_start, mmap_size, host, true)
299 } {
300 Ok(_) => mem_map.push(mmap),
301 Err(e) => {
302 error!(
303 "{}, index: {}, start_addr:0x{:x}, host:0x{:x}",
304 e, index, start_addr, host
305 );
306 break;
307 }
308 }
309 }
310 Err(e) => {
311 error!("register_memory failed: {}", e);
312 break;
313 }
314 }
315 }
316 }
317
318 mem_map
319 }
320
321 fn regions_mmap(&mut self) {
322 for mmio_info in self.mmio_regions.iter() {
323 let mut mem_map = self.region_mmap(mmio_info.index, mmio_info.start);
324 self.mem.append(&mut mem_map);
325 }
326 }
327
328 fn disable_irqs(&mut self, index: u32) {
329 if let Err(e) = self.device.irq_disable(index) {
330 error!("Platform irq disable failed: {}", e);
331 }
332 }
333
334 fn read_mmio(&mut self, addr: u64, data: &mut [u8]) {
335 if let Some(mmio_info) = self.find_region(addr) {
336 let offset = addr - mmio_info.start;
337 let index = mmio_info.index;
338 self.device.region_read(index, data, offset);
339 }
340 self.regions_mmap();
343 }
344
345 fn write_mmio(&mut self, addr: u64, data: &[u8]) {
346 if let Some(mmio_info) = self.find_region(addr) {
347 let offset = addr - mmio_info.start;
348 let index = mmio_info.index;
349 self.device.region_write(index, data, offset);
350 }
351 self.regions_mmap();
354 }
355
356 pub fn keep_rds(&self) -> Vec<RawDescriptor> {
357 let mut rds = self.device.keep_rds();
358
359 for irq_evt in self.interrupt_edge_evt.iter() {
360 rds.extend(irq_evt.as_raw_descriptors());
361 }
362
363 for irq_evt in self.interrupt_level_evt.iter() {
364 rds.extend(irq_evt.as_raw_descriptors());
365 }
366
367 rds.push(self.vm_memory_client.as_raw_descriptor());
368 rds
369 }
370
371 pub fn device_file(&self) -> &File {
373 self.device.device_file()
374 }
375
376 pub fn dt_symbol(&self) -> Option<&str> {
378 self.device.dt_symbol()
379 }
380
381 pub fn iommu(&self) -> Option<(IommuDevType, Option<u32>, &[u32])> {
384 self.device.iommu()
385 }
386}