This patch adds the initial support required to integrate Secure Encrypted Virtualization feature, the patch include the following changes:
- adds sev.c and sev.h files: the file will contain SEV APIs implemention. - add kvm_sev_enabled(): similar to kvm_enabled() this function can be used to check if sev is enabled on this guest. - implement functions to parse SEV specific configuration file. A typical SEV config file looks like this: [sev-launch] flags = "00000000" policy = "000000" dh_pub_qx = "0123456789abcdef0123456789abcdef" dh_pub_qy = "0123456789abcdef0123456789abcdef" nonce = "0123456789abcdef" vcpu_count = "1" vcpu_length = "30" vcpu_mask = "00ab" Signed-off-by: Brijesh Singh <brijesh.si...@amd.com> --- Makefile.target | 2 include/sysemu/kvm.h | 10 ++ include/sysemu/sev.h | 27 +++++ kvm-all.c | 6 + sev.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 include/sysemu/sev.h create mode 100644 sev.c diff --git a/Makefile.target b/Makefile.target index a440bcb..74ad204 100644 --- a/Makefile.target +++ b/Makefile.target @@ -136,7 +136,7 @@ ifdef CONFIG_SOFTMMU obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o obj-y += qtest.o bootdevice.o obj-y += hw/ -obj-$(CONFIG_KVM) += kvm-all.o +obj-$(CONFIG_KVM) += kvm-all.o sev.o obj-y += memory.o cputlb.o obj-y += memory_mapping.o obj-y += dump.o diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index c9c2436..7f83de0 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -40,6 +40,7 @@ #endif extern bool kvm_allowed; +extern bool kvm_sev_allowed; extern bool kvm_kernel_irqchip; extern bool kvm_split_irqchip; extern bool kvm_async_interrupts_allowed; @@ -56,6 +57,14 @@ extern bool kvm_ioeventfd_any_length_allowed; #if defined CONFIG_KVM || !defined NEED_CPU_H #define kvm_enabled() (kvm_allowed) + +/** + * kvm_sev_enabled: + * + * Returns: true if guest is running into SEV-enabled mode. + */ +#define kvm_sev_enabled() (kvm_sev_allowed) + /** * kvm_irqchip_in_kernel: * @@ -171,6 +180,7 @@ extern bool kvm_ioeventfd_any_length_allowed; #else #define kvm_enabled() (0) +#define kvm_sev_enabled() (false) #define kvm_irqchip_in_kernel() (false) #define kvm_irqchip_is_split() (false) #define kvm_async_interrupts_enabled() (false) diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h new file mode 100644 index 0000000..0ee8aff --- /dev/null +++ b/include/sysemu/sev.h @@ -0,0 +1,27 @@ +/* + * QEMU SEV support + * + * Copyright: Advanced Micro Devices, 2016 + * + * Authors: + * Brijesh Singh <brijesh.si...@amd.com> + * + * 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_SEV_H +#define QEMU_SEV_H + +#include "sysemu/kvm.h" + +/** + * sev_init - initialize Secure Encrypted Virtualization on this guest + * @kvm_state - KVM handle + * Returns: 1 on error, 0 on success + */ +int sev_init(KVMState *kvm_state); + +#endif + diff --git a/kvm-all.c b/kvm-all.c index ebf35b0..e194849 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -36,6 +36,7 @@ #include "qemu/event_notifier.h" #include "trace.h" #include "hw/irq.h" +#include "sysemu/sev.h" #include "hw/boards.h" @@ -119,6 +120,7 @@ bool kvm_readonly_mem_allowed; bool kvm_vm_attributes_allowed; bool kvm_direct_msi_allowed; bool kvm_ioeventfd_any_length_allowed; +bool kvm_sev_allowed; static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), @@ -1745,6 +1747,10 @@ static int kvm_init(MachineState *ms) kvm_state = s; + if (!sev_init(kvm_state)) { + kvm_sev_allowed = true; + } + if (kvm_eventfds_allowed) { s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; diff --git a/sev.c b/sev.c new file mode 100644 index 0000000..2d71ca6 --- /dev/null +++ b/sev.c @@ -0,0 +1,282 @@ +/* + * QEMU SEV support + * + * Copyright Advanced Micro Devices 2016 + * + * Author: + * Brijesh Singh <brijesh.si...@amd.com> + * + * 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 <sys/ioctl.h> +#include <sys/mman.h> + +#include <linux/kvm.h> + +#include "qemu-common.h" +#include "qemu/atomic.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "hw/hw.h" +#include "hw/pci/msi.h" +#include "hw/s390x/adapter.h" +#include "exec/gdbstub.h" +#include "sysemu/kvm_int.h" +#include "sysemu/sev.h" +#include "qemu/bswap.h" +#include "exec/memory.h" +#include "exec/ram_addr.h" +#include "exec/address-spaces.h" +#include "qemu/event_notifier.h" +#include "trace.h" +#include "hw/irq.h" + +//#define DEBUG_SEV + +#ifdef DEBUG_SEV +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +struct SEVInfo { + uint8_t state; /* guest current state */ + uint8_t type; /* guest type (encrypted, unencrypted) */ + struct kvm_sev_launch_start *launch_start; + struct kvm_sev_launch_update *launch_update; + struct kvm_sev_launch_finish *launch_finish; +}; + +typedef struct SEVInfo SEVInfo; +static SEVInfo *sev_info; +static const char *cfg_file; + +enum { + LAUNCH_OPTS = 0, +}; + +enum { + PRE_ENCRYPTED_GUEST = 0, + UNENCRYPTED_GUEST, +}; + +static QemuOptsList launch_opts = { + .name = "sev-launch", + .head = QTAILQ_HEAD_INITIALIZER(launch_opts.head), + .desc = { + { + .name = "flags", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "policy", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "dh_pub_qx", + .type = QEMU_OPT_STRING, + }, + { + .name = "dh_pub_qy", + .type = QEMU_OPT_STRING, + }, + { + .name = "nonce", + .type = QEMU_OPT_STRING, + }, + { + .name = "vcpu_length", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "vcpu_count", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "vcpu_mask", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList *config_groups[] = { + &launch_opts, + NULL +}; + +struct add_rule_data { + SEVInfo *s; + int action; +}; + +static unsigned int read_config_u32(QemuOpts *opts, const char *key) +{ + unsigned int val; + + val = qemu_opt_get_number_del(opts, key, -1); + DPRINTF(" %s = %08x\n", key, val); + + return val; +} + +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val) +{ + int i; + const char *v; + + v = qemu_opt_get(opts, key); + if (!v) { + return 0; + } + + DPRINTF(" %s = ", key); + i = 0; + while (*v) { + sscanf(v, "%2hhx", &val[i]); + DPRINTF("%02hhx", val[i]); + v += 2; + i++; + } + DPRINTF("\n"); + + return i; +} + +static int add_rule(void *opaque, QemuOpts *opts, Error **errp) +{ + struct add_rule_data *d = opaque; + + switch (d->action) { + case LAUNCH_OPTS: { + struct kvm_sev_launch_start *start; + struct kvm_sev_launch_update *update; + struct kvm_sev_launch_finish *finish; + + /* LAUNCH_START parameters */ + start = g_malloc0(sizeof(*start)); + + DPRINTF("Parsing 'sev-launch' parameters\n"); + start->flags = read_config_u32(opts, "flags"); + start->policy = read_config_u32(opts, "policy"); + read_config_u8(opts, "nonce", start->nonce); + read_config_u8(opts, "dh_pub_qx", start->dh_pub_qx); + read_config_u8(opts, "dh_pub_qy", start->dh_pub_qy); + sev_info->launch_start = start; + + /* LAUNCH_UPDATE */ + update = g_malloc0(sizeof(*update)); + sev_info->launch_update = update; + + /* LAUNCH_FINISH parameters */ + finish = g_malloc0(sizeof(*finish)); + + finish->vcpu_count = read_config_u32(opts, "vcpu_count"); + finish->vcpu_length = read_config_u32(opts, "vcpu_length"); + if (qemu_opt_get(opts, "vcpu_mask")) { + finish->vcpu_mask_length = + strlen(qemu_opt_get(opts, "vcpu_mask")) / 2; + finish->vcpu_mask_addr = (unsigned long) + g_malloc0(finish->vcpu_length); + read_config_u8(opts, "vcpu_mask", + (uint8_t *)finish->vcpu_mask_addr); + } + + sev_info->launch_finish = finish; + + break; + } + } + + return 0; +} + +static int parse_add_rules(QemuOptsList *list, struct add_rule_data *d) +{ + Error *local_err = NULL; + + qemu_opts_foreach(list, add_rule, d, &local_err); + if (local_err) { + return 1; + } + + return 0; +} + +static int parse_sev_cfg(SEVInfo *s, int type, const char *filename) +{ + FILE *f; + int ret = 0; + struct add_rule_data d; + + if (filename) { + f = fopen(filename, "r"); + if (f == NULL) { + return 1; + } + + ret = qemu_config_parse(f, config_groups, filename); + if (ret < 0) { + fprintf(stderr, "SEV: could not parse config file\n"); + exit(EXIT_FAILURE); + } + } + + switch (type) { + case LAUNCH_OPTS: + d.s = s; + d.action = type; + ret = parse_add_rules(&launch_opts, &d); + break; + } + + return ret; + +} + +int sev_init(KVMState *kvm_state) +{ + QemuOpts *opts; + const char *type; + + opts = qemu_find_opts_singleton("sev"); + cfg_file = qemu_opt_get(opts, "config"); + if (!cfg_file) { + return 1; + } + + type = qemu_opt_get(opts, "type"); + if (!type) { + return 1; + } + + sev_info = calloc(1, sizeof(*sev_info)); + if (!sev_info) { + return 1; + } + + if (!strcmp(type, "unencrypted")) { + sev_info->type = UNENCRYPTED_GUEST; + } else if (!strcmp(type, "encrypted")) { + sev_info->type = PRE_ENCRYPTED_GUEST; + } else { + fprintf(stderr, "SEV: unsupported type '%s'\n", type); + goto err; + } + + /* call SEV launch start APIs based on guest type */ + + return 0; +err: + free(sev_info); + sev_info = NULL; + return 1; +} +