Skip to contentSkip to conversation

Add Realm support to QEMU-VMM

RFC Superseded Mathieu Poirier 6 participants 83 messages Sep 3 – Nov 11

Subsystems (2)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 cover Add Realm support to QEMU-VMM

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
-M confidential-guest-support=rme0,memory-backend=ram0 \
-object rme-guest,id=rme0,convert-in-place=on \
-object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
-cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

RFC v3: https://lore.kernel.org/kvm/[email protected]/
RFC v2: https://lore.kernel.org/kvm/[email protected]/
RFC v1: https://lists.gnu.org/archive/html/qemu-devel/2026-07/msg02307.html

Jean-Philippe Brucker (23):
linux-headers: Add RME related definitions
target/arm/kvm: Return immediately on error in kvm_arch_init() target/arm: Add confidential guest support
target/arm/kvm: Split kvm_arch_get/put_registers target/arm/kvm-rme: Initialize vCPU
target/arm/kvm: Create scratch Realm VM when requested target/arm/kvm: Use kvm_vm_check_extension() where necessary hw/core/loader: Add a ROM loader notifier target/arm/kvm-rme: Keep track of images loaded in Realm memory target/arm/kvm-rme: Populate Realm with runtime images target/arm/cpu: Set number of breakpoints and watchpoints in KVM target/arm/cpu: Set number of PMU counters in KVM target/arm/cpu: Don't read Realm registers
hw/arm/virt: Set proper conduit method for Realms hw/arm/virt: Embed Realm VM type with IPA address space hw/arm/virt: Reserve one bit of guest physical address for RME hw/arm/virt: Disable DTB randomness for confidential VMs hw/arm/virt: Move virt_flash_create() to machvirt_init() hw/arm/virt: Use RAM instead of flash for confidential guest firmware target/arm/kvm-rme: Add DMA remapping for the shared memory region docs/interop/firmware.json: Add arm-rme firmware feature hw/arm/boot: Load DTB as is for confidential VMs hw/arm/boot: Skip bootloader for confidential guests

Mathieu Poirier (1):
target/arm/kvm-rme: Add mechanic to initialize realms

 docs/interop/firmware.json                 |   5 +-
 docs/system/arm/virt.rst                   |   9 +-
 docs/system/confidential-guest-support.rst |   1 +
 hw/arm/boot.c                              |  70 ++++-
 hw/arm/virt.c                              | 142 +++++++--
 hw/core/loader.c                           |  15 +
 include/hw/arm/boot.h                      |   9 +
 include/hw/arm/virt.h                      |   2 +-
 include/hw/core/loader.h                   |  17 ++
 linux-headers/asm-arm64/kvm.h              |   9 +
 linux-headers/linux/kvm.h                  |  10 +-
 qapi/qom.json                              |  13 +
 target/arm/arm-qmp-cmds.c                  |   1 +
 target/arm/cpu.c                           |   5 +
 target/arm/cpu.h                           |  10 +
 target/arm/cpu64.c                         | 122 ++++++++
 target/arm/kvm-rme.c                       | 336 +++++++++++++++++++++
 target/arm/kvm-stub.c                      |   9 +
 target/arm/kvm.c                           | 202 ++++++++++++-
 target/arm/kvm_arm.h                       |  20 ++
 target/arm/meson.build                     |   5 +-
 21 files changed, 962 insertions(+), 50 deletions(-)
 create mode 100644 target/arm/kvm-rme.c
Signature · 1 line
2.43.0
#1 lore raw

RFC v4 1/24 linux-headers: Add RME related definitions

From: Jean-Philippe Brucker <[email protected]>

This is a temporary patch intended for testing purposes only. It provides definitions related to supporting Realms by the QEMU-VMM.

---
 linux-headers/asm-arm64/kvm.h |  9 +++++++++
 linux-headers/linux/kvm.h     | 10 +++++++++-
 2 files changed, 18 insertions(+), 1 deletion(-)
linux-headers/asm-arm64/kvm.h +9 -0
index 6aefe7973814..4c08631ec7d2 100644
@@ -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
 
linux-headers/linux/kvm.h +9 -1
index 29f81c05aca2..8b7627f11447 100644
@@ -689,14 +689,19 @@ struct kvm_enable_cap {
  * 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.
+ *
+ * Bits[30-31] are reserved for the VM type
  */
 #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)
 
+#define KVM_VM_TYPE_ARM_NORMAL		0
+#define KVM_VM_TYPE_ARM_REALM		(1UL << 30)
 #define KVM_VM_TYPE_ARM_PROTECTED	(1UL << 31)
 #define KVM_VM_TYPE_ARM_MASK		(KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
-					 KVM_VM_TYPE_ARM_PROTECTED)
+					 KVM_VM_TYPE_ARM_PROTECTED | \
+					 KVM_VM_TYPE_ARM_REALM)
 
 /*
  * ioctls for /dev/kvm fds:
@@ -719,6 +724,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.
  */
@@ -988,6 +995,7 @@ struct kvm_enable_cap {
 #define KVM_CAP_S390_HPAGE_2G 249
 #define KVM_CAP_PPC_COMPAT_CAPS 250
 #define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 251
+#define KVM_CAP_ARM_RMI 252
 
 struct kvm_irq_routing_irqchip {
 	__u32 irqchip;
Signature · 1 line
2.43.0
#2 lore raw

Re: [RFC v4 01/24] linux-headers: Add RME related definitions

Hi Mathieu,

Quoted 67 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

This is a temporary patch intended for testing purposes only. It provides definitions related to supporting Realms by the QEMU-VMM.

---
  linux-headers/asm-arm64/kvm.h |  9 +++++++++
  linux-headers/linux/kvm.h     | 10 +++++++++-
  2 files changed, 18 insertions(+), 1 deletion(-)
linux-headers/asm-arm64/kvm.h +9 -0
index 6aefe7973814..4c08631ec7d2 100644
@@ -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
 
linux-headers/linux/kvm.h +8 -1
index 29f81c05aca2..8b7627f11447 100644
@@ -689,14 +689,19 @@ struct kvm_enable_cap {
   * 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.
+ *
+ * Bits[30-31] are reserved for the VM type
   */
  #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)
 
+#define KVM_VM_TYPE_ARM_NORMAL		0
+#define KVM_VM_TYPE_ARM_REALM		(1UL << 30)
  #define KVM_VM_TYPE_ARM_PROTECTED	(1UL << 31)
  #define KVM_VM_TYPE_ARM_MASK		(KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
-					 KVM_VM_TYPE_ARM_PROTECTED)
+					 KVM_VM_TYPE_ARM_PROTECTED | \
+					 KVM_VM_TYPE_ARM_REALM)
 
  /*
   * ioctls for /dev/kvm fds:
@@ -719,6 +724,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.
   */

linux-headers/linux/kvm.h struct kvm_enable_cap { · line 999

Quoted 5 lines
linux-headers/linux/kvm.h +1 -0
@@ -988,6 +995,7 @@ struct kvm_enable_cap {
  #define KVM_CAP_S390_HPAGE_2G 249
  #define KVM_CAP_PPC_COMPAT_CAPS 250
  #define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 251
+#define KVM_CAP_ARM_RMI 252
 

Both KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI aren't consistent to the definitions in Steven Price's (v16) host series [1]. They need corrections as below. Otherwise, realm VM can't be started and the following errors are raised by qemu.

[1] https://lore.kernel.org/kvm/[email protected]/

root@host:~# qemu-system-aarch64 -enable-kvm                  \
-object rme-guest,id=rme0,convert-in-place=on                 \
-machine virt,gic-version=3,confidential-guest-support=rme0   \
-m 2G -cpu host                                               \
-smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2  \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on   \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                      \
-serial mon:stdio -monitor none -nographic -nodefaults        \
-kernel /mnt/linux/arch/arm64/boot/Image                      \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz           \
-append earlycon=pl011,mmio,0x10009000000
    :
qemu-system-aarch64: VM doesn't support Realms
qemu-system-aarch64: failed to initialize kvm: No such device

-#define KVM_CAP_PPC_COMPAT_CAPS 250 -#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 251 -#define KVM_CAP_ARM_RMI 252
+#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 250 +#define KVM_CAP_ARM_RMI 251

linux-headers/linux/kvm.h struct kvm_enable_cap { · line 1001

Quoted 2 lines
linux-headers/linux/kvm.h +0 -0
  struct kvm_irq_routing_irqchip {
  	__u32 irqchip;

Thanks,
Gavin

#3 lore raw

Re: [RFC v4 01/24] linux-headers: Add RME related definitions

Quoted 66 lines · Gavin Shan

Hi Mathieu,

Quoted 63 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

This is a temporary patch intended for testing purposes only. It provides definitions related to supporting Realms by the QEMU-VMM.

---
  linux-headers/asm-arm64/kvm.h |  9 +++++++++
  linux-headers/linux/kvm.h     | 10 +++++++++-
  2 files changed, 18 insertions(+), 1 deletion(-)
linux-headers/asm-arm64/kvm.h +9 -0
index 6aefe7973814..4c08631ec7d2 100644
@@ -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
linux-headers/linux/kvm.h +8 -1
index 29f81c05aca2..8b7627f11447 100644
@@ -689,14 +689,19 @@ struct kvm_enable_cap {
   * 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.
+ *
+ * Bits[30-31] are reserved for the VM type
   */
  #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)
+#define KVM_VM_TYPE_ARM_NORMAL		0
+#define KVM_VM_TYPE_ARM_REALM		(1UL << 30)
  #define KVM_VM_TYPE_ARM_PROTECTED	(1UL << 31)
  #define KVM_VM_TYPE_ARM_MASK		(KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
-					 KVM_VM_TYPE_ARM_PROTECTED)
+					 KVM_VM_TYPE_ARM_PROTECTED | \
+					 KVM_VM_TYPE_ARM_REALM)
  /*
   * ioctls for /dev/kvm fds:
@@ -719,6 +724,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.
   */
@@ -988,6 +995,7 @@ struct kvm_enable_cap {
  #define KVM_CAP_S390_HPAGE_2G 249
Quoted 8 lines

linux-headers/linux/kvm.h struct kvm_enable_cap { · line 998

Quoted 3 lines
linux-headers/linux/kvm.h +1 -0
  #define KVM_CAP_PPC_COMPAT_CAPS 250
  #define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 251
+#define KVM_CAP_ARM_RMI 252

Both KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI aren't consistent to the definitions in Steven Price's (v16) host series [1]. They need corrections as below. Otherwise, realm VM can't be started and the following errors are raised by qemu.

The changelog is clear - numbers for those defines have been bumped up to keep pace with the definitions in the QEMU master branch. RFC v3 had defines that were similar to Steven's v16 and as such used an older QEMU baseline, but Markus said it wasn't appropriate.

Quoted 31 lines

[1] https://lore.kernel.org/kvm/[email protected]/

root@host:~# qemu-system-aarch64 -enable-kvm                  \
(quoted diff) +0 -10
-object rme-guest,id=rme0,convert-in-place=on                 \
-machine virt,gic-version=3,confidential-guest-support=rme0   \
-m 2G -cpu host                                               \
-smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2  \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on   \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                      \
-serial mon:stdio -monitor none -nographic -nodefaults        \
-kernel /mnt/linux/arch/arm64/boot/Image                      \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz           \
-append earlycon=pl011,mmio,0x10009000000
    :

qemu-system-aarch64: VM doesn't support Realms
qemu-system-aarch64: failed to initialize kvm: No such device

(quoted diff) +2 -3
-#define KVM_CAP_PPC_COMPAT_CAPS 250
-#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 251
-#define KVM_CAP_ARM_RMI 252
+#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 250
+#define KVM_CAP_ARM_RMI 251

linux-headers/linux/kvm.h struct kvm_enable_cap { · line 1001

Quoted 3 lines
linux-headers/linux/kvm.h +0 -0
  struct kvm_irq_routing_irqchip {
  	__u32 irqchip;

Thanks,
Gavin

#4 lore raw

RFC v4 2/24 target/arm/kvm: Return immediately on error in kvm_arch_init()

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 target/arm/kvm.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
target/arm/kvm.c +5 -4
index d40a6a985914..ed99be7fd80c 100644
@@ -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)
Signature · 1 line
2.43.0
#5 lore raw

Re: [RFC v4 02/24] target/arm/kvm: Return immediately on error in kvm_arch_init()

Quoted 29 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 target/arm/kvm.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
target/arm/kvm.c +1 -1
index d40a6a985914..ed99be7fd80c 100644
@@ -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.
      */

target/arm/kvm.c int kvm_arch_init(MachineState *ms, KVMState *s) · line 624

Quoted 8 lines
target/arm/kvm.c +1 -1
@@ -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)) {

There's a error_report("Failed to enable KVM_CAP_ARM_NISV_TO_USER cap") under here as well that doesn't have a return -EINVAL after this patch. Is that intentional?

#6 lore raw

Re: [RFC v4 02/24] target/arm/kvm: Return immediately on error in kvm_arch_init()

Quoted 37 lines · Michael Roth
Quoted 36 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 target/arm/kvm.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
target/arm/kvm.c +2 -2
index d40a6a985914..ed99be7fd80c 100644
@@ -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;
     }
 
Quoted 5 lines

target/arm/kvm.c int kvm_arch_init(MachineState *ms, KVMState *s) · line 624

Quoted 1 line
target/arm/kvm.c +0 -0
     if (kvm_check_extension(s, KVM_CAP_ARM_NISV_TO_USER)) {

There's a error_report("Failed to enable KVM_CAP_ARM_NISV_TO_USER cap") under here as well that doesn't have a return -EINVAL after this patch. Is that intentional?

This patch did not touch the clause that checks KVM_CAP_ARM_NISV_TO_USER. As far as I can tell, it is mainline code.

#7 lore raw

RFC v4 3/24 target/arm: Add confidential guest support

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +13 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0

42 lines not on this page · raw message

target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
Acked-by on v4 3/24
#8 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 13 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

[...]

Quoted 5 lines
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644

qapi/qom.json line 1296

Quoted 9 lines
qapi/qom.json +6 -0
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1

11.2 now.

Quoted 17 lines
qapi/qom.json +6 -0
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',

qapi/qom.json line 1445

Quoted 7 lines
qapi/qom.json +1 -0
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',

With the Since: fixed

[...]

#9 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

In reply to Mathieu Poirier
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#10 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 57 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

Quoted 8 lines

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#11 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#12 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 71 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 9 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

I'm almost done crafting a new revision that will be based on V20 of the KVM patchset. I will look into your request for the revision after this one.

Quoted 100 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#13 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

In reply to Kohei Enju
Quoted 71 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 8 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Are you suggesting that on Arm, we completely get rid of

    -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#14 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 76 lines · Mathieu Poirier
Quoted 75 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

Quoted 6 lines
Quoted 4 lines

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Are you suggesting that on Arm, we completely get rid of

Nit: obviously on Arm rme-guest configs.

Quoted 1 line
    -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

If that's what we want to make a default, I guess we ought to override create_default_memdev for arm machines + rme-guest configs ?

Pros and cons ? I am not sure how well received is setting implicit defaults for specific machine configs and what that really buys us eventually.

Quoted 4 lines

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

Yes please any feedback on this is welcome.

Thanks,
Lorenzo

Quoted 104 lines

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#15 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 77 lines · Lorenzo Pieralisi
Quoted 76 lines · Mathieu Poirier
Quoted 75 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Are you suggesting that on Arm, we completely get rid of

Nit: obviously on Arm rme-guest configs.

Indeed.

Quoted 8 lines
Quoted 2 lines
    -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

If that's what we want to make a default, I guess we ought to override create_default_memdev for arm machines + rme-guest configs ?

Pros and cons ? I am not sure how well received is setting implicit defaults for specific machine configs and what that really buys us eventually.

I agree, this entails a fair amount of changes for cosmetic gains only.

Quoted 115 lines
Quoted 5 lines

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

Yes please any feedback on this is welcome.

Thanks,
Lorenzo

Quoted 104 lines

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#16 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 92 lines · Mathieu Poirier
Quoted 77 lines · Lorenzo Pieralisi
Quoted 76 lines · Mathieu Poirier
Quoted 75 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Are you suggesting that on Arm, we completely get rid of

Nit: obviously on Arm rme-guest configs.

Indeed.

Quoted 3 lines
Quoted 2 lines
    -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on
Quoted 7 lines
Quoted 5 lines

If that's what we want to make a default, I guess we ought to override create_default_memdev for arm machines + rme-guest configs ?

Pros and cons ? I am not sure how well received is setting implicit defaults for specific machine configs and what that really buys us eventually.

I agree, this entails a fair amount of changes for cosmetic gains only.

I think it will also add some uneccessary confusion. For instance, based on Paolo's feedback[1], we will likely not expose any sort of option to switch between in-place/out-of-place conversion for SNP/TDX, but instead just use whatever mode the KVM module was actually configured to use.

For in-place conversion, it actually doesn't seem too bad to hook into create_default_memdev to have it default to memory-backend-memfd. However, currently, it used backend-file/backend-ram depending on other options, and those options are applicable to out-of-place conversion as well where e.g. maybe the user wants to use file-backed hugetlb memory for their shared memory. So we wouldn't want to change the behavior for out-of-place.

But then, for in-place, if we default to a different backend then stuff like mem-path=... parameters for out-of-place invocations will then become silently ignored, or we will need to generate an error for those, which works against the idea of having a common cmdline that "just works" for in-place/out-of-place.

If we (users, management/libvirt, etc.) consolidate around specifying the memfd backend for confidential VMs, then we can have a "default" that is more consistent between in-place vs. out-of-place, which i think is more beneficial than trying to consolidate around a syntacticly simpler cmdline that might not behave as expected when switching between older/newer kernels which may or may not have in-place support.

So, a longwinded "I agree" from me as well.

Thanks,

Mike

Quoted 116 lines
Quoted 116 lines
Quoted 6 lines

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

Yes please any feedback on this is welcome.

Thanks,
Lorenzo

Quoted 104 lines

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#17 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 96 lines · Michael Roth
Quoted 95 lines · Mathieu Poirier
Quoted 77 lines · Lorenzo Pieralisi
Quoted 76 lines · Mathieu Poirier
Quoted 75 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Are you suggesting that on Arm, we completely get rid of

Nit: obviously on Arm rme-guest configs.

Indeed.

Quoted 6 lines
Quoted 2 lines
    -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

If that's what we want to make a default, I guess we ought to override create_default_memdev for arm machines + rme-guest configs ?

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

Pros and cons ? I am not sure how well received is setting implicit defaults for specific machine configs and what that really buys us eventually.

I agree, this entails a fair amount of changes for cosmetic gains only.

I think it will also add some uneccessary confusion. For instance, based on Paolo's feedback[1], we will likely not expose any sort of option to

I think that [1] is:

https://lore.kernel.org/kvm/[email protected]

for completeness.

Quoted 6 lines

switch between in-place/out-of-place conversion for SNP/TDX, but instead just use whatever mode the KVM module was actually configured to use.

For in-place conversion, it actually doesn't seem too bad to hook into create_default_memdev to have it default to memory-backend-memfd. However, currently, it used backend-file/backend-ram depending on other

"it used" - can I ask what "it" refers to please ?

Quoted 4 lines

options, and those options are applicable to out-of-place conversion as well where e.g. maybe the user wants to use file-backed hugetlb memory for their shared memory. So we wouldn't want to change the behavior for out-of-place.

So, here you are saying that basically a sensible default for out-of-place might not work for convert-in-place and therefore it does not make much sense to have a default that does/doesn't work depending on how KVM is configured.

Quoted 5 lines

But then, for in-place, if we default to a different backend then stuff like mem-path=... parameters for out-of-place invocations will then become silently ignored, or we will need to generate an error for those, which works against the idea of having a common cmdline that "just works" for in-place/out-of-place.

Yes that makes sense.

Quoted 6 lines

If we (users, management/libvirt, etc.) consolidate around specifying the memfd backend for confidential VMs, then we can have a "default" that is more consistent between in-place vs. out-of-place, which i think is more beneficial than trying to consolidate around a syntacticly simpler cmdline that might not behave as expected when switching between older/newer kernels which may or may not have in-place support.

Can you elaborate on this Michael please ? In particular in relation to a more consistent "default" for in-place/out-of-place, are you referring to defining a -object memfd option that works for both in-place and out-of-place ? Isn't that the case already with your latest v3 (and the VMM detecting the KVM underlying config ie in-place vs out-of-place) ?

Quoted 5 lines

So, a longwinded "I agree" from me as well.

Thanks,

Mike

Thanks,
Lorenzo

Quoted 116 lines
Quoted 116 lines
Quoted 116 lines
Quoted 6 lines

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

Yes please any feedback on this is welcome.

Thanks,
Lorenzo

Quoted 104 lines

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#18 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 102 lines · Lorenzo Pieralisi
Quoted 101 lines · Michael Roth
Quoted 99 lines · Mathieu Poirier
Quoted 77 lines · Lorenzo Pieralisi
Quoted 76 lines · Mathieu Poirier
Quoted 75 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
 docs/system/confidential-guest-support.rst |  1 +
 qapi/qom.json                              | 13 +++++++
 target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
 target/arm/meson.build                     |  5 ++-
 4 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 target/arm/kvm-rme.c
docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
   'data': { '*pretty': 'bool',
             '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
  cgs->allow_convert_in_place = true;
  cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Are you suggesting that on Arm, we completely get rid of

Nit: obviously on Arm rme-guest configs.

Indeed.

Quoted 8 lines
Quoted 2 lines
    -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

If that's what we want to make a default, I guess we ought to override create_default_memdev for arm machines + rme-guest configs ?

Pros and cons ? I am not sure how well received is setting implicit defaults for specific machine configs and what that really buys us eventually.

I agree, this entails a fair amount of changes for cosmetic gains only.

Quoted 8 lines
Quoted 2 lines

I think it will also add some uneccessary confusion. For instance, based on Paolo's feedback[1], we will likely not expose any sort of option to

I think that [1] is:

https://lore.kernel.org/kvm/[email protected]

for completeness.

Yes, sorry, I should know better by now that adding citations before adding the links is a bad idea.

Quoted 9 lines
Quoted 7 lines

switch between in-place/out-of-place conversion for SNP/TDX, but instead just use whatever mode the KVM module was actually configured to use.

For in-place conversion, it actually doesn't seem too bad to hook into create_default_memdev to have it default to memory-backend-memfd. However, currently, it used backend-file/backend-ram depending on other

"it used" - can I ask what "it" refers to please ?

"it used" -> "QEMU uses" is what I meant.

Quoted 26 lines
Quoted 5 lines

options, and those options are applicable to out-of-place conversion as well where e.g. maybe the user wants to use file-backed hugetlb memory for their shared memory. So we wouldn't want to change the behavior for out-of-place.

So, here you are saying that basically a sensible default for out-of-place might not work for convert-in-place and therefore it does not make much sense to have a default that does/doesn't work depending on how KVM is configured.

Quoted 5 lines

But then, for in-place, if we default to a different backend then stuff like mem-path=... parameters for out-of-place invocations will then become silently ignored, or we will need to generate an error for those, which works against the idea of having a common cmdline that "just works" for in-place/out-of-place.

Yes that makes sense.

Quoted 6 lines

If we (users, management/libvirt, etc.) consolidate around specifying the memfd backend for confidential VMs, then we can have a "default" that is more consistent between in-place vs. out-of-place, which i think is more beneficial than trying to consolidate around a syntacticly simpler cmdline that might not behave as expected when switching between older/newer kernels which may or may not have in-place support.

Quoted 5 lines

Can you elaborate on this Michael please ? In particular in relation to a more consistent "default" for in-place/out-of-place, are you referring to defining a -object memfd option that works for both in-place and out-of-place ? Isn't that the case already with your latest v3 (and the VMM detecting the KVM underlying config ie in-place vs out-of-place) ?

To clarify, what I mean is that if we all CoCo platforms use a similar command-line like:

qemu-system-<arch> -object memory-backend-memfd,...

then that would be consistent not only across all CoCo platforms (for currently implementations anyway...) that support only in-place conversion, but it would also be consistent across CoCo platforms that support both in-place/out-of-place (SNP/TDX). There seems to be a desire for the latter based on Paolo's comments (thanks for fixing the references).

If however we introduce some syntactic sugar to have it handled automatically by -m / create_default_memdev(), then any users who previously ran SNP/TDX for out-of-place conversion with something like:

qemu-system-x86_64 -m <size> -machine ...,mem-path=/path/to/hugetlb

would break/misbehave if we enabled in-place conversion. And that's okay, but it makes clear that using -m comes with a number of restrictions in the case of in-place-conversion (e.g. mem-path not supported), that aren't restrictions for out-of-place-conversion, and that can cause some confusion if we don't document it appropriately.

I don't have any strong objection to that, but it's just trickier to document/explain, whereas if we all just generally agree to use:

qemu-system-<arch> -object memory-backend-memfd,...

a lot of this ambiguity goes away. So I prefer specifying the backend explicitly, especially since guest-memfd is a well-understood commonality for CoCo VMs and we can concentrate our documentation and how we expose that to management/libvirt through this same backend.

That said, there's no reason we can't also have -m "Do the right thing" for in-place conversion, I just don't think we should lean on that too heavily in our documentation/management tooling, and keep it as more of a 'shortcut' for non-production use-cases if we decide it's worth adding.

Thanks,

Mike

Quoted 126 lines
Quoted 6 lines

So, a longwinded "I agree" from me as well.

Thanks,

Mike

Thanks,
Lorenzo

Quoted 116 lines
Quoted 116 lines
Quoted 116 lines
Quoted 6 lines

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

Yes please any feedback on this is welcome.

Thanks,
Lorenzo

Quoted 104 lines

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
 ##
 # @ObjectType:
 #
@@ -1346,6 +1357,7 @@
     { 'name': 'pr-manager-helper',
       'if': 'CONFIG_LINUX' },
     'qtest',
+    'rme-guest',
     'rng-builtin',
     'rng-egd',
     { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
       'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                       'if': 'CONFIG_LINUX' },
       'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
       'rng-builtin':                'RngProperties',
       'rng-egd':                    'RngEgdProperties',
       'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#19 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

In reply to Mathieu Poirier
Quoted 81 lines · Mathieu Poirier
Quoted 71 lines · Kohei Enju
Quoted 62 lines · Lorenzo Pieralisi
Quoted 61 lines · Michael Roth
Quoted 39 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
  docs/system/confidential-guest-support.rst |  1 +
  qapi/qom.json                              | 13 +++++++
  target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
  target/arm/meson.build                     |  5 ++-
  4 files changed, 60 insertions(+), 1 deletion(-)

create mode 100644 target/arm/kvm-rme.c

docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
    'data': { '*pretty': 'bool',
              '*close-action': 'MonitorQMPCloseAction' } }

qapi/qom.json line 1299

Quoted 10 lines
qapi/qom.json +9 -0
 
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
   cgs->allow_convert_in_place = true;
   cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 8 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Quoted 9 lines

Are you suggesting that on Arm, we completely get rid of

     -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

I don't think that's what Kohei meant. He actually suggested the memory backend shall be pinned to memory-backend-guest-memfd and its in-place conversion shall be enabled when the memory backend isn't explicitly specified by users through "-object memory-backend-xxxx". For this inexplicit case, where the memory backend is determined by '-mem-path', the memory backend is either TYPE_MEMORY_BACKEND_FILE or TYPE_MEMORY_BACKEND_RAM. Please refer to create_default_memdev().

I think Kohei's suggestion makes sense. Besides, I think '-mem-path' should be disallowed for CCA Realms as TYPE_MEMORY_BACKEND_FILE isn't what we want.

Thanks,
Gavin

Quoted 105 lines

Thanks,
Mathieu

Quoted 101 lines

Thanks,
Kohei

Quoted 97 lines

Thanks,
Lorenzo

Quoted 93 lines

Thanks,

Mike

Quoted 89 lines
qapi/qom.json +4 -0
+  'data': {} }
+
  ##
  # @ObjectType:
  #
@@ -1346,6 +1357,7 @@
      { 'name': 'pr-manager-helper',
        'if': 'CONFIG_LINUX' },
      'qtest',
+    'rme-guest',
      'rng-builtin',
      'rng-egd',
      { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
        'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                        'if': 'CONFIG_LINUX' },
        'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
        'rng-builtin':                'RngProperties',
        'rng-egd':                    'RngEgdProperties',
        'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#20 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 97 lines · Gavin Shan
Quoted 79 lines · Mathieu Poirier
Quoted 69 lines · Kohei Enju
Quoted 60 lines · Lorenzo Pieralisi
Quoted 59 lines · Michael Roth
Quoted 37 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
  docs/system/confidential-guest-support.rst |  1 +
  qapi/qom.json                              | 13 +++++++
  target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
  target/arm/meson.build                     |  5 ++-
  4 files changed, 60 insertions(+), 1 deletion(-)

create mode 100644 target/arm/kvm-rme.c

docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
    'data': { '*pretty': 'bool',

qapi/qom.json line 1298

Quoted 10 lines
qapi/qom.json +9 -0
              '*close-action': 'MonitorQMPCloseAction' } }
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
   cgs->allow_convert_in_place = true;
   cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 8 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Quoted 9 lines

Are you suggesting that on Arm, we completely get rid of

     -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

I don't think that's what Kohei meant. He actually suggested the memory backend shall be pinned to memory-backend-guest-memfd and its in-place conversion shall be enabled when the memory backend isn't explicitly specified by users through "-object memory-backend-xxxx". For this inexplicit case, where the memory backend is determined by '-mem-path', the memory backend is either TYPE_MEMORY_BACKEND_FILE or TYPE_MEMORY_BACKEND_RAM. Please refer to create_default_memdev().

Quoted 2 lines

I think Kohei's suggestion makes sense. Besides, I think '-mem-path' should be disallowed for CCA Realms as TYPE_MEMORY_BACKEND_FILE isn't what we want.

Kohei, what were you suggesting then please ? I think TYPE_MEMORY_BACKEND_FILE would not pass the checks required by Michael's v3 [0] on CCA - I will test this and report back but the only backend allowed, rightly, is memfd,share=on, so it _is_ disallowed.

Lorenzo

[0] https://lore.kernel.org/kvm/[email protected]/

Quoted 108 lines

Thanks,
Gavin

Quoted 104 lines

Thanks,
Mathieu

Quoted 100 lines

Thanks,
Kohei

Quoted 96 lines

Thanks,
Lorenzo

Quoted 92 lines

Thanks,

Mike

Quoted 88 lines
qapi/qom.json +4 -0
+  'data': {} }
+
  ##
  # @ObjectType:
  #
@@ -1346,6 +1357,7 @@
      { 'name': 'pr-manager-helper',
        'if': 'CONFIG_LINUX' },
      'qtest',
+    'rme-guest',
      'rng-builtin',
      'rng-egd',
      { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
        'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                        'if': 'CONFIG_LINUX' },
        'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
        'rng-builtin':                'RngProperties',
        'rng-egd':                    'RngEgdProperties',
        'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#21 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 98 lines · Lorenzo Pieralisi
Quoted 97 lines · Gavin Shan
Quoted 79 lines · Mathieu Poirier
Quoted 69 lines · Kohei Enju
Quoted 60 lines · Lorenzo Pieralisi
Quoted 59 lines · Michael Roth
Quoted 37 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
  docs/system/confidential-guest-support.rst |  1 +
  qapi/qom.json                              | 13 +++++++
  target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
  target/arm/meson.build                     |  5 ++-
  4 files changed, 60 insertions(+), 1 deletion(-)

create mode 100644 target/arm/kvm-rme.c

docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
    'data': { '*pretty': 'bool',

qapi/qom.json line 1298

Quoted 10 lines
qapi/qom.json +9 -0
              '*close-action': 'MonitorQMPCloseAction' } }
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
   cgs->allow_convert_in_place = true;
   cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 8 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Quoted 9 lines

Are you suggesting that on Arm, we completely get rid of

     -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

I don't think that's what Kohei meant. He actually suggested the memory backend shall be pinned to memory-backend-guest-memfd and its in-place conversion shall be enabled when the memory backend isn't explicitly specified by users through "-object memory-backend-xxxx". For this inexplicit case, where the memory backend is determined by '-mem-path', the memory backend is either TYPE_MEMORY_BACKEND_FILE or TYPE_MEMORY_BACKEND_RAM. Please refer to create_default_memdev().

Quoted 7 lines
Quoted 2 lines

I think Kohei's suggestion makes sense. Besides, I think '-mem-path' should be disallowed for CCA Realms as TYPE_MEMORY_BACKEND_FILE isn't what we want.

Kohei, what were you suggesting then please ? I think TYPE_MEMORY_BACKEND_FILE would not pass the checks required by Michael's v3 [0] on CCA - I will test this and report back but the only backend allowed, rightly, is memfd,share=on, so it _is_ disallowed.

Sorry for the late response.

To clarify, I meant the latter: when rme-guest is selected and the user has not explicitly provided a memory backend, QEMU could automatically create the default RAM as a shared guest-memfd backend.

In other words, rather than only rejecting the default file/RAM backend, the Realm configuration could select the equivalent of

-object memory-backend-guest-memfd,id=<default-ram-id>,size=<RAMSIZE>,share=on

by default. Explicitly specified memory backends would still need to be validated against the Realm requirements.

My intention was that, if in-place conversion is the baseline for CCA Realms, -m <size> should be sufficient for the common Realm case, while preserving an explicit memory-backend option for users who need to control it.

That said, I do not have a strong preference for deciding the default -m behavior at this stage. If it is premature to make that decision before the memory-backend and conversion interfaces have settled, I am happy to keep the explicit guest-memfd backend requirement for now and revisit the default behavior later.

Thanks,
Kohei

Quoted 113 lines

Lorenzo

[0] https://lore.kernel.org/kvm/[email protected]/

Quoted 108 lines

Thanks,
Gavin

Quoted 104 lines

Thanks,
Mathieu

Quoted 100 lines

Thanks,
Kohei

Quoted 96 lines

Thanks,
Lorenzo

Quoted 92 lines

Thanks,

Mike

Quoted 88 lines
qapi/qom.json +4 -0
+  'data': {} }
+
  ##
  # @ObjectType:
  #
@@ -1346,6 +1357,7 @@
      { 'name': 'pr-manager-helper',
        'if': 'CONFIG_LINUX' },
      'qtest',
+    'rme-guest',
      'rng-builtin',
      'rng-egd',
      { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
        'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                        'if': 'CONFIG_LINUX' },
        'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
        'rng-builtin':                'RngProperties',
        'rng-egd':                    'RngEgdProperties',
        'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#22 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 118 lines · Kohei Enju
Quoted 98 lines · Lorenzo Pieralisi
Quoted 97 lines · Gavin Shan
Quoted 79 lines · Mathieu Poirier
Quoted 69 lines · Kohei Enju
Quoted 60 lines · Lorenzo Pieralisi
Quoted 59 lines · Michael Roth
Quoted 37 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
  docs/system/confidential-guest-support.rst |  1 +
  qapi/qom.json                              | 13 +++++++
  target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
  target/arm/meson.build                     |  5 ++-
  4 files changed, 60 insertions(+), 1 deletion(-)

create mode 100644 target/arm/kvm-rme.c

docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
    'data': { '*pretty': 'bool',

qapi/qom.json line 1298

Quoted 10 lines
qapi/qom.json +9 -0
              '*close-action': 'MonitorQMPCloseAction' } }
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
   cgs->allow_convert_in_place = true;
   cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 8 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Quoted 9 lines

Are you suggesting that on Arm, we completely get rid of

     -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

I don't think that's what Kohei meant. He actually suggested the memory backend shall be pinned to memory-backend-guest-memfd and its in-place conversion shall be enabled when the memory backend isn't explicitly specified by users through "-object memory-backend-xxxx". For this inexplicit case, where the memory backend is determined by '-mem-path', the memory backend is either TYPE_MEMORY_BACKEND_FILE or TYPE_MEMORY_BACKEND_RAM. Please refer to create_default_memdev().

Quoted 7 lines
Quoted 2 lines

I think Kohei's suggestion makes sense. Besides, I think '-mem-path' should be disallowed for CCA Realms as TYPE_MEMORY_BACKEND_FILE isn't what we want.

Kohei, what were you suggesting then please ? I think TYPE_MEMORY_BACKEND_FILE would not pass the checks required by Michael's v3 [0] on CCA - I will test this and report back but the only backend allowed, rightly, is memfd,share=on, so it _is_ disallowed.

Sorry for the late response.

To clarify, I meant the latter: when rme-guest is selected and the user has not explicitly provided a memory backend, QEMU could automatically create the default RAM as a shared guest-memfd backend.

In other words, rather than only rejecting the default file/RAM backend, the Realm configuration could select the equivalent of

-object memory-backend-guest-memfd,id=<default-ram-id>,size=<RAMSIZE>,share=on

Quoted 7 lines

by default. Explicitly specified memory backends would still need to be validated against the Realm requirements.

My intention was that, if in-place conversion is the baseline for CCA Realms, -m <size> should be sufficient for the common Realm case, while preserving an explicit memory-backend option for users who need to control it.

Many thanks for the clarification, it is very helpful. I will look into implementing something along those lines for V6 and we can reconsider how to move forward at that time.

Thanks,
Mathieu

Quoted 123 lines

That said, I do not have a strong preference for deciding the default -m behavior at this stage. If it is premature to make that decision before the memory-backend and conversion interfaces have settled, I am happy to keep the explicit guest-memfd backend requirement for now and revisit the default behavior later.

Thanks,
Kohei

Quoted 113 lines

Lorenzo

[0] https://lore.kernel.org/kvm/[email protected]/

Quoted 108 lines

Thanks,
Gavin

Quoted 104 lines

Thanks,
Mathieu

Quoted 100 lines

Thanks,
Kohei

Quoted 96 lines

Thanks,
Lorenzo

Quoted 92 lines

Thanks,

Mike

Quoted 88 lines
qapi/qom.json +4 -0
+  'data': {} }
+
  ##
  # @ObjectType:
  #
@@ -1346,6 +1357,7 @@
      { 'name': 'pr-manager-helper',
        'if': 'CONFIG_LINUX' },
      'qtest',
+    'rme-guest',
      'rng-builtin',
      'rng-egd',
      { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
        'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                        'if': 'CONFIG_LINUX' },
        'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
        'rng-builtin':                'RngProperties',
        'rng-egd':                    'RngEgdProperties',
        'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#23 lore raw

Re: [RFC v4 03/24] target/arm: Add confidential guest support

Quoted 122 lines · Mathieu Poirier
Quoted 121 lines · Kohei Enju
Quoted 98 lines · Lorenzo Pieralisi
Quoted 97 lines · Gavin Shan
Quoted 79 lines · Mathieu Poirier
Quoted 69 lines · Kohei Enju
Quoted 60 lines · Lorenzo Pieralisi
Quoted 59 lines · Michael Roth
Quoted 37 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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>

This is only the skeleton. Support will be added in following patches.

---
  docs/system/confidential-guest-support.rst |  1 +
  qapi/qom.json                              | 13 +++++++
  target/arm/kvm-rme.c                       | 42 ++++++++++++++++++++++
  target/arm/meson.build                     |  5 ++-
  4 files changed, 60 insertions(+), 1 deletion(-)

create mode 100644 target/arm/kvm-rme.c

docs/system/confidential-guest-support.rst +1 -0
index 562a7c3c2852..abb56923ad13 100644
@@ -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.
qapi/qom.json +0 -0
index 776b13027fd8..52997c29a32d 100644
@@ -1288,6 +1288,17 @@
    'data': { '*pretty': 'bool',

qapi/qom.json line 1298

Quoted 10 lines
qapi/qom.json +9 -0
              '*close-action': 'MonitorQMPCloseAction' } }
+##
+# @RmeGuestProperties:
+#
+# Properties for rme-guest objects.
+#
+# Since: 11.1
+##
+{ 'struct': 'RmeGuestProperties',
+  'base': 'ConfidentialGuestSupportProperties',

Just FYI, based on input from Markus I'm planning to drop this ConfidentialGuestSupportProperties base type from my series since it's probably only SNP/TDX that need to be able to switch it on/off, whereas this series appears to use/require it from the start.

I'm not exactly sure what it will look like for v3 yet, but if you were based on v2 you'd instead probably just do the following in the instance_init function for your rme-guest instance:

Quoted 7 lines
Quoted 4 lines
   cgs->allow_convert_in_place = true;
   cgs->convert_in_place = true;

rather than exposing it as a cmdline option.

Yep, it won't be an option for CCA Realms anyway since that will be the baseline.

Quoted 8 lines

IIUC, in-place conversion requires RAM backed by a shared/mappable guest_memfd. If in-place conversion is always enabled for CCA Realms, could QEMU also select the equivalent of

-object memory-backend-guest-memfd,id=ram0,size=...,share=on

automatically for the default RAM, so that specifying the RAM size with -m is sufficient?

Quoted 9 lines

Are you suggesting that on Arm, we completely get rid of

     -object memory-backend-guest-memfd,id=ram0,size=$(RAMSIZE),share=on

or simply to get rid of "size=$(RAMSIZE)" in the above because it is redundant with "-m $(RAMSIZE)"?

Gavin and Michael, what is your take on that suggestion?

I don't think that's what Kohei meant. He actually suggested the memory backend shall be pinned to memory-backend-guest-memfd and its in-place conversion shall be enabled when the memory backend isn't explicitly specified by users through "-object memory-backend-xxxx". For this inexplicit case, where the memory backend is determined by '-mem-path', the memory backend is either TYPE_MEMORY_BACKEND_FILE or TYPE_MEMORY_BACKEND_RAM. Please refer to create_default_memdev().

Quoted 7 lines
Quoted 2 lines

I think Kohei's suggestion makes sense. Besides, I think '-mem-path' should be disallowed for CCA Realms as TYPE_MEMORY_BACKEND_FILE isn't what we want.

Kohei, what were you suggesting then please ? I think TYPE_MEMORY_BACKEND_FILE would not pass the checks required by Michael's v3 [0] on CCA - I will test this and report back but the only backend allowed, rightly, is memfd,share=on, so it _is_ disallowed.

Sorry for the late response.

To clarify, I meant the latter: when rme-guest is selected and the user has not explicitly provided a memory backend, QEMU could automatically create the default RAM as a shared guest-memfd backend.

In other words, rather than only rejecting the default file/RAM backend, the Realm configuration could select the equivalent of

-object memory-backend-guest-memfd,id=<default-ram-id>,size=<RAMSIZE>,share=on

by default. Explicitly specified memory backends would still need to be validated against the Realm requirements.

Quoted 8 lines
Quoted 4 lines

My intention was that, if in-place conversion is the baseline for CCA Realms, -m <size> should be sufficient for the common Realm case, while preserving an explicit memory-backend option for users who need to control it.

Many thanks for the clarification, it is very helpful. I will look into implementing something along those lines for V6 and we can reconsider how to move forward at that time.

And then I read Michael's comment again, and again and again...

Quoted 11 lines

Thanks,
Mathieu

Quoted 7 lines

That said, I do not have a strong preference for deciding the default -m behavior at this stage. If it is premature to make that decision before the memory-backend and conversion interfaces have settled, I am happy to keep the explicit guest-memfd backend requirement for now and revisit the default behavior later.

For the time being I will hold back on moving forward with this request. Let's revisit at some point in the future.

Quoted 116 lines
Quoted 116 lines

Thanks,
Kohei

Quoted 113 lines

Lorenzo

[0] https://lore.kernel.org/kvm/[email protected]/

Quoted 108 lines

Thanks,
Gavin

Quoted 104 lines

Thanks,
Mathieu

Quoted 100 lines

Thanks,
Kohei

Quoted 96 lines

Thanks,
Lorenzo

Quoted 92 lines

Thanks,

Mike

Quoted 88 lines
qapi/qom.json +4 -0
+  'data': {} }
+
  ##
  # @ObjectType:
  #
@@ -1346,6 +1357,7 @@
      { 'name': 'pr-manager-helper',
        'if': 'CONFIG_LINUX' },
      'qtest',
+    'rme-guest',
      'rng-builtin',
      'rng-egd',
      { 'name': 'rng-random',
@@ -1427,6 +1439,7 @@
        'pr-manager-helper':          { 'type': 'PrManagerHelperProperties',
                                        'if': 'CONFIG_LINUX' },
        'qtest':                      'QtestProperties',
+      'rme-guest':                  'RmeGuestProperties',
        'rng-builtin':                'RngProperties',
        'rng-egd':                    'RngEgdProperties',
        'rng-random':                 { 'type': 'RngRandomProperties',
target/arm/kvm-rme.c new file +42 -0
new file mode 100644
index 000000000000..42e1d1e7b859
@@ -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)
+{
+}
target/arm/meson.build +4 -1
index 0369f96b4cc6..9d322a7aad28 100644
@@ -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'))
Signature · 1 line
2.43.0
#24 lore raw

RFC v4 4/24 target/arm/kvm-rme: Add mechanic to initialize realms

In reply to Mathieu Poirier

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.

---
 target/arm/kvm-rme.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
 target/arm/kvm.c     | 15 +++++++++++--
 2 files changed, 64 insertions(+), 2 deletions(-)
target/arm/kvm-rme.c +51 -0
index 42e1d1e7b859..a4a5e195c196 100644
@@ -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,12 +30,62 @@ 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 int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
+{
+    KVMState *s = KVM_STATE(current_accel());
+    static Error *rme_mig_blocker;
+
+    if (!rme_guest) {
+        return 0;
+    }
+
+    if (!kvm_vm_check_extension(s, KVM_CAP_ARM_RMI)) {
+        error_setg(errp, "VM 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;
+}
+
 static void rme_guest_class_init(ObjectClass *oc, const void *data)
 {
+    ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
+
+    klass->kvm_init = kvm_arm_rme_init;
 }
 
 static void rme_guest_init(Object *obj)
 {
+    ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj);
+
+    if (rme_guest) {
+        error_report("a single instance of RmeGuest is supported");
+        exit(1);
+    }
+    rme_guest = RME_GUEST(obj);
+    cgs->allow_convert_in_place = true;
 }
 
 static void rme_guest_finalize(Object *obj)
target/arm/kvm.c +13 -2
index ed99be7fd80c..401a6f669cbb 100644
@@ -33,6 +33,7 @@
 #include "hw/pci/pci.h"
 #include "exec/memattrs.h"
 #include "system/address-spaces.h"
+#include "system/confidential-guest-support.h"
 #include "gdbstub/enums.h"
 #include "hw/core/boards.h"
 #include "hw/core/irq.h"
@@ -596,7 +597,8 @@ int kvm_arch_get_default_type(MachineState *ms)
 
 int kvm_arch_init(MachineState *ms, KVMState *s)
 {
-    int ret;
+    Error *local_err = NULL;
+    int ret = 0;
     /* For ARM interrupt delivery is always asynchronous,
      * whether we are using an in-kernel VGIC or not.
      */
@@ -610,6 +612,15 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
 
     cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
 
+    /* Initialize confidential guest (Realm) if needed */
+    if (ms->cgs) {
+        ret = confidential_guest_kvm_init(ms->cgs, &local_err);
+            if (ret < 0) {
+                error_report_err(local_err);
+                return ret;
+        }
+    }
+
     /* Check whether user space can specify guest syndrome value */
     cap_has_inject_serror_esr =
         kvm_check_extension(s, KVM_CAP_ARM_INJECT_SERROR_ESR);
@@ -660,7 +671,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
     hw_breakpoints = g_array_sized_new(true, true,
                                        sizeof(HWBreakpoint), max_hw_bps);
 
-    return 0;
+    return ret;
 }
 
 unsigned long kvm_arch_vcpu_id(CPUState *cpu)
Signature · 1 line
2.43.0
#25 lore raw

Re: [RFC v4 04/24] target/arm/kvm-rme: Add mechanic to initialize realms

Quoted 110 lines · Mathieu Poirier

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.

---
  target/arm/kvm-rme.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
  target/arm/kvm.c     | 15 +++++++++++--
  2 files changed, 64 insertions(+), 2 deletions(-)
target/arm/kvm-rme.c +51 -0
index 42e1d1e7b859..a4a5e195c196 100644
@@ -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,12 +30,62 @@ 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 int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
+{
+    KVMState *s = KVM_STATE(current_accel());
+    static Error *rme_mig_blocker;
+
+    if (!rme_guest) {
+        return 0;
+    }
+
+    if (!kvm_vm_check_extension(s, KVM_CAP_ARM_RMI)) {
+        error_setg(errp, "VM 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;
+}
+
  static void rme_guest_class_init(ObjectClass *oc, const void *data)
  {
+    ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
+
+    klass->kvm_init = kvm_arm_rme_init;
  }
 
  static void rme_guest_init(Object *obj)
  {
+    ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj);
+
+    if (rme_guest) {
+        error_report("a single instance of RmeGuest is supported");
+        exit(1);
+    }
+    rme_guest = RME_GUEST(obj);
+    cgs->allow_convert_in_place = true;
  }
 
  static void rme_guest_finalize(Object *obj)
target/arm/kvm.c +3 -1
index ed99be7fd80c..401a6f669cbb 100644
@@ -33,6 +33,7 @@
  #include "hw/pci/pci.h"
  #include "exec/memattrs.h"
  #include "system/address-spaces.h"
+#include "system/confidential-guest-support.h"
  #include "gdbstub/enums.h"
  #include "hw/core/boards.h"
  #include "hw/core/irq.h"
@@ -596,7 +597,8 @@ int kvm_arch_get_default_type(MachineState *ms)
 
  int kvm_arch_init(MachineState *ms, KVMState *s)
  {
-    int ret;
+    Error *local_err = NULL;
+    int ret = 0;
      /* For ARM interrupt delivery is always asynchronous,
       * whether we are using an in-kernel VGIC or not.
       */

target/arm/kvm.c int kvm_arch_init(MachineState *ms, KVMState *s) · line 618

Quoted 7 lines
target/arm/kvm.c +4 -0
@@ -610,6 +612,15 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
 
      cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
 
+    /* Initialize confidential guest (Realm) if needed */
+    if (ms->cgs) {
+        ret = confidential_guest_kvm_init(ms->cgs, &local_err);
+            if (ret < 0) {

^^^^^

Code alignment issue here.

Quoted 9 lines
target/arm/kvm.c +5 -0
+                error_report_err(local_err);
+                return ret;
+        }
+    }
+
      /* Check whether user space can specify guest syndrome value */
      cap_has_inject_serror_esr =
          kvm_check_extension(s, KVM_CAP_ARM_INJECT_SERROR_ESR);

target/arm/kvm.c int kvm_arch_init(MachineState *ms, KVMState *s) · line 677

Quoted 8 lines
target/arm/kvm.c +1 -1
@@ -660,7 +671,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
      hw_breakpoints = g_array_sized_new(true, true,
                                         sizeof(HWBreakpoint), max_hw_bps);
 
-    return 0;
+    return ret;
  }
 
  unsigned long kvm_arch_vcpu_id(CPUState *cpu)

Thanks,
Gavin

#26 lore raw

RFC v4 5/24 target/arm/kvm: Split kvm_arch_get/put_registers

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 target/arm/kvm.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)
target/arm/kvm.c +28 -2
index 401a6f669cbb..33c3763aebfa 100644
@@ -2169,7 +2169,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;
@@ -2272,6 +2272,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)) {
@@ -2354,7 +2367,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;
@@ -2457,6 +2470,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;
Signature · 1 line
2.43.0
#27 lore raw

RFC v4 6/24 target/arm/kvm-rme: Initialize vCPU

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 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(-)
target/arm/cpu.h +3 -0
index e3f931dba26b..6bbc942f9a59 100644
@@ -1054,6 +1054,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;
 
target/arm/kvm-rme.c +9 -0
index a4a5e195c196..f7c2cc786e2c 100644
@@ -69,6 +69,15 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
     return 0;
 }
 
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+    if (!rme_guest) {
+        return;
+    }
+
+    cpu->kvm_rme = true;
+}
+
 static void rme_guest_class_init(ObjectClass *oc, const void *data)
 {
     ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
target/arm/kvm-stub.c +5 -0
index 88cbe8d85c41..5fde96f9b281 100644
@@ -119,3 +119,8 @@ char *kvm_print_register_name(uint64_t regidx)
 {
     g_assert_not_reached();
 }
+
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+    g_assert_not_reached();
+}
target/arm/kvm.c +52 -2

84 lines not on this page · raw message

target/arm/kvm_arm.h +10 -0
index e7c40fb003e4..d95381c13afa 100644
@@ -240,4 +240,14 @@ void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3);
  */
 char *kvm_print_register_name(uint64_t regidx);
 
+/**
+ * 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
Signature · 1 line
2.43.0
#28 lore raw

Re: [RFC v4 06/24] target/arm/kvm-rme: Initialize vCPU

Quoted 23 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  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(-)
target/arm/cpu.h +0 -0
index e3f931dba26b..6bbc942f9a59 100644

target/arm/cpu.h struct ArchCPU { · line 1059

Quoted 6 lines
target/arm/cpu.h +3 -0
@@ -1054,6 +1054,9 @@ struct ArchCPU {
      /* KVM steal time */
      OnOffAuto kvm_steal_time;
 
+    /* Realm Management Extension */
+    bool kvm_rme;
+

This field seems redundant to MachineState::cgs since all vCPUs support RME (Realm Management Extension) when MachineState::cgs points a valid instance. So I'm wandering why we need to maintain the redundant information here?

Quoted 10 lines
target/arm/cpu.h +0 -0
      /* Uniprocessor system with MP extensions */
      bool mp_is_up;
 
target/arm/kvm-rme.c +0 -0
index a4a5e195c196..f7c2cc786e2c 100644
@@ -69,6 +69,15 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
      return 0;
  }

target/arm/kvm-rme.c static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp) · line 80

Quoted 10 lines
target/arm/kvm-rme.c +9 -0
 
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+    if (!rme_guest) {
+        return;
+    }
+
+    cpu->kvm_rme = true;
+}
+

This could be simplified to:

      cpu->kvm_rme = !!rme_guest;
Quoted 117 lines
target/arm/kvm-rme.c +0 -0
  static void rme_guest_class_init(ObjectClass *oc, const void *data)
  {
      ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
target/arm/kvm-stub.c +5 -0
index 88cbe8d85c41..5fde96f9b281 100644
@@ -119,3 +119,8 @@ char *kvm_print_register_name(uint64_t regidx)
  {
      g_assert_not_reached();
  }
+
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+    g_assert_not_reached();
+}
target/arm/kvm.c +52 -2
index 33c3763aebfa..b4c6da1fb9f3 100644
@@ -2015,6 +2015,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) {
@@ -2169,6 +2171,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;
@@ -2280,7 +2305,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;
      }
@@ -2367,6 +2396,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;
@@ -2478,7 +2524,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;
      }
target/arm/kvm_arm.h +0 -0
index e7c40fb003e4..d95381c13afa 100644
@@ -240,4 +240,14 @@ void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3);
   */
  char *kvm_print_register_name(uint64_t regidx);
 

target/arm/kvm_arm.h void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3); · line 252

Quoted 10 lines
target/arm/kvm_arm.h +10 -0
+/**
+ * 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);
+

The comments here look outdated. This function simply set ARMCPU::kvm_rme to indicate RME should be supported for this given vCPU. So the comments would be something as below.

/*
*
* Mark that RME (Realm Management Extension) should be supported for the given
* vCPU if user has requested it. Realm vCPUs behave a little differently, for
* example most of their register states are confidential and invisible to us. */

target/arm/kvm_arm.h void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3); · line 253

Quoted 1 line
target/arm/kvm_arm.h +0 -0
  #endif

Thanks,
Gavin

#29 lore raw

Re: [RFC v4 06/24] target/arm/kvm-rme: Initialize vCPU

Quoted 19 lines · Gavin Shan
Quoted 18 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  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(-)
Quoted 15 lines

target/arm/cpu.h struct ArchCPU { · line 1058

Quoted 10 lines
target/arm/cpu.h +3 -0
index e3f931dba26b..6bbc942f9a59 100644
@@ -1054,6 +1054,9 @@ struct ArchCPU {
      /* KVM steal time */
      OnOffAuto kvm_steal_time;
+    /* Realm Management Extension */
+    bool kvm_rme;
+

This field seems redundant to MachineState::cgs since all vCPUs support RME (Realm Management Extension) when MachineState::cgs points a valid instance. So I'm wandering why we need to maintain the redundant information here?

It is redundant, at least at this time. I have removed it in favour of MACHINE(qdev_get_machine())->cgs where cpu->kvm_rme was used. With that it was possible to get rid of function kvm_arm_rme_vcpu_init().

Quoted 161 lines
Quoted 8 lines
target/arm/cpu.h +0 -0
      /* Uniprocessor system with MP extensions */
      bool mp_is_up;
target/arm/kvm-rme.c +0 -0
index a4a5e195c196..f7c2cc786e2c 100644
@@ -69,6 +69,15 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
      return 0;

target/arm/kvm-rme.c static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp) · line 79

Quoted 10 lines
target/arm/kvm-rme.c +9 -0
  }
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+    if (!rme_guest) {
+        return;
+    }
+
+    cpu->kvm_rme = true;
+}
+

This could be simplified to:

      cpu->kvm_rme = !!rme_guest;
Quoted 111 lines
target/arm/kvm-rme.c +0 -0
  static void rme_guest_class_init(ObjectClass *oc, const void *data)
  {
      ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
target/arm/kvm-stub.c +5 -0
index 88cbe8d85c41..5fde96f9b281 100644
@@ -119,3 +119,8 @@ char *kvm_print_register_name(uint64_t regidx)
  {
      g_assert_not_reached();
  }
+
+void kvm_arm_rme_vcpu_init(ARMCPU *cpu)
+{
+    g_assert_not_reached();
+}
target/arm/kvm.c +52 -2
index 33c3763aebfa..b4c6da1fb9f3 100644
@@ -2015,6 +2015,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) {
@@ -2169,6 +2171,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;
@@ -2280,7 +2305,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;
      }
@@ -2367,6 +2396,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;
@@ -2478,7 +2524,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;
      }
target/arm/kvm_arm.h +0 -0
index e7c40fb003e4..d95381c13afa 100644
@@ -240,4 +240,14 @@ void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3);
   */
  char *kvm_print_register_name(uint64_t regidx);

target/arm/kvm_arm.h void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3); · line 251

Quoted 10 lines
target/arm/kvm_arm.h +10 -0
+/**
+ * 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);
+

The comments here look outdated. This function simply set ARMCPU::kvm_rme to indicate RME should be supported for this given vCPU. So the comments would be something as below.

/*
*
* Mark that RME (Realm Management Extension) should be supported for the given
* vCPU if user has requested it. Realm vCPUs behave a little differently, for
* example most of their register states are confidential and invisible to us. */

target/arm/kvm_arm.h void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3); · line 252

Quoted 1 line
target/arm/kvm_arm.h +0 -0
  #endif

Thanks,
Gavin

#30 lore raw

RFC v4 7/24 target/arm/kvm: Create scratch Realm VM when requested

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 target/arm/kvm.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
target/arm/kvm.c +5 -1
index b4c6da1fb9f3..d86e66e056b9 100644
@@ -108,7 +108,9 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
                                       struct kvm_vcpu_init *init)
 {
     int ret = 0, kvmfd = -1, vmfd = -1, cpufd = -1;
+    MachineState *ms = MACHINE(qdev_get_machine());
     int max_vm_pa_size;
+    int vm_type;
 
     kvmfd = qemu_open_old("/dev/kvm", O_RDWR);
     if (kvmfd < 0) {
@@ -118,8 +120,10 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
     if (max_vm_pa_size < 0) {
         max_vm_pa_size = 0;
     }
+
+    vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);
     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;
Signature · 1 line
2.43.0
#31 lore raw

Re: [RFC v4 07/24] target/arm/kvm: Create scratch Realm VM when requested

Quoted 3 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

When a Realm is requested, create a scratch Realm VM by setting bit 8

nit:
KVM_VM_TYPE_ARM_REALM is no longer defined as 1 << 8, so shouldn't this be updated as well?

Quoted 5 lines

of the argument given to the KVM_CREATE_VM ioctl(). Bit 7-0 are reserved for the guest PA size.

#32 lore raw

Re: [RFC v4 07/24] target/arm/kvm: Create scratch Realm VM when requested

Quoted 8 lines · Kohei Enju
Quoted 3 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

When a Realm is requested, create a scratch Realm VM by setting bit 8

nit:
KVM_VM_TYPE_ARM_REALM is no longer defined as 1 << 8, so shouldn't this be updated as well?

This changes often - I'll look into it.

Quoted 6 lines
Quoted 6 lines

of the argument given to the KVM_CREATE_VM ioctl(). Bit 7-0 are reserved for the guest PA size.

#33 lore raw

Re: [RFC v4 07/24] target/arm/kvm: Create scratch Realm VM when requested

In reply to Mathieu Poirier
Quoted 17 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  target/arm/kvm.c | 6 +++++-
  1 file changed, 5 insertions(+), 1 deletion(-)
target/arm/kvm.c +0 -0
index b4c6da1fb9f3..d86e66e056b9 100644

target/arm/kvm.c bool kvm_arm_create_scratch_host_vcpu(int *fdarray, · line 114

Quoted 7 lines
target/arm/kvm.c +2 -0
@@ -108,7 +108,9 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
                                        struct kvm_vcpu_init *init)
  {
      int ret = 0, kvmfd = -1, vmfd = -1, cpufd = -1;
+    MachineState *ms = MACHINE(qdev_get_machine());
      int max_vm_pa_size;
+    int vm_type;
 

@vm_type can be initialized when it's declared.

       MachineState *ms = MACHINE(qdev_get_machine());
       int vm_type = ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL;
       int max_vm_pa_size;

target/arm/kvm.c bool kvm_arm_create_scratch_host_vcpu(int *fdarray, · line 124

Quoted 8 lines
target/arm/kvm.c +2 -0
      kvmfd = qemu_open_old("/dev/kvm", O_RDWR);
      if (kvmfd < 0) {
@@ -118,8 +120,10 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
      if (max_vm_pa_size < 0) {
          max_vm_pa_size = 0;
      }
+
+    vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);

() aren't needed.

target/arm/kvm.c bool kvm_arm_create_scratch_host_vcpu(int *fdarray, · line 129

Quoted 6 lines
target/arm/kvm.c +1 -1
      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;

Thanks,
Gavin

#34 lore raw

Re: [RFC v4 07/24] target/arm/kvm: Create scratch Realm VM when requested

Quoted 32 lines · Gavin Shan
Quoted 17 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  target/arm/kvm.c | 6 +++++-
  1 file changed, 5 insertions(+), 1 deletion(-)
target/arm/kvm.c +0 -0
index b4c6da1fb9f3..d86e66e056b9 100644

target/arm/kvm.c bool kvm_arm_create_scratch_host_vcpu(int *fdarray, · line 114

Quoted 7 lines
target/arm/kvm.c +2 -0
@@ -108,7 +108,9 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
                                        struct kvm_vcpu_init *init)
  {
      int ret = 0, kvmfd = -1, vmfd = -1, cpufd = -1;
+    MachineState *ms = MACHINE(qdev_get_machine());
      int max_vm_pa_size;
+    int vm_type;
 

@vm_type can be initialized when it's declared.

       MachineState *ms = MACHINE(qdev_get_machine());
       int vm_type = ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL;
       int max_vm_pa_size;
Quoted 11 lines

target/arm/kvm.c bool kvm_arm_create_scratch_host_vcpu(int *fdarray, · line 124

Quoted 8 lines
target/arm/kvm.c +2 -0
      kvmfd = qemu_open_old("/dev/kvm", O_RDWR);
      if (kvmfd < 0) {
@@ -118,8 +120,10 @@ bool kvm_arm_create_scratch_host_vcpu(int *fdarray,
      if (max_vm_pa_size < 0) {
          max_vm_pa_size = 0;
      }
+
+    vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);

() aren't needed.

All fixed.

Quoted 10 lines

target/arm/kvm.c bool kvm_arm_create_scratch_host_vcpu(int *fdarray, · line 129

Quoted 6 lines
target/arm/kvm.c +1 -1
      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;

Thanks,
Gavin

#35 lore raw

RFC v4 8/24 target/arm/kvm: Use kvm_vm_check_extension() where necessary

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 target/arm/kvm.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
target/arm/kvm.c +5 -5
index d86e66e056b9..ca25b707a0a8 100644
@@ -331,7 +331,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;
@@ -636,7 +636,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
         return -EINVAL;
     }
 
-    if (kvm_check_extension(s, KVM_CAP_ARM_NISV_TO_USER)) {
+    if (kvm_vm_check_extension(s, KVM_CAP_ARM_NISV_TO_USER)) {
         if (kvm_vm_enable_cap(s, KVM_CAP_ARM_NISV_TO_USER, 0)) {
             error_report("Failed to enable KVM_CAP_ARM_NISV_TO_USER cap");
         } else {
@@ -667,11 +667,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);
 
@@ -1913,7 +1913,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)) {
Signature · 1 line
2.43.0
#36 lore raw

RFC v4 9/24 hw/core/loader: Add a ROM loader notifier

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 hw/core/loader.c         | 15 +++++++++++++++
 include/hw/core/loader.h | 17 +++++++++++++++++
 2 files changed, 32 insertions(+)
hw/core/loader.c +15 -0
index 5cbfba0a86d2..388f10e828db 100644
@@ -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,
+            .data = rom->data,
+        };
+        notifier_list_notify(&rom_loader_notifier, &notify);
     }
 }
 
include/hw/core/loader.h +17 -0
index d9431e8a8d12..fdfddfa7443e 100644
@@ -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;
+    /* Blog data */
+    uint8_t *data;
+} 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)
Signature · 1 line
2.43.0
#37 lore raw

Re: [RFC v4 09/24] hw/core/loader: Add a ROM loader notifier

Quoted 51 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 hw/core/loader.c         | 15 +++++++++++++++
 include/hw/core/loader.h | 17 +++++++++++++++++
 2 files changed, 32 insertions(+)
hw/core/loader.c +8 -0
index 5cbfba0a86d2..388f10e828db 100644
@@ -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) {

hw/core/loader.c static void rom_reset(void *unused) · line 1307

Quoted 10 lines
hw/core/loader.c +7 -0
@@ -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,
+            .data = rom->data,
+        };
+        notifier_list_notify(&rom_loader_notifier, &notify);

I think it will easier to review this if it's squashed into the next patch where the fields are utilized.

-Mike

#38 lore raw

Re: [RFC v4 09/24] hw/core/loader: Add a ROM loader notifier

In reply to Mathieu Poirier
Quoted 69 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  hw/core/loader.c         | 15 +++++++++++++++
  include/hw/core/loader.h | 17 +++++++++++++++++
  2 files changed, 32 insertions(+)
hw/core/loader.c +15 -0
index 5cbfba0a86d2..388f10e828db 100644
@@ -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,
+            .data = rom->data,
+        };
+        notifier_list_notify(&rom_loader_notifier, &notify);
      }
  }
 
include/hw/core/loader.h +0 -0
index d9431e8a8d12..fdfddfa7443e 100644

include/hw/core/loader.h void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); · line 350

Quoted 9 lines
include/hw/core/loader.h +6 -0
@@ -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;
+    /* Blog data */
          ^^^^

I guess it would be 'Blob'.

include/hw/core/loader.h void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); · line 353

Quoted 3 lines
include/hw/core/loader.h +3 -0
+    uint8_t *data;
+} RomLoaderNotifyData;
+

There are enough space at the tail of field declaration lines, where the comments can be put:

typedef struct RomLoaderNotifyData {

    hwaddr  addr;   /* Address of the blob in guest memory */
    size_t  len;    /* Length of the blob */
    uint8_t *data;  /* Blob data */

} RomLoaderNotifyData;

include/hw/core/loader.h void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); · line 364

Quoted 11 lines
include/hw/core/loader.h +8 -0
+/**
+ * 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)

Thanks,
Gavin

#39 lore raw

Re: [RFC v4 09/24] hw/core/loader: Add a ROM loader notifier

Quoted 82 lines · Gavin Shan
Quoted 63 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  hw/core/loader.c         | 15 +++++++++++++++
  include/hw/core/loader.h | 17 +++++++++++++++++
  2 files changed, 32 insertions(+)
hw/core/loader.c +15 -0
index 5cbfba0a86d2..388f10e828db 100644
@@ -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,
+            .data = rom->data,
+        };
+        notifier_list_notify(&rom_loader_notifier, &notify);
      }
  }
include/hw/core/loader.h +0 -0
index d9431e8a8d12..fdfddfa7443e 100644

include/hw/core/loader.h void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); · line 349

Quoted 8 lines
include/hw/core/loader.h +6 -0
@@ -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;
+    /* Blog data */
          ^^^^

I guess it would be 'Blob'.

include/hw/core/loader.h void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); · line 353

Quoted 3 lines
include/hw/core/loader.h +3 -0
+    uint8_t *data;
+} RomLoaderNotifyData;
+

There are enough space at the tail of field declaration lines, where the comments can be put:

Quoted 6 lines

typedef struct RomLoaderNotifyData {

    hwaddr  addr;   /* Address of the blob in guest memory */
    size_t  len;    /* Length of the blob */
    uint8_t *data;  /* Blob data */

} RomLoaderNotifyData;

Fixed

Quoted 16 lines

include/hw/core/loader.h void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); · line 364

Quoted 12 lines
include/hw/core/loader.h +8 -0
+/**
+ * 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)

Thanks,
Gavin

#40 lore raw

RFC v4 10/24 target/arm/kvm-rme: Keep track of images loaded in Realm memory

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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).

---
 target/arm/kvm-rme.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
target/arm/kvm-rme.c +52 -0
index f7c2cc786e2c..9985f5daba89 100644
@@ -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"
@@ -22,8 +23,18 @@
 #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;
+    uint8_t *data;
+} RmeRamRegion;
+
 struct RmeGuest {
     ConfidentialGuestSupport parent_obj;
+    Notifier rom_load_notifier;
+    GSList *ram_regions;
 };
 
 OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -41,6 +52,44 @@ static void rme_vm_state_change(void *opaque, bool running, RunState state)
     kvm_mark_guest_state_protected();
 }
 
+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->data = rom->data;
+
+    /*
+     * 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);
+}
+
 static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
 {
     KVMState *s = KVM_STATE(current_accel());
@@ -58,6 +107,9 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
     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.
Signature · 1 line
2.43.0
#41 lore raw

Re: [RFC v4 10/24] target/arm/kvm-rme: Keep track of images loaded in Realm memory

Quoted 52 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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).

---
 target/arm/kvm-rme.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
target/arm/kvm-rme.c +16 -0
index f7c2cc786e2c..9985f5daba89 100644
@@ -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"
@@ -22,8 +23,18 @@
 #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;
+    uint8_t *data;
+} RmeRamRegion;
+
 struct RmeGuest {
     ConfidentialGuestSupport parent_obj;
+    Notifier rom_load_notifier;
+    GSList *ram_regions;
 };
 
 OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -41,6 +52,44 @@ static void rme_vm_state_change(void *opaque, bool running, RunState state)
     kvm_mark_guest_state_protected();
 }
 
+static gint rme_compare_ram_regions(gconstpointer a, gconstpointer b)
+{
+    const RmeRamRegion *ra = a;
+    const RmeRamRegion *rb = b;
+

target/arm/kvm-rme.c static void rme_vm_state_change(void *opaque, bool running, RunState state) · line 69

Quoted 10 lines
target/arm/kvm-rme.c +10 -0
+    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) {

Are these entries also added via the rom_reset() path in the previous patch? If so, wouldn't it be better to filter them out there?

Also, if that's where these entries are added, then it looks like we might call address_space_flush_icache_range() with this -1 addr before adding it to the notifier list. Is that expected?

-Mike

Quoted 40 lines
target/arm/kvm-rme.c +26 -0
+        /*
+         * 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->data = rom->data;
+
+    /*
+     * 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);
+}
+
 static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
 {
     KVMState *s = KVM_STATE(current_accel());
@@ -58,6 +107,9 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
     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.
Signature · 1 line
2.43.0
#42 lore raw

Re: [RFC v4 10/24] target/arm/kvm-rme: Keep track of images loaded in Realm memory

Hi Michael - thanks for taking the time to review this work.

Quoted 58 lines · Michael Roth
Quoted 57 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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).

---
 target/arm/kvm-rme.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
target/arm/kvm-rme.c +21 -0
index f7c2cc786e2c..9985f5daba89 100644
@@ -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"
@@ -22,8 +23,18 @@
 #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;
+    uint8_t *data;
+} RmeRamRegion;
+
 struct RmeGuest {
     ConfidentialGuestSupport parent_obj;
+    Notifier rom_load_notifier;
+    GSList *ram_regions;
 };
 
 OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -41,6 +52,44 @@ static void rme_vm_state_change(void *opaque, bool running, RunState state)
     kvm_mark_guest_state_protected();
 }
 
+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)
Quoted 8 lines

target/arm/kvm-rme.c static void rme_vm_state_change(void *opaque, bool running, RunState state) · line 69

Quoted 5 lines
target/arm/kvm-rme.c +5 -0
+{
+    RmeRamRegion *region;
+    RomLoaderNotifyData *rom = data;
+
+    if (rom->addr == -1) {

Are these entries also added via the rom_reset() path in the previous patch? If so, wouldn't it be better to filter them out there?

Duly noted. I will also wrap the previous patch into this one.

Quoted 4 lines

Also, if that's where these entries are added, then it looks like we might call address_space_flush_icache_range() with this -1 addr before adding it to the notifier list. Is that expected?

Function address_space_flush_icache_range() is indeed called with @rom->addr set to -1 (0xffffffffffffffff). Since this address is not accessible, the inner loop of the function skips over the entire Rom area without ever calling flush_idcache_range().

Regards,
Mathieu

Quoted 43 lines

-Mike

Quoted 40 lines
target/arm/kvm-rme.c +26 -0
+        /*
+         * 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->data = rom->data;
+
+    /*
+     * 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);
+}
+
 static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
 {
     KVMState *s = KVM_STATE(current_accel());
@@ -58,6 +107,9 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
     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.
Signature · 1 line
2.43.0
#43 lore raw

Re: [RFC v4 10/24] target/arm/kvm-rme: Keep track of images loaded in Realm memory

In reply to Mathieu Poirier
Quoted 26 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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).

---
  target/arm/kvm-rme.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 52 insertions(+)
target/arm/kvm-rme.c +1 -0
index f7c2cc786e2c..9985f5daba89 100644
@@ -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"
@@ -22,8 +23,18 @@
  #define TYPE_RME_GUEST "rme-guest"

target/arm/kvm-rme.c line 33

Quoted 10 lines
target/arm/kvm-rme.c +8 -0
  OBJECT_DECLARE_SIMPLE_TYPE(RmeGuest, RME_GUEST)
 
+#define RME_PAGE_SIZE qemu_real_host_page_size()
+
+typedef struct {
+    hwaddr base;
+    hwaddr size;
+    uint8_t *data;
+} RmeRamRegion;
+

This is the equivalent to 'RomLoaderNotifyData' defined in PATCH[09/24]. We may use 'RomLoaderNotifyData' if it can be renamed to reflect its purpose, no need to define an identical structure.

Quoted 53 lines
target/arm/kvm-rme.c +40 -0
  struct RmeGuest {
      ConfidentialGuestSupport parent_obj;
+    Notifier rom_load_notifier;
+    GSList *ram_regions;
  };
 
  OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -41,6 +52,44 @@ static void rme_vm_state_change(void *opaque, bool running, RunState state)
      kvm_mark_guest_state_protected();
  }
 
+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->data = rom->data;
+
+    /*
+     * 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);
+}
+
  static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
  {
      KVMState *s = KVM_STATE(current_accel());

target/arm/kvm-rme.c static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp) · line 115

Quoted 9 lines
target/arm/kvm-rme.c +3 -0
@@ -58,6 +107,9 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
      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.

Thanks,
Gavin

#44 lore raw

Re: [RFC v4 10/24] target/arm/kvm-rme: Keep track of images loaded in Realm memory

Quoted 31 lines · Gavin Shan
Quoted 30 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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).

---
  target/arm/kvm-rme.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 52 insertions(+)
target/arm/kvm-rme.c +5 -0
index f7c2cc786e2c..9985f5daba89 100644
@@ -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"
@@ -22,8 +23,18 @@
  #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;
Quoted 9 lines

target/arm/kvm-rme.c line 33

Quoted 4 lines
target/arm/kvm-rme.c +4 -0
+    hwaddr size;
+    uint8_t *data;
+} RmeRamRegion;
+

This is the equivalent to 'RomLoaderNotifyData' defined in PATCH[09/24]. We may use 'RomLoaderNotifyData' if it can be renamed to reflect its purpose, no need to define an identical structure.

I ended up using 'RomLoaderNotifyData' in kvm-rme.c instead of 'RmeRamRegion'.

Quoted 63 lines
Quoted 51 lines
target/arm/kvm-rme.c +40 -0
  struct RmeGuest {
      ConfidentialGuestSupport parent_obj;
+    Notifier rom_load_notifier;
+    GSList *ram_regions;
  };
  OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,
@@ -41,6 +52,44 @@ static void rme_vm_state_change(void *opaque, bool running, RunState state)
      kvm_mark_guest_state_protected();
  }
+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->data = rom->data;
+
+    /*
+     * 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);
+}
+
  static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
  {
      KVMState *s = KVM_STATE(current_accel());

target/arm/kvm-rme.c static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp) · line 114

Quoted 8 lines
target/arm/kvm-rme.c +3 -0
@@ -58,6 +107,9 @@ static int kvm_arm_rme_init(ConfidentialGuestSupport *cgs, Error **errp)
      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.

Thanks,
Gavin

#45 lore raw

RFC v4 11/24 target/arm/kvm-rme: Populate Realm with runtime images

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
 target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +76 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
 #include "migration/blocker.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
+#include "qemu/memalign.h"
 #include "qom/object_interfaces.h"
 #include "system/confidential-guest-support.h"
 #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
+    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();
 }
 
Signature · 1 line
2.43.0
#46 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 29 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
  target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +2 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
  #include "migration/blocker.h"
  #include "qapi/error.h"
  #include "qemu/error-report.h"
+#include "qemu/memalign.h"
  #include "qom/object_interfaces.h"
  #include "system/confidential-guest-support.h"
  #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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,

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 10 lines
target/arm/kvm-rme.c +10 -0
+                              Error **errp)
+{
+    int ret;
+    void *buffer;
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

Quoted 34 lines
target/arm/kvm-rme.c +34 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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")",

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 101

Quoted 10 lines
target/arm/kvm-rme.c +10 -0
+                start, end);
+            break;
+        }
+    }
+
+    qemu_vfree(buffer);
+
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE). I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

Quoted 19 lines
target/arm/kvm-rme.c +14 -0
+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;
      }

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 130

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
 
+    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();
  }
 

Thanks,
Gavin

#47 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
  target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
  #include "migration/blocker.h"
  #include "qapi/error.h"
  #include "qemu/error-report.h"
+#include "qemu/memalign.h"
  #include "qom/object_interfaces.h"
  #include "system/confidential-guest-support.h"
  #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

I think that IPA base and source_uaddr should be host page size aligned.

Quoted 37 lines
Quoted 37 lines
target/arm/kvm-rme.c +37 -0
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 101

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+
+    qemu_vfree(buffer);
+
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example,

I don't think they should be allowed either.

Quoted 8 lines

the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE). I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?

Yes, given how current code is written this is wrong.

Quoted 2 lines

- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

I can't follow you here. We are filling a temporary buffer so that KVM can populate from it ?

Anyway - we should refactor the code to discard regions unaligned to host page size.

Thanks,
Lorenzo

Quoted 31 lines
Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
          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();
  }

Thanks,
Gavin

#48 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

In reply to Gavin Shan
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
  target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
  #include "migration/blocker.h"
  #include "qapi/error.h"
  #include "qemu/error-report.h"
+#include "qemu/memalign.h"
  #include "qom/object_interfaces.h"
  #include "system/confidential-guest-support.h"
  #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

I refactored this function in V5 [1], along with the above declarations.

[1]. https://lore.kernel.org/kvm/[email protected]/

Quoted 43 lines
Quoted 43 lines
target/arm/kvm-rme.c +42 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
+    return ret;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 99

Quoted 2 lines
target/arm/kvm-rme.c +2 -0
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE). I guess we probably just reject unaligned regions if they're not expected.

To make sure I understand your point, please expand on what would be an unaligned regions. In your example, [0 128KB] is aligned on 64KB boundries.

Quoted 4 lines

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?

I'm confused. ROUND_UP(64KB+4KB, 64KB) would be 128KB. What am I missing?

Quoted 2 lines

- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

I agree with you. That part was refactored in V5 [1]:

        memcpy(buffer + (base - start), region->data, region->size); 
Quoted 32 lines
Quoted 18 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
          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();
  }

Thanks,
Gavin

#49 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 102 lines · Mathieu Poirier
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
   target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
   1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
   #include "migration/blocker.h"
   #include "qapi/error.h"
   #include "qemu/error-report.h"
+#include "qemu/memalign.h"
   #include "qom/object_interfaces.h"
   #include "system/confidential-guest-support.h"
   #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

I refactored this function in V5 [1], along with the above declarations.

[1]. https://lore.kernel.org/kvm/[email protected]/

Quoted 43 lines
Quoted 43 lines
target/arm/kvm-rme.c +42 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
+    return ret;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 99

Quoted 2 lines
target/arm/kvm-rme.c +2 -0
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE). I guess we probably just reject unaligned regions if they're not expected.

To make sure I understand your point, please expand on what would be an unaligned regions. In your example, [0 128KB] is aligned on 64KB boundries.

Quoted 1 line
Quoted 6 lines
Quoted 3 lines

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?

I'm confused. ROUND_UP(64KB+4KB, 64KB) would be 128KB. What am I missing?

ROUND_UP(64KB+4KB, 64KB) corresponds to ROUND_UP(size+base, 64KB) for local variable @end instead of @aligned_size. @aligned_size is calculated as ROUND_UP(64KB, 64KB) and the output is still 64KB.

Quoted 36 lines
Quoted 2 lines

- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

I agree with you. That part was refactored in V5 [1]:

         memcpy(buffer + (base - start), region->data, region->size);
Quoted 29 lines
Quoted 28 lines
target/arm/kvm-rme.c +20 -0
+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();
   }

Thanks,
Gavin

#50 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 105 lines · Gavin Shan
Quoted 104 lines · Mathieu Poirier
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
   target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
   1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
   #include "migration/blocker.h"
   #include "qapi/error.h"
   #include "qemu/error-report.h"
+#include "qemu/memalign.h"
   #include "qom/object_interfaces.h"
   #include "system/confidential-guest-support.h"
   #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

I refactored this function in V5 [1], along with the above declarations.

[1]. https://lore.kernel.org/kvm/[email protected]/

Quoted 43 lines
Quoted 43 lines
target/arm/kvm-rme.c +42 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
+    return ret;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 99

Quoted 2 lines
target/arm/kvm-rme.c +2 -0
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE). I guess we probably just reject unaligned regions if they're not expected.

To make sure I understand your point, please expand on what would be an unaligned regions. In your example, [0 128KB] is aligned on 64KB boundries.

Quoted 3 lines

Even if the unaligned regions are allowed, they seem not well handled.

Quoted 15 lines
Quoted 4 lines
Quoted 1 line

- @aligned_size is still 64KB instead of the 128KB ?

I'm confused. ROUND_UP(64KB+4KB, 64KB) would be 128KB. What am I missing?

ROUND_UP(64KB+4KB, 64KB) corresponds to ROUND_UP(size+base, 64KB) for local variable @end instead of @aligned_size. @aligned_size is calculated as ROUND_UP(64KB, 64KB) and the output is still 64KB.

Quoted 6 lines
Quoted 2 lines

- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

I agree with you. That part was refactored in V5 [1]:

         memcpy(buffer + (base - start), region->data, region->size);

I misunderstood Gavin's comment on this, so the issue isn't the memcpy per-se (that was the impression I had) but rather in relation to using the buffer in

kvm_vm_ioctl(kvm_state, KVM_ARM_RMI_POPULATE, &populate_args, 0);

with populate_args.base = start (that was aligned down).

Thanks,
Lorenzo

Quoted 33 lines
Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
target/arm/kvm-rme.c +15 -0
+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);
Quoted 8 lines
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 7 lines
target/arm/kvm-rme.c +5 -0
+    g_slist_free_full(g_steal_pointer(&rme_guest->ram_regions), g_free);
+    if (errp) {
+        return;
+    }
+
       kvm_mark_guest_state_protected();
   }

Thanks,
Gavin

#51 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

In reply to Gavin Shan
Quoted 105 lines · Gavin Shan
Quoted 104 lines · Mathieu Poirier
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
   target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
   1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
   #include "migration/blocker.h"
   #include "qapi/error.h"
   #include "qemu/error-report.h"
+#include "qemu/memalign.h"
   #include "qom/object_interfaces.h"
   #include "system/confidential-guest-support.h"
   #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

I refactored this function in V5 [1], along with the above declarations.

[1]. https://lore.kernel.org/kvm/[email protected]/

Quoted 43 lines
Quoted 43 lines
target/arm/kvm-rme.c +42 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
+    return ret;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 99

Quoted 2 lines
target/arm/kvm-rme.c +2 -0
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE). I guess we probably just reject unaligned regions if they're not expected.

To make sure I understand your point, please expand on what would be an unaligned regions. In your example, [0 128KB] is aligned on 64KB boundries.

Quoted 3 lines

Even if the unaligned regions are allowed, they seem not well handled.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

- @aligned_size is still 64KB instead of the 128KB ?

I'm confused. ROUND_UP(64KB+4KB, 64KB) would be 128KB. What am I missing?

ROUND_UP(64KB+4KB, 64KB) corresponds to ROUND_UP(size+base, 64KB) for local variable @end instead of @aligned_size. @aligned_size is calculated as ROUND_UP(64KB, 64KB) and the output is still 64KB.

Thanks for the explanation - that part was also addressed in V5. I will spin-off a proper V6 where we can continue the discussion on updated code.

Quoted 41 lines
Quoted 37 lines
Quoted 3 lines

- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

I agree with you. That part was refactored in V5 [1]:

         memcpy(buffer + (base - start), region->data, region->size);
Quoted 29 lines
Quoted 28 lines
target/arm/kvm-rme.c +20 -0
+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();
   }

Thanks,
Gavin

#52 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

In reply to Gavin Shan
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
  target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
  #include "migration/blocker.h"
  #include "qapi/error.h"
  #include "qemu/error-report.h"
+#include "qemu/memalign.h"
  #include "qom/object_interfaces.h"
  #include "system/confidential-guest-support.h"
  #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

It will always have to be qemu_real_host_page_size(), KVM configures it that way.

Quoted 41 lines
Quoted 41 lines
target/arm/kvm-rme.c +41 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 69

Quoted 3 lines
target/arm/kvm-rme.c +3 -0
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE).

Getting back to this because it is important. Can you define "safe" here ?

I think it is VMM's role to load with the required alignment and make sure it does not double populate (that is anyway prevented by KVM), what was your concern here to make sure we are in agreement ?

Thanks,
Lorenzo

Quoted 39 lines

I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
          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();
  }

Thanks,
Gavin

#53 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 95 lines · Lorenzo Pieralisi
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
   target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
   1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
   #include "migration/blocker.h"
   #include "qapi/error.h"
   #include "qemu/error-report.h"
+#include "qemu/memalign.h"
   #include "qom/object_interfaces.h"
   #include "system/confidential-guest-support.h"
   #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

It will always have to be qemu_real_host_page_size(), KVM configures it that way.

Quoted 41 lines
Quoted 41 lines
target/arm/kvm-rme.c +41 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 69

Quoted 3 lines
target/arm/kvm-rme.c +3 -0
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE).

Quoted 6 lines

Getting back to this because it is important. Can you define "safe" here ?

I think it is VMM's role to load with the required alignment and make sure it does not double populate (that is anyway prevented by KVM), what was your concern here to make sure we are in agreement ?

For example, there are two regions are populated: [0 4KB] and [4KB 64KB] on a 64KB host. With the alignment considered, we're populating [0 64KB] for region [0 4KB], and [0 64KB] for [4KB 64KB]. [0 64KB] are populated for twice. Is this allowed by TF-RMM? If it's allowed by TF-RMM, the data populated in the first time is overwritten by the second population.

Thanks,
Gavin

Quoted 43 lines

Thanks,
Lorenzo

Quoted 39 lines

I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
           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();
   }

Thanks,
Gavin

#54 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 103 lines · Gavin Shan
Quoted 95 lines · Lorenzo Pieralisi
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
   target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
   1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
   #include "migration/blocker.h"
   #include "qapi/error.h"
   #include "qemu/error-report.h"
+#include "qemu/memalign.h"
   #include "qom/object_interfaces.h"
   #include "system/confidential-guest-support.h"
   #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

It will always have to be qemu_real_host_page_size(), KVM configures it that way.

Quoted 41 lines
Quoted 41 lines
target/arm/kvm-rme.c +41 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 69

Quoted 3 lines
target/arm/kvm-rme.c +3 -0
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE).

Quoted 6 lines

Getting back to this because it is important. Can you define "safe" here ?

I think it is VMM's role to load with the required alignment and make sure it does not double populate (that is anyway prevented by KVM), what was your concern here to make sure we are in agreement ?

Quoted 5 lines

For example, there are two regions are populated: [0 4KB] and [4KB 64KB] on a 64KB host. With the alignment considered, we're populating [0 64KB] for region [0 4KB], and [0 64KB] for [4KB 64KB]. [0 64KB] are populated for twice. Is this allowed by TF-RMM? If it's allowed by TF-RMM, the data populated in the first time is overwritten by the second population.

It is not allowed by the RMM and it is a VMM bug that we have to prevent.

Thanks,
Lorenzo

Quoted 47 lines

Thanks,
Gavin

Quoted 43 lines

Thanks,
Lorenzo

Quoted 39 lines

I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.

Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
           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();
   }

Thanks,
Gavin

#55 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 104 lines · Lorenzo Pieralisi
Quoted 103 lines · Gavin Shan
Quoted 101 lines · Lorenzo Pieralisi
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
    target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
    1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
    #include "migration/blocker.h"
    #include "qapi/error.h"
    #include "qemu/error-report.h"
+#include "qemu/memalign.h"
    #include "qom/object_interfaces.h"
    #include "system/confidential-guest-support.h"
    #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

It will always have to be qemu_real_host_page_size(), KVM configures it that way.

Quoted 41 lines
Quoted 41 lines
target/arm/kvm-rme.c +41 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 69

Quoted 3 lines
target/arm/kvm-rme.c +3 -0
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE).

Getting back to this because it is important. Can you define "safe" here ?

I think it is VMM's role to load with the required alignment and make sure it does not double populate (that is anyway prevented by KVM), what was your concern here to make sure we are in agreement ?

Quoted 8 lines
Quoted 5 lines

For example, there are two regions are populated: [0 4KB] and [4KB 64KB] on a 64KB host. With the alignment considered, we're populating [0 64KB] for region [0 4KB], and [0 64KB] for [4KB 64KB]. [0 64KB] are populated for twice. Is this allowed by TF-RMM? If it's allowed by TF-RMM, the data populated in the first time is overwritten by the second population.

It is not allowed by the RMM and it is a VMM bug that we have to prevent.

Ok, thanks for your confirm. It's why I asked if we ever see an unaligned Rom region. If all Rom regions are aligned to host page size, then we needn't to handle the unaligned Rom regions in rme_populate_range(). Otherwise, we need come up with a solution to address the issue.

The solution would be to generate a temporary region list based on the existing Rom region list. For each region in the temporary list, its start/size are well aligned to host page size, and the temporary buffer is allocated, and the data is properly copied over from the original Rom region. The population is done based on the temporary list. For example, we need to have [0 64kb], [128kb 192kb] regions in the temporary list if we have Rom region [0 1kb], [4kb 5kb] [16kb 17kb], [132kb 133kb], [136kb 140kb]. The host page size is assumed to be 64kb in the example.

[0 64kb] covers [0 1kb], [4kb 5kb] [16kb 17kb] [128kb 192kb] covers [132kb 133kb], [136kb 140kb]

Thanks,
Gavin

Quoted 51 lines

Thanks,
Lorenzo

Quoted 47 lines

Thanks,
Gavin

Quoted 43 lines

Thanks,
Lorenzo

Quoted 39 lines

I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to

    [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.
Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
            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();
    }

Thanks,
Gavin

#56 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 127 lines · Gavin Shan
Quoted 104 lines · Lorenzo Pieralisi
Quoted 103 lines · Gavin Shan
Quoted 101 lines · Lorenzo Pieralisi
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
    target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
    1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
    #include "migration/blocker.h"
    #include "qapi/error.h"
    #include "qemu/error-report.h"
+#include "qemu/memalign.h"
    #include "qom/object_interfaces.h"
    #include "system/confidential-guest-support.h"
    #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 57

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

It will always have to be qemu_real_host_page_size(), KVM configures it that way.

Quoted 41 lines
Quoted 41 lines
target/arm/kvm-rme.c +41 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 69

Quoted 3 lines
target/arm/kvm-rme.c +3 -0
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE).

Getting back to this because it is important. Can you define "safe" here ?

I think it is VMM's role to load with the required alignment and make sure it does not double populate (that is anyway prevented by KVM), what was your concern here to make sure we are in agreement ?

Quoted 8 lines
Quoted 5 lines

For example, there are two regions are populated: [0 4KB] and [4KB 64KB] on a 64KB host. With the alignment considered, we're populating [0 64KB] for region [0 4KB], and [0 64KB] for [4KB 64KB]. [0 64KB] are populated for twice. Is this allowed by TF-RMM? If it's allowed by TF-RMM, the data populated in the first time is overwritten by the second population.

It is not allowed by the RMM and it is a VMM bug that we have to prevent.

Ok, thanks for your confirm. It's why I asked if we ever see an unaligned Rom region. If all Rom regions are aligned to host page size, then we needn't to handle the unaligned Rom regions in rme_populate_range(). Otherwise, we need come up with a solution to address the issue.

The solution would be to generate a temporary region list based on the existing Rom region list. For each region in the temporary list, its start/size are well aligned to host page size, and the temporary buffer is allocated, and the data is properly copied over from the original Rom region. The population is done based on the temporary list. For example, we need to have [0 64kb], [128kb 192kb] regions in the temporary list if we have Rom region [0 1kb], [4kb 5kb] [16kb 17kb], [132kb 133kb], [136kb 140kb]. The host page size is assumed to be 64kb in the example.

Quoted 2 lines
   [0 64kb] covers [0 1kb], [4kb 5kb] [16kb 17kb]
   [128kb 192kb] covers [132kb 133kb], [136kb 140kb]

That's exactly what needs to be done.

Thanks,
Lorenzo

Quoted 55 lines

Thanks,
Gavin

Quoted 51 lines

Thanks,
Lorenzo

Quoted 47 lines

Thanks,
Gavin

Quoted 43 lines

Thanks,
Lorenzo

Quoted 39 lines

I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to

    [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.
Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 129

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
            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();
    }

Thanks,
Gavin

#57 lore raw

Re: [RFC v4 11/24] target/arm/kvm-rme: Populate Realm with runtime images

Quoted 128 lines · Lorenzo Pieralisi
Quoted 127 lines · Gavin Shan
Quoted 104 lines · Lorenzo Pieralisi
Quoted 103 lines · Gavin Shan
Quoted 101 lines · Lorenzo Pieralisi
Quoted 32 lines · Gavin Shan
Quoted 31 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Once the Realm descriptor has been created, tell KVM to transfer runtime images (kernel, DT, and rootfs) from guest memory to Realm memory.

---
    target/arm/kvm-rme.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
    1 file changed, 76 insertions(+)
target/arm/kvm-rme.c +6 -0
index 9985f5daba89..c082a5d8f3d1 100644
@@ -15,6 +15,7 @@
    #include "migration/blocker.h"
    #include "qapi/error.h"
    #include "qemu/error-report.h"
+#include "qemu/memalign.h"
    #include "qom/object_interfaces.h"
    #include "system/confidential-guest-support.h"
    #include "system/kvm.h"
@@ -43,12 +44,87 @@ 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 *buffer;
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 55

Quoted 6 lines
target/arm/kvm-rme.c +6 -0
+    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;
+    size_t aligned_size = ROUND_UP(region->size, qemu_real_host_page_size());

s/qemu_real_host_page_size()/RME_PAGE_SIZE

It will always have to be qemu_real_host_page_size(), KVM configures it that way.

Quoted 41 lines
Quoted 41 lines
target/arm/kvm-rme.c +41 -0
+
+    if (!region->data) {
+        return -ENODEV;
+    }
+
+    ret = kvm_set_memory_attributes_private(start, end - start);
+    if (ret) {
+        error_report("RME: failed to configure initial"
+                     "private guest memory");
+        return ret;
+    }
+
+    /* Allocate page-aligned memory */
+    buffer = qemu_memalign(qemu_real_host_page_size(), aligned_size);
+
+    if (!buffer) {
+        return -ENOMEM;
+    }
+
+    memset(buffer, 0, aligned_size);
+    memcpy(buffer, region->data, region->size);
+
+    populate_args = (struct kvm_arm_rmi_populate) {
+        .base = start,
+        .size = end - start,
+        .source_uaddr = (uintptr_t)buffer,
+        .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;
+        }
+    }
+
+    qemu_vfree(buffer);
+
Quoted 8 lines

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 99

Quoted 3 lines
target/arm/kvm-rme.c +3 -0
+    return ret;
+}
+

I doubt that the unaligned regions (RmeRamRegion) are allowed. For example, the region [4KB 64KB+4KB] is turned to the aligned range [0 128KB] when RME_PAGE_SIZE is 64KB. The question is how do we know the added regions due to the alignment ([0 4KB] and [64KB+4KB 128KB]) are safe for kvm_vm_ioctl(KVM_ARM_RMI_POPULATE).

Getting back to this because it is important. Can you define "safe" here ?

I think it is VMM's role to load with the required alignment and make sure it does not double populate (that is anyway prevented by KVM), what was your concern here to make sure we are in agreement ?

Quoted 8 lines
Quoted 5 lines

For example, there are two regions are populated: [0 4KB] and [4KB 64KB] on a 64KB host. With the alignment considered, we're populating [0 64KB] for region [0 4KB], and [0 64KB] for [4KB 64KB]. [0 64KB] are populated for twice. Is this allowed by TF-RMM? If it's allowed by TF-RMM, the data populated in the first time is overwritten by the second population.

It is not allowed by the RMM and it is a VMM bug that we have to prevent.

Ok, thanks for your confirm. It's why I asked if we ever see an unaligned Rom region. If all Rom regions are aligned to host page size, then we needn't to handle the unaligned Rom regions in rme_populate_range(). Otherwise, we need come up with a solution to address the issue.

The solution would be to generate a temporary region list based on the existing Rom region list. For each region in the temporary list, its start/size are well aligned to host page size, and the temporary buffer is allocated, and the data is properly copied over from the original Rom region. The population is done based on the temporary list. For example, we need to have [0 64kb], [128kb 192kb] regions in the temporary list if we have Rom region [0 1kb], [4kb 5kb] [16kb 17kb], [132kb 133kb], [136kb 140kb]. The host page size is assumed to be 64kb in the example.

Quoted 5 lines
Quoted 2 lines
   [0 64kb] covers [0 1kb], [4kb 5kb] [16kb 17kb]
   [128kb 192kb] covers [132kb 133kb], [136kb 140kb]

That's exactly what needs to be done.

That is also what I understood. My goal is to present this enhancement in RFCv6.

Quoted 58 lines

Thanks,
Lorenzo

Quoted 55 lines

Thanks,
Gavin

Quoted 51 lines

Thanks,
Lorenzo

Quoted 47 lines

Thanks,
Gavin

Quoted 43 lines

Thanks,
Lorenzo

Quoted 39 lines

I guess we probably just reject unaligned regions if they're not expected.

Even if the unaligned regions are allowed, they seem not well handled.

- @aligned_size is still 64KB instead of the 128KB ?
- In "memcpy(buffer, region->data, region->size);", the blob data would be copied to

    [4KB, 4KB+64KB]. However, we're copying the data to [0 64KB] now.
Quoted 17 lines
target/arm/kvm-rme.c +14 -0
+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) {

target/arm/kvm-rme.c OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST, · line 126

Quoted 10 lines
target/arm/kvm-rme.c +6 -0
            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();
    }

Thanks,
Gavin

#58 lore raw

RFC v4 12/24 target/arm/cpu: Set number of breakpoints and watchpoints in KVM

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 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(+)
target/arm/arm-qmp-cmds.c +1 -0
index 83ec95c290f6..9ed65eb090d7 100644
@@ -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
 };
 
target/arm/cpu.h +4 -0
index 6bbc942f9a59..d256d66a3e1b 100644
@@ -1173,6 +1173,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 {
target/arm/cpu64.c +81 -0

93 lines not on this page · raw message

target/arm/kvm.c +56 -0

68 lines not on this page · raw message

Signature · 1 line
2.43.0
#59 lore raw

Re: [RFC v4 12/24] target/arm/cpu: Set number of breakpoints and watchpoints in KVM

Quoted 35 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  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(+)
target/arm/arm-qmp-cmds.c +1 -0
index 83ec95c290f6..9ed65eb090d7 100644
@@ -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
  };
 
target/arm/cpu.h +0 -0
index 6bbc942f9a59..d256d66a3e1b 100644

target/arm/cpu.h struct ArchCPU { · line 1181

Quoted 9 lines
target/arm/cpu.h +4 -0
@@ -1173,6 +1173,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;
  };
 

May improve the comments to indicate: these two fields are only needed by RME-vCPUs.

       /* Override the default configuration, only for RME vCPUs */
Quoted 64 lines
target/arm/cpu.h +0 -0
  typedef struct ARMCPUInfo {
target/arm/cpu64.c +55 -0
index 4e8c47253086..18adb06d78ad 100644
@@ -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;

target/arm/cpu64.c void aarch64_add_pauth_properties(Object *obj) · line 733

Quoted 10 lines
target/arm/cpu64.c +10 -0
+    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");
          ^^^^^

Code alignment issue here.

Quoted 67 lines
target/arm/cpu64.c +16 -0
+       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;
@@ -807,6 +887,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)
target/arm/kvm.c +33 -0
index ca25b707a0a8..93686b038263 100644
@@ -912,6 +912,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.

target/arm/kvm.c out: · line 957

Quoted 10 lines
target/arm/kvm.c +10 -0
+     */
+    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");
+    }

These two checks could be combined since we needn't to know how the write has been failing.

       if (ret || val != newval) {
           error_report("Failed to update KVM_REG_ARM_ID_AA64DFR0_EL1");
       }
Quoted 11 lines
target/arm/kvm.c +7 -0
+}
+
+static void kvm_arm_configure_vcpu_regs(ARMCPU *cpu)
+{
+    kvm_arm_configure_aa64dfr0(cpu);
+}
+
  /**
   * kvm_arm_cpreg_level:
   * @regidx: KVM register index

target/arm/kvm.c void kvm_arm_reset_vcpu(ARMCPU *cpu) · line 1184

Quoted 7 lines
target/arm/kvm.c +4 -0
@@ -1128,6 +1178,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
+     */

Can be squeezed into one line:

       /* Update KVM state before it's loaded to CPUState */

target/arm/kvm.c void kvm_arm_reset_vcpu(ARMCPU *cpu) · line 1186

Quoted 2 lines
target/arm/kvm.c +2 -0
+    kvm_arm_configure_vcpu_regs(cpu);
+

The newly added properties ("num-breakpoints", "num-watchpoints") are only needed by RME-vCPUs, but they're also visible to the normal vCPUs. So do we need to bail without adding the properties if RME isn't supported by the vCPUs in kvm_arm_configure_vcpu_regs() ?

target/arm/kvm.c void kvm_arm_reset_vcpu(ARMCPU *cpu) · line 1189

Quoted 3 lines
target/arm/kvm.c +0 -0
      if (!write_kvmstate_to_list(cpu)) {
          fprintf(stderr, "write_kvmstate_to_list failed\n");
          abort();

Thanks,
Gavin

#60 lore raw

Re: [RFC v4 12/24] target/arm/cpu: Set number of breakpoints and watchpoints in KVM

Quoted 230 lines · Gavin Shan
Quoted 34 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  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(+)
target/arm/arm-qmp-cmds.c +1 -0
index 83ec95c290f6..9ed65eb090d7 100644
@@ -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
  };
target/arm/cpu.h +0 -0
index 6bbc942f9a59..d256d66a3e1b 100644

target/arm/cpu.h struct ArchCPU { · line 1179

Quoted 7 lines
target/arm/cpu.h +4 -0
@@ -1173,6 +1173,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;
  };

May improve the comments to indicate: these two fields are only needed by RME-vCPUs.

       /* Override the default configuration, only for RME vCPUs */
Quoted 63 lines
target/arm/cpu.h +0 -0
  typedef struct ARMCPUInfo {
target/arm/cpu64.c +55 -0
index 4e8c47253086..18adb06d78ad 100644
@@ -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;

target/arm/cpu64.c void aarch64_add_pauth_properties(Object *obj) · line 732

Quoted 10 lines
target/arm/cpu64.c +10 -0
+    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");
          ^^^^^

Code alignment issue here.

Quoted 66 lines
target/arm/cpu64.c +16 -0
+       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;
@@ -807,6 +887,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)
target/arm/kvm.c +33 -0
index ca25b707a0a8..93686b038263 100644
@@ -912,6 +912,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.

target/arm/kvm.c out: · line 956

Quoted 10 lines
target/arm/kvm.c +10 -0
+     */
+    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");
+    }

These two checks could be combined since we needn't to know how the write has been failing.

       if (ret || val != newval) {
           error_report("Failed to update KVM_REG_ARM_ID_AA64DFR0_EL1");
       }
Quoted 11 lines
target/arm/kvm.c +7 -0
+}
+
+static void kvm_arm_configure_vcpu_regs(ARMCPU *cpu)
+{
+    kvm_arm_configure_aa64dfr0(cpu);
+}
+
  /**
   * kvm_arm_cpreg_level:
   * @regidx: KVM register index

target/arm/kvm.c void kvm_arm_reset_vcpu(ARMCPU *cpu) · line 1184

Quoted 7 lines
target/arm/kvm.c +4 -0
@@ -1128,6 +1178,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
+     */

Can be squeezed into one line:

Quoted 9 lines
       /* Update KVM state before it's loaded to CPUState */

target/arm/kvm.c void kvm_arm_reset_vcpu(ARMCPU *cpu) · line 1186

Quoted 2 lines
target/arm/kvm.c +2 -0
+    kvm_arm_configure_vcpu_regs(cpu);
+

The newly added properties ("num-breakpoints", "num-watchpoints") are only needed by RME-vCPUs, but they're also visible to the normal vCPUs. So do we need to bail without adding the properties if RME isn't supported by the vCPUs in kvm_arm_configure_vcpu_regs() ?

I added a check for confidential-guest-support in the get() functions of both options. I also addressed the rest of your comments above.

Thanks for the review,
Mathieu

Quoted 7 lines

target/arm/kvm.c void kvm_arm_reset_vcpu(ARMCPU *cpu) · line 1189

Quoted 3 lines
target/arm/kvm.c +0 -0
      if (!write_kvmstate_to_list(cpu)) {
          fprintf(stderr, "write_kvmstate_to_list failed\n");
          abort();

Thanks,
Gavin

#61 lore raw

RFC v4 13/24 target/arm/cpu: Set number of PMU counters in KVM

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 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(-)
target/arm/arm-qmp-cmds.c +1 -1
index 9ed65eb090d7..9350dc30d12f 100644
@@ -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
 };
 
target/arm/cpu.h +3 -0
index d256d66a3e1b..608c5e0124b9 100644
@@ -1177,6 +1177,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 {
@@ -2133,6 +2134,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
target/arm/cpu64.c +41 -0

53 lines not on this page · raw message

target/arm/kvm.c +34 -0
index 93686b038263..b8cb04f7eeb1 100644
@@ -957,9 +957,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);
 }
 
 /**
Signature · 1 line
2.43.0
#62 lore raw

RFC v4 14/24 target/arm/cpu: Don't read Realm registers

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

The host cannot access registers of a Realm. Instead of showing all registers as zero in "info registers", display a message about this restriction.

---
 target/arm/cpu.c | 5 +++++
 1 file changed, 5 insertions(+)
target/arm/cpu.c +5 -0
index 77aa78f00e2f..51c2527d840d 100644
@@ -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) {
Signature · 1 line
2.43.0
#63 lore raw

RFC v4 15/24 hw/arm/virt: Set proper conduit method for Realms

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

The HVC conduit for PSCI is not supported for Realms, so default to SMC.

---
 hw/arm/virt.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
hw/arm/virt.c +7 -1
index d738b95c7264..37211bc81926 100644
@@ -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;
Signature · 1 line
2.43.0
#64 lore raw

RFC v4 16/24 hw/arm/virt: Embed Realm VM type with IPA address space

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Set the Ream VM bit in the IPA address space size when needed.

---
 hw/arm/virt.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
hw/arm/virt.c +10 -1
index 37211bc81926..61751aa31e0d 100644
@@ -97,6 +97,8 @@
 #include "qemu/guest-random.h"
 #include "hw/watchdog/sbsa_gwdt.h"
 
+#include <linux/kvm.h>
+
 static GlobalProperty arm_virt_compat_defaults[] = {
     { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "48" },
 };
@@ -4053,9 +4055,12 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
     VirtMachineState *vms = VIRT_MACHINE(ms);
     int max_vm_pa_size, requested_pa_size;
     bool fixed_ipa;
+    int vm_type;
 
     max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
 
+    vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);
+
     /* we freeze the memory map to compute the highest gpa */
     virt_set_memmap(vms, max_vm_pa_size);
 
@@ -4080,7 +4085,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 | vm_type;
 }
 
 static int virt_get_physical_address_range(MachineState *ms,
Signature · 1 line
2.43.0
#65 lore raw

Re: [RFC v4 16/24] hw/arm/virt: Embed Realm VM type with IPA address space

Quoted 24 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Set the Ream VM bit in the IPA address space size when needed.

---
  hw/arm/virt.c | 11 ++++++++++-
  1 file changed, 10 insertions(+), 1 deletion(-)
hw/arm/virt.c +2 -0
index 37211bc81926..61751aa31e0d 100644
@@ -97,6 +97,8 @@
  #include "qemu/guest-random.h"
  #include "hw/watchdog/sbsa_gwdt.h"
 
+#include <linux/kvm.h>
+
  static GlobalProperty arm_virt_compat_defaults[] = {
      { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "48" },
  };

hw/arm/virt.c static int virt_kvm_type(MachineState *ms, const char *type_str) · line 4059

Quoted 5 lines
hw/arm/virt.c +1 -0
@@ -4053,9 +4055,12 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
      VirtMachineState *vms = VIRT_MACHINE(ms);
      int max_vm_pa_size, requested_pa_size;
      bool fixed_ipa;
+    int vm_type;
 

Lets initialize @vm_type when it's declared:

       VirtMachineState *vms = VIRT_MACHINE(ms)
       int vm_type = ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL;
       int max_vm_pa_size, requested_pa_size;
       bool fixed_ipa;
Quoted 10 lines
hw/arm/virt.c +2 -0
      max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
 
+    vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);
+
      /* we freeze the memory map to compute the highest gpa */
      virt_set_memmap(vms, max_vm_pa_size);
 
@@ -4080,7 +4085,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.

hw/arm/virt.c static int virt_kvm_type(MachineState *ms, const char *type_str) · line 4095

Quoted 10 lines
hw/arm/virt.c +5 -1
       */
-    return fixed_ipa ? 0 : requested_pa_size;
+    if (fixed_ipa) {
+        return 0;
+    }
+
+    return requested_pa_size | vm_type;
  }
 
  static int virt_get_physical_address_range(MachineState *ms,

Thanks,
Gavin

#66 lore raw

RFC v4 17/24 hw/arm/virt: Reserve one bit of guest physical address for RME

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 hw/arm/virt.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
hw/arm/virt.c +15 -5
index 61751aa31e0d..cf3a9320dc3a 100644
@@ -4054,17 +4054,27 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
 {
     VirtMachineState *vms = VIRT_MACHINE(ms);
     int max_vm_pa_size, requested_pa_size;
+    int rme_reserve_bit = 0;
     bool fixed_ipa;
     int vm_type;
 
-    max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
-
     vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);
 
+    if (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.
@@ -4073,11 +4083,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;
     }
     /*
Signature · 1 line
2.43.0
#67 lore raw

Re: [RFC v4 17/24] hw/arm/virt: Reserve one bit of guest physical address for RME

Quoted 25 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  hw/arm/virt.c | 20 +++++++++++++++-----
  1 file changed, 15 insertions(+), 5 deletions(-)
hw/arm/virt.c +1 -1
index 61751aa31e0d..cf3a9320dc3a 100644
@@ -4054,17 +4054,27 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
  {
      VirtMachineState *vms = VIRT_MACHINE(ms);
      int max_vm_pa_size, requested_pa_size;
+    int rme_reserve_bit = 0;
      bool fixed_ipa;
      int vm_type;
 
-    max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);

hw/arm/virt.c static int virt_kvm_type(MachineState *ms, const char *type_str) · line 4069

Quoted 10 lines
hw/arm/virt.c +7 -1
-
      vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);
 
+    if (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;
+    }

"if (vm_type)" are mapped to any VM types other than KVM_VM_TYPE_ARM_NORMAL, meaning it can be PKVM in theory. So this would be:

       if (vm_type == KVM_VM_TYPE_ARM_REALM) {
           ...
       }
Quoted 16 lines
hw/arm/virt.c +5 -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.
@@ -4073,11 +4083,11 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
          requested_pa_size = 32;
      }
 

hw/arm/virt.c static int virt_kvm_type(MachineState *ms, const char *type_str) · line 4093

Quoted 10 lines
hw/arm/virt.c +2 -2
-    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;
      }
      /*

Thanks,
Gavin

#68 lore raw

Re: [RFC v4 17/24] hw/arm/virt: Reserve one bit of guest physical address for RME

Quoted 38 lines · Gavin Shan
Quoted 23 lines · Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
  hw/arm/virt.c | 20 +++++++++++++++-----
  1 file changed, 15 insertions(+), 5 deletions(-)
hw/arm/virt.c +1 -0
index 61751aa31e0d..cf3a9320dc3a 100644
@@ -4054,17 +4054,27 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
  {
      VirtMachineState *vms = VIRT_MACHINE(ms);
      int max_vm_pa_size, requested_pa_size;
+    int rme_reserve_bit = 0;
      bool fixed_ipa;
      int vm_type;

hw/arm/virt.c static int virt_kvm_type(MachineState *ms, const char *type_str) · line 4067

Quoted 10 lines
hw/arm/virt.c +7 -2
-    max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
-
      vm_type = (ms->cgs ? KVM_VM_TYPE_ARM_REALM : KVM_VM_TYPE_ARM_NORMAL);
+    if (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;
+    }

"if (vm_type)" are mapped to any VM types other than KVM_VM_TYPE_ARM_NORMAL, meaning it can be PKVM in theory. So this would be:

Quoted 3 lines
       if (vm_type == KVM_VM_TYPE_ARM_REALM) {
           ...
       }

I agree.

Quoted 28 lines
Quoted 14 lines
hw/arm/virt.c +5 -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.
@@ -4073,11 +4083,11 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
          requested_pa_size = 32;
      }

hw/arm/virt.c static int virt_kvm_type(MachineState *ms, const char *type_str) · line 4092

Quoted 10 lines
hw/arm/virt.c +2 -2
-    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;
      }
      /*

Thanks,
Gavin

#69 lore raw

RFC v4 18/24 hw/arm/virt: Disable DTB randomness for confidential VMs

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 docs/system/arm/virt.rst |  9 +++++----
 hw/arm/virt.c            | 41 +++++++++++++++++++++++++---------------
 include/hw/arm/virt.h    |  2 +-
 3 files changed, 32 insertions(+), 20 deletions(-)
docs/system/arm/virt.rst +5 -4
index 5f6dd1797862..9882b842ddd5 100644
@@ -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.
hw/arm/virt.c +26 -15

94 lines not on this page · raw message

include/hw/arm/virt.h +1 -1
index 22e66d1a113c..d4c1aa04f664 100644
@@ -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;
Signature · 1 line
2.43.0
#70 lore raw

RFC v4 19/24 hw/arm/virt: Move virt_flash_create() to machvirt_init()

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 hw/arm/virt.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
hw/arm/virt.c +2 -2
index 329c881eb91a..f3ece746916a 100644
@@ -2911,6 +2911,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);
 
     /*
@@ -4435,8 +4437,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);
Signature · 1 line
2.43.0
#71 lore raw

RFC v4 20/24 hw/arm/virt: Use RAM instead of flash for confidential guest firmware

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 hw/arm/boot.c         | 37 +++++++++++++++++++++++++--
 hw/arm/virt.c         | 58 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/boot.h |  9 +++++++
 3 files changed, 102 insertions(+), 2 deletions(-)
hw/arm/boot.c +35 -2
index 9b7553dde5e1..1e8efada209f 100644
@@ -1112,7 +1112,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) */
 
@@ -1168,6 +1197,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
@@ -1212,7 +1245,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);
     }
hw/arm/virt.c +58 -0

89 lines not on this page · raw message

include/hw/arm/boot.h +9 -0
index 94386fdbea6c..fe7e14e94651 100644
@@ -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;
 };
 
 /**
Signature · 1 line
2.43.0
#72 lore raw

RFC v4 21/24 target/arm/kvm-rme: Add DMA remapping for the shared memory region

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 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(+)
hw/arm/virt.c +2 -0
index b1f425f8935c..47f91774e71b 100644
@@ -3327,6 +3327,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;
target/arm/kvm-rme.c +106 -0

132 lines not on this page · raw message

target/arm/kvm-stub.c +4 -0
index 5fde96f9b281..e5af5d20367f 100644
@@ -42,6 +42,10 @@ bool kvm_arm_el2_supported(void)
     return false;
 }
 
+void kvm_arm_rme_init_gpa_space(hwaddr highest_gpa, PCIBus *pci_bus)
+{
+}
+
 /*
  * These functions should never actually be called without KVM support.
  */
target/arm/kvm_arm.h +10 -0
index d95381c13afa..b4a293911f71 100644
@@ -250,4 +250,14 @@ char *kvm_print_register_name(uint64_t regidx);
  */
 void kvm_arm_rme_vcpu_init(ARMCPU *cpu);
 
+/**
+ * kvm_arm_rme_setup_gpa
+ * @highest_gpa: highest address of the lower half of the guest address space
+ * @pci_bus: The main PCI bus, for which PCI queries DMA address spaces
+ *
+ * Setup the guest-physical address space for a Realm. Install a memory region
+ * and notifier to manage the shared upper half of the address space.
+ */
+void kvm_arm_rme_init_gpa_space(hwaddr highest_gpa, PCIBus *pci_bus);
+
 #endif
Signature · 1 line
2.43.0
#73 lore raw

RFC v4 22/24 docs/interop/firmware.json: Add arm-rme firmware feature

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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.

---
 docs/interop/firmware.json | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
docs/interop/firmware.json +4 -1
index 421bee0e5ed4..2850b91078d9 100644
@@ -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' ] }
Signature · 1 line
2.43.0
#74 lore raw

RFC v4 23/24 hw/arm/boot: Load DTB as is for confidential VMs

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

Load DTB without modifications when provided. That way it is possible for a verification entity to reconstruct the Realm's initial measurement.

---
 hw/arm/boot.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
hw/arm/boot.c +8 -1
index 1e8efada209f..9b3d3558bcbc 100644
@@ -477,7 +477,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) {
Signature · 1 line
2.43.0
#75 lore raw

RFC v4 24/24 hw/arm/boot: Skip bootloader for confidential guests

In reply to Mathieu Poirier

From: Jean-Philippe Brucker <[email protected]>

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

---
 hw/arm/boot.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)
hw/arm/boot.c +18 -6
index 9b3d3558bcbc..da4f399a3026 100644
@@ -731,7 +731,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);
@@ -821,7 +827,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;
@@ -873,7 +880,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;
             }
         }
@@ -957,7 +965,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;
@@ -1098,8 +1107,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);
Signature · 1 line
2.43.0
#76 lore raw
In reply to Mathieu Poirier

Hi Mathieu,

Quoted 32 lines · Mathieu Poirier

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
-M confidential-guest-support=rme0,memory-backend=ram0 \
-object rme-guest,id=rme0,convert-in-place=on \
-object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
-cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

Quoted 8 lines

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

Give this series and (v19) arm/cca host series [1] a try, and found the following issues. I'm sharing the found issues that I need take chance to look at them closely in future.

[1] https://git.gitlab.arm.com/linux-arm/linux-cca.git (branch: cca/cca-host/kvm-v19/integration)

Note that the host is emulated using qemu (TCG mode) via 'virt' platform, using the following command lines

laptop$ /home/gshan/sandbox/cca/host/qemu/build/qemu-system-aarch64                 \
        -M virt,virtualization=on,secure=on,gic-version=3,acpi=off                  \
        -cpu max,x-rme=on -m 4G -smp 8                                              \
        -serial mon:stdio -monitor none -nographic -nodefaults                      \
        -bios /home/gshan/sandbox/cca/host/tf-a/flash.bin                           \
        -kernel /home/gshan/sandbox/cca/host/linux/arch/arm64/boot/Image            \
        -initrd /home/gshan/sandbox/cca/host/buildroot/output/images/rootfs.cpio.xz \
        -append kvm-arm.mode=rmm                                                    \
        -device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                       \
        -device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                       \
        -device pcie-root-port,bus=pcie.0,chassis=3,id=pcie.3                       \
        -device pcie-root-port,bus=pcie.0,chassis=4,id=pcie.4                       \
        -device virtio-9p-device,fsdev=shr0,mount_tag=shr0                          \
        -fsdev local,security_model=none,path=/home/gshan/sandbox/cca/guest,id=shr0 \
        -netdev tap,id=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown       \
        -device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:80

The realm VM is started using the following command lines.

host$ qemu-system-aarch64 -enable-kvm -object rme-guest,id=rme0,convert-in-place=on \
      -machine virt,gic-version=3,confidential-guest-support=rme0 -m 2G -cpu host   \
      -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2                  \
      -object memory-backend-guest-memfd,id=mem0,size=2G,share=on                   \
      -numa node,nodeid=0,cpus=0-3,memdev=mem0                                      \
      -serial mon:stdio -monitor none -nographic -nodefaults                        \
      -kernel /mnt/linux/arch/arm64/boot/Image                                      \
      -initrd /mnt/buildroot/output/images/rootfs.cpio.xz                           \
      -append earlycon=pl011,mmio,0x10009000000

Found issues
============

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

(2) qemu emulation can't be stopped and resumed

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

(3) qemu is terminiated on qmp command 'system_reset'

(qemu) system_reset
cpus are not resettable, terminating

(4) 'reboot' from the guest doesn't work

root@guest:~# reboot
   :
Requesting system reboot
[  123.264893] reboot: Restarting system
qemu-system-aarch64: cpus are not resettable, terminating

(5) Incompatible to virtio-iommu-pci. The realm guest doesn't boot up if a virtio-iommu-pci device is specified in the command lines

host$ qemu-system-aarch64 -enable-kvm                                          \
-object rme-guest,id=rme0,convert-in-place=on                                  \
-machine virt,gic-version=3,confidential-guest-support=rme0                    \
-m 2G -cpu host -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2   \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on                    \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                                       \
-serial mon:stdio -monitor none -nographic -nodefaults                         \
-kernel /mnt/linux/arch/arm64/boot/Image                                       \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz                            \
-append earlycon=pl011,mmio,0x10009000000                                      \
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                          \
-device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                          \
-device virtio-iommu-pci,bus=pcie.0                                            \
-drive file=/mnt/disk.qcow2,if=none,id=drive0                                  \
-device virtio-blk-pci,id=virtblk0,bus=pcie.1,drive=drive0,num-queues=4        \
-netdev tap,id=tap1,vhost=on,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown \
-device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:c0
     :
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[    0.000000] Linux version 7.2.0-rc5-gavin-gf5098b6bae76 ([email protected]) (gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4), GNU ld version 2.41-65.el10) #47 SMP PREEMPT Mon Jul 27 02:29:03 EDT 2026
[    0.000000] KASLR enabled
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] earlycon: pl11 at MMIO 0x0000010009000000 (options '')
[    0.000000] printk: legacy bootconsole [pl11] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[    0.000000] NODE_DATA(0) allocated [mem 0xbfbecb00-0xbfbf09bf]
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] RME: Using RSI version 1.0
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000040000000-0x00000000bfffffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
     :
[    6.664423] Serial: AMBA PL011 UART driver
[    7.151312] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
[    7.213181] printk: console [ttyAMA0] enabled
[    8.536454] HugeTLB: registered 1.00 Gi

<...The realm guest stops here, no output since then...>

Thanks,
Gavin

#77 lore raw

Good day,

Quoted 44 lines · Gavin Shan

Hi Mathieu,

Quoted 32 lines · Mathieu Poirier

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
-M confidential-guest-support=rme0,memory-backend=ram0 \
-object rme-guest,id=rme0,convert-in-place=on \
-object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
-cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

Quoted 8 lines

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

Quoted 3 lines

Give this series and (v19) arm/cca host series [1] a try, and found the following issues. I'm sharing the found issues that I need take chance to look at them closely in future.

Thank you for taking the time.

Quoted 5 lines

[1] https://git.gitlab.arm.com/linux-arm/linux-cca.git (branch: cca/cca-host/kvm-v19/integration)

Note that the host is emulated using qemu (TCG mode) via 'virt' platform, using the following command lines

I am currently focused on the SBSA machine but will try to reproduce your manipulations using 'upstream-v5' that I just posted.

Note that upstream-v5 doesn't address any of the comments you posted today. Since upstream-v4 was based on v16, I wanted to send another revision that is based on the very latest (v21) to keep pace with Suzuki and Steven.

I am off to Linux Plumbers next week but rest assured that I will look into your comments (and the content herein) when I'm back.

Thanks,
Mathieu

Quoted 110 lines
laptop$ /home/gshan/sandbox/cca/host/qemu/build/qemu-system-aarch64                 \
        -M virt,virtualization=on,secure=on,gic-version=3,acpi=off                  \
        -cpu max,x-rme=on -m 4G -smp 8                                              \
        -serial mon:stdio -monitor none -nographic -nodefaults                      \
        -bios /home/gshan/sandbox/cca/host/tf-a/flash.bin                           \
        -kernel /home/gshan/sandbox/cca/host/linux/arch/arm64/boot/Image            \
        -initrd /home/gshan/sandbox/cca/host/buildroot/output/images/rootfs.cpio.xz \
        -append kvm-arm.mode=rmm                                                    \
        -device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                       \
        -device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                       \
        -device pcie-root-port,bus=pcie.0,chassis=3,id=pcie.3                       \
        -device pcie-root-port,bus=pcie.0,chassis=4,id=pcie.4                       \
        -device virtio-9p-device,fsdev=shr0,mount_tag=shr0                          \
        -fsdev local,security_model=none,path=/home/gshan/sandbox/cca/guest,id=shr0 \
        -netdev tap,id=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown       \
        -device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:80

The realm VM is started using the following command lines.

host$ qemu-system-aarch64 -enable-kvm -object rme-guest,id=rme0,convert-in-place=on \
      -machine virt,gic-version=3,confidential-guest-support=rme0 -m 2G -cpu host   \
      -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2                  \
      -object memory-backend-guest-memfd,id=mem0,size=2G,share=on                   \
      -numa node,nodeid=0,cpus=0-3,memdev=mem0                                      \
      -serial mon:stdio -monitor none -nographic -nodefaults                        \
      -kernel /mnt/linux/arch/arm64/boot/Image                                      \
      -initrd /mnt/buildroot/output/images/rootfs.cpio.xz                           \
      -append earlycon=pl011,mmio,0x10009000000

Found issues
============

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

(2) qemu emulation can't be stopped and resumed

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

(3) qemu is terminiated on qmp command 'system_reset'

(qemu) system_reset
cpus are not resettable, terminating

(4) 'reboot' from the guest doesn't work

root@guest:~# reboot
   :
Requesting system reboot
[  123.264893] reboot: Restarting system
qemu-system-aarch64: cpus are not resettable, terminating

(5) Incompatible to virtio-iommu-pci. The realm guest doesn't boot up if a virtio-iommu-pci device is specified in the command lines

host$ qemu-system-aarch64 -enable-kvm \

(quoted diff) +0 -16
-object rme-guest,id=rme0,convert-in-place=on                                  \
-machine virt,gic-version=3,confidential-guest-support=rme0                    \
-m 2G -cpu host -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2   \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on                    \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                                       \
-serial mon:stdio -monitor none -nographic -nodefaults                         \
-kernel /mnt/linux/arch/arm64/boot/Image                                       \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz                            \
-append earlycon=pl011,mmio,0x10009000000                                      \
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                          \
-device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                          \
-device virtio-iommu-pci,bus=pcie.0                                            \
-drive file=/mnt/disk.qcow2,if=none,id=drive0                                  \
-device virtio-blk-pci,id=virtblk0,bus=pcie.1,drive=drive0,num-queues=4        \
-netdev tap,id=tap1,vhost=on,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown \
-device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:c0
     :
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[    0.000000] Linux version 7.2.0-rc5-gavin-gf5098b6bae76 ([email protected]) (gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4), GNU ld version 2.41-65.el10) #47 SMP PREEMPT Mon Jul 27 02:29:03 EDT 2026
[    0.000000] KASLR enabled
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] earlycon: pl11 at MMIO 0x0000010009000000 (options '')
[    0.000000] printk: legacy bootconsole [pl11] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[    0.000000] NODE_DATA(0) allocated [mem 0xbfbecb00-0xbfbf09bf]
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] RME: Using RSI version 1.0
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000040000000-0x00000000bfffffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
     :
[    6.664423] Serial: AMBA PL011 UART driver
[    7.151312] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
[    7.213181] printk: console [ttyAMA0] enabled
[    8.536454] HugeTLB: registered 1.00 Gi

<...The realm guest stops here, no output since then...>

Thanks,
Gavin

#78 lore raw
In reply to Gavin Shan

Hi Gavin,

Quoted 82 lines · Gavin Shan

Hi Mathieu,

Quoted 32 lines · Mathieu Poirier

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
-M confidential-guest-support=rme0,memory-backend=ram0 \
-object rme-guest,id=rme0,convert-in-place=on \
-object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
-cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

Quoted 8 lines

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

Give this series and (v19) arm/cca host series [1] a try, and found the following issues. I'm sharing the found issues that I need take chance to look at them closely in future.

[1] https://git.gitlab.arm.com/linux-arm/linux-cca.git (branch: cca/cca-host/kvm-v19/integration)

Note that the host is emulated using qemu (TCG mode) via 'virt' platform, using the following command lines

laptop$ /home/gshan/sandbox/cca/host/qemu/build/qemu-system-aarch64                 \
        -M virt,virtualization=on,secure=on,gic-version=3,acpi=off                  \
        -cpu max,x-rme=on -m 4G -smp 8                                              \
        -serial mon:stdio -monitor none -nographic -nodefaults                      \
        -bios /home/gshan/sandbox/cca/host/tf-a/flash.bin                           \
        -kernel /home/gshan/sandbox/cca/host/linux/arch/arm64/boot/Image            \
        -initrd /home/gshan/sandbox/cca/host/buildroot/output/images/rootfs.cpio.xz \
        -append kvm-arm.mode=rmm                                                    \
        -device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                       \
        -device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                       \
        -device pcie-root-port,bus=pcie.0,chassis=3,id=pcie.3                       \
        -device pcie-root-port,bus=pcie.0,chassis=4,id=pcie.4                       \
        -device virtio-9p-device,fsdev=shr0,mount_tag=shr0                          \
        -fsdev local,security_model=none,path=/home/gshan/sandbox/cca/guest,id=shr0 \
        -netdev tap,id=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown       \
        -device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:80

The realm VM is started using the following command lines.

host$ qemu-system-aarch64 -enable-kvm -object rme-guest,id=rme0,convert-in-place=on \
      -machine virt,gic-version=3,confidential-guest-support=rme0 -m 2G -cpu host   \
      -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2                  \
      -object memory-backend-guest-memfd,id=mem0,size=2G,share=on                   \
      -numa node,nodeid=0,cpus=0-3,memdev=mem0                                      \
      -serial mon:stdio -monitor none -nographic -nodefaults                        \
      -kernel /mnt/linux/arch/arm64/boot/Image                                      \
      -initrd /mnt/buildroot/output/images/rootfs.cpio.xz                           \
      -append earlycon=pl011,mmio,0x10009000000
Quoted 2 lines

Found issues
============

I was able to do the same manipulations on my side, but using the SBSA machine for system emulation, with results as described below:

Quoted 8 lines

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

On my side this command causes the Realm to abort, as I expected it to be. Realm memory should not be observable from the host. There may be a debug mode to enable this kind of operation but I will have to look at the documentation for details.

Quoted 6 lines

(2) qemu emulation can't be stopped and resumed

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

That too I was expecting. Anything that can comprimise a Realm will cause the HW to abort. I'll investigate the "Failed to set KVM_REG_ARM_TIMER_CNT" error message though.

Quoted 5 lines

(3) qemu is terminiated on qmp command 'system_reset'

(qemu) system_reset
cpus are not resettable, terminating

Quoted 8 lines

(4) 'reboot' from the guest doesn't work

root@guest:~# reboot
   :
Requesting system reboot
[  123.264893] reboot: Restarting system
qemu-system-aarch64: cpus are not resettable, terminating

I suspect 'system_reset' from the monitor and a 'reboot' from the command line will, at some point, end up following the same sequence. I need to think about the 'reboot' use case further.

Quoted 10 lines

(5) Incompatible to virtio-iommu-pci. The realm guest doesn't boot up if a virtio-iommu-pci device is specified in the command lines

host$ qemu-system-aarch64 -enable-kvm \

(quoted diff) +0 -6
-object rme-guest,id=rme0,convert-in-place=on                                  \
-machine virt,gic-version=3,confidential-guest-support=rme0                    \
-m 2G -cpu host -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2   \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on                    \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                                       \
-serial mon:stdio -monitor none -nographic -nodefaults                         \

Review

Quoted 10 lines
(quoted diff) +0 -10
-kernel /mnt/linux/arch/arm64/boot/Image                                       \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz                            \
-append earlycon=pl011,mmio,0x10009000000                                      \
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                          \
-device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                          \
-device virtio-iommu-pci,bus=pcie.0                                            \
-drive file=/mnt/disk.qcow2,if=none,id=drive0                                  \
-device virtio-blk-pci,id=virtblk0,bus=pcie.1,drive=drive0,num-queues=4        \
-netdev tap,id=tap1,vhost=on,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown \
-device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:c0

I added the following to my Realm's boot command parameters:

-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1 \
-device virtio-iommu-pci,bus=pcie.0 \

Quoted 26 lines
     :
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[    0.000000] Linux version 7.2.0-rc5-gavin-gf5098b6bae76 ([email protected]) (gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4), GNU ld version 2.41-65.el10) #47 SMP PREEMPT Mon Jul 27 02:29:03 EDT 2026
[    0.000000] KASLR enabled
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] earlycon: pl11 at MMIO 0x0000010009000000 (options '')
[    0.000000] printk: legacy bootconsole [pl11] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[    0.000000] NODE_DATA(0) allocated [mem 0xbfbecb00-0xbfbf09bf]
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] RME: Using RSI version 1.0
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000040000000-0x00000000bfffffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
     :
[    6.664423] Serial: AMBA PL011 UART driver
[    7.151312] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
[    7.213181] printk: console [ttyAMA0] enabled
[    8.536454] HugeTLB: registered 1.00 Gi
Quoted 2 lines

<...The realm guest stops here, no output since then...>

On my side I get to user space but virtio-serial and virtio-net don't work. I'm guessing virtio-iommu-pci does the same thing as this patch [1], but virtio devices are disrupted because it is not configured properly. I need to investigate to find the best approach to follow, i.e implicitly use (and configure) a virtio-iommu-pci or make sure that [1] and a virtio-iommu-pci device can play nice together.

Thanks,
Mathieu

[1]. [RFC v4 21/24] target/arm/kvm-rme: Add DMA remapping for the shared memory region

Quoted 3 lines

Thanks,
Gavin

#79 lore raw

Hi Mathieu,

Quoted 85 lines · Mathieu Poirier

Hi Gavin,

Quoted 82 lines · Gavin Shan

Hi Mathieu,

Quoted 32 lines · Mathieu Poirier

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
   -M confidential-guest-support=rme0,memory-backend=ram0 \
   -object rme-guest,id=rme0,convert-in-place=on \
   -object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
   -cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

Quoted 8 lines

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

Give this series and (v19) arm/cca host series [1] a try, and found the following issues. I'm sharing the found issues that I need take chance to look at them closely in future.

[1] https://git.gitlab.arm.com/linux-arm/linux-cca.git (branch: cca/cca-host/kvm-v19/integration)

Note that the host is emulated using qemu (TCG mode) via 'virt' platform, using the following command lines

laptop$ /home/gshan/sandbox/cca/host/qemu/build/qemu-system-aarch64                 \
         -M virt,virtualization=on,secure=on,gic-version=3,acpi=off                  \
         -cpu max,x-rme=on -m 4G -smp 8                                              \
         -serial mon:stdio -monitor none -nographic -nodefaults                      \
         -bios /home/gshan/sandbox/cca/host/tf-a/flash.bin                           \
         -kernel /home/gshan/sandbox/cca/host/linux/arch/arm64/boot/Image            \
         -initrd /home/gshan/sandbox/cca/host/buildroot/output/images/rootfs.cpio.xz \
         -append kvm-arm.mode=rmm                                                    \
         -device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                       \
         -device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                       \
         -device pcie-root-port,bus=pcie.0,chassis=3,id=pcie.3                       \
         -device pcie-root-port,bus=pcie.0,chassis=4,id=pcie.4                       \
         -device virtio-9p-device,fsdev=shr0,mount_tag=shr0                          \
         -fsdev local,security_model=none,path=/home/gshan/sandbox/cca/guest,id=shr0 \
         -netdev tap,id=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown       \
         -device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:80

The realm VM is started using the following command lines.

host$ qemu-system-aarch64 -enable-kvm -object rme-guest,id=rme0,convert-in-place=on \
       -machine virt,gic-version=3,confidential-guest-support=rme0 -m 2G -cpu host   \
       -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2                  \
       -object memory-backend-guest-memfd,id=mem0,size=2G,share=on                   \
       -numa node,nodeid=0,cpus=0-3,memdev=mem0                                      \
       -serial mon:stdio -monitor none -nographic -nodefaults                        \
       -kernel /mnt/linux/arch/arm64/boot/Image                                      \
       -initrd /mnt/buildroot/output/images/rootfs.cpio.xz                           \
       -append earlycon=pl011,mmio,0x10009000000
Quoted 6 lines
Quoted 2 lines

Found issues
============

I was able to do the same manipulations on my side, but using the SBSA machine for system emulation, with results as described below:

Thanks for double check on your side. Just a head up: I will re-run the broken test cases with (RFCv5) qemu series and share the result. I expect we will have more discussions on those broken test cases under RFCv5 series if you agree. I don't know when I get a chance to try RFCv5 series, but hopefully next week or early of the week after that.

Cheers,
Gavin

Quoted 118 lines
Quoted 8 lines

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

On my side this command causes the Realm to abort, as I expected it to be. Realm memory should not be observable from the host. There may be a debug mode to enable this kind of operation but I will have to look at the documentation for details.

Quoted 6 lines

(2) qemu emulation can't be stopped and resumed

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

That too I was expecting. Anything that can comprimise a Realm will cause the HW to abort. I'll investigate the "Failed to set KVM_REG_ARM_TIMER_CNT" error message though.

Quoted 5 lines

(3) qemu is terminiated on qmp command 'system_reset'

(qemu) system_reset
cpus are not resettable, terminating

Quoted 8 lines

(4) 'reboot' from the guest doesn't work

root@guest:~# reboot
    :
Requesting system reboot
[  123.264893] reboot: Restarting system
qemu-system-aarch64: cpus are not resettable, terminating

I suspect 'system_reset' from the monitor and a 'reboot' from the command line will, at some point, end up following the same sequence. I need to think about the 'reboot' use case further.

Quoted 10 lines

(5) Incompatible to virtio-iommu-pci. The realm guest doesn't boot up if a virtio-iommu-pci device is specified in the command lines

host$ qemu-system-aarch64 -enable-kvm \

(quoted diff) +0 -6
-object rme-guest,id=rme0,convert-in-place=on                                  \
-machine virt,gic-version=3,confidential-guest-support=rme0                    \
-m 2G -cpu host -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2   \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on                    \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                                       \
-serial mon:stdio -monitor none -nographic -nodefaults                         \

Review

Quoted 10 lines
(quoted diff) +0 -10
-kernel /mnt/linux/arch/arm64/boot/Image                                       \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz                            \
-append earlycon=pl011,mmio,0x10009000000                                      \
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                          \
-device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                          \
-device virtio-iommu-pci,bus=pcie.0                                            \
-drive file=/mnt/disk.qcow2,if=none,id=drive0                                  \
-device virtio-blk-pci,id=virtblk0,bus=pcie.1,drive=drive0,num-queues=4        \
-netdev tap,id=tap1,vhost=on,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown \
-device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:c0

I added the following to my Realm's boot command parameters:

(quoted diff) +0 -2
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1 \
-device virtio-iommu-pci,bus=pcie.0 \
Quoted 28 lines

      :
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[    0.000000] Linux version 7.2.0-rc5-gavin-gf5098b6bae76 ([email protected]) (gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4), GNU ld version 2.41-65.el10) #47 SMP PREEMPT Mon Jul 27 02:29:03 EDT 2026
[    0.000000] KASLR enabled
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] earlycon: pl11 at MMIO 0x0000010009000000 (options '')
[    0.000000] printk: legacy bootconsole [pl11] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[    0.000000] NODE_DATA(0) allocated [mem 0xbfbecb00-0xbfbf09bf]
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] RME: Using RSI version 1.0
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000040000000-0x00000000bfffffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
      :
[    6.664423] Serial: AMBA PL011 UART driver
[    7.151312] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
[    7.213181] printk: console [ttyAMA0] enabled
[    8.536454] HugeTLB: registered 1.00 Gi
Quoted 2 lines

<...The realm guest stops here, no output since then...>

On my side I get to user space but virtio-serial and virtio-net don't work. I'm guessing virtio-iommu-pci does the same thing as this patch [1], but virtio devices are disrupted because it is not configured properly. I need to investigate to find the best approach to follow, i.e implicitly use (and configure) a virtio-iommu-pci or make sure that [1] and a virtio-iommu-pci device can play nice together.

Thanks,
Mathieu

[1]. [RFC v4 21/24] target/arm/kvm-rme: Add DMA remapping for the shared memory region

Quoted 3 lines

Thanks,
Gavin

#80 lore raw
Quoted 95 lines · Gavin Shan

Hi Mathieu,

Quoted 85 lines · Mathieu Poirier

Hi Gavin,

Quoted 82 lines · Gavin Shan

Hi Mathieu,

Quoted 32 lines · Mathieu Poirier

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
   -M confidential-guest-support=rme0,memory-backend=ram0 \
   -object rme-guest,id=rme0,convert-in-place=on \
   -object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
   -cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

Quoted 8 lines

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

Give this series and (v19) arm/cca host series [1] a try, and found the following issues. I'm sharing the found issues that I need take chance to look at them closely in future.

[1] https://git.gitlab.arm.com/linux-arm/linux-cca.git (branch: cca/cca-host/kvm-v19/integration)

Note that the host is emulated using qemu (TCG mode) via 'virt' platform, using the following command lines

laptop$ /home/gshan/sandbox/cca/host/qemu/build/qemu-system-aarch64                 \
         -M virt,virtualization=on,secure=on,gic-version=3,acpi=off                  \
         -cpu max,x-rme=on -m 4G -smp 8                                              \
         -serial mon:stdio -monitor none -nographic -nodefaults                      \
         -bios /home/gshan/sandbox/cca/host/tf-a/flash.bin                           \
         -kernel /home/gshan/sandbox/cca/host/linux/arch/arm64/boot/Image            \
         -initrd /home/gshan/sandbox/cca/host/buildroot/output/images/rootfs.cpio.xz \
         -append kvm-arm.mode=rmm                                                    \
         -device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                       \
         -device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                       \
         -device pcie-root-port,bus=pcie.0,chassis=3,id=pcie.3                       \
         -device pcie-root-port,bus=pcie.0,chassis=4,id=pcie.4                       \
         -device virtio-9p-device,fsdev=shr0,mount_tag=shr0                          \
         -fsdev local,security_model=none,path=/home/gshan/sandbox/cca/guest,id=shr0 \
         -netdev tap,id=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown       \
         -device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:80

The realm VM is started using the following command lines.

host$ qemu-system-aarch64 -enable-kvm -object rme-guest,id=rme0,convert-in-place=on \
       -machine virt,gic-version=3,confidential-guest-support=rme0 -m 2G -cpu host   \
       -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2                  \
       -object memory-backend-guest-memfd,id=mem0,size=2G,share=on                   \
       -numa node,nodeid=0,cpus=0-3,memdev=mem0                                      \
       -serial mon:stdio -monitor none -nographic -nodefaults                        \
       -kernel /mnt/linux/arch/arm64/boot/Image                                      \
       -initrd /mnt/buildroot/output/images/rootfs.cpio.xz                           \
       -append earlycon=pl011,mmio,0x10009000000
Quoted 6 lines
Quoted 2 lines

Found issues
============

I was able to do the same manipulations on my side, but using the SBSA machine for system emulation, with results as described below:

Quoted 6 lines

Thanks for double check on your side. Just a head up: I will re-run the broken test cases with (RFCv5) qemu series and share the result. I expect we will have more discussions on those broken test cases under RFCv5 series if you agree. I don't know when I get a chance to try RFCv5 series, but hopefully next week or early of the week after that.

I omitted to mention that I used RFCv5 for my manipulations on SBSA, so I expect you'll find the same problems as with RFCv4. No need to rush to do the test since my next revision will _not_ address any of those issues. For RFCv6 I want to concentrate on the other comments you and Kohei made and keep the bigger issues (the ones you raised here) for future revision. That way we make progess on this patchset and keep synchronized with the KVM patches Suzuki and Steven release.

We can discuss further at the KVM forum if you are there.

Quoted 121 lines

Cheers,
Gavin

Quoted 117 lines
Quoted 8 lines

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

On my side this command causes the Realm to abort, as I expected it to be. Realm memory should not be observable from the host. There may be a debug mode to enable this kind of operation but I will have to look at the documentation for details.

Quoted 6 lines

(2) qemu emulation can't be stopped and resumed

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

That too I was expecting. Anything that can comprimise a Realm will cause the HW to abort. I'll investigate the "Failed to set KVM_REG_ARM_TIMER_CNT" error message though.

Quoted 5 lines

(3) qemu is terminiated on qmp command 'system_reset'

(qemu) system_reset
cpus are not resettable, terminating

Quoted 8 lines

(4) 'reboot' from the guest doesn't work

root@guest:~# reboot
    :
Requesting system reboot
[  123.264893] reboot: Restarting system
qemu-system-aarch64: cpus are not resettable, terminating

I suspect 'system_reset' from the monitor and a 'reboot' from the command line will, at some point, end up following the same sequence. I need to think about the 'reboot' use case further.

Quoted 10 lines

(5) Incompatible to virtio-iommu-pci. The realm guest doesn't boot up if a virtio-iommu-pci device is specified in the command lines

host$ qemu-system-aarch64 -enable-kvm \

(quoted diff) +0 -6
-object rme-guest,id=rme0,convert-in-place=on                                  \
-machine virt,gic-version=3,confidential-guest-support=rme0                    \
-m 2G -cpu host -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2   \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on                    \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                                       \
-serial mon:stdio -monitor none -nographic -nodefaults                         \

Review

Quoted 10 lines
(quoted diff) +0 -10
-kernel /mnt/linux/arch/arm64/boot/Image                                       \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz                            \
-append earlycon=pl011,mmio,0x10009000000                                      \
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                          \
-device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                          \
-device virtio-iommu-pci,bus=pcie.0                                            \
-drive file=/mnt/disk.qcow2,if=none,id=drive0                                  \
-device virtio-blk-pci,id=virtblk0,bus=pcie.1,drive=drive0,num-queues=4        \
-netdev tap,id=tap1,vhost=on,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown \
-device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:c0

I added the following to my Realm's boot command parameters:

(quoted diff) +0 -2
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1 \
-device virtio-iommu-pci,bus=pcie.0 \
Quoted 28 lines

      :
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[    0.000000] Linux version 7.2.0-rc5-gavin-gf5098b6bae76 ([email protected]) (gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4), GNU ld version 2.41-65.el10) #47 SMP PREEMPT Mon Jul 27 02:29:03 EDT 2026
[    0.000000] KASLR enabled
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] earlycon: pl11 at MMIO 0x0000010009000000 (options '')
[    0.000000] printk: legacy bootconsole [pl11] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[    0.000000] NODE_DATA(0) allocated [mem 0xbfbecb00-0xbfbf09bf]
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] RME: Using RSI version 1.0
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000040000000-0x00000000bfffffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
      :
[    6.664423] Serial: AMBA PL011 UART driver
[    7.151312] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
[    7.213181] printk: console [ttyAMA0] enabled
[    8.536454] HugeTLB: registered 1.00 Gi
Quoted 2 lines

<...The realm guest stops here, no output since then...>

On my side I get to user space but virtio-serial and virtio-net don't work. I'm guessing virtio-iommu-pci does the same thing as this patch [1], but virtio devices are disrupted because it is not configured properly. I need to investigate to find the best approach to follow, i.e implicitly use (and configure) a virtio-iommu-pci or make sure that [1] and a virtio-iommu-pci device can play nice together.

Thanks,
Mathieu

[1]. [RFC v4 21/24] target/arm/kvm-rme: Add DMA remapping for the shared memory region

Quoted 3 lines

Thanks,
Gavin

#81 lore raw
Quoted 111 lines · Mathieu Poirier
Quoted 95 lines · Gavin Shan

Hi Mathieu,

Quoted 85 lines · Mathieu Poirier

Hi Gavin,

Quoted 82 lines · Gavin Shan

Hi Mathieu,

Quoted 32 lines · Mathieu Poirier

This patchset provides minimal functionality to start a Realm VM from an Arm RME capable host using the following command line:

qemu-system-aarch64 \
   -M confidential-guest-support=rme0,memory-backend=ram0 \
   -object rme-guest,id=rme0,convert-in-place=on \
   -object memory-backend-guest-memfd,id=ram0,size=1G,share=on \
   -cpu host -M virt -enable-kvm -M gic-version=3,its=on -nodefaults ..

It is a refactoring of Jean-Philippe Brucker's initial work dating from a while back. It is compatible with Steven Price's v16 revision [1] of his work adding CCA support to KVM.

This revision is based on Michael Roth's work on guest memfd in-place memory conversion [2], rebased to QEMU master. I left out the AMD specific changes from Michael's work because I can't test the result. Other than the modications to the linux headers, this series is identical to V3.

It was tested on the QEMU SBSA machine. For convenience, a repository is hosted here [3], along with the TF-A [4] and RMM [5] for the SBSA machine (compatible with Steven's v16 patchset). The Repository for the kernel [6] is also available. Note that for the latter, defines for KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES and KVM_CAP_ARM_RMI have been modified to follow QEMU master.

Instructions to compile and run the entire stack can be found here [7].

Device Assignment is not included.

Thanks,
Mathieu

Quoted 8 lines

[1]. https://lore.kernel.org/kvm/[email protected]/
[2]. https://lore.kernel.org/kvm/[email protected]/
[3]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4?ref_type=heads
[4]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v16?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v4?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/build-instructions

Give this series and (v19) arm/cca host series [1] a try, and found the following issues. I'm sharing the found issues that I need take chance to look at them closely in future.

[1] https://git.gitlab.arm.com/linux-arm/linux-cca.git (branch: cca/cca-host/kvm-v19/integration)

Note that the host is emulated using qemu (TCG mode) via 'virt' platform, using the following command lines

laptop$ /home/gshan/sandbox/cca/host/qemu/build/qemu-system-aarch64                 \
         -M virt,virtualization=on,secure=on,gic-version=3,acpi=off                  \
         -cpu max,x-rme=on -m 4G -smp 8                                              \
         -serial mon:stdio -monitor none -nographic -nodefaults                      \
         -bios /home/gshan/sandbox/cca/host/tf-a/flash.bin                           \
         -kernel /home/gshan/sandbox/cca/host/linux/arch/arm64/boot/Image            \
         -initrd /home/gshan/sandbox/cca/host/buildroot/output/images/rootfs.cpio.xz \
         -append kvm-arm.mode=rmm                                                    \
         -device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                       \
         -device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                       \
         -device pcie-root-port,bus=pcie.0,chassis=3,id=pcie.3                       \
         -device pcie-root-port,bus=pcie.0,chassis=4,id=pcie.4                       \
         -device virtio-9p-device,fsdev=shr0,mount_tag=shr0                          \
         -fsdev local,security_model=none,path=/home/gshan/sandbox/cca/guest,id=shr0 \
         -netdev tap,id=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown       \
         -device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:80

The realm VM is started using the following command lines.

host$ qemu-system-aarch64 -enable-kvm -object rme-guest,id=rme0,convert-in-place=on \
       -machine virt,gic-version=3,confidential-guest-support=rme0 -m 2G -cpu host   \
       -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2                  \
       -object memory-backend-guest-memfd,id=mem0,size=2G,share=on                   \
       -numa node,nodeid=0,cpus=0-3,memdev=mem0                                      \
       -serial mon:stdio -monitor none -nographic -nodefaults                        \
       -kernel /mnt/linux/arch/arm64/boot/Image                                      \
       -initrd /mnt/buildroot/output/images/rootfs.cpio.xz                           \
       -append earlycon=pl011,mmio,0x10009000000
Quoted 6 lines
Quoted 2 lines

Found issues
============

I was able to do the same manipulations on my side, but using the SBSA machine for system emulation, with results as described below:

Quoted 6 lines

Thanks for double check on your side. Just a head up: I will re-run the broken test cases with (RFCv5) qemu series and share the result. I expect we will have more discussions on those broken test cases under RFCv5 series if you agree. I don't know when I get a chance to try RFCv5 series, but hopefully next week or early of the week after that.

I omitted to mention that I used RFCv5 for my manipulations on SBSA, so I expect you'll find the same problems as with RFCv4. No need to rush to do the test since my next revision will _not_ address any of those issues. For RFCv6 I want to concentrate on the other comments you and Kohei made and keep the bigger issues (the ones you raised here) for future revision. That way we make progess on this patchset and keep synchronized with the KVM patches Suzuki and Steven release.

Quoted 15 lines

We can discuss further at the KVM forum if you are there.

Quoted 13 lines

Cheers,
Gavin

Quoted 10 lines
Quoted 8 lines

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

On my side this command causes the Realm to abort, as I expected it to be.

Hi Mathieu, Gavin.

I've tested RFC v5, and I reproduced issue (1) on my side as well:

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000 Failed to set KVM_REG_ARM_TIMER_CNT
./qemu-ram-realm.sh: line 21: 77580 Aborted (core dumped) $QEMU -no-reboot -M confidential-guest-support=rme0,memory-backend=ram0 ...

This was resolved by adding kvm-no-adjvtime=on.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

Realm memory should not be observable from the host. There may be a debug mode to enable this kind of operation but I will have to look at the documentation for details.

Quoted 2 lines

(2) qemu emulation can't be stopped and resumed

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

That too I was expecting. Anything that can comprimise a Realm will cause the HW to abort. I'll investigate the "Failed to set KVM_REG_ARM_TIMER_CNT" error message though.

kvm-no-adjvtime=on also resolved issue (2) above.

Setting KVM_REG_ARM_TIMER_CNT for Realm VMs is not allowed, so we should either add this option or consider making it the default for Realm VMs in the future. What do you think?

Thanks,
Kohei

Quoted 95 lines
Quoted 95 lines
Quoted 94 lines
Quoted 6 lines

(3) qemu is terminiated on qmp command 'system_reset'

(qemu) system_reset
cpus are not resettable, terminating

Quoted 8 lines

(4) 'reboot' from the guest doesn't work

root@guest:~# reboot
    :
Requesting system reboot
[  123.264893] reboot: Restarting system
qemu-system-aarch64: cpus are not resettable, terminating

I suspect 'system_reset' from the monitor and a 'reboot' from the command line will, at some point, end up following the same sequence. I need to think about the 'reboot' use case further.

Quoted 10 lines

(5) Incompatible to virtio-iommu-pci. The realm guest doesn't boot up if a virtio-iommu-pci device is specified in the command lines

host$ qemu-system-aarch64 -enable-kvm \

(quoted diff) +0 -6
-object rme-guest,id=rme0,convert-in-place=on                                  \
-machine virt,gic-version=3,confidential-guest-support=rme0                    \
-m 2G -cpu host -smp maxcpus=4,cpus=4,sockets=1,clusters=1,cores=2,threads=2   \
-object memory-backend-guest-memfd,id=mem0,size=2G,share=on                    \
-numa node,nodeid=0,cpus=0-3,memdev=mem0                                       \
-serial mon:stdio -monitor none -nographic -nodefaults                         \

Review

Quoted 10 lines
(quoted diff) +0 -10
-kernel /mnt/linux/arch/arm64/boot/Image                                       \
-initrd /mnt/buildroot/output/images/rootfs.cpio.xz                            \
-append earlycon=pl011,mmio,0x10009000000                                      \
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1                          \
-device pcie-root-port,bus=pcie.0,chassis=2,id=pcie.2                          \
-device virtio-iommu-pci,bus=pcie.0                                            \
-drive file=/mnt/disk.qcow2,if=none,id=drive0                                  \
-device virtio-blk-pci,id=virtblk0,bus=pcie.1,drive=drive0,num-queues=4        \
-netdev tap,id=tap1,vhost=on,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown \
-device virtio-net-pci,bus=pcie.2,netdev=tap1,mac=b8:3f:d2:1d:3e:c0

I added the following to my Realm's boot command parameters:

(quoted diff) +0 -2
-device pcie-root-port,bus=pcie.0,chassis=1,id=pcie.1 \
-device virtio-iommu-pci,bus=pcie.0 \
Quoted 28 lines

      :
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[    0.000000] Linux version 7.2.0-rc5-gavin-gf5098b6bae76 ([email protected]) (gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4), GNU ld version 2.41-65.el10) #47 SMP PREEMPT Mon Jul 27 02:29:03 EDT 2026
[    0.000000] KASLR enabled
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] earlycon: pl11 at MMIO 0x0000010009000000 (options '')
[    0.000000] printk: legacy bootconsole [pl11] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[    0.000000] NODE_DATA(0) allocated [mem 0xbfbecb00-0xbfbf09bf]
[    0.000000] psci: probing for conduit method from DT.
[    0.000000] psci: PSCIv1.1 detected in firmware.
[    0.000000] psci: Using standard PSCI v0.2 function IDs
[    0.000000] psci: MIGRATE_INFO_TYPE not supported.
[    0.000000] psci: SMC Calling Convention v1.2
[    0.000000] RME: Using RSI version 1.0
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000040000000-0x00000000bfffffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
      :
[    6.664423] Serial: AMBA PL011 UART driver
[    7.151312] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
[    7.213181] printk: console [ttyAMA0] enabled
[    8.536454] HugeTLB: registered 1.00 Gi
Quoted 2 lines

<...The realm guest stops here, no output since then...>

On my side I get to user space but virtio-serial and virtio-net don't work. I'm guessing virtio-iommu-pci does the same thing as this patch [1], but virtio devices are disrupted because it is not configured properly. I need to investigate to find the best approach to follow, i.e implicitly use (and configure) a virtio-iommu-pci or make sure that [1] and a virtio-iommu-pci device can play nice together.

Thanks,
Mathieu

[1]. [RFC v4 21/24] target/arm/kvm-rme: Add DMA remapping for the shared memory region

Quoted 3 lines

Thanks,
Gavin

#82 lore raw

On Tue, Nov 10, 2026 at 05:01:14PM +0900, Kohei Enju wrote:

[...]

Quoted 34 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 7 lines

(1) qemu is terminiated on qmp command 'dump-guest-memory'

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000
**
ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start) Bail out! ERROR:../system/memory_mapping.c:215:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)

On my side this command causes the Realm to abort, as I expected it to be.

Hi Mathieu, Gavin.

I've tested RFC v5, and I reproduced issue (1) on my side as well:

(qemu) dump-guest-memory -p "/tmp/mem" 0x40000000 0x10000000 Failed to set KVM_REG_ARM_TIMER_CNT
./qemu-ram-realm.sh: line 21: 77580 Aborted (core dumped) $QEMU -no-reboot -M confidential-guest-support=rme0,memory-backend=ram0 ...

This was resolved by adding kvm-no-adjvtime=on.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

Realm memory should not be observable from the host. There may be a debug mode to enable this kind of operation but I will have to look at the documentation for details.

Quoted 2 lines

(2) qemu emulation can't be stopped and resumed

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

(qemu) stop
(qemu) cont
Failed to set KVM_REG_ARM_TIMER_CNT

That too I was expecting. Anything that can comprimise a Realm will cause the HW to abort. I'll investigate the "Failed to set KVM_REG_ARM_TIMER_CNT" error message though.

Quoted 5 lines

kvm-no-adjvtime=on also resolved issue (2) above.

Setting KVM_REG_ARM_TIMER_CNT for Realm VMs is not allowed, so we should either add this option or consider making it the default for Realm VMs in the future. What do you think?

I think that we should do something like we do for *put_registers(), where we detect whether we are running a Realm VM or not, regardless of the command line parameter or default that param to on for Realms.

Lorenzo