From: Manish Honap <[email protected]>
Some devices expose registers in a BAR that must be reached only through
a trap, not a direct guest mapping. A CXL Type-2 device's HDM decoder
block is one: mapping it would let userspace reprogram the physical
decoder that governs host memory decode. Give a provider a way to mark a
BAR sub-range off-limits to mmap; it is advertised as a sparse-mmap
region and refused in the mmap path, while the provider's own region
still serves it.
Signed-off-by: Manish Honap <[email protected]>
---
drivers/vfio/pci/vfio_pci_core.c | 72 ++++++++++++++++++++++++++++++
drivers/vfio/pci/vfio_pci_dmabuf.c | 13 ++++++
drivers/vfio/pci/vfio_pci_priv.h | 13 ++++++
include/linux/vfio_pci_core.h | 6 +++
4 files changed, 104 insertions(+)
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 0f9b5dfeea66..49dfbdaf3f05 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1009,6 +1009,67 @@ static int msix_mmappable_cap(struct vfio_pci_core_device *vdev,
return vfio_info_add_capability(caps, &header, sizeof(header));
}
+/*
+ * A provider can keep a BAR sub-range off mmap (for example a CXL device's
+ * trapped HDM decoder block). Callers hold the resource so /dev/mem is already
+ * blocked; this only governs the vfio mmap path.
+ */
+void vfio_pci_core_set_mmap_exclude(struct vfio_pci_core_device *vdev, int bar,
+ u64 start, u64 len)
+{
+ vdev->mmap_exclude_bar = bar;
+ vdev->mmap_exclude_start = start;
+ vdev->mmap_exclude_len = len;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_set_mmap_exclude);
+
+/* Advertise the BAR as mmappable minus the excluded sub-range. */
+static int vfio_pci_mmap_exclude_cap(struct vfio_pci_core_device *vdev,
+ int index, struct vfio_info_cap *caps)
+{
+ u64 bar_len = pci_resource_len(vdev->pdev, index);
+ u64 excl_start = ALIGN_DOWN(vdev->mmap_exclude_start, PAGE_SIZE);
+ u64 excl_end = ALIGN(vdev->mmap_exclude_start + vdev->mmap_exclude_len,
+ PAGE_SIZE);
+ struct vfio_region_info_cap_sparse_mmap *sparse;
+ int nr_areas = 0, i = 0, ret;
+ size_t size;
+
+ /*
+ * mmap is page granular, so the mmappable areas must stop at the page
+ * boundaries enclosing the excluded sub-range. The byte-granular
+ * exclusion still governs the fault and read/write paths; only the
+ * advertised mmap areas round out to whole pages.
+ */
+ if (excl_start > 0)
+ nr_areas++;
+ if (excl_end < bar_len)
+ nr_areas++;
+
+ size = struct_size(sparse, areas, nr_areas);
+ sparse = kzalloc(size, GFP_KERNEL);
+ if (!sparse)
+ return -ENOMEM;
+
+ sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+ sparse->header.version = 1;
+ sparse->nr_areas = nr_areas;
+
+ if (excl_start > 0) {
+ sparse->areas[i].offset = 0;
+ sparse->areas[i].size = excl_start;
+ i++;
+ }
+ if (excl_end < bar_len) {
+ sparse->areas[i].offset = excl_end;
+ sparse->areas[i].size = bar_len - excl_end;
+ }
+
+ ret = vfio_info_add_capability(caps, &sparse->header, size);
+ kfree(sparse);
+ return ret;
+}
+
int vfio_pci_core_register_dev_region(struct vfio_pci_core_device *vdev,
unsigned int type, unsigned int subtype,
const struct vfio_pci_regops *ops,
@@ -1157,6 +1218,13 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
if (ret)
return ret;
}
+ if (vdev->mmap_exclude_len &&
+ info->index == vdev->mmap_exclude_bar) {
+ ret = vfio_pci_mmap_exclude_cap(vdev, info->index,
+ caps);
+ if (ret)
+ return ret;
+ }
}
break;
@@ -1851,6 +1919,10 @@ int vfio_pci_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma
if (req_start + req_len > phys_len)
return -EINVAL;
+ /* An excluded sub-range is reachable only through its trap, not mmap. */
+ if (vfio_pci_bar_is_excluded(vdev, index, req_start, req_len))
+ return -EINVAL;
+
/*
* Ensure the BAR resource region is reserved for use.
*/
diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
index c16f460c01d6..51983105d38b 100644
--- a/drivers/vfio/pci/vfio_pci_dmabuf.c
+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
@@ -177,11 +177,24 @@ int vfio_pci_core_get_dmabuf_phys(struct vfio_pci_core_device *vdev,
size_t nr_ranges)
{
struct pci_dev *pdev = vdev->pdev;
+ unsigned int i;
*provider = pcim_p2pdma_provider(pdev, region_index);
if (!*provider)
return -EINVAL;
+ /*
+ * A provider (e.g. vfio-cxl) can exclude a BAR sub-range that must be
+ * reached only through its trap. The mmap and read/write paths already
+ * refuse it; reject a DMA-BUF export overlapping it too, so a device fd
+ * holder cannot map the excluded registers to a peer and bypass the trap.
+ */
+ for (i = 0; i < nr_ranges; i++)
+ if (vfio_pci_bar_is_excluded(vdev, region_index,
+ dma_ranges[i].offset,
+ dma_ranges[i].length))
+ return -EINVAL;
+
return vfio_pci_core_fill_phys_vec(
phys_vec, dma_ranges, nr_ranges,
pci_resource_start(pdev, region_index),
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index fca9d0dfac90..902d17815ab6 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -44,6 +44,19 @@ ssize_t vfio_pci_config_rw_single(struct vfio_pci_core_device *vdev,
ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
+/*
+ * A provider (e.g. vfio-cxl) can carve a sub-range out of a BAR that must be
+ * reached only through its trap, never the direct BAR. Returns true when
+ * [start, start + len) on this BAR overlaps that excluded range.
+ */
+static inline bool vfio_pci_bar_is_excluded(struct vfio_pci_core_device *vdev,
+ int bar, u64 start, u64 len)
+{
+ return vdev->mmap_exclude_len && bar == vdev->mmap_exclude_bar &&
+ start < vdev->mmap_exclude_start + vdev->mmap_exclude_len &&
+ start + len > vdev->mmap_exclude_start;
+}
+
#ifdef CONFIG_VFIO_PCI_VGA
ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 117cd67995d8..43755b91880f 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -162,6 +162,10 @@ struct vfio_pci_core_device {
struct notifier_block nb;
struct rw_semaphore memory_lock;
struct list_head dmabufs;
+ /* BAR sub-range a provider keeps off mmap, reached only through a trap */
+ int mmap_exclude_bar;
+ u64 mmap_exclude_start;
+ u64 mmap_exclude_len;
};
enum vfio_pci_io_width {
@@ -176,6 +180,8 @@ int vfio_pci_core_register_dev_region(struct vfio_pci_core_device *vdev,
unsigned int type, unsigned int subtype,
const struct vfio_pci_regops *ops,
size_t size, u32 flags, void *data);
+void vfio_pci_core_set_mmap_exclude(struct vfio_pci_core_device *vdev, int bar,
+ u64 start, u64 len);
void vfio_pci_core_close_device(struct vfio_device *core_vdev);
int vfio_pci_core_init_dev(struct vfio_device *core_vdev);
void vfio_pci_core_release_dev(struct vfio_device *core_vdev);
--
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.