diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 6db60854d73b9ade5f6a3aee7a30b31567fb4133..0fddb209210098e86f76a6b6a25f18a5e178ace2 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -52,6 +52,8 @@ #include "hw/boards.h" #include "sysemu/stats.h" +#include "sysemu/kvm.h" + /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD #include @@ -86,6 +88,9 @@ struct KVMParkedVcpu { }; KVMState *kvm_state; + +bool virtcca_cvm_allowed = false; + bool kvm_kernel_irqchip; bool kvm_split_irqchip; bool kvm_async_interrupts_allowed; @@ -2353,6 +2358,11 @@ uint32_t kvm_dirty_ring_size(void) return kvm_state->kvm_dirty_ring_size; } +static inline bool kvm_is_virtcca_cvm_type(int type) +{ + return type & VIRTCCA_CVM_TYPE; +} + static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -2445,6 +2455,10 @@ static int kvm_init(MachineState *ms) goto err; } + if (kvm_is_virtcca_cvm_type(type)) { + virtcca_cvm_allowed = true; + } + do { ret = kvm_ioctl(s, KVM_CREATE_VM, type); } while (ret == -EINTR); @@ -3511,6 +3525,28 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target) return r; } +int kvm_load_user_data(hwaddr loader_start, hwaddr image_end, hwaddr initrd_start, hwaddr dtb_end, hwaddr ram_size, + struct kvm_numa_info *numa_info) +{ + KVMState *state = kvm_state; + struct kvm_user_data data; + int ret; + + data.loader_start = loader_start; + data.image_end = image_end; + data.initrd_start = initrd_start; + data.dtb_end = dtb_end; + data.ram_size = ram_size; + memcpy(&data.numa_info, numa_info, sizeof(struct kvm_numa_info)); + + ret = kvm_vm_ioctl(state, KVM_LOAD_USER_DATA, &data); + if (ret < 0) { + error_report("%s: KVM_LOAD_USER_DATA failed!\n", __func__); + } + + return ret; +} + static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as, hwaddr start_addr, hwaddr size) { diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 45b23f61ce849e684708ac26dcecba515a2ff1d0..b90d5167559d6536408bd06b7ae6a026c8505706 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -26,6 +26,8 @@ bool kvm_readonly_mem_allowed; bool kvm_msi_use_devid; bool kvm_csv3_allowed; +bool virtcca_cvm_allowed; + void kvm_flush_coalesced_mmio_buffer(void) { } diff --git a/dump/dump.c b/dump/dump.c index 481905076493c7d723e01f84a08e76ea0e5550e3..787059ac2c8118311124ccbce3f1839219827df8 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -20,6 +20,7 @@ #include "sysemu/dump.h" #include "sysemu/runstate.h" #include "sysemu/cpus.h" +#include "sysemu/kvm.h" #include "qapi/error.h" #include "qapi/qapi-commands-dump.h" #include "qapi/qapi-events-dump.h" @@ -2065,6 +2066,12 @@ void qmp_dump_guest_memory(bool paging, const char *protocol, Error **errp) { ERRP_GUARD(); + + if (virtcca_cvm_enabled()) { + error_setg(errp, "The dump-guest-memory command is temporarily unsupported in cvm."); + return; + } + const char *p; int fd; DumpState *s; diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 84ea6a807a4eddd998cb48934a773ede5146fdf1..394d44ced38235139af44f3305d176a31be226ae 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -26,6 +26,7 @@ #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/units.h" +#include "kvm_arm.h" /* Kernel boot protocol is specified in the kernel docs * Documentation/arm/Booting and Documentation/arm64/booting.txt @@ -1141,6 +1142,16 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { ARM_CPU(cs)->env.boot_info = info; } + + if (kvm_enabled() && virtcca_cvm_enabled()) { + if (info->dtb_limit == 0) { + info->dtb_limit = info->dtb_start + 0x200000; + } + kvm_load_user_data(info->loader_start, image_high_addr, info->initrd_start, + info->dtb_limit, info->ram_size, (struct kvm_numa_info *)info->numa_info); + tmm_add_ram_region(info->loader_start, image_high_addr - info->loader_start, + info->initrd_start, info->dtb_limit - info->initrd_start, true); + } } static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info) @@ -1231,6 +1242,39 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) info->initrd_filename = ms->initrd_filename; info->dtb_filename = ms->dtb; info->dtb_limit = 0; + if (kvm_enabled() && virtcca_cvm_enabled()) { + info->ram_size = ms->ram_size; + info->numa_info = g_malloc(sizeof(struct kvm_numa_info)); + struct kvm_numa_info *numa_info = (struct kvm_numa_info *) info->numa_info; + if (ms->numa_state != NULL && ms->numa_state->num_nodes > 0) { + numa_info->numa_cnt = ms->numa_state->num_nodes; + uint64_t mem_base = info->loader_start; + for (int64_t i = 0; i < ms->numa_state->num_nodes && i < MAX_NUMA_NODE; i++) { + uint64_t mem_len = ms->numa_state->nodes[i].node_mem; + numa_info->numa_nodes[i].numa_id = i; + numa_info->numa_nodes[i].ipa_start = mem_base; + numa_info->numa_nodes[i].ipa_size = mem_len; + memcpy(numa_info->numa_nodes[i].host_numa_nodes, ms->numa_state->nodes[i].node_memdev->host_nodes, + MAX_NODES / BITS_PER_LONG * sizeof(uint64_t)); + mem_base += mem_len; + } + } else { + numa_info->numa_cnt = 1; + numa_info->numa_nodes[0].numa_id = 0; + numa_info->numa_nodes[0].ipa_start = info->loader_start; + numa_info->numa_nodes[0].ipa_size = info->ram_size; + memset(numa_info->numa_nodes[0].host_numa_nodes, 0, MAX_NODES / BITS_PER_LONG * sizeof(uint64_t)); + } + + for (int cpu_idx = ms->smp.cpus - 1; cpu_idx >= 0; cpu_idx--) { + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu_idx)); + CPUState *local_cs = CPU(armcpu); + uint64_t node_id = 0; + if (ms->possible_cpus->cpus[local_cs->cpu_index].props.has_node_id) + node_id = ms->possible_cpus->cpus[local_cs->cpu_index].props.node_id; + bitmap_set((unsigned long *)numa_info->numa_nodes[node_id].cpu_id, cpu_idx, 1); + } + } /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { @@ -1239,6 +1283,11 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) arm_setup_direct_kernel_boot(cpu, info); } + if (kvm_enabled() && virtcca_cvm_enabled()) { + g_free(info->numa_info); + info->numa_info = NULL; + } + /* * Disable the PSCI conduit if it is set up to target the same * or a lower EL than the one we're going to start the guest code in. diff --git a/hw/arm/virt.c b/hw/arm/virt.c index be2856c018aa14c6ffda283cd7f99fb0d8e7a803..6087207f3859871f5d55dce2b86def64335c950f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -271,8 +271,16 @@ static void create_fdt(VirtMachineState *vms) /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); + + g_autofree char *kvm_type = NULL; + if (object_property_find(OBJECT(current_machine), "kvm-type")) { + kvm_type = object_property_get_str(OBJECT(current_machine), + "kvm-type", &error_abort); + } if (vms->dtb_randomness) { - create_randomness(ms, "/chosen"); + if (!(kvm_type && !strcmp(kvm_type, "cvm"))) { + create_randomness(ms, "/chosen"); + } } if (vms->secure) { @@ -1775,6 +1783,19 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) vms->memmap[i] = base_memmap[i]; } + /* fix VIRT_MEM range */ + if (object_property_find(OBJECT(current_machine), "kvm-type")) { + g_autofree char *kvm_type = object_property_get_str(OBJECT(current_machine), + "kvm-type", &error_abort); + + if (!strcmp(kvm_type, "cvm")) { + vms->memmap[VIRT_MEM].base = 3 * GiB; + vms->memmap[VIRT_MEM].size = ms->ram_size; + info_report("[qemu] fix VIRT_MEM range 0x%llx - 0x%llx\n", (unsigned long long)(vms->memmap[VIRT_MEM].base), + (unsigned long long)(vms->memmap[VIRT_MEM].base + ms->ram_size)); + } + } + if (ms->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported number of memory slots: %"PRIu64, ms->ram_slots); @@ -2103,7 +2124,7 @@ static void machvirt_init(MachineState *machine) */ if (vms->secure && firmware_loaded) { vms->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; - } else if (vms->virt) { + } else if (vms->virt || virtcca_cvm_enabled()) { vms->psci_conduit = QEMU_PSCI_CONDUIT_SMC; } else { vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC; @@ -2155,6 +2176,14 @@ static void machvirt_init(MachineState *machine) exit(1); } + if (virtcca_cvm_enabled()) { + int ret = kvm_arm_tmm_init(machine->cgs, &error_fatal); + if (ret != 0) { + error_report("fail to initialize TMM"); + exit(1); + } + } + create_fdt(vms); assert(possible_cpus->len == max_cpus); @@ -2901,6 +2930,15 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, static int virt_kvm_type(MachineState *ms, const char *type_str) { VirtMachineState *vms = VIRT_MACHINE(ms); + int virtcca_cvm_type = 0; + if (object_property_find(OBJECT(current_machine), "kvm-type")) { + g_autofree char *kvm_type = object_property_get_str(OBJECT(current_machine), + "kvm-type", &error_abort); + + if (!strcmp(kvm_type, "cvm")) { + virtcca_cvm_type = VIRTCCA_CVM_TYPE; + } + } int max_vm_pa_size, requested_pa_size; bool fixed_ipa; @@ -2930,7 +2968,9 @@ 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; + return strcmp(type_str, "cvm") == 0 ? + ((fixed_ipa ? 0 : requested_pa_size) | virtcca_cvm_type) : + (fixed_ipa ? 0 : requested_pa_size); } static void virt_machine_class_init(ObjectClass *oc, void *data) @@ -3101,6 +3141,19 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) } +static char *virt_get_kvm_type(Object *obj, Error **errp G_GNUC_UNUSED) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + return g_strdup(vms->kvm_type); +} + +static void virt_set_kvm_type(Object *obj, const char *value, Error **errp G_GNUC_UNUSED) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + g_free(vms->kvm_type); + vms->kvm_type = g_strdup(value); +} + static void virt_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -3158,6 +3211,9 @@ static void virt_instance_init(Object *obj) vms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); vms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); + + object_property_add_str(obj, "kvm-type", virt_get_kvm_type, virt_set_kvm_type); + object_property_set_description(obj, "kvm-type", "CVM or Normal VM"); } static const TypeInfo virt_machine_info = { diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 896feb37a1caa805543e971c150d3673675b9a6b..4f16e7ef773e80b39c90066fe257d81350bd06cf 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "sysemu/kvm.h" #include "qapi/error.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio.h" @@ -81,6 +82,11 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) vdev->dma_as = &address_space_memory; if (has_iommu) { vdev_has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); + + if (virtcca_cvm_enabled() && (strcmp(vdev->name, "virtio-user-fs") == 0)) { + vdev_has_iommu = true; + } + /* * Present IOMMU_PLATFORM to the driver iff iommu_plattform=on and * device operational. If the driver does not accept IOMMU_PLATFORM diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index 80c492d7421e840174acadd71d9e525fa84fcc86..2329e1a72346589dfd49cf642d38aae73d333777 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -39,6 +39,7 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, /* arm_boot.c */ struct arm_boot_info { uint64_t ram_size; + void *numa_info; const char *kernel_filename; const char *kernel_cmdline; const char *initrd_filename; diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index f69239850e617332ec3ed223edcf4a69019ec973..1fced5e87697ebfbd1b065932b1adf077619c9b2 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -173,6 +173,7 @@ struct VirtMachineState { PCIBus *bus; char *oem_id; char *oem_table_id; + char *kvm_type; }; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 4d00ade5fa3f96b31bcf2af9564deadf6a18fdfb..7d08aae9fa7862438c06ee80a525ed7c85799a1a 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -19,6 +19,7 @@ #include "exec/memattrs.h" #include "qemu/accel.h" #include "qom/object.h" +#include "linux-headers/linux/kvm.h" #ifdef NEED_CPU_H # ifdef CONFIG_KVM @@ -32,6 +33,7 @@ #ifdef CONFIG_KVM_IS_POSSIBLE extern bool kvm_allowed; +extern bool virtcca_cvm_allowed; extern bool kvm_kernel_irqchip; extern bool kvm_split_irqchip; extern bool kvm_async_interrupts_allowed; @@ -45,6 +47,8 @@ extern bool kvm_msi_use_devid; extern bool kvm_csv3_allowed; #define kvm_enabled() (kvm_allowed) +#define virtcca_cvm_enabled() (virtcca_cvm_allowed) +#define VIRTCCA_CVM_TYPE (1UL << 8) /** * kvm_irqchip_in_kernel: * @@ -153,6 +157,8 @@ extern bool kvm_csv3_allowed; #else #define kvm_enabled() (0) +#define virtcca_cvm_enabled() (0) +#define VIRTCCA_CVM_TYPE (0) #define kvm_irqchip_in_kernel() (false) #define kvm_irqchip_is_split() (false) #define kvm_async_interrupts_enabled() (false) @@ -571,4 +577,7 @@ bool kvm_arch_cpu_check_are_resettable(void); bool kvm_dirty_ring_enabled(void); uint32_t kvm_dirty_ring_size(void); + +int kvm_load_user_data(hwaddr loader_start, hwaddr image_end, hwaddr initrd_start, hwaddr dtb_end, hwaddr ram_size, + struct kvm_numa_info *numa_info); #endif diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index c59ea55cd8eb02b4bf52aa7e984831af7e829450..2b040b5d608bed0f2bb4600f31d427fbadec6967 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -110,6 +110,7 @@ struct kvm_regs { #define KVM_ARM_VCPU_PTRAUTH_ADDRESS 5 /* VCPU uses address authentication */ #define KVM_ARM_VCPU_PTRAUTH_GENERIC 6 /* VCPU uses generic authentication */ #define KVM_ARM_VCPU_HAS_EL2 7 /* Support nested virtualization */ +#define KVM_ARM_VCPU_TEC 8 /* VCPU TEC state as part of cvm */ struct kvm_vcpu_init { __u32 target; @@ -523,6 +524,67 @@ struct reg_mask_range { __u32 reserved[13]; }; +/* KVM_CAP_ARM_TMM on VM fd */ +#define KVM_CAP_ARM_TMM_CONFIG_CVM 0 +#define KVM_CAP_ARM_TMM_CREATE_RD 1 +#define KVM_CAP_ARM_TMM_POPULATE_CVM 2 +#define KVM_CAP_ARM_TMM_ACTIVATE_CVM 3 + +#define KVM_CAP_ARM_TMM_MEASUREMENT_ALGO_SHA256 0 +#define KVM_CAP_ARM_TMM_MEASUREMENT_ALGO_SHA512 1 + +#define KVM_CAP_ARM_TMM_RPV_SIZE 64 + +/* List of configuration items accepted for KVM_CAP_ARM_RME_CONFIG_REALM */ +#define KVM_CAP_ARM_TMM_CFG_RPV 0 +#define KVM_CAP_ARM_TMM_CFG_HASH_ALGO 1 +#define KVM_CAP_ARM_TMM_CFG_SVE 2 +#define KVM_CAP_ARM_TMM_CFG_DBG 3 +#define KVM_CAP_ARM_TMM_CFG_PMU 4 + +struct kvm_cap_arm_tmm_config_item { + __u32 cfg; + union { + /* cfg == KVM_CAP_ARM_TMM_CFG_RPV */ + struct { + __u8 rpv[KVM_CAP_ARM_TMM_RPV_SIZE]; + }; + + /* cfg == KVM_CAP_ARM_TMM_CFG_HASH_ALGO */ + struct { + __u32 hash_algo; + }; + + /* cfg == KVM_CAP_ARM_TMM_CFG_SVE */ + struct { + __u32 sve_vq; + }; + + /* cfg == KVM_CAP_ARM_TMM_CFG_DBG */ + struct { + __u32 num_brps; + __u32 num_wrps; + }; + + /* cfg == KVM_CAP_ARM_TMM_CFG_PMU */ + struct { + __u32 num_pmu_cntrs; + }; + /* Fix the size of the union */ + __u8 reserved[256]; + }; +}; + +#define KVM_ARM_TMM_POPULATE_FLAGS_MEASURE (1U << 0) +struct kvm_cap_arm_tmm_populate_region_args { + __u64 populate_ipa_base1; + __u64 populate_ipa_size1; + __u64 populate_ipa_base2; + __u64 populate_ipa_size2; + __u32 flags; + __u32 reserved[3]; +}; + #endif #endif /* __ARM_KVM_H__ */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index f390989e7c7118b118bf805701e8b3cf34c48726..c75e4cde4806cae930839d581870a95cdd30607a 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -14,6 +14,8 @@ #include #include +#include "sysemu/numa.h" + #define KVM_API_VERSION 12 /* *** Deprecated interfaces *** */ @@ -1203,6 +1205,8 @@ struct kvm_ppc_resize_hpt { #define KVM_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE) +#define KVM_CAP_ARM_TMM 300 + #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing_irqchip { @@ -1478,6 +1482,32 @@ struct kvm_vfio_spapr_tce { __s32 tablefd; }; +#define MAX_NUMA_NODE 8 +#define MAX_CPU_BIT_MAP 4 +#define MAX_NODE_BIT_MAP (MAX_NODES / BITS_PER_LONG) + +struct kvm_numa_node { + __u64 numa_id; + __u64 ipa_start; + __u64 ipa_size; + __u64 host_numa_nodes[MAX_NODE_BIT_MAP]; + __u64 cpu_id[MAX_CPU_BIT_MAP]; +}; + +struct kvm_numa_info { + __u64 numa_cnt; + struct kvm_numa_node numa_nodes[MAX_NUMA_NODE]; +}; + +struct kvm_user_data { + __u64 loader_start; + __u64 image_end; + __u64 initrd_start; + __u64 dtb_end; + __u64 ram_size; + struct kvm_numa_info numa_info; +}; + /* * KVM_CREATE_VCPU receives as a parameter the vcpu slot, and returns * a vcpu fd. @@ -1490,7 +1520,7 @@ struct kvm_vfio_spapr_tce { struct kvm_userspace_memory_region) #define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47) #define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64) - +#define KVM_LOAD_USER_DATA _IOW(KVMIO, 0x49, struct kvm_user_data) /* enable ucontrol for s390 */ struct kvm_s390_ucas_mapping { __u64 user_addr; diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 58da696ff7afee186fa5ec464c15d1f8e468c666..a2675518e062c042b443723dc664a1101edc1f69 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -30,6 +30,7 @@ #include "sysemu/runstate.h" #include "ui/qemu-spice.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" #include "options.h" #include "migration.h" @@ -416,6 +417,11 @@ void hmp_loadvm(Monitor *mon, const QDict *qdict) const char *name = qdict_get_str(qdict, "name"); Error *err = NULL; + if (virtcca_cvm_enabled()) { + error_setg(&err, "The loadvm command is temporarily unsupported in cvm."); + return; + } + vm_stop(RUN_STATE_RESTORE_VM); if (load_snapshot(name, NULL, false, NULL, &err) && saved_vm_running) { diff --git a/migration/savevm.c b/migration/savevm.c index eec5503a422081a5e2fce7238820ec6fc8db1b8b..cf88057efacb06d17a4b1f1b1f26913de8a99335 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -61,6 +61,7 @@ #include "sysemu/replay.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" #include "sysemu/xen.h" #include "migration/colo.h" #include "qemu/bitmap.h" @@ -3042,6 +3043,11 @@ int qemu_loadvm_approve_switchover(void) bool save_snapshot(const char *name, bool overwrite, const char *vmstate, bool has_devices, strList *devices, Error **errp) { + if (virtcca_cvm_enabled()) { + error_setg(errp, "The savevm command is temporarily unsupported in cvm."); + return false; + } + BlockDriverState *bs; QEMUSnapshotInfo sn1, *sn = &sn1; int ret = -1, ret2; diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index b0f948d33766bb781b5bb1cfdfacb3d190828b9a..9737f23e48fe9a109d9acf199da8dab3520b3949 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -23,6 +23,7 @@ #include "sysemu/runstate.h" #include "sysemu/runstate-action.h" #include "sysemu/block-backend.h" +#include "sysemu/kvm.h" #include "qapi/error.h" #include "qapi/qapi-init-commands.h" #include "qapi/qapi-commands-control.h" @@ -49,6 +50,11 @@ void qmp_quit(Error **errp) void qmp_stop(Error **errp) { + if (virtcca_cvm_enabled()) { + error_setg(errp, "The stop command is temporarily unsupported in cvm."); + return; + } + /* if there is a dump in background, we should wait until the dump * finished */ if (qemu_system_dump_in_progress()) { diff --git a/qapi/qom.json b/qapi/qom.json index 89a2516b42e15f1643f1382ed365927ec1038a81..0853944ba8be8d91c218c53bdb6dc930d680f090 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -902,6 +902,29 @@ 'data': { '*cpu-affinity': ['uint16'], '*node-affinity': ['uint16'] } } +## +# @TmmGuestMeasurementAlgo: +# +# Algorithm to use for cvm measurements +# +# Since: FIXME +## +{ 'enum': 'TmmGuestMeasurementAlgo', +'data': ['default', 'sha256', 'sha512'] } + +## +# @TmmGuestProperties: +# +# Properties for tmm-guest objects. +# +# @sve-vector-length: SVE vector length (default: 0, SVE disabled) +# +# Since: FIXME +## +{ 'struct': 'TmmGuestProperties', + 'data': { '*sve-vector-length': 'uint32', + '*num-pmu-counters': 'uint32', + '*measurement-algo': 'TmmGuestMeasurementAlgo' } } ## # @ObjectType: @@ -965,7 +988,8 @@ 'tls-creds-x509', 'tls-cipher-suites', { 'name': 'x-remote-object', 'features': [ 'unstable' ] }, - { 'name': 'x-vfio-user-server', 'features': [ 'unstable' ] } + { 'name': 'x-vfio-user-server', 'features': [ 'unstable' ] }, + 'tmm-guest' ] } ## @@ -1032,7 +1056,8 @@ 'tls-creds-x509': 'TlsCredsX509Properties', 'tls-cipher-suites': 'TlsCredsProperties', 'x-remote-object': 'RemoteObjectProperties', - 'x-vfio-user-server': 'VfioUserServerProperties' + 'x-vfio-user-server': 'VfioUserServerProperties', + 'tmm-guest': 'TmmGuestProperties' } } ## diff --git a/target/arm/kvm-tmm.c b/target/arm/kvm-tmm.c new file mode 100644 index 0000000000000000000000000000000000000000..efe2ca000697f7c5e24807c3952ecb99d0b82b10 --- /dev/null +++ b/target/arm/kvm-tmm.c @@ -0,0 +1,344 @@ +/* + * QEMU add virtcca cvm feature. + * + * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "exec/confidential-guest-support.h" +#include "hw/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 "sysemu/kvm.h" +#include "sysemu/runstate.h" +#include "hw/loader.h" + +#define TYPE_TMM_GUEST "tmm-guest" +OBJECT_DECLARE_SIMPLE_TYPE(TmmGuest, TMM_GUEST) + +#define TMM_PAGE_SIZE qemu_real_host_page_size() +#define TMM_MAX_PMU_CTRS 0x20 +#define TMM_MAX_CFG 5 + +struct TmmGuest { + ConfidentialGuestSupport parent_obj; + GSList *ram_regions; + TmmGuestMeasurementAlgo measurement_algo; + uint32_t sve_vl; + uint32_t num_pmu_cntrs; +}; + +typedef struct { + hwaddr base1; + hwaddr len1; + hwaddr base2; + hwaddr len2; + bool populate; +} TmmRamRegion; + +static TmmGuest *tmm_guest; + +bool kvm_arm_tmm_enabled(void) +{ + return !!tmm_guest; +} + +static int tmm_configure_one(TmmGuest *guest, uint32_t cfg, Error **errp) +{ + int ret = 1; + const char *cfg_str; + struct kvm_cap_arm_tmm_config_item args = { + .cfg = cfg, + }; + + switch (cfg) { + case KVM_CAP_ARM_TMM_CFG_RPV: + return 0; + case KVM_CAP_ARM_TMM_CFG_HASH_ALGO: + switch (guest->measurement_algo) { + case TMM_GUEST_MEASUREMENT_ALGO_DEFAULT: + return 0; + case TMM_GUEST_MEASUREMENT_ALGO_SHA256: + args.hash_algo = KVM_CAP_ARM_TMM_MEASUREMENT_ALGO_SHA256; + break; + case TMM_GUEST_MEASUREMENT_ALGO_SHA512: + args.hash_algo = KVM_CAP_ARM_TMM_MEASUREMENT_ALGO_SHA512; + break; + default: + g_assert_not_reached(); + } + cfg_str = "hash algorithm"; + break; + case KVM_CAP_ARM_TMM_CFG_SVE: + if (!guest->sve_vl) { + return 0; + } + args.sve_vq = guest->sve_vl / 128; + cfg_str = "SVE"; + break; + case KVM_CAP_ARM_TMM_CFG_DBG: + return 0; + case KVM_CAP_ARM_TMM_CFG_PMU: + if (!guest->num_pmu_cntrs) { + return 0; + } + args.num_pmu_cntrs = guest->num_pmu_cntrs; + cfg_str = "PMU"; + break; + default: + g_assert_not_reached(); + } + + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_TMM, 0, + KVM_CAP_ARM_TMM_CONFIG_CVM, (intptr_t)&args); + if (ret) { + error_setg_errno(errp, -ret, "TMM: failed to configure %s", cfg_str); + } + + return ret; +} + +static gint tmm_compare_ram_regions(gconstpointer a, gconstpointer b) +{ + const TmmRamRegion *ra = a; + const TmmRamRegion *rb = b; + + g_assert(ra->base1 != rb->base1); + return ra->base1 < rb->base1 ? -1 : 1; +} + +void tmm_add_ram_region(hwaddr base1, hwaddr len1, hwaddr base2, hwaddr len2, bool populate) +{ + TmmRamRegion *region; + + region = g_new0(TmmRamRegion, 1); + region->base1 = QEMU_ALIGN_DOWN(base1, TMM_PAGE_SIZE); + region->len1 = QEMU_ALIGN_UP(len1, TMM_PAGE_SIZE); + region->base2 = QEMU_ALIGN_DOWN(base2, TMM_PAGE_SIZE); + region->len2 = QEMU_ALIGN_UP(len2, TMM_PAGE_SIZE); + region->populate = populate; + + tmm_guest->ram_regions = g_slist_insert_sorted(tmm_guest->ram_regions, + region, tmm_compare_ram_regions); +} + +static void tmm_populate_region(gpointer data, gpointer unused) +{ + int ret; + const TmmRamRegion *region = data; + struct kvm_cap_arm_tmm_populate_region_args populate_args = { + .populate_ipa_base1 = region->base1, + .populate_ipa_size1 = region->len1, + .populate_ipa_base2 = region->base2, + .populate_ipa_size2 = region->len2, + .flags = KVM_ARM_TMM_POPULATE_FLAGS_MEASURE, + }; + + if (!region->populate) { + return; + } + + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_TMM, 0, + KVM_CAP_ARM_TMM_POPULATE_CVM, + (intptr_t)&populate_args); + if (ret) { + error_report("TMM: failed to populate cvm region (0x%"HWADDR_PRIx", 0x%"HWADDR_PRIx", 0x%"HWADDR_PRIx", 0x%"HWADDR_PRIx"): %s", + region->base1, region->len1, region->base2, region->len2, strerror(-ret)); + exit(1); + } +} + +static int tmm_create_rd(Error **errp) +{ + int ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_TMM, 0, + KVM_CAP_ARM_TMM_CREATE_RD); + if (ret) { + error_setg_errno(errp, -ret, "TMM: failed to create tmm Descriptor"); + } + return ret; +} + +static void tmm_vm_state_change(void *opaque, bool running, RunState state) +{ + int ret; + CPUState *cs; + + if (!running) { + return; + } + + g_slist_foreach(tmm_guest->ram_regions, tmm_populate_region, NULL); + g_slist_free_full(g_steal_pointer(&tmm_guest->ram_regions), g_free); + + CPU_FOREACH(cs) { + ret = kvm_arm_vcpu_finalize(cs, KVM_ARM_VCPU_TEC); + if (ret) { + error_report("TMM: failed to finalize vCPU: %s", strerror(-ret)); + exit(1); + } + } + + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_TMM, 0, + KVM_CAP_ARM_TMM_ACTIVATE_CVM); + if (ret) { + error_report("TMM: failed to activate cvm: %s", strerror(-ret)); + exit(1); + } +} + +int kvm_arm_tmm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + int ret; + int cfg; + + if (!tmm_guest) { + return -ENODEV; + } + + if (!kvm_check_extension(kvm_state, KVM_CAP_ARM_TMM)) { + error_setg(errp, "KVM does not support TMM"); + return -ENODEV; + } + + for (cfg = 0; cfg < TMM_MAX_CFG; cfg++) { + ret = tmm_configure_one(tmm_guest, cfg, &error_abort); + if (ret) { + return ret; + } + } + + ret = tmm_create_rd(&error_abort); + if (ret) { + return ret; + } + + qemu_add_vm_change_state_handler(tmm_vm_state_change, NULL); + return 0; +} + +static void tmm_get_sve_vl(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TmmGuest *guest = TMM_GUEST(obj); + + visit_type_uint32(v, name, &guest->sve_vl, errp); +} + +static void tmm_set_sve_vl(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TmmGuest *guest = TMM_GUEST(obj); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + if (value & 0x7f || value >= ARM_MAX_VQ * 128) { + error_setg(errp, "invalid SVE vector length"); + return; + } + + guest->sve_vl = value; +} + +static void tmm_get_num_pmu_cntrs(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TmmGuest *guest = TMM_GUEST(obj); + + visit_type_uint32(v, name, &guest->num_pmu_cntrs, errp); +} + +static void tmm_set_num_pmu_cntrs(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TmmGuest *guest = TMM_GUEST(obj); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + if (value >= TMM_MAX_PMU_CTRS) { + error_setg(errp, "invalid number of PMU counters"); + return; + } + + guest->num_pmu_cntrs = value; +} + +static int tmm_get_measurement_algo(Object *obj, Error **errp G_GNUC_UNUSED) +{ + TmmGuest *guest = TMM_GUEST(obj); + + return guest->measurement_algo; +} + +static void tmm_set_measurement_algo(Object *obj, int algo, Error **errp G_GNUC_UNUSED) +{ + TmmGuest *guest = TMM_GUEST(obj); + + guest->measurement_algo = algo; +} + +static void tmm_guest_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_enum(oc, "measurement-algo", + "TmmGuestMeasurementAlgo", + &TmmGuestMeasurementAlgo_lookup, + tmm_get_measurement_algo, + tmm_set_measurement_algo); + object_class_property_set_description(oc, "measurement-algo", + "cvm measurement algorithm ('sha256', 'sha512')"); + /* + * This is not ideal. Normally SVE parameters are given to -cpu, but the + * cvm parameters are needed much earlier than CPU initialization. We also + * don't have a way to discover what is supported at the moment, the idea is + * that the user knows exactly what hardware it is running on because these + * parameters are part of the measurement and play in the attestation. + */ + object_class_property_add(oc, "sve-vector-length", "uint32", tmm_get_sve_vl, + tmm_set_sve_vl, NULL, NULL); + object_class_property_set_description(oc, "sve-vector-length", + "SVE vector length. 0 disables SVE (the default)"); + object_class_property_add(oc, "num-pmu-counters", "uint32", + tmm_get_num_pmu_cntrs, tmm_set_num_pmu_cntrs, + NULL, NULL); + object_class_property_set_description(oc, "num-pmu-counters", + "Number of PMU counters"); +} + +static void tmm_guest_instance_init(Object *obj) +{ + if (tmm_guest) { + error_report("a single instance of TmmGuest is supported"); + exit(1); + } + tmm_guest = TMM_GUEST(obj); +} + +static const TypeInfo tmm_guest_info = { + .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, + .name = TYPE_TMM_GUEST, + .instance_size = sizeof(struct TmmGuest), + .instance_init = tmm_guest_instance_init, + .class_init = tmm_guest_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void tmm_register_types(void) +{ + type_register_static(&tmm_guest_info); +} +type_init(tmm_register_types); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 7903e2ddde1b70bbd0db7f27992afe86763899d5..a42ddcc855eb9cf9dde0b264e8d45ba0433b583f 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -592,6 +592,10 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) continue; } + if (virtcca_cvm_enabled() && regidx == KVM_REG_ARM_TIMER_CNT) { + continue; + } + switch (regidx & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U32: v32 = cpu->cpreg_values[i]; @@ -1071,7 +1075,7 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) bool kvm_arch_cpu_check_are_resettable(void) { - return true; + return !virtcca_cvm_enabled(); } static void kvm_arch_get_eager_split_size(Object *obj, Visitor *v, diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 3c175c93a7a8f29a7a157c9bf059db601f546b7f..a09347254f6cedcab5bf3eb89a7f7c6a4325001f 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -543,6 +543,11 @@ static int kvm_arm_sve_set_vls(CPUState *cs) assert(cpu->sve_max_vq <= KVM_ARM64_SVE_VQ_MAX); + if (virtcca_cvm_enabled()) { + /* Already set through tmm config */ + return 0; + } + return kvm_set_one_reg(cs, KVM_REG_ARM64_SVE_VLS, &vls[0]); } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 051a0da41c4929c178134f2b2a67622e84ef42b5..9fd0a520fe8e1aa7652562903a727af655dfc437 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -377,6 +377,11 @@ void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa); int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); +void tmm_add_ram_region(hwaddr base1, hwaddr len1, hwaddr base2, hwaddr len2, bool populate); + +int kvm_arm_tmm_init(ConfidentialGuestSupport *cgs, Error **errp); +bool kvm_arm_tmm_enabled(void); + #else /* @@ -451,6 +456,16 @@ static inline uint32_t kvm_arm_sve_get_vls(CPUState *cs) g_assert_not_reached(); } +static inline int kvm_arm_tmm_init(ConfidentialGuestSupport *cgs, Error **errp G_GNUC_UNUSED) +{ + g_assert_not_reached(); +} + +static inline void tmm_add_ram_region(hwaddr base1, hwaddr len1, hwaddr base2, + hwaddr len2, bool populate) +{ + g_assert_not_reached(); +} #endif /** diff --git a/target/arm/meson.build b/target/arm/meson.build index 5d04a8e94f2ebace7dd56ee3c92923b4e1b8d55d..ee1ec5a5ff2c09ef4c1ad76f2df2a34a8133c02d 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -10,6 +10,7 @@ arm_ss.add(zlib) arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c')) arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) +arm_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c', 'kvm64.c', 'kvm-tmm.c'), if_false: files('kvm-stub.c')) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c',