aboutsummaryrefslogtreecommitdiff
path: root/target/i386/sev.c
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2021-10-13 06:56:45 -0700
committerRichard Henderson <richard.henderson@linaro.org>2021-10-13 06:56:45 -0700
commit946de558354c99e1989621abe053f2ab87dc8de9 (patch)
tree3b8333edfbfa1d6d2c0c17de4d1674463e56fc83 /target/i386/sev.c
parentee26ce674a93c824713542cec3b6a9ca85459165 (diff)
parenteeecc2ede44b2a5f2551dfcebd561a3945d4c132 (diff)
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* SEV and SGX cleanups (Philippe, Dov) * bugfixes for "check-block" * bugfix for static build * ObjectOptions cleanups (Thomas) * binutils fix for PVH (Cole) * HVF cleanup (Alex) # gpg: Signature made Wed 13 Oct 2021 01:47:56 AM PDT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] * remotes/bonzini/tags/for-upstream: (40 commits) ebpf: really include it only in system emulators target/i386/sev: Use local variable for kvm_sev_launch_measure target/i386/sev: Use local variable for kvm_sev_launch_start monitor: Tidy up find_device_state() Revert "hw/misc: applesmc: use host osk as default on macs" hw/i386/sgx: Move qmp_query_sgx() and hmp_info_sgx() to hw/i386/sgx.c hw/i386/sgx: Move qmp_query_sgx_capabilities() to hw/i386/sgx.c hw/i386/sgx: Have sgx_epc_get_section() return a boolean MAINTAINERS: Cover SGX documentation file with X86/KVM section hvf: Determine slot count from struct layout tests: tcg: Fix PVH test with binutils 2.36+ qapi: Make some ObjectTypes depend on the build settings MAINTAINERS: Cover SEV-related files with X86/KVM section monitor: Reduce hmp_info_sev() declaration target/i386/sev: Move qmp_query_sev() & hmp_info_sev() to sev.c target/i386/sev: Move qmp_query_sev_launch_measure() to sev.c target/i386/sev: Move qmp_query_sev_capabilities() to sev.c target/i386/sev: Move qmp_sev_inject_launch_secret() to sev.c target/i386/sev: Move qmp_query_sev_attestation_report() to sev.c target/i386/sev: Remove stubs by using code elision ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'target/i386/sev.c')
-rw-r--r--target/i386/sev.c201
1 files changed, 136 insertions, 65 deletions
diff --git a/target/i386/sev.c b/target/i386/sev.c
index bcd9260fa4..eede07f11d 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -25,13 +25,16 @@
#include "qemu/uuid.h"
#include "crypto/hash.h"
#include "sysemu/kvm.h"
-#include "sev_i386.h"
+#include "sev.h"
#include "sysemu/sysemu.h"
#include "sysemu/runstate.h"
#include "trace.h"
#include "migration/blocker.h"
#include "qom/object.h"
#include "monitor/monitor.h"
+#include "monitor/hmp-target.h"
+#include "qapi/qapi-commands-misc-target.h"
+#include "qapi/qmp/qerror.h"
#include "exec/confidential-guest-support.h"
#include "hw/i386/pc.h"
@@ -65,7 +68,6 @@ struct SevGuestState {
uint8_t api_major;
uint8_t api_minor;
uint8_t build_id;
- uint64_t me_mask;
int sev_fd;
SevState state;
gchar *measurement;
@@ -389,12 +391,6 @@ sev_es_enabled(void)
return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES);
}
-uint64_t
-sev_get_me_mask(void)
-{
- return sev_guest ? sev_guest->me_mask : ~0;
-}
-
uint32_t
sev_get_cbit_position(void)
{
@@ -407,8 +403,7 @@ sev_get_reduced_phys_bits(void)
return sev_guest ? sev_guest->reduced_phys_bits : 0;
}
-SevInfo *
-sev_get_info(void)
+static SevInfo *sev_get_info(void)
{
SevInfo *info;
@@ -427,6 +422,40 @@ sev_get_info(void)
return info;
}
+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);
+}
+
static int
sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
size_t *cert_chain_len, Error **errp)
@@ -440,7 +469,8 @@ sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
if (r < 0) {
if (err != SEV_RET_INVALID_LEN) {
- error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
+ error_setg(errp, "SEV: Failed to export PDH cert"
+ " ret=%d fw_err=%d (%s)",
r, err, fw_error_to_str(err));
return 1;
}
@@ -453,7 +483,7 @@ sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
if (r < 0) {
- error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
+ error_setg(errp, "SEV: Failed to export PDH cert ret=%d fw_err=%d (%s)",
r, err, fw_error_to_str(err));
goto e_free;
}
@@ -470,8 +500,7 @@ e_free:
return 1;
}
-SevCapability *
-sev_get_capabilities(Error **errp)
+static SevCapability *sev_get_capabilities(Error **errp)
{
SevCapability *cap = NULL;
guchar *pdh_data = NULL;
@@ -491,7 +520,7 @@ sev_get_capabilities(Error **errp)
fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
if (fd < 0) {
- error_setg_errno(errp, errno, "Failed to open %s",
+ error_setg_errno(errp, errno, "SEV: Failed to open %s",
DEFAULT_SEV_DEVICE);
return NULL;
}
@@ -521,14 +550,19 @@ out:
return cap;
}
-SevAttestationReport *
-sev_get_attestation_report(const char *mnonce, Error **errp)
+SevCapability *qmp_query_sev_capabilities(Error **errp)
+{
+ return sev_get_capabilities(errp);
+}
+
+static SevAttestationReport *sev_get_attestation_report(const char *mnonce,
+ Error **errp)
{
struct kvm_sev_attestation_report input = {};
SevAttestationReport *report = NULL;
SevGuestState *sev = sev_guest;
- guchar *data;
- guchar *buf;
+ g_autofree guchar *data = NULL;
+ g_autofree guchar *buf = NULL;
gsize len;
int err = 0, ret;
@@ -548,7 +582,6 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
if (len != sizeof(input.mnonce)) {
error_setg(errp, "SEV: mnonce must be %zu bytes (got %" G_GSIZE_FORMAT ")",
sizeof(input.mnonce), len);
- g_free(buf);
return NULL;
}
@@ -557,9 +590,9 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
&input, &err);
if (ret < 0) {
if (err != SEV_RET_INVALID_LEN) {
- error_setg(errp, "failed to query the attestation report length "
- "ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err));
- g_free(buf);
+ error_setg(errp, "SEV: Failed to query the attestation report"
+ " length ret=%d fw_err=%d (%s)",
+ ret, err, fw_error_to_str(err));
return NULL;
}
}
@@ -572,9 +605,9 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
&input, &err);
if (ret) {
- error_setg_errno(errp, errno, "Failed to get attestation report"
+ error_setg_errno(errp, errno, "SEV: Failed to get attestation report"
" ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err));
- goto e_free_data;
+ return NULL;
}
report = g_new0(SevAttestationReport, 1);
@@ -582,12 +615,15 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
trace_kvm_sev_attestation_report(mnonce, report->data);
-e_free_data:
- g_free(data);
- g_free(buf);
return report;
}
+SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce,
+ Error **errp)
+{
+ return sev_get_attestation_report(mnonce, errp);
+}
+
static int
sev_read_file_base64(const char *filename, guchar **data, gsize *len)
{
@@ -596,7 +632,7 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len)
GError *error = NULL;
if (!g_file_get_contents(filename, &base64, &sz, &error)) {
- error_report("failed to read '%s' (%s)", filename, error->message);
+ error_report("SEV: Failed to read '%s' (%s)", filename, error->message);
g_error_free(error);
return -1;
}
@@ -611,31 +647,29 @@ sev_launch_start(SevGuestState *sev)
gsize sz;
int ret = 1;
int fw_error, rc;
- struct kvm_sev_launch_start *start;
+ struct kvm_sev_launch_start start = {
+ .handle = sev->handle, .policy = sev->policy
+ };
guchar *session = NULL, *dh_cert = NULL;
- start = g_new0(struct kvm_sev_launch_start, 1);
-
- start->handle = sev->handle;
- start->policy = sev->policy;
if (sev->session_file) {
if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
goto out;
}
- start->session_uaddr = (unsigned long)session;
- start->session_len = sz;
+ 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) {
goto out;
}
- start->dh_uaddr = (unsigned long)dh_cert;
- start->dh_len = sz;
+ start.dh_uaddr = (unsigned long)dh_cert;
+ start.dh_len = sz;
}
- trace_kvm_sev_launch_start(start->policy, session, dh_cert);
- rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
+ trace_kvm_sev_launch_start(start.policy, session, dh_cert);
+ rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error);
if (rc < 0) {
error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
__func__, ret, fw_error, fw_error_to_str(fw_error));
@@ -643,11 +677,10 @@ sev_launch_start(SevGuestState *sev)
}
sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE);
- sev->handle = start->handle;
+ sev->handle = start.handle;
ret = 0;
out:
- g_free(start);
g_free(session);
g_free(dh_cert);
return ret;
@@ -695,8 +728,8 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
{
SevGuestState *sev = sev_guest;
int ret, error;
- guchar *data;
- struct kvm_sev_launch_measure *measurement;
+ g_autofree guchar *data = NULL;
+ struct kvm_sev_launch_measure measurement = {};
if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) {
return;
@@ -710,43 +743,35 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
}
}
- measurement = g_new0(struct kvm_sev_launch_measure, 1);
-
/* query the measurement blob length */
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
- measurement, &error);
- if (!measurement->len) {
+ &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;
+ return;
}
- data = g_new0(guchar, measurement->len);
- measurement->uaddr = (unsigned long)data;
+ data = g_new0(guchar, measurement.len);
+ measurement.uaddr = (unsigned long)data;
/* get the measurement blob */
ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
- measurement, &error);
+ &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;
+ return;
}
sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET);
/* encode the measurement value and emit the event */
- sev->measurement = g_base64_encode(data, measurement->len);
+ sev->measurement = g_base64_encode(data, measurement.len);
trace_kvm_sev_launch_measurement(sev->measurement);
-
-free_data:
- g_free(data);
-free_measurement:
- g_free(measurement);
}
-char *
-sev_get_launch_measurement(void)
+static char *sev_get_launch_measurement(void)
{
if (sev_guest &&
sev_guest->state >= SEV_STATE_LAUNCH_SECRET) {
@@ -756,6 +781,23 @@ sev_get_launch_measurement(void)
return NULL;
}
+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;
+}
+
static Notifier sev_machine_done_notify = {
.notify = sev_launch_get_measure,
};
@@ -831,8 +873,6 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
goto err;
}
- sev->me_mask = ~(1UL << sev->cbitpos);
-
devname = object_property_get_str(OBJECT(sev), "sev-device", NULL);
sev->sev_fd = open(devname, O_RDWR);
if (sev->sev_fd < 0) {
@@ -911,7 +951,7 @@ sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) {
int ret = sev_launch_update_data(sev_guest, ptr, len);
if (ret < 0) {
- error_setg(errp, "failed to encrypt pflash rom");
+ error_setg(errp, "SEV: Failed to encrypt pflash rom");
return ret;
}
}
@@ -930,7 +970,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
MemoryRegion *mr = NULL;
if (!sev_guest) {
- error_setg(errp, "SEV: SEV not enabled.");
+ error_setg(errp, "SEV not enabled for guest");
return 1;
}
@@ -982,6 +1022,37 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
return 0;
}
+#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);
+}
+
static int
sev_es_parse_reset_block(SevInfoBlock *info, uint32_t *addr)
{