2018-03-08 15:48:41 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-12-04 20:25:35 +03:00
|
|
|
#include "qemu/osdep.h"
|
|
|
|
|
2018-03-08 15:48:44 +03:00
|
|
|
#include <linux/kvm.h>
|
|
|
|
#include <linux/psp-sev.h>
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
2018-03-08 15:48:41 +03:00
|
|
|
#include "qapi/error.h"
|
|
|
|
#include "qom/object_interfaces.h"
|
|
|
|
#include "qemu/base64.h"
|
2019-05-23 17:35:07 +03:00
|
|
|
#include "qemu/module.h"
|
2018-03-08 15:48:41 +03:00
|
|
|
#include "sysemu/kvm.h"
|
|
|
|
#include "sev_i386.h"
|
|
|
|
#include "sysemu/sysemu.h"
|
2019-08-12 08:23:59 +03:00
|
|
|
#include "sysemu/runstate.h"
|
2018-03-08 15:48:44 +03:00
|
|
|
#include "trace.h"
|
2018-03-08 15:48:57 +03:00
|
|
|
#include "migration/blocker.h"
|
2020-09-03 23:43:22 +03:00
|
|
|
#include "qom/object.h"
|
2018-03-08 15:48:41 +03:00
|
|
|
|
2020-06-04 09:42:13 +03:00
|
|
|
#define TYPE_SEV_GUEST "sev-guest"
|
2020-09-16 21:25:19 +03:00
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
|
2020-06-04 09:42:12 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2020-06-04 09:42:13 +03:00
|
|
|
* SevGuestState:
|
2020-06-04 09:42:12 +03:00
|
|
|
*
|
2020-06-04 09:42:13 +03:00
|
|
|
* The SevGuestState object is used for creating and managing a SEV
|
|
|
|
* guest.
|
2020-06-04 09:42:12 +03:00
|
|
|
*
|
|
|
|
* # $QEMU \
|
|
|
|
* -object sev-guest,id=sev0 \
|
|
|
|
* -machine ...,memory-encryption=sev0
|
|
|
|
*/
|
2020-06-04 09:42:13 +03:00
|
|
|
struct SevGuestState {
|
2020-06-04 09:42:12 +03:00
|
|
|
Object parent_obj;
|
|
|
|
|
2020-06-04 09:42:14 +03:00
|
|
|
/* configuration parameters */
|
2020-06-04 09:42:12 +03:00
|
|
|
char *sev_device;
|
|
|
|
uint32_t policy;
|
|
|
|
char *dh_cert_file;
|
|
|
|
char *session_file;
|
|
|
|
uint32_t cbitpos;
|
|
|
|
uint32_t reduced_phys_bits;
|
|
|
|
|
2020-06-04 09:42:14 +03:00
|
|
|
/* runtime state */
|
2020-06-04 09:42:18 +03:00
|
|
|
uint32_t handle;
|
2020-06-04 09:42:19 +03:00
|
|
|
uint8_t api_major;
|
|
|
|
uint8_t api_minor;
|
|
|
|
uint8_t build_id;
|
|
|
|
uint64_t me_mask;
|
|
|
|
int sev_fd;
|
|
|
|
SevState state;
|
|
|
|
gchar *measurement;
|
2020-06-04 09:42:12 +03:00
|
|
|
};
|
|
|
|
|
2018-03-08 15:48:41 +03:00
|
|
|
#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
|
|
|
|
#define DEFAULT_SEV_DEVICE "/dev/sev"
|
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
static SevGuestState *sev_guest;
|
2018-03-08 15:48:57 +03:00
|
|
|
static Error *sev_mig_blocker;
|
2018-03-08 15:48:44 +03:00
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:49 +03:00
|
|
|
static bool
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_check_state(const SevGuestState *sev, SevState state)
|
2018-03-08 15:48:49 +03:00
|
|
|
{
|
2020-06-04 09:42:15 +03:00
|
|
|
assert(sev);
|
2020-06-04 09:42:19 +03:00
|
|
|
return sev->state == state ? true : false;
|
2018-03-08 15:48:49 +03:00
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:48 +03:00
|
|
|
static void
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_set_guest_state(SevGuestState *sev, SevState new_state)
|
2018-03-08 15:48:48 +03:00
|
|
|
{
|
|
|
|
assert(new_state < SEV_STATE__MAX);
|
2020-06-04 09:42:15 +03:00
|
|
|
assert(sev);
|
2018-03-08 15:48:48 +03:00
|
|
|
|
2020-06-04 09:42:19 +03:00
|
|
|
trace_kvm_sev_change_state(SevState_str(sev->state),
|
2018-03-08 15:48:48 +03:00
|
|
|
SevState_str(new_state));
|
2020-06-04 09:42:19 +03:00
|
|
|
sev->state = new_state;
|
2018-03-08 15:48:48 +03:00
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:45 +03:00
|
|
|
static void
|
|
|
|
sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
struct kvm_enc_region range;
|
2019-02-05 01:23:40 +03:00
|
|
|
ram_addr_t offset;
|
|
|
|
MemoryRegion *mr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The RAM device presents a memory region that should be treated
|
|
|
|
* as IO region and should not be pinned.
|
|
|
|
*/
|
|
|
|
mr = memory_region_from_host(host, &offset);
|
|
|
|
if (mr && memory_region_is_ram_device(mr)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-03-08 15:48:45 +03:00
|
|
|
|
|
|
|
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;
|
2019-07-15 17:28:39 +03:00
|
|
|
ram_addr_t offset;
|
|
|
|
MemoryRegion *mr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The RAM device presents a memory region that should be treated
|
|
|
|
* as IO region and should not have been pinned.
|
|
|
|
*/
|
|
|
|
mr = memory_region_from_host(host, &offset);
|
|
|
|
if (mr && memory_region_is_ram_device(mr)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-03-08 15:48:45 +03:00
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2018-03-08 15:48:41 +03:00
|
|
|
static void
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_finalize(Object *obj)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_get_session_file(Object *obj, Error **errp)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
return s->session_file ? g_strdup(s->session_file) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_set_session_file(Object *obj, const char *value, Error **errp)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
s->session_file = g_strdup(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_get_dh_cert_file(Object *obj, Error **errp)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
return g_strdup(s->dh_cert_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
s->dh_cert_file = g_strdup(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_get_sev_device(Object *obj, Error **errp)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
return g_strdup(sev->sev_device);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
sev->sev_device = g_strdup(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_class_init(ObjectClass *oc, void *data)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
|
|
|
object_class_property_add_str(oc, "sev-device",
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_get_sev_device,
|
|
|
|
sev_guest_set_sev_device);
|
2018-03-08 15:48:41 +03:00
|
|
|
object_class_property_set_description(oc, "sev-device",
|
2020-05-05 18:29:15 +03:00
|
|
|
"SEV device to use");
|
2018-03-08 15:48:41 +03:00
|
|
|
object_class_property_add_str(oc, "dh-cert-file",
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_get_dh_cert_file,
|
|
|
|
sev_guest_set_dh_cert_file);
|
2018-03-08 15:48:41 +03:00
|
|
|
object_class_property_set_description(oc, "dh-cert-file",
|
2020-05-05 18:29:15 +03:00
|
|
|
"guest owners DH certificate (encoded with base64)");
|
2018-03-08 15:48:41 +03:00
|
|
|
object_class_property_add_str(oc, "session-file",
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_get_session_file,
|
|
|
|
sev_guest_set_session_file);
|
2018-03-08 15:48:41 +03:00
|
|
|
object_class_property_set_description(oc, "session-file",
|
2020-05-05 18:29:15 +03:00
|
|
|
"guest owners session parameters (encoded with base64)");
|
2018-03-08 15:48:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 09:42:13 +03:00
|
|
|
sev_guest_instance_init(Object *obj)
|
2018-03-08 15:48:41 +03:00
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
2018-03-08 15:48:41 +03:00
|
|
|
|
|
|
|
sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
|
|
|
|
sev->policy = DEFAULT_GUEST_POLICY;
|
2020-02-04 16:16:01 +03:00
|
|
|
object_property_add_uint32_ptr(obj, "policy", &sev->policy,
|
qom: Drop parameter @errp of object_property_add() & friends
The only way object_property_add() can fail is when a property with
the same name already exists. Since our property names are all
hardcoded, failure is a programming error, and the appropriate way to
handle it is passing &error_abort.
Same for its variants, except for object_property_add_child(), which
additionally fails when the child already has a parent. Parentage is
also under program control, so this is a programming error, too.
We have a bit over 500 callers. Almost half of them pass
&error_abort, slightly fewer ignore errors, one test case handles
errors, and the remaining few callers pass them to their own callers.
The previous few commits demonstrated once again that ignoring
programming errors is a bad idea.
Of the few ones that pass on errors, several violate the Error API.
The Error ** argument must be NULL, &error_abort, &error_fatal, or a
pointer to a variable containing NULL. Passing an argument of the
latter kind twice without clearing it in between is wrong: if the
first call sets an error, it no longer points to NULL for the second
call. ich9_pm_add_properties(), sparc32_ledma_realize(),
sparc32_dma_realize(), xilinx_axidma_realize(), xilinx_enet_realize()
are wrong that way.
When the one appropriate choice of argument is &error_abort, letting
users pick the argument is a bad idea.
Drop parameter @errp and assert the preconditions instead.
There's one exception to "duplicate property name is a programming
error": the way object_property_add() implements the magic (and
undocumented) "automatic arrayification". Don't drop @errp there.
Instead, rename object_property_add() to object_property_try_add(),
and add the obvious wrapper object_property_add().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200505152926.18877-15-armbru@redhat.com>
[Two semantic rebase conflicts resolved]
2020-05-05 18:29:22 +03:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2020-02-04 16:16:01 +03:00
|
|
|
object_property_add_uint32_ptr(obj, "handle", &sev->handle,
|
qom: Drop parameter @errp of object_property_add() & friends
The only way object_property_add() can fail is when a property with
the same name already exists. Since our property names are all
hardcoded, failure is a programming error, and the appropriate way to
handle it is passing &error_abort.
Same for its variants, except for object_property_add_child(), which
additionally fails when the child already has a parent. Parentage is
also under program control, so this is a programming error, too.
We have a bit over 500 callers. Almost half of them pass
&error_abort, slightly fewer ignore errors, one test case handles
errors, and the remaining few callers pass them to their own callers.
The previous few commits demonstrated once again that ignoring
programming errors is a bad idea.
Of the few ones that pass on errors, several violate the Error API.
The Error ** argument must be NULL, &error_abort, &error_fatal, or a
pointer to a variable containing NULL. Passing an argument of the
latter kind twice without clearing it in between is wrong: if the
first call sets an error, it no longer points to NULL for the second
call. ich9_pm_add_properties(), sparc32_ledma_realize(),
sparc32_dma_realize(), xilinx_axidma_realize(), xilinx_enet_realize()
are wrong that way.
When the one appropriate choice of argument is &error_abort, letting
users pick the argument is a bad idea.
Drop parameter @errp and assert the preconditions instead.
There's one exception to "duplicate property name is a programming
error": the way object_property_add() implements the magic (and
undocumented) "automatic arrayification". Don't drop @errp there.
Instead, rename object_property_add() to object_property_try_add(),
and add the obvious wrapper object_property_add().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200505152926.18877-15-armbru@redhat.com>
[Two semantic rebase conflicts resolved]
2020-05-05 18:29:22 +03:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2020-02-04 16:16:01 +03:00
|
|
|
object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos,
|
qom: Drop parameter @errp of object_property_add() & friends
The only way object_property_add() can fail is when a property with
the same name already exists. Since our property names are all
hardcoded, failure is a programming error, and the appropriate way to
handle it is passing &error_abort.
Same for its variants, except for object_property_add_child(), which
additionally fails when the child already has a parent. Parentage is
also under program control, so this is a programming error, too.
We have a bit over 500 callers. Almost half of them pass
&error_abort, slightly fewer ignore errors, one test case handles
errors, and the remaining few callers pass them to their own callers.
The previous few commits demonstrated once again that ignoring
programming errors is a bad idea.
Of the few ones that pass on errors, several violate the Error API.
The Error ** argument must be NULL, &error_abort, &error_fatal, or a
pointer to a variable containing NULL. Passing an argument of the
latter kind twice without clearing it in between is wrong: if the
first call sets an error, it no longer points to NULL for the second
call. ich9_pm_add_properties(), sparc32_ledma_realize(),
sparc32_dma_realize(), xilinx_axidma_realize(), xilinx_enet_realize()
are wrong that way.
When the one appropriate choice of argument is &error_abort, letting
users pick the argument is a bad idea.
Drop parameter @errp and assert the preconditions instead.
There's one exception to "duplicate property name is a programming
error": the way object_property_add() implements the magic (and
undocumented) "automatic arrayification". Don't drop @errp there.
Instead, rename object_property_add() to object_property_try_add(),
and add the obvious wrapper object_property_add().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200505152926.18877-15-armbru@redhat.com>
[Two semantic rebase conflicts resolved]
2020-05-05 18:29:22 +03:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2020-02-04 16:16:01 +03:00
|
|
|
object_property_add_uint32_ptr(obj, "reduced-phys-bits",
|
|
|
|
&sev->reduced_phys_bits,
|
qom: Drop parameter @errp of object_property_add() & friends
The only way object_property_add() can fail is when a property with
the same name already exists. Since our property names are all
hardcoded, failure is a programming error, and the appropriate way to
handle it is passing &error_abort.
Same for its variants, except for object_property_add_child(), which
additionally fails when the child already has a parent. Parentage is
also under program control, so this is a programming error, too.
We have a bit over 500 callers. Almost half of them pass
&error_abort, slightly fewer ignore errors, one test case handles
errors, and the remaining few callers pass them to their own callers.
The previous few commits demonstrated once again that ignoring
programming errors is a bad idea.
Of the few ones that pass on errors, several violate the Error API.
The Error ** argument must be NULL, &error_abort, &error_fatal, or a
pointer to a variable containing NULL. Passing an argument of the
latter kind twice without clearing it in between is wrong: if the
first call sets an error, it no longer points to NULL for the second
call. ich9_pm_add_properties(), sparc32_ledma_realize(),
sparc32_dma_realize(), xilinx_axidma_realize(), xilinx_enet_realize()
are wrong that way.
When the one appropriate choice of argument is &error_abort, letting
users pick the argument is a bad idea.
Drop parameter @errp and assert the preconditions instead.
There's one exception to "duplicate property name is a programming
error": the way object_property_add() implements the magic (and
undocumented) "automatic arrayification". Don't drop @errp there.
Instead, rename object_property_add() to object_property_try_add(),
and add the obvious wrapper object_property_add().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200505152926.18877-15-armbru@redhat.com>
[Two semantic rebase conflicts resolved]
2020-05-05 18:29:22 +03:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2018-03-08 15:48:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* sev guest info */
|
2020-06-04 09:42:13 +03:00
|
|
|
static const TypeInfo sev_guest_info = {
|
2018-03-08 15:48:41 +03:00
|
|
|
.parent = TYPE_OBJECT,
|
2020-06-04 09:42:13 +03:00
|
|
|
.name = TYPE_SEV_GUEST,
|
|
|
|
.instance_size = sizeof(SevGuestState),
|
|
|
|
.instance_finalize = sev_guest_finalize,
|
|
|
|
.class_init = sev_guest_class_init,
|
|
|
|
.instance_init = sev_guest_instance_init,
|
2018-03-08 15:48:41 +03:00
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ TYPE_USER_CREATABLE },
|
|
|
|
{ }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-06-04 09:42:13 +03:00
|
|
|
static SevGuestState *
|
2018-03-08 15:48:44 +03:00
|
|
|
lookup_sev_guest_info(const char *id)
|
|
|
|
{
|
|
|
|
Object *obj;
|
2020-06-04 09:42:13 +03:00
|
|
|
SevGuestState *info;
|
2018-03-08 15:48:44 +03:00
|
|
|
|
|
|
|
obj = object_resolve_path_component(object_get_objects_root(), id);
|
|
|
|
if (!obj) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:13 +03:00
|
|
|
info = (SevGuestState *)
|
|
|
|
object_dynamic_cast(obj, TYPE_SEV_GUEST);
|
2018-03-08 15:48:44 +03:00
|
|
|
if (!info) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
sev_enabled(void)
|
|
|
|
{
|
2020-06-04 09:42:15 +03:00
|
|
|
return !!sev_guest;
|
2018-03-08 15:48:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
sev_get_me_mask(void)
|
|
|
|
{
|
2020-06-04 09:42:19 +03:00
|
|
|
return sev_guest ? sev_guest->me_mask : ~0;
|
2018-03-08 15:48:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
sev_get_cbit_position(void)
|
|
|
|
{
|
2020-06-04 09:42:16 +03:00
|
|
|
return sev_guest ? sev_guest->cbitpos : 0;
|
2018-03-08 15:48:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
sev_get_reduced_phys_bits(void)
|
|
|
|
{
|
2020-06-04 09:42:16 +03:00
|
|
|
return sev_guest ? sev_guest->reduced_phys_bits : 0;
|
2018-03-08 15:48:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
SevInfo *
|
|
|
|
sev_get_info(void)
|
|
|
|
{
|
|
|
|
SevInfo *info;
|
|
|
|
|
|
|
|
info = g_new0(SevInfo, 1);
|
2020-06-04 09:42:15 +03:00
|
|
|
info->enabled = sev_enabled();
|
2018-03-08 15:48:44 +03:00
|
|
|
|
|
|
|
if (info->enabled) {
|
2020-06-04 09:42:19 +03:00
|
|
|
info->api_major = sev_guest->api_major;
|
|
|
|
info->api_minor = sev_guest->api_minor;
|
|
|
|
info->build_id = sev_guest->build_id;
|
2020-06-04 09:42:17 +03:00
|
|
|
info->policy = sev_guest->policy;
|
2020-06-04 09:42:19 +03:00
|
|
|
info->state = sev_guest->state;
|
2020-06-04 09:42:18 +03:00
|
|
|
info->handle = sev_guest->handle;
|
2018-03-08 15:48:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:49:00 +03:00
|
|
|
static int
|
|
|
|
sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
|
2020-06-30 18:35:46 +03:00
|
|
|
size_t *cert_chain_len, Error **errp)
|
2018-03-08 15:49:00 +03:00
|
|
|
{
|
2018-04-27 16:11:26 +03:00
|
|
|
guchar *pdh_data = NULL;
|
|
|
|
guchar *cert_chain_data = NULL;
|
2018-03-08 15:49:00 +03:00
|
|
|
struct sev_user_data_pdh_cert_export export = {};
|
|
|
|
int err, r;
|
|
|
|
|
|
|
|
/* query the certificate length */
|
|
|
|
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
|
|
|
|
if (r < 0) {
|
|
|
|
if (err != SEV_RET_INVALID_LEN) {
|
2020-06-30 18:35:46 +03:00
|
|
|
error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
|
|
|
|
r, err, fw_error_to_str(err));
|
2018-03-08 15:49:00 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pdh_data = g_new(guchar, export.pdh_cert_len);
|
|
|
|
cert_chain_data = g_new(guchar, export.cert_chain_len);
|
|
|
|
export.pdh_cert_address = (unsigned long)pdh_data;
|
|
|
|
export.cert_chain_address = (unsigned long)cert_chain_data;
|
|
|
|
|
|
|
|
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
|
|
|
|
if (r < 0) {
|
2020-06-30 18:35:46 +03:00
|
|
|
error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
|
|
|
|
r, err, fw_error_to_str(err));
|
2018-03-08 15:49:00 +03:00
|
|
|
goto e_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pdh = pdh_data;
|
|
|
|
*pdh_len = export.pdh_cert_len;
|
|
|
|
*cert_chain = cert_chain_data;
|
|
|
|
*cert_chain_len = export.cert_chain_len;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_free:
|
|
|
|
g_free(pdh_data);
|
|
|
|
g_free(cert_chain_data);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SevCapability *
|
2020-06-30 18:35:46 +03:00
|
|
|
sev_get_capabilities(Error **errp)
|
2018-03-08 15:49:00 +03:00
|
|
|
{
|
2018-04-27 16:11:26 +03:00
|
|
|
SevCapability *cap = NULL;
|
|
|
|
guchar *pdh_data = NULL;
|
|
|
|
guchar *cert_chain_data = NULL;
|
2018-03-08 15:49:00 +03:00
|
|
|
size_t pdh_len = 0, cert_chain_len = 0;
|
|
|
|
uint32_t ebx;
|
|
|
|
int fd;
|
|
|
|
|
2020-06-30 18:38:18 +03:00
|
|
|
if (!kvm_enabled()) {
|
|
|
|
error_setg(errp, "KVM not enabled");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, NULL) < 0) {
|
|
|
|
error_setg(errp, "SEV is not enabled in KVM");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:49:00 +03:00
|
|
|
fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
|
|
|
|
if (fd < 0) {
|
2020-06-30 18:35:46 +03:00
|
|
|
error_setg_errno(errp, errno, "Failed to open %s",
|
|
|
|
DEFAULT_SEV_DEVICE);
|
2018-03-08 15:49:00 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
|
2020-06-30 18:35:46 +03:00
|
|
|
&cert_chain_data, &cert_chain_len, errp)) {
|
2018-04-27 16:11:26 +03:00
|
|
|
goto out;
|
2018-03-08 15:49:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cap = g_new0(SevCapability, 1);
|
|
|
|
cap->pdh = g_base64_encode(pdh_data, pdh_len);
|
|
|
|
cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len);
|
|
|
|
|
|
|
|
host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
|
|
|
|
cap->cbitpos = ebx & 0x3f;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When SEV feature is enabled, we loose one bit in guest physical
|
|
|
|
* addressing.
|
|
|
|
*/
|
|
|
|
cap->reduced_phys_bits = 1;
|
|
|
|
|
2018-04-27 16:11:26 +03:00
|
|
|
out:
|
2018-03-08 15:49:00 +03:00
|
|
|
g_free(pdh_data);
|
|
|
|
g_free(cert_chain_data);
|
|
|
|
close(fd);
|
|
|
|
return cap;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:48 +03:00
|
|
|
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);
|
2020-08-31 16:43:09 +03:00
|
|
|
g_error_free(error);
|
2018-03-08 15:48:48 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = g_base64_decode(base64, len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-06-04 09:42:14 +03:00
|
|
|
sev_launch_start(SevGuestState *sev)
|
2018-03-08 15:48:48 +03:00
|
|
|
{
|
|
|
|
gsize sz;
|
|
|
|
int ret = 1;
|
2018-04-27 16:11:26 +03:00
|
|
|
int fw_error, rc;
|
2018-03-08 15:48:48 +03:00
|
|
|
struct kvm_sev_launch_start *start;
|
|
|
|
guchar *session = NULL, *dh_cert = NULL;
|
|
|
|
|
|
|
|
start = g_new0(struct kvm_sev_launch_start, 1);
|
|
|
|
|
2020-06-04 09:42:18 +03:00
|
|
|
start->handle = sev->handle;
|
2020-06-04 09:42:17 +03:00
|
|
|
start->policy = sev->policy;
|
2018-03-08 15:48:48 +03:00
|
|
|
if (sev->session_file) {
|
|
|
|
if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
|
2018-04-27 16:11:26 +03:00
|
|
|
goto out;
|
2018-03-08 15:48:48 +03:00
|
|
|
}
|
|
|
|
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) {
|
2018-04-27 16:11:26 +03:00
|
|
|
goto out;
|
2018-03-08 15:48:48 +03:00
|
|
|
}
|
|
|
|
start->dh_uaddr = (unsigned long)dh_cert;
|
|
|
|
start->dh_len = sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_kvm_sev_launch_start(start->policy, session, dh_cert);
|
2020-06-04 09:42:19 +03:00
|
|
|
rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
|
2018-04-27 16:11:26 +03:00
|
|
|
if (rc < 0) {
|
2018-03-08 15:48:48 +03:00
|
|
|
error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
|
|
|
|
__func__, ret, fw_error, fw_error_to_str(fw_error));
|
2018-04-27 16:11:26 +03:00
|
|
|
goto out;
|
2018-03-08 15:48:48 +03:00
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE);
|
2020-06-04 09:42:18 +03:00
|
|
|
sev->handle = start->handle;
|
2018-04-27 16:11:26 +03:00
|
|
|
ret = 0;
|
2018-03-08 15:48:48 +03:00
|
|
|
|
2018-04-27 16:11:26 +03:00
|
|
|
out:
|
2018-03-08 15:48:48 +03:00
|
|
|
g_free(start);
|
|
|
|
g_free(session);
|
|
|
|
g_free(dh_cert);
|
2018-04-27 16:11:26 +03:00
|
|
|
return ret;
|
2018-03-08 15:48:48 +03:00
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:49 +03:00
|
|
|
static int
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len)
|
2018-03-08 15:48:49 +03:00
|
|
|
{
|
|
|
|
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);
|
2020-06-04 09:42:19 +03:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
|
2018-03-08 15:48:49 +03:00
|
|
|
&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;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:51 +03:00
|
|
|
static void
|
|
|
|
sev_launch_get_measure(Notifier *notifier, void *unused)
|
|
|
|
{
|
2020-06-04 09:42:15 +03:00
|
|
|
SevGuestState *sev = sev_guest;
|
2018-03-08 15:48:51 +03:00
|
|
|
int ret, error;
|
|
|
|
guchar *data;
|
|
|
|
struct kvm_sev_launch_measure *measurement;
|
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) {
|
2018-03-08 15:48:51 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
measurement = g_new0(struct kvm_sev_launch_measure, 1);
|
|
|
|
|
|
|
|
/* query the measurement blob length */
|
2020-06-04 09:42:19 +03:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
|
2018-03-08 15:48:51 +03:00
|
|
|
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 */
|
2020-06-04 09:42:19 +03:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
|
2018-03-08 15:48:51 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET);
|
2018-03-08 15:48:51 +03:00
|
|
|
|
|
|
|
/* encode the measurement value and emit the event */
|
2020-06-04 09:42:19 +03:00
|
|
|
sev->measurement = g_base64_encode(data, measurement->len);
|
|
|
|
trace_kvm_sev_launch_measurement(sev->measurement);
|
2018-03-08 15:48:51 +03:00
|
|
|
|
|
|
|
free_data:
|
|
|
|
g_free(data);
|
|
|
|
free_measurement:
|
|
|
|
g_free(measurement);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
sev_get_launch_measurement(void)
|
|
|
|
{
|
2020-06-04 09:42:15 +03:00
|
|
|
if (sev_guest &&
|
2020-06-04 09:42:19 +03:00
|
|
|
sev_guest->state >= SEV_STATE_LAUNCH_SECRET) {
|
|
|
|
return g_strdup(sev_guest->measurement);
|
2018-03-08 15:48:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Notifier sev_machine_done_notify = {
|
|
|
|
.notify = sev_launch_get_measure,
|
|
|
|
};
|
|
|
|
|
2018-03-08 15:48:52 +03:00
|
|
|
static void
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_launch_finish(SevGuestState *sev)
|
2018-03-08 15:48:52 +03:00
|
|
|
{
|
|
|
|
int ret, error;
|
2018-03-08 15:48:57 +03:00
|
|
|
Error *local_err = NULL;
|
2018-03-08 15:48:52 +03:00
|
|
|
|
|
|
|
trace_kvm_sev_launch_finish();
|
2020-06-04 09:42:19 +03:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error);
|
2018-03-08 15:48:52 +03:00
|
|
|
if (ret) {
|
|
|
|
error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'",
|
|
|
|
__func__, ret, error, fw_error_to_str(error));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_set_guest_state(sev, SEV_STATE_RUNNING);
|
2018-03-08 15:48:57 +03:00
|
|
|
|
|
|
|
/* add migration blocker */
|
|
|
|
error_setg(&sev_mig_blocker,
|
|
|
|
"SEV: Migration is not implemented");
|
|
|
|
ret = migrate_add_blocker(sev_mig_blocker, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
|
|
|
error_free(sev_mig_blocker);
|
|
|
|
exit(1);
|
|
|
|
}
|
2018-03-08 15:48:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sev_vm_state_change(void *opaque, int running, RunState state)
|
|
|
|
{
|
2020-06-04 09:42:15 +03:00
|
|
|
SevGuestState *sev = opaque;
|
2018-03-08 15:48:52 +03:00
|
|
|
|
|
|
|
if (running) {
|
2020-06-04 09:42:15 +03:00
|
|
|
if (!sev_check_state(sev, SEV_STATE_RUNNING)) {
|
|
|
|
sev_launch_finish(sev);
|
2018-03-08 15:48:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:44 +03:00
|
|
|
void *
|
|
|
|
sev_guest_init(const char *id)
|
|
|
|
{
|
2020-06-04 09:42:14 +03:00
|
|
|
SevGuestState *sev;
|
2018-03-08 15:48:44 +03:00
|
|
|
char *devname;
|
|
|
|
int ret, fw_error;
|
|
|
|
uint32_t ebx;
|
|
|
|
uint32_t host_cbitpos;
|
|
|
|
struct sev_user_data_status status = {};
|
|
|
|
|
2020-06-26 10:22:34 +03:00
|
|
|
ret = ram_block_discard_disable(true);
|
|
|
|
if (ret) {
|
|
|
|
error_report("%s: cannot disable RAM discard", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:14 +03:00
|
|
|
sev = lookup_sev_guest_info(id);
|
|
|
|
if (!sev) {
|
2018-03-08 15:48:44 +03:00
|
|
|
error_report("%s: '%s' is not a valid '%s' object",
|
2020-06-04 09:42:13 +03:00
|
|
|
__func__, id, TYPE_SEV_GUEST);
|
2018-03-08 15:48:44 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_guest = sev;
|
2020-06-04 09:42:19 +03:00
|
|
|
sev->state = SEV_STATE_UNINIT;
|
2018-03-08 15:48:44 +03:00
|
|
|
|
|
|
|
host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
|
|
|
|
host_cbitpos = ebx & 0x3f;
|
|
|
|
|
2020-06-04 09:42:16 +03:00
|
|
|
if (host_cbitpos != sev->cbitpos) {
|
2018-03-08 15:48:44 +03:00
|
|
|
error_report("%s: cbitpos check failed, host '%d' requested '%d'",
|
2020-06-04 09:42:16 +03:00
|
|
|
__func__, host_cbitpos, sev->cbitpos);
|
2018-03-08 15:48:44 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:16 +03:00
|
|
|
if (sev->reduced_phys_bits < 1) {
|
2018-03-08 15:48:44 +03:00
|
|
|
error_report("%s: reduced_phys_bits check failed, it should be >=1,"
|
2020-06-04 09:42:16 +03:00
|
|
|
" requested '%d'", __func__, sev->reduced_phys_bits);
|
2018-03-08 15:48:44 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:19 +03:00
|
|
|
sev->me_mask = ~(1UL << sev->cbitpos);
|
2018-03-08 15:48:44 +03:00
|
|
|
|
2020-06-04 09:42:14 +03:00
|
|
|
devname = object_property_get_str(OBJECT(sev), "sev-device", NULL);
|
2020-06-04 09:42:19 +03:00
|
|
|
sev->sev_fd = open(devname, O_RDWR);
|
|
|
|
if (sev->sev_fd < 0) {
|
2018-03-08 15:48:44 +03:00
|
|
|
error_report("%s: Failed to open %s '%s'", __func__,
|
|
|
|
devname, strerror(errno));
|
|
|
|
}
|
|
|
|
g_free(devname);
|
2020-06-04 09:42:19 +03:00
|
|
|
if (sev->sev_fd < 0) {
|
2018-03-29 12:10:21 +03:00
|
|
|
goto err;
|
|
|
|
}
|
2018-03-08 15:48:44 +03:00
|
|
|
|
2020-06-04 09:42:19 +03:00
|
|
|
ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status,
|
2018-03-08 15:48:44 +03:00
|
|
|
&fw_error);
|
|
|
|
if (ret) {
|
2019-07-19 13:41:18 +03:00
|
|
|
error_report("%s: failed to get platform status ret=%d "
|
2018-03-08 15:48:44 +03:00
|
|
|
"fw_error='%d: %s'", __func__, ret, fw_error,
|
|
|
|
fw_error_to_str(fw_error));
|
|
|
|
goto err;
|
|
|
|
}
|
2020-06-04 09:42:19 +03:00
|
|
|
sev->build_id = status.build;
|
|
|
|
sev->api_major = status.api_major;
|
|
|
|
sev->api_minor = status.api_minor;
|
2018-03-08 15:48:44 +03:00
|
|
|
|
|
|
|
trace_kvm_sev_init();
|
2020-06-04 09:42:19 +03:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_INIT, NULL, &fw_error);
|
2018-03-08 15:48:44 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-06-04 09:42:14 +03:00
|
|
|
ret = sev_launch_start(sev);
|
2018-03-08 15:48:48 +03:00
|
|
|
if (ret) {
|
|
|
|
error_report("%s: failed to create encryption context", __func__);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:45 +03:00
|
|
|
ram_block_notifier_add(&sev_ram_notifier);
|
2018-03-08 15:48:51 +03:00
|
|
|
qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
|
2020-06-04 09:42:15 +03:00
|
|
|
qemu_add_vm_change_state_handler(sev_vm_state_change, sev);
|
2018-03-08 15:48:45 +03:00
|
|
|
|
2020-06-04 09:42:15 +03:00
|
|
|
return sev;
|
2018-03-08 15:48:44 +03:00
|
|
|
err:
|
2020-06-04 09:42:15 +03:00
|
|
|
sev_guest = NULL;
|
2020-06-26 10:22:34 +03:00
|
|
|
ram_block_discard_disable(false);
|
2018-03-08 15:48:44 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:49 +03:00
|
|
|
int
|
|
|
|
sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
|
|
|
|
{
|
2020-06-04 09:42:15 +03:00
|
|
|
SevGuestState *sev = handle;
|
|
|
|
|
|
|
|
assert(sev);
|
2018-03-08 15:48:49 +03:00
|
|
|
|
|
|
|
/* if SEV is in update state then encrypt the data else do nothing */
|
2020-06-04 09:42:15 +03:00
|
|
|
if (sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) {
|
|
|
|
return sev_launch_update_data(sev, ptr, len);
|
2018-03-08 15:48:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:48:41 +03:00
|
|
|
static void
|
|
|
|
sev_register_types(void)
|
|
|
|
{
|
2020-06-04 09:42:13 +03:00
|
|
|
type_register_static(&sev_guest_info);
|
2018-03-08 15:48:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(sev_register_types);
|