[PATCH 02/13] virtio_balloon: add reporting_vq DMA map ops
From: Kevin Loughlin
Date: Mon Oct 26 2026 - 23:00:15 EST
In confidential computing guests where VIRTIO_F_ACCESS_PLATFORM is
negotiated, the standard DMA mapping layer rejects mapping unencrypted
or swiotlb-bounced scatterlists when reporting large free page blocks
through reporting_vq. However, the host VMM only inspects the guest
physical address and length of reported free page ranges in the
virtqueue descriptor table and never reads or writes the reported guest
memory payload.
When guest memory encryption is active (CC_ATTR_GUEST_MEM_ENCRYPT),
attach custom per-virtqueue mapping operations (balloon_reporting_map_ops)
to reporting_vq that translate scatterlist entries directly to guest
physical addresses without allocating swiotlb bounce buffers or
decrypting guest memory.
Signed-off-by: Kevin Loughlin <kevinloughlin@xxxxxxxxxx>
---
drivers/virtio/virtio_balloon.c | 56 ++++++++++++++++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index ab3e3d887e00..11f74848fdc4 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -19,6 +19,7 @@
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/page_reporting.h>
+#include <linux/cc_platform.h>
/*
* Balloon device works in 4K page units. So each page is pointed to by
@@ -580,6 +581,55 @@ static void update_balloon_size_func(struct work_struct *work)
finish_wakeup_event(vb);
}
+static dma_addr_t balloon_reporting_map_page(union virtio_map map,
+ struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ /*
+ * Free page reporting transfers physical address and size metadata
+ * via vring descriptors; the device/host never accesses or transfers
+ * data on the page itself. Return the true physical address without
+ * DMA mapping / bounce buffering.
+ */
+ return (dma_addr_t)page_to_phys(page) + offset;
+}
+
+static void balloon_reporting_unmap_page(union virtio_map map,
+ dma_addr_t map_handle, size_t size,
+ enum dma_data_direction dir,
+ unsigned long attrs)
+{
+}
+
+static void balloon_reporting_sync_single(union virtio_map map,
+ dma_addr_t map_handle, size_t size,
+ enum dma_data_direction dir)
+{
+}
+
+static bool balloon_reporting_need_sync(union virtio_map map,
+ dma_addr_t map_handle)
+{
+ return false;
+}
+
+static int balloon_reporting_mapping_error(union virtio_map map,
+ dma_addr_t map_handle)
+{
+ return 0;
+}
+
+static const struct virtio_map_ops balloon_reporting_map_ops = {
+ .map_page = balloon_reporting_map_page,
+ .unmap_page = balloon_reporting_unmap_page,
+ .sync_single_for_cpu = balloon_reporting_sync_single,
+ .sync_single_for_device = balloon_reporting_sync_single,
+ .need_sync = balloon_reporting_need_sync,
+ .mapping_error = balloon_reporting_mapping_error,
+};
+
static int init_vqs(struct virtio_balloon *vb)
{
struct virtqueue_info vqs_info[VIRTIO_BALLOON_VQ_MAX] = {};
@@ -641,8 +691,12 @@ static int init_vqs(struct virtio_balloon *vb)
if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
vb->free_page_vq = vqs[VIRTIO_BALLOON_VQ_FREE_PAGE];
- if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
+ if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING)) {
vb->reporting_vq = vqs[VIRTIO_BALLOON_VQ_REPORTING];
+ if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
+ virtqueue_set_map_ops(vb->reporting_vq,
+ &balloon_reporting_map_ops);
+ }
return 0;
}
--
2.56.0.307.g5fe7bd680c-goog