diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b8537a4260..7e8bd5f251 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20083,6 +20083,105 @@ qemuDomainGetLaunchSecurityInfo(virDomainPtr domain, return ret; } + +static int +qemuDomainSetLaunchSecurityState(virDomainPtr domain, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virQEMUDriver *driver = domain->conn->privateData; + virDomainObj *vm; + int ret = -1; + int rc; + size_t i; + g_autoptr(virQEMUCaps) qemucaps = NULL; + g_autofree char *secrethdr = NULL; + g_autofree char *secret = NULL; + unsigned long long setaddr = 0; + bool hasSetaddr = false; + int state; + + virCheckFlags(0, -1); + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET_HEADER, + VIR_TYPED_PARAM_STRING, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET, + VIR_TYPED_PARAM_STRING, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET_SET_ADDRESS, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) + return -1; + + if (!(vm = qemuDomainObjFromDomain(domain))) + goto cleanup; + + if (virDomainSetLaunchSecurityStateEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + /* Currently only SEV is supported */ + if (!vm->def->sec || + vm->def->sec->sectype != VIR_DOMAIN_LAUNCH_SECURITY_SEV) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("setting a launch secret is only supported in SEV-enabled domains")); + goto cleanup; + } + + if (!(qemucaps = virQEMUCapsCacheLookupDefault(driver->qemuCapsCache, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL))) + goto cleanup; + + if (!virQEMUCapsGet(qemucaps, QEMU_CAPS_SEV_INJECT_LAUNCH_SECRET)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("QEMU does not support setting a launch secret")); + goto cleanup; + } + + for (i = 0; i < nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + + if (STREQ(param->field, VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET_HEADER)) { + secrethdr = g_strdup(param->value.s); + } else if (STREQ(param->field, VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET)) { + secret = g_strdup(param->value.s); + } else if (STREQ(param->field, VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET_SET_ADDRESS)) { + setaddr = param->value.ul; + hasSetaddr = true; + } + } + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + state = virDomainObjGetState(vm, NULL); + if (state != VIR_DOMAIN_PAUSED) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain must be in a paused state")); + goto endjob; + } + + qemuDomainObjEnterMonitor(driver, vm); + rc = qemuMonitorSetLaunchSecurityState(QEMU_DOMAIN_PRIVATE(vm)->mon, + secrethdr, secret, setaddr, hasSetaddr); + qemuDomainObjExitMonitor(driver, vm); + if (rc < 0) + goto endjob; + + ret = 0; + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + + static const unsigned int qemuDomainGetGuestInfoSupportedTypes = VIR_DOMAIN_GUEST_INFO_USERS | VIR_DOMAIN_GUEST_INFO_OS | @@ -20956,6 +21055,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainAuthorizedSSHKeysSet = qemuDomainAuthorizedSSHKeysSet, /* 6.10.0 */ .domainGetMessages = qemuDomainGetMessages, /* 7.1.0 */ .domainStartDirtyRateCalc = qemuDomainStartDirtyRateCalc, /* 7.2.0 */ + .domainSetLaunchSecurityState = qemuDomainSetLaunchSecurityState, /* 8.0.0 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index dda6ae9796..5272d49c2e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4379,6 +4379,20 @@ qemuMonitorGetSEVInfo(qemuMonitor *mon, } +int +qemuMonitorSetLaunchSecurityState(qemuMonitor *mon, + const char *secrethdr, + const char *secret, + unsigned long long setaddr, + bool hasSetaddr) +{ + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSetLaunchSecurityState(mon, secrethdr, secret, + setaddr, hasSetaddr); +} + + int qemuMonitorGetPRManagerInfo(qemuMonitor *mon, GHashTable **retinfo) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 850e01f026..9b2e4e1421 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1457,6 +1457,13 @@ qemuMonitorGetSEVInfo(qemuMonitor *mon, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); +int +qemuMonitorSetLaunchSecurityState(qemuMonitor *mon, + const char *secrethdr, + const char *secret, + unsigned long long setaddr, + bool hasSetaddr); + typedef struct _qemuMonitorPRManagerInfo qemuMonitorPRManagerInfo; struct _qemuMonitorPRManagerInfo { bool connected; diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e45b43eb5a..5bbd82e022 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -8266,6 +8266,51 @@ qemuMonitorJSONGetSEVInfo(qemuMonitor *mon, } +/** + * Set a launch secret in guest memory + * + * Example JSON: + * + * { "execute" : "sev-inject-launch-secret", + * "data": { "packet-header": "str", "secret": "str", "gpa": "uint64" } } + * + * The guest physical address (gpa) parameter is optional + */ +int +qemuMonitorJSONSetLaunchSecurityState(qemuMonitor *mon, + const char *secrethdr, + const char *secret, + unsigned long long setaddr, + bool hasSetaddr) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + + if (hasSetaddr) { + cmd = qemuMonitorJSONMakeCommand("sev-inject-launch-secret", + "s:packet-header", secrethdr, + "s:secret", secret, + "U:gpa", setaddr, + NULL); + } else { + cmd = qemuMonitorJSONMakeCommand("sev-inject-launch-secret", + "s:packet-header", secrethdr, + "s:secret", secret, + NULL); + } + if (cmd == NULL) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} + + /* * Example return data * diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index e88dfc9d50..64d9ebdaa3 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -476,6 +476,12 @@ qemuMonitorJSONGetVersion(qemuMonitor *mon, char **package) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); +int qemuMonitorJSONSetLaunchSecurityState(qemuMonitor *mon, + const char *secrethdr, + const char *secret, + unsigned long long setaddr, + bool hasSetaddr); + int qemuMonitorJSONGetMachines(qemuMonitor *mon, qemuMonitorMachineInfo ***machines) diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 1b0bd0870d..48e2a457ab 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1196,6 +1196,8 @@ GEN_TEST_FUNC(qemuMonitorJSONSetAction, QEMU_MONITOR_ACTION_REBOOT_RESET, QEMU_MONITOR_ACTION_WATCHDOG_SHUTDOWN, QEMU_MONITOR_ACTION_PANIC_SHUTDOWN) +GEN_TEST_FUNC(qemuMonitorJSONSetLaunchSecurityState, "sev_secret_header", + "sev_secret", 0, true) static int testQemuMonitorJSONqemuMonitorJSONNBDServerStart(const void *opaque) @@ -3067,6 +3069,7 @@ mymain(void) DO_TEST_GEN(qemuMonitorJSONJobComplete); DO_TEST_GEN(qemuMonitorJSONBlockJobCancel); DO_TEST_GEN(qemuMonitorJSONSetAction); + DO_TEST_GEN(qemuMonitorJSONSetLaunchSecurityState); DO_TEST(qemuMonitorJSONGetBalloonInfo); DO_TEST(qemuMonitorJSONGetBlockInfo); DO_TEST(qemuMonitorJSONGetAllBlockStatsInfo);