2018-03-08 13:48:41 +01: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 18:25:35 +01:00
|
|
|
#include "qemu/osdep.h"
|
|
|
|
|
2018-03-08 13:48:44 +01:00
|
|
|
#include <linux/kvm.h>
|
|
|
|
#include <linux/psp-sev.h>
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
2018-03-08 13:48:41 +01:00
|
|
|
#include "qapi/error.h"
|
|
|
|
#include "qom/object_interfaces.h"
|
|
|
|
#include "qemu/base64.h"
|
2019-05-23 16:35:07 +02:00
|
|
|
#include "qemu/module.h"
|
2021-02-08 15:04:52 +01:00
|
|
|
#include "qemu/uuid.h"
|
2021-09-30 07:49:14 +02:00
|
|
|
#include "crypto/hash.h"
|
2018-03-08 13:48:41 +01:00
|
|
|
#include "sysemu/kvm.h"
|
2021-10-07 18:17:07 +02:00
|
|
|
#include "sev.h"
|
2018-03-08 13:48:41 +01:00
|
|
|
#include "sysemu/sysemu.h"
|
2019-08-12 07:23:59 +02:00
|
|
|
#include "sysemu/runstate.h"
|
2018-03-08 13:48:44 +01:00
|
|
|
#include "trace.h"
|
2018-03-08 13:48:57 +01:00
|
|
|
#include "migration/blocker.h"
|
2020-09-03 22:43:22 +02:00
|
|
|
#include "qom/object.h"
|
2020-10-27 18:03:03 +01:00
|
|
|
#include "monitor/monitor.h"
|
2021-10-07 18:17:15 +02:00
|
|
|
#include "monitor/hmp-target.h"
|
2021-10-07 18:17:10 +02:00
|
|
|
#include "qapi/qapi-commands-misc-target.h"
|
|
|
|
#include "qapi/qmp/qerror.h"
|
2020-05-05 09:00:30 +02:00
|
|
|
#include "exec/confidential-guest-support.h"
|
2021-02-08 15:04:52 +01:00
|
|
|
#include "hw/i386/pc.h"
|
2021-11-11 11:00:48 +01:00
|
|
|
#include "exec/address-spaces.h"
|
2018-03-08 13:48:41 +01:00
|
|
|
|
2020-06-04 08:42:13 +02:00
|
|
|
#define TYPE_SEV_GUEST "sev-guest"
|
2020-09-16 20:25:19 +02:00
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
|
2020-06-04 08:42:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2020-06-04 08:42:13 +02:00
|
|
|
* SevGuestState:
|
2020-06-04 08:42:12 +02:00
|
|
|
*
|
2020-06-04 08:42:13 +02:00
|
|
|
* The SevGuestState object is used for creating and managing a SEV
|
|
|
|
* guest.
|
2020-06-04 08:42:12 +02:00
|
|
|
*
|
|
|
|
* # $QEMU \
|
|
|
|
* -object sev-guest,id=sev0 \
|
|
|
|
* -machine ...,memory-encryption=sev0
|
|
|
|
*/
|
2020-06-04 08:42:13 +02:00
|
|
|
struct SevGuestState {
|
2020-05-05 09:00:30 +02:00
|
|
|
ConfidentialGuestSupport parent_obj;
|
2020-06-04 08:42:12 +02:00
|
|
|
|
2020-06-04 08:42:14 +02:00
|
|
|
/* configuration parameters */
|
2020-06-04 08:42:12 +02:00
|
|
|
char *sev_device;
|
|
|
|
uint32_t policy;
|
|
|
|
char *dh_cert_file;
|
|
|
|
char *session_file;
|
|
|
|
uint32_t cbitpos;
|
|
|
|
uint32_t reduced_phys_bits;
|
2021-11-11 11:00:43 +01:00
|
|
|
bool kernel_hashes;
|
2020-06-04 08:42:12 +02:00
|
|
|
|
2020-06-04 08:42:14 +02:00
|
|
|
/* runtime state */
|
2020-06-04 08:42:18 +02:00
|
|
|
uint32_t handle;
|
2020-06-04 08:42:19 +02:00
|
|
|
uint8_t api_major;
|
|
|
|
uint8_t api_minor;
|
|
|
|
uint8_t build_id;
|
|
|
|
int sev_fd;
|
|
|
|
SevState state;
|
|
|
|
gchar *measurement;
|
2021-02-08 15:04:52 +01:00
|
|
|
|
|
|
|
uint32_t reset_cs;
|
|
|
|
uint32_t reset_ip;
|
|
|
|
bool reset_data_valid;
|
2020-06-04 08:42:12 +02:00
|
|
|
};
|
|
|
|
|
2018-03-08 13:48:41 +01:00
|
|
|
#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
|
|
|
|
#define DEFAULT_SEV_DEVICE "/dev/sev"
|
|
|
|
|
2021-02-08 15:04:52 +01:00
|
|
|
#define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e"
|
|
|
|
typedef struct __attribute__((__packed__)) SevInfoBlock {
|
|
|
|
/* SEV-ES Reset Vector Address */
|
|
|
|
uint32_t reset_addr;
|
|
|
|
} SevInfoBlock;
|
|
|
|
|
2021-09-30 07:49:14 +02:00
|
|
|
#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454"
|
|
|
|
typedef struct QEMU_PACKED SevHashTableDescriptor {
|
|
|
|
/* SEV hash table area guest address */
|
|
|
|
uint32_t base;
|
|
|
|
/* SEV hash table area size (in bytes) */
|
|
|
|
uint32_t size;
|
|
|
|
} SevHashTableDescriptor;
|
|
|
|
|
|
|
|
/* hard code sha256 digest size */
|
|
|
|
#define HASH_SIZE 32
|
|
|
|
|
|
|
|
typedef struct QEMU_PACKED SevHashTableEntry {
|
|
|
|
QemuUUID guid;
|
|
|
|
uint16_t len;
|
|
|
|
uint8_t hash[HASH_SIZE];
|
|
|
|
} SevHashTableEntry;
|
|
|
|
|
|
|
|
typedef struct QEMU_PACKED SevHashTable {
|
|
|
|
QemuUUID guid;
|
|
|
|
uint16_t len;
|
|
|
|
SevHashTableEntry cmdline;
|
|
|
|
SevHashTableEntry initrd;
|
|
|
|
SevHashTableEntry kernel;
|
|
|
|
} SevHashTable;
|
|
|
|
|
2021-11-11 11:00:47 +01:00
|
|
|
/*
|
|
|
|
* Data encrypted by sev_encrypt_flash() must be padded to a multiple of
|
|
|
|
* 16 bytes.
|
|
|
|
*/
|
|
|
|
typedef struct QEMU_PACKED PaddedSevHashTable {
|
|
|
|
SevHashTable ht;
|
|
|
|
uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)];
|
|
|
|
} PaddedSevHashTable;
|
|
|
|
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0);
|
|
|
|
|
2020-06-04 08:42:15 +02:00
|
|
|
static SevGuestState *sev_guest;
|
2018-03-08 13:48:57 +01:00
|
|
|
static Error *sev_mig_blocker;
|
2018-03-08 13:48:44 +01:00
|
|
|
|
|
|
|
static const char *const sev_fw_errlist[] = {
|
2021-04-30 15:48:29 +02:00
|
|
|
[SEV_RET_SUCCESS] = "",
|
|
|
|
[SEV_RET_INVALID_PLATFORM_STATE] = "Platform state is invalid",
|
|
|
|
[SEV_RET_INVALID_GUEST_STATE] = "Guest state is invalid",
|
|
|
|
[SEV_RET_INAVLID_CONFIG] = "Platform configuration is invalid",
|
|
|
|
[SEV_RET_INVALID_LEN] = "Buffer too small",
|
|
|
|
[SEV_RET_ALREADY_OWNED] = "Platform is already owned",
|
|
|
|
[SEV_RET_INVALID_CERTIFICATE] = "Certificate is invalid",
|
|
|
|
[SEV_RET_POLICY_FAILURE] = "Policy is not allowed",
|
|
|
|
[SEV_RET_INACTIVE] = "Guest is not active",
|
|
|
|
[SEV_RET_INVALID_ADDRESS] = "Invalid address",
|
|
|
|
[SEV_RET_BAD_SIGNATURE] = "Bad signature",
|
|
|
|
[SEV_RET_BAD_MEASUREMENT] = "Bad measurement",
|
|
|
|
[SEV_RET_ASID_OWNED] = "ASID is already owned",
|
|
|
|
[SEV_RET_INVALID_ASID] = "Invalid ASID",
|
|
|
|
[SEV_RET_WBINVD_REQUIRED] = "WBINVD is required",
|
|
|
|
[SEV_RET_DFFLUSH_REQUIRED] = "DF_FLUSH is required",
|
|
|
|
[SEV_RET_INVALID_GUEST] = "Guest handle is invalid",
|
|
|
|
[SEV_RET_INVALID_COMMAND] = "Invalid command",
|
|
|
|
[SEV_RET_ACTIVE] = "Guest is active",
|
|
|
|
[SEV_RET_HWSEV_RET_PLATFORM] = "Hardware error",
|
|
|
|
[SEV_RET_HWSEV_RET_UNSAFE] = "Hardware unsafe",
|
|
|
|
[SEV_RET_UNSUPPORTED] = "Feature not supported",
|
|
|
|
[SEV_RET_INVALID_PARAM] = "Invalid parameter",
|
2021-04-30 15:48:30 +02:00
|
|
|
[SEV_RET_RESOURCE_LIMIT] = "Required firmware resource depleted",
|
|
|
|
[SEV_RET_SECURE_DATA_INVALID] = "Part-specific integrity check failure",
|
2018-03-08 13:48:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#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 13:48:49 +01:00
|
|
|
static bool
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_check_state(const SevGuestState *sev, SevState state)
|
2018-03-08 13:48:49 +01:00
|
|
|
{
|
2020-06-04 08:42:15 +02:00
|
|
|
assert(sev);
|
2020-06-04 08:42:19 +02:00
|
|
|
return sev->state == state ? true : false;
|
2018-03-08 13:48:49 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:48 +01:00
|
|
|
static void
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_set_guest_state(SevGuestState *sev, SevState new_state)
|
2018-03-08 13:48:48 +01:00
|
|
|
{
|
|
|
|
assert(new_state < SEV_STATE__MAX);
|
2020-06-04 08:42:15 +02:00
|
|
|
assert(sev);
|
2018-03-08 13:48:48 +01:00
|
|
|
|
2020-06-04 08:42:19 +02:00
|
|
|
trace_kvm_sev_change_state(SevState_str(sev->state),
|
2018-03-08 13:48:48 +01:00
|
|
|
SevState_str(new_state));
|
2020-06-04 08:42:19 +02:00
|
|
|
sev->state = new_state;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:45 +01:00
|
|
|
static void
|
2021-04-29 13:27:00 +02:00
|
|
|
sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size,
|
|
|
|
size_t max_size)
|
2018-03-08 13:48:45 +01:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
struct kvm_enc_region range;
|
2019-02-04 23:23:40 +01: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 13:48:45 +01:00
|
|
|
|
|
|
|
range.addr = (__u64)(unsigned long)host;
|
2021-04-29 13:27:00 +02:00
|
|
|
range.size = max_size;
|
2018-03-08 13:48:45 +01:00
|
|
|
|
2021-04-29 13:27:00 +02:00
|
|
|
trace_kvm_memcrypt_register_region(host, max_size);
|
2018-03-08 13:48:45 +01:00
|
|
|
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'",
|
2021-04-29 13:27:00 +02:00
|
|
|
__func__, host, max_size, strerror(errno));
|
2018-03-08 13:48:45 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-04-29 13:27:00 +02:00
|
|
|
sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size,
|
|
|
|
size_t max_size)
|
2018-03-08 13:48:45 +01:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
struct kvm_enc_region range;
|
2019-07-15 16:28:39 +02: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 13:48:45 +01:00
|
|
|
|
|
|
|
range.addr = (__u64)(unsigned long)host;
|
2021-04-29 13:27:00 +02:00
|
|
|
range.size = max_size;
|
2018-03-08 13:48:45 +01:00
|
|
|
|
2021-04-29 13:27:00 +02:00
|
|
|
trace_kvm_memcrypt_unregister_region(host, max_size);
|
2018-03-08 13:48:45 +01:00
|
|
|
r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range);
|
|
|
|
if (r) {
|
|
|
|
error_report("%s: failed to unregister region (%p+%#zx)",
|
2021-04-29 13:27:00 +02:00
|
|
|
__func__, host, max_size);
|
2018-03-08 13:48:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct RAMBlockNotifier sev_ram_notifier = {
|
|
|
|
.ram_block_added = sev_ram_block_added,
|
|
|
|
.ram_block_removed = sev_ram_block_removed,
|
|
|
|
};
|
|
|
|
|
2018-03-08 13:48:41 +01:00
|
|
|
static void
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_finalize(Object *obj)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_get_session_file(Object *obj, Error **errp)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
return s->session_file ? g_strdup(s->session_file) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_set_session_file(Object *obj, const char *value, Error **errp)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
s->session_file = g_strdup(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_get_dh_cert_file(Object *obj, Error **errp)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
return g_strdup(s->dh_cert_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *s = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
s->dh_cert_file = g_strdup(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_get_sev_device(Object *obj, Error **errp)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
return g_strdup(sev->sev_device);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
sev->sev_device = g_strdup(value);
|
|
|
|
}
|
|
|
|
|
2021-11-11 11:00:43 +01:00
|
|
|
static bool sev_guest_get_kernel_hashes(Object *obj, Error **errp)
|
|
|
|
{
|
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
|
|
|
|
|
|
|
return sev->kernel_hashes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sev_guest_set_kernel_hashes(Object *obj, bool value, Error **errp)
|
|
|
|
{
|
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
|
|
|
|
|
|
|
sev->kernel_hashes = value;
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:41 +01:00
|
|
|
static void
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_class_init(ObjectClass *oc, void *data)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
|
|
|
object_class_property_add_str(oc, "sev-device",
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_get_sev_device,
|
|
|
|
sev_guest_set_sev_device);
|
2018-03-08 13:48:41 +01:00
|
|
|
object_class_property_set_description(oc, "sev-device",
|
2020-05-05 17:29:15 +02:00
|
|
|
"SEV device to use");
|
2018-03-08 13:48:41 +01:00
|
|
|
object_class_property_add_str(oc, "dh-cert-file",
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_get_dh_cert_file,
|
|
|
|
sev_guest_set_dh_cert_file);
|
2018-03-08 13:48:41 +01:00
|
|
|
object_class_property_set_description(oc, "dh-cert-file",
|
2020-05-05 17:29:15 +02:00
|
|
|
"guest owners DH certificate (encoded with base64)");
|
2018-03-08 13:48:41 +01:00
|
|
|
object_class_property_add_str(oc, "session-file",
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_get_session_file,
|
|
|
|
sev_guest_set_session_file);
|
2018-03-08 13:48:41 +01:00
|
|
|
object_class_property_set_description(oc, "session-file",
|
2020-05-05 17:29:15 +02:00
|
|
|
"guest owners session parameters (encoded with base64)");
|
2021-11-11 11:00:43 +01:00
|
|
|
object_class_property_add_bool(oc, "kernel-hashes",
|
|
|
|
sev_guest_get_kernel_hashes,
|
|
|
|
sev_guest_set_kernel_hashes);
|
|
|
|
object_class_property_set_description(oc, "kernel-hashes",
|
|
|
|
"add kernel hashes to guest firmware for measured Linux boot");
|
2018-03-08 13:48:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-06-04 08:42:13 +02:00
|
|
|
sev_guest_instance_init(Object *obj)
|
2018-03-08 13:48:41 +01:00
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
SevGuestState *sev = SEV_GUEST(obj);
|
2018-03-08 13:48:41 +01:00
|
|
|
|
|
|
|
sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
|
|
|
|
sev->policy = DEFAULT_GUEST_POLICY;
|
2020-02-04 14:16:01 +01: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 17:29:22 +02:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2020-02-04 14:16:01 +01: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 17:29:22 +02:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2020-02-04 14:16:01 +01: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 17:29:22 +02:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2020-02-04 14:16:01 +01: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 17:29:22 +02:00
|
|
|
OBJ_PROP_FLAG_READWRITE);
|
2018-03-08 13:48:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* sev guest info */
|
2020-06-04 08:42:13 +02:00
|
|
|
static const TypeInfo sev_guest_info = {
|
2020-05-05 09:00:30 +02:00
|
|
|
.parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT,
|
2020-06-04 08:42:13 +02: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 13:48:41 +01:00
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ TYPE_USER_CREATABLE },
|
|
|
|
{ }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-08 13:48:44 +01:00
|
|
|
bool
|
|
|
|
sev_enabled(void)
|
|
|
|
{
|
2020-06-04 08:42:15 +02:00
|
|
|
return !!sev_guest;
|
2018-03-08 13:48:44 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 18:36:44 +01:00
|
|
|
bool
|
|
|
|
sev_es_enabled(void)
|
|
|
|
{
|
2021-01-26 18:36:49 +01:00
|
|
|
return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES);
|
2021-01-26 18:36:44 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:44 +01:00
|
|
|
uint32_t
|
|
|
|
sev_get_cbit_position(void)
|
|
|
|
{
|
2020-06-04 08:42:16 +02:00
|
|
|
return sev_guest ? sev_guest->cbitpos : 0;
|
2018-03-08 13:48:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
sev_get_reduced_phys_bits(void)
|
|
|
|
{
|
2020-06-04 08:42:16 +02:00
|
|
|
return sev_guest ? sev_guest->reduced_phys_bits : 0;
|
2018-03-08 13:48:44 +01:00
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:14 +02:00
|
|
|
static SevInfo *sev_get_info(void)
|
2018-03-08 13:48:44 +01:00
|
|
|
{
|
|
|
|
SevInfo *info;
|
|
|
|
|
|
|
|
info = g_new0(SevInfo, 1);
|
2020-06-04 08:42:15 +02:00
|
|
|
info->enabled = sev_enabled();
|
2018-03-08 13:48:44 +01:00
|
|
|
|
|
|
|
if (info->enabled) {
|
2020-06-04 08:42:19 +02: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 08:42:17 +02:00
|
|
|
info->policy = sev_guest->policy;
|
2020-06-04 08:42:19 +02:00
|
|
|
info->state = sev_guest->state;
|
2020-06-04 08:42:18 +02:00
|
|
|
info->handle = sev_guest->handle;
|
2018-03-08 13:48:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:14 +02:00
|
|
|
SevInfo *qmp_query_sev(Error **errp)
|
|
|
|
{
|
|
|
|
SevInfo *info;
|
|
|
|
|
|
|
|
info = sev_get_info();
|
|
|
|
if (!info) {
|
|
|
|
error_setg(errp, "SEV feature is not available");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
void hmp_info_sev(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
SevInfo *info = sev_get_info();
|
|
|
|
|
|
|
|
if (info && info->enabled) {
|
|
|
|
monitor_printf(mon, "handle: %d\n", info->handle);
|
|
|
|
monitor_printf(mon, "state: %s\n", SevState_str(info->state));
|
|
|
|
monitor_printf(mon, "build: %d\n", info->build_id);
|
|
|
|
monitor_printf(mon, "api version: %d.%d\n",
|
|
|
|
info->api_major, info->api_minor);
|
|
|
|
monitor_printf(mon, "debug: %s\n",
|
|
|
|
info->policy & SEV_POLICY_NODBG ? "off" : "on");
|
|
|
|
monitor_printf(mon, "key-sharing: %s\n",
|
|
|
|
info->policy & SEV_POLICY_NOKS ? "off" : "on");
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "SEV is not enabled\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
qapi_free_SevInfo(info);
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:49:00 +01:00
|
|
|
static int
|
|
|
|
sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
|
2020-06-30 17:35:46 +02:00
|
|
|
size_t *cert_chain_len, Error **errp)
|
2018-03-08 13:49:00 +01:00
|
|
|
{
|
2018-04-27 15:11:26 +02:00
|
|
|
guchar *pdh_data = NULL;
|
|
|
|
guchar *cert_chain_data = NULL;
|
2018-03-08 13:49:00 +01: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) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg(errp, "SEV: Failed to export PDH cert"
|
|
|
|
" ret=%d fw_err=%d (%s)",
|
2020-06-30 17:35:46 +02:00
|
|
|
r, err, fw_error_to_str(err));
|
2018-03-08 13:49:00 +01: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) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg(errp, "SEV: Failed to export PDH cert ret=%d fw_err=%d (%s)",
|
2020-06-30 17:35:46 +02:00
|
|
|
r, err, fw_error_to_str(err));
|
2018-03-08 13:49:00 +01: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;
|
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:12 +02:00
|
|
|
static SevCapability *sev_get_capabilities(Error **errp)
|
2018-03-08 13:49:00 +01:00
|
|
|
{
|
2018-04-27 15:11:26 +02:00
|
|
|
SevCapability *cap = NULL;
|
|
|
|
guchar *pdh_data = NULL;
|
|
|
|
guchar *cert_chain_data = NULL;
|
2018-03-08 13:49:00 +01:00
|
|
|
size_t pdh_len = 0, cert_chain_len = 0;
|
|
|
|
uint32_t ebx;
|
|
|
|
int fd;
|
|
|
|
|
2020-06-30 17:38:18 +02: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 13:49:00 +01:00
|
|
|
fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
|
|
|
|
if (fd < 0) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg_errno(errp, errno, "SEV: Failed to open %s",
|
2020-06-30 17:35:46 +02:00
|
|
|
DEFAULT_SEV_DEVICE);
|
2018-03-08 13:49:00 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
|
2020-06-30 17:35:46 +02:00
|
|
|
&cert_chain_data, &cert_chain_len, errp)) {
|
2018-04-27 15:11:26 +02:00
|
|
|
goto out;
|
2018-03-08 13:49:00 +01: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 15:11:26 +02:00
|
|
|
out:
|
2018-03-08 13:49:00 +01:00
|
|
|
g_free(pdh_data);
|
|
|
|
g_free(cert_chain_data);
|
|
|
|
close(fd);
|
|
|
|
return cap;
|
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:12 +02:00
|
|
|
SevCapability *qmp_query_sev_capabilities(Error **errp)
|
|
|
|
{
|
|
|
|
return sev_get_capabilities(errp);
|
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:10 +02:00
|
|
|
static SevAttestationReport *sev_get_attestation_report(const char *mnonce,
|
|
|
|
Error **errp)
|
2021-04-29 19:07:28 +02:00
|
|
|
{
|
|
|
|
struct kvm_sev_attestation_report input = {};
|
|
|
|
SevAttestationReport *report = NULL;
|
|
|
|
SevGuestState *sev = sev_guest;
|
2021-10-07 18:17:04 +02:00
|
|
|
g_autofree guchar *data = NULL;
|
|
|
|
g_autofree guchar *buf = NULL;
|
2021-04-29 19:07:28 +02:00
|
|
|
gsize len;
|
|
|
|
int err = 0, ret;
|
|
|
|
|
|
|
|
if (!sev_enabled()) {
|
|
|
|
error_setg(errp, "SEV is not enabled");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lets decode the mnonce string */
|
|
|
|
buf = g_base64_decode(mnonce, &len);
|
|
|
|
if (!buf) {
|
|
|
|
error_setg(errp, "SEV: failed to decode mnonce input");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify the input mnonce length */
|
|
|
|
if (len != sizeof(input.mnonce)) {
|
|
|
|
error_setg(errp, "SEV: mnonce must be %zu bytes (got %" G_GSIZE_FORMAT ")",
|
|
|
|
sizeof(input.mnonce), len);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Query the report length */
|
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
|
|
|
|
&input, &err);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (err != SEV_RET_INVALID_LEN) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg(errp, "SEV: Failed to query the attestation report"
|
|
|
|
" length ret=%d fw_err=%d (%s)",
|
|
|
|
ret, err, fw_error_to_str(err));
|
2021-04-29 19:07:28 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data = g_malloc(input.len);
|
|
|
|
input.uaddr = (unsigned long)data;
|
|
|
|
memcpy(input.mnonce, buf, sizeof(input.mnonce));
|
|
|
|
|
|
|
|
/* Query the report */
|
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
|
|
|
|
&input, &err);
|
|
|
|
if (ret) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg_errno(errp, errno, "SEV: Failed to get attestation report"
|
2021-04-29 19:07:28 +02:00
|
|
|
" ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err));
|
2021-10-07 18:17:04 +02:00
|
|
|
return NULL;
|
2021-04-29 19:07:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
report = g_new0(SevAttestationReport, 1);
|
|
|
|
report->data = g_base64_encode(data, input.len);
|
|
|
|
|
|
|
|
trace_kvm_sev_attestation_report(mnonce, report->data);
|
|
|
|
|
|
|
|
return report;
|
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:10 +02:00
|
|
|
SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
return sev_get_attestation_report(mnonce, errp);
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:48 +01:00
|
|
|
static int
|
|
|
|
sev_read_file_base64(const char *filename, guchar **data, gsize *len)
|
|
|
|
{
|
|
|
|
gsize sz;
|
2021-08-20 18:56:50 +02:00
|
|
|
g_autofree gchar *base64 = NULL;
|
2018-03-08 13:48:48 +01:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
if (!g_file_get_contents(filename, &base64, &sz, &error)) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_report("SEV: Failed to read '%s' (%s)", filename, error->message);
|
2020-08-31 15:43:09 +02:00
|
|
|
g_error_free(error);
|
2018-03-08 13:48:48 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = g_base64_decode(base64, len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-06-04 08:42:14 +02:00
|
|
|
sev_launch_start(SevGuestState *sev)
|
2018-03-08 13:48:48 +01:00
|
|
|
{
|
|
|
|
gsize sz;
|
|
|
|
int ret = 1;
|
2018-04-27 15:11:26 +02:00
|
|
|
int fw_error, rc;
|
2021-10-11 19:30:25 +02:00
|
|
|
struct kvm_sev_launch_start start = {
|
|
|
|
.handle = sev->handle, .policy = sev->policy
|
|
|
|
};
|
2018-03-08 13:48:48 +01:00
|
|
|
guchar *session = NULL, *dh_cert = NULL;
|
|
|
|
|
|
|
|
if (sev->session_file) {
|
|
|
|
if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
|
2018-04-27 15:11:26 +02:00
|
|
|
goto out;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
2021-10-11 19:30:25 +02:00
|
|
|
start.session_uaddr = (unsigned long)session;
|
|
|
|
start.session_len = sz;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sev->dh_cert_file) {
|
|
|
|
if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) {
|
2018-04-27 15:11:26 +02:00
|
|
|
goto out;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
2021-10-11 19:30:25 +02:00
|
|
|
start.dh_uaddr = (unsigned long)dh_cert;
|
|
|
|
start.dh_len = sz;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
|
|
|
|
2021-10-11 19:30:25 +02:00
|
|
|
trace_kvm_sev_launch_start(start.policy, session, dh_cert);
|
|
|
|
rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error);
|
2018-04-27 15:11:26 +02:00
|
|
|
if (rc < 0) {
|
2018-03-08 13:48:48 +01: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 15:11:26 +02:00
|
|
|
goto out;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
|
|
|
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE);
|
2021-10-11 19:30:25 +02:00
|
|
|
sev->handle = start.handle;
|
2018-04-27 15:11:26 +02:00
|
|
|
ret = 0;
|
2018-03-08 13:48:48 +01:00
|
|
|
|
2018-04-27 15:11:26 +02:00
|
|
|
out:
|
2018-03-08 13:48:48 +01:00
|
|
|
g_free(session);
|
|
|
|
g_free(dh_cert);
|
2018-04-27 15:11:26 +02:00
|
|
|
return ret;
|
2018-03-08 13:48:48 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:49 +01:00
|
|
|
static int
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len)
|
2018-03-08 13:48:49 +01: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 08:42:19 +02:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
|
2018-03-08 13:48:49 +01: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;
|
|
|
|
}
|
|
|
|
|
2021-01-26 18:36:44 +01:00
|
|
|
static int
|
|
|
|
sev_launch_update_vmsa(SevGuestState *sev)
|
|
|
|
{
|
|
|
|
int ret, fw_error;
|
|
|
|
|
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error);
|
|
|
|
if (ret) {
|
|
|
|
error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'",
|
|
|
|
__func__, ret, fw_error, fw_error_to_str(fw_error));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:51 +01:00
|
|
|
static void
|
|
|
|
sev_launch_get_measure(Notifier *notifier, void *unused)
|
|
|
|
{
|
2020-06-04 08:42:15 +02:00
|
|
|
SevGuestState *sev = sev_guest;
|
2018-03-08 13:48:51 +01:00
|
|
|
int ret, error;
|
2021-10-07 18:17:05 +02:00
|
|
|
g_autofree guchar *data = NULL;
|
2021-10-11 19:30:26 +02:00
|
|
|
struct kvm_sev_launch_measure measurement = {};
|
2018-03-08 13:48:51 +01:00
|
|
|
|
2020-06-04 08:42:15 +02:00
|
|
|
if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) {
|
2018-03-08 13:48:51 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-26 18:36:44 +01:00
|
|
|
if (sev_es_enabled()) {
|
|
|
|
/* measure all the VM save areas before getting launch_measure */
|
|
|
|
ret = sev_launch_update_vmsa(sev);
|
|
|
|
if (ret) {
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:51 +01:00
|
|
|
/* query the measurement blob length */
|
2020-06-04 08:42:19 +02:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
|
2021-10-11 19:30:26 +02:00
|
|
|
&measurement, &error);
|
|
|
|
if (!measurement.len) {
|
2018-03-08 13:48:51 +01:00
|
|
|
error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
|
|
|
|
__func__, ret, error, fw_error_to_str(errno));
|
2021-10-07 18:17:05 +02:00
|
|
|
return;
|
2018-03-08 13:48:51 +01:00
|
|
|
}
|
|
|
|
|
2021-10-11 19:30:26 +02:00
|
|
|
data = g_new0(guchar, measurement.len);
|
|
|
|
measurement.uaddr = (unsigned long)data;
|
2018-03-08 13:48:51 +01:00
|
|
|
|
|
|
|
/* get the measurement blob */
|
2020-06-04 08:42:19 +02:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
|
2021-10-11 19:30:26 +02:00
|
|
|
&measurement, &error);
|
2018-03-08 13:48:51 +01:00
|
|
|
if (ret) {
|
|
|
|
error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
|
|
|
|
__func__, ret, error, fw_error_to_str(errno));
|
2021-10-07 18:17:05 +02:00
|
|
|
return;
|
2018-03-08 13:48:51 +01:00
|
|
|
}
|
|
|
|
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET);
|
2018-03-08 13:48:51 +01:00
|
|
|
|
|
|
|
/* encode the measurement value and emit the event */
|
2021-10-11 19:30:26 +02:00
|
|
|
sev->measurement = g_base64_encode(data, measurement.len);
|
2020-06-04 08:42:19 +02:00
|
|
|
trace_kvm_sev_launch_measurement(sev->measurement);
|
2018-03-08 13:48:51 +01:00
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:13 +02:00
|
|
|
static char *sev_get_launch_measurement(void)
|
2018-03-08 13:48:51 +01:00
|
|
|
{
|
2020-06-04 08:42:15 +02:00
|
|
|
if (sev_guest &&
|
2020-06-04 08:42:19 +02:00
|
|
|
sev_guest->state >= SEV_STATE_LAUNCH_SECRET) {
|
|
|
|
return g_strdup(sev_guest->measurement);
|
2018-03-08 13:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-10-07 18:17:13 +02:00
|
|
|
SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp)
|
|
|
|
{
|
|
|
|
char *data;
|
|
|
|
SevLaunchMeasureInfo *info;
|
|
|
|
|
|
|
|
data = sev_get_launch_measurement();
|
|
|
|
if (!data) {
|
|
|
|
error_setg(errp, "SEV launch measurement is not available");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
info = g_malloc0(sizeof(*info));
|
|
|
|
info->data = data;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:51 +01:00
|
|
|
static Notifier sev_machine_done_notify = {
|
|
|
|
.notify = sev_launch_get_measure,
|
|
|
|
};
|
|
|
|
|
2018-03-08 13:48:52 +01:00
|
|
|
static void
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_launch_finish(SevGuestState *sev)
|
2018-03-08 13:48:52 +01:00
|
|
|
{
|
|
|
|
int ret, error;
|
|
|
|
|
|
|
|
trace_kvm_sev_launch_finish();
|
2020-06-04 08:42:19 +02:00
|
|
|
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error);
|
2018-03-08 13:48:52 +01: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 08:42:15 +02:00
|
|
|
sev_set_guest_state(sev, SEV_STATE_RUNNING);
|
2018-03-08 13:48:57 +01:00
|
|
|
|
|
|
|
/* add migration blocker */
|
|
|
|
error_setg(&sev_mig_blocker,
|
|
|
|
"SEV: Migration is not implemented");
|
2021-07-20 14:53:53 +02:00
|
|
|
migrate_add_blocker(sev_mig_blocker, &error_fatal);
|
2018-03-08 13:48:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-01-11 16:20:20 +01:00
|
|
|
sev_vm_state_change(void *opaque, bool running, RunState state)
|
2018-03-08 13:48:52 +01:00
|
|
|
{
|
2020-06-04 08:42:15 +02:00
|
|
|
SevGuestState *sev = opaque;
|
2018-03-08 13:48:52 +01:00
|
|
|
|
|
|
|
if (running) {
|
2020-06-04 08:42:15 +02:00
|
|
|
if (!sev_check_state(sev, SEV_STATE_RUNNING)) {
|
|
|
|
sev_launch_finish(sev);
|
2018-03-08 13:48:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-04 06:18:52 +02:00
|
|
|
int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
|
2018-03-08 13:48:44 +01:00
|
|
|
{
|
2020-10-16 05:52:30 +02:00
|
|
|
SevGuestState *sev
|
|
|
|
= (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST);
|
2018-03-08 13:48:44 +01:00
|
|
|
char *devname;
|
2021-01-26 18:36:44 +01:00
|
|
|
int ret, fw_error, cmd;
|
2018-03-08 13:48:44 +01:00
|
|
|
uint32_t ebx;
|
|
|
|
uint32_t host_cbitpos;
|
|
|
|
struct sev_user_data_status status = {};
|
|
|
|
|
2020-10-16 05:52:30 +02:00
|
|
|
if (!sev) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-26 09:22:34 +02:00
|
|
|
ret = ram_block_discard_disable(true);
|
|
|
|
if (ret) {
|
|
|
|
error_report("%s: cannot disable RAM discard", __func__);
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
return -1;
|
2020-06-26 09:22:34 +02:00
|
|
|
}
|
|
|
|
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_guest = sev;
|
2020-06-04 08:42:19 +02:00
|
|
|
sev->state = SEV_STATE_UNINIT;
|
2018-03-08 13:48:44 +01:00
|
|
|
|
|
|
|
host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
|
|
|
|
host_cbitpos = ebx & 0x3f;
|
|
|
|
|
2020-06-04 08:42:16 +02:00
|
|
|
if (host_cbitpos != sev->cbitpos) {
|
2020-06-04 06:18:52 +02:00
|
|
|
error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'",
|
|
|
|
__func__, host_cbitpos, sev->cbitpos);
|
2018-03-08 13:48:44 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 08:42:16 +02:00
|
|
|
if (sev->reduced_phys_bits < 1) {
|
2020-06-04 06:18:52 +02:00
|
|
|
error_setg(errp, "%s: reduced_phys_bits check failed, it should be >=1,"
|
|
|
|
" requested '%d'", __func__, sev->reduced_phys_bits);
|
2018-03-08 13:48:44 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 08:42:14 +02:00
|
|
|
devname = object_property_get_str(OBJECT(sev), "sev-device", NULL);
|
2020-06-04 08:42:19 +02:00
|
|
|
sev->sev_fd = open(devname, O_RDWR);
|
|
|
|
if (sev->sev_fd < 0) {
|
2020-06-04 06:18:52 +02:00
|
|
|
error_setg(errp, "%s: Failed to open %s '%s'", __func__,
|
|
|
|
devname, strerror(errno));
|
|
|
|
g_free(devname);
|
2018-03-29 11:10:21 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2020-06-04 06:18:52 +02:00
|
|
|
g_free(devname);
|
2018-03-08 13:48:44 +01:00
|
|
|
|
2020-06-04 08:42:19 +02:00
|
|
|
ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status,
|
2018-03-08 13:48:44 +01:00
|
|
|
&fw_error);
|
|
|
|
if (ret) {
|
2020-06-04 06:18:52 +02:00
|
|
|
error_setg(errp, "%s: failed to get platform status ret=%d "
|
|
|
|
"fw_error='%d: %s'", __func__, ret, fw_error,
|
|
|
|
fw_error_to_str(fw_error));
|
2018-03-08 13:48:44 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2020-06-04 08:42:19 +02:00
|
|
|
sev->build_id = status.build;
|
|
|
|
sev->api_major = status.api_major;
|
|
|
|
sev->api_minor = status.api_minor;
|
2018-03-08 13:48:44 +01:00
|
|
|
|
2021-01-26 18:36:44 +01:00
|
|
|
if (sev_es_enabled()) {
|
2021-01-26 18:36:45 +01:00
|
|
|
if (!kvm_kernel_irqchip_allowed()) {
|
|
|
|
error_report("%s: SEV-ES guests require in-kernel irqchip support",
|
|
|
|
__func__);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2021-01-26 18:36:44 +01:00
|
|
|
if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) {
|
|
|
|
error_report("%s: guest policy requires SEV-ES, but "
|
|
|
|
"host SEV-ES support unavailable",
|
|
|
|
__func__);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
cmd = KVM_SEV_ES_INIT;
|
|
|
|
} else {
|
|
|
|
cmd = KVM_SEV_INIT;
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:44 +01:00
|
|
|
trace_kvm_sev_init();
|
2021-01-26 18:36:44 +01:00
|
|
|
ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error);
|
2018-03-08 13:48:44 +01:00
|
|
|
if (ret) {
|
2020-06-04 06:18:52 +02:00
|
|
|
error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'",
|
|
|
|
__func__, ret, fw_error, fw_error_to_str(fw_error));
|
2018-03-08 13:48:44 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 08:42:14 +02:00
|
|
|
ret = sev_launch_start(sev);
|
2018-03-08 13:48:48 +01:00
|
|
|
if (ret) {
|
2020-06-04 06:18:52 +02:00
|
|
|
error_setg(errp, "%s: failed to create encryption context", __func__);
|
2018-03-08 13:48:48 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:45 +01:00
|
|
|
ram_block_notifier_add(&sev_ram_notifier);
|
2018-03-08 13:48:51 +01:00
|
|
|
qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
|
2020-06-04 08:42:15 +02:00
|
|
|
qemu_add_vm_change_state_handler(sev_vm_state_change, sev);
|
2018-03-08 13:48:45 +01:00
|
|
|
|
2020-10-20 08:01:19 +02:00
|
|
|
cgs->ready = true;
|
|
|
|
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
return 0;
|
2018-03-08 13:48:44 +01:00
|
|
|
err:
|
2020-06-04 08:42:15 +02:00
|
|
|
sev_guest = NULL;
|
2020-06-26 09:22:34 +02:00
|
|
|
ram_block_discard_disable(false);
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
return -1;
|
2018-03-08 13:48:44 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:49 +01:00
|
|
|
int
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
|
2018-03-08 13:48:49 +01:00
|
|
|
{
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
if (!sev_guest) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-03-08 13:48:49 +01:00
|
|
|
|
|
|
|
/* if SEV is in update state then encrypt the data else do nothing */
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) {
|
|
|
|
int ret = sev_launch_update_data(sev_guest, ptr, len);
|
|
|
|
if (ret < 0) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg(errp, "SEV: Failed to encrypt pflash rom");
|
sev: Remove false abstraction of flash encryption
When AMD's SEV memory encryption is in use, flash memory banks (which are
initialed by pc_system_flash_map()) need to be encrypted with the guest's
key, so that the guest can read them.
That's abstracted via the kvm_memcrypt_encrypt_data() callback in the KVM
state.. except, that it doesn't really abstract much at all.
For starters, the only call site is in code specific to the 'pc'
family of machine types, so it's obviously specific to those and to
x86 to begin with. But it makes a bunch of further assumptions that
need not be true about an arbitrary confidential guest system based on
memory encryption, let alone one based on other mechanisms:
* it assumes that the flash memory is defined to be encrypted with the
guest key, rather than being shared with hypervisor
* it assumes that that hypervisor has some mechanism to encrypt data into
the guest, even though it can't decrypt it out, since that's the whole
point
* the interface assumes that this encrypt can be done in place, which
implies that the hypervisor can write into a confidential guests's
memory, even if what it writes isn't meaningful
So really, this "abstraction" is actually pretty specific to the way SEV
works. So, this patch removes it and instead has the PC flash
initialization code call into a SEV specific callback.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
2021-01-12 01:58:04 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2018-03-08 13:48:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-27 18:03:03 +01:00
|
|
|
int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
|
|
|
|
uint64_t gpa, Error **errp)
|
|
|
|
{
|
|
|
|
struct kvm_sev_launch_secret input;
|
|
|
|
g_autofree guchar *data = NULL, *hdr = NULL;
|
|
|
|
int error, ret = 1;
|
|
|
|
void *hva;
|
|
|
|
gsize hdr_sz = 0, data_sz = 0;
|
|
|
|
MemoryRegion *mr = NULL;
|
|
|
|
|
|
|
|
if (!sev_guest) {
|
2021-10-07 18:16:58 +02:00
|
|
|
error_setg(errp, "SEV not enabled for guest");
|
2020-10-27 18:03:03 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* secret can be injected only in this state */
|
|
|
|
if (!sev_check_state(sev_guest, SEV_STATE_LAUNCH_SECRET)) {
|
|
|
|
error_setg(errp, "SEV: Not in correct state. (LSECRET) %x",
|
|
|
|
sev_guest->state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = g_base64_decode(packet_hdr, &hdr_sz);
|
|
|
|
if (!hdr || !hdr_sz) {
|
|
|
|
error_setg(errp, "SEV: Failed to decode sequence header");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = g_base64_decode(secret, &data_sz);
|
|
|
|
if (!data || !data_sz) {
|
|
|
|
error_setg(errp, "SEV: Failed to decode data");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hva = gpa2hva(&mr, gpa, data_sz, errp);
|
|
|
|
if (!hva) {
|
|
|
|
error_prepend(errp, "SEV: Failed to calculate guest address: ");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
input.hdr_uaddr = (uint64_t)(unsigned long)hdr;
|
|
|
|
input.hdr_len = hdr_sz;
|
|
|
|
|
|
|
|
input.trans_uaddr = (uint64_t)(unsigned long)data;
|
|
|
|
input.trans_len = data_sz;
|
|
|
|
|
|
|
|
input.guest_uaddr = (uint64_t)(unsigned long)hva;
|
|
|
|
input.guest_len = data_sz;
|
|
|
|
|
|
|
|
trace_kvm_sev_launch_secret(gpa, input.guest_uaddr,
|
|
|
|
input.trans_uaddr, input.trans_len);
|
|
|
|
|
|
|
|
ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_LAUNCH_SECRET,
|
|
|
|
&input, &error);
|
|
|
|
if (ret) {
|
|
|
|
error_setg(errp, "SEV: failed to inject secret ret=%d fw_error=%d '%s'",
|
|
|
|
ret, error, fw_error_to_str(error));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
target/i386/sev: Move qmp_sev_inject_launch_secret() to sev.c
Move qmp_sev_inject_launch_secret() from monitor.c to sev.c
and make sev_inject_launch_secret() static. We don't need the
stub anymore, remove it.
Previously with binaries built without SEV, management layer
was getting an empty response:
{ "execute": "sev-inject-launch-secret",
"arguments": { "packet-header": "mypkt", "secret": "mypass", "gpa": 4294959104 }
}
{
"return": {
}
}
Now the response is explicit, mentioning the feature is disabled:
{ "execute": "sev-inject-launch-secret",
"arguments": { "packet-header": "mypkt", "secret": "mypass", "gpa": 4294959104 }
}
{
"error": {
"class": "GenericError",
"desc": "this feature or command is not currently supported"
}
}
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20211007161716.453984-19-philmd@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-10-07 18:17:11 +02:00
|
|
|
#define SEV_SECRET_GUID "4c2eb361-7d9b-4cc3-8081-127c90d3d294"
|
|
|
|
struct sev_secret_area {
|
|
|
|
uint32_t base;
|
|
|
|
uint32_t size;
|
|
|
|
};
|
|
|
|
|
|
|
|
void qmp_sev_inject_launch_secret(const char *packet_hdr,
|
|
|
|
const char *secret,
|
|
|
|
bool has_gpa, uint64_t gpa,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
if (!sev_enabled()) {
|
|
|
|
error_setg(errp, "SEV not enabled for guest");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!has_gpa) {
|
|
|
|
uint8_t *data;
|
|
|
|
struct sev_secret_area *area;
|
|
|
|
|
|
|
|
if (!pc_system_ovmf_table_find(SEV_SECRET_GUID, &data, NULL)) {
|
|
|
|
error_setg(errp, "SEV: no secret area found in OVMF,"
|
|
|
|
" gpa must be specified.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
area = (struct sev_secret_area *)data;
|
|
|
|
gpa = area->base;
|
|
|
|
}
|
|
|
|
|
|
|
|
sev_inject_launch_secret(packet_hdr, secret, gpa, errp);
|
|
|
|
}
|
|
|
|
|
2021-02-08 15:04:52 +01:00
|
|
|
static int
|
|
|
|
sev_es_parse_reset_block(SevInfoBlock *info, uint32_t *addr)
|
|
|
|
{
|
|
|
|
if (!info->reset_addr) {
|
|
|
|
error_report("SEV-ES reset address is zero");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*addr = info->reset_addr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sev_es_find_reset_vector(void *flash_ptr, uint64_t flash_size,
|
|
|
|
uint32_t *addr)
|
|
|
|
{
|
|
|
|
QemuUUID info_guid, *guid;
|
|
|
|
SevInfoBlock *info;
|
|
|
|
uint8_t *data;
|
|
|
|
uint16_t *len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the address to zero. An address of zero with a successful
|
|
|
|
* return code indicates that SEV-ES is not active.
|
|
|
|
*/
|
|
|
|
*addr = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the AP reset vector for SEV-ES guests by locating the SEV GUID.
|
|
|
|
* The SEV GUID is located on its own (original implementation) or within
|
|
|
|
* the Firmware GUID Table (new implementation), either of which are
|
|
|
|
* located 32 bytes from the end of the flash.
|
|
|
|
*
|
|
|
|
* Check the Firmware GUID Table first.
|
|
|
|
*/
|
|
|
|
if (pc_system_ovmf_table_find(SEV_INFO_BLOCK_GUID, &data, NULL)) {
|
|
|
|
return sev_es_parse_reset_block((SevInfoBlock *)data, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SEV info block not found in the Firmware GUID Table (or there isn't
|
|
|
|
* a Firmware GUID Table), fall back to the original implementation.
|
|
|
|
*/
|
|
|
|
data = flash_ptr + flash_size - 0x20;
|
|
|
|
|
|
|
|
qemu_uuid_parse(SEV_INFO_BLOCK_GUID, &info_guid);
|
|
|
|
info_guid = qemu_uuid_bswap(info_guid); /* GUIDs are LE */
|
|
|
|
|
|
|
|
guid = (QemuUUID *)(data - sizeof(info_guid));
|
|
|
|
if (!qemu_uuid_is_equal(guid, &info_guid)) {
|
|
|
|
error_report("SEV information block/Firmware GUID Table block not found in pflash rom");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = (uint16_t *)((uint8_t *)guid - sizeof(*len));
|
|
|
|
info = (SevInfoBlock *)(data - le16_to_cpu(*len));
|
|
|
|
|
|
|
|
return sev_es_parse_reset_block(info, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sev_es_set_reset_vector(CPUState *cpu)
|
|
|
|
{
|
|
|
|
X86CPU *x86;
|
|
|
|
CPUX86State *env;
|
|
|
|
|
|
|
|
/* Only update if we have valid reset information */
|
|
|
|
if (!sev_guest || !sev_guest->reset_data_valid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do not update the BSP reset state */
|
|
|
|
if (cpu->cpu_index == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
x86 = X86_CPU(cpu);
|
|
|
|
env = &x86->env;
|
|
|
|
|
|
|
|
cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_guest->reset_cs, 0xffff,
|
|
|
|
DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK |
|
|
|
|
DESC_R_MASK | DESC_A_MASK);
|
|
|
|
|
|
|
|
env->eip = sev_guest->reset_ip;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
|
|
|
|
{
|
|
|
|
CPUState *cpu;
|
|
|
|
uint32_t addr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!sev_es_enabled()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = 0;
|
|
|
|
ret = sev_es_find_reset_vector(flash_ptr, flash_size,
|
|
|
|
&addr);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr) {
|
|
|
|
sev_guest->reset_cs = addr & 0xffff0000;
|
|
|
|
sev_guest->reset_ip = addr & 0x0000ffff;
|
|
|
|
sev_guest->reset_data_valid = true;
|
|
|
|
|
|
|
|
CPU_FOREACH(cpu) {
|
|
|
|
sev_es_set_reset_vector(cpu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-09-30 07:49:14 +02:00
|
|
|
static const QemuUUID sev_hash_table_header_guid = {
|
|
|
|
.data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93,
|
|
|
|
0xd4, 0x11, 0xfd, 0x21)
|
|
|
|
};
|
|
|
|
|
|
|
|
static const QemuUUID sev_kernel_entry_guid = {
|
|
|
|
.data = UUID_LE(0x4de79437, 0xabd2, 0x427f, 0xb8, 0x35, 0xd5, 0xb1,
|
|
|
|
0x72, 0xd2, 0x04, 0x5b)
|
|
|
|
};
|
|
|
|
static const QemuUUID sev_initrd_entry_guid = {
|
|
|
|
.data = UUID_LE(0x44baf731, 0x3a2f, 0x4bd7, 0x9a, 0xf1, 0x41, 0xe2,
|
|
|
|
0x91, 0x69, 0x78, 0x1d)
|
|
|
|
};
|
|
|
|
static const QemuUUID sev_cmdline_entry_guid = {
|
|
|
|
.data = UUID_LE(0x97d02dd8, 0xbd20, 0x4c94, 0xaa, 0x78, 0xe7, 0x71,
|
|
|
|
0x4d, 0x36, 0xab, 0x2a)
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page
|
|
|
|
* which is included in SEV's initial memory measurement.
|
|
|
|
*/
|
|
|
|
bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp)
|
|
|
|
{
|
|
|
|
uint8_t *data;
|
|
|
|
SevHashTableDescriptor *area;
|
|
|
|
SevHashTable *ht;
|
2021-11-11 11:00:47 +01:00
|
|
|
PaddedSevHashTable *padded_ht;
|
2021-09-30 07:49:14 +02:00
|
|
|
uint8_t cmdline_hash[HASH_SIZE];
|
|
|
|
uint8_t initrd_hash[HASH_SIZE];
|
|
|
|
uint8_t kernel_hash[HASH_SIZE];
|
|
|
|
uint8_t *hashp;
|
|
|
|
size_t hash_len = HASH_SIZE;
|
2021-11-11 11:00:48 +01:00
|
|
|
hwaddr mapped_len = sizeof(*padded_ht);
|
|
|
|
MemTxAttrs attrs = { 0 };
|
|
|
|
bool ret = true;
|
2021-09-30 07:49:14 +02:00
|
|
|
|
2021-11-11 11:00:44 +01:00
|
|
|
/*
|
|
|
|
* Only add the kernel hashes if the sev-guest configuration explicitly
|
|
|
|
* stated kernel-hashes=on.
|
|
|
|
*/
|
|
|
|
if (!sev_guest->kernel_hashes) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-09-30 07:49:14 +02:00
|
|
|
if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) {
|
2021-11-11 11:00:45 +01:00
|
|
|
error_setg(errp, "SEV: kernel specified but guest firmware "
|
|
|
|
"has no hashes table GUID");
|
2021-09-30 07:49:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
area = (SevHashTableDescriptor *)data;
|
2021-11-11 11:00:47 +01:00
|
|
|
if (!area->base || area->size < sizeof(PaddedSevHashTable)) {
|
2021-11-11 11:00:46 +01:00
|
|
|
error_setg(errp, "SEV: guest firmware hashes table area is invalid "
|
|
|
|
"(base=0x%x size=0x%x)", area->base, area->size);
|
|
|
|
return false;
|
|
|
|
}
|
2021-09-30 07:49:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate hash of kernel command-line with the terminating null byte. If
|
|
|
|
* the user doesn't supply a command-line via -append, the 1-byte "\0" will
|
|
|
|
* be used.
|
|
|
|
*/
|
|
|
|
hashp = cmdline_hash;
|
|
|
|
if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->cmdline_data,
|
|
|
|
ctx->cmdline_size, &hashp, &hash_len, errp) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
assert(hash_len == HASH_SIZE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate hash of initrd. If the user doesn't supply an initrd via
|
|
|
|
* -initrd, an empty buffer will be used (ctx->initrd_size == 0).
|
|
|
|
*/
|
|
|
|
hashp = initrd_hash;
|
|
|
|
if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->initrd_data,
|
|
|
|
ctx->initrd_size, &hashp, &hash_len, errp) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
assert(hash_len == HASH_SIZE);
|
|
|
|
|
|
|
|
/* Calculate hash of the kernel */
|
|
|
|
hashp = kernel_hash;
|
|
|
|
struct iovec iov[2] = {
|
|
|
|
{ .iov_base = ctx->setup_data, .iov_len = ctx->setup_size },
|
|
|
|
{ .iov_base = ctx->kernel_data, .iov_len = ctx->kernel_size }
|
|
|
|
};
|
|
|
|
if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, iov, ARRAY_SIZE(iov),
|
|
|
|
&hashp, &hash_len, errp) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
assert(hash_len == HASH_SIZE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Populate the hashes table in the guest's memory at the OVMF-designated
|
|
|
|
* area for the SEV hashes table
|
|
|
|
*/
|
2021-11-11 11:00:48 +01:00
|
|
|
padded_ht = address_space_map(&address_space_memory, area->base,
|
|
|
|
&mapped_len, true, attrs);
|
|
|
|
if (!padded_ht || mapped_len != sizeof(*padded_ht)) {
|
|
|
|
error_setg(errp, "SEV: cannot map hashes table guest memory area");
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-11 11:00:47 +01:00
|
|
|
ht = &padded_ht->ht;
|
2021-09-30 07:49:14 +02:00
|
|
|
|
|
|
|
ht->guid = sev_hash_table_header_guid;
|
|
|
|
ht->len = sizeof(*ht);
|
|
|
|
|
|
|
|
ht->cmdline.guid = sev_cmdline_entry_guid;
|
|
|
|
ht->cmdline.len = sizeof(ht->cmdline);
|
|
|
|
memcpy(ht->cmdline.hash, cmdline_hash, sizeof(ht->cmdline.hash));
|
|
|
|
|
|
|
|
ht->initrd.guid = sev_initrd_entry_guid;
|
|
|
|
ht->initrd.len = sizeof(ht->initrd);
|
|
|
|
memcpy(ht->initrd.hash, initrd_hash, sizeof(ht->initrd.hash));
|
|
|
|
|
|
|
|
ht->kernel.guid = sev_kernel_entry_guid;
|
|
|
|
ht->kernel.len = sizeof(ht->kernel);
|
|
|
|
memcpy(ht->kernel.hash, kernel_hash, sizeof(ht->kernel.hash));
|
|
|
|
|
2021-11-11 11:00:47 +01:00
|
|
|
/* zero the excess data so the measurement can be reliably calculated */
|
|
|
|
memset(padded_ht->padding, 0, sizeof(padded_ht->padding));
|
2021-09-30 07:49:14 +02:00
|
|
|
|
2021-11-11 11:00:47 +01:00
|
|
|
if (sev_encrypt_flash((uint8_t *)padded_ht, sizeof(*padded_ht), errp) < 0) {
|
2021-11-11 11:00:48 +01:00
|
|
|
ret = false;
|
2021-09-30 07:49:14 +02:00
|
|
|
}
|
|
|
|
|
2021-11-11 11:00:48 +01:00
|
|
|
address_space_unmap(&address_space_memory, padded_ht,
|
|
|
|
mapped_len, true, mapped_len);
|
|
|
|
|
|
|
|
return ret;
|
2021-09-30 07:49:14 +02:00
|
|
|
}
|
|
|
|
|
2018-03-08 13:48:41 +01:00
|
|
|
static void
|
|
|
|
sev_register_types(void)
|
|
|
|
{
|
2020-06-04 08:42:13 +02:00
|
|
|
type_register_static(&sev_guest_info);
|
2018-03-08 13:48:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(sev_register_types);
|