[PATCH v1 00/18] LIBVIRT: X86: TDX support

Hi, This series brings libvirt the x86 TDX support. * What's TDX? TDX stands for Trust Domain Extensions which isolates VMs from the virtual-machine manager (VMM)/hypervisor and any other software on the platform. This patchset extends libvirt to support TDX, with which one can start a TDX guest from high level rather than running qemu directly. * Misc As QEMU use a software emulated way to reset guest which isn't supported by TDX guest for security reason. We simulate reboot for TDX guest by kill and create a new one in FakeReboot framework. Complete code can be found at [1], matching qemu code can be found at [2]. There is a 'QGS' element for attestation which isn't in matching qemu[2] yet. I keep them intentionally as they will be implemented in qemu as extention series of [2]. * Test start/stop/reboot/reset with virsh stop/reboot trigger in guest stop with on_poweroff=destroy/restart reboot with on_reboot=destroy/restart * Patch organization - patch 1-4: Support query of TDX capabilities - patch 5-11: Add TDX type to launchsecurity framework - patch 12-17: Add reboot/reset support to TDX guest - patch 18: Add docs TODO: - add reconnect logic in virsh command [1] https://github.com/intel/libvirt-tdx/commits/tdx_for_upstream_v1 [2] https://github.com/intel-staging/qemu-tdx/tree/tdx-qemu-upstream-v8 Thanks Zhenzhong Changelog: v1: - s/virQEMUCapsKVMSupportsSecureGuestINTEL/virQEMUCapsKVMSupportsSecureGuestTDX (Daniel) - make policy element optional and expose to QEMU directly (Daniel) - s/qemuProcessSecFakeReboot/qemuProcessFakeRebootViaRecreate (Daniel) - simplify QGS element schema by supporting only UNIX socket (Daniel) - add new events VIR_DOMAIN_EVENT_[STOPPED|STARTED] for control plane (Daniel) - s/quoteGenerationService/quoteGenerationSocket as QEMU - add virsh reset support rfcv4: - add a check to tools/virt-host-validate-qemu.c (Daniel) - remove check of q35 (Daniel) - model 'SocktetAddress' QAPI in xml schema (Daniel) - s/Quote-Generation-Service/quoteGenerationService/ (Daniel) - define bits in tdx->policy and add validating logic (Daniel) - presume QEMU choose split kernel irqchip for TDX guest by default (Daniel) - utilize existing FakeReboot framework to do reboot for TDX guest (Daniel) - drop patch11 'conf: Add support to keep same domid for hard reboot' (Daniel) - add test in tests/ to validate parsing and formatting logic (Daniel) - add doc in docs/formatdomain.rst (Daniel) - add R-B rfcv3: - Change to generate qemu cmdline with -bios - drop firmware auto match as -bios is used - add a hard reboot method to reboot TDX guest rfcv3: https://www.mail-archive.com/devel@lists.libvirt.org/msg00385.html rfcv2: - give up using qmp cmd and check TDX directly on host for TDX capabilities. - use launchsecurity framework to support TDX - use <os>.<loader> for general loader - add auto firmware match feature for TDX A example TDVF fimware description file 70-edk2-x86_64-tdx.json: { "description": "UEFI firmware for x86_64, supporting Intel TDX", "interface-types": [ "uefi" ], "mapping": { "device": "generic", "filename": "/usr/share/OVMF/OVMF_CODE-tdx.fd" }, "targets": [ { "architecture": "x86_64", "machines": [ "pc-q35-*" ] } ], "features": [ "intel-tdx", "verbose-dynamic" ], "tags": [ ] } rfcv2: https://www.mail-archive.com/libvir-list@redhat.com/msg219378.html Zhenzhong Duan (18): tools: Secure guest check for Intel in virt-host-validate qemu: Check if INTEL Trust Domain Extention support is enabled qemu: Add TDX capability conf: Expose TDX feature in domain capabilities conf: Add tdx as launch security type conf: Validate TDX launchSecurity element mrConfigId/mrOwner/mrOwnerConfig qemu: Add command line and validation for TDX type conf: Expose TDX type in domain launch security capability qemu: Force special parameters enabled for TDX guest conf: Add Intel TDX Quote Generation Service(QGS) support qemu: Add command line for TDX Quote Generation Service(QGS) qemu: Add FakeReboot support for TDX guest qemu: Support reboot command in guest qemu: Avoid duplicate FakeReboot for secure guest qemu: Send event VIR_DOMAIN_EVENT_[STOPPED|STARTED] during recreation qemu: Bypass sending VIR_DOMAIN_EVENT_RESUMED event when TD VM reboot qemu: Support domain reset command for TDX guest docs: domain: Add documentation for Intel TDX guest docs/formatdomain.rst | 63 ++++++++++++++++++ docs/formatdomaincaps.rst | 1 + examples/c/misc/event-test.c | 6 ++ include/libvirt/libvirt-domain.h | 2 + src/conf/domain_capabilities.c | 1 + src/conf/domain_capabilities.h | 1 + src/conf/domain_conf.c | 82 +++++++++++++++++++++++ src/conf/domain_conf.h | 21 ++++++ src/conf/domain_validate.c | 11 ++++ src/conf/schemas/domaincaps.rng | 9 +++ src/conf/schemas/domaincommon.rng | 41 ++++++++++++ src/conf/virconftypes.h | 2 + src/qemu/qemu_capabilities.c | 38 ++++++++++- src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_cgroup.c | 1 + src/qemu/qemu_command.c | 54 +++++++++++++++ src/qemu/qemu_driver.c | 7 ++ src/qemu/qemu_firmware.c | 1 + src/qemu/qemu_monitor.c | 28 +++++++- src/qemu/qemu_monitor.h | 2 +- src/qemu/qemu_monitor_json.c | 6 +- src/qemu/qemu_namespace.c | 1 + src/qemu/qemu_process.c | 105 ++++++++++++++++++++++++++++-- src/qemu/qemu_process.h | 2 + src/qemu/qemu_validate.c | 45 +++++++++++++ src/security/security_dac.c | 2 + tools/virsh-domain-event.c | 6 +- tools/virt-host-validate-common.c | 31 ++++++++- tools/virt-host-validate-common.h | 1 + 29 files changed, 558 insertions(+), 13 deletions(-) -- 2.34.1

Add check in virt-host-validate for secure guest support on x86 for Intel Trust Domain Extentions. Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- tools/virt-host-validate-common.c | 31 ++++++++++++++++++++++++++++++- tools/virt-host-validate-common.h | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/tools/virt-host-validate-common.c b/tools/virt-host-validate-common.c index 63cc3dbe7b..59f6ac3319 100644 --- a/tools/virt-host-validate-common.c +++ b/tools/virt-host-validate-common.c @@ -44,7 +44,8 @@ VIR_ENUM_IMPL(virHostValidateCPUFlag, "svm", "sie", "158", - "sev"); + "sev", + "tdx_host_platform"); int virHostValidateDeviceExists(const char *hvname, @@ -434,12 +435,36 @@ virHostValidateAMDSev(const char *hvname, } +static int virHostValidateIntelTDX(virValidateLevel level) +{ + g_autofree char *mod_value = NULL; + + if (virFileReadValueString(&mod_value, "/sys/module/kvm_intel/parameters/tdx") < 0) { + virValidateFail(level, "Intel Trust Domain Extentions not " + "supported by the currently used kernel"); + return VIR_VALIDATE_FAILURE(level); + } + + if (mod_value[0] != 'Y') { + virValidateFail(level, + "Intel Trust Domain Extentions appears to be " + "disabled in kernel. Add kvm_intel.tdx=Y " + "to the kernel cmdline arguments"); + return VIR_VALIDATE_FAILURE(level); + } + + virValidatePass(); + return 1; +} + + int virHostValidateSecureGuests(const char *hvname, virValidateLevel level) { g_autoptr(virBitmap) flags = NULL; bool hasFac158 = false; bool hasAMDSev = false; + bool hasIntelTDX = false; virArch arch = virArchFromHost(); g_autofree char *cmdline = NULL; static const char *kIBMValues[] = {"y", "Y", "on", "ON", "oN", "On", "1"}; @@ -450,6 +475,8 @@ int virHostValidateSecureGuests(const char *hvname, hasFac158 = true; else if (flags && virBitmapIsBitSet(flags, VIR_HOST_VALIDATE_CPU_FLAG_SEV)) hasAMDSev = true; + else if (flags && virBitmapIsBitSet(flags, VIR_HOST_VALIDATE_CPU_FLAG_TDX)) + hasIntelTDX = true; virValidateCheck(hvname, "%s", _("Checking for secure guest support")); if (ARCH_IS_S390(arch)) { @@ -485,6 +512,8 @@ int virHostValidateSecureGuests(const char *hvname, } } else if (hasAMDSev) { return virHostValidateAMDSev(hvname, level); + } else if (hasIntelTDX) { + return virHostValidateIntelTDX(level); } virValidateFail(level, diff --git a/tools/virt-host-validate-common.h b/tools/virt-host-validate-common.h index 7fb3545fe3..c81d203933 100644 --- a/tools/virt-host-validate-common.h +++ b/tools/virt-host-validate-common.h @@ -32,6 +32,7 @@ typedef enum { VIR_HOST_VALIDATE_CPU_FLAG_SIE, VIR_HOST_VALIDATE_CPU_FLAG_FACILITY_158, VIR_HOST_VALIDATE_CPU_FLAG_SEV, + VIR_HOST_VALIDATE_CPU_FLAG_TDX, VIR_HOST_VALIDATE_CPU_FLAG_LAST, } virHostValidateCPUFlag; -- 2.34.1

Implement TDX check in order to generate domain feature capability correctly in case the availability of the feature changed. For INTEL TDX the verification is: - checking if "/sys/module/kvm_intel/parameters/tdx" contains the value 'Y': meaning TDX is enabled in the host kernel. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- src/qemu/qemu_capabilities.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 665f5e225e..280854d6a6 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5299,6 +5299,24 @@ virQEMUCapsKVMSupportsSecureGuestAMD(void) } +/* + * Check whether INTEL Trust Domain Extention (x86) is enabled + */ +static bool +virQEMUCapsKVMSupportsSecureGuestTDX(void) +{ + g_autofree char *modValue = NULL; + + if (virFileReadValueString(&modValue, "/sys/module/kvm_intel/parameters/tdx") < 0) + return false; + + if (modValue[0] != 'Y') + return false; + + return true; +} + + /* * Check whether the secure guest functionality is enabled. * See the specific architecture function for details on the verifications made. @@ -5312,7 +5330,8 @@ virQEMUCapsKVMSupportsSecureGuest(void) return virQEMUCapsKVMSupportsSecureGuestS390(); if (ARCH_IS_X86(arch)) - return virQEMUCapsKVMSupportsSecureGuestAMD(); + return virQEMUCapsKVMSupportsSecureGuestAMD() || + virQEMUCapsKVMSupportsSecureGuestTDX(); return false; } -- 2.34.1

QEMU_CAPS_TDX_GUEST set means TDX supported with this QEMU. Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 280854d6a6..f241321b0d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -732,6 +732,7 @@ VIR_ENUM_IMPL(virQEMUCaps, /* 475 */ "virtio-scsi.iothread-mapping", /* QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING */ + "tdx-guest", /* QEMU_CAPS_TDX_GUEST */ ); @@ -1420,6 +1421,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "sev-snp-guest", QEMU_CAPS_SEV_SNP_GUEST }, { "acpi-erst", QEMU_CAPS_DEVICE_ACPI_ERST }, { "virtio-mem-ccw", QEMU_CAPS_DEVICE_VIRTIO_MEM_CCW }, + { "tdx-guest", QEMU_CAPS_TDX_GUEST}, }; diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 685a938bbc..b7263d9348 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -713,6 +713,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 475 */ QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING, /* virtio-scsi supports per-virtqueue iothread mapping */ + QEMU_CAPS_TDX_GUEST, /* -object tdx-guest,... */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; -- 2.34.1

Extend qemu TDX capability to domain capabilities. Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/formatdomaincaps.rst | 1 + src/conf/domain_capabilities.c | 1 + src/conf/domain_capabilities.h | 1 + src/conf/schemas/domaincaps.rng | 9 +++++++++ src/qemu/qemu_capabilities.c | 13 +++++++++++++ 5 files changed, 25 insertions(+) diff --git a/docs/formatdomaincaps.rst b/docs/formatdomaincaps.rst index ed95af4fee..664194b16d 100644 --- a/docs/formatdomaincaps.rst +++ b/docs/formatdomaincaps.rst @@ -720,6 +720,7 @@ capabilities. All features occur as children of the main ``features`` element. <backingStoreInput supported='yes'/> <backup supported='yes'/> <async-teardown supported='yes'/> + <tdx supported='yes'/> <sev> <cbitpos>47</cbitpos> <reduced-phys-bits>1</reduced-phys-bits> diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c index 27551f6102..524246c1e2 100644 --- a/src/conf/domain_capabilities.c +++ b/src/conf/domain_capabilities.c @@ -44,6 +44,7 @@ VIR_ENUM_IMPL(virDomainCapsFeature, "async-teardown", "s390-pv", "ps2", + "tdx", ); static virClass *virDomainCapsClass; diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h index 69dd1a15c1..eacbd6b6b3 100644 --- a/src/conf/domain_capabilities.h +++ b/src/conf/domain_capabilities.h @@ -274,6 +274,7 @@ typedef enum { VIR_DOMAIN_CAPS_FEATURE_ASYNC_TEARDOWN, VIR_DOMAIN_CAPS_FEATURE_S390_PV, VIR_DOMAIN_CAPS_FEATURE_PS2, + VIR_DOMAIN_CAPS_FEATURE_TDX, VIR_DOMAIN_CAPS_FEATURE_LAST } virDomainCapsFeature; diff --git a/src/conf/schemas/domaincaps.rng b/src/conf/schemas/domaincaps.rng index 595dbcd634..c33edbea5f 100644 --- a/src/conf/schemas/domaincaps.rng +++ b/src/conf/schemas/domaincaps.rng @@ -357,6 +357,9 @@ <optional> <ref name="ps2"/> </optional> + <optional> + <ref name="tdx"/> + </optional> <optional> <ref name="sev"/> </optional> @@ -421,6 +424,12 @@ </element> </define> + <define name="tdx"> + <element name="tdx"> + <ref name="supported"/> + </element> + </define> + <define name="sev"> <element name="sev"> <ref name="supported"/> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index f241321b0d..5b8afa3c41 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6956,6 +6956,18 @@ virQEMUCapsFillDomainFeatureHypervCaps(virQEMUCaps *qemuCaps, } +static void +virQEMUCapsFillDomainFeatureTDXCaps(virQEMUCaps *qemuCaps, + virDomainCaps *domCaps) +{ + if (domCaps->arch == VIR_ARCH_X86_64 && + domCaps->virttype == VIR_DOMAIN_VIRT_KVM && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_TDX_GUEST) && + virQEMUCapsGetKVMSupportsSecureGuest(qemuCaps)) + domCaps->features[VIR_DOMAIN_CAPS_FEATURE_TDX] = VIR_TRISTATE_BOOL_YES; +} + + int virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, virQEMUCaps *qemuCaps, @@ -7023,6 +7035,7 @@ virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, virQEMUCapsFillDomainLaunchSecurity(qemuCaps, launchSecurity); virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, net); virQEMUCapsFillDomainDevicePanicCaps(qemuCaps, domCaps->machine, panic); + virQEMUCapsFillDomainFeatureTDXCaps(qemuCaps, domCaps); return 0; } -- 2.34.1

When 'tdx' is used, the VM will be launched with Intel TDX feature enabled. TDX feature supports running encrypted VM (Trust Domain, TD) under the control of KVM. A TD runs in a CPU model which protects the confidentiality of its memory and its CPU state from other software. There are four optional child elements. Element policy is 64bit hex, bit 0 is set to enable TDX debug, bit 28 is set to enable sept-ve-disable, other bits are reserved currently. When policy isn't specified, QEMU will use its own default value 0x10000000. mrConfigId, mrOwner and mrOwnerConfig are base64 encoded SHA384 digest string. For example: <launchSecurity type='tdx'> <policy>0x10000001</policy> <mrConfigId>xxx</mrConfigId> <mrOwner>xxx</mrOwner> <mrOwnerConfig>xxx</mrOwnerConfig> </launchSecurity> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/conf/domain_conf.c | 49 +++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 11 +++++++ src/conf/domain_validate.c | 1 + src/conf/schemas/domaincommon.rng | 32 ++++++++++++++++++++ src/conf/virconftypes.h | 2 ++ src/qemu/qemu_cgroup.c | 1 + src/qemu/qemu_command.c | 2 ++ src/qemu/qemu_driver.c | 1 + src/qemu/qemu_firmware.c | 1 + src/qemu/qemu_namespace.c | 1 + src/qemu/qemu_process.c | 2 ++ src/qemu/qemu_validate.c | 1 + src/security/security_dac.c | 2 ++ 13 files changed, 106 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c724638180..458852854b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1538,6 +1538,7 @@ VIR_ENUM_IMPL(virDomainLaunchSecurity, "sev", "sev-snp", "s390-pv", + "tdx", ); VIR_ENUM_IMPL(virDomainPstoreBackend, @@ -3949,6 +3950,11 @@ virDomainSecDefFree(virDomainSecDef *def) g_free(def->data.sev_snp.id_auth); g_free(def->data.sev_snp.host_data); break; + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: + g_free(def->data.tdx.mrconfigid); + g_free(def->data.tdx.mrowner); + g_free(def->data.tdx.mrownerconfig); + break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: @@ -14174,6 +14180,29 @@ virDomainSEVSNPDefParseXML(virDomainSEVSNPDef *def, } +static int +virDomainTDXDefParseXML(virDomainTDXDef *def, + xmlXPathContextPtr ctxt) +{ + int rc; + + rc = virXPathULongLongBase("string(./policy)", ctxt, 16, &def->policy); + if (rc == 0) { + def->havePolicy = true; + } else if (rc == -2) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("failed to get launch security policy for launch security type TDX")); + return -1; + } + + def->mrconfigid = virXPathString("string(./mrConfigId)", ctxt); + def->mrowner = virXPathString("string(./mrOwner)", ctxt); + def->mrownerconfig = virXPathString("string(./mrOwnerConfig)", ctxt); + + return 0; +} + + static virDomainSecDef * virDomainSecDefParseXML(xmlNodePtr lsecNode, xmlXPathContextPtr ctxt) @@ -14197,6 +14226,10 @@ virDomainSecDefParseXML(xmlNodePtr lsecNode, if (virDomainSEVSNPDefParseXML(&sec->data.sev_snp, ctxt) < 0) return NULL; break; + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: + if (virDomainTDXDefParseXML(&sec->data.tdx, ctxt) < 0) + return NULL; + break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: @@ -27597,6 +27630,18 @@ virDomainSEVSNPDefFormat(virBuffer *attrBuf, } +static void +virDomainTDXDefFormat(virBuffer *childBuf, virDomainTDXDef *def) +{ + if (def->havePolicy) + virBufferAsprintf(childBuf, "<policy>0x%llx</policy>\n", def->policy); + + virBufferEscapeString(childBuf, "<mrConfigId>%s</mrConfigId>\n", def->mrconfigid); + virBufferEscapeString(childBuf, "<mrOwner>%s</mrOwner>\n", def->mrowner); + virBufferEscapeString(childBuf, "<mrOwnerConfig>%s</mrOwnerConfig>\n", def->mrownerconfig); +} + + static void virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) { @@ -27618,6 +27663,10 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) virDomainSEVSNPDefFormat(&attrBuf, &childBuf, &sec->data.sev_snp); break; + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: + virDomainTDXDefFormat(&childBuf, &sec->data.tdx); + break; + case VIR_DOMAIN_LAUNCH_SECURITY_PV: break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8dfadbb98d..f838f39aee 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2956,6 +2956,7 @@ typedef enum { VIR_DOMAIN_LAUNCH_SECURITY_SEV, VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP, VIR_DOMAIN_LAUNCH_SECURITY_PV, + VIR_DOMAIN_LAUNCH_SECURITY_TDX, VIR_DOMAIN_LAUNCH_SECURITY_LAST, } virDomainLaunchSecurity; @@ -2990,11 +2991,21 @@ struct _virDomainSEVSNPDef { }; +struct _virDomainTDXDef { + bool havePolicy; + unsigned long long policy; + char *mrconfigid; + char *mrowner; + char *mrownerconfig; +}; + + struct _virDomainSecDef { virDomainLaunchSecurity sectype; union { virDomainSEVDef sev; virDomainSEVSNPDef sev_snp; + virDomainTDXDef tdx; } data; }; diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index d0d4bc0bf4..1902a0544a 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1934,6 +1934,7 @@ virDomainDefLaunchSecurityValidate(const virDomainDef *def) case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_SEV: case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: break; } diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 5597d5a66b..4581a7288d 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -549,6 +549,9 @@ <value>s390-pv</value> </attribute> </group> + <group> + <ref name="launchSecurityTDX"/> + </group> </choice> </element> </define> @@ -644,6 +647,35 @@ </optional> </interleave> </define> + + <define name="launchSecurityTDX"> + <attribute name="type"> + <value>tdx</value> + </attribute> + <interleave> + <optional> + <element name="policy"> + <ref name="hexuint"/> + </element> + </optional> + <optional> + <element name="mrConfigId"> + <data type="string"/> + </element> + </optional> + <optional> + <element name="mrOwner"> + <data type="string"/> + </element> + </optional> + <optional> + <element name="mrOwnerConfig"> + <data type="string"/> + </element> + </optional> + </interleave> + </define> + <!-- Enable or disable perf events for the domain. For each of the events the following rules apply: diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index c70437bc05..3f8b6a801a 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -220,6 +220,8 @@ typedef struct _virDomainSEVDef virDomainSEVDef; typedef struct _virDomainSEVSNPDef virDomainSEVSNPDef; +typedef struct _virDomainTDXDef virDomainTDXDef; + typedef struct _virDomainSecDef virDomainSecDef; typedef struct _virDomainShmemDef virDomainShmemDef; diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 48af467bf9..d6fa006a98 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -863,6 +863,7 @@ qemuSetupDevicesCgroup(virDomainObj *vm) return -1; break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index e6d308534f..7b788cdd30 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7040,6 +7040,7 @@ qemuBuildMachineCommandLine(virCommand *cmd, } break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: virBufferAddLit(&buf, ",confidential-guest-support=lsec0"); break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: @@ -9805,6 +9806,7 @@ qemuBuildSecCommandLine(virDomainObj *vm, virCommand *cmd, case VIR_DOMAIN_LAUNCH_SECURITY_PV: return qemuBuildPVCommandLine(cmd); + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: virReportEnumRangeError(virDomainLaunchSecurity, sec->sectype); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6ce949dd07..10e8700d08 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -19268,6 +19268,7 @@ qemuDomainGetLaunchSecurityInfo(virDomainPtr domain, goto cleanup; break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c index 2d0ec0b4fa..6c65a2751b 100644 --- a/src/qemu/qemu_firmware.c +++ b/src/qemu/qemu_firmware.c @@ -1371,6 +1371,7 @@ qemuFirmwareMatchDomain(const virDomainDef *def, } break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: diff --git a/src/qemu/qemu_namespace.c b/src/qemu/qemu_namespace.c index 59421ec9d1..f72da83929 100644 --- a/src/qemu/qemu_namespace.c +++ b/src/qemu/qemu_namespace.c @@ -665,6 +665,7 @@ qemuDomainSetupLaunchSecurity(virDomainObj *vm, VIR_DEBUG("Set up launch security for SEV"); break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 015a98d035..870b089024 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6999,6 +6999,7 @@ qemuProcessPrepareDomain(virQEMUDriver *driver, return -1; break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: @@ -7071,6 +7072,7 @@ qemuProcessPrepareLaunchSecurityGuestInput(virDomainObj *vm) case VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP: break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: return 0; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index b2c3c9e2f6..52d688fe34 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -1413,6 +1413,7 @@ qemuValidateDomainDef(const virDomainDef *def, return -1; } break; + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: virReportEnumRangeError(virDomainLaunchSecurity, def->sec->sectype); diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 3ecbc7277d..2f788b872a 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -2017,6 +2017,7 @@ virSecurityDACRestoreAllLabel(virSecurityManager *mgr, rc = -1; break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: @@ -2263,6 +2264,7 @@ virSecurityDACSetAllLabel(virSecurityManager *mgr, return -1; break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: -- 2.34.1

mrConfigId/mrOwner/mrOwnerConfig are base64 encoded SHA384 digest, can be provided for TDX attestation. Check their decoded lengths to ensure they are 48 bytes. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/conf/domain_validate.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 1902a0544a..ed4ce6d382 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1913,10 +1913,13 @@ virDomainDefValidateIOThreads(const virDomainDef *def) } \ } +#define SHA384_DIGEST_SIZE 48 + static int virDomainDefLaunchSecurityValidate(const virDomainDef *def) { virDomainSEVSNPDef *sev_snp; + virDomainTDXDef *tdx; if (!def->sec) return 0; @@ -1931,10 +1934,17 @@ virDomainDefLaunchSecurityValidate(const virDomainDef *def) CHECK_BASE64_LEN(sev_snp->host_data, "hostData", 32); break; + case VIR_DOMAIN_LAUNCH_SECURITY_TDX: + tdx = &def->sec->data.tdx; + + CHECK_BASE64_LEN(tdx->mrconfigid, "mrConfigId", SHA384_DIGEST_SIZE); + CHECK_BASE64_LEN(tdx->mrowner, "mrOwner", SHA384_DIGEST_SIZE); + CHECK_BASE64_LEN(tdx->mrownerconfig, "mrOwnerConfig", SHA384_DIGEST_SIZE); + break; + case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_SEV: case VIR_DOMAIN_LAUNCH_SECURITY_PV: - case VIR_DOMAIN_LAUNCH_SECURITY_TDX: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: break; } -- 2.34.1

QEMU will provides 'tdx-guest' object which is used to launch encrypted VMs on Intel platform using TDX feature. Command line looks like: $QEMU ... \ -object '{"qom-type":"tdx-guest","id":"lsec0","mrconfigid":"xxx","mrowner":"xxx","mrownerconfig":"xxx","attributes":268435457}' \ -machine pc-q35-6.0,confidential-guest-support=lsec0 Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/conf/domain_conf.h | 5 +++++ src/qemu/qemu_command.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_validate.c | 12 ++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index f838f39aee..030e6baab8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3000,6 +3000,11 @@ struct _virDomainTDXDef { }; +#define VIR_DOMAIN_TDX_POLICY_DEBUG 0x1 +#define VIR_DOMAIN_TDX_POLICY_SEPT_VE_DISABLE 0x10000000 +#define VIR_DOMAIN_TDX_POLICY_ALLOWED_MASK (VIR_DOMAIN_TDX_POLICY_DEBUG | \ + VIR_DOMAIN_TDX_POLICY_SEPT_VE_DISABLE) + struct _virDomainSecDef { virDomainLaunchSecurity sectype; union { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7b788cdd30..7d75a53345 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -9789,6 +9789,32 @@ qemuBuildPVCommandLine(virCommand *cmd) } +static int +qemuBuildTDXCommandLine(virCommand *cmd, virDomainTDXDef *tdx) +{ + g_autoptr(virJSONValue) props = NULL; + + if (tdx->havePolicy) + VIR_DEBUG("policy=0x%llx", tdx->policy); + + if (qemuMonitorCreateObjectProps(&props, "tdx-guest", "lsec0", + "S:mrconfigid", tdx->mrconfigid, + "S:mrowner", tdx->mrowner, + "S:mrownerconfig", tdx->mrownerconfig, + NULL) < 0) + return -1; + + if (tdx->havePolicy && + virJSONValueObjectAdd(&props, "U:attributes", tdx->policy, NULL) < 0) + return -1; + + if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0) + return -1; + + return 0; +} + + static int qemuBuildSecCommandLine(virDomainObj *vm, virCommand *cmd, virDomainSecDef *sec) @@ -9807,6 +9833,7 @@ qemuBuildSecCommandLine(virDomainObj *vm, virCommand *cmd, return qemuBuildPVCommandLine(cmd); case VIR_DOMAIN_LAUNCH_SECURITY_TDX: + return qemuBuildTDXCommandLine(cmd, &sec->data.tdx); case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: virReportEnumRangeError(virDomainLaunchSecurity, sec->sectype); diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 52d688fe34..42396d229e 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -1414,6 +1414,18 @@ qemuValidateDomainDef(const virDomainDef *def, } break; case VIR_DOMAIN_LAUNCH_SECURITY_TDX: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_TDX_GUEST)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Intel TDX launch security is not supported with this QEMU binary")); + return -1; + } + if (def->sec->data.tdx.havePolicy && + def->sec->data.tdx.policy & ~VIR_DOMAIN_TDX_POLICY_ALLOWED_MASK) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only bit0(debug) and bit28(sept-ve-disable) are supported intel TDX launch security policy")); + return -1; + } + break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: virReportEnumRangeError(virDomainLaunchSecurity, def->sec->sectype); -- 2.34.1

As the tdx launch security type support is added, expose it in domain capabilities so that domain definition validation check can take effect. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/qemu/qemu_capabilities.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 5b8afa3c41..3038bb616b 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6750,6 +6750,8 @@ virQEMUCapsFillDomainLaunchSecurity(virQEMUCaps *qemuCaps, if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_S390_PV_GUEST) && virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT)) VIR_DOMAIN_CAPS_ENUM_SET(launchSecurity->sectype, VIR_DOMAIN_LAUNCH_SECURITY_PV); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_TDX_GUEST)) + VIR_DOMAIN_CAPS_ENUM_SET(launchSecurity->sectype, VIR_DOMAIN_LAUNCH_SECURITY_TDX); if (launchSecurity->sectype.values == 0) { launchSecurity->supported = VIR_TRISTATE_BOOL_NO; -- 2.34.1

TDX guest requires some special parameters to boot, currently: "kernel_irqchip=split" "pmu!=on" "smm!=on" "-bios" If not specified explicitly, QEMU should configure this option implicitly when start a TDX guest. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- src/qemu/qemu_validate.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 42396d229e..6e8acdebcb 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -1425,6 +1425,38 @@ qemuValidateDomainDef(const virDomainDef *def, _("Only bit0(debug) and bit28(sept-ve-disable) are supported intel TDX launch security policy")); return -1; } + if (def->features[VIR_DOMAIN_FEATURE_IOAPIC] == VIR_DOMAIN_IOAPIC_KVM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Intel TDX launch security needs split kernel irqchip")); + return -1; + } + /* Current KVM doesn't support PMU for TD guest. It returns + * error if TD is created with PMU bit being set in attributes. + * By default, QEMU disable PMU for TD guest. + */ + if (def->features[VIR_DOMAIN_FEATURE_PMU] == VIR_TRISTATE_SWITCH_ON) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Intel TDX launch security is not supported with PMU enabled")); + return -1; + } + /* TDX doesn't support SMM and VMM cannot emulate SMM for TDX VMs + * because VMM cannot manipulate TDX VM's memory. + * By default, QEMU disable SMM for TD guest. + */ + if (def->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Intel TDX launch security is not supported with SMM enabled")); + return -1; + } + /* TDVF(OVMF) needs to run at private memory for TD guest. TDX cannot + * support pflash device since it doesn't support read-only private memory. + * Thus load TDVF(OVMF) with -bios option for TDs. + */ + if (def->os.loader && def->os.loader->type == VIR_DOMAIN_LOADER_TYPE_PFLASH) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Intel TDX launch security is not supported with pflash loader")); + return -1; + } break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: -- 2.34.1

Add element "quoteGenerationSocket" to tdx launch security type. It contains only an optional unix socket address attribute, when omitted, libvirt will use default QGS server address "/var/run/tdx-qgs/qgs.socket". UNIX sockets offer the required functionality with greater security than vsock, so libvirt only provides support for unix socket. XML example: <launchSecurity type='tdx'> <policy>0x0</policy> <mrConfigId>xxx</mrConfigId> <mrOwner>xxx</mrOwner> <mrOwnerConfig>xxx</mrOwnerConfig> <quoteGenerationSocket path="/var/run/tdx-qgs/qgs.socket"/> </launchSecurity> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/conf/domain_conf.c | 35 ++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 2 ++ src/conf/schemas/domaincommon.rng | 9 ++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 458852854b..f2bf980e5f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3954,6 +3954,7 @@ virDomainSecDefFree(virDomainSecDef *def) g_free(def->data.tdx.mrconfigid); g_free(def->data.tdx.mrowner); g_free(def->data.tdx.mrownerconfig); + g_free(def->data.tdx.qgs_unix_path); break; case VIR_DOMAIN_LAUNCH_SECURITY_PV: case VIR_DOMAIN_LAUNCH_SECURITY_NONE: @@ -14180,6 +14181,33 @@ virDomainSEVSNPDefParseXML(virDomainSEVSNPDef *def, } +static int +virDomainTDXQGSDefParseXML(virDomainTDXDef *def, xmlXPathContextPtr ctxt) +{ + g_autofree xmlNodePtr *nodes = NULL; + xmlNodePtr node; + int n; + + if ((n = virXPathNodeSet("./quoteGenerationSocket", ctxt, &nodes)) < 0) + return -1; + + if (!n) + return 0; + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only a single QGS element is supported")); + return -1; + } + node = nodes[0]; + + def->haveQGS = true; + def->qgs_unix_path = virXMLPropString(node, "path"); + + return 0; +} + + static int virDomainTDXDefParseXML(virDomainTDXDef *def, xmlXPathContextPtr ctxt) @@ -14199,7 +14227,7 @@ virDomainTDXDefParseXML(virDomainTDXDef *def, def->mrowner = virXPathString("string(./mrOwner)", ctxt); def->mrownerconfig = virXPathString("string(./mrOwnerConfig)", ctxt); - return 0; + return virDomainTDXQGSDefParseXML(def, ctxt); } @@ -27639,6 +27667,11 @@ virDomainTDXDefFormat(virBuffer *childBuf, virDomainTDXDef *def) virBufferEscapeString(childBuf, "<mrConfigId>%s</mrConfigId>\n", def->mrconfigid); virBufferEscapeString(childBuf, "<mrOwner>%s</mrOwner>\n", def->mrowner); virBufferEscapeString(childBuf, "<mrOwnerConfig>%s</mrOwnerConfig>\n", def->mrownerconfig); + if (def->haveQGS) { + virBufferAddLit(childBuf, "<quoteGenerationSocket"); + virBufferEscapeString(childBuf, " path='%s'", def->qgs_unix_path); + virBufferAddLit(childBuf, "/>\n"); + } } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 030e6baab8..93f8d0f960 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2997,6 +2997,8 @@ struct _virDomainTDXDef { char *mrconfigid; char *mrowner; char *mrownerconfig; + bool haveQGS; + char *qgs_unix_path; }; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 4581a7288d..00ae1deae0 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -673,6 +673,15 @@ <data type="string"/> </element> </optional> + <optional> + <element name="quoteGenerationSocket"> + <optional> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + </optional> + </element> + </optional> </interleave> </define> -- 2.34.1

'tdx-guest' object supports a "quote-generation-socket" property for attestation purpose. When "quote-generation-socket" is configured in guest xml, libvirt generates unix socket format cmdline for QEMU. 'Path' element can be omitted, default path "/var/run/tdx-qgs/qgs.socket" is used in this case. QEMU command line example: qemu-system-x86_64 \ -object '{"qom-type":"tdx-guest","id":"lsec0","mrconfigid":"xxx","mrowner":"xxx","mrownerconfig":"xxx","quote-generation-socket":{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"},"attributes":268435457}' \ -machine pc-q35-6.0,confidential-guest-support=lsec0 Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/conf/domain_conf.h | 3 +++ src/qemu/qemu_command.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 93f8d0f960..86b7da6f45 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2991,6 +2991,9 @@ struct _virDomainSEVSNPDef { }; +/* Copied from QGS source code */ +#define QGS_UNIX_SOCKET_FILE "/var/run/tdx-qgs/qgs.socket" + struct _virDomainTDXDef { bool havePolicy; unsigned long long policy; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7d75a53345..92e6779f18 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -9789,18 +9789,43 @@ qemuBuildPVCommandLine(virCommand *cmd) } +static virJSONValue * +qemuBuildTDXQGSCommandLine(virDomainTDXDef *tdx) +{ + g_autoptr(virJSONValue) addr = NULL; + const char *path; + + if (!tdx->haveQGS) + return NULL; + + path = tdx->qgs_unix_path ? : QGS_UNIX_SOCKET_FILE; + + if (virJSONValueObjectAdd(&addr, + "s:type", "unix", + "s:path", path, + NULL) < 0) + return NULL; + + return g_steal_pointer(&addr); +} + + static int qemuBuildTDXCommandLine(virCommand *cmd, virDomainTDXDef *tdx) { + g_autoptr(virJSONValue) addr = NULL; g_autoptr(virJSONValue) props = NULL; if (tdx->havePolicy) VIR_DEBUG("policy=0x%llx", tdx->policy); + addr = qemuBuildTDXQGSCommandLine(tdx); + if (qemuMonitorCreateObjectProps(&props, "tdx-guest", "lsec0", "S:mrconfigid", tdx->mrconfigid, "S:mrowner", tdx->mrowner, "S:mrownerconfig", tdx->mrownerconfig, + "A:quote-generation-socket", &addr, NULL) < 0) return -1; -- 2.34.1

Utilize the existing fake reboot mechanism to do reboot for TDX guest. Different from normal guest, TDX guest doesn't support system_reset, so have to kill the old guest and start a new one to simulate the reboot. Co-developed-by: Chenyi Qiang <chenyi.qiang@intel.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/qemu/qemu_process.c | 81 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 870b089024..cb9deecb22 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -446,6 +446,68 @@ qemuProcessHandleReset(qemuMonitor *mon G_GNUC_UNUSED, } +/* + * Secure guest doesn't support fake reboot via machine CPU reset. + * We thus fake reboot via QEMU re-creation. + */ +static void +qemuProcessFakeRebootViaRecreate(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv = vm->privateData; + virQEMUDriver *driver = priv->driver; + int ret = -1; + + VIR_DEBUG("Handle secure guest reboot: destroy phase"); + + virObjectLock(vm); + if (qemuProcessBeginStopJob(vm, VIR_JOB_DESTROY, 0) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) { + qemuProcessEndStopJob(vm); + goto cleanup; + } + + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED, + VIR_ASYNC_JOB_NONE, 0); + virDomainAuditStop(vm, "destroyed"); + + /* skip remove inactive domain from active list */ + qemuProcessEndStopJob(vm); + + VIR_DEBUG("Handle secure guest reboot: boot phase"); + + if (qemuProcessBeginJob(vm, VIR_DOMAIN_JOB_OPERATION_START, 0) < 0) { + qemuDomainRemoveInactive(driver, vm, 0, false); + goto cleanup; + } + + if (qemuProcessStart(NULL, driver, vm, NULL, VIR_ASYNC_JOB_START, + NULL, -1, NULL, NULL, NULL, + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, + 0) < 0) { + virDomainAuditStart(vm, "booted", false); + qemuDomainRemoveInactive(driver, vm, 0, false); + goto endjob; + } + + virDomainAuditStart(vm, "booted", true); + + qemuDomainSaveStatus(vm); + ret = 0; + + endjob: + qemuProcessEndJob(vm); + + cleanup: + priv->pausedShutdown = false; + qemuDomainSetFakeReboot(vm, false); + if (ret == -1) + ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_FORCE)); + virDomainObjEndAPI(&vm); +} + + /* * Since we have the '-no-shutdown' flag set, the * QEMU process will currently have guest OS shutdown @@ -455,15 +517,13 @@ qemuProcessHandleReset(qemuMonitor *mon G_GNUC_UNUSED, * guest OS booting up again */ static void -qemuProcessFakeReboot(void *opaque) +qemuProcessFakeRebootViaReset(virDomainObj *vm) { - virDomainObj *vm = opaque; qemuDomainObjPrivate *priv = vm->privateData; virQEMUDriver *driver = priv->driver; virDomainRunningReason reason = VIR_DOMAIN_RUNNING_BOOTED; int ret = -1, rc; - VIR_DEBUG("vm=%p", vm); virObjectLock(vm); if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) goto cleanup; @@ -509,6 +569,21 @@ qemuProcessFakeReboot(void *opaque) } +static void +qemuProcessFakeReboot(void *opaque) +{ + virDomainObj *vm = opaque; + + VIR_DEBUG("vm=%p", vm); + + if (vm->def->sec && + vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_TDX) + qemuProcessFakeRebootViaRecreate(vm); + else + qemuProcessFakeRebootViaReset(vm); +} + + void qemuProcessShutdownOrReboot(virDomainObj *vm) { -- 2.34.1

We can reboot a TDX guest with 'virsh reboot' or 'virsh shutdown' if action for onPoweroff is 'restart'. But running reboot command in guest shell will always lead to shutdown. This behavior is not consistent with normal guest, fix it by checking shutdown reason and action configuration to trigger FakeReboot. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- src/qemu/qemu_monitor.c | 18 +++++++++++++++++- src/qemu/qemu_monitor.h | 2 +- src/qemu/qemu_monitor_json.c | 6 +++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6340f437dd..3aa316980b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1060,10 +1060,26 @@ qemuMonitorEmitEvent(qemuMonitor *mon, const char *event, void -qemuMonitorEmitShutdown(qemuMonitor *mon, virTristateBool guest) +qemuMonitorEmitShutdown(qemuMonitor *mon, virTristateBool guest, + const char *reason) { + virDomainObj *vm = mon->vm; + VIR_DEBUG("mon=%p guest=%u", mon, guest); + /* This isn't best place to set FakeReboot but we need to access + * mon->vm which is defined in this file. Reboot command in guest + * will trigger SHUTDOWN event for TDX guest, so we has to deal + * with it here. */ + if (vm->def->sec && + vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_TDX) { + if ((STREQ_NULLABLE(reason, "guest-shutdown") && + vm->def->onPoweroff == VIR_DOMAIN_LIFECYCLE_ACTION_RESTART) || + (STREQ_NULLABLE(reason, "guest-reset") && + vm->def->onReboot == VIR_DOMAIN_LIFECYCLE_ACTION_RESTART)) + qemuDomainSetFakeReboot(vm, true); + } + QEMU_MONITOR_CALLBACK(mon, domainShutdown, mon->vm, guest); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index db451c448b..446b720077 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -445,7 +445,7 @@ int qemuMonitorUpdateVideoVram64Size(qemuMonitor *mon, void qemuMonitorEmitEvent(qemuMonitor *mon, const char *event, long long seconds, unsigned int micros, const char *details); -void qemuMonitorEmitShutdown(qemuMonitor *mon, virTristateBool guest); +void qemuMonitorEmitShutdown(qemuMonitor *mon, virTristateBool guest, const char *reason); void qemuMonitorEmitReset(qemuMonitor *mon); void qemuMonitorEmitStop(qemuMonitor *mon); void qemuMonitorEmitResume(qemuMonitor *mon); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 34ed42b004..01db89b5b3 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -546,12 +546,16 @@ qemuMonitorJSONMakeCommand(const char *cmdname, static void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data) { bool guest = false; + const char *reason = NULL; virTristateBool guest_initiated = VIR_TRISTATE_BOOL_ABSENT; if (data && virJSONValueObjectGetBoolean(data, "guest", &guest) == 0) guest_initiated = virTristateBoolFromBool(guest); - qemuMonitorEmitShutdown(mon, guest_initiated); + if (data) + reason = virJSONValueObjectGetString(data, "reason"); + + qemuMonitorEmitShutdown(mon, guest_initiated, reason); } static void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED) -- 2.34.1

For secure guest, FakeReboot kills original QEMU instance and create new one. During this process, QEMU send SHUTDOWN event with "host-signal" reason which can trigger another FakeReboot. Check if a FakeReboot is ongoing and bypass "host-signal" processing which originally comes from FakeReboot. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- src/qemu/qemu_monitor.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3aa316980b..838c102219 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1073,6 +1073,16 @@ qemuMonitorEmitShutdown(qemuMonitor *mon, virTristateBool guest, * with it here. */ if (vm->def->sec && vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_TDX) { + qemuDomainObjPrivate *priv = vm->privateData; + + /* For secure guest, FakeReboot kills original QEMU instance and + * create new one. During this process, QEMU send SHUTDOWN event + * with "host-signal" reason which can trigger another FakeReboot. + * Check if a FakeReboot is ongoing and bypass "host-signal" + * processing which is originally come from FakeReboot. */ + if (priv->fakeReboot && STREQ_NULLABLE(reason, "host-signal")) + return; + if ((STREQ_NULLABLE(reason, "guest-shutdown") && vm->def->onPoweroff == VIR_DOMAIN_LIFECYCLE_ACTION_RESTART) || (STREQ_NULLABLE(reason, "guest-reset") && -- 2.34.1

For secure guest, FakeReboot kills original QEMU instance and create new one which is quite different from normal guest. To reflect this fact, VIR_DOMAIN_EVENT_[STOPPED|STARTED] are sent to control plane with new introduced reasons VIR_DOMAIN_EVENT_[STOPPED|STARTED]_RECREATION. That would let control plane software understand that these events are from a fake reboot. Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- examples/c/misc/event-test.c | 6 ++++++ include/libvirt/libvirt-domain.h | 2 ++ src/qemu/qemu_process.c | 10 ++++++++++ tools/virsh-domain-event.c | 6 ++++-- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/c/misc/event-test.c b/examples/c/misc/event-test.c index a61dbf4529..bafa929c47 100644 --- a/examples/c/misc/event-test.c +++ b/examples/c/misc/event-test.c @@ -143,6 +143,9 @@ eventDetailToString(int event, case VIR_DOMAIN_EVENT_STARTED_WAKEUP: return "Event wakeup"; + case VIR_DOMAIN_EVENT_STARTED_RECREATION: + return "Recreation"; + case VIR_DOMAIN_EVENT_STARTED_LAST: break; } @@ -227,6 +230,9 @@ eventDetailToString(int event, case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT: return "Snapshot"; + case VIR_DOMAIN_EVENT_STOPPED_RECREATION: + return "Recreation"; + case VIR_DOMAIN_EVENT_STOPPED_LAST: break; } diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9496631bcc..1e78708d1c 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5053,6 +5053,7 @@ typedef enum { VIR_DOMAIN_EVENT_STARTED_RESTORED = 2, /* Restored from a state file (Since: 0.5.0) */ VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT = 3, /* Restored from snapshot (Since: 0.8.0) */ VIR_DOMAIN_EVENT_STARTED_WAKEUP = 4, /* Started due to wakeup event (Since: 0.9.11) */ + VIR_DOMAIN_EVENT_STARTED_RECREATION = 5, /* Secure guest recreation (Since: 11.3.0) */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_STARTED_LAST /* (Since: 0.9.10) */ @@ -5117,6 +5118,7 @@ typedef enum { VIR_DOMAIN_EVENT_STOPPED_SAVED = 4, /* Saved to a state file (Since: 0.5.0) */ VIR_DOMAIN_EVENT_STOPPED_FAILED = 5, /* Host emulator/mgmt failed (Since: 0.5.0) */ VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT = 6, /* offline snapshot loaded (Since: 0.8.0) */ + VIR_DOMAIN_EVENT_STOPPED_RECREATION = 7, /* Secure guest recreation (Since: 11.3.0) */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_STOPPED_LAST /* (Since: 0.9.10) */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index cb9deecb22..62cbc3a3f8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -455,6 +455,7 @@ qemuProcessFakeRebootViaRecreate(virDomainObj *vm) { qemuDomainObjPrivate *priv = vm->privateData; virQEMUDriver *driver = priv->driver; + virObjectEvent *event = NULL; int ret = -1; VIR_DEBUG("Handle secure guest reboot: destroy phase"); @@ -472,6 +473,11 @@ qemuProcessFakeRebootViaRecreate(virDomainObj *vm) VIR_ASYNC_JOB_NONE, 0); virDomainAuditStop(vm, "destroyed"); + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_RECREATION); + virObjectEventStateQueue(driver->domainEventState, event); + /* skip remove inactive domain from active list */ qemuProcessEndStopJob(vm); @@ -492,6 +498,10 @@ qemuProcessFakeRebootViaRecreate(virDomainObj *vm) } virDomainAuditStart(vm, "booted", true); + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_RECREATION); + virObjectEventStateQueue(driver->domainEventState, event); qemuDomainSaveStatus(vm); ret = 0; diff --git a/tools/virsh-domain-event.c b/tools/virsh-domain-event.c index 69a68d857d..9e36e75792 100644 --- a/tools/virsh-domain-event.c +++ b/tools/virsh-domain-event.c @@ -70,7 +70,8 @@ VIR_ENUM_IMPL(virshDomainEventStarted, N_("Migrated"), N_("Restored"), N_("Snapshot"), - N_("Event wakeup")); + N_("Event wakeup"), + N_("Recreation")); VIR_ENUM_DECL(virshDomainEventSuspended); VIR_ENUM_IMPL(virshDomainEventSuspended, @@ -103,7 +104,8 @@ VIR_ENUM_IMPL(virshDomainEventStopped, N_("Migrated"), N_("Saved"), N_("Failed"), - N_("Snapshot")); + N_("Snapshot"), + N_("Recreation")); VIR_ENUM_DECL(virshDomainEventShutdown); VIR_ENUM_IMPL(virshDomainEventShutdown, -- 2.34.1

When TD VM reboot, qemu process is recreated by destroying old and creating new one. When new qemu process starts, it sends a RESUME event while libvirt domain isn't in run state yet. Then event VIR_DOMAIN_EVENT_RESUMED is sent out and confuse control plane. Check priv->pausedShutdown and bypass the event for TD VM, for normal VM, domain is in run state and already bypassing it. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/qemu/qemu_process.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 62cbc3a3f8..67b4679745 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -813,9 +813,11 @@ qemuProcessHandleResume(qemuMonitor *mon G_GNUC_UNUSED, reason = VIR_DOMAIN_RUNNING_POSTCOPY; } virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason); - event = virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_RESUMED, - eventDetail); + + if (!priv->pausedShutdown) + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_RESUMED, + eventDetail); qemuDomainSaveStatus(vm); } -- 2.34.1

TDX guest doesn't support system_reset, so have to kill the old guest and start a new one to simulate the reset. This can be achieved by calling qemuProcessFakeRebootViaRecreate(). Domain lock is already hold in qemuDomainReset() before calling qemuProcessFakeRebootViaRecreate(), so bypass locking in it. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- src/qemu/qemu_driver.c | 6 ++++++ src/qemu/qemu_process.c | 14 +++++++++----- src/qemu/qemu_process.h | 2 ++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 10e8700d08..67fbf8bf80 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1976,6 +1976,12 @@ qemuDomainReset(virDomainPtr dom, unsigned int flags) if (virDomainResetEnsureACL(dom->conn, vm->def) < 0) goto cleanup; + if (vm->def->sec && + vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_TDX) { + ret = qemuProcessFakeRebootViaRecreate(vm, true); + goto cleanup; + } + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) goto cleanup; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 67b4679745..3df6049b6a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -450,8 +450,8 @@ qemuProcessHandleReset(qemuMonitor *mon G_GNUC_UNUSED, * Secure guest doesn't support fake reboot via machine CPU reset. * We thus fake reboot via QEMU re-creation. */ -static void -qemuProcessFakeRebootViaRecreate(virDomainObj *vm) +int +qemuProcessFakeRebootViaRecreate(virDomainObj *vm, bool locked) { qemuDomainObjPrivate *priv = vm->privateData; virQEMUDriver *driver = priv->driver; @@ -460,7 +460,9 @@ qemuProcessFakeRebootViaRecreate(virDomainObj *vm) VIR_DEBUG("Handle secure guest reboot: destroy phase"); - virObjectLock(vm); + if (!locked) + virObjectLock(vm); + if (qemuProcessBeginStopJob(vm, VIR_JOB_DESTROY, 0) < 0) goto cleanup; @@ -514,7 +516,9 @@ qemuProcessFakeRebootViaRecreate(virDomainObj *vm) qemuDomainSetFakeReboot(vm, false); if (ret == -1) ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_FORCE)); - virDomainObjEndAPI(&vm); + if (!locked) + virDomainObjEndAPI(&vm); + return ret; } @@ -588,7 +592,7 @@ qemuProcessFakeReboot(void *opaque) if (vm->def->sec && vm->def->sec->sectype == VIR_DOMAIN_LAUNCH_SECURITY_TDX) - qemuProcessFakeRebootViaRecreate(vm); + ignore_value(qemuProcessFakeRebootViaRecreate(vm, false)); else qemuProcessFakeRebootViaReset(vm); } diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 8f7b3f24c4..37f4ff2d53 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -191,6 +191,8 @@ typedef enum { int qemuProcessKill(virDomainObj *vm, unsigned int flags); +int qemuProcessFakeRebootViaRecreate(virDomainObj *vm, bool locked); + void qemuProcessShutdownOrReboot(virDomainObj *vm); void qemuProcessAutoDestroy(virDomainObj *dom, -- 2.34.1

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- docs/formatdomain.rst | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 41fccfeb84..ff8b1ba4cb 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -9485,6 +9485,69 @@ The ``<launchSecurity/>`` element then accepts the following child elements: the SNP_LAUNCH_FINISH command in the SEV-SNP firmware ABI. +The contents of the ``<launchSecurity type='tdx'>`` element is used to provide +the guest owners input used for creating an encrypted VM using the Intel TDX +(Trusted Domain eXtensions). Intel TDX refers to an Intel technology that +extends Virtual Machine Extensions (VMX) and Multi-Key Total Memory Encryption +(MKTME) with a new kind of virtual machine guest called a Trust Domain (TD). +A TD runs in a CPU mode that is designed to protect the confidentiality of its +memory contents and its CPU state from any other software, including the hosting +Virtual Machine Monitor (VMM), unless explicitly shared by the TD itself. +Example configuration: + +:: + + <domain> + ... + <launchSecurity type='tdx'> + <policy>0x10000001</policy> + <mrConfigId>xxx</mrConfigId> + <mrOwner>xxx</mrOwner> + <mrOwnerConfig>xxx</mrOwnerConfig> + <quoteGenerationSocket path="/var/run/tdx-qgs/qgs.socket"/> + </launchSecurity> + ... + </domain> + +``policy`` + The optional ``policy`` element provides the guest TD attributes which is + passed by the host VMM as a guest TD initialization parameter as part of + TD_PARAMS, it exactly matches the definition of TD_PARAMS.ATTRIBUTES in + (Intel TDX Module Spec Table 22.2: ATTRIBUTES Definition). It is reported + to the guest TD by TDG.VP.INFO and as part of TDREPORT_STRUCT returned by + TDG.MR.REPORT. The guest policy is 64bit unsigned with the fields shown + in Table: + + ====== ==================================================================================== + Bit(s) Description + ====== ==================================================================================== + 0 Guest TD runs in off-TD debug mode when set + 1:27 reserved + 28 Disable EPT violation conversion to #VE on guest TD access of PENDING pages when set + 29:63 reserved + ====== ==================================================================================== + +``mrConfigId`` + The optional ``mrConfigId`` element provides ID for non-owner-defined + configuration of the guest TD, e.g., run-time or OS configuration + (base64 encoded SHA384 digest). + +``@mrowner`` + The optional ``@mrowner`` element provides ID for the guest TD’s owner + (base64 encoded SHA384 digest). + +``mrownerconfig`` + The optional ``mrownerconfig`` element provides ID for owner-defined + configuration of the guest TD, e.g., specific to the workload rather than + the run-time or OS (base64 encoded SHA384 digest). + +``quoteGenerationSocket`` + The optional ``quoteGenerationSocket`` subelement provides Quote Generation + Service(QGS) daemon socket address configuration. It includes an optional + ``path`` attribute to determine the UNIX socket address, when omitted, + ``/var/run/tdx-qgs/qgs.socket`` is used as default. User in TD guest cannot + get TD quoting for attestation if this subelement is not provided. + Example configs =============== -- 2.34.1

On Thu, Apr 03, 2025 at 18:28:23 +0800, Zhenzhong Duan wrote: [...]
Zhenzhong Duan (18): tools: Secure guest check for Intel in virt-host-validate qemu: Check if INTEL Trust Domain Extention support is enabled qemu: Add TDX capability conf: Expose TDX feature in domain capabilities conf: Add tdx as launch security type conf: Validate TDX launchSecurity element mrConfigId/mrOwner/mrOwnerConfig qemu: Add command line and validation for TDX type conf: Expose TDX type in domain launch security capability qemu: Force special parameters enabled for TDX guest conf: Add Intel TDX Quote Generation Service(QGS) support qemu: Add command line for TDX Quote Generation Service(QGS)
[1]
qemu: Add FakeReboot support for TDX guest qemu: Support reboot command in guest qemu: Avoid duplicate FakeReboot for secure guest qemu: Send event VIR_DOMAIN_EVENT_[STOPPED|STARTED] during recreation qemu: Bypass sending VIR_DOMAIN_EVENT_RESUMED event when TD VM reboot qemu: Support domain reset command for TDX guest docs: domain: Add documentation for Intel TDX guest
docs/formatdomain.rst | 63 ++++++++++++++++++ docs/formatdomaincaps.rst | 1 + examples/c/misc/event-test.c | 6 ++ include/libvirt/libvirt-domain.h | 2 + src/conf/domain_capabilities.c | 1 + src/conf/domain_capabilities.h | 1 + src/conf/domain_conf.c | 82 +++++++++++++++++++++++ src/conf/domain_conf.h | 21 ++++++ src/conf/domain_validate.c | 11 ++++ src/conf/schemas/domaincaps.rng | 9 +++ src/conf/schemas/domaincommon.rng | 41 ++++++++++++ src/conf/virconftypes.h | 2 + src/qemu/qemu_capabilities.c | 38 ++++++++++- src/qemu/qemu_capabilities.h | 1 +
I'm seeing a capability being added but it's not detected anywhere.
src/qemu/qemu_cgroup.c | 1 + src/qemu/qemu_command.c | 54 +++++++++++++++ src/qemu/qemu_driver.c | 7 ++ src/qemu/qemu_firmware.c | 1 + src/qemu/qemu_monitor.c | 28 +++++++- src/qemu/qemu_monitor.h | 2 +- src/qemu/qemu_monitor_json.c | 6 +- src/qemu/qemu_namespace.c | 1 + src/qemu/qemu_process.c | 105 ++++++++++++++++++++++++++++-- src/qemu/qemu_process.h | 2 + src/qemu/qemu_validate.c | 45 +++++++++++++ src/security/security_dac.c | 2 + tools/virsh-domain-event.c | 6 +- tools/virt-host-validate-common.c | 31 ++++++++- tools/virt-host-validate-common.h | 1 + 29 files changed, 558 insertions(+), 13 deletions(-)
Also there are no qemuxmlconftest cases to be seen, while there are commandline changes [1]. IIUC the qemu patches are not merged yet, but See commits 0e58c04fc98c93482ce63589bf2b3042e7b5dd6c and 17945b8ec979fcc93232d55d3111cfc363e3cacc on how to add a variant of capability test data and how the caps dump looks. See also tests/qemucapabilitiesdata/README.rst You then can add qemuxmlconftest test cases based on the capability data: 8a852c3a909f0d11a61e1e3cd3bae89937e3a07c Note that if you create the capability dump based on a in-development qemu version (which is acceptable after the patches are merged upstream) you will be expected to update the capability dump on the same hardware once the qemu version becomes released: b0527a8f8e00f30911b6ffc5ac93d9d9bba6bff0

-----Original Message----- From: Peter Krempa <pkrempa@redhat.com> Subject: Re: [PATCH v1 00/18] LIBVIRT: X86: TDX support
On Thu, Apr 03, 2025 at 18:28:23 +0800, Zhenzhong Duan wrote:
[...]
Zhenzhong Duan (18): tools: Secure guest check for Intel in virt-host-validate qemu: Check if INTEL Trust Domain Extention support is enabled qemu: Add TDX capability conf: Expose TDX feature in domain capabilities conf: Add tdx as launch security type conf: Validate TDX launchSecurity element mrConfigId/mrOwner/mrOwnerConfig qemu: Add command line and validation for TDX type conf: Expose TDX type in domain launch security capability qemu: Force special parameters enabled for TDX guest conf: Add Intel TDX Quote Generation Service(QGS) support qemu: Add command line for TDX Quote Generation Service(QGS)
[1]
qemu: Add FakeReboot support for TDX guest qemu: Support reboot command in guest qemu: Avoid duplicate FakeReboot for secure guest qemu: Send event VIR_DOMAIN_EVENT_[STOPPED|STARTED] during recreation qemu: Bypass sending VIR_DOMAIN_EVENT_RESUMED event when TD VM reboot qemu: Support domain reset command for TDX guest docs: domain: Add documentation for Intel TDX guest
docs/formatdomain.rst | 63 ++++++++++++++++++ docs/formatdomaincaps.rst | 1 + examples/c/misc/event-test.c | 6 ++ include/libvirt/libvirt-domain.h | 2 + src/conf/domain_capabilities.c | 1 + src/conf/domain_capabilities.h | 1 + src/conf/domain_conf.c | 82 +++++++++++++++++++++++ src/conf/domain_conf.h | 21 ++++++ src/conf/domain_validate.c | 11 ++++ src/conf/schemas/domaincaps.rng | 9 +++ src/conf/schemas/domaincommon.rng | 41 ++++++++++++ src/conf/virconftypes.h | 2 + src/qemu/qemu_capabilities.c | 38 ++++++++++- src/qemu/qemu_capabilities.h | 1 +
I'm seeing a capability being added but it's not detected anywhere.
src/qemu/qemu_cgroup.c | 1 + src/qemu/qemu_command.c | 54 +++++++++++++++ src/qemu/qemu_driver.c | 7 ++ src/qemu/qemu_firmware.c | 1 + src/qemu/qemu_monitor.c | 28 +++++++- src/qemu/qemu_monitor.h | 2 +- src/qemu/qemu_monitor_json.c | 6 +- src/qemu/qemu_namespace.c | 1 + src/qemu/qemu_process.c | 105 ++++++++++++++++++++++++++++-- src/qemu/qemu_process.h | 2 + src/qemu/qemu_validate.c | 45 +++++++++++++ src/security/security_dac.c | 2 + tools/virsh-domain-event.c | 6 +- tools/virt-host-validate-common.c | 31 ++++++++- tools/virt-host-validate-common.h | 1 + 29 files changed, 558 insertions(+), 13 deletions(-)
Also there are no qemuxmlconftest cases to be seen, while there are commandline changes [1].
IIUC the qemu patches are not merged yet, but
See commits 0e58c04fc98c93482ce63589bf2b3042e7b5dd6c and 17945b8ec979fcc93232d55d3111cfc363e3cacc
on how to add a variant of capability test data and how the caps dump looks. See also tests/qemucapabilitiesdata/README.rst
You then can add qemuxmlconftest test cases based on the capability data:
8a852c3a909f0d11a61e1e3cd3bae89937e3a07c
Note that if you create the capability dump based on a in-development qemu version (which is acceptable after the patches are merged upstream) you will be expected to update the capability dump on the same hardware once the qemu version becomes released:
b0527a8f8e00f30911b6ffc5ac93d9d9bba6bff0
Thanks for your guidance, I have added test in v2 candidate, see https://github.com/intel/libvirt-tdx/commits/tdx_for_upstream_v2.wip But I'd like to collect more comments before send v2. Thanks Zhenzhong
participants (3)
-
Duan, Zhenzhong
-
Peter Krempa
-
Zhenzhong Duan