(
caps: &[VfioRegionInfoCap],
region_index: u32,
region_start: u64,
region_size: u64,
vfio_msix: Option<&VfioMsix>,
)
| 1549 | } |
| 1550 | |
| 1551 | fn generate_sparse_areas( |
| 1552 | caps: &[VfioRegionInfoCap], |
| 1553 | region_index: u32, |
| 1554 | region_start: u64, |
| 1555 | region_size: u64, |
| 1556 | vfio_msix: Option<&VfioMsix>, |
| 1557 | ) -> Result<Vec<VfioRegionSparseMmapArea>, VfioPciError> { |
| 1558 | for cap in caps { |
| 1559 | match cap { |
| 1560 | VfioRegionInfoCap::SparseMmap(sparse_mmap) => return Ok(sparse_mmap.areas.clone()), |
| 1561 | VfioRegionInfoCap::MsixMappable => { |
| 1562 | if !is_4k_aligned(region_start) { |
| 1563 | error!( |
| 1564 | "Region start address 0x{region_start:x} must be at least aligned on 4KiB" |
| 1565 | ); |
| 1566 | return Err(VfioPciError::RegionAlignment); |
| 1567 | } |
| 1568 | if !is_4k_multiple(region_size) { |
| 1569 | error!("Region size 0x{region_size:x} must be at least a multiple of 4KiB"); |
| 1570 | return Err(VfioPciError::RegionSize); |
| 1571 | } |
| 1572 | |
| 1573 | // In case the region contains the MSI-X vectors table or |
| 1574 | // the MSI-X PBA table, we must calculate the subregions |
| 1575 | // around them, leading to a list of sparse areas. |
| 1576 | // We want to make sure we will still trap MMIO accesses |
| 1577 | // to these MSI-X specific ranges. If these region don't align |
| 1578 | // with pagesize, we can achieve it by enlarging its range. |
| 1579 | // |
| 1580 | // Using a BtreeMap as the list provided through the iterator is sorted |
| 1581 | // by key. This ensures proper split of the whole region. |
| 1582 | let mut inter_ranges = BTreeMap::new(); |
| 1583 | if let Some(msix) = vfio_msix { |
| 1584 | if region_index == msix.cap.table_bir() { |
| 1585 | let (offset, size) = msix.cap.table_range(); |
| 1586 | let offset = align_page_size_down(offset); |
| 1587 | let size = align_page_size_up(size); |
| 1588 | // MSI-X mmap region safety: when a device has a non page |
| 1589 | // aligned MSI-X offset, fixup_msix_region() relocates MSI-X |
| 1590 | // to the upper half of an enlarged virtual BAR, causing the |
| 1591 | // offsets in msix.cap to exceed the physical BAR size. This |
| 1592 | // check skips carving a hole, preventing invalid offsets from |
| 1593 | // reaching the mmap path. With no holes, |
| 1594 | // generate_sparse_areas() returns a single sparse region |
| 1595 | // covering the entire physical BAR. The relocated MSI-X in |
| 1596 | // the virtual BAR remains trapped because its upper half has |
| 1597 | // no mmap backing. Exposing the physical MSI-X region through |
| 1598 | // mmap is safe when the kernel advertises |
| 1599 | // VFIO_REGION_INFO_CAP_MSIX_MAPPABLE. When MSI-X offsets are |
| 1600 | // already page aligned, fixup_msix_region() does not relocate |
| 1601 | // and this check is satisfied, so a hole is carved at the |
| 1602 | // intended offset as before. |
| 1603 | if offset < region_size { |
| 1604 | inter_ranges.insert(offset, size); |
| 1605 | } |
| 1606 | } |
| 1607 | if region_index == msix.cap.pba_bir() { |
| 1608 | let (offset, size) = msix.cap.pba_range(); |
nothing calls this directly
no test coverage detected