Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user
@ 2026-11-05 15:37 Piórkowski, Piotr
  2026-11-05 15:37 ` [RFC PATCH 1/4] vfio/pci: Move the excluded range handling out of vfio_pci_core_do_io_rw() Piórkowski, Piotr
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Piórkowski, Piotr @ 2026-11-05 15:37 UTC (permalink / raw)
  To: alex, kvm; +Cc: intel-xe, Piotr Piórkowski

From: Piotr Piórkowski <piotr.piorkowski@intel.com>

Some devices have BAR ranges which shouldn't be exposed to the VFIO
user, but vfio-pci exposes the memory BARs as a whole, apart from the
MSI-X table.

This series lets the host hide ranges of the memory BARs. Reads of a
hidden range return ones, writes to it are dropped, the pages containing
it are never mapped (a sparse mmap capability advertises the rest of the
BAR), and ioeventfds and dma-bufs covering it are rejected. The ranges
are fixed while the device is bound and listed in
/sys/.../vfio-dev/vfioX/hidden_bar_ranges.

The ranges can be hidden by the driver, with
vfio_pci_core_hide_bar_ranges(), and by the host administrator, through
configfs (CONFIG_VFIO_PCI_CONFIGFS). Only drivers which access their
BARs exclusively through vfio-pci-core can hide ranges; vfio-pci and
xe-vfio-pci declare it.

Patch 1 is a preparatory refactor, patch 2 adds the core mechanism,
patch 3 the configfs interface and patch 4 per-device-ID defaults in
xe-vfio-pci, with no ranges defined yet.

Sent as RFC for feedback on the approach and the interfaces.

Piotr Piórkowski (4):
  vfio/pci: Move the excluded range handling out of
    vfio_pci_core_do_io_rw()
  vfio/pci: Allow hiding BAR ranges from the VFIO user
  vfio/pci: Allow configuring hidden BAR ranges through configfs
  vfio/xe: Hide BAR ranges by default per device ID

 Documentation/ABI/testing/configfs-vfio-pci   |  49 +++
 .../ABI/testing/sysfs-devices-vfio-dev        |  13 +
 drivers/vfio/pci/Kconfig                      |  13 +
 drivers/vfio/pci/Makefile                     |   4 +-
 drivers/vfio/pci/nvgrace-gpu/main.c           |   4 +-
 drivers/vfio/pci/vfio_pci.c                   |   1 +
 drivers/vfio/pci/vfio_pci_configfs.c          | 312 +++++++++++++++++
 drivers/vfio/pci/vfio_pci_core.c              |  42 ++-
 drivers/vfio/pci/vfio_pci_dmabuf.c            |  11 +
 drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c | 313 ++++++++++++++++++
 drivers/vfio/pci/vfio_pci_priv.h              |  53 +++
 drivers/vfio/pci/vfio_pci_rdwr.c              | 156 +++++++--
 drivers/vfio/pci/xe/main.c                    |  60 +++-
 include/linux/vfio_pci_core.h                 |  36 +-
 14 files changed, 1009 insertions(+), 58 deletions(-)
 create mode 100644 Documentation/ABI/testing/configfs-vfio-pci
 create mode 100644 drivers/vfio/pci/vfio_pci_configfs.c
 create mode 100644 drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/4] vfio/pci: Move the excluded range handling out of vfio_pci_core_do_io_rw()
  2026-11-05 15:37 [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
@ 2026-11-05 15:37 ` Piórkowski, Piotr
  2026-11-05 15:37 ` [RFC PATCH 2/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Piórkowski, Piotr @ 2026-11-05 15:37 UTC (permalink / raw)
  To: alex, kvm
  Cc: intel-xe, Piotr Piórkowski, Jason Gunthorpe, Yishai Hadas,
	Shameer Kolothum, Kevin Tian, Ankit Agrawal

From: Piotr Piórkowski <piotr.piorkowski@intel.com>

vfio_pci_core_do_io_rw() takes an excluded range, but only
vfio_pci_bar_rw() uses it, for the MSI-X table and the unused tail of
ROM BARs. Move that logic to vfio_pci_bar_rw(), so that
vfio_pci_core_do_io_rw() just does the I/O, and let it skip more than
one range. The next patch uses that to skip hidden BAR ranges.

No functional change intended.

Cc: Alex Williamson <alex@shazbot.org>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Yishai Hadas <yishaih@nvidia.com>
Cc: Shameer Kolothum <skolothumtho@nvidia.com>
Cc: Kevin Tian <kevin.tian@intel.com>
Cc: Ankit Agrawal <ankita@nvidia.com>
Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
---
 drivers/vfio/pci/nvgrace-gpu/main.c |  4 +-
 drivers/vfio/pci/vfio_pci_rdwr.c    | 94 ++++++++++++++++++++---------
 include/linux/vfio_pci_core.h       |  3 +-
 3 files changed, 67 insertions(+), 34 deletions(-)

diff --git a/drivers/vfio/pci/nvgrace-gpu/main.c b/drivers/vfio/pci/nvgrace-gpu/main.c
index 5ddec340b73ba..432f6b9c2842b 100644
--- a/drivers/vfio/pci/nvgrace-gpu/main.c
+++ b/drivers/vfio/pci/nvgrace-gpu/main.c
@@ -787,7 +787,7 @@ nvgrace_gpu_map_and_read(struct nvgrace_gpu_pci_core_device *nvdev,
 		ret = vfio_pci_core_do_io_rw(&nvdev->core_device, false,
 					     nvdev->resmem.ioaddr,
 					     buf, offset, mem_count,
-					     0, 0, false, VFIO_PCI_IO_WIDTH_8);
+					     false, VFIO_PCI_IO_WIDTH_8);
 	}
 
 	return ret;
@@ -925,7 +925,7 @@ nvgrace_gpu_map_and_write(struct nvgrace_gpu_pci_core_device *nvdev,
 		ret = vfio_pci_core_do_io_rw(&nvdev->core_device, false,
 					     nvdev->resmem.ioaddr,
 					     (char __user *)buf, pos, mem_count,
-					     0, 0, true, VFIO_PCI_IO_WIDTH_8);
+					     true, VFIO_PCI_IO_WIDTH_8);
 	}
 
 	return ret;
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index df88480876815..f3ab8d6531e8f 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -126,77 +126,108 @@ VFIO_IORDWR(16)
 VFIO_IORDWR(32)
 VFIO_IORDWR(64)
 
-/*
- * Read or write from an __iomem region (MMIO or I/O port) with an excluded
- * range which is inaccessible.  The excluded range drops writes and fills
- * reads with -1.  This is intended for handling MSI-X vector tables and
- * leftover space for ROM BARs.
- */
+/* Read or write from an __iomem region (MMIO or I/O port) */
 ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
 			       void __iomem *io, char __user *buf,
-			       loff_t off, size_t count, size_t x_start,
-			       size_t x_end, bool iswrite,
+			       loff_t off, size_t count, bool iswrite,
 			       enum vfio_pci_io_width max_width)
 {
 	ssize_t done = 0;
 	int ret;
 
 	while (count) {
-		size_t fillable, filled;
-
-		if (off < x_start)
-			fillable = min(count, (size_t)(x_start - off));
-		else if (off >= x_end)
-			fillable = count;
-		else
-			fillable = 0;
+		size_t filled;
 
-		if (fillable >= 8 && !(off % 8) && max_width >= 8) {
+		if (count >= 8 && !(off % 8) && max_width >= 8) {
 			ret = vfio_pci_iordwr64(vdev, iswrite, test_mem,
 						io, buf, off, &filled);
 			if (ret)
 				return ret;
 
-		} else if (fillable >= 4 && !(off % 4) && max_width >= 4) {
+		} else if (count >= 4 && !(off % 4) && max_width >= 4) {
 			ret = vfio_pci_iordwr32(vdev, iswrite, test_mem,
 						io, buf, off, &filled);
 			if (ret)
 				return ret;
 
-		} else if (fillable >= 2 && !(off % 2) && max_width >= 2) {
+		} else if (count >= 2 && !(off % 2) && max_width >= 2) {
 			ret = vfio_pci_iordwr16(vdev, iswrite, test_mem,
 						io, buf, off, &filled);
 			if (ret)
 				return ret;
 
-		} else if (fillable) {
+		} else {
 			ret = vfio_pci_iordwr8(vdev, iswrite, test_mem,
 					       io, buf, off, &filled);
 			if (ret)
 				return ret;
+		}
+
+		count -= filled;
+		done += filled;
+		off += filled;
+		buf += filled;
+	}
+
+	return done;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw);
 
+struct vfio_pci_excl_range {
+	loff_t	start;
+	loff_t	end;
+};
+
+/*
+ * Read or write from a BAR with excluded ranges which are inaccessible.  The
+ * excluded ranges, sorted by their start and possibly overlapping, drop writes
+ * and fill reads with -1.  This is intended for handling MSI-X vector tables
+ * and leftover space for ROM BARs.
+ */
+static ssize_t vfio_pci_bar_do_io_rw(struct vfio_pci_core_device *vdev,
+				     bool test_mem, void __iomem *io,
+				     char __user *buf, loff_t off, size_t count,
+				     const struct vfio_pci_excl_range *x,
+				     unsigned int nr_x, bool iswrite,
+				     enum vfio_pci_io_width max_width)
+{
+	ssize_t done = 0, ret;
+
+	while (count) {
+		size_t len;
+
+		while (nr_x && off >= x->end) {
+			x++;
+			nr_x--;
+		}
+
+		if (!nr_x || off < x->start) {
+			len = nr_x ? min(count, (size_t)(x->start - off)) : count;
+			ret = vfio_pci_core_do_io_rw(vdev, test_mem, io, buf, off,
+						     len, iswrite, max_width);
+			if (ret < 0)
+				return ret;
 		} else {
 			/* Fill reads with -1, drop writes */
-			filled = min(count, (size_t)(x_end - off));
+			len = min(count, (size_t)(x->end - off));
 			if (!iswrite) {
 				u8 val = 0xFF;
 				size_t i;
 
-				for (i = 0; i < filled; i++)
+				for (i = 0; i < len; i++)
 					if (copy_to_user(buf + i, &val, 1))
 						return -EFAULT;
 			}
 		}
 
-		count -= filled;
-		done += filled;
-		off += filled;
-		buf += filled;
+		count -= len;
+		done += len;
+		off += len;
+		buf += len;
 	}
 
 	return done;
 }
-EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw);
 
 ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 			size_t count, loff_t *ppos, bool iswrite)
@@ -204,6 +235,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 	struct pci_dev *pdev = vdev->pdev;
 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+	struct vfio_pci_excl_range x = {};
 	size_t x_start = 0, x_end = 0;
 	resource_size_t end;
 	void __iomem *io;
@@ -261,8 +293,10 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 		x_end = vdev->msix_offset + vdev->msix_size;
 	}
 
-	done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos,
-				      count, x_start, x_end, iswrite, max_width);
+	x.start = x_start;
+	x.end = x_end;
+	done = vfio_pci_bar_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf,
+				     pos, count, &x, 1, iswrite, max_width);
 
 	if (done >= 0)
 		*ppos += done;
@@ -336,7 +370,7 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 	 * to the memory enable bit in the command register.
 	 */
 	done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count,
-				      0, 0, iswrite, VFIO_PCI_IO_WIDTH_4);
+				      iswrite, VFIO_PCI_IO_WIDTH_4);
 
 	vga_put(vdev->pdev, rsrc);
 
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 2f3fa2787737a..b7225be31e308 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -196,8 +196,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev);
 void vfio_pci_core_finish_enable(struct vfio_pci_core_device *vdev);
 ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
 			       void __iomem *io, char __user *buf,
-			       loff_t off, size_t count, size_t x_start,
-			       size_t x_end, bool iswrite,
+			       loff_t off, size_t count, bool iswrite,
 			       enum vfio_pci_io_width max_width);
 bool __vfio_pci_memory_enabled(struct vfio_pci_core_device *vdev);
 bool vfio_pci_core_range_intersect_range(loff_t buf_start, size_t buf_cnt,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/4] vfio/pci: Allow hiding BAR ranges from the VFIO user
  2026-11-05 15:37 [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
  2026-11-05 15:37 ` [RFC PATCH 1/4] vfio/pci: Move the excluded range handling out of vfio_pci_core_do_io_rw() Piórkowski, Piotr
@ 2026-11-05 15:37 ` Piórkowski, Piotr
  2026-11-05 16:24   ` sashiko-bot
  2026-11-05 15:37 ` [RFC PATCH 3/4] vfio/pci: Allow configuring hidden BAR ranges through configfs Piórkowski, Piotr
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Piórkowski, Piotr @ 2026-11-05 15:37 UTC (permalink / raw)
  To: alex, kvm; +Cc: intel-xe, Piotr Piórkowski

From: Piotr Piórkowski <piotr.piorkowski@intel.com>

Some devices have BAR ranges which shouldn't be exposed to the VFIO
user, but the only part of a BAR vfio-pci can keep out of reach is the
MSI-X table.

Add vfio_pci_core_hide_bar_ranges(), so that a driver can hide parts of
its memory BARs. Reads of a hidden range return all ones, writes to it
are dropped, its pages are never mmapped, and ioeventfds and dma-bufs
covering it are refused. The hidden ranges are listed in sysfs.

This only works if all the BAR accesses go through vfio-pci-core, so
the driver has to opt in by setting hidden_ranges_supported.

Cc: Alex Williamson <alex@shazbot.org>
Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
---
 .../ABI/testing/sysfs-devices-vfio-dev        |  13 +
 drivers/vfio/pci/Makefile                     |   3 +-
 drivers/vfio/pci/vfio_pci_core.c              |  22 +-
 drivers/vfio/pci/vfio_pci_dmabuf.c            |  11 +
 drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c | 289 ++++++++++++++++++
 drivers/vfio/pci/vfio_pci_priv.h              |  17 ++
 drivers/vfio/pci/vfio_pci_rdwr.c              |  80 ++++-
 include/linux/vfio_pci_core.h                 |  29 ++
 8 files changed, 447 insertions(+), 17 deletions(-)
 create mode 100644 drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c

diff --git a/Documentation/ABI/testing/sysfs-devices-vfio-dev b/Documentation/ABI/testing/sysfs-devices-vfio-dev
index e21424fd96662..e689dd206b71e 100644
--- a/Documentation/ABI/testing/sysfs-devices-vfio-dev
+++ b/Documentation/ABI/testing/sysfs-devices-vfio-dev
@@ -6,3 +6,16 @@ Description:
 		 vfio driver. The layout under this directory matches what
 		 exists for a standard 'struct device'. 'X' is a unique
 		 index marking this device in vfio.
+
+What:		/sys/.../<device>/vfio-dev/vfioX/hidden_bar_ranges
+Date:		November 2026
+Contact:	kvm@vger.kernel.org
+Description:
+		(RO) Lists the ranges of the memory BARs hidden from the VFIO
+		user, one per line as "<bar>:<offset>:<size>", where <bar> is
+		the decimal BAR index and <offset> and <size> are hexadecimal
+		values prefixed with 0x. Reads of a hidden range return all
+		ones, writes to it are dropped and the pages containing it
+		can't be mapped. Empty if no range is hidden. The list is set
+		up when the device is bound to the VFIO driver and doesn't
+		change while it stays bound.
diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile
index 6138f1bf241df..be3bf15505c3b 100644
--- a/drivers/vfio/pci/Makefile
+++ b/drivers/vfio/pci/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
+vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o \
+		   vfio_pci_hidden_bar_ranges.o
 vfio-pci-core-$(CONFIG_VFIO_PCI_ZDEV_KVM) += vfio_pci_zdev.o
 vfio-pci-core-$(CONFIG_VFIO_PCI_DMABUF) += vfio_pci_dmabuf.o
 obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index fcbb2da860e2a..cd6cb3d829b51 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1147,6 +1147,10 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
 				if (ret)
 					return ret;
 			}
+
+			ret = vfio_pci_hidden_add_sparse_mmap(vdev, info, caps);
+			if (ret)
+				return ret;
 		}
 
 		break;
@@ -1771,11 +1775,20 @@ static vm_fault_t vfio_pci_mmap_huge_fault(struct vm_fault *vmf,
 	unsigned long addr = vmf->address & ~((PAGE_SIZE << order) - 1);
 	unsigned long pgoff = linear_page_delta(vma, addr);
 	unsigned long pfn = vma_to_pfn(vma) + pgoff;
+	unsigned int index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
+	u64 bar_pgoff = (vma->vm_pgoff &
+			 ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1)) + pgoff;
 	vm_fault_t ret = VM_FAULT_FALLBACK;
 
 	if (is_aligned_for_order(vma, addr, pfn, order)) {
-		scoped_guard(rwsem_read, &vdev->memory_lock)
-			ret = vfio_pci_vmf_insert_pfn(vdev, vmf, pfn, order);
+		if (vfio_pci_bar_range_hidden(vdev, index, bar_pgoff << PAGE_SHIFT,
+					      PAGE_SIZE << order)) {
+			if (!order)
+				ret = VM_FAULT_SIGBUS;
+		} else {
+			scoped_guard(rwsem_read, &vdev->memory_lock)
+				ret = vfio_pci_vmf_insert_pfn(vdev, vmf, pfn, order);
+		}
 	}
 
 	dev_dbg_ratelimited(&vdev->pdev->dev,
@@ -2279,10 +2292,15 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
 	if (!vdev->disable_idle_d3)
 		pm_runtime_put(dev);
 
+	vdev->vdev.device.groups = vfio_pci_hidden_bar_ranges_groups;
 	ret = vfio_register_group_dev(&vdev->vdev);
 	if (ret)
 		goto out_power;
 
+	if (vdev->num_hidden_ranges)
+		pci_dbg(pdev, "Hiding %u BAR range(s) from the VFIO user\n",
+			vdev->num_hidden_ranges);
+
 	vfio_pci_core_debugfs_init(vdev);
 
 	return 0;
diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
index c16f460c01d68..409b327926478 100644
--- a/drivers/vfio/pci/vfio_pci_dmabuf.c
+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
@@ -227,6 +227,7 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
 	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
 	struct vfio_pci_dma_buf *priv;
 	size_t length;
+	u32 i;
 	int ret;
 
 	if (!vdev->pci_ops || !vdev->pci_ops->get_dmabuf_phys)
@@ -260,6 +261,16 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
 	if (ret)
 		goto err_free_ranges;
 
+	/* the importers access the BAR directly, bypassing the hidden ranges */
+	for (i = 0; i < get_dma_buf.nr_ranges; i++) {
+		if (vfio_pci_bar_range_hidden(vdev, get_dma_buf.region_index,
+					      dma_ranges[i].offset,
+					      dma_ranges[i].length)) {
+			ret = -EINVAL;
+			goto err_free_ranges;
+		}
+	}
+
 	priv = kzalloc_obj(*priv);
 	if (!priv) {
 		ret = -ENOMEM;
diff --git a/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c b/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c
new file mode 100644
index 0000000000000..666820dc24080
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VFIO PCI BAR ranges hidden from the VFIO user
+ *
+ * Some devices expose registers that must stay under control of the host,
+ * e.g. device management registers, in the same BAR as the functional ones.
+ * The ranges of such registers can be hidden from the VFIO user: region
+ * reads of them are filled with ones and writes to them are dropped, the
+ * pages containing them are never mapped, and neither ioeventfds nor
+ * dma-bufs may cover them.
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_priv.h"
+
+static bool vfio_pci_bar_ranges_overlap(const struct vfio_pci_bar_range *range,
+					unsigned int bar, u64 offset, u64 size)
+{
+	return range->bar == bar && offset < range->offset + range->size &&
+	       range->offset < offset + size;
+}
+
+bool __vfio_pci_bar_range_hidden(struct vfio_pci_core_device *vdev,
+				 unsigned int bar, u64 offset, u64 size)
+{
+	unsigned int i;
+
+	/* treat an overflowing range as hidden, the callers clamp it if needed */
+	if (size > U64_MAX - offset)
+		return true;
+
+	for (i = 0; i < vdev->num_hidden_ranges; i++) {
+		if (vfio_pci_bar_ranges_overlap(&vdev->hidden_ranges[i], bar,
+						offset, size))
+			return true;
+	}
+
+	return false;
+}
+
+static int vfio_pci_check_bar_range(const struct vfio_pci_bar_range *range)
+{
+	if (range->bar >= PCI_STD_NUM_BARS || !range->size ||
+	    range->size > U64_MAX - range->offset)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int vfio_pci_add_bar_range(struct vfio_pci_bar_range *ranges,
+				  unsigned int *num,
+				  const struct vfio_pci_bar_range *range)
+{
+	unsigned int i;
+	int ret;
+
+	ret = vfio_pci_check_bar_range(range);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < *num; i++) {
+		if (vfio_pci_bar_ranges_overlap(&ranges[i], range->bar,
+						range->offset, range->size))
+			return -EEXIST;
+	}
+
+	if (*num == VFIO_PCI_MAX_HIDDEN_BAR_RANGES)
+		return -ENOSPC;
+
+	for (i = *num; i > 0; i--) {
+		if (ranges[i - 1].bar < range->bar ||
+		    (ranges[i - 1].bar == range->bar &&
+		     ranges[i - 1].offset < range->offset))
+			break;
+
+		ranges[i] = ranges[i - 1];
+	}
+
+	ranges[i] = *range;
+	(*num)++;
+
+	return 0;
+}
+
+static void vfio_pci_remove_bar_range(struct vfio_pci_bar_range *ranges,
+				      unsigned int *num, unsigned int i)
+{
+	memmove(&ranges[i], &ranges[i + 1], (*num - i - 1) * sizeof(*ranges));
+	(*num)--;
+}
+
+static ssize_t vfio_pci_show_bar_ranges(const struct vfio_pci_bar_range *ranges,
+					unsigned int num, char *buf)
+{
+	unsigned int i;
+	ssize_t len = 0;
+
+	for (i = 0; i < num; i++)
+		len += sysfs_emit_at(buf, len, "%u:%#llx:%#llx\n", ranges[i].bar,
+				     ranges[i].offset, ranges[i].size);
+
+	return len;
+}
+
+static int vfio_pci_hide_bar_range(struct vfio_pci_core_device *vdev,
+				   const struct vfio_pci_bar_range *range)
+{
+	struct vfio_pci_bar_range merged = *range;
+	struct pci_dev *pdev = vdev->pdev;
+	unsigned int i = 0;
+	int ret;
+
+	ret = vfio_pci_check_bar_range(range);
+	if (ret)
+		return ret;
+
+	if (!(pci_resource_flags(pdev, range->bar) & IORESOURCE_MEM) ||
+	    !pci_resource_start(pdev, range->bar) ||
+	    range->size > pci_resource_len(pdev, range->bar) ||
+	    range->offset > pci_resource_len(pdev, range->bar) - range->size)
+		return -EINVAL;
+
+	while (i < vdev->num_hidden_ranges) {
+		const struct vfio_pci_bar_range *hidden = &vdev->hidden_ranges[i];
+		u64 end;
+
+		if (!vfio_pci_bar_ranges_overlap(hidden, merged.bar,
+						 merged.offset, merged.size)) {
+			i++;
+			continue;
+		}
+
+		end = max(hidden->offset + hidden->size,
+			  merged.offset + merged.size);
+		merged.offset = min(hidden->offset, merged.offset);
+		merged.size = end - merged.offset;
+		vfio_pci_remove_bar_range(vdev->hidden_ranges,
+					  &vdev->num_hidden_ranges, i);
+	}
+
+	return vfio_pci_add_bar_range(vdev->hidden_ranges,
+				      &vdev->num_hidden_ranges, &merged);
+}
+
+static int __vfio_pci_hide_bar_ranges(struct vfio_pci_core_device *vdev,
+				      const struct vfio_pci_bar_range *ranges,
+				      unsigned int num, const char *source)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < num; i++) {
+		ret = vfio_pci_hide_bar_range(vdev, &ranges[i]);
+		if (ret) {
+			pci_err(vdev->pdev, "Failed to hide BAR range %u:%#llx:%#llx from %s (%pe)\n",
+				ranges[i].bar, ranges[i].offset, ranges[i].size,
+				source, ERR_PTR(ret));
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * vfio_pci_core_hide_bar_ranges - hide BAR ranges from the VFIO user
+ * @vdev: device the ranges belong to
+ * @ranges: ranges to hide, of the memory BARs of the device
+ * @num: number of entries in @ranges
+ *
+ * Must be called before vfio_pci_core_register_device(), so that the ranges
+ * are hidden before the device can be opened. The ranges are hidden by the
+ * accesses done through vfio-pci-core: region reads and writes, region infos,
+ * mmaps, ioeventfds and dma-bufs. The driver must declare that it doesn't
+ * access its memory BARs in any other way by setting
+ * &vfio_pci_core_device.hidden_ranges_supported first.
+ *
+ * The accesses crossing the boundaries of a hidden range are split, so the
+ * range should match the access granularity of the device, e.g. its register
+ * width. A range overlapping already hidden ones is merged with them, at most
+ * %VFIO_PCI_MAX_HIDDEN_BAR_RANGES ranges can be hidden on a device after
+ * merging.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int vfio_pci_core_hide_bar_ranges(struct vfio_pci_core_device *vdev,
+				  const struct vfio_pci_bar_range *ranges,
+				  unsigned int num)
+{
+	if (WARN_ON(!vdev->hidden_ranges_supported))
+		return -EINVAL;
+
+	return __vfio_pci_hide_bar_ranges(vdev, ranges, num, vdev->vdev.ops->name);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_hide_bar_ranges);
+
+/*
+ * Advertise only the parts of the BAR outside of the pages containing the
+ * hidden ranges as mmappable, so that the VFIO user traps accesses to those
+ * pages instead of trying to mmap the whole BAR and getting SIGBUS.
+ */
+int vfio_pci_hidden_add_sparse_mmap(struct vfio_pci_core_device *vdev,
+				    struct vfio_region_info *info,
+				    struct vfio_info_cap *caps)
+{
+	struct vfio_region_info_cap_sparse_mmap *sparse;
+	u64 start = 0, end = PAGE_ALIGN(info->size);
+	u64 hidden_start, hidden_end;
+	unsigned int i, nr_areas = 0;
+	int ret = 0;
+
+	if (!vfio_pci_bar_range_hidden(vdev, info->index, 0, info->size))
+		return 0;
+
+	sparse = kzalloc_flex(*sparse, areas, vdev->num_hidden_ranges + 1);
+	if (!sparse)
+		return -ENOMEM;
+
+	for (i = 0; i < vdev->num_hidden_ranges; i++) {
+		const struct vfio_pci_bar_range *range = &vdev->hidden_ranges[i];
+
+		if (range->bar != info->index)
+			continue;
+
+		hidden_start = ALIGN_DOWN(range->offset, PAGE_SIZE);
+		hidden_end = PAGE_ALIGN(range->offset + range->size);
+
+		if (hidden_start > start) {
+			sparse->areas[nr_areas].offset = start;
+			sparse->areas[nr_areas].size = hidden_start - start;
+			nr_areas++;
+		}
+		/* ranges may share a page with the previous one */
+		start = max(start, hidden_end);
+	}
+
+	if (start < end) {
+		sparse->areas[nr_areas].offset = start;
+		sparse->areas[nr_areas].size = end - start;
+		nr_areas++;
+	}
+
+	if (!nr_areas) {
+		info->flags &= ~VFIO_REGION_INFO_FLAG_MMAP;
+		goto out;
+	}
+
+	sparse->nr_areas = nr_areas;
+	sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+	sparse->header.version = 1;
+
+	ret = vfio_info_add_capability(caps, &sparse->header,
+				       struct_size(sparse, areas, nr_areas));
+out:
+	kfree(sparse);
+	return ret;
+}
+
+static ssize_t hidden_bar_ranges_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct vfio_pci_core_device *vdev =
+		container_of(dev, struct vfio_pci_core_device, vdev.device);
+
+	return vfio_pci_show_bar_ranges(vdev->hidden_ranges,
+					vdev->num_hidden_ranges, buf);
+}
+static DEVICE_ATTR_RO(hidden_bar_ranges);
+
+static struct attribute *vfio_pci_hidden_bar_ranges_attrs[] = {
+	&dev_attr_hidden_bar_ranges.attr,
+	NULL
+};
+
+static const struct attribute_group vfio_pci_hidden_bar_ranges_group = {
+	.attrs = vfio_pci_hidden_bar_ranges_attrs,
+};
+
+const struct attribute_group *vfio_pci_hidden_bar_ranges_groups[] = {
+	&vfio_pci_hidden_bar_ranges_group,
+	NULL
+};
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index 4e7162234a2eb..a3d42a37176de 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -44,6 +44,23 @@ 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);
 
+bool __vfio_pci_bar_range_hidden(struct vfio_pci_core_device *vdev,
+				 unsigned int bar, u64 offset, u64 size);
+
+/* inline, so that the devices without hidden ranges don't pay for a call */
+static inline bool vfio_pci_bar_range_hidden(struct vfio_pci_core_device *vdev,
+					     unsigned int bar, u64 offset,
+					     u64 size)
+{
+	return unlikely(vdev->num_hidden_ranges) &&
+	       __vfio_pci_bar_range_hidden(vdev, bar, offset, size);
+}
+
+int vfio_pci_hidden_add_sparse_mmap(struct vfio_pci_core_device *vdev,
+				    struct vfio_region_info *info,
+				    struct vfio_info_cap *caps);
+extern const struct attribute_group *vfio_pci_hidden_bar_ranges_groups[];
+
 #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/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index f3ab8d6531e8f..fc4d4537ee01a 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -12,6 +12,7 @@
 
 #include <linux/fs.h>
 #include <linux/pci.h>
+#include <linux/sched.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
 #include <linux/vfio.h>
@@ -178,11 +179,32 @@ struct vfio_pci_excl_range {
 	loff_t	end;
 };
 
+/* a hidden range can be large, fill it in chunks and let others run */
+static int vfio_pci_fill_ones(char __user *buf, size_t count)
+{
+	u8 ones[128];
+
+	memset(ones, 0xff, sizeof(ones));
+
+	while (count) {
+		size_t len = min(count, sizeof(ones));
+
+		if (copy_to_user(buf, ones, len))
+			return -EFAULT;
+
+		buf += len;
+		count -= len;
+		cond_resched();
+	}
+
+	return 0;
+}
+
 /*
  * Read or write from a BAR with excluded ranges which are inaccessible.  The
  * excluded ranges, sorted by their start and possibly overlapping, drop writes
- * and fill reads with -1.  This is intended for handling MSI-X vector tables
- * and leftover space for ROM BARs.
+ * and fill reads with -1.  This is intended for handling MSI-X vector tables,
+ * leftover space for ROM BARs and BAR ranges hidden from the VFIO user.
  */
 static ssize_t vfio_pci_bar_do_io_rw(struct vfio_pci_core_device *vdev,
 				     bool test_mem, void __iomem *io,
@@ -210,14 +232,8 @@ static ssize_t vfio_pci_bar_do_io_rw(struct vfio_pci_core_device *vdev,
 		} else {
 			/* Fill reads with -1, drop writes */
 			len = min(count, (size_t)(x->end - off));
-			if (!iswrite) {
-				u8 val = 0xFF;
-				size_t i;
-
-				for (i = 0; i < len; i++)
-					if (copy_to_user(buf + i, &val, 1))
-						return -EFAULT;
-			}
+			if (!iswrite && vfio_pci_fill_ones(buf, len))
+				return -EFAULT;
 		}
 
 		count -= len;
@@ -229,14 +245,44 @@ static ssize_t vfio_pci_bar_do_io_rw(struct vfio_pci_core_device *vdev,
 	return done;
 }
 
+/*
+ * Add the hidden ranges of a BAR to its excluded ranges, which hold the MSI-X
+ * table, if any, keeping them sorted by their start.
+ */
+static unsigned int vfio_pci_bar_excl_hidden(struct vfio_pci_core_device *vdev,
+					     unsigned int bar,
+					     struct vfio_pci_excl_range *x,
+					     unsigned int nr_x)
+{
+	unsigned int i;
+
+	for (i = 0; i < vdev->num_hidden_ranges; i++) {
+		const struct vfio_pci_bar_range *range = &vdev->hidden_ranges[i];
+
+		if (range->bar != bar)
+			continue;
+
+		x[nr_x].start = range->offset;
+		x[nr_x].end = range->offset + range->size;
+		nr_x++;
+	}
+
+	/* the hidden ranges are sorted, only the MSI-X table may be out of place */
+	for (i = 1; i < nr_x && x[i].start < x[i - 1].start; i++)
+		swap(x[i], x[i - 1]);
+
+	return nr_x;
+}
+
 ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 			size_t count, loff_t *ppos, bool iswrite)
 {
 	struct pci_dev *pdev = vdev->pdev;
 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
-	struct vfio_pci_excl_range x = {};
+	struct vfio_pci_excl_range x[VFIO_PCI_MAX_HIDDEN_BAR_RANGES + 1];
 	size_t x_start = 0, x_end = 0;
+	unsigned int nr_x = 1;
 	resource_size_t end;
 	void __iomem *io;
 	struct resource *res = &vdev->pdev->resource[bar];
@@ -293,10 +339,13 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 		x_end = vdev->msix_offset + vdev->msix_size;
 	}
 
-	x.start = x_start;
-	x.end = x_end;
+	x[0].start = x_start;
+	x[0].end = x_end;
+	if (vfio_pci_bar_range_hidden(vdev, bar, pos, count))
+		nr_x = vfio_pci_bar_excl_hidden(vdev, bar, x, nr_x);
+
 	done = vfio_pci_bar_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf,
-				     pos, count, &x, 1, iswrite, max_width);
+				     pos, count, x, nr_x, iswrite, max_width);
 
 	if (done >= 0)
 		*ppos += done;
@@ -457,6 +506,9 @@ int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
 	      pos >= vdev->msix_offset + vdev->msix_size))
 		return -EINVAL;
 
+	if (vfio_pci_bar_range_hidden(vdev, bar, pos, count))
+		return -EINVAL;
+
 	io = vfio_pci_core_get_iomap(vdev, bar);
 	if (IS_ERR(io))
 		return PTR_ERR(io);
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index b7225be31e308..3745911fb2f1f 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -97,6 +97,20 @@ static inline int vfio_pci_core_get_dmabuf_phys(
 }
 #endif
 
+#define VFIO_PCI_MAX_HIDDEN_BAR_RANGES	16
+
+/**
+ * struct vfio_pci_bar_range - range of a memory BAR
+ * @bar: index of the BAR
+ * @offset: offset of the range in the BAR
+ * @size: size of the range
+ */
+struct vfio_pci_bar_range {
+	unsigned int	bar;
+	u64		offset;
+	u64		size;
+};
+
 struct vfio_pci_core_device {
 	struct vfio_device	vdev;
 	struct pci_dev		*pdev;
@@ -151,6 +165,18 @@ struct vfio_pci_core_device {
 	struct notifier_block	nb;
 	struct rw_semaphore	memory_lock;
 	struct list_head	dmabufs;
+	/*
+	 * BAR ranges hidden from the VFIO user, sorted by BAR and offset.
+	 * Set up before the device is registered and not changed afterwards.
+	 */
+	struct vfio_pci_bar_range hidden_ranges[VFIO_PCI_MAX_HIDDEN_BAR_RANGES];
+	unsigned int		num_hidden_ranges;
+	/*
+	 * Set by the driver before registering the device if all the accesses
+	 * to its memory BARs go through vfio-pci-core, so that it can hide
+	 * BAR ranges.
+	 */
+	bool			hidden_ranges_supported;
 };
 
 enum vfio_pci_io_width {
@@ -170,6 +196,9 @@ int vfio_pci_core_init_dev(struct vfio_device *core_vdev);
 void vfio_pci_core_release_dev(struct vfio_device *core_vdev);
 int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev);
 void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev);
+int vfio_pci_core_hide_bar_ranges(struct vfio_pci_core_device *vdev,
+				  const struct vfio_pci_bar_range *ranges,
+				  unsigned int num);
 int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev,
 				  int nr_virtfn);
 long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/4] vfio/pci: Allow configuring hidden BAR ranges through configfs
  2026-11-05 15:37 [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
  2026-11-05 15:37 ` [RFC PATCH 1/4] vfio/pci: Move the excluded range handling out of vfio_pci_core_do_io_rw() Piórkowski, Piotr
  2026-11-05 15:37 ` [RFC PATCH 2/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
@ 2026-11-05 15:37 ` Piórkowski, Piotr
  2026-11-05 15:37 ` [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID Piórkowski, Piotr
  2026-11-05 15:50 ` [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Alex Williamson
  4 siblings, 0 replies; 9+ messages in thread
From: Piórkowski, Piotr @ 2026-11-05 15:37 UTC (permalink / raw)
  To: alex, kvm; +Cc: intel-xe, Piotr Piórkowski

From: Piotr Piórkowski <piotr.piorkowski@intel.com>

Let the host administrator hide BAR ranges through
/sys/kernel/config/vfio-pci/<device>/. The ranges are applied when the
device is bound, on top of the ones hidden by the driver.

The configuration is locked while the device is bound, so it always
matches what is actually hidden. Any change fails with -EBUSY until the
device is unbound.

If ranges are configured but the driver can't hide them, binding fails
instead of silently exposing them. vfio-pci opts in.

The configfs interface is optional, behind CONFIG_VFIO_PCI_CONFIGFS.

Cc: Alex Williamson <alex@shazbot.org>
Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
---
 Documentation/ABI/testing/configfs-vfio-pci   |  49 +++
 drivers/vfio/pci/Kconfig                      |  13 +
 drivers/vfio/pci/Makefile                     |   1 +
 drivers/vfio/pci/vfio_pci.c                   |   1 +
 drivers/vfio/pci/vfio_pci_configfs.c          | 312 ++++++++++++++++++
 drivers/vfio/pci/vfio_pci_core.c              |  20 +-
 drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c |  50 ++-
 drivers/vfio/pci/vfio_pci_priv.h              |  36 ++
 include/linux/vfio_pci_core.h                 |   4 +
 9 files changed, 472 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/ABI/testing/configfs-vfio-pci
 create mode 100644 drivers/vfio/pci/vfio_pci_configfs.c

diff --git a/Documentation/ABI/testing/configfs-vfio-pci b/Documentation/ABI/testing/configfs-vfio-pci
new file mode 100644
index 0000000000000..fe565cb88f2c8
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-vfio-pci
@@ -0,0 +1,49 @@
+What:		/sys/kernel/config/vfio-pci/<device>/
+Date:		November 2026
+Contact:	kvm@vger.kernel.org
+Description:
+		Configuration of a PCI device applied when it is bound to
+		vfio-pci or to a vfio-pci variant driver. The directory name
+		is the canonical name of the PCI device, as in
+		/sys/bus/pci/devices/, e.g. 0000:03:00.1. It can be created
+		before the device exists, e.g. for a VF before SR-IOV is
+		enabled. The configuration is applied when the device is
+		bound and can't change until it is unbound: creating and
+		removing the directory and writing hide_bar_range and
+		unhide_bar_range fail with EBUSY while the device is bound.
+		Available if CONFIG_VFIO_PCI_CONFIGFS is enabled.
+
+What:		/sys/kernel/config/vfio-pci/<device>/hide_bar_range
+Date:		November 2026
+Contact:	kvm@vger.kernel.org
+Description:
+		(WO) Adds a range of a memory BAR to hide from the VFIO user,
+		written as "<bar>:<offset>:<size>", where <bar> is the decimal
+		BAR index and <offset> and <size> are hexadecimal values with
+		an optional 0x prefix. The range must not overlap the ranges
+		already added, at most 16 ranges can be added. The accesses
+		crossing the boundaries of the range are split, so it should
+		match the access granularity of the device, e.g. its register
+		width. Whether the range fits into a memory BAR of the device
+		is checked when the device is bound, which fails if it
+		doesn't. Binding also fails if the driver of the device
+		accesses its memory BARs in ways which can't hide the ranges,
+		e.g. a vfio-pci variant driver with its own BAR accesses. The
+		ranges are merged with the ones hidden by the driver of the
+		device, see
+		/sys/.../<device>/vfio-dev/vfioX/hidden_bar_ranges.
+
+What:		/sys/kernel/config/vfio-pci/<device>/unhide_bar_range
+Date:		November 2026
+Contact:	kvm@vger.kernel.org
+Description:
+		(WO) Removes a range added through hide_bar_range, written in
+		the same format, which must match the added range exactly.
+
+What:		/sys/kernel/config/vfio-pci/<device>/hidden_bar_ranges
+Date:		November 2026
+Contact:	kvm@vger.kernel.org
+Description:
+		(RO) Lists the ranges added through hide_bar_range, one per
+		line as "<bar>:<offset>:<size>", with <offset> and <size>
+		prefixed with 0x.
diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig
index 296bf01e185ec..78793b9ea50e3 100644
--- a/drivers/vfio/pci/Kconfig
+++ b/drivers/vfio/pci/Kconfig
@@ -7,6 +7,19 @@ config VFIO_PCI_CORE
 	select VFIO_VIRQFD
 	select IRQ_BYPASS_MANAGER
 
+config VFIO_PCI_CONFIGFS
+	bool "VFIO PCI configuration through configfs"
+	depends on VFIO_PCI_CORE
+	select CONFIGFS_FS
+	help
+	  Let the host administrator configure PCI devices before they are
+	  bound to vfio-pci or to a vfio-pci variant driver, through
+	  /sys/kernel/config/vfio-pci/<device>/. The configuration allows
+	  hiding BAR ranges of a device from the VFIO user, in addition to
+	  the ones hidden by its driver.
+
+	  If you don't know what to do here, say N.
+
 config VFIO_PCI_INTX
 	def_bool y if !S390
 	depends on VFIO_PCI_CORE
diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile
index be3bf15505c3b..51026d23beb02 100644
--- a/drivers/vfio/pci/Makefile
+++ b/drivers/vfio/pci/Makefile
@@ -4,6 +4,7 @@ vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_con
 		   vfio_pci_hidden_bar_ranges.o
 vfio-pci-core-$(CONFIG_VFIO_PCI_ZDEV_KVM) += vfio_pci_zdev.o
 vfio-pci-core-$(CONFIG_VFIO_PCI_DMABUF) += vfio_pci_dmabuf.o
+vfio-pci-core-$(CONFIG_VFIO_PCI_CONFIGFS) += vfio_pci_configfs.o
 obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o
 
 vfio-pci-y := vfio_pci.o
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e7c012febe912..18a2f9e0f6dc4 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -188,6 +188,7 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	dev_set_drvdata(&pdev->dev, vdev);
 	vdev->pci_ops = &vfio_pci_dev_ops;
+	vdev->hidden_ranges_supported = true;
 	ret = vfio_pci_core_register_device(vdev);
 	if (ret)
 		goto out_put_vdev;
diff --git a/drivers/vfio/pci/vfio_pci_configfs.c b/drivers/vfio/pci/vfio_pci_configfs.c
new file mode 100644
index 0000000000000..39de90ac23411
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_configfs.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VFIO PCI configuration through configfs
+ *
+ * The host administrator can hide BAR ranges of a device, in addition to the
+ * ones hidden by its driver, through /sys/kernel/config/vfio-pci/<device>/.
+ */
+
+#include <linux/configfs.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "vfio_pci_priv.h"
+
+/*
+ * Parse a "<bar>:<offset>:<size>" range, where the BAR index is decimal and
+ * the offset and the size are hexadecimal, with an optional 0x prefix.
+ */
+static int vfio_pci_parse_bar_range(const char *str,
+				    struct vfio_pci_bar_range *range)
+{
+	char *buf __free(kfree) = kstrdup(str, GFP_KERNEL);
+	char *cur, *bar, *offset;
+
+	if (!buf)
+		return -ENOMEM;
+
+	cur = strim(buf);
+	bar = strsep(&cur, ":");
+	offset = strsep(&cur, ":");
+
+	if (!cur || kstrtouint(bar, 10, &range->bar) ||
+	    kstrtou64(offset, 16, &range->offset) ||
+	    kstrtou64(cur, 16, &range->size))
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Ranges hidden when a device is registered, configured in a configfs
+ * directory named after the PCI device, which can be created before the
+ * device exists, e.g. for a VF before SR-IOV is enabled. The configuration
+ * can't change while the device is bound, so that it always matches the
+ * ranges hidden on a bound device.
+ */
+struct vfio_pci_configfs_device {
+	struct config_group group;
+	/* protects the ranges and bound */
+	struct mutex lock;
+	struct vfio_pci_bar_range hidden_ranges[VFIO_PCI_MAX_HIDDEN_BAR_RANGES];
+	unsigned int num_hidden_ranges;
+	/* the device is bound and pins the directory */
+	bool bound;
+};
+
+/* the devices bound to a VFIO driver, protected by the subsystem su_mutex */
+static LIST_HEAD(vfio_pci_configfs_bound);
+
+static struct vfio_pci_configfs_device *
+to_vfio_pci_configfs_device(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct vfio_pci_configfs_device, group);
+}
+
+static ssize_t vfio_pci_configfs_hidden_bar_ranges_show(struct config_item *item,
+							char *page)
+{
+	struct vfio_pci_configfs_device *cfg = to_vfio_pci_configfs_device(item);
+
+	guard(mutex)(&cfg->lock);
+
+	return vfio_pci_show_bar_ranges(cfg->hidden_ranges,
+					cfg->num_hidden_ranges, page);
+}
+
+static ssize_t vfio_pci_configfs_store_bar_range(struct config_item *item,
+						 const char *page, size_t len,
+						 bool hide)
+{
+	struct vfio_pci_configfs_device *cfg = to_vfio_pci_configfs_device(item);
+	struct vfio_pci_bar_range range;
+	int ret;
+
+	ret = vfio_pci_parse_bar_range(page, &range);
+	if (ret)
+		return ret;
+
+	guard(mutex)(&cfg->lock);
+
+	if (cfg->bound)
+		return -EBUSY;
+
+	if (hide)
+		ret = vfio_pci_add_bar_range(cfg->hidden_ranges,
+					     &cfg->num_hidden_ranges, &range);
+	else
+		ret = vfio_pci_del_bar_range(cfg->hidden_ranges,
+					     &cfg->num_hidden_ranges, &range);
+
+	return ret ?: len;
+}
+
+static ssize_t vfio_pci_configfs_hide_bar_range_store(struct config_item *item,
+						      const char *page, size_t len)
+{
+	return vfio_pci_configfs_store_bar_range(item, page, len, true);
+}
+
+static ssize_t vfio_pci_configfs_unhide_bar_range_store(struct config_item *item,
+							const char *page, size_t len)
+{
+	return vfio_pci_configfs_store_bar_range(item, page, len, false);
+}
+
+CONFIGFS_ATTR_RO(vfio_pci_configfs_, hidden_bar_ranges);
+CONFIGFS_ATTR_WO(vfio_pci_configfs_, hide_bar_range);
+CONFIGFS_ATTR_WO(vfio_pci_configfs_, unhide_bar_range);
+
+static struct configfs_attribute *vfio_pci_configfs_device_attrs[] = {
+	&vfio_pci_configfs_attr_hidden_bar_ranges,
+	&vfio_pci_configfs_attr_hide_bar_range,
+	&vfio_pci_configfs_attr_unhide_bar_range,
+	NULL
+};
+
+static void vfio_pci_configfs_device_release(struct config_item *item)
+{
+	struct vfio_pci_configfs_device *cfg = to_vfio_pci_configfs_device(item);
+
+	mutex_destroy(&cfg->lock);
+	kfree(cfg);
+}
+
+static struct configfs_item_operations vfio_pci_configfs_device_item_ops = {
+	.release = vfio_pci_configfs_device_release,
+};
+
+static const struct config_item_type vfio_pci_configfs_device_type = {
+	.ct_item_ops = &vfio_pci_configfs_device_item_ops,
+	.ct_attrs = vfio_pci_configfs_device_attrs,
+	.ct_owner = THIS_MODULE,
+};
+
+/* only the canonical name, as returned by pci_name(), is looked up */
+static int vfio_pci_configfs_check_name(const char *name)
+{
+	unsigned int domain, bus, slot, function;
+	char canonical[sizeof("ffffffff:ff:1f.7")];
+
+	if (sscanf(name, "%x:%x:%x.%x", &domain, &bus, &slot, &function) != 4 ||
+	    bus > 0xff || slot > 0x1f || function > 7)
+		return -EINVAL;
+
+	scnprintf(canonical, sizeof(canonical), "%04x:%02x:%02x.%u",
+		  domain, bus, slot, function);
+
+	return strcmp(name, canonical) ? -EINVAL : 0;
+}
+
+static struct config_group *vfio_pci_configfs_make_group(struct config_group *group,
+							 const char *name)
+{
+	struct vfio_pci_configfs_device *cfg;
+	struct vfio_pci_core_device *vdev;
+	int ret;
+
+	ret = vfio_pci_configfs_check_name(name);
+	if (ret)
+		return ERR_PTR(ret);
+
+	list_for_each_entry(vdev, &vfio_pci_configfs_bound, configfs_node) {
+		if (!strcmp(pci_name(vdev->pdev), name))
+			return ERR_PTR(-EBUSY);
+	}
+
+	cfg = kzalloc_obj(*cfg);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&cfg->lock);
+	config_group_init_type_name(&cfg->group, name,
+				    &vfio_pci_configfs_device_type);
+
+	return &cfg->group;
+}
+
+static struct configfs_group_operations vfio_pci_configfs_group_ops = {
+	.make_group = vfio_pci_configfs_make_group,
+};
+
+static const struct config_item_type vfio_pci_configfs_type = {
+	.ct_group_ops = &vfio_pci_configfs_group_ops,
+	.ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem vfio_pci_configfs = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "vfio-pci",
+			.ci_type = &vfio_pci_configfs_type,
+		},
+	},
+};
+
+/*
+ * Hide the ranges configured for the device being bound and keep the
+ * configuration from changing, including the creation and removal of the
+ * directory, until it is unbound.
+ */
+int vfio_pci_configfs_bind(struct vfio_pci_core_device *vdev)
+{
+	struct vfio_pci_configfs_device *cfg;
+	struct config_item *item;
+	int ret;
+
+	scoped_guard(mutex, &vfio_pci_configfs.su_mutex) {
+		item = config_group_find_item(&vfio_pci_configfs.su_group,
+					      pci_name(vdev->pdev));
+		list_add(&vdev->configfs_node, &vfio_pci_configfs_bound);
+	}
+	if (!item)
+		return 0;
+
+	cfg = to_vfio_pci_configfs_device(item);
+
+	ret = configfs_depend_item(&vfio_pci_configfs, item);
+	if (ret) {
+		pci_err(vdev->pdev, "configfs directory being created or removed (%pe)\n",
+			ERR_PTR(ret));
+		ret = -EBUSY;
+		goto out_unbind;
+	}
+
+	scoped_guard(mutex, &cfg->lock) {
+		if (cfg->num_hidden_ranges && !vdev->hidden_ranges_supported) {
+			pci_err(vdev->pdev, "%s doesn't support hiding the BAR ranges configured through configfs\n",
+				vdev->vdev.ops->name);
+			ret = -EOPNOTSUPP;
+		} else {
+			ret = __vfio_pci_hide_bar_ranges(vdev, cfg->hidden_ranges,
+							 cfg->num_hidden_ranges,
+							 "configfs");
+		}
+		cfg->bound = !ret;
+	}
+	if (ret) {
+		configfs_undepend_item(item);
+		goto out_unbind;
+	}
+
+	config_item_put(item);
+
+	return 0;
+
+out_unbind:
+	config_item_put(item);
+	scoped_guard(mutex, &vfio_pci_configfs.su_mutex)
+		list_del(&vdev->configfs_node);
+
+	return ret;
+}
+
+void vfio_pci_configfs_unbind(struct vfio_pci_core_device *vdev)
+{
+	struct vfio_pci_configfs_device *cfg;
+	struct config_item *item;
+	bool pinned;
+
+	scoped_guard(mutex, &vfio_pci_configfs.su_mutex) {
+		list_del(&vdev->configfs_node);
+		item = config_group_find_item(&vfio_pci_configfs.su_group,
+					      pci_name(vdev->pdev));
+	}
+	if (!item)
+		return;
+
+	cfg = to_vfio_pci_configfs_device(item);
+
+	scoped_guard(mutex, &cfg->lock) {
+		pinned = cfg->bound;
+		cfg->bound = false;
+	}
+	if (pinned)
+		configfs_undepend_item(item);
+
+	config_item_put(item);
+}
+
+int vfio_pci_configfs_init(void)
+{
+	int ret;
+
+	config_group_init(&vfio_pci_configfs.su_group);
+	mutex_init(&vfio_pci_configfs.su_mutex);
+
+	ret = configfs_register_subsystem(&vfio_pci_configfs);
+	if (ret)
+		mutex_destroy(&vfio_pci_configfs.su_mutex);
+
+	return ret;
+}
+
+void vfio_pci_configfs_exit(void)
+{
+	configfs_unregister_subsystem(&vfio_pci_configfs);
+	mutex_destroy(&vfio_pci_configfs.su_mutex);
+}
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index cd6cb3d829b51..e32428a07583f 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -2275,6 +2275,10 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
 	if (ret)
 		goto out_vf;
 
+	ret = vfio_pci_configfs_bind(vdev);
+	if (ret)
+		goto out_vga;
+
 	vfio_pci_probe_power_state(vdev);
 
 	/*
@@ -2310,6 +2314,8 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
 		pm_runtime_get_noresume(dev);
 
 	pm_runtime_forbid(dev);
+	vfio_pci_configfs_unbind(vdev);
+out_vga:
 	vfio_pci_vga_uninit(vdev);
 out_vf:
 	vfio_pci_vf_uninit(vdev);
@@ -2323,6 +2329,7 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev)
 
 	vfio_unregister_group_dev(&vdev->vdev);
 
+	vfio_pci_configfs_unbind(vdev);
 	vfio_pci_vf_uninit(vdev);
 	vfio_pci_vga_uninit(vdev);
 
@@ -2667,14 +2674,25 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
 
 static void vfio_pci_core_cleanup(void)
 {
+	vfio_pci_configfs_exit();
 	vfio_pci_uninit_perm_bits();
 	rcu_barrier();
 }
 
 static int __init vfio_pci_core_init(void)
 {
+	int ret;
+
 	/* Allocate shared config space permission data used by all devices */
-	return vfio_pci_init_perm_bits();
+	ret = vfio_pci_init_perm_bits();
+	if (ret)
+		return ret;
+
+	ret = vfio_pci_configfs_init();
+	if (ret)
+		vfio_pci_uninit_perm_bits();
+
+	return ret;
 }
 
 module_init(vfio_pci_core_init);
diff --git a/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c b/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c
index 666820dc24080..dd8418aa81305 100644
--- a/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c
+++ b/drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c
@@ -8,6 +8,10 @@
  * reads of them are filled with ones and writes to them are dropped, the
  * pages containing them are never mapped, and neither ioeventfds nor
  * dma-bufs may cover them.
+ *
+ * The ranges are hidden by the driver of the device and the host
+ * administrator can hide more of them through configfs, see
+ * vfio_pci_configfs.c.
  */
 
 #include <linux/device.h>
@@ -54,9 +58,9 @@ static int vfio_pci_check_bar_range(const struct vfio_pci_bar_range *range)
 	return 0;
 }
 
-static int vfio_pci_add_bar_range(struct vfio_pci_bar_range *ranges,
-				  unsigned int *num,
-				  const struct vfio_pci_bar_range *range)
+int vfio_pci_add_bar_range(struct vfio_pci_bar_range *ranges,
+			   unsigned int *num,
+			   const struct vfio_pci_bar_range *range)
 {
 	unsigned int i;
 	int ret;
@@ -96,8 +100,8 @@ static void vfio_pci_remove_bar_range(struct vfio_pci_bar_range *ranges,
 	(*num)--;
 }
 
-static ssize_t vfio_pci_show_bar_ranges(const struct vfio_pci_bar_range *ranges,
-					unsigned int num, char *buf)
+ssize_t vfio_pci_show_bar_ranges(const struct vfio_pci_bar_range *ranges,
+				 unsigned int num, char *buf)
 {
 	unsigned int i;
 	ssize_t len = 0;
@@ -149,9 +153,9 @@ static int vfio_pci_hide_bar_range(struct vfio_pci_core_device *vdev,
 				      &vdev->num_hidden_ranges, &merged);
 }
 
-static int __vfio_pci_hide_bar_ranges(struct vfio_pci_core_device *vdev,
-				      const struct vfio_pci_bar_range *ranges,
-				      unsigned int num, const char *source)
+int __vfio_pci_hide_bar_ranges(struct vfio_pci_core_device *vdev,
+			       const struct vfio_pci_bar_range *ranges,
+			       unsigned int num, const char *source)
 {
 	unsigned int i;
 	int ret;
@@ -176,11 +180,13 @@ static int __vfio_pci_hide_bar_ranges(struct vfio_pci_core_device *vdev,
  * @num: number of entries in @ranges
  *
  * Must be called before vfio_pci_core_register_device(), so that the ranges
- * are hidden before the device can be opened. The ranges are hidden by the
- * accesses done through vfio-pci-core: region reads and writes, region infos,
- * mmaps, ioeventfds and dma-bufs. The driver must declare that it doesn't
- * access its memory BARs in any other way by setting
- * &vfio_pci_core_device.hidden_ranges_supported first.
+ * are hidden before the device can be opened. The ranges configured by the
+ * host administrator through configfs, if CONFIG_VFIO_PCI_CONFIGFS is enabled,
+ * are hidden by vfio_pci_core_register_device() after the ones of the driver.
+ * The ranges are hidden by the accesses done through vfio-pci-core: region
+ * reads and writes, region infos, mmaps, ioeventfds and dma-bufs. The driver
+ * must declare that it doesn't access its memory BARs in any other way by
+ * setting &vfio_pci_core_device.hidden_ranges_supported first.
  *
  * The accesses crossing the boundaries of a hidden range are split, so the
  * range should match the access granularity of the device, e.g. its register
@@ -287,3 +293,21 @@ const struct attribute_group *vfio_pci_hidden_bar_ranges_groups[] = {
 	&vfio_pci_hidden_bar_ranges_group,
 	NULL
 };
+
+int vfio_pci_del_bar_range(struct vfio_pci_bar_range *ranges,
+			   unsigned int *num,
+			   const struct vfio_pci_bar_range *range)
+{
+	unsigned int i;
+
+	for (i = 0; i < *num; i++) {
+		if (ranges[i].bar == range->bar &&
+		    ranges[i].offset == range->offset &&
+		    ranges[i].size == range->size) {
+			vfio_pci_remove_bar_range(ranges, num, i);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index a3d42a37176de..6632a0eccbe4d 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -60,6 +60,42 @@ int vfio_pci_hidden_add_sparse_mmap(struct vfio_pci_core_device *vdev,
 				    struct vfio_region_info *info,
 				    struct vfio_info_cap *caps);
 extern const struct attribute_group *vfio_pci_hidden_bar_ranges_groups[];
+int vfio_pci_add_bar_range(struct vfio_pci_bar_range *ranges,
+			   unsigned int *num,
+			   const struct vfio_pci_bar_range *range);
+int vfio_pci_del_bar_range(struct vfio_pci_bar_range *ranges,
+			   unsigned int *num,
+			   const struct vfio_pci_bar_range *range);
+ssize_t vfio_pci_show_bar_ranges(const struct vfio_pci_bar_range *ranges,
+				 unsigned int num, char *buf);
+int __vfio_pci_hide_bar_ranges(struct vfio_pci_core_device *vdev,
+			       const struct vfio_pci_bar_range *ranges,
+			       unsigned int num, const char *source);
+
+#ifdef CONFIG_VFIO_PCI_CONFIGFS
+int vfio_pci_configfs_bind(struct vfio_pci_core_device *vdev);
+void vfio_pci_configfs_unbind(struct vfio_pci_core_device *vdev);
+int vfio_pci_configfs_init(void);
+void vfio_pci_configfs_exit(void);
+#else
+static inline int vfio_pci_configfs_bind(struct vfio_pci_core_device *vdev)
+{
+	return 0;
+}
+
+static inline void vfio_pci_configfs_unbind(struct vfio_pci_core_device *vdev)
+{
+}
+
+static inline int vfio_pci_configfs_init(void)
+{
+	return 0;
+}
+
+static inline void vfio_pci_configfs_exit(void)
+{
+}
+#endif
 
 #ifdef CONFIG_VFIO_PCI_VGA
 ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 3745911fb2f1f..8bc5140958ce6 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -177,6 +177,10 @@ struct vfio_pci_core_device {
 	 * BAR ranges.
 	 */
 	bool			hidden_ranges_supported;
+#ifdef CONFIG_VFIO_PCI_CONFIGFS
+	/* in the list of the bound devices, whose configfs can't change */
+	struct list_head	configfs_node;
+#endif
 };
 
 enum vfio_pci_io_width {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID
  2026-11-05 15:37 [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
                   ` (2 preceding siblings ...)
  2026-11-05 15:37 ` [RFC PATCH 3/4] vfio/pci: Allow configuring hidden BAR ranges through configfs Piórkowski, Piotr
@ 2026-11-05 15:37 ` Piórkowski, Piotr
  2026-11-05 15:55   ` Alex Williamson
  2026-11-05 15:50 ` [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Alex Williamson
  4 siblings, 1 reply; 9+ messages in thread
From: Piórkowski, Piotr @ 2026-11-05 15:37 UTC (permalink / raw)
  To: alex, kvm
  Cc: intel-xe, Piotr Piórkowski, Michał Winiarski,
	Jason Gunthorpe, Yishai Hadas, Shameer Kolothum, Kevin Tian

From: Piotr Piórkowski <piotr.piorkowski@intel.com>

xe-vfio-pci accesses the BARs only through vfio-pci-core, so opt in to
hidden BAR ranges.

Let each entry of the PCI ID table describe the ranges to hide by
default. The description is always looked up in the static table, so
binding through new_id or driver_override can't skip them.

No device defines any ranges yet.

Cc: Michał Winiarski <michal.winiarski@intel.com>
Cc: Alex Williamson <alex@shazbot.org>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Yishai Hadas <yishaih@nvidia.com>
Cc: Shameer Kolothum <skolothumtho@nvidia.com>
Cc: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
---
 drivers/vfio/pci/xe/main.c | 60 ++++++++++++++++++++++++++++----------
 1 file changed, 45 insertions(+), 15 deletions(-)

diff --git a/drivers/vfio/pci/xe/main.c b/drivers/vfio/pci/xe/main.c
index 7dab3e024aa8a..e63af6123f598 100644
--- a/drivers/vfio/pci/xe/main.c
+++ b/drivers/vfio/pci/xe/main.c
@@ -24,6 +24,16 @@ struct xe_vfio_pci_migration_file {
 	u8 disabled:1;
 };
 
+/**
+ * struct xe_vfio_pci_desc - device specific description
+ * @hidden_ranges: BAR ranges hidden from the VFIO user by default
+ * @num_hidden_ranges: number of entries in @hidden_ranges
+ */
+struct xe_vfio_pci_desc {
+	const struct vfio_pci_bar_range *hidden_ranges;
+	unsigned int num_hidden_ranges;
+};
+
 struct xe_vfio_pci_core_device {
 	struct vfio_pci_core_device core_device;
 	struct xe_device *xe;
@@ -543,8 +553,32 @@ static const struct vfio_device_ops xe_vfio_pci_ops = {
 	.detach_ioas = vfio_iommufd_physical_detach_ioas,
 };
 
+#define INTEL_PCI_VFIO_DEVICE(_id, _desc) { \
+	PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_INTEL, (_id)), \
+	.driver_data = (kernel_ulong_t)(_desc), \
+}
+
+static const struct pci_device_id xe_vfio_pci_table[] = {
+	INTEL_BMG_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
+	INTEL_CRI_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
+	INTEL_NVLS_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
+	INTEL_NVLP_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
+	INTEL_PTL_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
+	INTEL_WCL_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
+	{}
+};
+MODULE_DEVICE_TABLE(pci, xe_vfio_pci_table);
+
 static int xe_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+	/*
+	 * Dynamic IDs added through new_id are matched before the static ones
+	 * and carry no driver data, look the description up in the static
+	 * table so that they can't bypass the default hidden ranges.
+	 */
+	const struct pci_device_id *match = pci_match_id(xe_vfio_pci_table, pdev);
+	const struct xe_vfio_pci_desc *desc =
+		match ? (const void *)match->driver_data : NULL;
 	struct xe_vfio_pci_core_device *xe_vdev;
 	int ret;
 
@@ -554,6 +588,17 @@ static int xe_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
 		return PTR_ERR(xe_vdev);
 
 	dev_set_drvdata(&pdev->dev, xe_vdev);
+	xe_vdev->core_device.hidden_ranges_supported = true;
+
+	if (desc) {
+		ret = vfio_pci_core_hide_bar_ranges(&xe_vdev->core_device,
+						    desc->hidden_ranges,
+						    desc->num_hidden_ranges);
+		if (ret) {
+			vfio_put_device(&xe_vdev->core_device.vdev);
+			return ret;
+		}
+	}
 
 	ret = vfio_pci_core_register_device(&xe_vdev->core_device);
 	if (ret) {
@@ -572,21 +617,6 @@ static void xe_vfio_pci_remove(struct pci_dev *pdev)
 	vfio_put_device(&xe_vdev->core_device.vdev);
 }
 
-#define INTEL_PCI_VFIO_DEVICE(_id) { \
-	PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_INTEL, (_id)) \
-}
-
-static const struct pci_device_id xe_vfio_pci_table[] = {
-	INTEL_BMG_IDS(INTEL_PCI_VFIO_DEVICE),
-	INTEL_CRI_IDS(INTEL_PCI_VFIO_DEVICE),
-	INTEL_NVLS_IDS(INTEL_PCI_VFIO_DEVICE),
-	INTEL_NVLP_IDS(INTEL_PCI_VFIO_DEVICE),
-	INTEL_PTL_IDS(INTEL_PCI_VFIO_DEVICE),
-	INTEL_WCL_IDS(INTEL_PCI_VFIO_DEVICE),
-	{}
-};
-MODULE_DEVICE_TABLE(pci, xe_vfio_pci_table);
-
 static struct pci_driver xe_vfio_pci_driver = {
 	.name = "xe-vfio-pci",
 	.id_table = xe_vfio_pci_table,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user
  2026-11-05 15:37 [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
                   ` (3 preceding siblings ...)
  2026-11-05 15:37 ` [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID Piórkowski, Piotr
@ 2026-11-05 15:50 ` Alex Williamson
  4 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2026-11-05 15:50 UTC (permalink / raw)
  To: Piórkowski, Piotr; +Cc: kvm, intel-xe, alex, Manish Honap

On Thu, 5 Nov 2026 16:37:34 +0100
"Piórkowski, Piotr" <piotr.piorkowski@intel.com> wrote:

> From: Piotr Piórkowski <piotr.piorkowski@intel.com>
> 
> Some devices have BAR ranges which shouldn't be exposed to the VFIO
> user, but vfio-pci exposes the memory BARs as a whole, apart from the
> MSI-X table.
> 
> This series lets the host hide ranges of the memory BARs. Reads of a
> hidden range return ones, writes to it are dropped, the pages containing
> it are never mapped (a sparse mmap capability advertises the rest of the
> BAR), and ioeventfds and dma-bufs covering it are rejected. The ranges
> are fixed while the device is bound and listed in
> /sys/.../vfio-dev/vfioX/hidden_bar_ranges.
> 
> The ranges can be hidden by the driver, with
> vfio_pci_core_hide_bar_ranges(), and by the host administrator, through
> configfs (CONFIG_VFIO_PCI_CONFIGFS). Only drivers which access their
> BARs exclusively through vfio-pci-core can hide ranges; vfio-pci and
> xe-vfio-pci declare it.
> 
> Patch 1 is a preparatory refactor, patch 2 adds the core mechanism,
> patch 3 the configfs interface and patch 4 per-device-ID defaults in
> xe-vfio-pci, with no ranges defined yet.
> 
> Sent as RFC for feedback on the approach and the interfaces.

See Manish's proposal for a generic excluded range interface to
generalize MSI-X table read/write and disable all access to CXL HDM
decoders:

https://lore.kernel.org/all/20261016161839.2765592-9-mhonap@nvidia.com/

Granting users the ability to manipulate excluded ranges through
configfs seems like a terrible idea, frankly.  Thanks,

Alex

> 
> Piotr Piórkowski (4):
>   vfio/pci: Move the excluded range handling out of
>     vfio_pci_core_do_io_rw()
>   vfio/pci: Allow hiding BAR ranges from the VFIO user
>   vfio/pci: Allow configuring hidden BAR ranges through configfs
>   vfio/xe: Hide BAR ranges by default per device ID
> 
>  Documentation/ABI/testing/configfs-vfio-pci   |  49 +++
>  .../ABI/testing/sysfs-devices-vfio-dev        |  13 +
>  drivers/vfio/pci/Kconfig                      |  13 +
>  drivers/vfio/pci/Makefile                     |   4 +-
>  drivers/vfio/pci/nvgrace-gpu/main.c           |   4 +-
>  drivers/vfio/pci/vfio_pci.c                   |   1 +
>  drivers/vfio/pci/vfio_pci_configfs.c          | 312 +++++++++++++++++
>  drivers/vfio/pci/vfio_pci_core.c              |  42 ++-
>  drivers/vfio/pci/vfio_pci_dmabuf.c            |  11 +
>  drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c | 313 ++++++++++++++++++
>  drivers/vfio/pci/vfio_pci_priv.h              |  53 +++
>  drivers/vfio/pci/vfio_pci_rdwr.c              | 156 +++++++--
>  drivers/vfio/pci/xe/main.c                    |  60 +++-
>  include/linux/vfio_pci_core.h                 |  36 +-
>  14 files changed, 1009 insertions(+), 58 deletions(-)
>  create mode 100644 Documentation/ABI/testing/configfs-vfio-pci
>  create mode 100644 drivers/vfio/pci/vfio_pci_configfs.c
>  create mode 100644 drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID
  2026-11-05 15:37 ` [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID Piórkowski, Piotr
@ 2026-11-05 15:55   ` Alex Williamson
  2026-11-05 17:50     ` Piotr Piórkowski
  0 siblings, 1 reply; 9+ messages in thread
From: Alex Williamson @ 2026-11-05 15:55 UTC (permalink / raw)
  To: Piórkowski, Piotr, Shameer Kolothum
  Cc: kvm, intel-xe, Michał Winiarski, Jason Gunthorpe,
	Yishai Hadas, Kevin Tian, alex

On Thu, 5 Nov 2026 16:37:38 +0100
"Piórkowski, Piotr" <piotr.piorkowski@intel.com> wrote:

> From: Piotr Piórkowski <piotr.piorkowski@intel.com>
> 
> xe-vfio-pci accesses the BARs only through vfio-pci-core, so opt in to
> hidden BAR ranges.
> 
> Let each entry of the PCI ID table describe the ranges to hide by
> default. The description is always looked up in the static table, so
> binding through new_id or driver_override can't skip them.
> 
> No device defines any ranges yet.

We don't merge dead code, so the whole series is to justify the
configfs mechanism to introduce excluded ranges without publishing them
to the community?  Hard no.  Thanks,

Alex

> 
> Cc: Michał Winiarski <michal.winiarski@intel.com>
> Cc: Alex Williamson <alex@shazbot.org>
> Cc: Jason Gunthorpe <jgg@ziepe.ca>
> Cc: Yishai Hadas <yishaih@nvidia.com>
> Cc: Shameer Kolothum <skolothumtho@nvidia.com>
> Cc: Kevin Tian <kevin.tian@intel.com>
> Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
> ---
>  drivers/vfio/pci/xe/main.c | 60 ++++++++++++++++++++++++++++----------
>  1 file changed, 45 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/vfio/pci/xe/main.c b/drivers/vfio/pci/xe/main.c
> index 7dab3e024aa8a..e63af6123f598 100644
> --- a/drivers/vfio/pci/xe/main.c
> +++ b/drivers/vfio/pci/xe/main.c
> @@ -24,6 +24,16 @@ struct xe_vfio_pci_migration_file {
>  	u8 disabled:1;
>  };
>  
> +/**
> + * struct xe_vfio_pci_desc - device specific description
> + * @hidden_ranges: BAR ranges hidden from the VFIO user by default
> + * @num_hidden_ranges: number of entries in @hidden_ranges
> + */
> +struct xe_vfio_pci_desc {
> +	const struct vfio_pci_bar_range *hidden_ranges;
> +	unsigned int num_hidden_ranges;
> +};
> +
>  struct xe_vfio_pci_core_device {
>  	struct vfio_pci_core_device core_device;
>  	struct xe_device *xe;
> @@ -543,8 +553,32 @@ static const struct vfio_device_ops xe_vfio_pci_ops = {
>  	.detach_ioas = vfio_iommufd_physical_detach_ioas,
>  };
>  
> +#define INTEL_PCI_VFIO_DEVICE(_id, _desc) { \
> +	PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_INTEL, (_id)), \
> +	.driver_data = (kernel_ulong_t)(_desc), \
> +}
> +
> +static const struct pci_device_id xe_vfio_pci_table[] = {
> +	INTEL_BMG_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> +	INTEL_CRI_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> +	INTEL_NVLS_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> +	INTEL_NVLP_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> +	INTEL_PTL_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> +	INTEL_WCL_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, xe_vfio_pci_table);
> +
>  static int xe_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  {
> +	/*
> +	 * Dynamic IDs added through new_id are matched before the static ones
> +	 * and carry no driver data, look the description up in the static
> +	 * table so that they can't bypass the default hidden ranges.
> +	 */
> +	const struct pci_device_id *match = pci_match_id(xe_vfio_pci_table, pdev);
> +	const struct xe_vfio_pci_desc *desc =
> +		match ? (const void *)match->driver_data : NULL;
>  	struct xe_vfio_pci_core_device *xe_vdev;
>  	int ret;
>  
> @@ -554,6 +588,17 @@ static int xe_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
>  		return PTR_ERR(xe_vdev);
>  
>  	dev_set_drvdata(&pdev->dev, xe_vdev);
> +	xe_vdev->core_device.hidden_ranges_supported = true;
> +
> +	if (desc) {
> +		ret = vfio_pci_core_hide_bar_ranges(&xe_vdev->core_device,
> +						    desc->hidden_ranges,
> +						    desc->num_hidden_ranges);
> +		if (ret) {
> +			vfio_put_device(&xe_vdev->core_device.vdev);
> +			return ret;
> +		}
> +	}
>  
>  	ret = vfio_pci_core_register_device(&xe_vdev->core_device);
>  	if (ret) {
> @@ -572,21 +617,6 @@ static void xe_vfio_pci_remove(struct pci_dev *pdev)
>  	vfio_put_device(&xe_vdev->core_device.vdev);
>  }
>  
> -#define INTEL_PCI_VFIO_DEVICE(_id) { \
> -	PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_INTEL, (_id)) \
> -}
> -
> -static const struct pci_device_id xe_vfio_pci_table[] = {
> -	INTEL_BMG_IDS(INTEL_PCI_VFIO_DEVICE),
> -	INTEL_CRI_IDS(INTEL_PCI_VFIO_DEVICE),
> -	INTEL_NVLS_IDS(INTEL_PCI_VFIO_DEVICE),
> -	INTEL_NVLP_IDS(INTEL_PCI_VFIO_DEVICE),
> -	INTEL_PTL_IDS(INTEL_PCI_VFIO_DEVICE),
> -	INTEL_WCL_IDS(INTEL_PCI_VFIO_DEVICE),
> -	{}
> -};
> -MODULE_DEVICE_TABLE(pci, xe_vfio_pci_table);
> -
>  static struct pci_driver xe_vfio_pci_driver = {
>  	.name = "xe-vfio-pci",
>  	.id_table = xe_vfio_pci_table,


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 2/4] vfio/pci: Allow hiding BAR ranges from the VFIO user
  2026-11-05 15:37 ` [RFC PATCH 2/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
@ 2026-11-05 16:24   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-05 16:24 UTC (permalink / raw)
  To: Piórkowski, Piotr, kvm; +Cc: intel-xe

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] vfio/pci: MSIX_MAPPABLE cap added before vfio_pci_hidden_add_sparse_mmap()
--

commit 34f058d0ce2fee9063501ec235bccdb3f8fe57f0
Author: Piotr Piórkowski <piotr.piorkowski@intel.com>

vfio/pci: Allow hiding BAR ranges from the VFIO user

This commit adds vfio_pci_core_hide_bar_ranges() to allow VFIO PCI drivers
to hide parts of their memory BARs from userspace across region reads and
writes, mmap, ioeventfd, and dma-buf operations, while listing the hidden
ranges in sysfs.

> diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
> index fcbb2da860e2a..cd6cb3d829b51 100644
> --- a/drivers/vfio/pci/vfio_pci_core.c
> +++ b/drivers/vfio/pci/vfio_pci_core.c
> @@ -1147,6 +1147,10 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
>  				if (ret)
>  					return ret;
>  			}
> +
> +			ret = vfio_pci_hidden_add_sparse_mmap(vdev, info, caps);
> +			if (ret)
> +				return ret;
>  		}
>  
>  		break;

[Severity: Medium]
Does calling msix_mmappable_cap() before vfio_pci_hidden_add_sparse_mmap()
report inconsistent region capabilities when hidden ranges are present on
vdev->msix_bar?

In vfio_pci_ioctl_get_region_info(), when vdev->bar_mmap_supported[info->index]
is true and info->index == vdev->msix_bar, msix_mmappable_cap() appends
VFIO_REGION_INFO_CAP_MSIX_MAPPABLE to caps before
vfio_pci_hidden_add_sparse_mmap() runs:

drivers/vfio/pci/vfio_pci_core.c:vfio_pci_ioctl_get_region_info() {
		if (vdev->bar_mmap_supported[info->index]) {
			info->flags |= VFIO_REGION_INFO_FLAG_MMAP;
			if (info->index == vdev->msix_bar) {
				ret = msix_mmappable_cap(vdev, caps);
				if (ret)
					return ret;
			}

			ret = vfio_pci_hidden_add_sparse_mmap(vdev, info, caps);
			if (ret)
				return ret;
		}
}

If hidden ranges cover all pages of that BAR, vfio_pci_hidden_add_sparse_mmap()
computes nr_areas == 0 and clears VFIO_REGION_INFO_FLAG_MMAP:

drivers/vfio/pci/vfio_pci_hidden_bar_ranges.c:vfio_pci_hidden_add_sparse_mmap() {
	if (!nr_areas) {
		info->flags &= ~VFIO_REGION_INFO_FLAG_MMAP;
		goto out;
	}
}

Because VFIO_REGION_INFO_CAP_MSIX_MAPPABLE was already added to caps,
VFIO_DEVICE_GET_REGION_INFO will return a region with
VFIO_REGION_INFO_FLAG_MMAP cleared while still advertising
VFIO_REGION_INFO_FLAG_CAPS and VFIO_REGION_INFO_CAP_MSIX_MAPPABLE.

Similarly, even when nr_areas > 0, if a hidden range overlaps the page(s)
containing the MSI-X table (vdev->msix_offset to vdev->msix_offset +
vdev->msix_size), those pages are excluded from
VFIO_REGION_INFO_CAP_SPARSE_MMAP and faulting them in
vfio_pci_mmap_huge_fault() returns VM_FAULT_SIGBUS, while
VFIO_REGION_INFO_CAP_MSIX_MAPPABLE still tells userspace the MSI-X range is
mmappable.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261105153738.1938345-1-piotr.piorkowski@intel.com?part=2

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID
  2026-11-05 15:55   ` Alex Williamson
@ 2026-11-05 17:50     ` Piotr Piórkowski
  0 siblings, 0 replies; 9+ messages in thread
From: Piotr Piórkowski @ 2026-11-05 17:50 UTC (permalink / raw)
  To: Alex Williamson
  Cc: Shameer Kolothum, kvm, intel-xe, Michał Winiarski,
	Jason Gunthorpe, Yishai Hadas, Kevin Tian

Alex Williamson <alex@shazbot.org> wrote on czw [2026-lis-05 08:55:58 -0700]:
> On Thu, 5 Nov 2026 16:37:38 +0100
> "Piórkowski, Piotr" <piotr.piorkowski@intel.com> wrote:
> 
> > From: Piotr Piórkowski <piotr.piorkowski@intel.com>
> > 
> > xe-vfio-pci accesses the BARs only through vfio-pci-core, so opt in to
> > hidden BAR ranges.
> > 
> > Let each entry of the PCI ID table describe the ranges to hide by
> > default. The description is always looked up in the static table, so
> > binding through new_id or driver_override can't skip them.
> > 
> > No device defines any ranges yet.
> 
> We don't merge dead code, so the whole series is to justify the
> configfs mechanism to introduce excluded ranges without publishing them
> to the community?  Hard no.  Thanks,

Thanks for the link, I wasn't aware of that series. I went through
patches 08 to 10 and they cover most of what I did here: the per-BAR
excluded-range list, the sparse mmap capability around the ranges, and
refusing ioeventfds and dma-buf exports over them. So patches 1 and 2
of my series are largely redundant, and the per-path flags
(EXCLUDE_MMAP/READ/WRITE) are more flexible than my all-or-nothing
hiding. 

On the configfs part: fair enough, I'll drop it.

I should also be clearer about the intent of the series, because the
way I posted it was misleading. It wasn't an attempt to merge dead
code, or to sneak in a configfs interface. I'm working on hardware
where part of a memory BAR has to stay with the host while the device
is assigned to a VM, and I wanted early feedback on the mechanism
while the design can still change. The fact that no device declares
any range yet is just a consequence of that timing - the hardware
isn't public, so I had nothing to put in the table.

I'll come back once his series settles, with just the device-specific
part on top of it.

Thanks,
Piotr

> 
> Alex
> 
> > 
> > Cc: Michał Winiarski <michal.winiarski@intel.com>
> > Cc: Alex Williamson <alex@shazbot.org>
> > Cc: Jason Gunthorpe <jgg@ziepe.ca>
> > Cc: Yishai Hadas <yishaih@nvidia.com>
> > Cc: Shameer Kolothum <skolothumtho@nvidia.com>
> > Cc: Kevin Tian <kevin.tian@intel.com>
> > Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
> > ---
> >  drivers/vfio/pci/xe/main.c | 60 ++++++++++++++++++++++++++++----------
> >  1 file changed, 45 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/vfio/pci/xe/main.c b/drivers/vfio/pci/xe/main.c
> > index 7dab3e024aa8a..e63af6123f598 100644
> > --- a/drivers/vfio/pci/xe/main.c
> > +++ b/drivers/vfio/pci/xe/main.c
> > @@ -24,6 +24,16 @@ struct xe_vfio_pci_migration_file {
> >  	u8 disabled:1;
> >  };
> >  
> > +/**
> > + * struct xe_vfio_pci_desc - device specific description
> > + * @hidden_ranges: BAR ranges hidden from the VFIO user by default
> > + * @num_hidden_ranges: number of entries in @hidden_ranges
> > + */
> > +struct xe_vfio_pci_desc {
> > +	const struct vfio_pci_bar_range *hidden_ranges;
> > +	unsigned int num_hidden_ranges;
> > +};
> > +
> >  struct xe_vfio_pci_core_device {
> >  	struct vfio_pci_core_device core_device;
> >  	struct xe_device *xe;
> > @@ -543,8 +553,32 @@ static const struct vfio_device_ops xe_vfio_pci_ops = {
> >  	.detach_ioas = vfio_iommufd_physical_detach_ioas,
> >  };
> >  
> > +#define INTEL_PCI_VFIO_DEVICE(_id, _desc) { \
> > +	PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_INTEL, (_id)), \
> > +	.driver_data = (kernel_ulong_t)(_desc), \
> > +}
> > +
> > +static const struct pci_device_id xe_vfio_pci_table[] = {
> > +	INTEL_BMG_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> > +	INTEL_CRI_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> > +	INTEL_NVLS_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> > +	INTEL_NVLP_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> > +	INTEL_PTL_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> > +	INTEL_WCL_IDS(INTEL_PCI_VFIO_DEVICE, NULL),
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(pci, xe_vfio_pci_table);
> > +
> >  static int xe_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  {
> > +	/*
> > +	 * Dynamic IDs added through new_id are matched before the static ones
> > +	 * and carry no driver data, look the description up in the static
> > +	 * table so that they can't bypass the default hidden ranges.
> > +	 */
> > +	const struct pci_device_id *match = pci_match_id(xe_vfio_pci_table, pdev);
> > +	const struct xe_vfio_pci_desc *desc =
> > +		match ? (const void *)match->driver_data : NULL;
> >  	struct xe_vfio_pci_core_device *xe_vdev;
> >  	int ret;
> >  
> > @@ -554,6 +588,17 @@ static int xe_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
> >  		return PTR_ERR(xe_vdev);
> >  
> >  	dev_set_drvdata(&pdev->dev, xe_vdev);
> > +	xe_vdev->core_device.hidden_ranges_supported = true;
> > +
> > +	if (desc) {
> > +		ret = vfio_pci_core_hide_bar_ranges(&xe_vdev->core_device,
> > +						    desc->hidden_ranges,
> > +						    desc->num_hidden_ranges);
> > +		if (ret) {
> > +			vfio_put_device(&xe_vdev->core_device.vdev);
> > +			return ret;
> > +		}
> > +	}
> >  
> >  	ret = vfio_pci_core_register_device(&xe_vdev->core_device);
> >  	if (ret) {
> > @@ -572,21 +617,6 @@ static void xe_vfio_pci_remove(struct pci_dev *pdev)
> >  	vfio_put_device(&xe_vdev->core_device.vdev);
> >  }
> >  
> > -#define INTEL_PCI_VFIO_DEVICE(_id) { \
> > -	PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_INTEL, (_id)) \
> > -}
> > -
> > -static const struct pci_device_id xe_vfio_pci_table[] = {
> > -	INTEL_BMG_IDS(INTEL_PCI_VFIO_DEVICE),
> > -	INTEL_CRI_IDS(INTEL_PCI_VFIO_DEVICE),
> > -	INTEL_NVLS_IDS(INTEL_PCI_VFIO_DEVICE),
> > -	INTEL_NVLP_IDS(INTEL_PCI_VFIO_DEVICE),
> > -	INTEL_PTL_IDS(INTEL_PCI_VFIO_DEVICE),
> > -	INTEL_WCL_IDS(INTEL_PCI_VFIO_DEVICE),
> > -	{}
> > -};
> > -MODULE_DEVICE_TABLE(pci, xe_vfio_pci_table);
> > -
> >  static struct pci_driver xe_vfio_pci_driver = {
> >  	.name = "xe-vfio-pci",
> >  	.id_table = xe_vfio_pci_table,
> 

-- 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-11-05 17:50 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 15:37 [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
2026-11-05 15:37 ` [RFC PATCH 1/4] vfio/pci: Move the excluded range handling out of vfio_pci_core_do_io_rw() Piórkowski, Piotr
2026-11-05 15:37 ` [RFC PATCH 2/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Piórkowski, Piotr
2026-11-05 16:24   ` sashiko-bot
2026-11-05 15:37 ` [RFC PATCH 3/4] vfio/pci: Allow configuring hidden BAR ranges through configfs Piórkowski, Piotr
2026-11-05 15:37 ` [RFC PATCH 4/4] vfio/xe: Hide BAR ranges by default per device ID Piórkowski, Piotr
2026-11-05 15:55   ` Alex Williamson
2026-11-05 17:50     ` Piotr Piórkowski
2026-11-05 15:50 ` [RFC PATCH 0/4] vfio/pci: Allow hiding BAR ranges from the VFIO user Alex Williamson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox