From fb3b728c16e8dd511212697265c5d3c840b235fa Mon Sep 17 00:00:00 2001 From: mohanasv Date: Fri, 24 Jan 2025 11:17:23 +0530 Subject: [PATCH] Initial AMD SEV-SNP Support patches Signed-off-by: mohanasv --- ...n-Report-error-in-error-paths-in-SEV.patch | 54 + ...embers-of-virDomainSEVDef-into-virDo.patch | 282 ++++++ ...ss-NULL-checks-guarding-virBufferEsc.patch | 384 +++++++ ...arate-SEV-formatting-into-a-function.patch | 93 ++ ...-typecast-to-virDomainLaunchSecurity.patch | 127 +++ ...-_virDomainSecDef-sectype-checks-to-.patch | 179 ++++ ...ow-querying-SEV-SNP-state-in-query-s.patch | 372 +++++++ ...policy-in-virDomainGetLaunchSecurity.patch | 57 ++ ...es-Introduce-QEMU_CAPS_SEV_SNP_GUEST.patch | 82 ++ 0024-conf-Introduce-SEV-SNP-support.patch | 584 +++++++++++ 0025-qemu-Build-cmd-line-for-SEV-SNP.patch | 107 ++ ...ck-the-right-firmware-for-SEV-SNP-gu.patch | 111 +++ 0027-domcaps-Report-launchSecurity.patch | 136 +++ ...mu-Fill-launchSecurity-in-domaincaps.patch | 936 ++++++++++++++++++ libvirt.spec | 20 +- 15 files changed, 3523 insertions(+), 1 deletion(-) create mode 100644 0015-qemu_monitor_json-Report-error-in-error-paths-in-SEV.patch create mode 100644 0016-conf-Move-some-members-of-virDomainSEVDef-into-virDo.patch create mode 100644 0017-conf-Drop-needless-NULL-checks-guarding-virBufferEsc.patch create mode 100644 0018-conf-Separate-SEV-formatting-into-a-function.patch create mode 100644 0019-Drop-needless-typecast-to-virDomainLaunchSecurity.patch create mode 100644 0020-src-Convert-some-_virDomainSecDef-sectype-checks-to-.patch create mode 100644 0021-qemu_monitor-Allow-querying-SEV-SNP-state-in-query-s.patch create mode 100644 0022-qemu-Report-snp-policy-in-virDomainGetLaunchSecurity.patch create mode 100644 0023-qemu_capabilities-Introduce-QEMU_CAPS_SEV_SNP_GUEST.patch create mode 100644 0024-conf-Introduce-SEV-SNP-support.patch create mode 100644 0025-qemu-Build-cmd-line-for-SEV-SNP.patch create mode 100644 0026-qemu_firmware-Pick-the-right-firmware-for-SEV-SNP-gu.patch create mode 100644 0027-domcaps-Report-launchSecurity.patch create mode 100644 0028-qemu-Fill-launchSecurity-in-domaincaps.patch diff --git a/0015-qemu_monitor_json-Report-error-in-error-paths-in-SEV.patch b/0015-qemu_monitor_json-Report-error-in-error-paths-in-SEV.patch new file mode 100644 index 0000000..a3d9a00 --- /dev/null +++ b/0015-qemu_monitor_json-Report-error-in-error-paths-in-SEV.patch @@ -0,0 +1,54 @@ +From 1567bb681b80c75254466a099808ae1176bedf9f Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Tue, 11 Jun 2024 10:44:24 +0200 +Subject: [PATCH 01/14] qemu_monitor_json: Report error in error paths in SEV + related code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 66efdfabd9b2c8527008c600bf66f21528d70889 upstream + +While working on qemuMonitorJSONGetSEVMeasurement() and +qemuMonitorJSONGetSEVInfo() I've noticed that if these functions +fail, they do so without appropriate error set. Fill in error +reporting. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Abhishek Rajput +Signed-off-by: mohanasv + +diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c +index a9133793f6a0..066b1c2459de 100644 +--- a/src/qemu/qemu_monitor_json.c ++++ b/src/qemu/qemu_monitor_json.c +@@ -7943,8 +7943,11 @@ qemuMonitorJSONGetSEVMeasurement(qemuMonitor *mon) + if (!(data = qemuMonitorJSONGetReply(cmd, reply, VIR_JSON_TYPE_OBJECT))) + return NULL; + +- if (!(tmp = virJSONValueObjectGetString(data, "data"))) ++ if (!(tmp = virJSONValueObjectGetString(data, "data"))) { ++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", ++ _("query-sev-launch-measure reply was missing 'data'")); + return NULL; ++ } + + return g_strdup(tmp); + } +@@ -7987,8 +7990,11 @@ qemuMonitorJSONGetSEVInfo(qemuMonitor *mon, + if (virJSONValueObjectGetNumberUint(data, "api-major", apiMajor) < 0 || + virJSONValueObjectGetNumberUint(data, "api-minor", apiMinor) < 0 || + virJSONValueObjectGetNumberUint(data, "build-id", buildID) < 0 || +- virJSONValueObjectGetNumberUint(data, "policy", policy) < 0) ++ virJSONValueObjectGetNumberUint(data, "policy", policy) < 0) { ++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", ++ _("query-sev reply was missing some data")); + return -1; ++ } + + return 0; + } +-- +2.41.0 + diff --git a/0016-conf-Move-some-members-of-virDomainSEVDef-into-virDo.patch b/0016-conf-Move-some-members-of-virDomainSEVDef-into-virDo.patch new file mode 100644 index 0000000..3a1247d --- /dev/null +++ b/0016-conf-Move-some-members-of-virDomainSEVDef-into-virDo.patch @@ -0,0 +1,282 @@ +From 58482fd8d75be467682095c4cfee49cb7680b2a5 Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Tue, 11 Jun 2024 12:12:08 +0200 +Subject: [PATCH 02/14] conf: Move some members of virDomainSEVDef into + virDomainSEVCommonDef +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit d2cad18ca3c5dd88c416b6e4dd9c709f527b1dbe upstream + +Some parts of SEV are to be shared with SEV SNP. In order to +reuse XML parsing / formatting code cleanly, let's move those +common bits into a new struct (virDomainSEVCommonDef) and adjust +rest of the code. + +[Backport Changes] + +1. In src/conf/domain_conf.h, the struct member user_id,secret_header and +secret in the struct _virDomainSEVDef is retained. This member was +originally introduced in commit 25c173735c4d ("conf: qemu: add libvirt +support reuse id for Hygon CSV") and feda5479eeeb ("conf: qemu: support +provide inject secret for Hygon CSV") as part of the Hygon hardware +support changes. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Abhishek Rajput +Signed-off-by: mohanasv + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 833904b829f7..bfc83d275dae 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -13452,8 +13452,8 @@ virDomainMemoryTargetDefParseXML(xmlNodePtr node, + + + static int +-virDomainSEVDefParseXML(virDomainSEVDef *def, +- xmlXPathContextPtr ctxt) ++virDomainSEVCommonDefParseXML(virDomainSEVCommonDef *def, ++ xmlXPathContextPtr ctxt) + { + int rc; + +@@ -13461,12 +13461,6 @@ virDomainSEVDefParseXML(virDomainSEVDef *def, + &def->kernel_hashes) < 0) + return -1; + +- if (virXPathUIntBase("string(./policy)", ctxt, 16, &def->policy) < 0) { +- virReportError(VIR_ERR_XML_ERROR, "%s", +- _("failed to get launch security policy")); +- return -1; +- } +- + /* the following attributes are platform dependent and if missing, we can + * autofill them from domain capabilities later + */ +@@ -13489,6 +13483,23 @@ virDomainSEVDefParseXML(virDomainSEVDef *def, + return -1; + } + ++ return 0; ++} ++ ++ ++static int ++virDomainSEVDefParseXML(virDomainSEVDef *def, ++ xmlXPathContextPtr ctxt) ++{ ++ if (virDomainSEVCommonDefParseXML(&def->common, ctxt) < 0) ++ return -1; ++ ++ if (virXPathUIntBase("string(./policy)", ctxt, 16, &def->policy) < 0) { ++ virReportError(VIR_ERR_XML_ERROR, "%s", ++ _("failed to get launch security policy")); ++ return -1; ++ } ++ + def->dh_cert = virXPathString("string(./dhCert)", ctxt); + def->session = virXPathString("string(./session)", ctxt); + def->user_id = virXPathString("string(./userid)", ctxt); +@@ -26504,6 +26515,24 @@ virDomainKeyWrapDefFormat(virBuffer *buf, virDomainKeyWrapDef *keywrap) + } + + ++static void ++virDomainSEVCommonDefFormat(virBuffer *attrBuf, ++ virBuffer *childBuf, ++ virDomainSEVCommonDef *def) ++{ ++ if (def->kernel_hashes != VIR_TRISTATE_BOOL_ABSENT) ++ virBufferAsprintf(attrBuf, " kernelHashes='%s'", ++ virTristateBoolTypeToString(def->kernel_hashes)); ++ ++ if (def->haveCbitpos) ++ virBufferAsprintf(childBuf, "%d\n", def->cbitpos); ++ ++ if (def->haveReducedPhysBits) ++ virBufferAsprintf(childBuf, "%d\n", ++ def->reduced_phys_bits); ++} ++ ++ + static void + virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + { +@@ -26520,16 +26549,8 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: { + virDomainSEVDef *sev = &sec->data.sev; + +- if (sev->kernel_hashes != VIR_TRISTATE_BOOL_ABSENT) +- virBufferAsprintf(&attrBuf, " kernelHashes='%s'", +- virTristateBoolTypeToString(sev->kernel_hashes)); +- +- if (sev->haveCbitpos) +- virBufferAsprintf(&childBuf, "%d\n", sev->cbitpos); ++ virDomainSEVCommonDefFormat(&attrBuf, &childBuf, &sev->common); + +- if (sev->haveReducedPhysBits) +- virBufferAsprintf(&childBuf, "%d\n", +- sev->reduced_phys_bits); + virBufferAsprintf(&childBuf, "0x%04x\n", sev->policy); + if (sev->dh_cert) + virBufferEscapeString(&childBuf, "%s\n", sev->dh_cert); +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index b5c0302a8b06..980da7742f54 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -2852,15 +2852,20 @@ typedef enum { + } virDomainLaunchSecurity; + + +-struct _virDomainSEVDef { +- char *dh_cert; +- char *session; +- unsigned int policy; ++struct _virDomainSEVCommonDef { + bool haveCbitpos; + unsigned int cbitpos; + bool haveReducedPhysBits; + unsigned int reduced_phys_bits; + virTristateBool kernel_hashes; ++}; ++ ++ ++struct _virDomainSEVDef { ++ virDomainSEVCommonDef common; ++ char *dh_cert; ++ char *session; ++ unsigned int policy; + char *user_id; + char *secret_header; + char *secret; +diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng +index f31603b2fd6a..966e5831f41d 100644 +--- a/src/conf/schemas/domaincommon.rng ++++ b/src/conf/schemas/domaincommon.rng +@@ -527,6 +527,19 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + sev +@@ -537,16 +550,7 @@ + + + +- +- +- +- +- +- +- +- +- +- ++ + + + +diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h +index 26cb96619417..4d30534f7da3 100644 +--- a/src/conf/virconftypes.h ++++ b/src/conf/virconftypes.h +@@ -208,6 +208,8 @@ typedef struct _virDomainResctrlMonDef virDomainResctrlMonDef; + + typedef struct _virDomainResourceDef virDomainResourceDef; + ++typedef struct _virDomainSEVCommonDef virDomainSEVCommonDef; ++ + typedef struct _virDomainSEVDef virDomainSEVDef; + + typedef struct _virDomainSecDef virDomainSecDef; +diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c +index f7142d7f3c1c..404bc5bbbdd1 100644 +--- a/src/qemu/qemu_command.c ++++ b/src/qemu/qemu_command.c +@@ -9679,7 +9679,7 @@ qemuBuildSEVCommandLine(virDomainObj *vm, virCommand *cmd, + g_autofree char *secretpath = NULL; + + VIR_DEBUG("policy=0x%x cbitpos=%d reduced_phys_bits=%d", +- sev->policy, sev->cbitpos, sev->reduced_phys_bits); ++ sev->policy, sev->common.cbitpos, sev->common.reduced_phys_bits); + + if (sev->user_id) + VIR_DEBUG("user_id=%s", sev->user_id); +@@ -9697,15 +9697,15 @@ qemuBuildSEVCommandLine(virDomainObj *vm, virCommand *cmd, + secretpath = g_strdup_printf("%s/secret.base64", priv->libDir); + + if (qemuMonitorCreateObjectProps(&props, "sev-guest", "lsec0", +- "u:cbitpos", sev->cbitpos, +- "u:reduced-phys-bits", sev->reduced_phys_bits, ++ "u:cbitpos", sev->common.cbitpos, ++ "u:reduced-phys-bits", sev->common.reduced_phys_bits, + "u:policy", sev->policy, + "S:user-id", sev->user_id, + "S:dh-cert-file", dhpath, + "S:session-file", sessionpath, +- "T:kernel-hashes", sev->kernel_hashes, + "S:secret-header-file", secretheaderpath, + "S:secret-file", secretpath, ++ "T:kernel-hashes", sev->common.kernel_hashes, + NULL) < 0) + return -1; + +diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c +index 0082049f5f4c..1cef0771563e 100644 +--- a/src/qemu/qemu_process.c ++++ b/src/qemu/qemu_process.c +@@ -6508,14 +6508,14 @@ qemuProcessUpdateSEVInfo(virDomainObj *vm) + * mandatory on QEMU cmdline + */ + sevCaps = virQEMUCapsGetSEVCapabilities(qemuCaps); +- if (!sev->haveCbitpos) { +- sev->cbitpos = sevCaps->cbitpos; +- sev->haveCbitpos = true; ++ if (!sev->common.haveCbitpos) { ++ sev->common.cbitpos = sevCaps->cbitpos; ++ sev->common.haveCbitpos = true; + } + +- if (!sev->haveReducedPhysBits) { +- sev->reduced_phys_bits = sevCaps->reduced_phys_bits; +- sev->haveReducedPhysBits = true; ++ if (!sev->common.haveReducedPhysBits) { ++ sev->common.reduced_phys_bits = sevCaps->reduced_phys_bits; ++ sev->common.haveReducedPhysBits = true; + } + + return 0; +diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c +index 01c8b6e36d04..39d3222eedb7 100644 +--- a/src/qemu/qemu_validate.c ++++ b/src/qemu/qemu_validate.c +@@ -1302,7 +1302,7 @@ qemuValidateDomainDef(const virDomainDef *def, + return -1; + } + +- if (def->sec->data.sev.kernel_hashes != VIR_TRISTATE_BOOL_ABSENT && ++ if (def->sec->data.sev.common.kernel_hashes != VIR_TRISTATE_BOOL_ABSENT && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST_KERNEL_HASHES)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SEV measured direct kernel boot is not supported with this QEMU binary")); +-- +2.41.0 + diff --git a/0017-conf-Drop-needless-NULL-checks-guarding-virBufferEsc.patch b/0017-conf-Drop-needless-NULL-checks-guarding-virBufferEsc.patch new file mode 100644 index 0000000..f1a709d --- /dev/null +++ b/0017-conf-Drop-needless-NULL-checks-guarding-virBufferEsc.patch @@ -0,0 +1,384 @@ +From ba940d33c0cdd9c665a8204289d686fec70b9db5 Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Mon, 17 Jun 2024 11:53:46 +0200 +Subject: [PATCH 03/14] conf: Drop needless NULL checks guarding + virBufferEscapeString() + +commit be58733d903fdd4a9a64483f14dbb4af97e526f3 upstream + +There's no need to guard virBufferEscapeString() with a call to +NULL as the very first thing the function does is check all three +arguments for NULL. + +This patch was generated using the following spatch: + + @@ + expression X, Y, E; + @@ + + - if (E) + virBufferEscapeString(X, Y, E); + +Signed-off-by: Michal Privoznik +Reviewed-by: Jonathon Jongsma +Signed-off-by: Abhishek Rajput +Signed-off-by: mohanasv + +diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c +index 32badee7b3da..63f86d5d8169 100644 +--- a/src/conf/capabilities.c ++++ b/src/conf/capabilities.c +@@ -688,10 +688,8 @@ virCapabilitiesDomainDataLookupInternal(virCaps *caps, + if (domaintype > VIR_DOMAIN_VIRT_NONE) + virBufferAsprintf(&buf, "domaintype=%s ", + virDomainVirtTypeToString(domaintype)); +- if (emulator) +- virBufferEscapeString(&buf, "emulator=%s ", emulator); +- if (machinetype) +- virBufferEscapeString(&buf, "machine=%s ", machinetype); ++ virBufferEscapeString(&buf, "emulator=%s ", emulator); ++ virBufferEscapeString(&buf, "machine=%s ", machinetype); + if (virBufferCurrentContent(&buf) && + !virBufferCurrentContent(&buf)[0]) + virBufferAsprintf(&buf, "%s", _("any configuration")); +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index bfc83d275dae..5ab1c1dbef25 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -5371,8 +5371,7 @@ virDomainDeviceInfoFormat(virBuffer *buf, + if (rombar) + virBufferAsprintf(buf, " bar='%s'", rombar); + } +- if (info->romfile) +- virBufferEscapeString(buf, " file='%s'", info->romfile); ++ virBufferEscapeString(buf, " file='%s'", info->romfile); + virBufferAddLit(buf, "/>\n"); + } + +@@ -22101,8 +22100,7 @@ virSecurityDeviceLabelDefFormat(virBuffer *buf, + + virBufferAddLit(buf, "model) +- virBufferEscapeString(buf, " model='%s'", def->model); ++ virBufferEscapeString(buf, " model='%s'", def->model); + + if (def->labelskip) + virBufferAddLit(buf, " labelskip='yes'"); +@@ -22297,8 +22295,7 @@ virDomainDiskSourceFormatNetwork(virBuffer *attrBuf, + virBufferAsprintf(childBuf, "\n", src->timeout); + + if (src->protocol == VIR_STORAGE_NET_PROTOCOL_SSH) { +- if (src->ssh_known_hosts_file) +- virBufferEscapeString(childBuf, "\n", src->ssh_known_hosts_file); ++ virBufferEscapeString(childBuf, "\n", src->ssh_known_hosts_file); + if (src->ssh_keyfile || src->ssh_agent) { + virBufferAddLit(childBuf, "idx); + +- if (model) +- virBufferEscapeString(&attrBuf, " model='%s'", model); ++ virBufferEscapeString(&attrBuf, " model='%s'", model); + + switch (def->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: +@@ -24466,8 +24462,7 @@ virDomainChrTargetDefFormat(virBuffer *buf, + + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN: + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: +- if (def->target.name) +- virBufferEscapeString(buf, " name='%s'", def->target.name); ++ virBufferEscapeString(buf, " name='%s'", def->target.name); + + if (def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO && + def->state != VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT && +@@ -25874,9 +25869,8 @@ virDomainGraphicsDefFormat(virBuffer *buf, + break; + } + +- if (def->data.vnc.keymap) +- virBufferEscapeString(buf, " keymap='%s'", +- def->data.vnc.keymap); ++ virBufferEscapeString(buf, " keymap='%s'", ++ def->data.vnc.keymap); + + if (def->data.vnc.sharePolicy) + virBufferAsprintf(buf, " sharePolicy='%s'", +@@ -25891,13 +25885,11 @@ virDomainGraphicsDefFormat(virBuffer *buf, + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: +- if (def->data.sdl.display) +- virBufferEscapeString(buf, " display='%s'", +- def->data.sdl.display); ++ virBufferEscapeString(buf, " display='%s'", ++ def->data.sdl.display); + +- if (def->data.sdl.xauth) +- virBufferEscapeString(buf, " xauth='%s'", +- def->data.sdl.xauth); ++ virBufferEscapeString(buf, " xauth='%s'", ++ def->data.sdl.xauth); + if (def->data.sdl.fullscreen) + virBufferAddLit(buf, " fullscreen='yes'"); + +@@ -25936,9 +25928,8 @@ virDomainGraphicsDefFormat(virBuffer *buf, + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: +- if (def->data.desktop.display) +- virBufferEscapeString(buf, " display='%s'", +- def->data.desktop.display); ++ virBufferEscapeString(buf, " display='%s'", ++ def->data.desktop.display); + + if (def->data.desktop.fullscreen) + virBufferAddLit(buf, " fullscreen='yes'"); +@@ -25991,9 +25982,8 @@ virDomainGraphicsDefFormat(virBuffer *buf, + break; + } + +- if (def->data.spice.keymap) +- virBufferEscapeString(buf, " keymap='%s'", +- def->data.spice.keymap); ++ virBufferEscapeString(buf, " keymap='%s'", ++ def->data.spice.keymap); + + if (def->data.spice.defaultMode != VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY) + virBufferAsprintf(buf, " defaultMode='%s'", +@@ -26365,11 +26355,9 @@ virDomainResourceDefFormat(virBuffer *buf, + if (!def) + return; + +- if (def->partition) +- virBufferEscapeString(&childBuf, "%s\n", def->partition); ++ virBufferEscapeString(&childBuf, "%s\n", def->partition); + +- if (def->appid) +- virBufferEscapeString(&childBuf, "\n", def->appid); ++ virBufferEscapeString(&childBuf, "\n", def->appid); + + virXMLFormatElement(buf, "resource", NULL, &childBuf); + } +@@ -26552,11 +26540,9 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + virDomainSEVCommonDefFormat(&attrBuf, &childBuf, &sev->common); + + virBufferAsprintf(&childBuf, "0x%04x\n", sev->policy); +- if (sev->dh_cert) +- virBufferEscapeString(&childBuf, "%s\n", sev->dh_cert); ++ virBufferEscapeString(&childBuf, "%s\n", sev->dh_cert); + +- if (sev->session) +- virBufferEscapeString(&childBuf, "%s\n", sev->session); ++ virBufferEscapeString(&childBuf, "%s\n", sev->session); + + if (sev->user_id) + virBufferEscapeString(&childBuf, "%s\n", sev->user_id); +@@ -27787,9 +27773,8 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def, + for (i = 0; def->os.initenv && def->os.initenv[i]; i++) + virBufferAsprintf(buf, "%s\n", + def->os.initenv[i]->name, def->os.initenv[i]->value); +- if (def->os.initdir) +- virBufferEscapeString(buf, "%s\n", +- def->os.initdir); ++ virBufferEscapeString(buf, "%s\n", ++ def->os.initdir); + if (def->os.inituser) + virBufferAsprintf(buf, "%s\n", def->os.inituser); + if (def->os.initgroup) +diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c +index 0449b6f07c73..cc8956af538c 100644 +--- a/src/conf/network_conf.c ++++ b/src/conf/network_conf.c +@@ -2043,10 +2043,8 @@ virNetworkDNSDefFormat(virBuffer *buf, + def->srvs[i].service); + virBufferEscapeString(buf, "protocol='%s'", def->srvs[i].protocol); + +- if (def->srvs[i].domain) +- virBufferEscapeString(buf, " domain='%s'", def->srvs[i].domain); +- if (def->srvs[i].target) +- virBufferEscapeString(buf, " target='%s'", def->srvs[i].target); ++ virBufferEscapeString(buf, " domain='%s'", def->srvs[i].domain); ++ virBufferEscapeString(buf, " target='%s'", def->srvs[i].target); + if (def->srvs[i].port) + virBufferAsprintf(buf, " port='%d'", def->srvs[i].port); + if (def->srvs[i].priority) +diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c +index f722ab37c60a..6c78c9a26338 100644 +--- a/src/conf/node_device_conf.c ++++ b/src/conf/node_device_conf.c +@@ -176,20 +176,16 @@ virNodeDeviceCapSystemDefFormat(virBuffer *buf, + { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + +- if (data->system.product_name) +- virBufferEscapeString(buf, "%s\n", +- data->system.product_name); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.product_name); + virBufferAddLit(buf, "\n"); + virBufferAdjustIndent(buf, 2); +- if (data->system.hardware.vendor_name) +- virBufferEscapeString(buf, "%s\n", +- data->system.hardware.vendor_name); +- if (data->system.hardware.version) +- virBufferEscapeString(buf, "%s\n", +- data->system.hardware.version); +- if (data->system.hardware.serial) +- virBufferEscapeString(buf, "%s\n", +- data->system.hardware.serial); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.hardware.vendor_name); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.hardware.version); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.hardware.serial); + virUUIDFormat(data->system.hardware.uuid, uuidstr); + virBufferAsprintf(buf, "%s\n", uuidstr); + virBufferAdjustIndent(buf, -2); +@@ -197,15 +193,12 @@ virNodeDeviceCapSystemDefFormat(virBuffer *buf, + + virBufferAddLit(buf, "\n"); + virBufferAdjustIndent(buf, 2); +- if (data->system.firmware.vendor_name) +- virBufferEscapeString(buf, "%s\n", +- data->system.firmware.vendor_name); +- if (data->system.firmware.version) +- virBufferEscapeString(buf, "%s\n", +- data->system.firmware.version); +- if (data->system.firmware.release_date) +- virBufferEscapeString(buf, "%s\n", +- data->system.firmware.release_date); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.firmware.vendor_name); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.firmware.version); ++ virBufferEscapeString(buf, "%s\n", ++ data->system.firmware.release_date); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "\n"); + } +@@ -225,9 +218,8 @@ virNodeDeviceCapMdevTypesFormat(virBuffer *buf, + virMediatedDeviceType *type = mdev_types[i]; + virBufferEscapeString(buf, "\n", type->id); + virBufferAdjustIndent(buf, 2); +- if (type->name) +- virBufferEscapeString(buf, "%s\n", +- type->name); ++ virBufferEscapeString(buf, "%s\n", ++ type->name); + virBufferEscapeString(buf, "%s\n", + type->device_api); + virBufferAsprintf(buf, +@@ -451,10 +443,9 @@ virNodeDeviceCapUSBInterfaceDefFormat(virBuffer *buf, + data->usb_if.subclass); + virBufferAsprintf(buf, "%d\n", + data->usb_if.protocol); +- if (data->usb_if.description) +- virBufferEscapeString(buf, +- "%s\n", +- data->usb_if.description); ++ virBufferEscapeString(buf, ++ "%s\n", ++ data->usb_if.description); + } + + +@@ -466,9 +457,8 @@ virNodeDeviceCapNetDefFormat(virBuffer *buf, + + virBufferEscapeString(buf, "%s\n", + data->net.ifname); +- if (data->net.address) +- virBufferEscapeString(buf, "
%s
\n", +- data->net.address); ++ virBufferEscapeString(buf, "
%s
\n", ++ data->net.address); + virInterfaceLinkFormat(buf, &data->net.lnk); + if (data->net.features) { + for (i = 0; i < VIR_NET_DEV_FEAT_LAST; i++) { +@@ -530,9 +520,8 @@ virNodeDeviceCapSCSIDefFormat(virBuffer *buf, + virBufferAsprintf(buf, "%d\n", + data->scsi.target); + virBufferAsprintf(buf, "%d\n", data->scsi.lun); +- if (data->scsi.type) +- virBufferEscapeString(buf, "%s\n", +- data->scsi.type); ++ virBufferEscapeString(buf, "%s\n", ++ data->scsi.type); + } + + +diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c +index d7fcded302a4..039ed77b846c 100644 +--- a/src/conf/snapshot_conf.c ++++ b/src/conf/snapshot_conf.c +@@ -819,9 +819,8 @@ virDomainSnapshotDefFormatInternal(virBuffer *buf, + virBufferAdjustIndent(buf, 2); + + virBufferEscapeString(buf, "%s\n", def->parent.name); +- if (def->parent.description) +- virBufferEscapeString(buf, "%s\n", +- def->parent.description); ++ virBufferEscapeString(buf, "%s\n", ++ def->parent.description); + if (def->state) + virBufferAsprintf(buf, "%s\n", + virDomainSnapshotStateTypeToString(def->state)); +diff --git a/src/conf/storage_encryption_conf.c b/src/conf/storage_encryption_conf.c +index 1849df5c6c9b..b86001ec5092 100644 +--- a/src/conf/storage_encryption_conf.c ++++ b/src/conf/storage_encryption_conf.c +@@ -317,16 +317,13 @@ virStorageEncryptionInfoDefFormat(virBuffer *buf, + { + virBufferEscapeString(buf, "cipher_name); + virBufferAsprintf(buf, " size='%u'", enc->cipher_size); +- if (enc->cipher_mode) +- virBufferEscapeString(buf, " mode='%s'", enc->cipher_mode); +- if (enc->cipher_hash) +- virBufferEscapeString(buf, " hash='%s'", enc->cipher_hash); ++ virBufferEscapeString(buf, " mode='%s'", enc->cipher_mode); ++ virBufferEscapeString(buf, " hash='%s'", enc->cipher_hash); + virBufferAddLit(buf, "/>\n"); + + if (enc->ivgen_name) { + virBufferEscapeString(buf, "ivgen_name); +- if (enc->ivgen_hash) +- virBufferEscapeString(buf, " hash='%s'", enc->ivgen_hash); ++ virBufferEscapeString(buf, " hash='%s'", enc->ivgen_hash); + virBufferAddLit(buf, "/>\n"); + } + } +diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c +index f974a521b1f6..003fbf031e29 100644 +--- a/src/conf/storage_source_conf.c ++++ b/src/conf/storage_source_conf.c +@@ -1347,8 +1347,7 @@ int + virStorageSourcePrivateDataFormatRelPath(virStorageSource *src, + virBuffer *buf) + { +- if (src->relPath) +- virBufferEscapeString(buf, "%s\n", src->relPath); ++ virBufferEscapeString(buf, "%s\n", src->relPath); + + return 0; + } +diff --git a/src/conf/virnwfilterbindingdef.c b/src/conf/virnwfilterbindingdef.c +index 423ed7a392b3..fe45c8434721 100644 +--- a/src/conf/virnwfilterbindingdef.c ++++ b/src/conf/virnwfilterbindingdef.c +@@ -203,8 +203,7 @@ virNWFilterBindingDefFormatBuf(virBuffer *buf, + virBufferAddLit(buf, "\n"); + + virBufferEscapeString(buf, "\n", def->portdevname); +- if (def->linkdevname) +- virBufferEscapeString(buf, "\n", def->linkdevname); ++ virBufferEscapeString(buf, "\n", def->linkdevname); + + virMacAddrFormat(&def->mac, mac); + virBufferAsprintf(buf, "\n", mac); +-- +2.41.0 + diff --git a/0018-conf-Separate-SEV-formatting-into-a-function.patch b/0018-conf-Separate-SEV-formatting-into-a-function.patch new file mode 100644 index 0000000..1856f91 --- /dev/null +++ b/0018-conf-Separate-SEV-formatting-into-a-function.patch @@ -0,0 +1,93 @@ +From f683ab66b6044d4e7109ff2285c0f1858f85031f Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Tue, 11 Jun 2024 13:00:58 +0200 +Subject: [PATCH 04/14] conf: Separate SEV formatting into a function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit faa3548ed5f7d389810669e9f723029bc937fbbf upstream + +To avoid convolution of switch() inside of virDomainSecDefFormat() even +more (as new sectypes are added), move formatting into a separate +function. + +[Backport Changes] + +1.In src/conf/domain_conf.c, in function virDomainSecDefFormat(),current +commit modifies switch case VIR_DOMAIN_LAUNCH_SECURITY_SEV and migrates +its contents into a separate function for improved code clarity and +maintainability. As part of this migration, an if logic that verifies the +existence of 'user_id','secret_header' and 'secret' before calling +virBufferEscapeString() was also moved. This condition was originally +introduced in commit 25c1737 ("conf: qemu: add libvirt support reuse id +for Hygon CSV") and feda5479eeeb64 ("conf: qemu: support provide inject +secret for Hygon CSV") as part of adding support for Hygon hardware. This +refactoring ensures that these changes are preserved. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Abhishek Rajput +Signed-off-by: mohanasv + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 5ab1c1dbef25..00e02416d4e3 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -26521,6 +26521,25 @@ virDomainSEVCommonDefFormat(virBuffer *attrBuf, + } + + ++static void ++virDomainSEVDefFormat(virBuffer *attrBuf, ++ virBuffer *childBuf, ++ virDomainSEVDef *def) ++{ ++ virDomainSEVCommonDefFormat(attrBuf, childBuf, &def->common); ++ ++ virBufferAsprintf(childBuf, "0x%04x\n", def->policy); ++ virBufferEscapeString(childBuf, "%s\n", def->dh_cert); ++ virBufferEscapeString(childBuf, "%s\n", def->session); ++ if (def->user_id) ++ virBufferEscapeString(childBuf, "%s\n", def->user_id); ++ if (def->secret_header) ++ virBufferEscapeString(childBuf, "%s\n", def->secret_header); ++ if (def->secret) ++ virBufferEscapeString(childBuf, "%s\n", def->secret); ++} ++ ++ + static void + virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + { +@@ -26534,25 +26553,9 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + virDomainLaunchSecurityTypeToString(sec->sectype)); + + switch ((virDomainLaunchSecurity) sec->sectype) { +- case VIR_DOMAIN_LAUNCH_SECURITY_SEV: { +- virDomainSEVDef *sev = &sec->data.sev; +- +- virDomainSEVCommonDefFormat(&attrBuf, &childBuf, &sev->common); +- +- virBufferAsprintf(&childBuf, "0x%04x\n", sev->policy); +- virBufferEscapeString(&childBuf, "%s\n", sev->dh_cert); +- +- virBufferEscapeString(&childBuf, "%s\n", sev->session); +- +- if (sev->user_id) +- virBufferEscapeString(&childBuf, "%s\n", sev->user_id); +- if (sev->secret_header) +- virBufferEscapeString(&childBuf, "%s\n", sev->secret_header); +- if (sev->secret) +- virBufferEscapeString(&childBuf, "%s\n", sev->secret); +- ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: ++ virDomainSEVDefFormat(&attrBuf, &childBuf, &sec->data.sev); + break; +- } + + case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_CVM: +-- +2.41.0 + diff --git a/0019-Drop-needless-typecast-to-virDomainLaunchSecurity.patch b/0019-Drop-needless-typecast-to-virDomainLaunchSecurity.patch new file mode 100644 index 0000000..1498305 --- /dev/null +++ b/0019-Drop-needless-typecast-to-virDomainLaunchSecurity.patch @@ -0,0 +1,127 @@ +From 5e5f9d915ba57f88b17d4bb838a6f83f37c0863a Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Wed, 12 Jun 2024 10:06:57 +0200 +Subject: [PATCH 05/14] Drop needless typecast to virDomainLaunchSecurity +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit a44a43361feea6cf9809cb9ea5873cdf197975fb upstream + +The sectype member of _virDomainSecDef struct is already declared +as of virDomainLaunchSecurity type. There's no need to typecast +it to the very same type when passing it to switch(). + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Abhishek Rajput +Signed-off-by: mohanasv + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 00e02416d4e3..1f3c2161afc7 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -3818,7 +3818,7 @@ virDomainSecDefFree(virDomainSecDef *def) + if (!def) + return; + +- switch ((virDomainLaunchSecurity) def->sectype) { ++ switch (def->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + g_free(def->data.sev.dh_cert); + g_free(def->data.sev.session); +@@ -13523,7 +13523,7 @@ virDomainSecDefParseXML(xmlNodePtr lsecNode, + &sec->sectype) < 0) + return NULL; + +- switch ((virDomainLaunchSecurity) sec->sectype) { ++ switch (sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + if (virDomainSEVDefParseXML(&sec->data.sev, ctxt) < 0) + return NULL; +@@ -26552,7 +26552,7 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + virBufferAsprintf(&attrBuf, " type='%s'", + virDomainLaunchSecurityTypeToString(sec->sectype)); + +- switch ((virDomainLaunchSecurity) sec->sectype) { ++ switch (sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + virDomainSEVDefFormat(&attrBuf, &childBuf, &sec->data.sev); + break; +diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c +index 404bc5bbbdd1..66ee79cbb390 100644 +--- a/src/qemu/qemu_command.c ++++ b/src/qemu/qemu_command.c +@@ -6993,7 +6993,7 @@ qemuBuildMachineCommandLine(virCommand *cmd, + qemuAppendLoadparmMachineParm(&buf, def); + + if (def->sec) { +- switch ((virDomainLaunchSecurity) def->sec->sectype) { ++ switch (def->sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT)) { + virBufferAddLit(&buf, ",confidential-guest-support=lsec0"); +@@ -9740,7 +9740,7 @@ qemuBuildSecCommandLine(virDomainObj *vm, virCommand *cmd, + if (!sec) + return 0; + +- switch ((virDomainLaunchSecurity) sec->sectype) { ++ switch (sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + return qemuBuildSEVCommandLine(vm, cmd, &sec->data.sev); + break; +diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c +index 31ed6e881bb9..8ee16daf8d49 100644 +--- a/src/qemu/qemu_firmware.c ++++ b/src/qemu/qemu_firmware.c +@@ -1358,7 +1358,7 @@ qemuFirmwareMatchDomain(const virDomainDef *def, + } + + if (def->sec) { +- switch ((virDomainLaunchSecurity) def->sec->sectype) { ++ switch (def->sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + if (!supportsSEV) { + VIR_DEBUG("Domain requires SEV, firmware '%s' doesn't support it", +diff --git a/src/qemu/qemu_namespace.c b/src/qemu/qemu_namespace.c +index ff314ce243c0..821e0594802d 100644 +--- a/src/qemu/qemu_namespace.c ++++ b/src/qemu/qemu_namespace.c +@@ -651,7 +651,7 @@ qemuDomainSetupLaunchSecurity(virDomainObj *vm, + if (!sec) + return 0; + +- switch ((virDomainLaunchSecurity) sec->sectype) { ++ switch (sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + VIR_DEBUG("Setting up launch security for SEV"); + +diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c +index 1cef0771563e..edeff9da1082 100644 +--- a/src/qemu/qemu_process.c ++++ b/src/qemu/qemu_process.c +@@ -6751,7 +6751,7 @@ qemuProcessPrepareLaunchSecurityGuestInput(virDomainObj *vm) + if (!sec) + return 0; + +- switch ((virDomainLaunchSecurity) sec->sectype) { ++ switch (sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + return qemuProcessPrepareSEVGuestInput(vm); + case VIR_DOMAIN_LAUNCH_SECURITY_PV: +diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c +index 39d3222eedb7..a1d08cd3e9e7 100644 +--- a/src/qemu/qemu_validate.c ++++ b/src/qemu/qemu_validate.c +@@ -1294,7 +1294,7 @@ qemuValidateDomainDef(const virDomainDef *def, + return -1; + + if (def->sec) { +- switch ((virDomainLaunchSecurity) def->sec->sectype) { ++ switch (def->sec->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", +-- +2.41.0 + diff --git a/0020-src-Convert-some-_virDomainSecDef-sectype-checks-to-.patch b/0020-src-Convert-some-_virDomainSecDef-sectype-checks-to-.patch new file mode 100644 index 0000000..25e8f33 --- /dev/null +++ b/0020-src-Convert-some-_virDomainSecDef-sectype-checks-to-.patch @@ -0,0 +1,179 @@ +From 11e185d0f75d46eede6d3cb161ad55d4194381dc Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Wed, 12 Jun 2024 09:29:59 +0200 +Subject: [PATCH 06/14] src: Convert some _virDomainSecDef::sectype checks to + switch() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 7d16c296e3f0ed61443d3095fd6f8951de57f79d upstream + +In a few instances there is a plain if() check for +_virDomainSecDef::sectype. While this works perfectly for now, +soon there'll be another type and we can utilize compiler to +identify all the places that need adaptation. Switch those if() +statements to switch(). + +[Backport Changes] + +1. The enumeration value VIR_DOMAIN_LAUNCH_SECURITY_CVM, a member of the +virDomainLaunchSecurity enum, was introduced in Anolis libvirt as part of +CSV support in commit c1dd70f9a86f. However, the recent upstream patch's +switch statements do not account for this new enum member. This backport +ensures that VIR_DOMAIN_LAUNCH_SECURITY_CVM is correctly handled in all +the newly added switch statements by adding the appropriate case block in +all affected files. This change prevents compiler warnings and enhances +the completeness of the code. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Abhishek Rajput +Signed-off-by: mohanasv + +diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c +index 47402b37507b..1280989a0170 100644 +--- a/src/qemu/qemu_cgroup.c ++++ b/src/qemu/qemu_cgroup.c +@@ -844,10 +844,21 @@ qemuSetupDevicesCgroup(virDomainObj *vm) + return -1; + } + +- if (vm->def->sec && +- vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_SEV && +- qemuSetupSEVCgroup(vm) < 0) +- return -1; ++ if (vm->def->sec) { ++ switch (vm->def->sec->sectype) { ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: ++ if (qemuSetupSEVCgroup(vm) < 0) ++ return -1; ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_PV: ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_CVM: ++ case VIR_DOMAIN_LAUNCH_SECURITY_NONE: ++ case VIR_DOMAIN_LAUNCH_SECURITY_LAST: ++ virReportEnumRangeError(virDomainLaunchSecurity, vm->def->sec->sectype); ++ return -1; ++ } ++ } + + return 0; + } +diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c +index bf755c72b35c..8f55187158d0 100644 +--- a/src/qemu/qemu_driver.c ++++ b/src/qemu/qemu_driver.c +@@ -19102,10 +19102,23 @@ qemuDomainGetLaunchSecurityInfo(virDomainPtr domain, + if (virDomainGetLaunchSecurityInfoEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + +- if (vm->def->sec && +- vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_SEV) { ++ if (!vm->def->sec) { ++ ret = 0; ++ goto cleanup; ++ } ++ ++ switch (vm->def->sec->sectype) { ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + if (qemuDomainGetSEVInfo(vm, params, nparams, flags) < 0) + goto cleanup; ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_PV: ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_CVM: ++ case VIR_DOMAIN_LAUNCH_SECURITY_NONE: ++ case VIR_DOMAIN_LAUNCH_SECURITY_LAST: ++ virReportEnumRangeError(virDomainLaunchSecurity, vm->def->sec->sectype); ++ return -1; + } + + ret = 0; +diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c +index edeff9da1082..034a35f74eca 100644 +--- a/src/qemu/qemu_process.c ++++ b/src/qemu/qemu_process.c +@@ -6676,11 +6676,21 @@ qemuProcessPrepareDomain(virQEMUDriver *driver, + for (i = 0; i < vm->def->nshmems; i++) + qemuDomainPrepareShmemChardev(vm->def->shmems[i]); + +- if (vm->def->sec && +- vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_SEV) { +- VIR_DEBUG("Updating SEV platform info"); +- if (qemuProcessUpdateSEVInfo(vm) < 0) ++ if (vm->def->sec) { ++ switch (vm->def->sec->sectype) { ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: ++ VIR_DEBUG("Updating SEV platform info"); ++ if (qemuProcessUpdateSEVInfo(vm) < 0) ++ return -1; ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_PV: ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_CVM: ++ case VIR_DOMAIN_LAUNCH_SECURITY_NONE: ++ case VIR_DOMAIN_LAUNCH_SECURITY_LAST: ++ virReportEnumRangeError(virDomainLaunchSecurity, vm->def->sec->sectype); + return -1; ++ } + } + + return 0; +diff --git a/src/security/security_dac.c b/src/security/security_dac.c +index c07e488db7fa..b1596a07e29a 100644 +--- a/src/security/security_dac.c ++++ b/src/security/security_dac.c +@@ -1975,10 +1975,20 @@ virSecurityDACRestoreAllLabel(virSecurityManager *mgr, + rc = -1; + } + +- if (def->sec && +- def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_SEV) { +- if (virSecurityDACRestoreSEVLabel(mgr, def) < 0) +- rc = -1; ++ if (def->sec) { ++ switch (def->sec->sectype) { ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: ++ if (virSecurityDACRestoreSEVLabel(mgr, def) < 0) ++ rc = -1; ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_PV: ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_CVM: ++ case VIR_DOMAIN_LAUNCH_SECURITY_NONE: ++ case VIR_DOMAIN_LAUNCH_SECURITY_LAST: ++ virReportEnumRangeError(virDomainLaunchSecurity, def->sec->sectype); ++ return -1; ++ } + } + + for (i = 0; i < def->nsysinfo; i++) { +@@ -2199,10 +2209,20 @@ virSecurityDACSetAllLabel(virSecurityManager *mgr, + return -1; + } + +- if (def->sec && +- def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_SEV) { +- if (virSecurityDACSetSEVLabel(mgr, def) < 0) ++ if (def->sec) { ++ switch (def->sec->sectype) { ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: ++ if (virSecurityDACSetSEVLabel(mgr, def) < 0) ++ return -1; ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_PV: ++ break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_CVM: ++ case VIR_DOMAIN_LAUNCH_SECURITY_NONE: ++ case VIR_DOMAIN_LAUNCH_SECURITY_LAST: ++ virReportEnumRangeError(virDomainLaunchSecurity, def->sec->sectype); + return -1; ++ } + } + + if (virSecurityDACGetImageIds(secdef, priv, &user, &group)) +-- +2.41.0 + diff --git a/0021-qemu_monitor-Allow-querying-SEV-SNP-state-in-query-s.patch b/0021-qemu_monitor-Allow-querying-SEV-SNP-state-in-query-s.patch new file mode 100644 index 0000000..6ec7b56 --- /dev/null +++ b/0021-qemu_monitor-Allow-querying-SEV-SNP-state-in-query-s.patch @@ -0,0 +1,372 @@ +From b4803934ff3d969958e3d453aa56b8e975f8cc15 Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Mon, 10 Jun 2024 16:17:26 +0200 +Subject: [PATCH 07/14] qemu_monitor: Allow querying SEV-SNP state in + 'query-sev' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 914b986275718b404ef5f700a7da58d5e1172c93 upstream + +In QEMU commit v9.0.0-1155-g59d3740cb4 the return type of +'query-sev' monitor command changed to accommodate SEV-SNP. Even +though we currently support launching plain SNP guests, this will +soon change. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Rahul Kumar +Signed-off-by: mohanasv + +diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c +index 8f55187158d0..01a39bd38cd2 100644 +--- a/src/qemu/qemu_driver.c ++++ b/src/qemu/qemu_driver.c +@@ -19027,10 +19027,7 @@ qemuDomainGetSEVInfo(virDomainObj *vm, + int ret = -1; + int rv; + g_autofree char *tmp = NULL; +- unsigned int apiMajor = 0; +- unsigned int apiMinor = 0; +- unsigned int buildID = 0; +- unsigned int policy = 0; ++ qemuMonitorSEVInfo info = { }; + int maxpar = 0; + + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); +@@ -19045,14 +19042,12 @@ qemuDomainGetSEVInfo(virDomainObj *vm, + qemuDomainObjEnterMonitor(vm); + tmp = qemuMonitorGetSEVMeasurement(QEMU_DOMAIN_PRIVATE(vm)->mon); + +- + if (!tmp) { + qemuDomainObjExitMonitor(vm); + goto endjob; + } + +- rv = qemuMonitorGetSEVInfo(QEMU_DOMAIN_PRIVATE(vm)->mon, +- &apiMajor, &apiMinor, &buildID, &policy); ++ rv = qemuMonitorGetSEVInfo(QEMU_DOMAIN_PRIVATE(vm)->mon, &info); + qemuDomainObjExitMonitor(vm); + + if (rv < 0) +@@ -19064,21 +19059,30 @@ qemuDomainGetSEVInfo(virDomainObj *vm, + goto endjob; + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_API_MAJOR, +- apiMajor) < 0) ++ info.apiMajor) < 0) + goto endjob; + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_API_MINOR, +- apiMinor) < 0) ++ info.apiMinor) < 0) + goto endjob; + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_BUILD_ID, +- buildID) < 0) +- goto endjob; +- if (virTypedParamsAddUInt(params, nparams, &maxpar, +- VIR_DOMAIN_LAUNCH_SECURITY_SEV_POLICY, +- policy) < 0) ++ info.buildID) < 0) + goto endjob; + ++ switch (info.type) { ++ case QEMU_MONITOR_SEV_GUEST_TYPE_SEV: ++ if (virTypedParamsAddUInt(params, nparams, &maxpar, ++ VIR_DOMAIN_LAUNCH_SECURITY_SEV_POLICY, ++ info.data.sev.policy) < 0) ++ goto endjob; ++ break; ++ ++ case QEMU_MONITOR_SEV_GUEST_TYPE_SEV_SNP: ++ case QEMU_MONITOR_SEV_GUEST_TYPE_LAST: ++ break; ++ } ++ + ret = 0; + + endjob: +diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c +index ec586b9036bc..2e9b5cf8d1d8 100644 +--- a/src/qemu/qemu_monitor.c ++++ b/src/qemu/qemu_monitor.c +@@ -4051,14 +4051,11 @@ qemuMonitorGetSEVMeasurement(qemuMonitor *mon) + + int + qemuMonitorGetSEVInfo(qemuMonitor *mon, +- unsigned int *apiMajor, +- unsigned int *apiMinor, +- unsigned int *buildID, +- unsigned int *policy) ++ qemuMonitorSEVInfo *info) + { + QEMU_CHECK_MONITOR(mon); + +- return qemuMonitorJSONGetSEVInfo(mon, apiMajor, apiMinor, buildID, policy); ++ return qemuMonitorJSONGetSEVInfo(mon, info); + } + + +diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h +index c4af9b407d2d..2dc83f7a1df1 100644 +--- a/src/qemu/qemu_monitor.h ++++ b/src/qemu/qemu_monitor.h +@@ -1331,14 +1331,43 @@ int qemuMonitorBlockdevMediumInsert(qemuMonitor *mon, + char * + qemuMonitorGetSEVMeasurement(qemuMonitor *mon); + ++typedef struct _qemuMonitorSEVGuestInfo qemuMonitorSEVGuestInfo; ++struct _qemuMonitorSEVGuestInfo { ++ unsigned int policy; ++ unsigned int handle; ++}; ++ ++typedef struct _qemuMonitorSEVSNPGuestInfo qemuMonitorSEVSNPGuestInfo; ++struct _qemuMonitorSEVSNPGuestInfo { ++ unsigned long long snp_policy; ++}; ++ ++ ++typedef enum { ++ QEMU_MONITOR_SEV_GUEST_TYPE_SEV, ++ QEMU_MONITOR_SEV_GUEST_TYPE_SEV_SNP, ++ ++ QEMU_MONITOR_SEV_GUEST_TYPE_LAST ++} qemuMonitorSEVGuestType; ++ ++VIR_ENUM_DECL(qemuMonitorSEVGuest); ++ ++typedef struct _qemuMonitorSEVInfo qemuMonitorSEVInfo; ++struct _qemuMonitorSEVInfo { ++ unsigned int apiMajor; ++ unsigned int apiMinor; ++ unsigned int buildID; ++ qemuMonitorSEVGuestType type; ++ union { ++ qemuMonitorSEVGuestInfo sev; ++ qemuMonitorSEVSNPGuestInfo sev_snp; ++ } data; ++}; ++ + int + qemuMonitorGetSEVInfo(qemuMonitor *mon, +- unsigned int *apiMajor, +- unsigned int *apiMinor, +- unsigned int *buildID, +- unsigned int *policy) +- ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) +- ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); ++ qemuMonitorSEVInfo *info) ++ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + + int + qemuMonitorSetLaunchSecurityState(qemuMonitor *mon, +diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c +index 066b1c2459de..457e4c08e8db 100644 +--- a/src/qemu/qemu_monitor_json.c ++++ b/src/qemu/qemu_monitor_json.c +@@ -7953,6 +7953,10 @@ qemuMonitorJSONGetSEVMeasurement(qemuMonitor *mon) + } + + ++VIR_ENUM_IMPL(qemuMonitorSEVGuest, ++ QEMU_MONITOR_SEV_GUEST_TYPE_LAST, ++ "sev", "sev-snp"); ++ + /** + * Retrieve info about the SEV setup, returning those fields that + * are required to do a launch attestation, as per +@@ -7966,13 +7970,15 @@ qemuMonitorJSONGetSEVMeasurement(qemuMonitor *mon) + * { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, + * "build-id" : 0, "policy" : 0, "state" : "running", + * "handle" : 1 } } ++ * ++ * Or newer (as of QEMU v9.0.0-1155-g59d3740cb4): ++ * ++ * {"return": {"enabled": true, "api-minor": 55, "handle": 1, "state": "launch-secret", ++ * "api-major": 1, "sev-type": "sev", "build-id": 21, "policy": 1}} + */ + int + qemuMonitorJSONGetSEVInfo(qemuMonitor *mon, +- unsigned int *apiMajor, +- unsigned int *apiMinor, +- unsigned int *buildID, +- unsigned int *policy) ++ qemuMonitorSEVInfo *info) + { + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; +@@ -7987,16 +7993,51 @@ qemuMonitorJSONGetSEVInfo(qemuMonitor *mon, + if (!(data = qemuMonitorJSONGetReply(cmd, reply, VIR_JSON_TYPE_OBJECT))) + return -1; + +- if (virJSONValueObjectGetNumberUint(data, "api-major", apiMajor) < 0 || +- virJSONValueObjectGetNumberUint(data, "api-minor", apiMinor) < 0 || +- virJSONValueObjectGetNumberUint(data, "build-id", buildID) < 0 || +- virJSONValueObjectGetNumberUint(data, "policy", policy) < 0) { +- virReportError(VIR_ERR_INTERNAL_ERROR, "%s", +- _("query-sev reply was missing some data")); +- return -1; ++ if (virJSONValueObjectGetNumberUint(data, "api-major", &info->apiMajor) < 0 || ++ virJSONValueObjectGetNumberUint(data, "api-minor", &info->apiMinor) < 0 || ++ virJSONValueObjectGetNumberUint(data, "build-id", &info->buildID) < 0) { ++ goto error; ++ } ++ ++ if (virJSONValueObjectHasKey(data, "sev-type")) { ++ const char *sevTypeStr = virJSONValueObjectGetString(data, "sev-type"); ++ int sevType; ++ ++ if ((sevType = qemuMonitorSEVGuestTypeFromString(sevTypeStr)) < 0) { ++ virReportError(VIR_ERR_INTERNAL_ERROR, ++ _("unknown SEV type '%1$s'"), ++ sevTypeStr); ++ return -1; ++ } ++ ++ info->type = sevType; ++ } else { ++ info->type = QEMU_MONITOR_SEV_GUEST_TYPE_SEV; ++ } ++ ++ switch (info->type) { ++ case QEMU_MONITOR_SEV_GUEST_TYPE_SEV: ++ if (virJSONValueObjectGetNumberUint(data, "policy", &info->data.sev.policy) < 0 || ++ virJSONValueObjectGetNumberUint(data, "handle", &info->data.sev.handle) < 0) { ++ goto error; ++ } ++ break; ++ ++ case QEMU_MONITOR_SEV_GUEST_TYPE_SEV_SNP: ++ if (virJSONValueObjectGetNumberUlong(data, "snp-policy", &info->data.sev_snp.snp_policy) < 0) ++ goto error; ++ break; ++ ++ case QEMU_MONITOR_SEV_GUEST_TYPE_LAST: ++ break; + } + + return 0; ++ ++ error: ++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", ++ _("query-sev reply was missing some data")); ++ return -1; + } + + +diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h +index ed0027c118e6..53ba584fb950 100644 +--- a/src/qemu/qemu_monitor_json.h ++++ b/src/qemu/qemu_monitor_json.h +@@ -417,12 +417,8 @@ qemuMonitorJSONGetSEVMeasurement(qemuMonitor *mon); + + int + qemuMonitorJSONGetSEVInfo(qemuMonitor *mon, +- unsigned int *apiMajor, +- unsigned int *apiMinor, +- unsigned int *buildID, +- unsigned int *policy) +- ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) +- ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); ++ qemuMonitorSEVInfo *info) ++ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + + int + qemuMonitorJSONGetVersion(qemuMonitor *mon, +diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c +index d9ebb429e71f..a9af904f1c5e 100644 +--- a/tests/qemumonitorjsontest.c ++++ b/tests/qemumonitorjsontest.c +@@ -2727,10 +2727,7 @@ testQemuMonitorJSONGetSEVInfo(const void *opaque) + const testGenericData *data = opaque; + virDomainXMLOption *xmlopt = data->xmlopt; + g_autoptr(qemuMonitorTest) test = NULL; +- unsigned int apiMajor = 0; +- unsigned int apiMinor = 0; +- unsigned int buildID = 0; +- unsigned int policy = 0; ++ qemuMonitorSEVInfo info = { }; + + if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) + return -1; +@@ -2750,16 +2747,70 @@ testQemuMonitorJSONGetSEVInfo(const void *opaque) + "}") < 0) + return -1; + +- if (qemuMonitorGetSEVInfo(qemuMonitorTestGetMonitor(test), +- &apiMajor, &apiMinor, &buildID, &policy) < 0) ++ if (qemuMonitorGetSEVInfo(qemuMonitorTestGetMonitor(test), &info) < 0) + return -1; + +- if (apiMajor != 1 || apiMinor != 8 || buildID != 834 || policy != 3) { ++ if (info.apiMajor != 1 || info.apiMinor != 8 || info.buildID != 834 || ++ info.type != QEMU_MONITOR_SEV_GUEST_TYPE_SEV || ++ info.data.sev.policy != 3 || info.data.sev.handle != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "Unexpected SEV info values"); + return -1; + } + ++ if (qemuMonitorTestAddItem(test, "query-sev", ++ "{" ++ " \"return\": {" ++ " \"enabled\": true," ++ " \"api-minor\": 55," ++ " \"handle\": 1," ++ " \"state\": \"running\"," ++ " \"api-major\": 1," ++ " \"sev-type\": \"sev\"," ++ " \"build-id\": 21," ++ " \"policy\": 1" ++ " }," ++ " \"id\": \"libvirt-16\"" ++ "}") < 0) ++ return -1; ++ ++ if (qemuMonitorGetSEVInfo(qemuMonitorTestGetMonitor(test), &info) < 0) ++ return -1; ++ ++ if (info.apiMajor != 1 || info.apiMinor != 55 || info.buildID != 21 || ++ info.type != QEMU_MONITOR_SEV_GUEST_TYPE_SEV || ++ info.data.sev.policy != 1 || info.data.sev.handle != 1) { ++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", ++ "Unexpected SEV info values"); ++ return -1; ++ } ++ ++ if (qemuMonitorTestAddItem(test, "query-sev", ++ "{" ++ " \"return\": {" ++ " \"enabled\": true," ++ " \"api-minor\": 55," ++ " \"state\": \"running\"," ++ " \"api-major\": 1," ++ " \"sev-type\": \"sev-snp\"," ++ " \"build-id\": 21," ++ " \"snp-policy\": 196608" ++ " }," ++ " \"id\": \"libvirt-16\"" ++ "}") < 0) ++ return -1; ++ ++ if (qemuMonitorGetSEVInfo(qemuMonitorTestGetMonitor(test), &info) < 0) ++ return -1; ++ ++ if (info.apiMajor != 1 || info.apiMinor != 55 || info.buildID != 21 || ++ info.type != QEMU_MONITOR_SEV_GUEST_TYPE_SEV_SNP || ++ info.data.sev_snp.snp_policy != 0x30000) { ++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", ++ "Unexpected SEV SNP info values"); ++ return -1; ++ } ++ + return 0; + } + +-- +2.41.0 + diff --git a/0022-qemu-Report-snp-policy-in-virDomainGetLaunchSecurity.patch b/0022-qemu-Report-snp-policy-in-virDomainGetLaunchSecurity.patch new file mode 100644 index 0000000..91d9ebb --- /dev/null +++ b/0022-qemu-Report-snp-policy-in-virDomainGetLaunchSecurity.patch @@ -0,0 +1,57 @@ +From ac73d00d09b4e578c64884077941bf32550276ad Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Tue, 11 Jun 2024 11:53:43 +0200 +Subject: [PATCH 08/14] qemu: Report snp-policy in + virDomainGetLaunchSecurityInfo() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit be26d0ebbed6167cef69f892ca0e7618fefa9b91 upstream + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Rahul Kumar +Signed-off-by: mohanasv + +diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h +index a1902546bb3c..c11c90a64184 100644 +--- a/include/libvirt/libvirt-domain.h ++++ b/include/libvirt/libvirt-domain.h +@@ -6311,6 +6311,16 @@ int virDomainSetLifecycleAction(virDomainPtr domain, + */ + # define VIR_DOMAIN_LAUNCH_SECURITY_SEV_POLICY "sev-policy" + ++/** ++ * VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP_POLICY: ++ * ++ * Macro represents the policy of the SEV-SNP guest, ++ * as VIR_TYPED_PARAM_ULLONG. ++ * ++ * Since: 10.5.0 ++ */ ++# define VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP_POLICY "sev-snp-policy" ++ + /** + * VIR_DOMAIN_LAUNCH_SECURITY_SEV_SECRET_HEADER: + * +diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c +index 01a39bd38cd2..331e7da75837 100644 +--- a/src/qemu/qemu_driver.c ++++ b/src/qemu/qemu_driver.c +@@ -19079,6 +19079,12 @@ qemuDomainGetSEVInfo(virDomainObj *vm, + break; + + case QEMU_MONITOR_SEV_GUEST_TYPE_SEV_SNP: ++ if (virTypedParamsAddULLong(params, nparams, &maxpar, ++ VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP_POLICY, ++ info.data.sev_snp.snp_policy) < 0) ++ goto endjob; ++ break; ++ + case QEMU_MONITOR_SEV_GUEST_TYPE_LAST: + break; + } +-- +2.41.0 + diff --git a/0023-qemu_capabilities-Introduce-QEMU_CAPS_SEV_SNP_GUEST.patch b/0023-qemu_capabilities-Introduce-QEMU_CAPS_SEV_SNP_GUEST.patch new file mode 100644 index 0000000..3e78ba5 --- /dev/null +++ b/0023-qemu_capabilities-Introduce-QEMU_CAPS_SEV_SNP_GUEST.patch @@ -0,0 +1,82 @@ +From 415620a88c1096f73ebbcc41481b286e6f08d82e Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Wed, 12 Jun 2024 09:04:16 +0200 +Subject: [PATCH 09/14] qemu_capabilities: Introduce QEMU_CAPS_SEV_SNP_GUEST +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 1abcba9d4d8566b298168e1f57bce7507a0790b4 upstream + +This capability tracks sev-snp-guest object availability. + +[Backport Changes] + +1. In the file src/qemu/qemu_capabilities.c, within enum VIR_ENUM_IMPL, +added missing members from index 453 to 459 prior to sev-snp-guest to +support sev-snp and to maintain the enum integrity. + +2. In the file src/qemu/qemu_capabilities.h, within the enum +virQEMUCapsFlags, member QEMU_CAPS_SEV_SNP_GUEST assigned with index +value 460 to maintain the enum integrity. + +3. Changes in tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml were skipped +because Libvirt 9.10 supports XML files only for QEMU 8.2 and earlier +versions. Since the backport effort focuses on enabling Anolis Libvirt +version 9.10 to work with Anolis QEMU version 8.2, the XML file changes are +limited to QEMU versions 8.2 and below. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: priyanka-mani +Signed-off-by: mohanasv + +diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c +index cf6416f91153..91ea784df159 100644 +--- a/src/qemu/qemu_capabilities.c ++++ b/src/qemu/qemu_capabilities.c +@@ -698,6 +698,19 @@ VIR_ENUM_IMPL(virQEMUCaps, + /* 450 */ + "run-with.async-teardown", /* QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN */ + "virtio-blk-vhost-vdpa", /* QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA */ ++ "virtio-blk.iothread-mapping", /* QEMU_CAPS_VIRTIO_BLK_IOTHREAD_MAPPING */ ++ "smp-clusters", /* QEMU_CAPS_SMP_CLUSTERS */ ++ "virtio-mem-pci.dynamic-memslots", /* QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI_DYNAMIC_MEMSLOTS */ ++ ++ /* 455 */ ++ "blockjob.backing-mask-protocol", /* QEMU_CAPS_BLOCKJOB_BACKING_MASK_PROTOCOL */ ++ "display-reload", /* QEMU_CAPS_DISPLAY_RELOAD */ ++ "usb-mtp", /* QEMU_CAPS_DEVICE_USB_MTP */ ++ "machine.virt.ras", /* QEMU_CAPS_MACHINE_VIRT_RAS */ ++ "virtio-sound", /* QEMU_CAPS_DEVICE_VIRTIO_SOUND */ ++ ++ /* 460 */ ++ "sev-snp-guest", /* QEMU_CAPS_SEV_SNP_GUEST */ + ); + + +@@ -1390,6 +1403,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { + { "virtio-crypto-device", QEMU_CAPS_DEVICE_VIRTIO_CRYPTO }, + { "cryptodev-backend-lkcf", QEMU_CAPS_OBJECT_CRYPTO_LKCF }, + { "pvpanic-pci", QEMU_CAPS_DEVICE_PANIC_PCI }, ++ { "sev-snp-guest", QEMU_CAPS_SEV_SNP_GUEST }, + }; + + +diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h +index 3c4f7f625b45..1c93258b3d11 100644 +--- a/src/qemu/qemu_capabilities.h ++++ b/src/qemu/qemu_capabilities.h +@@ -678,6 +678,9 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ + QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN, /* asynchronous teardown -run-with async-teardown=on|off */ + QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA, /* virtio-blk-vhost-vdpa block driver */ + ++ /* 460 */ ++ QEMU_CAPS_SEV_SNP_GUEST = 460, /* -object sev-snp-guest */ ++ + QEMU_CAPS_LAST /* this must always be the last item */ + } virQEMUCapsFlags; + +-- +2.41.0 + diff --git a/0024-conf-Introduce-SEV-SNP-support.patch b/0024-conf-Introduce-SEV-SNP-support.patch new file mode 100644 index 0000000..33f2e37 --- /dev/null +++ b/0024-conf-Introduce-SEV-SNP-support.patch @@ -0,0 +1,584 @@ +From ef6f74ee6885ce48bd00e24c91246b97eb12d8a2 Mon Sep 17 00:00:00 2001 +From: Michal Privoznik +Date: Tue, 11 Jun 2024 11:58:41 +0200 +Subject: [PATCH 10/14] conf: Introduce SEV-SNP support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit c65eba1f57ab1670f5bfc6bd178df1d5c3b09e2a upstream + +SEV-SNP is an enhancement of SEV/SEV-ES and thus it shares some +fields with it. Nevertheless, on XML level, it's yet another type +of . + +[Backport Changes] + +1. The enumeration value VIR_DOMAIN_LAUNCH_SECURITY_CVM, a member of the +virDomainLaunchSecurity enum, was introduced in Anolis libvirt as part of +CSV support in commit c1dd70f9a86f. However, the recent upstream patch's +switch statements do not account for this new enum member. This backport +ensures that VIR_DOMAIN_LAUNCH_SECURITY_CVM is correctly handled in all +the newly added switch statements by adding the appropriate case block in +file src/conf/domain_validate.c. This change prevents compiler warnings +and enhances the completeness of the code. + +Signed-off-by: Michal Privoznik +Reviewed-by: Daniel P. Berrangé +Signed-off-by: priyanka-mani +Signed-off-by: mohanasv + +diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst +index 310d2bc4275b..fb89bff64dcd 100644 +--- a/docs/formatdomain.rst ++++ b/docs/formatdomain.rst +@@ -8748,6 +8748,114 @@ spec `__ + session blob defined in the SEV API spec. See SEV spec LAUNCH_START section + for the session blob format. + ++ ++Some modern AMD processors support Secure Encrypted Virtualization with Secure ++Nested Paging enhancement, also known as SEV-SNP. :since:`Since 10.5.0` To ++enable it ```` should be used. It shares some ++attributes and elements with ``type='sev'`` but differs in others. Example configuration: ++ ++:: ++ ++ ++ ... ++ ++ 47 ++ 1 ++ 0x00030000 ++ ... ++ ... ++ ... ++ .../hostData> ++ ++ ... ++ ++ ++The ```` element accepts the following attributes: ++ ++``kernelHashes`` ++ The optional ``kernelHashes`` attribute indicates whether the ++ hashes of the kernel, ramdisk and command line should be included ++ in the measurement done by the firmware. This is only valid if ++ using direct kernel boot. ++ ++``authorKey`` ++ The optional ``authorKey`` attribute indicates whether ```` element ++ contains the 'AUTHOR_KEY' field defined SEV-SNP firmware ABI. ++ ++``vcek`` ++ The optional ``vcek`` attribute indicates whether the guest is allowed to ++ chose between VLEK (Versioned Loaded Endorsement Key) or VCEK (Versioned ++ Chip Endorsement Key) when requesting attestation reports from firmware. ++ Set this to ``no`` to disable the use of VCEK. ++ ++Aforementioned SEV-SNP firmware ABI can be found here: ++``__ ++ ++The ```` element then accepts the following child elements: ++ ++``cbitpos`` ++ The required ``cbitpos`` element provides the C-bit (aka encryption bit) ++ location in guest page table entry. The value of ``cbitpos`` is hypervisor ++ dependent and can be obtained through the ``sev`` element from the domain ++ capabilities. ++``reducedPhysBits`` ++ The required ``reducedPhysBits`` element provides the physical address bit ++ reduction. Similar to ``cbitpos`` the value of ``reduced-phys-bit`` is ++ hypervisor dependent and can be obtained through the ``sev`` element from the ++ domain capabilities. ++``policy`` ++ The required ``policy`` element provides the guest policy which must be ++ maintained by the SEV-SNP firmware. This policy is enforced by the firmware ++ and restricts what configuration and operational commands can be performed ++ on this guest by the hypervisor. The guest policy provided during guest ++ launch is bound to the guest and cannot be changed throughout the lifetime ++ of the guest. The policy is also transmitted during snapshot and migration ++ flows and enforced on the destination platform. The guest policy is a 64bit ++ unsigned number with the fields shown in table (See section `4.3 Guest ++ Policy` in aforementioned firmware ABI specification): ++ ++ ====== ========================================================================================= ++ Bit(s) Description ++ ====== ========================================================================================= ++ 63:25 Reserved. Must be zero. ++ 24 Ciphertext hiding must be enabled when set, otherwise may be enabled or disabled. ++ 23 Running Average Power Limit (RAPL) must be disabled when set. ++ 22 Require AES 256 XTS for memory encryption when set, otherwise AES 128 XEX may be allowed. ++ 21 CXL can be populated with devices or memory when set. ++ 20 Guest can be activated only on one socket when set. ++ 19 Debugging is allowed when set. ++ 18 Association with a migration agent is allowed when set. ++ 17 Reserved. Must be set. ++ 16 SMT is allowed. ++ 15:8 The minimum ABI major version required for this guest to run. ++ 7:0 The minimum ABI minor version required for this guest to run. ++ ====== ========================================================================================= ++ ++ The default value is hypervisor dependant and QEMU defaults to value 0x30000 ++ meaning no minimum ABI major/minor version is required and SMT is allowed. ++ ++``guestVisibleWorkarounds`` ++ The optional ``guestVisibleWorkarounds`` element is a 16-byte, ++ base64-encoded blob to report hypervisor-defined workarounds, corresponding ++ to the 'GOSVW' parameter of the SNP_LAUNCH_START command defined in the ++ SEV-SNP firmware ABI. ++ ++``idBlock`` ++ The optional ``idBlock`` element is a 96-byte, base64-encoded blob to ++ provide the 'ID Block' structure for the SNP_LAUNCH_FINISH command defined ++ in the SEV-SNP firmware ABI. ++ ++``idAuth`` ++ The optional ``idAuth`` element is a 4096-byte, base64-encoded blob to ++ provide the 'ID Authentication Information Structure' for the ++ SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI. ++ ++``hostData`` ++ The optional ``hostData`` element is a 32-byte, base64-encoded, user-defined ++ blob to provide to the guest, as documented for the 'HOST_DATA' parameter of ++ the SNP_LAUNCH_FINISH command in the SEV-SNP firmware ABI. ++ ++ + Example configs + =============== + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 1f3c2161afc7..59f8351a3282 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -1513,6 +1513,7 @@ VIR_ENUM_IMPL(virDomainLaunchSecurity, + VIR_DOMAIN_LAUNCH_SECURITY_LAST, + "", + "sev", ++ "sev-snp", + "s390-pv", + "cvm", + ); +@@ -3826,6 +3827,12 @@ virDomainSecDefFree(virDomainSecDef *def) + g_free(def->data.sev.secret_header); + g_free(def->data.sev.secret); + break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP: ++ g_free(def->data.sev_snp.guest_visible_workarounds); ++ g_free(def->data.sev_snp.id_block); ++ g_free(def->data.sev_snp.id_auth); ++ g_free(def->data.sev_snp.host_data); ++ break; + case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_CVM: + case VIR_DOMAIN_LAUNCH_SECURITY_NONE: +@@ -13509,6 +13516,36 @@ virDomainSEVDefParseXML(virDomainSEVDef *def, + } + + ++static int ++virDomainSEVSNPDefParseXML(virDomainSEVSNPDef *def, ++ xmlXPathContextPtr ctxt) ++{ ++ if (virDomainSEVCommonDefParseXML(&def->common, ctxt) < 0) ++ return -1; ++ ++ if (virXMLPropTristateBool(ctxt->node, "authorKey", VIR_XML_PROP_NONE, ++ &def->author_key) < 0) ++ return -1; ++ ++ if (virXMLPropTristateBool(ctxt->node, "vcek", VIR_XML_PROP_NONE, ++ &def->vcek) < 0) ++ return -1; ++ ++ if (virXPathULongLongBase("string(./policy)", ctxt, 16, &def->policy) < 0) { ++ virReportError(VIR_ERR_XML_ERROR, "%s", ++ _("failed to get launch security policy")); ++ return -1; ++ } ++ ++ def->guest_visible_workarounds = virXPathString("string(./guestVisibleWorkarounds)", ctxt); ++ def->id_block = virXPathString("string(./idBlock)", ctxt); ++ def->id_auth = virXPathString("string(./idAuth)", ctxt); ++ def->host_data = virXPathString("string(./hostData)", ctxt); ++ ++ return 0; ++} ++ ++ + static virDomainSecDef * + virDomainSecDefParseXML(xmlNodePtr lsecNode, + xmlXPathContextPtr ctxt) +@@ -13528,6 +13565,10 @@ virDomainSecDefParseXML(xmlNodePtr lsecNode, + if (virDomainSEVDefParseXML(&sec->data.sev, ctxt) < 0) + return NULL; + break; ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP: ++ if (virDomainSEVSNPDefParseXML(&sec->data.sev_snp, ctxt) < 0) ++ return NULL; ++ break; + case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_CVM: + break; +@@ -26540,6 +26581,34 @@ virDomainSEVDefFormat(virBuffer *attrBuf, + } + + ++static void ++virDomainSEVSNPDefFormat(virBuffer *attrBuf, ++ virBuffer *childBuf, ++ virDomainSEVSNPDef *def) ++{ ++ virDomainSEVCommonDefFormat(attrBuf, childBuf, &def->common); ++ ++ if (def->author_key != VIR_TRISTATE_BOOL_ABSENT) { ++ virBufferAsprintf(attrBuf, " authorKey='%s'", ++ virTristateBoolTypeToString(def->author_key)); ++ } ++ ++ if (def->vcek != VIR_TRISTATE_BOOL_ABSENT) { ++ virBufferAsprintf(attrBuf, " vcek='%s'", ++ virTristateBoolTypeToString(def->vcek)); ++ } ++ ++ virBufferAsprintf(childBuf, "0x%08llx\n", def->policy); ++ ++ virBufferEscapeString(childBuf, ++ "%s\n", ++ def->guest_visible_workarounds); ++ virBufferEscapeString(childBuf, "%s\n", def->id_block); ++ virBufferEscapeString(childBuf, "%s\n", def->id_auth); ++ virBufferEscapeString(childBuf, "%s\n", def->host_data); ++} ++ ++ + static void + virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + { +@@ -26557,6 +26626,10 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) + virDomainSEVDefFormat(&attrBuf, &childBuf, &sec->data.sev); + break; + ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP: ++ virDomainSEVSNPDefFormat(&attrBuf, &childBuf, &sec->data.sev_snp); ++ break; ++ + case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_CVM: + break; +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index 980da7742f54..e6316517d324 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -2845,6 +2845,7 @@ struct _virDomainKeyWrapDef { + typedef enum { + VIR_DOMAIN_LAUNCH_SECURITY_NONE, + VIR_DOMAIN_LAUNCH_SECURITY_SEV, ++ VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP, + VIR_DOMAIN_LAUNCH_SECURITY_PV, + VIR_DOMAIN_LAUNCH_SECURITY_CVM, + +@@ -2871,10 +2872,24 @@ struct _virDomainSEVDef { + char *secret; + }; + ++ ++struct _virDomainSEVSNPDef { ++ virDomainSEVCommonDef common; ++ unsigned long long policy; ++ char *guest_visible_workarounds; ++ char *id_block; ++ char *id_auth; ++ char *host_data; ++ virTristateBool author_key; ++ virTristateBool vcek; ++}; ++ ++ + struct _virDomainSecDef { + virDomainLaunchSecurity sectype; + union { + virDomainSEVDef sev; ++ virDomainSEVSNPDef sev_snp; + } data; + }; + +diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c +index c72108886ecc..a983af8834dd 100644 +--- a/src/conf/domain_validate.c ++++ b/src/conf/domain_validate.c +@@ -1787,6 +1787,48 @@ virDomainDefValidateIOThreads(const virDomainDef *def) + } + + ++#define CHECK_BASE64_LEN(val, elemName, exp_len) \ ++{ \ ++ size_t len; \ ++ g_autofree unsigned char *tmp = NULL; \ ++ if (val && (tmp = g_base64_decode(val, &len)) && len != exp_len) { \ ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ ++ _("Unexpected length of '%1$s', expected %2$u got %3$zu"), \ ++ elemName, exp_len, len); \ ++ return -1; \ ++ } \ ++} ++ ++static int ++virDomainDefLaunchSecurityValidate(const virDomainDef *def) ++{ ++ virDomainSEVSNPDef *sev_snp; ++ ++ if (!def->sec) ++ return 0; ++ ++ switch (def->sec->sectype) { ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP: ++ sev_snp = &def->sec->data.sev_snp; ++ ++ CHECK_BASE64_LEN(sev_snp->guest_visible_workarounds, "guestVisibleWorkarounds", 16); ++ CHECK_BASE64_LEN(sev_snp->id_block, "idBlock", 96); ++ CHECK_BASE64_LEN(sev_snp->id_auth, "idAuth", 4096); ++ CHECK_BASE64_LEN(sev_snp->host_data, "hostData", 32); ++ break; ++ ++ case VIR_DOMAIN_LAUNCH_SECURITY_NONE: ++ case VIR_DOMAIN_LAUNCH_SECURITY_SEV: ++ case VIR_DOMAIN_LAUNCH_SECURITY_PV: ++ case VIR_DOMAIN_LAUNCH_SECURITY_CVM: ++ case VIR_DOMAIN_LAUNCH_SECURITY_LAST: ++ } ++ ++ return 0; ++} ++ ++#undef CHECK_BASE64_LEN ++ + static int + virDomainDefValidateInternal(const virDomainDef *def, + virDomainXMLOption *xmlopt) +@@ -1842,6 +1884,9 @@ virDomainDefValidateInternal(const virDomainDef *def, + if (virDomainDefValidateIOThreads(def) < 0) + return -1; + ++ if (virDomainDefLaunchSecurityValidate(def) < 0) ++ return -1; ++ + return 0; + } + +diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng +index 966e5831f41d..44420d2c62da 100644 +--- a/src/conf/schemas/domaincommon.rng ++++ b/src/conf/schemas/domaincommon.rng +@@ -515,6 +515,9 @@ + + + ++ ++ ++ + + + s390-pv +@@ -578,6 +581,52 @@ + +
+ ++ ++ ++ sev-snp ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +