diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 5164d838b9..e72a19aaf8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -123,10 +123,6 @@ struct KVMState KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; - /* memory encryption */ - void *memcrypt_handle; - int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); - /* For "info mtree -f" to tell if an MR is registered in KVM */ int nr_as; struct KVMAs { @@ -225,26 +221,6 @@ int kvm_get_max_memslots(void) return s->nr_slots; } -bool kvm_memcrypt_enabled(void) -{ - if (kvm_state && kvm_state->memcrypt_handle) { - return true; - } - - return false; -} - -int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) -{ - if (kvm_state->memcrypt_handle && - kvm_state->memcrypt_encrypt_data) { - return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, - ptr, len); - } - - return 1; -} - /* Called with KVMMemoryListener.slots_lock held */ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { @@ -2204,20 +2180,6 @@ static int kvm_init(MachineState *ms) kvm_state = s; - /* - * if memory encryption object is specified then initialize the memory - * encryption context. - */ - if (ms->memory_encryption) { - kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); - if (!kvm_state->memcrypt_handle) { - ret = -1; - goto err; - } - - kvm_state->memcrypt_encrypt_data = sev_encrypt_data; - } - ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; diff --git a/accel/kvm/sev-stub.c b/accel/kvm/sev-stub.c index 4f97452585..9587d1b2a3 100644 --- a/accel/kvm/sev-stub.c +++ b/accel/kvm/sev-stub.c @@ -15,12 +15,8 @@ #include "qemu-common.h" #include "sysemu/sev.h" -int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - abort(); -} - -void *sev_guest_init(const char *id) -{ - return NULL; + /* If we get here, cgs must be some non-SEV thing */ + return 0; } diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 680e099463..0f17acfac0 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -81,16 +81,6 @@ int kvm_on_sigbus(int code, void *addr) return 1; } -bool kvm_memcrypt_enabled(void) -{ - return false; -} - -int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) -{ - return 1; -} - #ifndef CONFIG_USER_ONLY int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) { diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c new file mode 100644 index 0000000000..052fde8db0 --- /dev/null +++ b/backends/confidential-guest-support.c @@ -0,0 +1,33 @@ +/* + * QEMU Confidential Guest support + * + * Copyright Red Hat. + * + * Authors: + * David Gibson + * + * 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" + +OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, + confidential_guest_support, + CONFIDENTIAL_GUEST_SUPPORT, + OBJECT) + +static void confidential_guest_support_class_init(ObjectClass *oc, void *data) +{ +} + +static void confidential_guest_support_init(Object *obj) +{ +} + +static void confidential_guest_support_finalize(Object *obj) +{ +} diff --git a/backends/meson.build b/backends/meson.build index 484456ece7..d4221831fc 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -6,6 +6,7 @@ softmmu_ss.add([files( 'rng-builtin.c', 'rng-egd.c', 'rng.c', + 'confidential-guest-support.c', ), numa]) softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c')) diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt index 80b8eb00e9..145896aec7 100644 --- a/docs/amd-memory-encryption.txt +++ b/docs/amd-memory-encryption.txt @@ -73,7 +73,7 @@ complete flow chart. To launch a SEV guest # ${QEMU} \ - -machine ...,memory-encryption=sev0 \ + -machine ...,confidential-guest-support=sev0 \ -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 Debugging diff --git a/docs/confidential-guest-support.txt b/docs/confidential-guest-support.txt new file mode 100644 index 0000000000..71d07ba57a --- /dev/null +++ b/docs/confidential-guest-support.txt @@ -0,0 +1,49 @@ +Confidential Guest Support +========================== + +Traditionally, hypervisors such as QEMU have complete access to a +guest's memory and other state, meaning that a compromised hypervisor +can compromise any of its guests. A number of platforms have added +mechanisms in hardware and/or firmware which give guests at least some +protection from a compromised hypervisor. This is obviously +especially desirable for public cloud environments. + +These mechanisms have different names and different modes of +operation, but are often referred to as Secure Guests or Confidential +Guests. We use the term "Confidential Guest Support" to distinguish +this from other aspects of guest security (such as security against +attacks from other guests, or from network sources). + +Running a Confidential Guest +---------------------------- + +To run a confidential guest you need to add two command line parameters: + +1. Use "-object" to create a "confidential guest support" object. The + type and parameters will vary with the specific mechanism to be + used +2. Set the "confidential-guest-support" machine parameter to the ID of + the object from (1). + +Example (for AMD SEV):: + + qemu-system-x86_64 \ + \ + -machine ...,confidential-guest-support=sev0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 + +Supported mechanisms +-------------------- + +Currently supported confidential guest mechanisms are: + +AMD Secure Encrypted Virtualization (SEV) + docs/amd-memory-encryption.txt + +POWER Protected Execution Facility (PEF) + docs/papr-pef.txt + +s390x Protected Virtualization (PV) + docs/system/s390x/protvirt.rst + +Other mechanisms may be supported in future. diff --git a/docs/papr-pef.txt b/docs/papr-pef.txt new file mode 100644 index 0000000000..72550e9bf8 --- /dev/null +++ b/docs/papr-pef.txt @@ -0,0 +1,30 @@ +POWER (PAPR) Protected Execution Facility (PEF) +=============================================== + +Protected Execution Facility (PEF), also known as Secure Guest support +is a feature found on IBM POWER9 and POWER10 processors. + +If a suitable firmware including an Ultravisor is installed, it adds +an extra memory protection mode to the CPU. The ultravisor manages a +pool of secure memory which cannot be accessed by the hypervisor. + +When this feature is enabled in QEMU, a guest can use ultracalls to +enter "secure mode". This transfers most of its memory to secure +memory, where it cannot be eavesdropped by a compromised hypervisor. + +Launching +--------- + +To launch a guest which will be permitted to enter PEF secure mode: + +# ${QEMU} \ + -object pef-guest,id=pef0 \ + -machine confidential-guest-support=pef0 \ + ... + +Live Migration +---------------- + +Live migration is not yet implemented for PEF guests. For +consistency, we currently prevent migration if the PEF feature is +enabled, whether or not the guest has actually entered secure mode. diff --git a/docs/system/s390x/protvirt.rst b/docs/system/s390x/protvirt.rst index 712974ad87..0f481043d9 100644 --- a/docs/system/s390x/protvirt.rst +++ b/docs/system/s390x/protvirt.rst @@ -22,15 +22,22 @@ If those requirements are met, the capability `KVM_CAP_S390_PROTECTED` will indicate that KVM can support PVMs on that LPAR. -QEMU Settings -------------- +Running a Protected Virtual Machine +----------------------------------- -To indicate to the VM that it can transition into protected mode, the +To run a PVM you will need to select a CPU model which includes the `Unpack facility` (stfle bit 161 represented by the feature -`unpack`/`S390_FEAT_UNPACK`) needs to be part of the cpu model of -the VM. +`unpack`/`S390_FEAT_UNPACK`), and add these options to the command line:: + + -object s390-pv-guest,id=pv0 \ + -machine confidential-guest-support=pv0 + +Adding these options will: + +* Ensure the `unpack` facility is available +* Enable the IOMMU by default for all I/O devices +* Initialize the PV mechanism -All I/O devices need to use the IOMMU. Passthrough (vfio) devices are currently not supported. Host huge page backings are not supported. However guests can use huge diff --git a/hw/core/machine.c b/hw/core/machine.c index 5d6163ab70..970046f438 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -32,6 +32,9 @@ #include "hw/mem/nvdimm.h" #include "migration/global_state.h" #include "migration/vmstate.h" +#include "exec/confidential-guest-support.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-pci.h" GlobalProperty hw_compat_5_2[] = {}; const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2); @@ -427,24 +430,37 @@ static char *machine_get_memory_encryption(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); - return g_strdup(ms->memory_encryption); + if (ms->cgs) { + return g_strdup(object_get_canonical_path_component(OBJECT(ms->cgs))); + } + + return NULL; } static void machine_set_memory_encryption(Object *obj, const char *value, Error **errp) { - MachineState *ms = MACHINE(obj); + Object *cgs = + object_resolve_path_component(object_get_objects_root(), value); - g_free(ms->memory_encryption); - ms->memory_encryption = g_strdup(value); - - /* - * With memory encryption, the host can't see the real contents of RAM, - * so there's no point in it trying to merge areas. - */ - if (value) { - machine_set_mem_merge(obj, false, errp); + if (!cgs) { + error_setg(errp, "No such memory encryption object '%s'", value); + return; } + + object_property_set_link(obj, "confidential-guest-support", cgs, errp); +} + +static void machine_check_confidential_guest_support(const Object *obj, + const char *name, + Object *new_target, + Error **errp) +{ + /* + * So far the only constraint is that the target has the + * TYPE_CONFIDENTIAL_GUEST_SUPPORT interface, and that's checked + * by the QOM core + */ } static bool machine_get_nvdimm(Object *obj, Error **errp) @@ -844,6 +860,15 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "suppress-vmdesc", "Set on to disable self-describing migration"); + object_class_property_add_link(oc, "confidential-guest-support", + TYPE_CONFIDENTIAL_GUEST_SUPPORT, + offsetof(MachineState, cgs), + machine_check_confidential_guest_support, + OBJ_PROP_LINK_STRONG); + object_class_property_set_description(oc, "confidential-guest-support", + "Set confidential guest scheme to support"); + + /* For compatibility */ object_class_property_add_str(oc, "memory-encryption", machine_get_memory_encryption, machine_set_memory_encryption); object_class_property_set_description(oc, "memory-encryption", @@ -1166,6 +1191,26 @@ void machine_run_board_init(MachineState *machine) cc->deprecation_note); } + if (machine->cgs) { + /* + * With confidential guests, the host can't see the real + * contents of RAM, so there's no point in it trying to merge + * areas. + */ + machine_set_mem_merge(OBJECT(machine), false, &error_abort); + + /* + * Virtio devices can't count on directly accessing guest + * memory, so they need iommu_platform=on to use normal DMA + * mechanisms. That requires also disabling legacy virtio + * support for those virtio pci devices which allow it. + */ + object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy", + "on", true); + object_register_sugar_prop(TYPE_VIRTIO_DEVICE, "iommu_platform", + "on", false); + } + machine_class->init(machine); phase_advance(PHASE_MACHINE_INITIALIZED); } diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 92e90ff013..11172214f1 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -38,6 +38,7 @@ #include "sysemu/sysemu.h" #include "hw/block/flash.h" #include "sysemu/kvm.h" +#include "sysemu/sev.h" #define FLASH_SECTOR_SIZE 4096 @@ -147,7 +148,7 @@ static void pc_system_flash_map(PCMachineState *pcms, PFlashCFI01 *system_flash; MemoryRegion *flash_mem; void *flash_ptr; - int ret, flash_size; + int flash_size; assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); @@ -191,16 +192,10 @@ static void pc_system_flash_map(PCMachineState *pcms, flash_mem = pflash_cfi01_get_memory(system_flash); pc_isa_bios_init(rom_memory, flash_mem, size); - /* Encrypt the pflash boot ROM */ - if (kvm_memcrypt_enabled()) { - flash_ptr = memory_region_get_ram_ptr(flash_mem); - flash_size = memory_region_size(flash_mem); - ret = kvm_memcrypt_encrypt_data(flash_ptr, flash_size); - if (ret) { - error_report("failed to encrypt pflash rom"); - exit(1); - } - } + /* Encrypt the pflash boot ROM, if necessary */ + flash_ptr = memory_region_get_ram_ptr(flash_mem); + flash_size = memory_region_size(flash_mem); + sev_encrypt_flash(flash_ptr, flash_size, &error_fatal); } } } diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index ffa2ec37fa..218631c883 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -27,6 +27,7 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files( 'spapr_nvdimm.c', 'spapr_rtas_ddw.c', 'spapr_numa.c', + 'pef.c', )) ppc_ss.add(when: 'CONFIG_SPAPR_RNG', if_true: files('spapr_rng.c')) ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_LINUX'], if_true: files( diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c new file mode 100644 index 0000000000..573be3ed79 --- /dev/null +++ b/hw/ppc/pef.c @@ -0,0 +1,140 @@ +/* + * PEF (Protected Execution Facility) for POWER support + * + * Copyright Red Hat. + * + * 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 "qapi/error.h" +#include "qom/object_interfaces.h" +#include "sysemu/kvm.h" +#include "migration/blocker.h" +#include "exec/confidential-guest-support.h" +#include "hw/ppc/pef.h" + +#define TYPE_PEF_GUEST "pef-guest" +OBJECT_DECLARE_SIMPLE_TYPE(PefGuest, PEF_GUEST) + +typedef struct PefGuest PefGuest; +typedef struct PefGuestClass PefGuestClass; + +struct PefGuestClass { + ConfidentialGuestSupportClass parent_class; +}; + +/** + * PefGuest: + * + * The PefGuest object is used for creating and managing a PEF + * guest. + * + * # $QEMU \ + * -object pef-guest,id=pef0 \ + * -machine ...,confidential-guest-support=pef0 + */ +struct PefGuest { + ConfidentialGuestSupport parent_obj; +}; + +static int kvmppc_svm_init(Error **errp) +{ +#ifdef CONFIG_KVM + static Error *pef_mig_blocker; + + if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_SECURE_GUEST)) { + error_setg(errp, + "KVM implementation does not support Secure VMs (is an ultravisor running?)"); + return -1; + } else { + int ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_SECURE_GUEST, 0, 1); + + if (ret < 0) { + error_setg(errp, + "Error enabling PEF with KVM"); + return -1; + } + } + + /* add migration blocker */ + error_setg(&pef_mig_blocker, "PEF: Migration is not implemented"); + /* NB: This can fail if --only-migratable is used */ + migrate_add_blocker(pef_mig_blocker, &error_fatal); + + return 0; +#else + g_assert_not_reached(); +#endif +} + +/* + * Don't set error if KVM_PPC_SVM_OFF ioctl is invoked on kernels + * that don't support this ioctl. + */ +static int kvmppc_svm_off(Error **errp) +{ +#ifdef CONFIG_KVM + int rc; + + rc = kvm_vm_ioctl(KVM_STATE(current_accel()), KVM_PPC_SVM_OFF); + if (rc && rc != -ENOTTY) { + error_setg_errno(errp, -rc, "KVM_PPC_SVM_OFF ioctl failed"); + return rc; + } + return 0; +#else + g_assert_not_reached(); +#endif +} + +int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) { + return 0; + } + + if (!kvm_enabled()) { + error_setg(errp, "PEF requires KVM"); + return -1; + } + + return kvmppc_svm_init(errp); +} + +int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) { + return 0; + } + + /* + * If we don't have KVM we should never have been able to + * initialize PEF, so we should never get this far + */ + assert(kvm_enabled()); + + return kvmppc_svm_off(errp); +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest, + pef_guest, + PEF_GUEST, + CONFIDENTIAL_GUEST_SUPPORT, + { TYPE_USER_CREATABLE }, + { NULL }) + +static void pef_guest_class_init(ObjectClass *oc, void *data) +{ +} + +static void pef_guest_init(Object *obj) +{ +} + +static void pef_guest_finalize(Object *obj) +{ +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 6c47466fc2..612356e9ec 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -83,6 +83,7 @@ #include "hw/ppc/spapr_tpm_proxy.h" #include "hw/ppc/spapr_nvdimm.h" #include "hw/ppc/spapr_numa.h" +#include "hw/ppc/pef.h" #include "monitor/monitor.h" @@ -1574,7 +1575,7 @@ static void spapr_machine_reset(MachineState *machine) void *fdt; int rc; - kvmppc_svm_off(&error_fatal); + pef_kvm_reset(machine->cgs, &error_fatal); spapr_caps_apply(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); @@ -2658,6 +2659,11 @@ static void spapr_machine_init(MachineState *machine) char *filename; Error *resize_hpt_err = NULL; + /* + * if Secure VM (PEF) support is configured, then initialize it + */ + pef_kvm_init(machine->cgs, &error_fatal); + msi_nonbroken = true; QLIST_INIT(&spapr->phbs); diff --git a/hw/s390x/pv.c b/hw/s390x/pv.c index ab3a2482aa..93eccfc05d 100644 --- a/hw/s390x/pv.c +++ b/hw/s390x/pv.c @@ -14,8 +14,11 @@ #include #include "cpu.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "qom/object_interfaces.h" +#include "exec/confidential-guest-support.h" #include "hw/s390x/ipl.h" #include "hw/s390x/pv.h" @@ -111,3 +114,62 @@ void s390_pv_inject_reset_error(CPUState *cs) /* Report that we are unable to enter protected mode */ env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; } + +#define TYPE_S390_PV_GUEST "s390-pv-guest" +OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST) + +/** + * S390PVGuest: + * + * The S390PVGuest object is basically a dummy used to tell the + * confidential guest support system to use s390's PV mechanism. + * + * # $QEMU \ + * -object s390-pv-guest,id=pv0 \ + * -machine ...,confidential-guest-support=pv0 + */ +struct S390PVGuest { + ConfidentialGuestSupport parent_obj; +}; + +typedef struct S390PVGuestClass S390PVGuestClass; + +struct S390PVGuestClass { + ConfidentialGuestSupportClass parent_class; +}; + +int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { + return 0; + } + + if (!s390_has_feat(S390_FEAT_UNPACK)) { + error_setg(errp, + "CPU model does not support Protected Virtualization"); + return -1; + } + + cgs->ready = true; + + return 0; +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, + s390_pv_guest, + S390_PV_GUEST, + CONFIDENTIAL_GUEST_SUPPORT, + { TYPE_USER_CREATABLE }, + { NULL }) + +static void s390_pv_guest_class_init(ObjectClass *oc, void *data) +{ +} + +static void s390_pv_guest_init(Object *obj) +{ +} + +static void s390_pv_guest_finalize(Object *obj) +{ +} diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a2d9a79c84..2972b607f3 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -250,6 +250,9 @@ static void ccw_init(MachineState *machine) /* init CPUs (incl. CPU model) early so s390_has_feature() works */ s390_init_cpus(machine); + /* Need CPU model to be determined before we can set up PV */ + s390_pv_init(machine->cgs, &error_fatal); + s390_flic_init(); /* init the SIGP facility */ diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h new file mode 100644 index 0000000000..ba2dd4b5df --- /dev/null +++ b/include/exec/confidential-guest-support.h @@ -0,0 +1,62 @@ +/* + * QEMU Confidential Guest support + * This interface describes the common pieces between various + * schemes for protecting guest memory or other state against a + * compromised hypervisor. This includes memory encryption (AMD's + * SEV and Intel's MKTME) or special protection modes (PEF on POWER, + * or PV on s390x). + * + * Copyright Red Hat. + * + * Authors: + * David Gibson + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ +#ifndef QEMU_CONFIDENTIAL_GUEST_SUPPORT_H +#define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H + +#ifndef CONFIG_USER_ONLY + +#include "qom/object.h" + +#define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" +OBJECT_DECLARE_SIMPLE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) + +struct ConfidentialGuestSupport { + Object parent; + + /* + * ready: flag set by CGS initialization code once it's ready to + * start executing instructions in a potentially-secure + * guest + * + * The definition here is a bit fuzzy, because this is essentially + * part of a self-sanity-check, rather than a strict mechanism. + * + * It's not feasible to have a single point in the common machine + * init path to configure confidential guest support, because + * different mechanisms have different interdependencies requiring + * initialization in different places, often in arch or machine + * type specific code. It's also usually not possible to check + * for invalid configurations until that initialization code. + * That means it would be very easy to have a bug allowing CGS + * init to be bypassed entirely in certain configurations. + * + * Silently ignoring a requested security feature would be bad, so + * to avoid that we check late in init that this 'ready' flag is + * set if CGS was requested. If the CGS init hasn't happened, and + * so 'ready' is not set, we'll abort. + */ + bool ready; +}; + +typedef struct ConfidentialGuestSupportClass { + ObjectClass parent; +} ConfidentialGuestSupportClass; + +#endif /* !CONFIG_USER_ONLY */ + +#endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */ diff --git a/include/hw/boards.h b/include/hw/boards.h index 85af4faf76..a46dfe5d1a 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -270,7 +270,7 @@ struct MachineState { bool iommu; bool suppress_vmdesc; bool enable_graphics; - char *memory_encryption; + ConfidentialGuestSupport *cgs; char *ram_memdev_id; /* * convenience alias to ram_memdev_id backend memory region diff --git a/include/hw/ppc/pef.h b/include/hw/ppc/pef.h new file mode 100644 index 0000000000..707dbe524c --- /dev/null +++ b/include/hw/ppc/pef.h @@ -0,0 +1,17 @@ +/* + * PEF (Protected Execution Facility) for POWER support + * + * Copyright Red Hat. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HW_PPC_PEF_H +#define HW_PPC_PEF_H + +int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); +int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp); + +#endif /* HW_PPC_PEF_H */ diff --git a/include/hw/s390x/pv.h b/include/hw/s390x/pv.h index aee758bc2d..1f1f545bfc 100644 --- a/include/hw/s390x/pv.h +++ b/include/hw/s390x/pv.h @@ -12,6 +12,9 @@ #ifndef HW_S390_PV_H #define HW_S390_PV_H +#include "qapi/error.h" +#include "sysemu/kvm.h" + #ifdef CONFIG_KVM #include "cpu.h" #include "hw/s390x/s390-virtio-ccw.h" @@ -55,4 +58,18 @@ static inline void s390_pv_unshare(void) {} static inline void s390_pv_inject_reset_error(CPUState *cs) {}; #endif /* CONFIG_KVM */ +int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); +static inline int s390_pv_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!cgs) { + return 0; + } + if (kvm_enabled()) { + return s390_pv_kvm_init(cgs, errp); + } + + error_setg(errp, "Protected Virtualization requires KVM"); + return -1; +} + #endif /* HW_S390_PV_H */ diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 68deb74ef6..dc39b05c30 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -37,6 +37,7 @@ typedef struct Chardev Chardev; typedef struct Clock Clock; typedef struct CompatProperty CompatProperty; typedef struct CoMutex CoMutex; +typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUState CPUState; typedef struct DeviceListener DeviceListener; diff --git a/include/qom/object.h b/include/qom/object.h index d378f13a11..6721cd312e 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -638,7 +638,8 @@ bool object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp); void object_set_machine_compat_props(GPtrArray *compat_props); void object_set_accelerator_compat_props(GPtrArray *compat_props); -void object_register_sugar_prop(const char *driver, const char *prop, const char *value); +void object_register_sugar_prop(const char *driver, const char *prop, + const char *value, bool optional); void object_apply_compat_props(Object *obj); /** diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 739682f3c3..c5546bdecc 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -233,22 +233,6 @@ int kvm_has_intx_set_mask(void); */ bool kvm_arm_supports_user_irq(void); -/** - * kvm_memcrypt_enabled - return boolean indicating whether memory encryption - * is enabled - * Returns: 1 memory encryption is enabled - * 0 memory encryption is disabled - */ -bool kvm_memcrypt_enabled(void); - -/** - * kvm_memcrypt_encrypt_data: encrypt the memory range - * - * Return: 1 failed to encrypt the range - * 0 succesfully encrypted memory region - */ -int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len); - #ifdef NEED_CPU_H #include "cpu.h" diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h index 7ab6e3e31d..5c5a13c6ca 100644 --- a/include/sysemu/sev.h +++ b/include/sysemu/sev.h @@ -16,8 +16,8 @@ #include "sysemu/kvm.h" -void *sev_guest_init(const char *id); -int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len); +int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); +int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); int sev_inject_launch_secret(const char *hdr, const char *secret, uint64_t gpa, Error **errp); #endif diff --git a/qom/object.c b/qom/object.c index 2fa0119647..491823db4a 100644 --- a/qom/object.c +++ b/qom/object.c @@ -442,7 +442,8 @@ static GPtrArray *object_compat_props[3]; * other than "-global". These are generally used for syntactic * sugar and legacy command line options. */ -void object_register_sugar_prop(const char *driver, const char *prop, const char *value) +void object_register_sugar_prop(const char *driver, const char *prop, + const char *value, bool optional) { GlobalProperty *g; if (!object_compat_props[2]) { @@ -452,6 +453,7 @@ void object_register_sugar_prop(const char *driver, const char *prop, const char g->driver = g_strdup(driver); g->property = g_strdup(prop); g->value = g_strdup(value); + g->optional = optional; g_ptr_array_add(object_compat_props[2], g); } diff --git a/softmmu/rtc.c b/softmmu/rtc.c index e1e15ef613..5632684fc9 100644 --- a/softmmu/rtc.c +++ b/softmmu/rtc.c @@ -179,7 +179,8 @@ void configure_rtc(QemuOpts *opts) if (!strcmp(value, "slew")) { object_register_sugar_prop("mc146818rtc", "lost_tick_policy", - "slew"); + "slew", + false); } else if (!strcmp(value, "none")) { /* discard is default */ } else { diff --git a/softmmu/vl.c b/softmmu/vl.c index 2bf94ece9c..9eb9dab1fc 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -101,6 +101,7 @@ #include "qemu/plugin.h" #include "qemu/queue.h" #include "sysemu/arch_init.h" +#include "exec/confidential-guest-support.h" #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" @@ -1663,16 +1664,20 @@ static int machine_set_property(void *opaque, return 0; } if (g_str_equal(qom_name, "igd-passthru")) { - object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value); + object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value, + false); return 0; } if (g_str_equal(qom_name, "kvm-shadow-mem")) { - object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value); + object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value, + false); return 0; } if (g_str_equal(qom_name, "kernel-irqchip")) { - object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value); - object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), qom_name, value); + object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value, + false); + object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), qom_name, value, + false); return 0; } @@ -2298,9 +2303,10 @@ static void qemu_process_sugar_options(void) val = g_strdup_printf("%d", (uint32_t) qemu_opt_get_number(qemu_find_opts_singleton("smp-opts"), "cpus", 1)); - object_register_sugar_prop("memory-backend", "prealloc-threads", val); + object_register_sugar_prop("memory-backend", "prealloc-threads", val, + false); g_free(val); - object_register_sugar_prop("memory-backend", "prealloc", "on"); + object_register_sugar_prop("memory-backend", "prealloc", "on", false); } if (watchdog) { @@ -2493,6 +2499,8 @@ static void qemu_create_cli_devices(void) static void qemu_machine_creation_done(void) { + MachineState *machine = MACHINE(qdev_get_machine()); + /* Did we create any drives that we failed to create a device for? */ drive_check_orphaned(); @@ -2512,6 +2520,13 @@ static void qemu_machine_creation_done(void) qdev_machine_creation_done(); + if (machine->cgs) { + /* + * Verify that Confidential Guest Support has actually been initialized + */ + assert(machine->cgs->ready); + } + if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { exit(1); } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6dc1ee052d..4788139128 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -42,6 +42,7 @@ #include "hw/i386/intel_iommu.h" #include "hw/i386/x86-iommu.h" #include "hw/i386/e820_memory_layout.h" +#include "sysemu/sev.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" @@ -2135,6 +2136,25 @@ int kvm_arch_init(MachineState *ms, KVMState *s) uint64_t shadow_mem; int ret; struct utsname utsname; + Error *local_err = NULL; + + /* + * Initialize SEV context, if required + * + * If no memory encryption is requested (ms->cgs == NULL) this is + * a no-op. + * + * It's also a no-op if a non-SEV confidential guest support + * mechanism is selected. SEV is the only mechanism available to + * select on x86 at present, so this doesn't arise, but if new + * mechanisms are supported in future (e.g. TDX), they'll need + * their own initialization either here or elsewhere. + */ + ret = sev_kvm_init(ms->cgs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } if (!kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { error_report("kvm: KVM_CAP_IRQ_ROUTING not supported by KVM"); diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index c1fecc2101..1ac1fd5b94 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -54,3 +54,8 @@ int sev_inject_launch_secret(const char *hdr, const char *secret, { return 1; } + +int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +{ + return 0; +} diff --git a/target/i386/sev.c b/target/i386/sev.c index 1546606811..11c9a3cc21 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -31,6 +31,7 @@ #include "qom/object.h" #include "exec/address-spaces.h" #include "monitor/monitor.h" +#include "exec/confidential-guest-support.h" #define TYPE_SEV_GUEST "sev-guest" OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) @@ -47,7 +48,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) * -machine ...,memory-encryption=sev0 */ struct SevGuestState { - Object parent_obj; + ConfidentialGuestSupport parent_obj; /* configuration parameters */ char *sev_device; @@ -322,7 +323,7 @@ sev_guest_instance_init(Object *obj) /* sev guest info */ static const TypeInfo sev_guest_info = { - .parent = TYPE_OBJECT, + .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, .name = TYPE_SEV_GUEST, .instance_size = sizeof(SevGuestState), .instance_finalize = sev_guest_finalize, @@ -334,26 +335,6 @@ static const TypeInfo sev_guest_info = { } }; -static SevGuestState * -lookup_sev_guest_info(const char *id) -{ - Object *obj; - SevGuestState *info; - - obj = object_resolve_path_component(object_get_objects_root(), id); - if (!obj) { - return NULL; - } - - info = (SevGuestState *) - object_dynamic_cast(obj, TYPE_SEV_GUEST); - if (!info) { - return NULL; - } - - return info; -} - bool sev_enabled(void) { @@ -681,27 +662,24 @@ sev_vm_state_change(void *opaque, int running, RunState state) } } -void * -sev_guest_init(const char *id) +int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - SevGuestState *sev; + SevGuestState *sev + = (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); char *devname; int ret, fw_error; uint32_t ebx; uint32_t host_cbitpos; struct sev_user_data_status status = {}; + if (!sev) { + return 0; + } + ret = ram_block_discard_disable(true); if (ret) { error_report("%s: cannot disable RAM discard", __func__); - return NULL; - } - - sev = lookup_sev_guest_info(id); - if (!sev) { - error_report("%s: '%s' is not a valid '%s' object", - __func__, id, TYPE_SEV_GUEST); - goto err; + return -1; } sev_guest = sev; @@ -711,14 +689,14 @@ sev_guest_init(const char *id) host_cbitpos = ebx & 0x3f; if (host_cbitpos != sev->cbitpos) { - error_report("%s: cbitpos check failed, host '%d' requested '%d'", - __func__, host_cbitpos, sev->cbitpos); + error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", + __func__, host_cbitpos, sev->cbitpos); goto err; } if (sev->reduced_phys_bits < 1) { - error_report("%s: reduced_phys_bits check failed, it should be >=1," - " requested '%d'", __func__, sev->reduced_phys_bits); + error_setg(errp, "%s: reduced_phys_bits check failed, it should be >=1," + " requested '%d'", __func__, sev->reduced_phys_bits); goto err; } @@ -727,20 +705,19 @@ sev_guest_init(const char *id) devname = object_property_get_str(OBJECT(sev), "sev-device", NULL); sev->sev_fd = open(devname, O_RDWR); if (sev->sev_fd < 0) { - error_report("%s: Failed to open %s '%s'", __func__, - devname, strerror(errno)); - } - g_free(devname); - if (sev->sev_fd < 0) { + error_setg(errp, "%s: Failed to open %s '%s'", __func__, + devname, strerror(errno)); + g_free(devname); goto err; } + g_free(devname); ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status, &fw_error); if (ret) { - error_report("%s: failed to get platform status ret=%d " - "fw_error='%d: %s'", __func__, ret, fw_error, - fw_error_to_str(fw_error)); + error_setg(errp, "%s: failed to get platform status ret=%d " + "fw_error='%d: %s'", __func__, ret, fw_error, + fw_error_to_str(fw_error)); goto err; } sev->build_id = status.build; @@ -750,14 +727,14 @@ sev_guest_init(const char *id) trace_kvm_sev_init(); ret = sev_ioctl(sev->sev_fd, KVM_SEV_INIT, NULL, &fw_error); if (ret) { - error_report("%s: failed to initialize ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); goto err; } ret = sev_launch_start(sev); if (ret) { - error_report("%s: failed to create encryption context", __func__); + error_setg(errp, "%s: failed to create encryption context", __func__); goto err; } @@ -765,23 +742,29 @@ sev_guest_init(const char *id) qemu_add_machine_init_done_notifier(&sev_machine_done_notify); qemu_add_vm_change_state_handler(sev_vm_state_change, sev); - return sev; + cgs->ready = true; + + return 0; err: sev_guest = NULL; ram_block_discard_disable(false); - return NULL; + return -1; } int -sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) { - SevGuestState *sev = handle; - - assert(sev); + if (!sev_guest) { + return 0; + } /* if SEV is in update state then encrypt the data else do nothing */ - if (sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) { - return sev_launch_update_data(sev, ptr, len); + if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) { + int ret = sev_launch_update_data(sev_guest, ptr, len); + if (ret < 0) { + error_setg(errp, "failed to encrypt pflash rom"); + return ret; + } } return 0; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index daf690a678..0c5056dd5b 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2929,21 +2929,3 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) kvm_set_one_reg(cs, KVM_REG_PPC_TB_OFFSET, &tb_offset); } } - -/* - * Don't set error if KVM_PPC_SVM_OFF ioctl is invoked on kernels - * that don't support this ioctl. - */ -void kvmppc_svm_off(Error **errp) -{ - int rc; - - if (!kvm_enabled()) { - return; - } - - rc = kvm_vm_ioctl(KVM_STATE(current_accel()), KVM_PPC_SVM_OFF); - if (rc && rc != -ENOTTY) { - error_setg_errno(errp, -rc, "KVM_PPC_SVM_OFF ioctl failed"); - } -} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 73ce2bc951..989f61ace0 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -39,7 +39,6 @@ int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, bool radix, bool gtse, uint64_t proc_tbl); -void kvmppc_svm_off(Error **errp); #ifndef CONFIG_USER_ONLY bool kvmppc_spapr_use_multitce(void); int kvmppc_spapr_enable_inkernel_multitce(void); @@ -216,11 +215,6 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, return 0; } -static inline void kvmppc_svm_off(Error **errp) -{ - return; -} - static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) {