From: Mathieu Poirier <mathieu.poirier@linaro.org>
To: berrange@redhat.com, kchamart@redhat.com,
pierrick.bouvier@oss.qualcomm.com, peter.maydell@linaro.org,
mst@redhat.com, cohuck@redhat.com, pbonzini@redhat.com,
eblake@redhat.com, armbru@redhat.com,
lorenzo.pieralisi@linaro.org, gshan@redhat.com,
enju.kohei@fujitsu.com
Cc: qemu-devel@nongnu.org, qemu-arm@nongnu.org, kvm@vger.kernel.org,
mathieu.poirier@linaro.org
Subject: [RFC v3 21/24] target/arm/kvm-rme: Add DMA remapping for the shared memory region
Date: Tue, 25 Aug 2026 16:00:58 -0600 [thread overview]
Message-ID: <20260825220101.3443954-22-mathieu.poirier@linaro.org> (raw)
In-Reply-To: <20260825220101.3443954-1-mathieu.poirier@linaro.org>
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
In Arm CCA, the guest-physical address space is split in half. The top
half represents memory shared between guest and host, and the bottom
half is private to the guest. From QEMU's point of view, the two halves
are merged into a single region, and pages within this region are either
shared or private.
Virtual devices implemented by the host are only allowed to access the
top half. For emulated MMIO, KVM strips the GPA before returning to
QEMU, so the GPA already belongs to QEMU's merged view of guest memory.
However DMA addresses cannot be stripped this way and need special
handling by the VMM.
When emulating DMA the VMM needs to translate the addresses into its
merged view. Add an IOMMU memory region on the top half, that
retargets DMA accesses to the merged sysmem.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/virt.c | 2 +
target/arm/kvm-rme.c | 106 ++++++++++++++++++++++++++++++++++++++++++
target/arm/kvm-stub.c | 4 ++
target/arm/kvm_arm.h | 10 ++++
4 files changed, 122 insertions(+)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index b799e5f44432..74df4b0d60f9 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -3277,6 +3277,8 @@ static void machvirt_init(MachineState *machine)
vms->fw_cfg, OBJECT(vms));
}
+ kvm_arm_rme_init_gpa_space(vms->highest_gpa, vms->bus);
+
vms->bootinfo.ram_size = machine->ram_size;
vms->bootinfo.board_id = -1;
vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base;
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index c082a5d8f3d1..4adfe37a7e8d 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -11,11 +11,13 @@
#include "hw/core/boards.h"
#include "hw/core/cpu.h"
#include "hw/core/loader.h"
+#include "hw/pci/pci.h"
#include "kvm_arm.h"
#include "migration/blocker.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/memalign.h"
+#include "qemu/units.h"
#include "qom/object_interfaces.h"
#include "system/confidential-guest-support.h"
#include "system/kvm.h"
@@ -26,6 +28,23 @@ OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
#define RME_PAGE_SIZE qemu_real_host_page_size()
+/*
+ * Realms have a split guest-physical address space: the bottom half is private
+ * to the realm, and the top half is shared with the host. Within QEMU, we use a
+ * merged view of both halves. Most of RAM is private to the guest and not
+ * accessible to us, but the guest shares some pages with us.
+ *
+ * RealmDmaRegion performs remapping of top-half accesses to system memory.
+ */
+struct RealmDmaRegion {
+ IOMMUMemoryRegion parent_obj;
+};
+
+#define TYPE_REALM_DMA_REGION "realm-dma-region"
+OBJECT_DECLARE_SIMPLE_TYPE(RealmDmaRegion, REALM_DMA_REGION)
+OBJECT_DEFINE_SIMPLE_TYPE(RealmDmaRegion, realm_dma_region,
+ REALM_DMA_REGION, IOMMU_MEMORY_REGION);
+
typedef struct {
hwaddr base;
hwaddr size;
@@ -36,6 +55,10 @@ struct RmeGuest {
ConfidentialGuestSupport parent_obj;
Notifier rom_load_notifier;
GSList *ram_regions;
+ uint8_t ipa_bits;
+
+ RealmDmaRegion *dma_region;
+ AddressSpace dma_as;
};
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -228,3 +251,86 @@ static void rme_guest_init(Object *obj)
static void rme_guest_finalize(Object *obj)
{
}
+
+static AddressSpace *rme_dma_get_address_space(PCIBus *bus, void *opaque,
+ int devfn)
+{
+ return &rme_guest->dma_as;
+}
+
+static const PCIIOMMUOps rme_dma_ops = {
+ .get_address_space = rme_dma_get_address_space,
+};
+
+void kvm_arm_rme_init_gpa_space(hwaddr highest_gpa, PCIBus *pci_bus)
+{
+ RealmDmaRegion *dma_region;
+ const unsigned int ipa_bits = 64 - clz64(highest_gpa) + 1;
+
+ if (!rme_guest) {
+ return;
+ }
+
+ assert(ipa_bits < 64);
+
+ /*
+ * Setup a DMA translation from the shared top half of the guest-physical
+ * address space to our merged view of RAM.
+ */
+ dma_region = g_new0(RealmDmaRegion, 1);
+
+ memory_region_init_iommu(dma_region, sizeof(*dma_region),
+ TYPE_REALM_DMA_REGION, OBJECT(rme_guest),
+ "realm-dma-region", 1ULL << ipa_bits);
+ address_space_init(&rme_guest->dma_as, MEMORY_REGION(dma_region),
+ TYPE_REALM_DMA_REGION);
+ rme_guest->dma_region = dma_region;
+ rme_guest->ipa_bits = ipa_bits;
+
+ pci_setup_iommu(pci_bus, &rme_dma_ops, NULL);
+}
+
+static void realm_dma_region_init(Object *obj)
+{
+}
+
+static IOMMUTLBEntry realm_dma_region_translate(IOMMUMemoryRegion *mr,
+ hwaddr addr,
+ IOMMUAccessFlags flag,
+ int iommu_idx)
+{
+ const hwaddr address_mask = MAKE_64BIT_MASK(0, rme_guest->ipa_bits - 1);
+ IOMMUTLBEntry entry = {
+ .target_as = &address_space_memory,
+ .iova = addr,
+ .translated_addr = addr & address_mask,
+ /*
+ * Somewhat arbitrary granule for users that need one, such as
+ * address_space_get_iotlb_entry(). Should be relatively large to
+ * avoid frequent TLB misses. It can't be larger than memory region
+ * alignment (eg. address_mask) because that would mask the whole
+ * address, preventing vhost from finding the correct memory region.
+ */
+ .addr_mask = 4 * KiB - 1,
+ .perm = IOMMU_RW,
+ };
+
+ return entry;
+}
+
+static void realm_dma_region_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n)
+{
+ /* Nothing is shared at boot */
+}
+
+static void realm_dma_region_finalize(Object *obj)
+{
+}
+
+static void realm_dma_region_class_init(ObjectClass *oc, const void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(oc);
+
+ imrc->translate = realm_dma_region_translate;
+ imrc->replay = realm_dma_region_replay;
+}
diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
index 5fde96f9b281..e5af5d20367f 100644
--- a/target/arm/kvm-stub.c
+++ b/target/arm/kvm-stub.c
@@ -42,6 +42,10 @@ bool kvm_arm_el2_supported(void)
return false;
}
+void kvm_arm_rme_init_gpa_space(hwaddr highest_gpa, PCIBus *pci_bus)
+{
+}
+
/*
* These functions should never actually be called without KVM support.
*/
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index d95381c13afa..b4a293911f71 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -250,4 +250,14 @@ char *kvm_print_register_name(uint64_t regidx);
*/
void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
+/**
+ * kvm_arm_rme_setup_gpa
+ * @highest_gpa: highest address of the lower half of the guest address space
+ * @pci_bus: The main PCI bus, for which PCI queries DMA address spaces
+ *
+ * Setup the guest-physical address space for a Realm. Install a memory region
+ * and notifier to manage the shared upper half of the address space.
+ */
+void kvm_arm_rme_init_gpa_space(hwaddr highest_gpa, PCIBus *pci_bus);
+
#endif
--
2.43.0
next prev parent reply other threads:[~2026-08-25 22:01 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 22:00 [RFC v3 00/24] Add Realm support to QEMU-VMM Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 01/24] linux-headers: Add RME related definitions Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 02/24] target/arm/kvm: Return immediately on error in kvm_arch_init() Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 03/24] target/arm: Add confidential guest support Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 04/24] target/arm/kvm-rme: Add mechanic to initialize realms Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 05/24] target/arm/kvm: Split kvm_arch_get/put_registers Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 06/24] target/arm/kvm-rme: Initialize vCPU Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 07/24] target/arm/kvm: Create scratch Realm VM when requested Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 08/24] target/arm/kvm: Use kvm_vm_check_extension() where necessary Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 09/24] hw/core/loader: Add a ROM loader notifier Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 10/24] target/arm/kvm-rme: Keep track of images loaded in Realm memory Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 11/24] target/arm/kvm-rme: Populate Realm with runtime images Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 12/24] target/arm/cpu: Set number of breakpoints and watchpoints in KVM Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 13/24] target/arm/cpu: Set number of PMU counters " Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 14/24] target/arm/cpu: Don't read Realm registers Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 15/24] hw/arm/virt: Set proper conduit method for Realms Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 16/24] hw/arm/virt: Embed Realm VM type with IPA address space Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 17/24] hw/arm/virt: Reserve one bit of guest physical address for RME Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 18/24] hw/arm/virt: Disable DTB randomness for confidential VMs Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 19/24] hw/arm/virt: Move virt_flash_create() to machvirt_init() Mathieu Poirier
2026-08-25 22:00 ` [RFC v3 20/24] hw/arm/virt: Use RAM instead of flash for confidential guest firmware Mathieu Poirier
2026-08-25 22:00 ` Mathieu Poirier [this message]
2026-08-25 22:00 ` [RFC v3 22/24] docs/interop/firmware.json: Add arm-rme firmware feature Mathieu Poirier
2026-08-25 22:01 ` [RFC v3 23/24] hw/arm/boot: Load DTB as is for confidential VMs Mathieu Poirier
2026-08-25 22:01 ` [RFC v3 24/24] hw/arm/boot: Skip bootloader for confidential guests Mathieu Poirier
2026-08-27 13:06 ` [RFC v3 00/24] Add Realm support to QEMU-VMM Daniel P. Berrangé
2026-08-27 13:28 ` Lorenzo Pieralisi
2026-09-01 13:20 ` Markus Armbruster
2026-09-02 15:45 ` Mathieu Poirier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260825220101.3443954-22-mathieu.poirier@linaro.org \
--to=mathieu.poirier@linaro.org \
--cc=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=cohuck@redhat.com \
--cc=eblake@redhat.com \
--cc=enju.kohei@fujitsu.com \
--cc=gshan@redhat.com \
--cc=kchamart@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=lorenzo.pieralisi@linaro.org \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=pierrick.bouvier@oss.qualcomm.com \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox