mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 17:53:56 -06:00

During machine creation we encrypted the guest bios image, the LAUNCH_MEASURE command can be used to retrieve the measurement of the encrypted memory region. This measurement is a signature of the memory contents that can be sent to the guest owner as an attestation that the memory was encrypted correctly by the firmware. VM management tools like libvirt can query the measurement using query-sev-launch-measure QMP command. 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>
686 lines
17 KiB
C
686 lines
17 KiB
C
/*
|
|
* 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 <linux/kvm.h>
|
|
#include <linux/psp-sev.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#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"
|
|
#include "trace.h"
|
|
|
|
#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
|
|
#define DEFAULT_SEV_DEVICE "/dev/sev"
|
|
|
|
static SEVState *sev_state;
|
|
|
|
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"
|
|
};
|
|
|
|
#define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist)
|
|
|
|
static int
|
|
sev_ioctl(int fd, int cmd, void *data, int *error)
|
|
{
|
|
int r;
|
|
struct kvm_sev_cmd input;
|
|
|
|
memset(&input, 0x0, sizeof(input));
|
|
|
|
input.id = cmd;
|
|
input.sev_fd = fd;
|
|
input.data = (__u64)(unsigned long)data;
|
|
|
|
r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input);
|
|
|
|
if (error) {
|
|
*error = input.error;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
sev_platform_ioctl(int fd, int cmd, void *data, int *error)
|
|
{
|
|
int r;
|
|
struct sev_issue_cmd arg;
|
|
|
|
arg.cmd = cmd;
|
|
arg.data = (unsigned long)data;
|
|
r = ioctl(fd, SEV_ISSUE_CMD, &arg);
|
|
if (error) {
|
|
*error = arg.error;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static const char *
|
|
fw_error_to_str(int code)
|
|
{
|
|
if (code < 0 || code >= SEV_FW_MAX_ERROR) {
|
|
return "unknown error";
|
|
}
|
|
|
|
return sev_fw_errlist[code];
|
|
}
|
|
|
|
static bool
|
|
sev_check_state(SevState state)
|
|
{
|
|
assert(sev_state);
|
|
return sev_state->state == state ? true : false;
|
|
}
|
|
|
|
static void
|
|
sev_set_guest_state(SevState new_state)
|
|
{
|
|
assert(new_state < SEV_STATE__MAX);
|
|
assert(sev_state);
|
|
|
|
trace_kvm_sev_change_state(SevState_str(sev_state->state),
|
|
SevState_str(new_state));
|
|
sev_state->state = new_state;
|
|
}
|
|
|
|
static void
|
|
sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
|
|
{
|
|
int r;
|
|
struct kvm_enc_region range;
|
|
|
|
range.addr = (__u64)(unsigned long)host;
|
|
range.size = size;
|
|
|
|
trace_kvm_memcrypt_register_region(host, size);
|
|
r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
|
|
if (r) {
|
|
error_report("%s: failed to register region (%p+%#zx) error '%s'",
|
|
__func__, host, size, strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size)
|
|
{
|
|
int r;
|
|
struct kvm_enc_region range;
|
|
|
|
range.addr = (__u64)(unsigned long)host;
|
|
range.size = size;
|
|
|
|
trace_kvm_memcrypt_unregister_region(host, size);
|
|
r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range);
|
|
if (r) {
|
|
error_report("%s: failed to unregister region (%p+%#zx)",
|
|
__func__, host, size);
|
|
}
|
|
}
|
|
|
|
static struct RAMBlockNotifier sev_ram_notifier = {
|
|
.ram_block_added = sev_ram_block_added,
|
|
.ram_block_removed = sev_ram_block_removed,
|
|
};
|
|
|
|
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 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;
|
|
}
|
|
|
|
bool
|
|
sev_enabled(void)
|
|
{
|
|
return sev_state ? true : false;
|
|
}
|
|
|
|
uint64_t
|
|
sev_get_me_mask(void)
|
|
{
|
|
return sev_state ? sev_state->me_mask : ~0;
|
|
}
|
|
|
|
uint32_t
|
|
sev_get_cbit_position(void)
|
|
{
|
|
return sev_state ? sev_state->cbitpos : 0;
|
|
}
|
|
|
|
uint32_t
|
|
sev_get_reduced_phys_bits(void)
|
|
{
|
|
return sev_state ? sev_state->reduced_phys_bits : 0;
|
|
}
|
|
|
|
SevInfo *
|
|
sev_get_info(void)
|
|
{
|
|
SevInfo *info;
|
|
|
|
info = g_new0(SevInfo, 1);
|
|
info->enabled = sev_state ? true : false;
|
|
|
|
if (info->enabled) {
|
|
info->api_major = sev_state->api_major;
|
|
info->api_minor = sev_state->api_minor;
|
|
info->build_id = sev_state->build_id;
|
|
info->policy = sev_state->policy;
|
|
info->state = sev_state->state;
|
|
info->handle = sev_state->handle;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
static int
|
|
sev_read_file_base64(const char *filename, guchar **data, gsize *len)
|
|
{
|
|
gsize sz;
|
|
gchar *base64;
|
|
GError *error = NULL;
|
|
|
|
if (!g_file_get_contents(filename, &base64, &sz, &error)) {
|
|
error_report("failed to read '%s' (%s)", filename, error->message);
|
|
return -1;
|
|
}
|
|
|
|
*data = g_base64_decode(base64, len);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sev_launch_start(SEVState *s)
|
|
{
|
|
gsize sz;
|
|
int ret = 1;
|
|
int fw_error;
|
|
QSevGuestInfo *sev = s->sev_info;
|
|
struct kvm_sev_launch_start *start;
|
|
guchar *session = NULL, *dh_cert = NULL;
|
|
|
|
start = g_new0(struct kvm_sev_launch_start, 1);
|
|
|
|
start->handle = object_property_get_int(OBJECT(sev), "handle",
|
|
&error_abort);
|
|
start->policy = object_property_get_int(OBJECT(sev), "policy",
|
|
&error_abort);
|
|
if (sev->session_file) {
|
|
if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
|
|
return 1;
|
|
}
|
|
start->session_uaddr = (unsigned long)session;
|
|
start->session_len = sz;
|
|
}
|
|
|
|
if (sev->dh_cert_file) {
|
|
if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) {
|
|
return 1;
|
|
}
|
|
start->dh_uaddr = (unsigned long)dh_cert;
|
|
start->dh_len = sz;
|
|
}
|
|
|
|
trace_kvm_sev_launch_start(start->policy, session, dh_cert);
|
|
ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
|
|
if (ret < 0) {
|
|
error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
|
|
__func__, ret, fw_error, fw_error_to_str(fw_error));
|
|
return 1;
|
|
}
|
|
|
|
object_property_set_int(OBJECT(sev), start->handle, "handle",
|
|
&error_abort);
|
|
sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE);
|
|
s->handle = start->handle;
|
|
s->policy = start->policy;
|
|
|
|
g_free(start);
|
|
g_free(session);
|
|
g_free(dh_cert);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sev_launch_update_data(uint8_t *addr, uint64_t len)
|
|
{
|
|
int ret, fw_error;
|
|
struct kvm_sev_launch_update_data update;
|
|
|
|
if (!addr || !len) {
|
|
return 1;
|
|
}
|
|
|
|
update.uaddr = (__u64)(unsigned long)addr;
|
|
update.len = len;
|
|
trace_kvm_sev_launch_update_data(addr, len);
|
|
ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
|
|
&update, &fw_error);
|
|
if (ret) {
|
|
error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
|
|
__func__, ret, fw_error, fw_error_to_str(fw_error));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
sev_launch_get_measure(Notifier *notifier, void *unused)
|
|
{
|
|
int ret, error;
|
|
guchar *data;
|
|
SEVState *s = sev_state;
|
|
struct kvm_sev_launch_measure *measurement;
|
|
|
|
if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
|
|
return;
|
|
}
|
|
|
|
measurement = g_new0(struct kvm_sev_launch_measure, 1);
|
|
|
|
/* query the measurement blob length */
|
|
ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
|
|
measurement, &error);
|
|
if (!measurement->len) {
|
|
error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
|
|
__func__, ret, error, fw_error_to_str(errno));
|
|
goto free_measurement;
|
|
}
|
|
|
|
data = g_new0(guchar, measurement->len);
|
|
measurement->uaddr = (unsigned long)data;
|
|
|
|
/* get the measurement blob */
|
|
ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
|
|
measurement, &error);
|
|
if (ret) {
|
|
error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
|
|
__func__, ret, error, fw_error_to_str(errno));
|
|
goto free_data;
|
|
}
|
|
|
|
sev_set_guest_state(SEV_STATE_LAUNCH_SECRET);
|
|
|
|
/* encode the measurement value and emit the event */
|
|
s->measurement = g_base64_encode(data, measurement->len);
|
|
trace_kvm_sev_launch_measurement(s->measurement);
|
|
|
|
free_data:
|
|
g_free(data);
|
|
free_measurement:
|
|
g_free(measurement);
|
|
}
|
|
|
|
char *
|
|
sev_get_launch_measurement(void)
|
|
{
|
|
if (sev_state &&
|
|
sev_state->state >= SEV_STATE_LAUNCH_SECRET) {
|
|
return g_strdup(sev_state->measurement);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static Notifier sev_machine_done_notify = {
|
|
.notify = sev_launch_get_measure,
|
|
};
|
|
|
|
void *
|
|
sev_guest_init(const char *id)
|
|
{
|
|
SEVState *s;
|
|
char *devname;
|
|
int ret, fw_error;
|
|
uint32_t ebx;
|
|
uint32_t host_cbitpos;
|
|
struct sev_user_data_status status = {};
|
|
|
|
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;
|
|
}
|
|
|
|
sev_state = s;
|
|
s->state = SEV_STATE_UNINIT;
|
|
|
|
host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
|
|
host_cbitpos = ebx & 0x3f;
|
|
|
|
s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL);
|
|
if (host_cbitpos != s->cbitpos) {
|
|
error_report("%s: cbitpos check failed, host '%d' requested '%d'",
|
|
__func__, host_cbitpos, s->cbitpos);
|
|
goto err;
|
|
}
|
|
|
|
s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info),
|
|
"reduced-phys-bits", NULL);
|
|
if (s->reduced_phys_bits < 1) {
|
|
error_report("%s: reduced_phys_bits check failed, it should be >=1,"
|
|
"' requested '%d'", __func__, s->reduced_phys_bits);
|
|
goto err;
|
|
}
|
|
|
|
s->me_mask = ~(1UL << s->cbitpos);
|
|
|
|
devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL);
|
|
s->sev_fd = open(devname, O_RDWR);
|
|
if (s->sev_fd < 0) {
|
|
error_report("%s: Failed to open %s '%s'", __func__,
|
|
devname, strerror(errno));
|
|
goto err;
|
|
}
|
|
g_free(devname);
|
|
|
|
ret = sev_platform_ioctl(s->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));
|
|
goto err;
|
|
}
|
|
s->build_id = status.build;
|
|
s->api_major = status.api_major;
|
|
s->api_minor = status.api_minor;
|
|
|
|
trace_kvm_sev_init();
|
|
ret = sev_ioctl(s->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));
|
|
goto err;
|
|
}
|
|
|
|
ret = sev_launch_start(s);
|
|
if (ret) {
|
|
error_report("%s: failed to create encryption context", __func__);
|
|
goto err;
|
|
}
|
|
|
|
ram_block_notifier_add(&sev_ram_notifier);
|
|
qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
|
|
|
|
return s;
|
|
err:
|
|
g_free(sev_state);
|
|
sev_state = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
|
|
{
|
|
assert(handle);
|
|
|
|
/* if SEV is in update state then encrypt the data else do nothing */
|
|
if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
|
|
return sev_launch_update_data(ptr, len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sev_register_types(void)
|
|
{
|
|
type_register_static(&qsev_guest_info);
|
|
}
|
|
|
|
type_init(sev_register_types);
|