sev: add sev-inject-launch-secret
AMD SEV allows a guest owner to inject a secret blob into the memory of a virtual machine. The secret is encrypted with the SEV Transport Encryption Key and integrity is guaranteed with the Transport Integrity Key. Although QEMU facilitates the injection of the launch secret, it cannot access the secret. Signed-off-by: Tobin Feldman-Fitzthum <tobin@linux.ibm.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Brijesh Singh <brijesh.singh@amd.com> Message-Id: <20201027170303.47550-1-tobin@linux.ibm.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
This commit is contained in:
parent
1bf8b88f14
commit
c7f7e6970d
@ -4,6 +4,7 @@
|
||||
#include "block/block.h"
|
||||
#include "qapi/qapi-types-misc.h"
|
||||
#include "qemu/readline.h"
|
||||
#include "include/exec/hwaddr.h"
|
||||
|
||||
typedef struct MonitorHMP MonitorHMP;
|
||||
typedef struct MonitorOptions MonitorOptions;
|
||||
@ -37,6 +38,8 @@ void monitor_flush(Monitor *mon);
|
||||
int monitor_set_cpu(Monitor *mon, int cpu_index);
|
||||
int monitor_get_cpu_index(Monitor *mon);
|
||||
|
||||
void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp);
|
||||
|
||||
void monitor_read_command(MonitorHMP *mon, int show_prompt);
|
||||
int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
|
||||
void *opaque);
|
||||
|
@ -18,4 +18,6 @@
|
||||
|
||||
void *sev_guest_init(const char *id);
|
||||
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len);
|
||||
int sev_inject_launch_secret(const char *hdr, const char *secret,
|
||||
uint64_t gpa, Error **errp);
|
||||
#endif
|
||||
|
@ -667,10 +667,11 @@ static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict)
|
||||
memory_dump(mon, count, format, size, addr, 1);
|
||||
}
|
||||
|
||||
static void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, Error **errp)
|
||||
void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp)
|
||||
{
|
||||
Int128 gpa_region_size;
|
||||
MemoryRegionSection mrs = memory_region_find(get_system_memory(),
|
||||
addr, 1);
|
||||
addr, size);
|
||||
|
||||
if (!mrs.mr) {
|
||||
error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr);
|
||||
@ -683,6 +684,14 @@ static void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpa_region_size = int128_make64(size);
|
||||
if (int128_lt(mrs.size, gpa_region_size)) {
|
||||
error_setg(errp, "Size of memory region at 0x%" HWADDR_PRIx
|
||||
" exceeded.", addr);
|
||||
memory_region_unref(mrs.mr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*p_mr = mrs.mr;
|
||||
return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
|
||||
}
|
||||
@ -694,7 +703,7 @@ static void hmp_gpa2hva(Monitor *mon, const QDict *qdict)
|
||||
MemoryRegion *mr = NULL;
|
||||
void *ptr;
|
||||
|
||||
ptr = gpa2hva(&mr, addr, &local_err);
|
||||
ptr = gpa2hva(&mr, addr, 1, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
return;
|
||||
@ -770,7 +779,7 @@ static void hmp_gpa2hpa(Monitor *mon, const QDict *qdict)
|
||||
void *ptr;
|
||||
uint64_t physaddr;
|
||||
|
||||
ptr = gpa2hva(&mr, addr, &local_err);
|
||||
ptr = gpa2hva(&mr, addr, 1, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
return;
|
||||
|
@ -201,6 +201,24 @@
|
||||
{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability',
|
||||
'if': 'defined(TARGET_I386)' }
|
||||
|
||||
##
|
||||
# @sev-inject-launch-secret:
|
||||
#
|
||||
# This command injects a secret blob into memory of SEV guest.
|
||||
#
|
||||
# @packet-header: the launch secret packet header encoded in base64
|
||||
#
|
||||
# @secret: the launch secret data to be injected encoded in base64
|
||||
#
|
||||
# @gpa: the guest physical address where secret will be injected.
|
||||
#
|
||||
# Since: 6.0
|
||||
#
|
||||
##
|
||||
{ 'command': 'sev-inject-launch-secret',
|
||||
'data': { 'packet-header': 'str', 'secret': 'str', 'gpa': 'uint64' },
|
||||
'if': 'defined(TARGET_I386)' }
|
||||
|
||||
##
|
||||
# @dump-skeys:
|
||||
#
|
||||
|
@ -729,3 +729,10 @@ SevCapability *qmp_query_sev_capabilities(Error **errp)
|
||||
{
|
||||
return sev_get_capabilities(errp);
|
||||
}
|
||||
|
||||
void qmp_sev_inject_launch_secret(const char *packet_hdr,
|
||||
const char *secret, uint64_t gpa,
|
||||
Error **errp)
|
||||
{
|
||||
sev_inject_launch_secret(packet_hdr, secret, gpa, errp);
|
||||
}
|
||||
|
@ -49,3 +49,8 @@ SevCapability *sev_get_capabilities(Error **errp)
|
||||
error_setg(errp, "SEV is not available in this QEMU");
|
||||
return NULL;
|
||||
}
|
||||
int sev_inject_launch_secret(const char *hdr, const char *secret,
|
||||
uint64_t gpa, Error **errp)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "trace.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "qom/object.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "monitor/monitor.h"
|
||||
|
||||
#define TYPE_SEV_GUEST "sev-guest"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
|
||||
@ -785,6 +787,69 @@ sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
error_setg(errp, "SEV: SEV not enabled.");
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
sev_register_types(void)
|
||||
{
|
||||
|
@ -15,3 +15,4 @@ kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session
|
||||
kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIu64
|
||||
kvm_sev_launch_measurement(const char *value) "data %s"
|
||||
kvm_sev_launch_finish(void) ""
|
||||
kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d"
|
||||
|
Loading…
Reference in New Issue
Block a user