Add Realm support to QEMU-VMM
Subsystems (2)
v5 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-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 Suzuki Poulose's basic KVM plumbing for Realms [1], supplemented with Aneesh Kumar's latest work on swiotbl [2].
This revision is based on Michael Roth's second revision of his work on guest-memfd in-place memory conversion [3], rebased to QEMU master. It was tested on the QEMU SBSA machine.
For convenience a repository is hosted here [4], along with the TF-A [5], RMM [6] and kernel [7].
Instructions to compile and run the entire stack can be found here [8].
Thanks,
Mathieu
[1]. https://lore.kernel.org/kvm/[email protected]/T/#m6a4fb7b947ba687795a9dd3c90874d9fb1eef02e
[2]. https://lore.kernel.org/all/[email protected]/
[3]. https://lore.kernel.org/kvm/[email protected]/
[4]. https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v5?ref_type=heads
[5]. https://gitlab.com/Linaro/cca-public/tf-a/trusted-firmware-a/-/tree/cca/v13?ref_type=heads
[6]. https://gitlab.com/Linaro/cca-public/rmm/-/tree/cca/v20?ref_type=heads
[7]. https://gitlab.com/Linaro/cca-public/linux/-/tree/upstream-v5-kvmv21?ref_type=heads
[8]. https://gitlab.com/Linaro/cca-public/build-instructions
RFC v4: https://lore.kernel.org/kvm/[email protected]/
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
Changes since V4:
- Discarded original 02/24 because it was merged (thank you Philippe).
- Adjusted QEMU version i patch 03/24.
- Fixed changelog in 06/24 (Kohei).
- Combined patches 09 and 10 (Michael).
- Moved check for "rom->addr == -1" to rom_reset() (Michael).
Jean-Philippe Brucker (21):
linux-headers: Add RME related definitions
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 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 | 24 ++ include/hw/arm/boot.h | 9 + include/hw/arm/virt.h | 2 +- include/hw/core/loader.h | 17 ++ linux-headers/linux/kvm.h | 18 +- 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 | 325 +++++++++++++++++++++ target/arm/kvm-stub.c | 9 + target/arm/kvm.c | 201 ++++++++++++- target/arm/kvm_arm.h | 20 ++ target/arm/meson.build | 5 +- 20 files changed, 958 insertions(+), 50 deletions(-) create mode 100644 target/arm/kvm-rme.c
Signature · 1 line
2.43.0
RFC v5 1/22 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/linux/kvm.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
linux-headers/linux/kvm.h +17 -1
@@ -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:
@@ -988,6 +993,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 252 +#define KVM_CAP_ARM_RMI 253struct kvm_irq_routing_irqchip {__u32 irqchip;
@@ -1674,4 +1680,14 @@ struct kvm_pre_fault_memory { __u64 padding[5]; }; +#define KVM_ARM_RMI_POPULATE _IOWR(KVMIO, 0xd7, struct kvm_arm_rmi_populate) +#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; +}; +#endif /* __LINUX_KVM_H */
Signature · 1 line
2.43.0
RFC v5 2/22 target/arm: Add confidential guest support
In reply to Mathieu PoirierFrom: 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
@@ -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
@@ -1289,6 +1289,17 @@ 'data': { '*pretty': 'bool', '*close-action': 'MonitorQMPCloseAction' } } +## +# @RmeGuestProperties: +# +# Properties for rme-guest objects. +# +# Since: 11.2 +## +{ 'struct': 'RmeGuestProperties', + 'base': 'ConfidentialGuestSupportProperties', + 'data': {} } + ## # @ObjectType: #
@@ -1345,6 +1356,7 @@ { 'name': 'pr-manager-helper', 'if': 'CONFIG_LINUX' }, 'qtest', + 'rme-guest', 'rng-builtin', 'rng-egd', { 'name': 'rng-random',
@@ -1424,6 +1436,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
@@ -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
RFC v5 3/22 target/arm/kvm-rme: Add mechanic to initialize realms
In reply to Mathieu PoirierInitialise 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
@@ -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
@@ -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"
@@ -639,7 +640,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. */
@@ -653,6 +655,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 );
@@ -703,7 +714,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
RFC v5 4/22 target/arm/kvm: Split kvm_arch_get/put_registers
In reply to Mathieu PoirierFrom: 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
@@ -2212,7 +2212,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;
@@ -2315,6 +2315,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)) {
@@ -2397,7 +2410,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;
@@ -2500,6 +2513,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
RFC v5 5/22 target/arm/kvm-rme: Initialize vCPU
In reply to Mathieu PoirierFrom: 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
@@ -1070,6 +1070,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
@@ -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
@@ -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
@@ -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
RFC v5 6/22 target/arm/kvm: Create scratch Realm VM when requested
In reply to Mathieu PoirierFrom: Jean-Philippe Brucker <[email protected]>
When a Realm is requested, create a scratch Realm VM by setting bit 30 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
@@ -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
RFC v5 7/22 target/arm/kvm: Use kvm_vm_check_extension() where necessary
In reply to Mathieu PoirierFrom: 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
@@ -370,7 +370,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 ;
@@ -679,7 +679,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 {
@@ -710,11 +710,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);
@@ -1956,7 +1956,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
RFC v5 8/22 target/arm/kvm-rme: Keep track of images loaded in Realm memory
In reply to Mathieu PoirierFrom: 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).
--- hw/core/loader.c | 24 +++++++++++++++++++++++ include/hw/core/loader.h | 17 +++++++++++++++++ target/arm/kvm-rme.c | 41 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+)
hw/core/loader.c +24 -0
@@ -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,22 @@ 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); + +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. +*/ +continue ; + } + + notify = (RomLoaderNotifyData ) { + .addr = rom->addr, + .len = rom->datasize, + .data = rom->data, + }; +notifier_list_notify (&rom_loader_notifier, ¬ify); } }
include/hw/core/loader.h +17 -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 */ +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)
target/arm/kvm-rme.c +41 -0
67 lines not on this page · raw message
Signature · 1 line
2.43.0
RFC v5 9/22 target/arm/kvm-rme: Populate Realm with runtime images
In reply to Mathieu PoirierFrom: 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
@@ -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"
@@ -41,12 +42,87 @@ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RmeGuest, rme_guest, RME_GUEST,static RmeGuest *rme_guest; +static int rme_populate_range (const RmeRamRegion *region,Error **errp) +{ +int ret; +void *buffer; +hwaddr size = region->size; +hwaddr base = region->base; +unsigned long page_size =qemu_real_host_page_size (); +hwaddr start =QEMU_ALIGN_DOWN (base, page_size); +hwaddr end =QEMU_ALIGN_UP (base + size, page_size); +size_t aligned_size = end - start; +struct kvm_arm_rmi_populate populate_args; + +if (!region->data) { +return -ENODEV ; + } + + ret =kvm_set_memory_attributes_private (start, aligned_size); +if (ret) { +error_report ("RME: failed to configure initial" +"private guest memory" ); +return ret; + } + +/* Allocate page-aligned memory */ + buffer =qemu_memalign (page_size, aligned_size); + +if (!buffer) { +return -ENOMEM ; + } + +memset (buffer,0 , aligned_size); +memcpy (buffer + (base - start), region->data, region->size); + + populate_args = (struct kvm_arm_rmi_populate ) { + .base = start, + .size = aligned_size, + .source_uaddr = (uintptr_t )buffer, + .flags =KVM_ARM_RMI_POPULATE_FLAGS_MEASURE , + }; + +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, 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
RFC v5 10/22 target/arm/cpu: Set number of breakpoints and watchpoints in KVM
In reply to Mathieu PoirierFrom: 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
@@ -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
@@ -1197,6 +1197,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
RFC v5 11/22 target/arm/cpu: Set number of PMU counters in KVM
In reply to Mathieu PoirierFrom: 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
@@ -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
@@ -1201,6 +1201,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 {
@@ -2158,6 +2159,8 @@ FIELD(MFAR, FPA, 12, 40) FIELD(MFAR, NSE, 62, 1) FIELD(MFAR, NS, 63, 1) +FIELD (PMCR ,N ,11 ,5 ) +/* If adding a feature bit which corresponds to a Linux ELF * HWCAP bit, remember to update the feature-bit-to-hwcap * mapping in linux-user/elfload.c:get_elf_hwcap().
target/arm/cpu64.c +41 -0
53 lines not on this page · raw message
target/arm/kvm.c +34 -0
@@ -1000,9 +1000,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
RFC v5 12/22 target/arm/cpu: Don't read Realm registers
In reply to Mathieu PoirierFrom: 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
@@ -967,6 +967,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
RFC v5 13/22 hw/arm/virt: Set proper conduit method for Realms
In reply to Mathieu PoirierFrom: 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
@@ -272,6 +272,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 {
@@ -2945,10 +2950,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
RFC v5 14/22 hw/arm/virt: Embed Realm VM type with IPA address space
In reply to Mathieu PoirierFrom: 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
@@ -96,6 +96,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" }, };
@@ -4047,9 +4049,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);
@@ -4074,7 +4079,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
RFC v5 15/22 hw/arm/virt: Reserve one bit of guest physical address for RME
In reply to Mathieu PoirierFrom: 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
@@ -4048,17 +4048,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.
@@ -4067,11 +4077,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
RFC v5 16/22 hw/arm/virt: Disable DTB randomness for confidential VMs
In reply to Mathieu PoirierFrom: 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
@@ -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
@@ -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
RFC v5 17/22 hw/arm/virt: Move virt_flash_create() to machvirt_init()
In reply to Mathieu PoirierFrom: 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
@@ -2904,6 +2904,8 @@ static void machvirt_init(MachineState *machine)unsigned int smp_cpus = machine->smp.cpus;unsigned int max_cpus = machine->smp.max_cpus; +virt_flash_create (vms); + possible_cpus = mc->possible_cpu_arch_ids (machine);/*
@@ -4429,8 +4431,6 @@ static void virt_instance_init(Object *obj) vms->virtio_transports =NUM_VIRTIO_TRANSPORTS ; -virt_flash_create (vms); - vms->oem_id =g_strndup (ACPI_BUILD_APPNAME6 ,6 ); vms->oem_table_id =g_strndup (ACPI_BUILD_APPNAME8 ,8 );cxl_machine_init (obj, &vms->cxl_devices_state);
Signature · 1 line
2.43.0
RFC v5 18/22 hw/arm/virt: Use RAM instead of flash for confidential guest firmware
In reply to Mathieu PoirierFrom: 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
@@ -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
@@ -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
RFC v5 19/22 target/arm/kvm-rme: Add DMA remapping for the shared memory region
In reply to Mathieu PoirierFrom: 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
@@ -3320,6 +3320,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
@@ -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
@@ -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
RFC v5 20/22 docs/interop/firmware.json: Add arm-rme firmware feature
In reply to Mathieu PoirierFrom: 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
@@ -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
RFC v5 21/22 hw/arm/boot: Load DTB as is for confidential VMs
In reply to Mathieu PoirierFrom: 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
@@ -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
RFC v5 22/22 hw/arm/boot: Skip bootloader for confidential guests
In reply to Mathieu PoirierFrom: 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
@@ -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