diff options
Diffstat (limited to 'target/i386/sev.c')
-rw-r--r-- | target/i386/sev.c | 201 |
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) { |