On Thu, Nov 20, 2025 at 02:17:47PM +0100, Peter Krempa wrote:
> On Thu, Nov 20, 2025 at 11:57:53 +0000, Daniel P. Berrangé via Devel wrote:
> > From: Daniel P. Berrangé <[email protected]>
> >
> > 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é <[email protected]>
> > ---
> > src/qemu/qemu_capabilities.c | 60 ++++++++++++++++++++++++++++++++++++
> > src/qemu/qemu_capabilities.h | 3 ++
> > tests/domaincapsmock.c | 6 ++++
> > 3 files changed, 69 insertions(+)
> >
> > diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
> > index 205bf3d0b8..67fe5d7acf 100644
> > --- a/src/qemu/qemu_capabilities.c
> > +++ b/src/qemu/qemu_capabilities.c
> > @@ -54,11 +54,16 @@
> > # include <sys/types.h>
> > # include <sys/sysctl.h>
> > #endif
> > +#ifdef __linux__
> > +# include <linux/kvm.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
> > @@ -3655,6 +3660,59 @@ virQEMUCapsProbeQMPSEVCapabilities(virQEMUCaps
> > *qemuCaps,
> > }
> >
> >
> > +int
> > +virQEMUCapsKVMSupportsVMTypeTDX(void)
> > +{
> > +#if defined(KVM_CAP_VM_TYPES) && defined(KVM_X86_TDX_VM)
> > + int kvmfd = -1;
>
> Preferrably use VIR_AUTOCLOSE here if anyone ever extends this function.
ok
> > + int types;
> > +
> > + if (!virFileExists(KVM_DEVICE))
> > + return 0;
> > +
> > + if ((kvmfd = open(KVM_DEVICE, O_RDONLY)) < 0) {
> > + VIR_DEBUG("Unable to open %s, cannot check TDX", KVM_DEVICE);
> > + return 0;
> > + }
> > +
> > + types = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_VM_TYPES);
> > +
> > + VIR_FORCE_CLOSE(kvmfd);
> > + VIR_DEBUG("KVM VM types: 0x%x", types);
> > +
> > + return !!(types & (1 << KVM_X86_TDX_VM));
>
> I didn't bother looking at what the IOCTL returns but I'd expect a < 0
> check here as bit-checking a negative value could be 'fun'.
KVM_CHECK_EXTENSION will return 0 if any capability
does not exist, so this should be safe in old KVM, but
for safety, lets squash -1 to be 0 explicitly.
> So looking at all branches this only returns 0 or 1 ...
Oh yes, I removed the -1 branch from failure to open
/dev/kvm as I decided that shouldn't be allowed to
break caps probing.
>
> > +#else
> > + VIR_DEBUG("KVM not compiled");
> > + return 0;
> > +#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 int
> > +virQEMUCapsProbeTDXCapabilities(virQEMUCaps *qemuCaps)
> > +{
> > + int rc;
> > +
> > + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_TDX_GUEST))
> > + return 0;
> > +
> > + if ((rc = virQEMUCapsKVMSupportsVMTypeTDX()) < 0)
> > + return -1;
>
> So this branch is dead code. All other branches return 0 so this
> function can be turned into a void function.
Yep.
>
> > +
> > + if (rc == 0) {
> > + virQEMUCapsClear(qemuCaps, QEMU_CAPS_TDX_GUEST);
> > + return 0;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +
> > static int
> > virQEMUCapsProbeQMPSGXCapabilities(virQEMUCaps *qemuCaps,
> > qemuMonitor *mon)
> > @@ -5837,6 +5895,8 @@ virQEMUCapsInitQMPMonitor(virQEMUCaps *qemuCaps,
> > return -1;
> > if (virQEMUCapsProbeQMPSGXCapabilities(qemuCaps, mon) < 0)
> > return -1;
> > + if (virQEMUCapsProbeTDXCapabilities(qemuCaps) < 0)
> > + return -1;
> >
> > virQEMUCapsInitProcessCaps(qemuCaps);
> >
> > diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
> > index efbef2acef..64e5c4ff55 100644
> > --- a/src/qemu/qemu_capabilities.h
> > +++ b/src/qemu/qemu_capabilities.h
> > @@ -979,3 +979,6 @@ int
> > virQEMUCapsProbeQMPMachineTypes(virQEMUCaps *qemuCaps,
> > virDomainVirtType virtType,
> > qemuMonitor *mon);
> > +
> > +int
> > +virQEMUCapsKVMSupportsVMTypeTDX(void) ATTRIBUTE_MOCKABLE;
> > diff --git a/tests/domaincapsmock.c b/tests/domaincapsmock.c
> > index cb6e98dbb8..e882c01260 100644
> > --- a/tests/domaincapsmock.c
> > +++ b/tests/domaincapsmock.c
> > @@ -48,6 +48,12 @@ virHostCPUGetPhysAddrSize(const virArch hostArch,
> > }
> >
> > #if WITH_QEMU
> > +int
> > +virQEMUCapsKVMSupportsVMTypeTDX(void)
> > +{
> > + return 1;
> > +}
> > +
> > static bool (*real_virQEMUCapsGetKVMSupportsSecureGuest)(virQEMUCaps
> > *qemuCaps);
> >
> > bool
> > --
> > 2.51.1
> >
>
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 :|