When memory encryption is enabled, KVM_SEV_INIT command is used to initialize the platform. The command loads the SEV related persistent data from non-volatile storage and initializes the platform context. This command should be first issued before invoking any other guest commands provided by the SEV firmware. Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx> Signed-off-by: Brijesh Singh <brijesh.singh@xxxxxxx> --- accel/kvm/kvm-all.c | 15 +++++ accel/kvm/sev.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++ accel/kvm/trace-events | 2 + accel/stubs/kvm-stub.c | 28 +++++++++ include/sysemu/sev.h | 16 +++++ 5 files changed, 222 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f290f487a573..6e5f3fd65066 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -38,6 +38,7 @@ #include "qemu/event_notifier.h" #include "trace.h" #include "hw/irq.h" +#include "sysemu/sev.h" #include "hw/boards.h" @@ -103,6 +104,9 @@ struct KVMState #endif KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; + + /* memory encryption */ + void *memcrypt_handle; }; KVMState *kvm_state; @@ -1632,6 +1636,17 @@ 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) { + goto err; + } + } + ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; diff --git a/accel/kvm/sev.c b/accel/kvm/sev.c index 57e092a0bddd..d5fd975792b8 100644 --- a/accel/kvm/sev.c +++ b/accel/kvm/sev.c @@ -18,10 +18,74 @@ #include "sysemu/kvm.h" #include "sysemu/sev.h" #include "sysemu/sysemu.h" +#include "trace.h" #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ #define DEFAULT_SEV_DEVICE "/dev/sev" +static uint64_t me_mask; +static bool sev_active; +static int sev_fd; + +#define SEV_FW_MAX_ERROR 0x17 + +static const char *const sev_fw_errlist[] = { + "", + "Platform state is invalid", + "Guest state is invalid", + "Platform configuration is invalid", + "Buffer too small", + "Platform is already owned", + "Certificate is invalid", + "Policy is not allowed", + "Guest is not active", + "Invalid address", + "Bad signature", + "Bad measurement", + "Asid is already owned", + "Invalid ASID", + "WBINVD is required", + "DF_FLUSH is required", + "Guest handle is invalid", + "Invalid command", + "Guest is active", + "Hardware error", + "Hardware unsafe", + "Feature not supported", + "Invalid parameter" +}; + +static int +sev_ioctl(int cmd, void *data, int *error) +{ + int r; + struct kvm_sev_cmd input; + + memset(&input, 0x0, sizeof(input)); + + input.id = cmd; + input.sev_fd = sev_fd; + input.data = (__u64)data; + + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input); + + if (error) { + *error = input.error; + } + + return r; +} + +static const char * +fw_error_to_str(int code) +{ + if (code >= SEV_FW_MAX_ERROR) { + return "unknown error"; + } + + return sev_fw_errlist[code]; +} + static void qsev_guest_finalize(Object *obj) { @@ -205,6 +269,103 @@ static const TypeInfo qsev_guest_info = { } }; +static QSevGuestInfo * +lookup_sev_guest_info(const char *id) +{ + Object *obj; + QSevGuestInfo *info; + + obj = object_resolve_path_component(object_get_objects_root(), id); + if (!obj) { + return NULL; + } + + info = (QSevGuestInfo *) + object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO); + if (!info) { + return NULL; + } + + return info; +} + +uint64_t +sev_get_me_mask(void) +{ + return ~me_mask; +} + +void +sev_get_current_state(char **state) +{ +} + +bool +sev_enabled(void) +{ + return sev_active; +} + +void +sev_get_fw_version(uint8_t *major, uint8_t *minor, uint8_t *build) +{ +} + +void +sev_get_policy(uint32_t *policy) +{ +} + +void * +sev_guest_init(const char *id) +{ + SEVState *s; + char *devname; + int ret, fw_error; + uint32_t host_cbitpos, cbitpos; + + s = g_new0(SEVState, 1); + s->sev_info = lookup_sev_guest_info(id); + if (!s->sev_info) { + error_report("%s: '%s' is not a valid '%s' object", + __func__, id, TYPE_QSEV_GUEST_INFO); + goto err; + } + + host_cbitpos = sev_get_host_cbitpos(); + cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL); + if (host_cbitpos != cbitpos) { + error_report("%s: cbitpos check failed, host '%d' request '%d'", + __func__, host_cbitpos, cbitpos); + goto err; + } + + me_mask = (1UL << cbitpos); + + devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL); + sev_fd = open(devname, O_RDWR); + if (sev_fd < 0) { + error_report("%s: Failed to open %s '%s'", __func__, + devname, strerror(errno)); + goto err; + } + g_free(devname); + + trace_kvm_sev_init(); + ret = sev_ioctl(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)); + goto err; + } + + sev_active = true; + return s; +err: + g_free(s); + return NULL; +} + static void sev_register_types(void) { diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index f89ba5578dc1..ea487e5a5913 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -13,3 +13,5 @@ kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d vi kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_release_virq(int virq) "virq %d" +# sev.c +kvm_sev_init(void) "" diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index c964af3e1c97..bb78a1f1b950 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -14,6 +14,7 @@ #include "qemu-common.h" #include "cpu.h" #include "sysemu/kvm.h" +#include "sysemu/sev.h" #ifndef CONFIG_USER_ONLY #include "hw/pci/msi.h" @@ -33,6 +34,11 @@ bool kvm_readonly_mem_allowed; bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; +bool sev_allowed; +uint8_t sev_fw_major; +uint8_t sev_fw_minor; +uint8_t sev_fw_build; + int kvm_destroy_vcpu(CPUState *cpu) { return -ENOSYS; @@ -105,6 +111,28 @@ int kvm_on_sigbus(int code, void *addr) return 1; } +void sev_get_current_state(char **state) +{ +} + +bool sev_enabled(void) +{ + return false; +} + +uint64_t sev_get_me_mask(void) +{ + return ~0UL; +} + +void sev_get_fw_version(uint8_t *major, uint8_t *minor, uint8_t *build) +{ +} + +void sev_get_policy(uint32_t *policy) +{ +} + #ifndef CONFIG_USER_ONLY int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) { diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h index eed679653dbc..121e7e4aa44c 100644 --- a/include/sysemu/sev.h +++ b/include/sysemu/sev.h @@ -14,6 +14,8 @@ #ifndef QEMU_SEV_H #define QEMU_SEV_H +#include <linux/kvm.h> + #include "qom/object.h" #include "qapi/error.h" #include "sysemu/kvm.h" @@ -23,6 +25,12 @@ #define QSEV_GUEST_INFO(obj) \ OBJECT_CHECK(QSevGuestInfo, (obj), TYPE_QSEV_GUEST_INFO) +extern bool sev_enabled(void); +extern uint64_t sev_get_me_mask(void); +extern void sev_get_current_state(char **state); +extern void sev_get_fw_version(uint8_t *major, uint8_t *minor, uint8_t *build); +extern void sev_get_policy(uint32_t *policy); + typedef struct QSevGuestInfo QSevGuestInfo; typedef struct QSevGuestInfoClass QSevGuestInfoClass; @@ -50,5 +58,13 @@ struct QSevGuestInfoClass { ObjectClass parent_class; }; +struct SEVState { + QSevGuestInfo *sev_info; +}; + +typedef struct SEVState SEVState; + +void *sev_guest_init(const char *id); + #endif -- 2.14.3