target/i386: add Secure Encrypted Virtualization (SEV) object

Add a new memory encryption object 'sev-guest'. The object will be used
to create encrypted 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>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Eduardo Habkost <ehabkost@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Brijesh Singh 2018-03-08 06:48:41 -06:00 committed by Paolo Bonzini
parent 54e8953967
commit a9b4942f48
7 changed files with 353 additions and 0 deletions

View file

@ -63,3 +63,4 @@ CONFIG_PXB=y
CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
CONFIG_I2C=y
CONFIG_SEV=$(CONFIG_KVM)

View file

@ -63,3 +63,4 @@ CONFIG_PXB=y
CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
CONFIG_I2C=y
CONFIG_SEV=$(CONFIG_KVM)

View file

@ -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,cbitpos=47,reduced-phys-bits=1
Debugging
-----------
Since memory contents of SEV guest is encrypted hence hypervisor access to the

View file

@ -4353,6 +4353,50 @@ contents of @code{iv.b64} to the second secret
data=$SECRET,iv=$(<iv.b64)
@end example
@item -object sev-guest,id=@var{id},cbitpos=@var{cbitpos},reduced-phys-bits=@var{val},[sev-device=@var{string},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.
When memory encryption is enabled, one of the physical address bit (aka the
C-bit) is utilized to mark if a memory page is protected. The @option{cbitpos}
is used to provide the C-bit position. The C-bit position is Host family dependent
hence user must provide this value. On EPYC, the value should be 47.
When memory encryption is enabled, we loose certain bits in physical address space.
The @option{reduced-phys-bits} is used to provide the number of bits we loose in
physical address space. Similar to C-bit, the value is Host family dependent.
On EPYC, the value should be 5.
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{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,cbitpos=47,reduced-phys-bits=5 \
-machine ...,memory-encryption=sev0
.....
@end example
@end table
ETEXI

View file

@ -5,6 +5,7 @@ obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o
obj-$(CONFIG_KVM) += kvm.o hyperv.o
obj-$(CONFIG_SEV) += sev.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
# HAX support
ifdef CONFIG_WIN32

228
target/i386/sev.c Normal file
View file

@ -0,0 +1,228 @@
/*
* 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 "sev_i386.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_set_reduced_phys_bits(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->reduced_phys_bits = 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 void
qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
uint32_t value;
QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
value = sev->reduced_phys_bits;
visit_type_uint32(v, name, &value, errp);
}
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;
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);
object_property_add(obj, "reduced-phys-bits", "uint32",
qsev_guest_get_reduced_phys_bits,
qsev_guest_set_reduced_phys_bits, 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);

61
target/i386/sev_i386.h Normal file
View file

@ -0,0 +1,61 @@
/*
* 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_I386_H
#define QEMU_SEV_I386_H
#include "qom/object.h"
#include "qapi/error.h"
#include "sysemu/kvm.h"
#include "qemu/error-report.h"
#define SEV_POLICY_NODBG 0x1
#define SEV_POLICY_NOKS 0x2
#define SEV_POLICY_ES 0x4
#define SEV_POLICY_NOSEND 0x8
#define SEV_POLICY_DOMAIN 0x10
#define SEV_POLICY_SEV 0x20
#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;
uint32_t reduced_phys_bits;
};
struct QSevGuestInfoClass {
ObjectClass parent_class;
};
#endif