The virDomainGetSevVmMeasurement() can be used to retrieve the measurement
of encrypted VM launched using AMD SEV feature. The measurement is a
signature of the memory contents that can be sent to the guest owner as
an attestation that the memory was encrypted correctly by the firmware
before booting the guest.
Signed-off-by: Xiaogang Chen <Xiaogang.Chen(a)amd.com>
Signed-off-by: Brijesh Singh <brijesh.singh(a)amd.com>
---
include/libvirt/libvirt-domain.h | 4 +++
src/driver-hypervisor.h | 4 +++
src/libvirt-domain.c | 41 +++++++++++++++++++++++++++++
src/libvirt_public.syms | 1 +
src/qemu/qemu_driver.c | 57 ++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor.c | 8 ++++++
src/qemu/qemu_monitor.h | 3 +++
src/qemu/qemu_monitor_json.c | 33 +++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 2 ++
src/remote/remote_driver.c | 3 ++-
src/remote/remote_protocol.x | 17 +++++++++++-
11 files changed, 171 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 4048acf38aaf..c0bcfea4723c 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -4756,4 +4756,8 @@ int virDomainSetLifecycleAction(virDomainPtr domain,
unsigned int action,
unsigned int flags);
+char *
+virDomainGetSevVmMeasurement(virDomainPtr domain,
+ unsigned int flags);
+
#endif /* __VIR_LIBVIRT_DOMAIN_H__ */
diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
index ce0e2b252552..73edcd8f059f 100644
--- a/src/driver-hypervisor.h
+++ b/src/driver-hypervisor.h
@@ -1283,6 +1283,9 @@ typedef int
unsigned int action,
unsigned int flags);
+typedef char *
+(*virDrvDomainGetSevVmMeasurement)(virDomainPtr dommain,
+ unsigned int flags);
typedef struct _virHypervisorDriver virHypervisorDriver;
typedef virHypervisorDriver *virHypervisorDriverPtr;
@@ -1528,6 +1531,7 @@ struct _virHypervisorDriver {
virDrvDomainSetVcpu domainSetVcpu;
virDrvDomainSetBlockThreshold domainSetBlockThreshold;
virDrvDomainSetLifecycleAction domainSetLifecycleAction;
+ virDrvDomainGetSevVmMeasurement domainGetSevVmMeasurement;
};
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index eaec0979ad49..f285a3121548 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -12095,3 +12095,44 @@ int virDomainSetLifecycleAction(virDomainPtr domain,
virDispatchError(domain->conn);
return -1;
}
+
+/**
+ * virDomainGetSevVmMeasurement:
+ * @domain: pointer to domain object
+ * @flags: currently unused, pass 0
+ *
+ * Get launch measurement of SEV guest VM
+ *
+ * Returns a measurement string, or NULL in case of error.
+ */
+char *
+virDomainGetSevVmMeasurement(virDomainPtr domain,
+ unsigned int flags)
+{
+ virConnectPtr conn;
+ VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);
+
+ virResetLastError();
+
+ virCheckDomainReturn(domain, NULL);
+ conn = domain->conn;
+
+ virCheckReadOnlyGoto(conn->flags, error);
+
+ if (conn->driver->domainGetSevVmMeasurement) {
+ char *ret;
+
+ ret = conn->driver->domainGetSevVmMeasurement(domain,
+ flags);
+ if (!ret)
+ goto error;
+
+ return ret;
+ }
+
+ virReportUnsupportedError();
+
+error:
+ virDispatchError(domain->conn);
+ return NULL;
+}
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 95df3a0dbc7b..6e956d965a26 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -783,6 +783,7 @@ LIBVIRT_3.9.0 {
LIBVIRT_4.1.0 {
global:
virStoragePoolLookupByTargetPath;
+ virDomainGetSevVmMeasurement;
} LIBVIRT_3.9.0;
# .... define new API here using predicted next version number ....
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 313d730c791f..852d1f0fd2f7 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -21254,6 +21254,62 @@ qemuDomainSetLifecycleAction(virDomainPtr dom,
return ret;
}
+static char *
+qemuDomainGetSevVmMeasurement(virDomainPtr dom,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ char *ret = NULL, *tmp;
+
+ virCheckFlags(0, NULL);
+
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ goto endjob;
+ }
+
+ if (virDomainGetSevVmMeasurementEnsureACL(dom->conn, vm->def) < 0){
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("get sev vm measurement is not allowed"));
+ goto cleanup;
+ }
+
+ if (vm->def->sev) {
+ goto endjob;
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("domain is not SEV guest"));
+ }
+
+ if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
+ goto endjob;
+
+ VIR_DEBUG("query sev launch measurement");
+ if(!(tmp = qemuMonitorGetSevMeasurement(QEMU_DOMAIN_PRIVATE(vm)->mon))){
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get measurement"));
+ goto endjob;
+ }
+
+ if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ goto endjob;
+
+ ret = tmp;
+
+ endjob:
+ qemuDomainObjEndJob(driver, vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
static virHypervisorDriver qemuHypervisorDriver = {
.name = QEMU_DRIVER_NAME,
@@ -21474,6 +21530,7 @@ static virHypervisorDriver qemuHypervisorDriver = {
.domainSetVcpu = qemuDomainSetVcpu, /* 3.1.0 */
.domainSetBlockThreshold = qemuDomainSetBlockThreshold, /* 3.2.0 */
.domainSetLifecycleAction = qemuDomainSetLifecycleAction, /* 3.9.0 */
+ .domainGetSevVmMeasurement = qemuDomainGetSevVmMeasurement, /* 4.2.0 */
};
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 195248c88ae1..e3dd078e4e73 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4400,3 +4400,11 @@ qemuMonitorSetWatchdogAction(qemuMonitorPtr mon,
return qemuMonitorJSONSetWatchdogAction(mon, action);
}
+
+char *
+qemuMonitorGetSevMeasurement(qemuMonitorPtr mon)
+{
+ QEMU_CHECK_MONITOR_NULL(mon);
+
+ return qemuMonitorJSONGetSevMeasurement(mon);
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 1b2513650c58..dd0821178c47 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1176,4 +1176,7 @@ virJSONValuePtr qemuMonitorQueryNamedBlockNodes(qemuMonitorPtr
mon);
int qemuMonitorSetWatchdogAction(qemuMonitorPtr mon,
const char *action);
+char *
+qemuMonitorGetSevMeasurement(qemuMonitorPtr mon);
+
#endif /* QEMU_MONITOR_H */
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 4424abfa7148..1d7f0e7c168e 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -7974,3 +7974,36 @@ qemuMonitorJSONSetWatchdogAction(qemuMonitorPtr mon,
virJSONValueFree(reply);
return ret;
}
+
+char *
+qemuMonitorJSONGetSevMeasurement(qemuMonitorPtr mon)
+{
+ const char *tmp;
+ char *measurement = NULL;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr data;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-launch-measure", NULL)))
+ return NULL;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ data = virJSONValueObjectGetObject(reply, "return");
+
+ if (!(tmp = virJSONValueObjectGetString(data, "data")))
+ goto cleanup;
+
+ if (VIR_STRDUP(measurement, tmp) < 0){
+ goto cleanup;
+ }
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return measurement;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 305f789902e9..b03b35ae0e8b 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -342,6 +342,8 @@ int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon,
int qemuMonitorJSONSystemWakeup(qemuMonitorPtr mon);
+char * qemuMonitorJSONGetSevMeasurement(qemuMonitorPtr mon);
+
int qemuMonitorJSONGetVersion(qemuMonitorPtr mon,
int *major,
int *minor,
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 9ea726dc45c0..080d244db156 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -8497,7 +8497,8 @@ static virHypervisorDriver hypervisor_driver = {
.domainSetGuestVcpus = remoteDomainSetGuestVcpus, /* 2.0.0 */
.domainSetVcpu = remoteDomainSetVcpu, /* 3.1.0 */
.domainSetBlockThreshold = remoteDomainSetBlockThreshold, /* 3.2.0 */
- .domainSetLifecycleAction = remoteDomainSetLifecycleAction /* 3.9.0 */
+ .domainSetLifecycleAction = remoteDomainSetLifecycleAction, /* 3.9.0 */
+ .domainGetSevVmMeasurement = remoteDomainGetSevVmMeasurement /* 4.2.0 */
};
static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 9dbd497b2fff..227ee8345683 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3448,6 +3448,15 @@ struct remote_domain_set_lifecycle_action_args {
unsigned int flags;
};
+struct remote_domain_get_sev_vm_measurement_args {
+ remote_nonnull_domain dom;
+ unsigned int flags;
+};
+
+struct remote_domain_get_sev_vm_measurement_ret {
+ remote_nonnull_string sev_measurement;
+};
+
/*----- Protocol. -----*/
/* Define the program number, protocol version and procedure numbers here. */
@@ -6135,5 +6144,11 @@ enum remote_procedure {
* @priority: high
* @acl: storage_pool:getattr
*/
- REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_TARGET_PATH = 391
+ REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_TARGET_PATH = 391,
+
+ /**
+ * @generate: both
+ * @acl: domain:read
+ */
+ REMOTE_PROC_DOMAIN_GET_SEV_VM_MEASUREMENT = 392
};
--
2.14.3