[PATCH v2 1/3] qemu: correctly detect working TDX support

Daniel P. Berrangé via Devel posted 3 patches 2 weeks, 4 days ago
[PATCH v2 1/3] qemu: correctly detect working TDX support
Posted by Daniel P. Berrangé via Devel 2 weeks, 4 days ago
From: Daniel P. Berrangé <berrange@redhat.com>

Querying existence of the 'tdx-guest' type merely tells us whether
QEMU has been compiled with TDX support, not whether it is usable
on the host. Thus QEMU was incorrectly reporting

    <tdx supported='yes'/>
    ...
    <launchSecurity supported='yes'>
      <enum name='sectype'>
        <value>tdx</value>
      </enum>
    </launchSecurity>

on every platform with new enough QEMU.

Unfortunately an earlier patch for a 'query-tdx-capabilities' QMP
command in QEMU was dropped, so there is no way to ask QEMU whether
it can launch a TDX guest. Libvirt must directly query the KVM
device and ask for supported VM types.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 src/qemu/qemu_capabilities.c | 51 ++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_capabilities.h |  3 +++
 tests/domaincapsmock.c       |  6 +++++
 3 files changed, 60 insertions(+)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 4050d5ccc6..9196ea1bb6 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -54,11 +54,17 @@
 # include <sys/types.h>
 # include <sys/sysctl.h>
 #endif
+#ifdef __linux__
+# include <linux/kvm.h>
+# include <sys/ioctl.h>
+#endif
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
 VIR_LOG_INIT("qemu.qemu_capabilities");
 
+#define KVM_DEVICE "/dev/kvm"
+
 /* While not public, these strings must not change. They
  * are used in domain status files which are read on
  * daemon restarts
@@ -3686,6 +3692,50 @@ virQEMUCapsProbeQMPSEVCapabilities(virQEMUCaps *qemuCaps,
 }
 
 
+bool
+virQEMUCapsKVMSupportsVMTypeTDX(void)
+{
+#if defined(KVM_CAP_VM_TYPES) && defined(KVM_X86_TDX_VM)
+    VIR_AUTOCLOSE kvmfd = -1;
+    int types;
+
+    if (!virFileExists(KVM_DEVICE))
+        return false;
+
+    if ((kvmfd = open(KVM_DEVICE, O_RDONLY)) < 0) {
+        VIR_DEBUG("Unable to open %s, cannot check TDX", KVM_DEVICE);
+        return false;
+    }
+
+    if ((types = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_VM_TYPES)) < 0)
+        types = false;
+
+    VIR_DEBUG("KVM VM types: 0x%x", types);
+
+    return !!(types & (1 << KVM_X86_TDX_VM));
+#else
+    VIR_DEBUG("KVM not compiled");
+    return false;
+#endif
+}
+
+
+/* This ought to be virQEMUCapsProbeQMPTDXCapabilities,
+ * but there is no 'query-tdx-capabilities' command
+ * available in QEMU currently. If one arrives, rename
+ * this method & switch to using that on new enough QEMU
+ */
+static void
+virQEMUCapsProbeTDXCapabilities(virQEMUCaps *qemuCaps)
+{
+    if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_TDX_GUEST))
+        return;
+
+    if (!virQEMUCapsKVMSupportsVMTypeTDX())
+        virQEMUCapsClear(qemuCaps, QEMU_CAPS_TDX_GUEST);
+}
+
+
 static int
 virQEMUCapsProbeQMPSGXCapabilities(virQEMUCaps *qemuCaps,
                                    qemuMonitor *mon)
@@ -5910,6 +5960,7 @@ virQEMUCapsInitQMPMonitor(virQEMUCaps *qemuCaps,
         return -1;
     if (virQEMUCapsProbeQMPSGXCapabilities(qemuCaps, mon) < 0)
         return -1;
+    virQEMUCapsProbeTDXCapabilities(qemuCaps);
 
     virQEMUCapsInitProcessCaps(qemuCaps);
 
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 2b519be3a8..f180844e66 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -980,3 +980,6 @@ int
 virQEMUCapsProbeQMPMachineTypes(virQEMUCaps *qemuCaps,
                                 virDomainVirtType virtType,
                                 qemuMonitor *mon);
+
+bool
+virQEMUCapsKVMSupportsVMTypeTDX(void) ATTRIBUTE_MOCKABLE;
diff --git a/tests/domaincapsmock.c b/tests/domaincapsmock.c
index cb6e98dbb8..7bece6c8c1 100644
--- a/tests/domaincapsmock.c
+++ b/tests/domaincapsmock.c
@@ -48,6 +48,12 @@ virHostCPUGetPhysAddrSize(const virArch hostArch,
 }
 
 #if WITH_QEMU
+bool
+virQEMUCapsKVMSupportsVMTypeTDX(void)
+{
+    return true;
+}
+
 static bool (*real_virQEMUCapsGetKVMSupportsSecureGuest)(virQEMUCaps *qemuCaps);
 
 bool
-- 
2.51.1

Re: [PATCH v2 1/3] qemu: correctly detect working TDX support
Posted by Peter Krempa via Devel 2 weeks, 4 days ago
On Mon, Nov 24, 2025 at 12:15:06 +0000, Daniel P. Berrangé via Devel wrote:
> From: Daniel P. Berrangé <berrange@redhat.com>
> 
> Querying existence of the 'tdx-guest' type merely tells us whether
> QEMU has been compiled with TDX support, not whether it is usable
> on the host. Thus QEMU was incorrectly reporting
> 
>     <tdx supported='yes'/>
>     ...
>     <launchSecurity supported='yes'>
>       <enum name='sectype'>
>         <value>tdx</value>
>       </enum>
>     </launchSecurity>
> 
> on every platform with new enough QEMU.
> 
> Unfortunately an earlier patch for a 'query-tdx-capabilities' QMP
> command in QEMU was dropped, so there is no way to ask QEMU whether
> it can launch a TDX guest. Libvirt must directly query the KVM
> device and ask for supported VM types.
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  src/qemu/qemu_capabilities.c | 51 ++++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_capabilities.h |  3 +++
>  tests/domaincapsmock.c       |  6 +++++
>  3 files changed, 60 insertions(+)

[...]


> @@ -3686,6 +3692,50 @@ virQEMUCapsProbeQMPSEVCapabilities(virQEMUCaps *qemuCaps,
>  }
>  
>  
> +bool
> +virQEMUCapsKVMSupportsVMTypeTDX(void)
> +{
> +#if defined(KVM_CAP_VM_TYPES) && defined(KVM_X86_TDX_VM)
> +    VIR_AUTOCLOSE kvmfd = -1;
> +    int types;
> +
> +    if (!virFileExists(KVM_DEVICE))
> +        return false;
> +
> +    if ((kvmfd = open(KVM_DEVICE, O_RDONLY)) < 0) {
> +        VIR_DEBUG("Unable to open %s, cannot check TDX", KVM_DEVICE);
> +        return false;
> +    }
> +
> +    if ((types = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_VM_TYPES)) < 0)
> +        types = false;

Either 'types = 0' or 'return false;'


> +
> +    VIR_DEBUG("KVM VM types: 0x%x", types);
> +
> +    return !!(types & (1 << KVM_X86_TDX_VM));

Is there possibility that the answer could change based on some external
input where libvirt's cache isn't invalidated?

> +#else
> +    VIR_DEBUG("KVM not compiled");
> +    return false;
> +#endif
> +}

Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Re: [PATCH v2 1/3] qemu: correctly detect working TDX support
Posted by Daniel P. Berrangé via Devel 2 weeks, 4 days ago
On Mon, Nov 24, 2025 at 01:47:08PM +0100, Peter Krempa wrote:
> On Mon, Nov 24, 2025 at 12:15:06 +0000, Daniel P. Berrangé via Devel wrote:
> > From: Daniel P. Berrangé <berrange@redhat.com>
> > 
> > Querying existence of the 'tdx-guest' type merely tells us whether
> > QEMU has been compiled with TDX support, not whether it is usable
> > on the host. Thus QEMU was incorrectly reporting
> > 
> >     <tdx supported='yes'/>
> >     ...
> >     <launchSecurity supported='yes'>
> >       <enum name='sectype'>
> >         <value>tdx</value>
> >       </enum>
> >     </launchSecurity>
> > 
> > on every platform with new enough QEMU.
> > 
> > Unfortunately an earlier patch for a 'query-tdx-capabilities' QMP
> > command in QEMU was dropped, so there is no way to ask QEMU whether
> > it can launch a TDX guest. Libvirt must directly query the KVM
> > device and ask for supported VM types.
> > 
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> > ---
> >  src/qemu/qemu_capabilities.c | 51 ++++++++++++++++++++++++++++++++++++
> >  src/qemu/qemu_capabilities.h |  3 +++
> >  tests/domaincapsmock.c       |  6 +++++
> >  3 files changed, 60 insertions(+)
> 
> [...]
> 
> 
> > @@ -3686,6 +3692,50 @@ virQEMUCapsProbeQMPSEVCapabilities(virQEMUCaps *qemuCaps,
> >  }
> >  
> >  
> > +bool
> > +virQEMUCapsKVMSupportsVMTypeTDX(void)
> > +{
> > +#if defined(KVM_CAP_VM_TYPES) && defined(KVM_X86_TDX_VM)
> > +    VIR_AUTOCLOSE kvmfd = -1;
> > +    int types;
> > +
> > +    if (!virFileExists(KVM_DEVICE))
> > +        return false;
> > +
> > +    if ((kvmfd = open(KVM_DEVICE, O_RDONLY)) < 0) {
> > +        VIR_DEBUG("Unable to open %s, cannot check TDX", KVM_DEVICE);
> > +        return false;
> > +    }
> > +
> > +    if ((types = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_VM_TYPES)) < 0)
> > +        types = false;
> 
> Either 'types = 0' or 'return false;'

Opps, yes, it should be 'types=0', as I wanted the following
VIR_DEBUG line to remain visible.

> > +
> > +    VIR_DEBUG("KVM VM types: 0x%x", types);
> > +
> > +    return !!(types & (1 << KVM_X86_TDX_VM));
> 
> Is there possibility that the answer could change based on some external
> input where libvirt's cache isn't invalidated?

We already have logic to invalidate the cache when the kernel version
changes, as we have other areas where we rely on /dev/kvm behaviour.
So that existing logic should be sufficient for this.

> 
> > +#else
> > +    VIR_DEBUG("KVM not compiled");
> > +    return false;
> > +#endif
> > +}
> 
> Reviewed-by: Peter Krempa <pkrempa@redhat.com>
> 

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|