* [RFC v1 01/25] linux-headers: Add RME related definitions
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-10 1:09 ` Gavin Shan
2026-07-07 22:42 ` [RFC v1 02/25] target/arm/kvm: Return immediately on error in kvm_arch_init() Mathieu Poirier
` (23 subsequent siblings)
24 siblings, 1 reply; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
This is a temporary patch intended for testing purposes only. It provides
definitions related to supporting Realms by the QEMU-VMM.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
| 9 +++++++++
| 21 +++++++++++++++++----
2 files changed, 26 insertions(+), 4 deletions(-)
--git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h
index 6aefe7973814..4c08631ec7d2 100644
--- a/linux-headers/asm-arm64/kvm.h
+++ b/linux-headers/asm-arm64/kvm.h
@@ -208,6 +208,15 @@ struct kvm_arm_counter_offset {
__u64 reserved;
};
+#define KVM_ARM_RMI_POPULATE_FLAGS_MEASURE (1 << 0)
+struct kvm_arm_rmi_populate {
+ __u64 base;
+ __u64 size;
+ __u64 source_uaddr;
+ __u32 flags;
+ __u32 reserved;
+};
+
#define KVM_ARM_TAGS_TO_GUEST 0
#define KVM_ARM_TAGS_FROM_GUEST 1
--git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 50e87ed72c20..dae6e5ba8457 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -685,11 +685,21 @@ struct kvm_enable_cap {
#define KVM_S390_SIE_PAGE_OFFSET 1
/*
- * On arm64, machine type can be used to request the physical
- * address size for the VM. Bits[7-0] are reserved for the guest
- * PA size shift (i.e, log2(PA_Size)). For backward compatibility,
- * value 0 implies the default IPA size, 40bits.
+ * On arm64, machine type can be used to request both the machine type and
+ * the physical address size for the VM.
+ *
+ * Bits[31-30] are reserved for the ARM specific machine type.
+ *
+ * Bits[7-0] are reserved for the guest PA size shift (i.e, log2(PA_Size)).
+ * For backward compatibility, value 0 implies the default IPA size, 40bits.
*/
+#define KVM_VM_TYPE_ARM_SHIFT 30
+#define KVM_VM_TYPE_ARM_MASK (0x3ULL << KVM_VM_TYPE_ARM_SHIFT)
+#define KVM_VM_TYPE_ARM(_type) \
+ (((_type) << KVM_VM_TYPE_ARM_SHIFT) & KVM_VM_TYPE_ARM_MASK)
+#define KVM_VM_TYPE_ARM_NORMAL KVM_VM_TYPE_ARM(0)
+#define KVM_VM_TYPE_ARM_REALM KVM_VM_TYPE_ARM(1)
+
#define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL
#define KVM_VM_TYPE_ARM_IPA_SIZE(x) \
((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
@@ -719,6 +729,8 @@ struct kvm_enable_cap {
#define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2)
#define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list)
+#define KVM_ARM_RMI_POPULATE _IOWR(KVMIO, 0xd7, struct kvm_arm_rmi_populate)
+
/*
* Extension capability list.
*/
@@ -985,6 +997,7 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_USER_OPEREXEC 246
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
+#define KVM_CAP_ARM_RMI 249
struct kvm_irq_routing_irqchip {
__u32 irqchip;
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 01/25] linux-headers: Add RME related definitions
2026-07-07 22:42 ` [RFC v1 01/25] linux-headers: Add RME related definitions Mathieu Poirier
@ 2026-07-10 1:09 ` Gavin Shan
2026-07-10 7:36 ` Lorenzo Pieralisi
2026-07-10 10:46 ` Lorenzo Pieralisi
0 siblings, 2 replies; 38+ messages in thread
From: Gavin Shan @ 2026-07-10 1:09 UTC (permalink / raw)
To: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
On 7/8/26 8:42 AM, Mathieu Poirier wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> This is a temporary patch intended for testing purposes only. It provides
> definitions related to supporting Realms by the QEMU-VMM.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> linux-headers/asm-arm64/kvm.h | 9 +++++++++
> linux-headers/linux/kvm.h | 21 +++++++++++++++++----
> 2 files changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h
> index 6aefe7973814..4c08631ec7d2 100644
> --- a/linux-headers/asm-arm64/kvm.h
> +++ b/linux-headers/asm-arm64/kvm.h
> @@ -208,6 +208,15 @@ struct kvm_arm_counter_offset {
> __u64 reserved;
> };
>
> +#define KVM_ARM_RMI_POPULATE_FLAGS_MEASURE (1 << 0)
> +struct kvm_arm_rmi_populate {
> + __u64 base;
> + __u64 size;
> + __u64 source_uaddr;
> + __u32 flags;
> + __u32 reserved;
> +};
> +
> #define KVM_ARM_TAGS_TO_GUEST 0
> #define KVM_ARM_TAGS_FROM_GUEST 1
>
> diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
> index 50e87ed72c20..dae6e5ba8457 100644
> --- a/linux-headers/linux/kvm.h
> +++ b/linux-headers/linux/kvm.h
> @@ -685,11 +685,21 @@ struct kvm_enable_cap {
> #define KVM_S390_SIE_PAGE_OFFSET 1
>
> /*
> - * On arm64, machine type can be used to request the physical
> - * address size for the VM. Bits[7-0] are reserved for the guest
> - * PA size shift (i.e, log2(PA_Size)). For backward compatibility,
> - * value 0 implies the default IPA size, 40bits.
> + * On arm64, machine type can be used to request both the machine type and
> + * the physical address size for the VM.
> + *
> + * Bits[31-30] are reserved for the ARM specific machine type.
> + *
> + * Bits[7-0] are reserved for the guest PA size shift (i.e, log2(PA_Size)).
> + * For backward compatibility, value 0 implies the default IPA size, 40bits.
> */
> +#define KVM_VM_TYPE_ARM_SHIFT 30
> +#define KVM_VM_TYPE_ARM_MASK (0x3ULL << KVM_VM_TYPE_ARM_SHIFT)
> +#define KVM_VM_TYPE_ARM(_type) \
> + (((_type) << KVM_VM_TYPE_ARM_SHIFT) & KVM_VM_TYPE_ARM_MASK)
> +#define KVM_VM_TYPE_ARM_NORMAL KVM_VM_TYPE_ARM(0)
> +#define KVM_VM_TYPE_ARM_REALM KVM_VM_TYPE_ARM(1)
> +
> #define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL
> #define KVM_VM_TYPE_ARM_IPA_SIZE(x) \
> ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
The definitions in this section aren't exactly same to that in (v14) host series,
leading to KVM_VM_TYPE_ARM_REALM = 0 in my environment and the realm guest can't
be started with the following error message. At least, KVM_VM_TYPE_ARM_MASK has
been redefined with different values.
qemu-system-aarch64: KVM does not support guest_memfd
----> linux-headers/linux/kvm.h
#define KVM_VM_TYPE_ARM_SHIFT 30
#define KVM_VM_TYPE_ARM_MASK (0x3ULL << KVM_VM_TYPE_ARM_SHIFT)
#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
KVM_VM_TYPE_ARM_PROTECTED)
After syncing the definitions with those in (v14) host series, I'm able to boot
a simple realm guest (no virtio devices) with one warning.
qemu-system-aarch64: Failed to enable KVM_CAP_ARM_NISV_TO_USER cap
Thanks,
Gavin
> @@ -719,6 +729,8 @@ struct kvm_enable_cap {
> #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2)
> #define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list)
>
> +#define KVM_ARM_RMI_POPULATE _IOWR(KVMIO, 0xd7, struct kvm_arm_rmi_populate)
> +
> /*
> * Extension capability list.
> */
> @@ -985,6 +997,7 @@ struct kvm_enable_cap {
> #define KVM_CAP_S390_USER_OPEREXEC 246
> #define KVM_CAP_S390_KEYOP 247
> #define KVM_CAP_S390_VSIE_ESAMODE 248
> +#define KVM_CAP_ARM_RMI 249
>
> struct kvm_irq_routing_irqchip {
> __u32 irqchip;
^ permalink raw reply [flat|nested] 38+ messages in thread* Re: [RFC v1 01/25] linux-headers: Add RME related definitions
2026-07-10 1:09 ` Gavin Shan
@ 2026-07-10 7:36 ` Lorenzo Pieralisi
2026-07-10 10:46 ` Lorenzo Pieralisi
1 sibling, 0 replies; 38+ messages in thread
From: Lorenzo Pieralisi @ 2026-07-10 7:36 UTC (permalink / raw)
To: Gavin Shan
Cc: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi, qemu-devel, qemu-arm, kvm
On Fri, Jul 10, 2026 at 11:09:43AM +1000, Gavin Shan wrote:
> On 7/8/26 8:42 AM, Mathieu Poirier wrote:
> > From: Jean-Philippe Brucker <jean-philippe@linaro.org>
> >
> > This is a temporary patch intended for testing purposes only. It provides
> > definitions related to supporting Realms by the QEMU-VMM.
> >
> > Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> > linux-headers/asm-arm64/kvm.h | 9 +++++++++
> > linux-headers/linux/kvm.h | 21 +++++++++++++++++----
> > 2 files changed, 26 insertions(+), 4 deletions(-)
> >
> > diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h
> > index 6aefe7973814..4c08631ec7d2 100644
> > --- a/linux-headers/asm-arm64/kvm.h
> > +++ b/linux-headers/asm-arm64/kvm.h
> > @@ -208,6 +208,15 @@ struct kvm_arm_counter_offset {
> > __u64 reserved;
> > };
> > +#define KVM_ARM_RMI_POPULATE_FLAGS_MEASURE (1 << 0)
> > +struct kvm_arm_rmi_populate {
> > + __u64 base;
> > + __u64 size;
> > + __u64 source_uaddr;
> > + __u32 flags;
> > + __u32 reserved;
> > +};
> > +
> > #define KVM_ARM_TAGS_TO_GUEST 0
> > #define KVM_ARM_TAGS_FROM_GUEST 1
> > diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
> > index 50e87ed72c20..dae6e5ba8457 100644
> > --- a/linux-headers/linux/kvm.h
> > +++ b/linux-headers/linux/kvm.h
> > @@ -685,11 +685,21 @@ struct kvm_enable_cap {
> > #define KVM_S390_SIE_PAGE_OFFSET 1
> > /*
> > - * On arm64, machine type can be used to request the physical
> > - * address size for the VM. Bits[7-0] are reserved for the guest
> > - * PA size shift (i.e, log2(PA_Size)). For backward compatibility,
> > - * value 0 implies the default IPA size, 40bits.
> > + * On arm64, machine type can be used to request both the machine type and
> > + * the physical address size for the VM.
> > + *
> > + * Bits[31-30] are reserved for the ARM specific machine type.
> > + *
> > + * Bits[7-0] are reserved for the guest PA size shift (i.e, log2(PA_Size)).
> > + * For backward compatibility, value 0 implies the default IPA size, 40bits.
> > */
> > +#define KVM_VM_TYPE_ARM_SHIFT 30
> > +#define KVM_VM_TYPE_ARM_MASK (0x3ULL << KVM_VM_TYPE_ARM_SHIFT)
> > +#define KVM_VM_TYPE_ARM(_type) \
> > + (((_type) << KVM_VM_TYPE_ARM_SHIFT) & KVM_VM_TYPE_ARM_MASK)
> > +#define KVM_VM_TYPE_ARM_NORMAL KVM_VM_TYPE_ARM(0)
> > +#define KVM_VM_TYPE_ARM_REALM KVM_VM_TYPE_ARM(1)
> > +
> > #define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL
> > #define KVM_VM_TYPE_ARM_IPA_SIZE(x) \
> > ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
>
> The definitions in this section aren't exactly same to that in (v14) host series,
> leading to KVM_VM_TYPE_ARM_REALM = 0 in my environment and the realm guest can't
> be started with the following error message. At least, KVM_VM_TYPE_ARM_MASK has
> been redefined with different values.
>
> qemu-system-aarch64: KVM does not support guest_memfd
>
> ----> linux-headers/linux/kvm.h
>
> #define KVM_VM_TYPE_ARM_SHIFT 30
> #define KVM_VM_TYPE_ARM_MASK (0x3ULL << KVM_VM_TYPE_ARM_SHIFT)
>
> #define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
> KVM_VM_TYPE_ARM_PROTECTED)
>
> After syncing the definitions with those in (v14) host series, I'm able to boot
> a simple realm guest (no virtio devices) with one warning.
>
> qemu-system-aarch64: Failed to enable KVM_CAP_ARM_NISV_TO_USER cap
Yep, that's correct the VM type handling has to be updated and I am looking
into the cap failure.
Thanks,
Lorenzo
> Thanks,
> Gavin
>
> > @@ -719,6 +729,8 @@ struct kvm_enable_cap {
> > #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2)
> > #define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list)
> > +#define KVM_ARM_RMI_POPULATE _IOWR(KVMIO, 0xd7, struct kvm_arm_rmi_populate)
> > +
> > /*
> > * Extension capability list.
> > */
> > @@ -985,6 +997,7 @@ struct kvm_enable_cap {
> > #define KVM_CAP_S390_USER_OPEREXEC 246
> > #define KVM_CAP_S390_KEYOP 247
> > #define KVM_CAP_S390_VSIE_ESAMODE 248
> > +#define KVM_CAP_ARM_RMI 249
> > struct kvm_irq_routing_irqchip {
> > __u32 irqchip;
>
^ permalink raw reply [flat|nested] 38+ messages in thread* Re: [RFC v1 01/25] linux-headers: Add RME related definitions
2026-07-10 1:09 ` Gavin Shan
2026-07-10 7:36 ` Lorenzo Pieralisi
@ 2026-07-10 10:46 ` Lorenzo Pieralisi
1 sibling, 0 replies; 38+ messages in thread
From: Lorenzo Pieralisi @ 2026-07-10 10:46 UTC (permalink / raw)
To: Gavin Shan
Cc: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi, qemu-devel, qemu-arm, kvm
On Fri, Jul 10, 2026 at 11:09:43AM +1000, Gavin Shan wrote:
[...]
> After syncing the definitions with those in (v14) host series, I'm able to boot
> a simple realm guest (no virtio devices) with one warning.
>
> qemu-system-aarch64: Failed to enable KVM_CAP_ARM_NISV_TO_USER cap
That's because we need to use the VM instance specific check extension
rather than the global one before trying to enable the cap.
It is harmless.
Lorenzo
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 02/25] target/arm/kvm: Return immediately on error in kvm_arch_init()
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 01/25] linux-headers: Add RME related definitions Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-08 5:08 ` Philippe Mathieu-Daudé
2026-07-07 22:42 ` [RFC v1 03/25] target/arm: Add confidential guest support Mathieu Poirier
` (22 subsequent siblings)
24 siblings, 1 reply; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Returning an error to kvm_init() is fatal anyway, no need to continue
the initialization.
Leave the `ret` variable in the function scope because it will be reused
when adding RME support.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index d40a6a985914..ed99be7fd80c 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -596,7 +596,7 @@ int kvm_arch_get_default_type(MachineState *ms)
int kvm_arch_init(MachineState *ms, KVMState *s)
{
- int ret = 0;
+ int ret;
/* For ARM interrupt delivery is always asynchronous,
* whether we are using an in-kernel VGIC or not.
*/
@@ -618,7 +618,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
!kvm_check_extension(s, KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)) {
error_report("Using more than 256 vcpus requires a host kernel "
"with KVM_CAP_ARM_IRQ_LINE_LAYOUT_2");
- ret = -EINVAL;
+ return -EINVAL;
}
if (kvm_check_extension(s, KVM_CAP_ARM_NISV_TO_USER)) {
@@ -640,13 +640,14 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
warn_report("Eager Page Split support not available");
} else if (!(s->kvm_eager_split_size & sizes)) {
error_report("Eager Page Split requested chunk size not valid");
- ret = -EINVAL;
+ return -EINVAL;
} else {
ret = kvm_vm_enable_cap(s, KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE, 0,
s->kvm_eager_split_size);
if (ret < 0) {
error_report("Enabling of Eager Page Split failed: %s",
strerror(-ret));
+ return ret;
}
}
}
@@ -659,7 +660,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
hw_breakpoints = g_array_sized_new(true, true,
sizeof(HWBreakpoint), max_hw_bps);
- return ret;
+ return 0;
}
unsigned long kvm_arch_vcpu_id(CPUState *cpu)
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 02/25] target/arm/kvm: Return immediately on error in kvm_arch_init()
2026-07-07 22:42 ` [RFC v1 02/25] target/arm/kvm: Return immediately on error in kvm_arch_init() Mathieu Poirier
@ 2026-07-08 5:08 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-08 5:08 UTC (permalink / raw)
To: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
On 8/7/26 00:42, Mathieu Poirier wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> Returning an error to kvm_init() is fatal anyway, no need to continue
> the initialization.
>
> Leave the `ret` variable in the function scope because it will be reused
> when adding RME support.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> target/arm/kvm.c | 9 +++++----
> 1 file changed, 5 insertions(+), 4 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 03/25] target/arm: Add confidential guest support
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 01/25] linux-headers: Add RME related definitions Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 02/25] target/arm/kvm: Return immediately on error in kvm_arch_init() Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-08 4:39 ` Markus Armbruster
2026-07-07 22:42 ` [RFC v1 04/25] target/arm/kvm-rme: Add mechanic to initialize realms Mathieu Poirier
` (21 subsequent siblings)
24 siblings, 1 reply; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Add a new RmeGuest object, inheriting from ConfidentialGuestSupport, to
support the Arm Realm Management Extension (RME). It is instantiated by
passing on the command-line:
-M virt,confidential-guest-support=<id>
-object rme-guest,id=<id>[,options...]
This is only the skeleton. Support will be added in following patches.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
docs/system/confidential-guest-support.rst | 1 +
qapi/qom.json | 1 +
target/arm/kvm-rme.c | 42 ++++++++++++++++++++++
target/arm/meson.build | 5 ++-
4 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 target/arm/kvm-rme.c
diff --git a/docs/system/confidential-guest-support.rst b/docs/system/confidential-guest-support.rst
index 562a7c3c2852..abb56923ad13 100644
--- a/docs/system/confidential-guest-support.rst
+++ b/docs/system/confidential-guest-support.rst
@@ -42,5 +42,6 @@ Currently supported confidential guest mechanisms are:
* POWER Protected Execution Facility (PEF) (see :ref:`power-papr-protected-execution-facility-pef`)
* s390x Protected Virtualization (PV) (see :doc:`s390x/protvirt`)
* AWS Nitro Enclaves (see :doc:`nitro`)
+* Arm Realm Management Extension (RME)
Other mechanisms may be supported in future.
diff --git a/qapi/qom.json b/qapi/qom.json
index dd45ac1087c3..285d32c2cce3 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -1241,6 +1241,7 @@
{ 'name': 'pr-manager-helper',
'if': 'CONFIG_LINUX' },
'qtest',
+ 'rme-guest',
'rng-builtin',
'rng-egd',
{ 'name': 'rng-random',
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
new file mode 100644
index 000000000000..42e1d1e7b859
--- /dev/null
+++ b/target/arm/kvm-rme.c
@@ -0,0 +1,42 @@
+/*
+ * QEMU Arm RME support
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright Linaro 2026
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/core/boards.h"
+#include "hw/core/cpu.h"
+#include "kvm_arm.h"
+#include "migration/blocker.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "system/confidential-guest-support.h"
+#include "system/kvm.h"
+#include "system/runstate.h"
+
+#define TYPE_RME_GUEST "rme-guest"
+OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
+
+struct RmeGuest {
+ ConfidentialGuestSupport parent_obj;
+};
+
+OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
+ CONFIDENTIAL_GUEST_SUPPORT,
+ { TYPE_USER_CREATABLE }, { })
+
+static void rme_guest_class_init(ObjectClass *oc, const void *data)
+{
+}
+
+static void rme_guest_init(Object *obj)
+{
+}
+
+static void rme_guest_finalize(Object *obj)
+{
+}
diff --git a/target/arm/meson.build b/target/arm/meson.build
index 4412fde065f2..aff52a3f6eeb 100644
--- a/target/arm/meson.build
+++ b/target/arm/meson.build
@@ -31,7 +31,10 @@ arm_common_user_system_ss.add(when: 'TARGET_AARCH64', if_true: files(
arm_common_system_ss.add(files(
'arm-qmp-cmds.c',
))
-arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'))
+arm_system_ss.add(when: 'CONFIG_KVM', if_true: files(
+ 'hyp_gdbstub.c',
+ 'kvm.c',
+ 'kvm-rme.c'))
arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c'))
arm_user_ss.add(files('cpu.c'))
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 03/25] target/arm: Add confidential guest support
2026-07-07 22:42 ` [RFC v1 03/25] target/arm: Add confidential guest support Mathieu Poirier
@ 2026-07-08 4:39 ` Markus Armbruster
2026-07-09 20:22 ` Mathieu Poirier
0 siblings, 1 reply; 38+ messages in thread
From: Markus Armbruster @ 2026-07-08 4:39 UTC (permalink / raw)
To: Mathieu Poirier
Cc: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi, qemu-devel,
qemu-arm, kvm
Mathieu Poirier <mathieu.poirier@linaro.org> writes:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> Add a new RmeGuest object, inheriting from ConfidentialGuestSupport, to
> support the Arm Realm Management Extension (RME). It is instantiated by
> passing on the command-line:
>
> -M virt,confidential-guest-support=<id>
> -object rme-guest,id=<id>[,options...]
>
> This is only the skeleton. Support will be added in following patches.
Which of the patches add options?
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [RFC v1 03/25] target/arm: Add confidential guest support
2026-07-08 4:39 ` Markus Armbruster
@ 2026-07-09 20:22 ` Mathieu Poirier
0 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-09 20:22 UTC (permalink / raw)
To: Markus Armbruster
Cc: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, jpb, lorenzo.pieralisi, qemu-devel, qemu-arm,
kvm
On Wed, Jul 08, 2026 at 06:39:22AM +0200, Markus Armbruster wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
> > From: Jean-Philippe Brucker <jean-philippe@linaro.org>
> >
> > Add a new RmeGuest object, inheriting from ConfidentialGuestSupport, to
> > support the Arm Realm Management Extension (RME). It is instantiated by
> > passing on the command-line:
> >
> > -M virt,confidential-guest-support=<id>
> > -object rme-guest,id=<id>[,options...]
> >
> > This is only the skeleton. Support will be added in following patches.
>
> Which of the patches add options?
Good catch - options have been removed because they are not supported on the V14
kernel.
>
> > Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 04/25] target/arm/kvm-rme: Add mechanic to initialize realms
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (2 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 03/25] target/arm: Add confidential guest support Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 05/25] target/arm/kvm: Split kvm_arch_get/put_registers Mathieu Poirier
` (20 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Initialise an @rme_guest object and install a handler to get notified
when the Realm is ready to start. That way we can finish reading the
Realm once we know everything is in place.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm-rme.c | 50 +++++++++++++++++++++++++++++++++++++++++++
target/arm/kvm-stub.c | 5 +++++
target/arm/kvm.c | 7 +++++-
target/arm/kvm_arm.h | 9 ++++++++
4 files changed, 70 insertions(+), 1 deletion(-)
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index 42e1d1e7b859..b86b9d6a25cb 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -13,6 +13,7 @@
#include "kvm_arm.h"
#include "migration/blocker.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qom/object_interfaces.h"
#include "system/confidential-guest-support.h"
#include "system/kvm.h"
@@ -29,14 +30,63 @@ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
CONFIDENTIAL_GUEST_SUPPORT,
{ TYPE_USER_CREATABLE }, { })
+static RmeGuest *rme_guest;
+
+static void rme_vm_state_change(void *opaque, bool running, RunState state)
+{
+ if (!running) {
+ return;
+ }
+
+ kvm_mark_guest_state_protected();
+}
+
static void rme_guest_class_init(ObjectClass *oc, const void *data)
{
}
static void rme_guest_init(Object *obj)
{
+ if (rme_guest) {
+ error_report("a single instance of RmeGuest is supported");
+ exit(1);
+ }
+ rme_guest = RME_GUEST(obj);
}
static void rme_guest_finalize(Object *obj)
{
}
+
+int kvm_arm_rme_init(MachineState *ms, KVMState *s)
+{
+ static Error *rme_mig_blocker;
+ ConfidentialGuestSupport *cgs = ms->cgs;
+
+ if (!rme_guest) {
+ return 0;
+ }
+
+ if (!cgs) {
+ error_report("missing -machine confidential-guest-support parameter");
+ return -EINVAL;
+ }
+
+ if (!kvm_vm_check_extension(s, KVM_CAP_ARM_RMI)) {
+ error_report("KVM doesn't support Realms");
+ return -ENODEV;
+ }
+
+ error_setg(&rme_mig_blocker, "RME: migration is not implemented");
+ migrate_add_blocker(&rme_mig_blocker, &error_fatal);
+
+ /*
+ * The realm activation is done last, when the VM starts, after all images
+ * have been loaded and all vcpus finalized.
+ */
+ qemu_add_vm_change_state_handler(rme_vm_state_change, NULL);
+
+ cgs->require_guest_memfd = true;
+ cgs->ready = true;
+ return 0;
+}
diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
index 88cbe8d85c41..eec1ca3dd2e5 100644
--- a/target/arm/kvm-stub.c
+++ b/target/arm/kvm-stub.c
@@ -119,3 +119,8 @@ char *kvm_print_register_name(uint64_t regidx)
{
g_assert_not_reached();
}
+
+int kvm_arm_rme_init(MachineState *ms, KVMState *s)
+{
+ g_assert_not_reached();
+}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index ed99be7fd80c..28d5643d6779 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -660,7 +660,12 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
hw_breakpoints = g_array_sized_new(true, true,
sizeof(HWBreakpoint), max_hw_bps);
- return 0;
+ ret = kvm_arm_rme_init(ms, s);
+ if (ret) {
+ error_report("Failed to enable RME: %s", strerror(-ret));
+ }
+
+ return ret;
}
unsigned long kvm_arch_vcpu_id(CPUState *cpu)
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index e7c40fb003e4..6d058b0e08a2 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -240,4 +240,13 @@ void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3);
*/
char *kvm_print_register_name(uint64_t regidx);
+/**
+ * kvm_arm_rme_init
+ * @ms: the machine state
+ * @s: State of KVM
+ *
+ * Prepare the machine to be a Realm, if the user enabled it.
+ */
+int kvm_arm_rme_init(MachineState *ms, KVMState *s);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 05/25] target/arm/kvm: Split kvm_arch_get/put_registers
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (3 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 04/25] target/arm/kvm-rme: Add mechanic to initialize realms Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 06/25] target/arm/kvm-rme: Initialize vCPU Mathieu Poirier
` (19 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
For a Realm, KVM is much more restrictive on the registers a VMM can
initialize. To prepare for Realm VM initialization, split the get/put
register functions for a non-secure VM in their own function.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 28d5643d6779..b609f29c11c6 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -2163,7 +2163,7 @@ static int kvm_arch_put_sve(CPUState *cs, uint32_t vq, bool have_ffr)
return 0;
}
-int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
+static int kvm_arm_put_core_regs(CPUState *cs, Error **errp)
{
uint64_t val;
uint32_t fpr;
@@ -2266,6 +2266,19 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
return ret;
}
+ return 0;
+}
+
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
+{
+ int ret;
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ ret = kvm_arm_put_core_regs(cs, errp);
+ if (ret) {
+ return ret;
+ }
+
write_cpustate_to_list(cpu, true);
if (!write_list_to_kvmstate(cpu, level)) {
@@ -2348,7 +2361,7 @@ static int kvm_arch_get_sve(CPUState *cs, uint32_t vq, bool have_ffr)
return 0;
}
-int kvm_arch_get_registers(CPUState *cs, Error **errp)
+static int kvm_arm_get_core_regs(CPUState *cs, Error **errp)
{
uint64_t val;
unsigned int el;
@@ -2451,6 +2464,19 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
}
vfp_set_fpcr(env, fpr);
+ return 0;
+}
+
+int kvm_arch_get_registers(CPUState *cs, Error **errp)
+{
+ int ret;
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ ret = kvm_arm_get_core_regs(cs, errp);
+ if (ret) {
+ return ret;
+ }
+
ret = kvm_get_vcpu_events(cpu);
if (ret) {
return ret;
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 06/25] target/arm/kvm-rme: Initialize vCPU
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (4 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 05/25] target/arm/kvm: Split kvm_arch_get/put_registers Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 07/25] target/arm/kvm: Create scratch Realm VM when requested Mathieu Poirier
` (18 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The target code calls kvm_arm_vcpu_init() to mark the vCPU as part of a
Realm. For a Realm vCPU, only x0-x7 can be set at runtime. Before boot,
the PC can also be set, and is ignored at runtime. KVM also accepts a
few system register changes during initial configuration, as returned by
KVM_GET_REG_LIST.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/cpu.h | 3 +++
target/arm/kvm-rme.c | 9 ++++++++
target/arm/kvm-stub.c | 5 ++++
target/arm/kvm.c | 54 +++++++++++++++++++++++++++++++++++++++++--
target/arm/kvm_arm.h | 10 ++++++++
5 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 03a30afcbed1..c7927036360c 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1055,6 +1055,9 @@ struct ArchCPU {
/* KVM steal time */
OnOffAuto kvm_steal_time;
+ /* Realm Management Extension */
+ bool kvm_rme;
+
/* Uniprocessor system with MP extensions */
bool mp_is_up;
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index b86b9d6a25cb..90c1d30ba24c 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -90,3 +90,12 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s)
cgs->ready = true;
return 0;
}
+
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+ if (!rme_guest) {
+ return;
+ }
+
+ cpu->kvm_rme = true;
+}
diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
index eec1ca3dd2e5..4ab6f08637d1 100644
--- a/target/arm/kvm-stub.c
+++ b/target/arm/kvm-stub.c
@@ -124,3 +124,8 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s)
{
g_assert_not_reached();
}
+
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+ g_assert_not_reached();
+}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index b609f29c11c6..4c212ed0c337 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -2009,6 +2009,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_HAS_EL2;
}
+ kvm_arm_rme_vcpu_init(cpu);
+
/* Do KVM_ARM_VCPU_INIT ioctl */
ret = kvm_arm_vcpu_init(cpu);
if (ret) {
@@ -2163,6 +2165,29 @@ static int kvm_arch_put_sve(CPUState *cs, uint32_t vq, bool have_ffr)
return 0;
}
+static int kvm_arm_rme_put_core_regs(CPUState *cs, Error **errp)
+{
+ int i, ret;
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ /* The RME ABI only allows us to set 8 GPRs and the PC */
+ for (i = 0; i < 8; i++) {
+ ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(regs.regs[i]),
+ &env->xregs[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(regs.pc), &env->pc);
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
static int kvm_arm_put_core_regs(CPUState *cs, Error **errp)
{
uint64_t val;
@@ -2274,7 +2299,11 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
int ret;
ARMCPU *cpu = ARM_CPU(cs);
- ret = kvm_arm_put_core_regs(cs, errp);
+ if (cpu->kvm_rme) {
+ ret = kvm_arm_rme_put_core_regs(cs, errp);
+ } else {
+ ret = kvm_arm_put_core_regs(cs, errp);
+ }
if (ret) {
return ret;
}
@@ -2361,6 +2390,23 @@ static int kvm_arch_get_sve(CPUState *cs, uint32_t vq, bool have_ffr)
return 0;
}
+static int kvm_arm_rme_get_core_regs(CPUState *cs, Error **errp)
+{
+ int i, ret;
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ for (i = 0; i < 8; i++) {
+ ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(regs.regs[i]),
+ &env->xregs[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int kvm_arm_get_core_regs(CPUState *cs, Error **errp)
{
uint64_t val;
@@ -2472,7 +2518,11 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
int ret;
ARMCPU *cpu = ARM_CPU(cs);
- ret = kvm_arm_get_core_regs(cs, errp);
+ if (cpu->kvm_rme) {
+ ret = kvm_arm_rme_get_core_regs(cs, errp);
+ } else {
+ ret = kvm_arm_get_core_regs(cs, errp);
+ }
if (ret) {
return ret;
}
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 6d058b0e08a2..12ed1eee18b4 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -249,4 +249,14 @@ char *kvm_print_register_name(uint64_t regidx);
*/
int kvm_arm_rme_init(MachineState *ms, KVMState *s);
+/**
+ * kvm_arm_rme_vcpu_init
+ * @cs: the CPU
+ *
+ * If the user requested a Realm, setup the given vCPU accordingly. Realm vCPUs
+ * behave a little differently, for example most of their register state is
+ * hidden from the host.
+ */
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 07/25] target/arm/kvm: Create scratch Realm VM when requested
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (5 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 06/25] target/arm/kvm-rme: Initialize vCPU Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 08/25] target/arm/kvm: Use kvm_vm_check_extension() where necessary Mathieu Poirier
` (17 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
When a Realm is requested, create a scratch Realm VM by setting bit 8
of the argument given to the KVM_CREATE_VM ioctl(). Bit 7-0 are reserved
for the guest PA size.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm-rme.c | 8 ++++++++
target/arm/kvm-stub.c | 5 +++++
target/arm/kvm.c | 4 +++-
target/arm/kvm_arm.h | 8 ++++++++
4 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index 90c1d30ba24c..f763d5ed6199 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -99,3 +99,11 @@ void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
cpu->kvm_rme = true;
}
+
+int kvm_arm_rme_vm_type(void)
+{
+ if (rme_guest) {
+ return KVM_VM_TYPE_ARM_REALM;
+ }
+ return 0;
+}
diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
index 4ab6f08637d1..6d52f172c5df 100644
--- a/target/arm/kvm-stub.c
+++ b/target/arm/kvm-stub.c
@@ -129,3 +129,8 @@ void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
{
g_assert_not_reached();
}
+
+int kvm_arm_rme_vm_type(void)
+{
+ g_assert_not_reached();
+}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 4c212ed0c337..6aa1cd04e484 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -108,6 +108,7 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
{
int ret = 0, kvmfd = -1, vmfd = -1, cpufd = -1;
int max_vm_pa_size;
+ int vm_type;
kvmfd = qemu_open_old("/dev/kvm", O_RDWR);
if (kvmfd < 0) {
@@ -117,8 +118,9 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
if (max_vm_pa_size < 0) {
max_vm_pa_size = 0;
}
+ vm_type = kvm_arm_rme_vm_type();
do {
- vmfd = ioctl(kvmfd, KVM_CREATE_VM, max_vm_pa_size);
+ vmfd = ioctl(kvmfd, KVM_CREATE_VM, max_vm_pa_size | vm_type);
} while (vmfd == -1 && errno == EINTR);
if (vmfd < 0) {
goto err;
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 12ed1eee18b4..65ec1b970606 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -259,4 +259,12 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s);
*/
void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
+/**
+ * kvm_arm_rme_vm_type
+ * @ms: the machine state
+ *
+ * Returns the Realm KVM VM type if the user requested a Realm, 0 otherwise.
+ */
+int kvm_arm_rme_vm_type(void);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 08/25] target/arm/kvm: Use kvm_vm_check_extension() where necessary
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (6 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 07/25] target/arm/kvm: Create scratch Realm VM when requested Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 09/25] target/arm/kvm-rme: Initialise Realm Initial Address space Mathieu Poirier
` (16 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The Arm KVM code can return different values from KVM_CHECK_EXTENSION
depending on the VM type. Use kvm_vm_check_extension() where necessary
to ensure we get the right response from KVM.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 6aa1cd04e484..c69b5310c184 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -328,7 +328,7 @@ static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
1 << KVM_ARM_VCPU_PTRAUTH_GENERIC);
}
- if (kvm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3)) {
+ if (kvm_vm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3)) {
init.features[0] |= 1 << KVM_ARM_VCPU_PMU_V3;
pmu_supported = true;
features |= 1ULL << ARM_FEATURE_PMU;
@@ -654,11 +654,11 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
}
}
- max_hw_wps = kvm_check_extension(s, KVM_CAP_GUEST_DEBUG_HW_WPS);
+ max_hw_wps = kvm_vm_check_extension(s, KVM_CAP_GUEST_DEBUG_HW_WPS);
hw_watchpoints = g_array_sized_new(true, true,
sizeof(HWWatchpoint), max_hw_wps);
- max_hw_bps = kvm_check_extension(s, KVM_CAP_GUEST_DEBUG_HW_BPS);
+ max_hw_bps = kvm_vm_check_extension(s, KVM_CAP_GUEST_DEBUG_HW_BPS);
hw_breakpoints = g_array_sized_new(true, true,
sizeof(HWBreakpoint), max_hw_bps);
@@ -1905,7 +1905,7 @@ void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa)
void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp)
{
- bool has_steal_time = kvm_check_extension(kvm_state, KVM_CAP_STEAL_TIME);
+ bool has_steal_time = kvm_vm_check_extension(kvm_state, KVM_CAP_STEAL_TIME);
if (cpu->kvm_steal_time == ON_OFF_AUTO_AUTO) {
if (!has_steal_time || !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 09/25] target/arm/kvm-rme: Initialise Realm Initial Address space
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (7 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 08/25] target/arm/kvm: Use kvm_vm_check_extension() where necessary Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-09 23:44 ` Gavin Shan
2026-07-07 22:42 ` [RFC v1 10/25] hw/core/loader: Add a ROM loader notifier Mathieu Poirier
` (15 subsequent siblings)
24 siblings, 1 reply; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Initialise the Realm's initial physical address space based on
the VM's configuration.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/boot.c | 4 ++++
target/arm/kvm-rme.c | 18 ++++++++++++++++++
target/arm/kvm-stub.c | 4 ++++
target/arm/kvm_arm.h | 10 ++++++++++
4 files changed, 36 insertions(+)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 9b7553dde5e1..bcab21d349b4 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -32,6 +32,7 @@
#include "qemu/option.h"
#include "qemu/units.h"
#include "qemu/bswap.h"
+#include "kvm_arm.h"
/* Kernel boot protocol is specified in the kernel docs
* Documentation/arm/Booting and Documentation/arm64/booting.txt
@@ -1210,6 +1211,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
/* We assume the CPU passed as argument is the primary CPU. */
info->primary_cpu = cpu;
+ /* Mark all Realm memory as RAM */
+ kvm_arm_rme_init_guest_ram(info->loader_start, info->ram_size);
+
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
arm_setup_firmware_boot(cpu, info);
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index f763d5ed6199..697e7d507f0d 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -22,8 +22,16 @@
#define TYPE_RME_GUEST "rme-guest"
OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
+#define RME_PAGE_SIZE qemu_real_host_page_size()
+
+typedef struct {
+ hwaddr base;
+ hwaddr size;
+} RmeRamRegion;
+
struct RmeGuest {
ConfidentialGuestSupport parent_obj;
+ RmeRamRegion init_ram;
};
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -91,6 +99,16 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s)
return 0;
}
+void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size)
+{
+ if (!rme_guest) {
+ return;
+ }
+
+ rme_guest->init_ram.base = base;
+ rme_guest->init_ram.size = size;
+}
+
void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
{
if (!rme_guest) {
diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
index 6d52f172c5df..790e1940fd8a 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_guest_ram(hwaddr base, size_t size)
+{
+}
+
/*
* 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 65ec1b970606..c7dc45595159 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -267,4 +267,14 @@ void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
*/
int kvm_arm_rme_vm_type(void);
+/*
+ * kvm_arm_rme_init_guest_ram
+ * @base: base address of RAM
+ * @size: size of RAM
+ *
+ * If the user requested a Realm, set the base and size of guest RAM, in order
+ * to initialize the Realm IPA space.
+ */
+void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 09/25] target/arm/kvm-rme: Initialise Realm Initial Address space
2026-07-07 22:42 ` [RFC v1 09/25] target/arm/kvm-rme: Initialise Realm Initial Address space Mathieu Poirier
@ 2026-07-09 23:44 ` Gavin Shan
2026-07-10 7:37 ` Lorenzo Pieralisi
0 siblings, 1 reply; 38+ messages in thread
From: Gavin Shan @ 2026-07-09 23:44 UTC (permalink / raw)
To: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
Hi Mathieu,
On 7/8/26 8:42 AM, Mathieu Poirier wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> Initialise the Realm's initial physical address space based on
> the VM's configuration.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> hw/arm/boot.c | 4 ++++
> target/arm/kvm-rme.c | 18 ++++++++++++++++++
> target/arm/kvm-stub.c | 4 ++++
> target/arm/kvm_arm.h | 10 ++++++++++
> 4 files changed, 36 insertions(+)
>
Thanks for the posting.
If RmeGuest::init_ram isn't needed in the future, this patch can be dropped.
There is no API exposed from (v14) host series to initialize RIPAS for the
specified IPA range, as we did in the earlier revisions. So this patch was
added for the earlier host serieses, not for (v14) host series any more?
Thanks,
Gavin
> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> index 9b7553dde5e1..bcab21d349b4 100644
> --- a/hw/arm/boot.c
> +++ b/hw/arm/boot.c
> @@ -32,6 +32,7 @@
> #include "qemu/option.h"
> #include "qemu/units.h"
> #include "qemu/bswap.h"
> +#include "kvm_arm.h"
>
> /* Kernel boot protocol is specified in the kernel docs
> * Documentation/arm/Booting and Documentation/arm64/booting.txt
> @@ -1210,6 +1211,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
> /* We assume the CPU passed as argument is the primary CPU. */
> info->primary_cpu = cpu;
>
> + /* Mark all Realm memory as RAM */
> + kvm_arm_rme_init_guest_ram(info->loader_start, info->ram_size);
> +
> /* Load the kernel. */
> if (!info->kernel_filename || info->firmware_loaded) {
> arm_setup_firmware_boot(cpu, info);
> diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
> index f763d5ed6199..697e7d507f0d 100644
> --- a/target/arm/kvm-rme.c
> +++ b/target/arm/kvm-rme.c
> @@ -22,8 +22,16 @@
> #define TYPE_RME_GUEST "rme-guest"
> OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
>
> +#define RME_PAGE_SIZE qemu_real_host_page_size()
> +
> +typedef struct {
> + hwaddr base;
> + hwaddr size;
> +} RmeRamRegion;
> +
> struct RmeGuest {
> ConfidentialGuestSupport parent_obj;
> + RmeRamRegion init_ram;
> };
>
> OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
> @@ -91,6 +99,16 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s)
> return 0;
> }
>
> +void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size)
> +{
> + if (!rme_guest) {
> + return;
> + }
> +
> + rme_guest->init_ram.base = base;
> + rme_guest->init_ram.size = size;
> +}
> +
> void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
> {
> if (!rme_guest) {
> diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
> index 6d52f172c5df..790e1940fd8a 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_guest_ram(hwaddr base, size_t size)
> +{
> +}
> +
> /*
> * 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 65ec1b970606..c7dc45595159 100644
> --- a/target/arm/kvm_arm.h
> +++ b/target/arm/kvm_arm.h
> @@ -267,4 +267,14 @@ void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
> */
> int kvm_arm_rme_vm_type(void);
>
> +/*
> + * kvm_arm_rme_init_guest_ram
> + * @base: base address of RAM
> + * @size: size of RAM
> + *
> + * If the user requested a Realm, set the base and size of guest RAM, in order
> + * to initialize the Realm IPA space.
> + */
> +void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size);
> +
> #endif
^ permalink raw reply [flat|nested] 38+ messages in thread* Re: [RFC v1 09/25] target/arm/kvm-rme: Initialise Realm Initial Address space
2026-07-09 23:44 ` Gavin Shan
@ 2026-07-10 7:37 ` Lorenzo Pieralisi
0 siblings, 0 replies; 38+ messages in thread
From: Lorenzo Pieralisi @ 2026-07-10 7:37 UTC (permalink / raw)
To: Gavin Shan
Cc: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi, qemu-devel, qemu-arm, kvm
On Fri, Jul 10, 2026 at 09:44:11AM +1000, Gavin Shan wrote:
> Hi Mathieu,
>
> On 7/8/26 8:42 AM, Mathieu Poirier wrote:
> > From: Jean-Philippe Brucker <jean-philippe@linaro.org>
> >
> > Initialise the Realm's initial physical address space based on
> > the VM's configuration.
> >
> > Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> > Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> > ---
> > hw/arm/boot.c | 4 ++++
> > target/arm/kvm-rme.c | 18 ++++++++++++++++++
> > target/arm/kvm-stub.c | 4 ++++
> > target/arm/kvm_arm.h | 10 ++++++++++
> > 4 files changed, 36 insertions(+)
> >
>
> Thanks for the posting.
>
> If RmeGuest::init_ram isn't needed in the future, this patch can be dropped.
> There is no API exposed from (v14) host series to initialize RIPAS for the
> specified IPA range, as we did in the earlier revisions. So this patch was
> added for the earlier host serieses, not for (v14) host series any more?
Yes, as this series stands this patch looks like a leftover.
Thanks,
Lorenzo
> Thanks,
> Gavin
>
> > diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> > index 9b7553dde5e1..bcab21d349b4 100644
> > --- a/hw/arm/boot.c
> > +++ b/hw/arm/boot.c
> > @@ -32,6 +32,7 @@
> > #include "qemu/option.h"
> > #include "qemu/units.h"
> > #include "qemu/bswap.h"
> > +#include "kvm_arm.h"
> > /* Kernel boot protocol is specified in the kernel docs
> > * Documentation/arm/Booting and Documentation/arm64/booting.txt
> > @@ -1210,6 +1211,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
> > /* We assume the CPU passed as argument is the primary CPU. */
> > info->primary_cpu = cpu;
> > + /* Mark all Realm memory as RAM */
> > + kvm_arm_rme_init_guest_ram(info->loader_start, info->ram_size);
> > +
> > /* Load the kernel. */
> > if (!info->kernel_filename || info->firmware_loaded) {
> > arm_setup_firmware_boot(cpu, info);
> > diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
> > index f763d5ed6199..697e7d507f0d 100644
> > --- a/target/arm/kvm-rme.c
> > +++ b/target/arm/kvm-rme.c
> > @@ -22,8 +22,16 @@
> > #define TYPE_RME_GUEST "rme-guest"
> > OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
> > +#define RME_PAGE_SIZE qemu_real_host_page_size()
> > +
> > +typedef struct {
> > + hwaddr base;
> > + hwaddr size;
> > +} RmeRamRegion;
> > +
> > struct RmeGuest {
> > ConfidentialGuestSupport parent_obj;
> > + RmeRamRegion init_ram;
> > };
> > OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
> > @@ -91,6 +99,16 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s)
> > return 0;
> > }
> > +void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size)
> > +{
> > + if (!rme_guest) {
> > + return;
> > + }
> > +
> > + rme_guest->init_ram.base = base;
> > + rme_guest->init_ram.size = size;
> > +}
> > +
> > void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
> > {
> > if (!rme_guest) {
> > diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
> > index 6d52f172c5df..790e1940fd8a 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_guest_ram(hwaddr base, size_t size)
> > +{
> > +}
> > +
> > /*
> > * 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 65ec1b970606..c7dc45595159 100644
> > --- a/target/arm/kvm_arm.h
> > +++ b/target/arm/kvm_arm.h
> > @@ -267,4 +267,14 @@ void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
> > */
> > int kvm_arm_rme_vm_type(void);
> > +/*
> > + * kvm_arm_rme_init_guest_ram
> > + * @base: base address of RAM
> > + * @size: size of RAM
> > + *
> > + * If the user requested a Realm, set the base and size of guest RAM, in order
> > + * to initialize the Realm IPA space.
> > + */
> > +void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size);
> > +
> > #endif
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 10/25] hw/core/loader: Add a ROM loader notifier
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (8 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 09/25] target/arm/kvm-rme: Initialise Realm Initial Address space Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 11/25] target/arm/kvm-rme: Keep track of images loaded in Realm memory Mathieu Poirier
` (14 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Add a function to register a notifier that is invoked when ROMs get
loaded into guest memory.
It will be used by Arm confidential guest support, in order to register
all blobs loaded into memory with KVM, so that their content is moved
into Realm state and measured into the initial VM state.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/core/loader.c | 15 +++++++++++++++
include/hw/core/loader.h | 17 +++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 5cbfba0a86d2..b3d769bec5cb 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -74,6 +74,8 @@
#endif
static int roms_loaded;
+static NotifierList rom_loader_notifier =
+ NOTIFIER_LIST_INITIALIZER(rom_loader_notifier);
/* return the size or -1 if error */
int64_t get_image_size(const char *filename, Error **errp)
@@ -1201,6 +1203,11 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
return mr;
}
+void rom_add_load_notifier(Notifier *notifier)
+{
+ notifier_list_add(&rom_loader_notifier, notifier);
+}
+
/* This function is specific for elf program because we don't need to allocate
* all the rom. We just allocate the first part and the rest is just zeros. This
* is why romsize and datasize are different. Also, this function takes its own
@@ -1242,6 +1249,7 @@ ssize_t rom_add_option(const char *file, int32_t bootindex)
static void rom_reset(void *unused)
{
Rom *rom;
+ RomLoaderNotifyData notify;
QTAILQ_FOREACH(rom, &roms, next) {
if (rom->fw_file) {
@@ -1290,6 +1298,13 @@ static void rom_reset(void *unused)
address_space_flush_icache_range(rom->as, rom->addr, rom->datasize);
trace_loader_write_rom(rom->name, rom->addr, rom->datasize, rom->isrom);
+
+ notify = (RomLoaderNotifyData) {
+ .addr = rom->addr,
+ .len = rom->datasize,
+ .as = rom->as,
+ };
+ notifier_list_notify(&rom_loader_notifier, ¬ify);
}
}
diff --git a/include/hw/core/loader.h b/include/hw/core/loader.h
index d9431e8a8d12..94cf6ad2e26b 100644
--- a/include/hw/core/loader.h
+++ b/include/hw/core/loader.h
@@ -342,6 +342,23 @@ void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size);
ssize_t rom_add_vga(const char *file);
ssize_t rom_add_option(const char *file, int32_t bootindex);
+typedef struct RomLoaderNotifyData {
+ /* Address of the blob in guest memory */
+ hwaddr addr;
+ /* Length of the blob */
+ size_t len;
+ /* Address space of the blob */
+ AddressSpace *as;
+} RomLoaderNotifyData;
+
+/**
+ * rom_add_load_notifier - Add a notifier for loaded images
+ *
+ * Add a notifier that will be invoked with a RomLoaderNotifyData structure for
+ * each blob loaded into guest memory, after the blob is loaded.
+ */
+void rom_add_load_notifier(Notifier *notifier);
+
/* This is the usual maximum in uboot, so if a uImage overflows this, it would
* overflow on real hardware too. */
#define UBOOT_MAX_DECOMPRESSED_BYTES (64 << 20)
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 11/25] target/arm/kvm-rme: Keep track of images loaded in Realm memory
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (9 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 10/25] hw/core/loader: Add a ROM loader notifier Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 12/25] target/arm/kvm-rme: Populate Realm with runtime images Mathieu Poirier
` (13 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Add a Rom notifier to keep track of binary blobs loaded in Realm memory.
That way we can deterministically calculate the Realm Initial Measurement (RIM).
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm-rme.c | 46 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index 697e7d507f0d..97ec7c8934c9 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -10,6 +10,7 @@
#include "hw/core/boards.h"
#include "hw/core/cpu.h"
+#include "hw/core/loader.h"
#include "kvm_arm.h"
#include "migration/blocker.h"
#include "qapi/error.h"
@@ -27,11 +28,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
typedef struct {
hwaddr base;
hwaddr size;
+ AddressSpace *as;
} RmeRamRegion;
struct RmeGuest {
ConfidentialGuestSupport parent_obj;
+ Notifier rom_load_notifier;
RmeRamRegion init_ram;
+ GSList *ram_regions;
};
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -66,6 +70,44 @@ static void rme_guest_finalize(Object *obj)
{
}
+static gint rme_compare_ram_regions(gconstpointer a, gconstpointer b)
+{
+ const RmeRamRegion *ra = a;
+ const RmeRamRegion *rb = b;
+
+ g_assert(ra->base != rb->base);
+ return ra->base < rb->base ? -1 : 1;
+}
+
+static void rme_rom_load_notify(Notifier *notifier, void *data)
+{
+ RmeRamRegion *region;
+ RomLoaderNotifyData *rom = data;
+
+ if (rom->addr == -1) {
+ /*
+ * These blobs (ACPI tables) are not loaded into guest RAM at reset.
+ * Instead the firmware will load them via fw_cfg and measure them
+ * itself.
+ */
+ return;
+ }
+
+ region = g_new0(RmeRamRegion, 1);
+ region->base = rom->addr;
+ region->size = rom->len;
+ region->as = rom->as;
+
+ /*
+ * The Realm Initial Measurement (RIM) depends on the order in which we
+ * initialize and populate the RAM regions. To help a verifier
+ * independently calculate the RIM, sort regions by GPA.
+ */
+ rme_guest->ram_regions = g_slist_insert_sorted(rme_guest->ram_regions,
+ region,
+ rme_compare_ram_regions);
+}
+
int kvm_arm_rme_init(MachineState *ms, KVMState *s)
{
static Error *rme_mig_blocker;
@@ -88,6 +130,9 @@ int kvm_arm_rme_init(MachineState *ms, KVMState *s)
error_setg(&rme_mig_blocker, "RME: migration is not implemented");
migrate_add_blocker(&rme_mig_blocker, &error_fatal);
+ rme_guest->rom_load_notifier.notify = rme_rom_load_notify;
+ rom_add_load_notifier(&rme_guest->rom_load_notifier);
+
/*
* The realm activation is done last, when the VM starts, after all images
* have been loaded and all vcpus finalized.
@@ -107,6 +152,7 @@ void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size)
rme_guest->init_ram.base = base;
rme_guest->init_ram.size = size;
+ rme_guest->init_ram.as = NULL;
}
void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 12/25] target/arm/kvm-rme: Populate Realm with runtime images
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (10 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 11/25] target/arm/kvm-rme: Keep track of images loaded in Realm memory Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 13/25] target/arm/cpu: Set number of breakpoints and watchpoints in KVM Mathieu Poirier
` (12 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Once the Realm descriptor has been created, tell KVM to transfer runtime
images (kernel, DT, and rootfs) from guest memory to Realm memory.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/kvm-rme.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c
index 97ec7c8934c9..58669f4a50f1 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -44,12 +44,68 @@ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
static RmeGuest *rme_guest;
+static int rme_populate_range(const RmeRamRegion *region, bool measure,
+ Error **errp)
+{
+ int ret;
+ void *host_ua;
+ hwaddr size = region->size;
+ hwaddr base = region->base;
+ hwaddr start = QEMU_ALIGN_DOWN(base, RME_PAGE_SIZE);
+ hwaddr end = QEMU_ALIGN_UP(base + size, RME_PAGE_SIZE);
+ struct kvm_arm_rmi_populate populate_args;
+
+ host_ua = address_space_map(region->as, base, &size, false,
+ MEMTXATTRS_UNSPECIFIED);
+
+ populate_args = (struct kvm_arm_rmi_populate) {
+ .base = start,
+ .size = end - start,
+ .source_uaddr = (uintptr_t)host_ua,
+ .flags = measure ? KVM_ARM_RMI_POPULATE_FLAGS_MEASURE : 0,
+ };
+
+ while (populate_args.size > 0) {
+ ret = kvm_vm_ioctl(kvm_state, KVM_ARM_RMI_POPULATE, &populate_args, 0);
+ if (ret) {
+ error_setg_errno(errp, -ret,
+ "failed to populate realm [0x%"HWADDR_PRIx", 0x%"HWADDR_PRIx")",
+ start, end);
+ break;
+ }
+ }
+
+ address_space_unmap(region->as, host_ua, size, false, 0);
+
+ return ret;
+}
+
+static void rme_populate_ram_region(gpointer data, gpointer err)
+{
+ Error **errp = err;
+ const RmeRamRegion *region = data;
+
+ if (*errp) {
+ return;
+ }
+
+ rme_populate_range(region, /* measure */ true, errp);
+}
+
static void rme_vm_state_change(void *opaque, bool running, RunState state)
{
+ Error *errp = NULL;
+
if (!running) {
return;
}
+ g_slist_foreach(rme_guest->ram_regions, rme_populate_ram_region, &errp);
+ g_slist_free_full(g_steal_pointer(&rme_guest->ram_regions), g_free);
+ if (errp) {
+ return;
+ }
+
kvm_mark_guest_state_protected();
}
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 13/25] target/arm/cpu: Set number of breakpoints and watchpoints in KVM
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (11 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 12/25] target/arm/kvm-rme: Populate Realm with runtime images Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 14/25] target/arm/cpu: Set number of PMU counters " Mathieu Poirier
` (11 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Add "num-breakpoints" and "num-watchpoints" CPU parameters to configure
the debug features that KVM presents to the guest. The KVM vCPU
configuration is modified by calling SET_ONE_REG on the ID register.
This is needed for Realm VMs, whose parameters include breakpoints and
watchpoints, and influence the Realm Initial Measurement.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/arm-qmp-cmds.c | 1 +
target/arm/cpu.h | 4 ++
target/arm/cpu64.c | 81 +++++++++++++++++++++++++++++++++++++++
target/arm/kvm.c | 56 +++++++++++++++++++++++++++
4 files changed, 142 insertions(+)
diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c
index 83ec95c290f6..9ed65eb090d7 100644
--- a/target/arm/arm-qmp-cmds.c
+++ b/target/arm/arm-qmp-cmds.c
@@ -76,6 +76,7 @@ static const char *cpu_model_advertised_features[] = {
"sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048",
"kvm-no-adjvtime", "kvm-steal-time",
"pauth", "pauth-impdef", "pauth-qarma3", "pauth-qarma5",
+ "num-breakpoints", "num-watchpoints",
NULL
};
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index c7927036360c..d6f64daec2a3 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1174,6 +1174,10 @@ struct ArchCPU {
/* Generic timer counter frequency, in Hz */
uint64_t gt_cntfrq_hz;
+
+ /* Allows to override the default configuration */
+ uint8_t num_bps;
+ uint8_t num_wps;
};
typedef struct ARMCPUInfo {
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 28167355773b..3f12009befa8 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -666,6 +666,86 @@ void aarch64_add_pauth_properties(Object *obj)
}
}
+#if defined(CONFIG_KVM)
+static void arm_cpu_get_num_wps(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ val = cpu->num_wps;
+ if (val == 0) {
+ val = FIELD_EX64(cpu->isar.idregs[ID_AA64DFR0_EL1_IDX],
+ ID_AA64DFR0, WRPS) + 1;
+ }
+
+ visit_type_uint8(v, name, &val, errp);
+}
+
+static void arm_cpu_set_num_wps(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint8_t max_wps = FIELD_EX64(cpu->isar.idregs[ID_AA64DFR0_EL1_IDX],
+ ID_AA64DFR0, WRPS) + 1;
+
+ if (!visit_type_uint8(v, name, &val, errp)) {
+ return;
+ }
+
+ if (val < 2 || val > max_wps) {
+ error_setg(errp, "invalid number of watchpoints");
+ return;
+ }
+
+ cpu->num_wps = val;
+}
+
+static void arm_cpu_get_num_bps(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ val = cpu->num_bps;
+ if (val == 0) {
+ val = FIELD_EX64(cpu->isar.idregs[ID_AA64DFR0_EL1_IDX],
+ ID_AA64DFR0, BRPS) + 1;
+ }
+
+ visit_type_uint8(v, name, &val, errp);
+}
+
+static void arm_cpu_set_num_bps(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint8_t max_bps = FIELD_EX64(cpu->isar.idregs[ID_AA64DFR0_EL1_IDX],
+ ID_AA64DFR0, BRPS) + 1;
+
+ if (!visit_type_uint8(v, name, &val, errp)) {
+ return;
+ }
+
+ if (val < 2 || val > max_bps) {
+ error_setg(errp, "invalid number of breakpoints");
+ return;
+ }
+
+ cpu->num_bps = val;
+}
+
+static void aarch64_add_kvm_writable_properties(Object *obj)
+{
+ object_property_add(obj, "num-breakpoints", "uint8", arm_cpu_get_num_bps,
+ arm_cpu_set_num_bps, NULL, NULL);
+ object_property_add(obj, "num-watchpoints", "uint8", arm_cpu_get_num_wps,
+ arm_cpu_set_num_wps, NULL, NULL);
+}
+#endif /* CONFIG_KVM */
+
void aarch64_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp)
{
uint64_t t;
@@ -806,6 +886,7 @@ void aarch64_host_initfn(Object *obj)
kvm_arm_set_cpreg_mig_tolerances(cpu);
kvm_arm_set_cpu_features_from_host(cpu);
aarch64_add_sve_properties(obj);
+ aarch64_add_kvm_writable_properties(obj);
#elif defined(CONFIG_HVF)
hvf_arm_set_cpu_features_from_host(cpu);
#elif defined(CONFIG_WHPX)
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index c69b5310c184..e4b4d5450c70 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -904,6 +904,56 @@ out:
return ret;
}
+#define KVM_REG_ARM_ID_AA64DFR0_EL1 ARM64_SYS_REG(3, 0, 0, 5, 0)
+
+static void kvm_arm_configure_aa64dfr0(ARMCPU *cpu)
+{
+ int ret;
+ uint64_t val, newval;
+ CPUState *cs = CPU(cpu);
+
+ if (!cpu->num_bps && !cpu->num_wps) {
+ return;
+ }
+
+ newval = cpu->isar.idregs[ID_AA64DFR0_EL1_IDX];
+ if (cpu->num_bps) {
+ uint64_t ctx_cmps = FIELD_EX64(newval, ID_AA64DFR0, CTX_CMPS);
+
+ /* CTX_CMPs is never greater than BRPs */
+ ctx_cmps = MIN(ctx_cmps, cpu->num_bps - 1);
+ newval = FIELD_DP64(newval, ID_AA64DFR0, BRPS, cpu->num_bps - 1);
+ newval = FIELD_DP64(newval, ID_AA64DFR0, CTX_CMPS, ctx_cmps);
+ }
+ if (cpu->num_wps) {
+ newval = FIELD_DP64(newval, ID_AA64DFR0, WRPS, cpu->num_wps - 1);
+ }
+ ret = kvm_set_one_reg(cs, KVM_REG_ARM_ID_AA64DFR0_EL1, &newval);
+ if (ret) {
+ error_report("Failed to set KVM_REG_ARM_ID_AA64DFR0_EL1");
+ return;
+ }
+
+ /*
+ * Check if the write succeeded. KVM does offer the writable mask for this
+ * register, but this way we also check if the value we wrote was sane.
+ */
+ ret = kvm_get_one_reg(cs, KVM_REG_ARM_ID_AA64DFR0_EL1, &val);
+ if (ret) {
+ error_report("Failed to get KVM_REG_ARM_ID_AA64DFR0_EL1");
+ return;
+ }
+
+ if (val != newval) {
+ error_report("Failed to update KVM_REG_ARM_ID_AA64DFR0_EL1");
+ }
+}
+
+static void kvm_arm_configure_vcpu_regs(ARMCPU *cpu)
+{
+ kvm_arm_configure_aa64dfr0(cpu);
+}
+
/**
* kvm_arm_cpreg_level:
* @regidx: KVM register index
@@ -1120,6 +1170,12 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu)
fprintf(stderr, "kvm_arm_vcpu_init failed: %s\n", strerror(-ret));
abort();
}
+
+ /*
+ * Before loading the KVM values into CPUState, update the KVM configuration
+ */
+ kvm_arm_configure_vcpu_regs(cpu);
+
if (!write_kvmstate_to_list(cpu)) {
fprintf(stderr, "write_kvmstate_to_list failed\n");
abort();
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 14/25] target/arm/cpu: Set number of PMU counters in KVM
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (12 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 13/25] target/arm/cpu: Set number of breakpoints and watchpoints in KVM Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 15/25] target/arm/cpu: Don't read Realm registers Mathieu Poirier
` (10 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Add a "num-pmu-counters" CPU parameter to configure the number of
counters that KVM presents to the guest. This is needed for Realm VMs,
whose parameters include the number of PMU counters and influence the
Realm Initial Measurement.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/arm-qmp-cmds.c | 2 +-
target/arm/cpu.h | 3 +++
target/arm/cpu64.c | 41 +++++++++++++++++++++++++++++++++++++++
target/arm/kvm.c | 34 ++++++++++++++++++++++++++++++++
4 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c
index 9ed65eb090d7..9350dc30d12f 100644
--- a/target/arm/arm-qmp-cmds.c
+++ b/target/arm/arm-qmp-cmds.c
@@ -76,7 +76,7 @@ static const char *cpu_model_advertised_features[] = {
"sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048",
"kvm-no-adjvtime", "kvm-steal-time",
"pauth", "pauth-impdef", "pauth-qarma3", "pauth-qarma5",
- "num-breakpoints", "num-watchpoints",
+ "num-breakpoints", "num-watchpoints", "num-pmu-counters",
NULL
};
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index d6f64daec2a3..50d482b09b0d 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1178,6 +1178,7 @@ struct ArchCPU {
/* Allows to override the default configuration */
uint8_t num_bps;
uint8_t num_wps;
+ int8_t num_pmu_ctrs;
};
typedef struct ARMCPUInfo {
@@ -2134,6 +2135,8 @@ FIELD(MFAR, FPA, 12, 40)
FIELD(MFAR, NSE, 62, 1)
FIELD(MFAR, NS, 63, 1)
+FIELD(PMCR, N, 11, 5)
+
QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK);
/* If adding a feature bit which corresponds to a Linux ELF
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 3f12009befa8..00b3511dcbc3 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -737,12 +737,53 @@ static void arm_cpu_set_num_bps(Object *obj, Visitor *v, const char *name,
cpu->num_bps = val;
}
+static void arm_cpu_get_num_pmu_ctrs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ if (cpu->num_pmu_ctrs == -1) {
+ val = FIELD_EX64(cpu->isar.reset_pmcr_el0, PMCR, N);
+ } else {
+ val = cpu->num_pmu_ctrs;
+ }
+
+ visit_type_uint8(v, name, &val, errp);
+}
+
+static void arm_cpu_set_num_pmu_ctrs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint8_t max_ctrs = FIELD_EX64(cpu->isar.reset_pmcr_el0, PMCR, N);
+
+ if (!visit_type_uint8(v, name, &val, errp)) {
+ return;
+ }
+
+ if (val > max_ctrs) {
+ error_setg(errp, "invalid number of PMU counters");
+ return;
+ }
+
+ cpu->num_pmu_ctrs = val;
+}
+
static void aarch64_add_kvm_writable_properties(Object *obj)
{
+ ARMCPU *cpu = ARM_CPU(obj);
+
object_property_add(obj, "num-breakpoints", "uint8", arm_cpu_get_num_bps,
arm_cpu_set_num_bps, NULL, NULL);
object_property_add(obj, "num-watchpoints", "uint8", arm_cpu_get_num_wps,
arm_cpu_set_num_wps, NULL, NULL);
+
+ cpu->num_pmu_ctrs = -1;
+ object_property_add(obj, "num-pmu-counters", "uint8",
+ arm_cpu_get_num_pmu_ctrs, arm_cpu_set_num_pmu_ctrs,
+ NULL, NULL);
}
#endif /* CONFIG_KVM */
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index e4b4d5450c70..3d5e16a21cf5 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -949,9 +949,43 @@ static void kvm_arm_configure_aa64dfr0(ARMCPU *cpu)
}
}
+#define KVM_REG_ARM_PMCR_EL0 ARM64_SYS_REG(3, 3, 9, 12, 0)
+
+static void kvm_arm_configure_pmcr(ARMCPU *cpu)
+{
+ int ret;
+ uint64_t val, newval;
+ CPUState *cs = CPU(cpu);
+
+ if (cpu->num_pmu_ctrs == -1) {
+ return;
+ }
+
+ newval = FIELD_DP64(cpu->isar.reset_pmcr_el0, PMCR, N, cpu->num_pmu_ctrs);
+ ret = kvm_set_one_reg(cs, KVM_REG_ARM_PMCR_EL0, &newval);
+ if (ret) {
+ error_report("Failed to set KVM_REG_ARM_PMCR_EL0");
+ return;
+ }
+
+ /*
+ * Check if the write succeeded, since older versions of KVM ignore it.
+ */
+ ret = kvm_get_one_reg(cs, KVM_REG_ARM_PMCR_EL0, &val);
+ if (ret) {
+ error_report("Failed to get KVM_REG_ARM_PMCR_EL0");
+ return;
+ }
+
+ if (val != newval) {
+ error_report("Failed to update KVM_REG_ARM_PMCR_EL0");
+ }
+}
+
static void kvm_arm_configure_vcpu_regs(ARMCPU *cpu)
{
kvm_arm_configure_aa64dfr0(cpu);
+ kvm_arm_configure_pmcr(cpu);
}
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 15/25] target/arm/cpu: Don't read Realm registers
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (13 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 14/25] target/arm/cpu: Set number of PMU counters " Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 16/25] hw/arm/virt: Set proper conduit method for Realms Mathieu Poirier
` (9 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The host cannot access registers of a Realm. Instead of showing all
registers as zero in "info registers", display a message about this
restriction.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
target/arm/cpu.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 787e4dc7ab23..7679b8758262 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -965,6 +965,11 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
const char *ns_status;
bool sve;
+ if (cpu->kvm_rme) {
+ qemu_fprintf(f, "the CPU registers are confidential to the realm\n");
+ return;
+ }
+
qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc);
for (i = 0; i < 32; i++) {
if (i == 31) {
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 16/25] hw/arm/virt: Set proper conduit method for Realms
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (14 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 15/25] target/arm/cpu: Don't read Realm registers Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-08 5:12 ` Philippe Mathieu-Daudé
2026-07-07 22:42 ` [RFC v1 17/25] hw/arm/virt: Embed Realm VM type with IPA address space Mathieu Poirier
` (8 subsequent siblings)
24 siblings, 1 reply; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The HVC conduit for PSCI is not supported for Realms, so default
to SMC.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/virt.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 6a3c69ba8f90..317816f942de 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -274,6 +274,11 @@ static const int a15irqmap[] = {
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
};
+static bool virt_machine_is_confidential(VirtMachineState *vms)
+{
+ return MACHINE(vms)->cgs;
+}
+
static void create_randomness(MachineState *ms, const char *node)
{
struct {
@@ -2952,10 +2957,11 @@ static void machvirt_init(MachineState *machine)
* if the guest has EL2 then we will use SMC as the conduit,
* and otherwise we will use HVC (for backwards compatibility and
* because if we're using KVM then we must use HVC).
+ * Realm guests must also use SMC.
*/
if (vms->secure && firmware_loaded) {
vms->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
- } else if (vms->virt) {
+ } else if (vms->virt || virt_machine_is_confidential(vms)) {
vms->psci_conduit = QEMU_PSCI_CONDUIT_SMC;
} else {
vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC;
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 16/25] hw/arm/virt: Set proper conduit method for Realms
2026-07-07 22:42 ` [RFC v1 16/25] hw/arm/virt: Set proper conduit method for Realms Mathieu Poirier
@ 2026-07-08 5:12 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-08 5:12 UTC (permalink / raw)
To: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
On 8/7/26 00:42, Mathieu Poirier wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> The HVC conduit for PSCI is not supported for Realms, so default
> to SMC.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> hw/arm/virt.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 17/25] hw/arm/virt: Embed Realm VM type with IPA address space
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (15 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 16/25] hw/arm/virt: Set proper conduit method for Realms Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:42 ` [RFC v1 18/25] hw/arm/virt: Reserve one bit of guest physical address for RME Mathieu Poirier
` (7 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Set the Ream VM bit in the IPA address space size when needed.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/virt.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 317816f942de..ac16290b442d 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -4051,6 +4051,7 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
static int virt_kvm_type(MachineState *ms, const char *type_str)
{
VirtMachineState *vms = VIRT_MACHINE(ms);
+ int rme_vm_type = kvm_arm_rme_vm_type();
int max_vm_pa_size, requested_pa_size;
bool fixed_ipa;
@@ -4080,7 +4081,11 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
* the implicit legacy 40b IPA setting, in which case the kvm_type
* must be 0.
*/
- return fixed_ipa ? 0 : requested_pa_size;
+ if (fixed_ipa) {
+ return 0;
+ }
+
+ return requested_pa_size | rme_vm_type;
}
static int virt_get_physical_address_range(MachineState *ms,
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 18/25] hw/arm/virt: Reserve one bit of guest physical address for RME
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (16 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 17/25] hw/arm/virt: Embed Realm VM type with IPA address space Mathieu Poirier
@ 2026-07-07 22:42 ` Mathieu Poirier
2026-07-07 22:43 ` [RFC v1 19/25] hw/arm/virt: Disable DTB randomness for confidential VMs Mathieu Poirier
` (6 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:42 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
When RME is enabled, the upper GPA bit is used to distinguish protected
from unprotected addresses. Reserve it when setting up the guest memory
map.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/virt.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ac16290b442d..d98d8108c8d2 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -4053,14 +4053,24 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
VirtMachineState *vms = VIRT_MACHINE(ms);
int rme_vm_type = kvm_arm_rme_vm_type();
int max_vm_pa_size, requested_pa_size;
+ int rme_reserve_bit = 0;
bool fixed_ipa;
- max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
+ if (rme_vm_type) {
+ /*
+ * With RME, the upper GPA bit differentiates Realm from NS memory.
+ * Reserve the upper bit to ensure that highmem devices will fit.
+ */
+ rme_reserve_bit = 1;
+ }
+
+ max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa) -
+ rme_reserve_bit;
/* we freeze the memory map to compute the highest gpa */
virt_set_memmap(vms, max_vm_pa_size);
- requested_pa_size = 64 - clz64(vms->highest_gpa);
+ requested_pa_size = 64 - clz64(vms->highest_gpa) + rme_reserve_bit;
/*
* KVM requires the IPA size to be at least 32 bits.
@@ -4069,11 +4079,11 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
requested_pa_size = 32;
}
- if (requested_pa_size > max_vm_pa_size) {
+ if (requested_pa_size > max_vm_pa_size + rme_reserve_bit) {
error_report("-m and ,maxmem option values "
"require an IPA range (%d bits) larger than "
"the one supported by the host (%d bits)",
- requested_pa_size, max_vm_pa_size);
+ requested_pa_size, max_vm_pa_size + rme_reserve_bit);
return -1;
}
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 19/25] hw/arm/virt: Disable DTB randomness for confidential VMs
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (17 preceding siblings ...)
2026-07-07 22:42 ` [RFC v1 18/25] hw/arm/virt: Reserve one bit of guest physical address for RME Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
2026-07-07 22:43 ` [RFC v1 20/25] hw/arm/virt: Move virt_flash_create() to machvirt_init() Mathieu Poirier
` (5 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The dtb-randomness feature, which adds random seeds to the DTB, isn't
really compatible with confidential VMs since it randomizes the Realm
Initial Measurement. Enabling it is not an error, but it prevents
attestation. It also isn't useful to a Realm, which doesn't trust host
input.
Currently the feature is automatically enabled, unless the user disables
it on the command-line. Change it to OnOffAuto, and automatically
disable it for confidential VMs, unless the user explicitly enables it.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
docs/system/arm/virt.rst | 9 +++++----
hw/arm/virt.c | 41 +++++++++++++++++++++++++---------------
include/hw/arm/virt.h | 2 +-
3 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index 5f6dd1797862..9882b842ddd5 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -238,10 +238,11 @@ dtb-randomness
rng-seed and kaslr-seed nodes (in both "/chosen" and
"/secure-chosen") to use for features like the random number
generator and address space randomisation. The default is
- ``on``. You will want to disable it if your trusted boot chain
- will verify the DTB it is passed, since this option causes the
- DTB to be non-deterministic. It would be the responsibility of
- the firmware to come up with a seed and pass it on if it wants to.
+ ``off`` for confidential VMs, and ``on`` otherwise. You will want
+ to disable it if your trusted boot chain will verify the DTB it is
+ passed, since this option causes the DTB to be non-deterministic.
+ It would be the responsibility of the firmware to come up with a
+ seed and pass it on if it wants to.
dtb-kaslr-seed
A deprecated synonym for dtb-randomness.
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index d98d8108c8d2..0f104447e008 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -399,6 +399,7 @@ static int gic_fdt_irq_type_spi(const VirtMachineState *vms)
static void create_fdt(VirtMachineState *vms)
{
+ bool dtb_randomness = true;
MachineState *ms = MACHINE(vms);
int nb_numa_nodes = ms->numa_state->num_nodes;
void *fdt = create_device_tree(&vms->fdt_size);
@@ -408,6 +409,16 @@ static void create_fdt(VirtMachineState *vms)
exit(1);
}
+ /*
+ * Including random data in the DTB causes random intial measurement on CCA,
+ * so disable it for confidential VMs.
+ */
+ if (vms->dtb_randomness == ON_OFF_AUTO_OFF ||
+ (vms->dtb_randomness == ON_OFF_AUTO_AUTO &&
+ virt_machine_is_confidential(vms))) {
+ dtb_randomness = false;
+ }
+
ms->fdt = fdt;
/* Header */
@@ -429,13 +440,13 @@ static void create_fdt(VirtMachineState *vms)
/* /chosen must exist for load_dtb to fill in necessary properties later */
qemu_fdt_add_subnode(fdt, "/chosen");
- if (vms->dtb_randomness) {
+ if (dtb_randomness) {
create_randomness(ms, "/chosen");
}
if (vms->secure) {
qemu_fdt_add_subnode(fdt, "/secure-chosen");
- if (vms->dtb_randomness) {
+ if (dtb_randomness) {
create_randomness(ms, "/secure-chosen");
}
}
@@ -3510,18 +3521,21 @@ static void virt_set_virtio_transports(Object *obj, Visitor *v,
vms->virtio_transports = transports;
}
-static bool virt_get_dtb_randomness(Object *obj, Error **errp)
+static void virt_get_dtb_randomness(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
+ OnOffAuto dtb_randomness = vms->dtb_randomness;
- return vms->dtb_randomness;
+ visit_type_OnOffAuto(v, name, &dtb_randomness, errp);
}
-static void virt_set_dtb_randomness(Object *obj, bool value, Error **errp)
+static void virt_set_dtb_randomness(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- vms->dtb_randomness = value;
+ visit_type_OnOffAuto(v, name, &vms->dtb_randomness, errp);
}
static char *virt_get_oem_id(Object *obj, Error **errp)
@@ -4339,16 +4353,16 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
"Set MSI settings. "
"Valid values are auto, gicv2m, its and off");
- object_class_property_add_bool(oc, "dtb-randomness",
- virt_get_dtb_randomness,
- virt_set_dtb_randomness);
+ object_class_property_add(oc, "dtb-randomness", "OnOffAuto",
+ virt_get_dtb_randomness, virt_set_dtb_randomness,
+ NULL, NULL);
object_class_property_set_description(oc, "dtb-randomness",
"Set off to disable passing random or "
"non-deterministic dtb nodes to guest");
- object_class_property_add_bool(oc, "dtb-kaslr-seed",
- virt_get_dtb_randomness,
- virt_set_dtb_randomness);
+ object_class_property_add(oc, "dtb-kaslr-seed", "OnOffAuto",
+ virt_get_dtb_randomness, virt_set_dtb_randomness,
+ NULL, NULL);
object_class_property_set_description(oc, "dtb-kaslr-seed",
"Deprecated synonym of dtb-randomness");
@@ -4411,9 +4425,6 @@ static void virt_instance_init(Object *obj)
/* MTE is disabled by default. */
vms->mte = false;
- /* Supply kaslr-seed and rng-seed by default */
- vms->dtb_randomness = true;
-
vms->irqmap = a15irqmap;
vms->virtio_transports = NUM_VIRTIO_TRANSPORTS;
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 22e66d1a113c..d4c1aa04f664 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -176,7 +176,7 @@ struct VirtMachineState {
bool virt;
bool ras;
bool mte;
- bool dtb_randomness;
+ OnOffAuto dtb_randomness;
bool second_ns_uart_present;
OnOffAuto acpi;
VirtGICType gic_version;
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 20/25] hw/arm/virt: Move virt_flash_create() to machvirt_init()
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (18 preceding siblings ...)
2026-07-07 22:43 ` [RFC v1 19/25] hw/arm/virt: Disable DTB randomness for confidential VMs Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
2026-07-08 5:14 ` Philippe Mathieu-Daudé
2026-07-07 22:43 ` [RFC v1 21/25] hw/arm/virt: Use RAM instead of flash for confidential guest firmware Mathieu Poirier
` (4 subsequent siblings)
24 siblings, 1 reply; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
For confidential VMs we'll want to skip flash device creation.
Unfortunately, in virt_instance_init() the machine->cgs member has not
yet been initialized, so we cannot check whether confidential guest is
enabled. Move virt_flash_create() to machvirt_init(), where we can
access the machine->cgs member.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/virt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 0f104447e008..2e8e29244284 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2909,6 +2909,8 @@ static void machvirt_init(MachineState *machine)
unsigned int smp_cpus = machine->smp.cpus;
unsigned int max_cpus = machine->smp.max_cpus;
+ virt_flash_create(vms);
+
possible_cpus = mc->possible_cpu_arch_ids(machine);
/*
@@ -4429,8 +4431,6 @@ static void virt_instance_init(Object *obj)
vms->virtio_transports = NUM_VIRTIO_TRANSPORTS;
- virt_flash_create(vms);
-
vms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
vms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
cxl_machine_init(obj, &vms->cxl_devices_state);
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 20/25] hw/arm/virt: Move virt_flash_create() to machvirt_init()
2026-07-07 22:43 ` [RFC v1 20/25] hw/arm/virt: Move virt_flash_create() to machvirt_init() Mathieu Poirier
@ 2026-07-08 5:14 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-08 5:14 UTC (permalink / raw)
To: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
On 8/7/26 00:43, Mathieu Poirier wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> For confidential VMs we'll want to skip flash device creation.
> Unfortunately, in virt_instance_init() the machine->cgs member has not
> yet been initialized, so we cannot check whether confidential guest is
> enabled. Move virt_flash_create() to machvirt_init(), where we can
> access the machine->cgs member.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> hw/arm/virt.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 21/25] hw/arm/virt: Use RAM instead of flash for confidential guest firmware
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (19 preceding siblings ...)
2026-07-07 22:43 ` [RFC v1 20/25] hw/arm/virt: Move virt_flash_create() to machvirt_init() Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
2026-07-07 22:43 ` [RFC v1 22/25] target/arm/kvm-rme: Add DMA remapping for the shared memory region Mathieu Poirier
` (3 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Use RAM instead of flash to store firmware image and boot information,
including UEFI variables. That way the firmware can be measured and
included in the Realm's initial measurements. It also prevents
variable stored in flash and modified as part of the boot process to
be leaked outside the Realm.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/boot.c | 37 +++++++++++++++++++++++++--
hw/arm/virt.c | 58 +++++++++++++++++++++++++++++++++++++++++++
include/hw/arm/boot.h | 9 +++++++
3 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index bcab21d349b4..aea044fbed9a 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -1113,7 +1113,36 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
}
}
-static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info)
+static void arm_setup_confidential_firmware_boot(ARMCPU *cpu,
+ struct arm_boot_info *info,
+ const char *firmware_filename)
+{
+ ssize_t fw_size;
+ const char *fname;
+ AddressSpace *as = arm_boot_address_space(cpu, info);
+
+ fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware_filename);
+ if (!fname) {
+ error_report("Could not find firmware image '%s'", firmware_filename);
+ exit(1);
+ }
+
+ /*
+ * Load the firmware image in the Realm's address space. Mapping of the
+ * firmware area in the Realm's address space is done in function
+ * virt_confidential_firmware_init().
+ */
+ fw_size = load_image_targphys_as(firmware_filename,
+ info->firmware_base,
+ info->firmware_max_size, as, NULL);
+ if (fw_size <= 0) {
+ error_report("could not load firmware '%s'", firmware_filename);
+ exit(1);
+ }
+}
+
+static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info,
+ const char *firmware_filename)
{
/* Set up for booting firmware (which might load a kernel via fw_cfg) */
@@ -1169,6 +1198,10 @@ static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info)
}
}
+ if (info->confidential) {
+ arm_setup_confidential_firmware_boot(cpu, info, firmware_filename);
+ }
+
/*
* We will start from address 0 (typically a boot ROM image) in the
* same way as hardware. Leave env->boot_info NULL, so that
@@ -1216,7 +1249,7 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
- arm_setup_firmware_boot(cpu, info);
+ arm_setup_firmware_boot(cpu, info, ms->firmware);
} else {
arm_setup_direct_kernel_boot(cpu, info);
}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 2e8e29244284..8b79c3ea28e5 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1821,6 +1821,16 @@ static PFlashCFI01 *virt_flash_create1(VirtMachineState *vms,
static void virt_flash_create(VirtMachineState *vms)
{
+ /*
+ * For Realms, the firmware image is placed directly in the guest's
+ * RAM area. The association between the final location in the
+ * guest's RAM and the system memory is done in function
+ * virt_confidential_firmware_init().
+ */
+ if (virt_machine_is_confidential(vms)) {
+ return;
+ }
+
vms->flash[0] = virt_flash_create1(vms, "virt.flash0", "pflash0");
vms->flash[1] = virt_flash_create1(vms, "virt.flash1", "pflash1");
}
@@ -1871,6 +1881,16 @@ static void virt_flash_fdt(VirtMachineState *vms,
MachineState *ms = MACHINE(vms);
char *nodename;
+ /*
+ * For Realms the firmware images are stored in the guest's address
+ * space. As such there is no need for flash configuration in the FDT.
+ * See function virt_confidential_firmware_init() and
+ * arm_setup_confidential_firmware_boot() for details.
+ */
+ if (virt_machine_is_confidential(vms)) {
+ return;
+ }
+
if (sysmem == secure_sysmem) {
/* Report both flash devices as a single node in the DT */
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
@@ -1906,6 +1926,32 @@ static void virt_flash_fdt(VirtMachineState *vms,
}
}
+static bool virt_confidential_firmware_init(VirtMachineState *vms,
+ MemoryRegion *sysmem)
+{
+ MemoryRegion *fw_ram;
+ hwaddr fw_base = vms->memmap[VIRT_FLASH].base;
+ hwaddr fw_size = vms->memmap[VIRT_FLASH].size;
+
+ if (!MACHINE(vms)->firmware) {
+ return false;
+ }
+
+ assert(machine_require_guest_memfd(MACHINE(vms)));
+
+ fw_ram = g_new(MemoryRegion, 1);
+ memory_region_init_ram_guest_memfd(fw_ram, NULL, "fw_ram", fw_size,
+ &error_fatal);
+ /*
+ * Map the guest's firmware image directly in its address space.
+ * Copying of the firmware image itself is done in function
+ * arm_setup_confidential_firmware_boot().
+ */
+ memory_region_add_subregion(sysmem, fw_base, fw_ram);
+
+ return true;
+}
+
static bool virt_firmware_init(VirtMachineState *vms,
MemoryRegion *sysmem,
MemoryRegion *secure_sysmem)
@@ -1914,6 +1960,15 @@ static bool virt_firmware_init(VirtMachineState *vms,
const char *bios_name;
BlockBackend *pflash_blk0;
+ /*
+ * For a confidential VM, the firmware image and any boot information,
+ * including EFI variables, are stored in RAM in order to be measurable and
+ * private. Create a RAM region and load the firmware image there.
+ */
+ if (virt_machine_is_confidential(vms)) {
+ return virt_confidential_firmware_init(vms, sysmem);
+ }
+
/* Map legacy -drive if=pflash to machine properties */
for (i = 0; i < ARRAY_SIZE(vms->flash); i++) {
pflash_cfi01_legacy_drive(vms->flash[i],
@@ -3276,7 +3331,10 @@ static void machvirt_init(MachineState *machine)
vms->bootinfo.get_dtb = machvirt_dtb;
vms->bootinfo.skip_dtb_autoload = true;
vms->bootinfo.firmware_loaded = firmware_loaded;
+ vms->bootinfo.firmware_base = vms->memmap[VIRT_FLASH].base;
+ vms->bootinfo.firmware_max_size = vms->memmap[VIRT_FLASH].size;
vms->bootinfo.psci_conduit = vms->psci_conduit;
+ vms->bootinfo.confidential = virt_machine_is_confidential(vms);
arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo);
vms->machine_done.notify = virt_machine_done;
diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h
index 94386fdbea6c..fe7e14e94651 100644
--- a/include/hw/arm/boot.h
+++ b/include/hw/arm/boot.h
@@ -113,6 +113,10 @@ struct arm_boot_info {
*/
bool firmware_loaded;
+ /* Used when loading firmware into RAM */
+ hwaddr firmware_base;
+ hwaddr firmware_max_size;
+
/* Address at which board specific loader/setup code exists. If enabled,
* this code-blob will run before anything else. It must return to the
* caller via the link register. There is no stack set up. Enabled by
@@ -136,6 +140,11 @@ struct arm_boot_info {
/* CPU having load the kernel and that should be the first to boot. */
ARMCPU *primary_cpu;
+
+ /*
+ * Confidential guest boot loads everything into RAM so it can be measured.
+ */
+ bool confidential;
};
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 22/25] target/arm/kvm-rme: Add DMA remapping for the shared memory region
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (20 preceding siblings ...)
2026-07-07 22:43 ` [RFC v1 21/25] hw/arm/virt: Use RAM instead of flash for confidential guest firmware Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
2026-07-07 22:43 ` [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature Mathieu Poirier
` (2 subsequent siblings)
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
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 8b79c3ea28e5..a8d777d63cd7 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -3325,6 +3325,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 58669f4a50f1..7d5f37ab9230 100644
--- a/target/arm/kvm-rme.c
+++ b/target/arm/kvm-rme.c
@@ -11,10 +11,12 @@
#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/units.h"
#include "qom/object_interfaces.h"
#include "system/confidential-guest-support.h"
#include "system/kvm.h"
@@ -25,6 +27,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 {
Notifier rom_load_notifier;
RmeRamRegion init_ram;
GSList *ram_regions;
+ uint8_t ipa_bits;
+
+ RealmDmaRegion *dma_region;
+ AddressSpace dma_as;
};
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -227,3 +250,86 @@ int kvm_arm_rme_vm_type(void)
}
return 0;
}
+
+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 790e1940fd8a..38a2b819fa5c 100644
--- a/target/arm/kvm-stub.c
+++ b/target/arm/kvm-stub.c
@@ -46,6 +46,10 @@ void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size)
{
}
+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 c7dc45595159..4716785e0fc0 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -277,4 +277,14 @@ int kvm_arm_rme_vm_type(void);
*/
void kvm_arm_rme_init_guest_ram(hwaddr base, size_t size);
+/**
+ * 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
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (21 preceding siblings ...)
2026-07-07 22:43 ` [RFC v1 22/25] target/arm/kvm-rme: Add DMA remapping for the shared memory region Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
2026-07-08 4:37 ` Markus Armbruster
2026-07-08 5:16 ` Philippe Mathieu-Daudé
2026-07-07 22:43 ` [RFC v1 24/25] hw/arm/boot: Load DTB as is for confidential VMs Mathieu Poirier
2026-07-07 22:43 ` [RFC v1 25/25] hw/arm/boot: Skip bootloader for confidential guests Mathieu Poirier
24 siblings, 2 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Some distributions provide packages continaing firmware to be run under
QEMU, such as "qemu-efi-aarch64" or "edk2-aarch64". Those packages also
contain descriptors in /usr/share/qemu/firmware/*.json listing the
firmware features, so that environments like libvirt can figure out
which firmware they can load.
Define an optional feature for arm64 firmware to indicate that a
firmware supports running in a Realm. Firmware implementations need
extra support for running in a Realm, in particular to distinguish
shared from private guest memory.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
docs/interop/firmware.json | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
index 421bee0e5ed4..c68d7f5013f8 100644
--- a/docs/interop/firmware.json
+++ b/docs/interop/firmware.json
@@ -161,6 +161,9 @@
# options related to this feature are documented in
# "docs/system/i386/amd-memory-encryption.rst".
#
+# @arm-rme: The firmware supports running in a Realm, under the Arm Realm
+# Management Extension (RME).
+#
# @intel-tdx: The firmware supports running under Intel Trust Domain
# Extensions (TDX).
#
@@ -229,7 +232,7 @@
{ 'enum' : 'FirmwareFeature',
'data' : [ 'acpi-s3', 'acpi-s4',
'amd-sev', 'amd-sev-es', 'amd-sev-snp',
- 'intel-tdx',
+ 'arm-rme', 'intel-tdx',
'enrolled-keys', 'requires-smm',
'secure-boot', 'host-uefi-vars',
'verbose-dynamic', 'verbose-static' ] }
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* Re: [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature
2026-07-07 22:43 ` [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature Mathieu Poirier
@ 2026-07-08 4:37 ` Markus Armbruster
2026-07-08 5:16 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 38+ messages in thread
From: Markus Armbruster @ 2026-07-08 4:37 UTC (permalink / raw)
To: Mathieu Poirier
Cc: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, jpb, lorenzo.pieralisi, qemu-devel, qemu-arm,
kvm
Mathieu Poirier <mathieu.poirier@linaro.org> writes:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> Some distributions provide packages continaing firmware to be run under
> QEMU, such as "qemu-efi-aarch64" or "edk2-aarch64". Those packages also
> contain descriptors in /usr/share/qemu/firmware/*.json listing the
> firmware features, so that environments like libvirt can figure out
> which firmware they can load.
>
> Define an optional feature for arm64 firmware to indicate that a
> firmware supports running in a Realm. Firmware implementations need
> extra support for running in a Realm, in particular to distinguish
> shared from private guest memory.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> docs/interop/firmware.json | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
> index 421bee0e5ed4..c68d7f5013f8 100644
> --- a/docs/interop/firmware.json
> +++ b/docs/interop/firmware.json
> @@ -161,6 +161,9 @@
> # options related to this feature are documented in
> # "docs/system/i386/amd-memory-encryption.rst".
> #
> +# @arm-rme: The firmware supports running in a Realm, under the Arm Realm
> +# Management Extension (RME).
> +#
Please format like this
# @arm-rme: The firmware supports running in a Realm, under the Arm
# Realm Management Extension (RME).
> # @intel-tdx: The firmware supports running under Intel Trust Domain
> # Extensions (TDX).
> #
> @@ -229,7 +232,7 @@
> { 'enum' : 'FirmwareFeature',
> 'data' : [ 'acpi-s3', 'acpi-s4',
> 'amd-sev', 'amd-sev-es', 'amd-sev-snp',
> - 'intel-tdx',
> + 'arm-rme', 'intel-tdx',
> 'enrolled-keys', 'requires-smm',
> 'secure-boot', 'host-uefi-vars',
> 'verbose-dynamic', 'verbose-static' ] }
With the formatting tidied up
Acked-by: Markus Armbruster <armbru@redhat.com>
^ permalink raw reply [flat|nested] 38+ messages in thread* Re: [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature
2026-07-07 22:43 ` [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature Mathieu Poirier
2026-07-08 4:37 ` Markus Armbruster
@ 2026-07-08 5:16 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-08 5:16 UTC (permalink / raw)
To: Mathieu Poirier, berrange, kchamart, pierrick.bouvier,
peter.maydell, mst, cohuck, pbonzini, eblake, armbru, jpb,
lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
On 8/7/26 00:43, Mathieu Poirier wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
> Some distributions provide packages continaing firmware to be run under
> QEMU, such as "qemu-efi-aarch64" or "edk2-aarch64". Those packages also
> contain descriptors in /usr/share/qemu/firmware/*.json listing the
> firmware features, so that environments like libvirt can figure out
> which firmware they can load.
>
> Define an optional feature for arm64 firmware to indicate that a
> firmware supports running in a Realm. Firmware implementations need
> extra support for running in a Realm, in particular to distinguish
> shared from private guest memory.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
> docs/interop/firmware.json | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [RFC v1 24/25] hw/arm/boot: Load DTB as is for confidential VMs
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (22 preceding siblings ...)
2026-07-07 22:43 ` [RFC v1 23/25] docs/interop/firmware.json: Add arm-rme firmware feature Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
2026-07-07 22:43 ` [RFC v1 25/25] hw/arm/boot: Skip bootloader for confidential guests Mathieu Poirier
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
Load DTB without modifications when provided. That way it is possible
for a verification entity to reconstruct the Realm's initial measurement.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/boot.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index aea044fbed9a..aae77870a5c4 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -478,7 +478,14 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
g_autoptr(MemoryDeviceInfoList) md_list = NULL;
Error *err = NULL;
- if (binfo->dtb_filename) {
+ if (binfo->dtb_filename && binfo->confidential) {
+ /*
+ * If the user is providing a DTB for a confidential VM, it is already
+ * tailored to this configuration and measured. Load it as is, without
+ * any modification.
+ */
+ return rom_add_file_fixed_as(binfo->dtb_filename, addr, -1, as);
+ } else if (binfo->dtb_filename) {
char *filename;
filename = qemu_find_file(QEMU_FILE_TYPE_DTB, binfo->dtb_filename);
if (!filename) {
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread* [RFC v1 25/25] hw/arm/boot: Skip bootloader for confidential guests
2026-07-07 22:42 [RFC v1 00/25] Add Realm support to QEMU-VMM Mathieu Poirier
` (23 preceding siblings ...)
2026-07-07 22:43 ` [RFC v1 24/25] hw/arm/boot: Load DTB as is for confidential VMs Mathieu Poirier
@ 2026-07-07 22:43 ` Mathieu Poirier
24 siblings, 0 replies; 38+ messages in thread
From: Mathieu Poirier @ 2026-07-07 22:43 UTC (permalink / raw)
To: berrange, kchamart, pierrick.bouvier, peter.maydell, mst, cohuck,
pbonzini, eblake, armbru, jpb, lorenzo.pieralisi
Cc: qemu-devel, qemu-arm, kvm
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
An independent verifier needs to reconstruct the content of guest memory
in order to attest that it is running trusted code. To avoid having to
reconstruct the bootloader generated by QEMU, skip this step and jump
directly to the kernel, with the DTB address in x0 as specified by the
Linux boot protocol [1].
[1] https://docs.kernel.org/arch/arm64/booting.html
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
hw/arm/boot.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index aae77870a5c4..a5918b50d278 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -732,7 +732,13 @@ static void do_cpu_reset(void *opaque)
if (cpu == info->primary_cpu) {
AddressSpace *as = arm_boot_address_space(cpu, info);
- cpu_set_pc(cs, info->loader_start);
+ if (info->confidential) {
+ assert(is_a64(env));
+ env->xregs[0] = info->dtb_start;
+ cpu_set_pc(cs, info->entry);
+ } else {
+ cpu_set_pc(cs, info->loader_start);
+ }
if (!have_dtb(info)) {
set_kernel_args(info, as);
@@ -822,7 +828,8 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
}
static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
- hwaddr *entry, AddressSpace *as)
+ hwaddr *entry, AddressSpace *as,
+ bool confidential)
{
const size_t max_bytes = LOAD_IMAGE_MAX_DECOMPRESSED_BYTES;
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
@@ -874,7 +881,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
* bootloader, we can just load it starting at 2MB+offset rather
* than 0MB + offset.
*/
- if (kernel_load_offset < BOOTLOADER_MAX_SIZE) {
+ if (kernel_load_offset < BOOTLOADER_MAX_SIZE &&
+ !confidential) {
kernel_load_offset += 2 * MiB;
}
}
@@ -958,7 +966,8 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
kernel_size = load_aarch64_image(info->kernel_filename,
- info->loader_start, &entry, as);
+ info->loader_start, &entry, as,
+ info->confidential);
is_linux = 1;
if (kernel_size >= 0) {
image_low_addr = entry;
@@ -1099,8 +1108,11 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
fixupcontext[FIXUP_ENTRYPOINT_LO] = entry;
fixupcontext[FIXUP_ENTRYPOINT_HI] = entry >> 32;
- arm_write_bootloader("bootloader", as, info->loader_start,
- primary_loader, fixupcontext);
+ /* Immediately jump to the kernel when guest is a Realm */
+ if (!info->confidential) {
+ arm_write_bootloader("bootloader", as, info->loader_start,
+ primary_loader, fixupcontext);
+ }
if (info->write_board_setup) {
info->write_board_setup(cpu, info);
--
2.43.0
^ permalink raw reply related [flat|nested] 38+ messages in thread