qemu-e2k/target/i386/sev.c

453 lines
11 KiB
C
Raw Normal View History

/*
* 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 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;
}
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;
}
return s;
err:
g_free(sev_state);
sev_state = NULL;
return NULL;
}
static void
sev_register_types(void)
{
type_register_static(&qsev_guest_info);
}
type_init(sev_register_types);