404 lines
12 KiB
Diff
404 lines
12 KiB
Diff
|
From 725b55269e39ee0c64daf556b019d1eb70940b21 Mon Sep 17 00:00:00 2001
|
||
|
From: Brijesh Singh <brijesh.singh@amd.com>
|
||
|
Date: Tue, 6 Feb 2018 19:08:08 -0600
|
||
|
Subject: [PATCH] accel: add Secure Encrypted Virtulization (SEV) object
|
||
|
|
||
|
Add a new memory encryption object 'sev-guest'. The object will be used
|
||
|
to create enrypted VMs on AMD EPYC CPU. The object provides the properties
|
||
|
to pass guest owner's public Diffie-hellman key, guest policy and session
|
||
|
information required to create the memory encryption context within the
|
||
|
SEV firmware.
|
||
|
|
||
|
e.g to launch SEV guest
|
||
|
# $QEMU \
|
||
|
-object sev-guest,id=sev0 \
|
||
|
-machine ....,memory-encryption=sev0
|
||
|
|
||
|
Cc: Paolo Bonzini <pbonzini@redhat.com>
|
||
|
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
|
||
|
[BR: FATE#322124]
|
||
|
Signed-off-by: Bruce Rogers <brogers@suse.com>
|
||
|
---
|
||
|
accel/kvm/Makefile.objs | 2 +-
|
||
|
accel/kvm/sev.c | 214 +++++++++++++++++++++++++++++++++++++++++
|
||
|
docs/amd-memory-encryption.txt | 17 ++++
|
||
|
include/sysemu/sev.h | 54 +++++++++++
|
||
|
qemu-options.hx | 36 +++++++
|
||
|
5 files changed, 322 insertions(+), 1 deletion(-)
|
||
|
create mode 100644 accel/kvm/sev.c
|
||
|
create mode 100644 include/sysemu/sev.h
|
||
|
|
||
|
diff --git a/accel/kvm/Makefile.objs b/accel/kvm/Makefile.objs
|
||
|
index 85351e7de7..666ceef3da 100644
|
||
|
--- a/accel/kvm/Makefile.objs
|
||
|
+++ b/accel/kvm/Makefile.objs
|
||
|
@@ -1 +1 @@
|
||
|
-obj-$(CONFIG_KVM) += kvm-all.o
|
||
|
+obj-$(CONFIG_KVM) += kvm-all.o sev.o
|
||
|
diff --git a/accel/kvm/sev.c b/accel/kvm/sev.c
|
||
|
new file mode 100644
|
||
|
index 0000000000..57e092a0bd
|
||
|
--- /dev/null
|
||
|
+++ b/accel/kvm/sev.c
|
||
|
@@ -0,0 +1,214 @@
|
||
|
+/*
|
||
|
+ * QEMU SEV support
|
||
|
+ *
|
||
|
+ * Copyright Advanced Micro Devices 2016-2018
|
||
|
+ *
|
||
|
+ * Author:
|
||
|
+ * Brijesh Singh <brijesh.singh@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 "qapi/error.h"
|
||
|
+#include "qom/object_interfaces.h"
|
||
|
+#include "qemu/base64.h"
|
||
|
+#include "sysemu/kvm.h"
|
||
|
+#include "sysemu/sev.h"
|
||
|
+#include "sysemu/sysemu.h"
|
||
|
+
|
||
|
+#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
|
||
|
+#define DEFAULT_SEV_DEVICE "/dev/sev"
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_finalize(Object *obj)
|
||
|
+{
|
||
|
+}
|
||
|
+
|
||
|
+static char *
|
||
|
+qsev_guest_get_session_file(Object *obj, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ return s->session_file ? g_strdup(s->session_file) : NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_set_session_file(Object *obj, const char *value, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ s->session_file = g_strdup(value);
|
||
|
+}
|
||
|
+
|
||
|
+static char *
|
||
|
+qsev_guest_get_dh_cert_file(Object *obj, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ return g_strdup(s->dh_cert_file);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ s->dh_cert_file = g_strdup(value);
|
||
|
+}
|
||
|
+
|
||
|
+static char *
|
||
|
+qsev_guest_get_sev_device(Object *obj, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ return g_strdup(sev->sev_device);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ sev->sev_device = g_strdup(value);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_class_init(ObjectClass *oc, void *data)
|
||
|
+{
|
||
|
+ object_class_property_add_str(oc, "sev-device",
|
||
|
+ qsev_guest_get_sev_device,
|
||
|
+ qsev_guest_set_sev_device,
|
||
|
+ NULL);
|
||
|
+ object_class_property_set_description(oc, "sev-device",
|
||
|
+ "SEV device to use", NULL);
|
||
|
+ object_class_property_add_str(oc, "dh-cert-file",
|
||
|
+ qsev_guest_get_dh_cert_file,
|
||
|
+ qsev_guest_set_dh_cert_file,
|
||
|
+ NULL);
|
||
|
+ object_class_property_set_description(oc, "dh-cert-file",
|
||
|
+ "guest owners DH certificate (encoded with base64)", NULL);
|
||
|
+ object_class_property_add_str(oc, "session-file",
|
||
|
+ qsev_guest_get_session_file,
|
||
|
+ qsev_guest_set_session_file,
|
||
|
+ NULL);
|
||
|
+ object_class_property_set_description(oc, "session-file",
|
||
|
+ "guest owners session parameters (encoded with base64)", NULL);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_set_handle(Object *obj, Visitor *v, const char *name,
|
||
|
+ void *opaque, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+ uint32_t value;
|
||
|
+
|
||
|
+ visit_type_uint32(v, name, &value, errp);
|
||
|
+ sev->handle = value;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_set_policy(Object *obj, Visitor *v, const char *name,
|
||
|
+ void *opaque, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+ uint32_t value;
|
||
|
+
|
||
|
+ visit_type_uint32(v, name, &value, errp);
|
||
|
+ sev->policy = value;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name,
|
||
|
+ void *opaque, Error **errp)
|
||
|
+{
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+ uint32_t value;
|
||
|
+
|
||
|
+ visit_type_uint32(v, name, &value, errp);
|
||
|
+ sev->cbitpos = value;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_get_policy(Object *obj, Visitor *v, const char *name,
|
||
|
+ void *opaque, Error **errp)
|
||
|
+{
|
||
|
+ uint32_t value;
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ value = sev->policy;
|
||
|
+ visit_type_uint32(v, name, &value, errp);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_get_handle(Object *obj, Visitor *v, const char *name,
|
||
|
+ void *opaque, Error **errp)
|
||
|
+{
|
||
|
+ uint32_t value;
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ value = sev->handle;
|
||
|
+ visit_type_uint32(v, name, &value, errp);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name,
|
||
|
+ void *opaque, Error **errp)
|
||
|
+{
|
||
|
+ uint32_t value;
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ value = sev->cbitpos;
|
||
|
+ visit_type_uint32(v, name, &value, errp);
|
||
|
+}
|
||
|
+
|
||
|
+static uint32_t
|
||
|
+sev_get_host_cbitpos(void)
|
||
|
+{
|
||
|
+ uint32_t ebx;
|
||
|
+
|
||
|
+ host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
|
||
|
+
|
||
|
+ return ebx & 0x3f;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+qsev_guest_init(Object *obj)
|
||
|
+{
|
||
|
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
|
||
|
+
|
||
|
+ sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
|
||
|
+ sev->policy = DEFAULT_GUEST_POLICY;
|
||
|
+ sev->cbitpos = sev_get_host_cbitpos();
|
||
|
+ object_property_add(obj, "policy", "uint32", qsev_guest_get_policy,
|
||
|
+ qsev_guest_set_policy, NULL, NULL, NULL);
|
||
|
+ object_property_add(obj, "handle", "uint32", qsev_guest_get_handle,
|
||
|
+ qsev_guest_set_handle, NULL, NULL, NULL);
|
||
|
+ object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos,
|
||
|
+ qsev_guest_set_cbitpos, NULL, NULL, NULL);
|
||
|
+}
|
||
|
+
|
||
|
+/* sev guest info */
|
||
|
+static const TypeInfo qsev_guest_info = {
|
||
|
+ .parent = TYPE_OBJECT,
|
||
|
+ .name = TYPE_QSEV_GUEST_INFO,
|
||
|
+ .instance_size = sizeof(QSevGuestInfo),
|
||
|
+ .instance_finalize = qsev_guest_finalize,
|
||
|
+ .class_size = sizeof(QSevGuestInfoClass),
|
||
|
+ .class_init = qsev_guest_class_init,
|
||
|
+ .instance_init = qsev_guest_init,
|
||
|
+ .interfaces = (InterfaceInfo[]) {
|
||
|
+ { TYPE_USER_CREATABLE },
|
||
|
+ { }
|
||
|
+ }
|
||
|
+};
|
||
|
+
|
||
|
+static void
|
||
|
+sev_register_types(void)
|
||
|
+{
|
||
|
+ type_register_static(&qsev_guest_info);
|
||
|
+}
|
||
|
+
|
||
|
+type_init(sev_register_types);
|
||
|
diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
|
||
|
index 72a92b6c63..1527f603ea 100644
|
||
|
--- a/docs/amd-memory-encryption.txt
|
||
|
+++ b/docs/amd-memory-encryption.txt
|
||
|
@@ -35,10 +35,21 @@ in bad measurement). The guest policy is a 4-byte data structure containing
|
||
|
several flags that restricts what can be done on running SEV guest.
|
||
|
See KM Spec section 3 and 6.2 for more details.
|
||
|
|
||
|
+The guest policy can be provided via the 'policy' property (see below)
|
||
|
+
|
||
|
+# ${QEMU} \
|
||
|
+ sev-guest,id=sev0,policy=0x1...\
|
||
|
+
|
||
|
Guest owners provided DH certificate and session parameters will be used to
|
||
|
establish a cryptographic session with the guest owner to negotiate keys used
|
||
|
for the attestation.
|
||
|
|
||
|
+The DH certificate and session blob can be provided via 'dh-cert-file' and
|
||
|
+'session-file' property (see below
|
||
|
+
|
||
|
+# ${QEMU} \
|
||
|
+ sev-guest,id=sev0,dh-cert-file=<file1>,session-file=<file2>
|
||
|
+
|
||
|
LAUNCH_UPDATE_DATA encrypts the memory region using the cryptographic context
|
||
|
created via LAUNCH_START command. If required, this command can be called
|
||
|
multiple times to encrypt different memory regions. The command also calculates
|
||
|
@@ -59,6 +70,12 @@ context.
|
||
|
See SEV KM API Spec [1] 'Launching a guest' usage flow (Appendix A) for the
|
||
|
complete flow chart.
|
||
|
|
||
|
+To launch a SEV guest
|
||
|
+
|
||
|
+# ${QEMU} \
|
||
|
+ -machine ...,memory-encryption=sev0 \
|
||
|
+ -object sev-guest,id=sev0
|
||
|
+
|
||
|
Debugging
|
||
|
-----------
|
||
|
Since memory contents of SEV guest is encrypted hence hypervisor access to the
|
||
|
diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h
|
||
|
new file mode 100644
|
||
|
index 0000000000..eed679653d
|
||
|
--- /dev/null
|
||
|
+++ b/include/sysemu/sev.h
|
||
|
@@ -0,0 +1,54 @@
|
||
|
+/*
|
||
|
+ * QEMU Secure Encrypted Virutualization (SEV) support
|
||
|
+ *
|
||
|
+ * Copyright: Advanced Micro Devices, 2016-2018
|
||
|
+ *
|
||
|
+ * Authors:
|
||
|
+ * Brijesh Singh <brijesh.singh@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 "qom/object.h"
|
||
|
+#include "qapi/error.h"
|
||
|
+#include "sysemu/kvm.h"
|
||
|
+#include "qemu/error-report.h"
|
||
|
+
|
||
|
+#define TYPE_QSEV_GUEST_INFO "sev-guest"
|
||
|
+#define QSEV_GUEST_INFO(obj) \
|
||
|
+ OBJECT_CHECK(QSevGuestInfo, (obj), TYPE_QSEV_GUEST_INFO)
|
||
|
+
|
||
|
+typedef struct QSevGuestInfo QSevGuestInfo;
|
||
|
+typedef struct QSevGuestInfoClass QSevGuestInfoClass;
|
||
|
+
|
||
|
+/**
|
||
|
+ * QSevGuestInfo:
|
||
|
+ *
|
||
|
+ * The QSevGuestInfo object is used for creating a SEV guest.
|
||
|
+ *
|
||
|
+ * # $QEMU \
|
||
|
+ * -object sev-guest,id=sev0 \
|
||
|
+ * -machine ...,memory-encryption=sev0
|
||
|
+ */
|
||
|
+struct QSevGuestInfo {
|
||
|
+ Object parent_obj;
|
||
|
+
|
||
|
+ char *sev_device;
|
||
|
+ uint32_t policy;
|
||
|
+ uint32_t handle;
|
||
|
+ char *dh_cert_file;
|
||
|
+ char *session_file;
|
||
|
+ uint32_t cbitpos;
|
||
|
+};
|
||
|
+
|
||
|
+struct QSevGuestInfoClass {
|
||
|
+ ObjectClass parent_class;
|
||
|
+};
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
diff --git a/qemu-options.hx b/qemu-options.hx
|
||
|
index 5385832707..5acf180991 100644
|
||
|
--- a/qemu-options.hx
|
||
|
+++ b/qemu-options.hx
|
||
|
@@ -4470,6 +4470,42 @@ contents of @code{iv.b64} to the second secret
|
||
|
data=$SECRET,iv=$(<iv.b64)
|
||
|
@end example
|
||
|
|
||
|
+@item -object sev-guest,id=@var{id},sev-device=@var{string}[cbitpos=@var{cbitpos},policy=@var{policy},handle=@var{handle},dh-cert-file=@var{file},session-file=@var{file}]
|
||
|
+
|
||
|
+Create a Secure Encrypted Virtualization (SEV) guest object, which can be used
|
||
|
+to provide the guest memory encryption support on AMD processors.
|
||
|
+
|
||
|
+The @option{sev-device} provides the device file to use for communicating with
|
||
|
+the SEV firmware running inside AMD Secure Processor. The default device is
|
||
|
+'/dev/sev'. If hardware supports memory encryption then /dev/sev devices are
|
||
|
+created by CCP driver.
|
||
|
+
|
||
|
+The @option{cbitpos} provide the C-bit location in guest page table entry to use.
|
||
|
+
|
||
|
+The @option{policy} provides the guest policy to be enforced by the SEV firmware
|
||
|
+and restrict what configuration and operational commands can be performed on this
|
||
|
+guest by the hypervisor. The policy should be provided by the guest owner and is
|
||
|
+bound to the guest and cannot be changed throughout the lifetime of the guest.
|
||
|
+The default is 0.
|
||
|
+
|
||
|
+If guest @option{policy} allows sharing the key with another SEV guest then
|
||
|
+@option{handle} can be use to provide handle of the guest from which to share
|
||
|
+the key.
|
||
|
+
|
||
|
+The @option{dh-cert-file} and @option{session-file} provides the guest owner's
|
||
|
+Public Diffie-Hillman key defined in SEV spec. The PDH and session parameters
|
||
|
+are used for establishing a cryptographic session with the guest owner to
|
||
|
+negotiate keys used for attestation. The file must be encoded in base64.
|
||
|
+
|
||
|
+e.g to launch a SEV guest
|
||
|
+@example
|
||
|
+ # $QEMU \
|
||
|
+ ......
|
||
|
+ -object sev-guest,id=sev0 \
|
||
|
+ -machine ...,memory-encryption=sev0
|
||
|
+ .....
|
||
|
+
|
||
|
+@end example
|
||
|
@end table
|
||
|
|
||
|
ETEXI
|