Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Xiaoyao Li <[email protected]>
| Newsgroups | dev.linux.lists.linux-coco,org.kernel.vger.kvm,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest |
|---|---|
| Message-ID | <[email protected]> |
On 7/23/2026 7:13 AM, Lisa Wang wrote: > From: Ackerley Tng <[email protected]> > > Implement TDX ucall using TDCALL-based MMIO to pass the ucall address > from the VM to the host. > > In standard KVM selftests, ucall uses a PIO instruction as a trigger > to exit to the host, which then retrieves the ucall address by reading > the guest's RDI register. This approach is incompatible with TDX > because the host cannot access guest registers. > > Furthermore, PIO exits only expose 4 bytes of immediate data, which > is insufficient for a 8-byte ucall address. By using TDCALL-based MMIO, > the VM can share the full 8-byte address in a single exit without > refactoring the common ucall framework and other non-x86 architectures. > > Signed-off-by: Ackerley Tng <[email protected]> > Co-developed-by: Sagi Shahar <[email protected]> > Signed-off-by: Sagi Shahar <[email protected]> > Co-developed-by: Lisa Wang <[email protected]> > Signed-off-by: Lisa Wang <[email protected]> > --- > tools/testing/selftests/kvm/include/x86/ucall.h | 6 ----- > tools/testing/selftests/kvm/lib/x86/ucall.c | 33 +++++++++++++++++++++++++ > 2 files changed, 33 insertions(+), 6 deletions(-) > > diff --git a/tools/testing/selftests/kvm/include/x86/ucall.h b/tools/testing/selftests/kvm/include/x86/ucall.h > index 0e4950041e3e..7e54ec2c1a45 100644 > --- a/tools/testing/selftests/kvm/include/x86/ucall.h > +++ b/tools/testing/selftests/kvm/include/x86/ucall.h > @@ -2,12 +2,6 @@ > #ifndef SELFTEST_KVM_UCALL_H > #define SELFTEST_KVM_UCALL_H > > -#include "kvm_util.h" > - > #define UCALL_EXIT_REASON KVM_EXIT_IO This seems to leave the potential issue for future since x86 now supports different EXIT_REASON for UCALL. > -static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) > -{ > -} > - > #endif > diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c > index e7dd5791959b..7a954b2d0bdd 100644 > --- a/tools/testing/selftests/kvm/lib/x86/ucall.c > +++ b/tools/testing/selftests/kvm/lib/x86/ucall.c > @@ -5,11 +5,35 @@ > * Copyright (C) 2018, Red Hat, Inc. > */ > #include "kvm_util.h" > +#include "tdx/tdx.h" > +#include "tdx/tdx_util.h" > > #define UCALL_PIO_PORT ((u16)0x1000) > > +/* HPET address is guaranteed to be unused for ucall MMIO */ > +#define UCALL_MMIO_GPA 0xfed00000 > + > +static u8 vm_type; > +static gpa_t ucall_mmio_gpa; > + > +void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) > +{ > + vm_type = vm->type; > + sync_global_to_guest(vm, vm_type); It works and it looks simple. But we have the architectural approach to test if a guest is TD guest, by checking the CPUID 0x21. Since checking CPUID 0x21 is not complex, and as a bonus it can help test if TDX module behaves correctly for CPUID leaf 0x21, I think we should switch to use CPUID 0x21 to check if it is TDX VM in guest code? > + if (is_tdx_vm(vm)) { > + ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; So the passed-in @mmio_gpa is not used. Why cannot use the mmio_gpa, slot0->region.guest_phys_addr + slot0->region.memory_size chosen by __vm_create()? > + sync_global_to_guest(vm, ucall_mmio_gpa); > + } > +} > + > void ucall_arch_do_ucall(gva_t uc) > { > + if (vm_type == KVM_X86_TDX_VM) { > + tdx_mmio_write(ucall_mmio_gpa, sizeof(gva_t), uc); > + return; > + } > + > /* > * FIXME: Revert this hack (the entire commit that added it) once nVMX > * preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g. > @@ -46,6 +70,15 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) > { > struct kvm_run *run = vcpu->run; > > + if (vm_type == KVM_X86_TDX_VM) { > + if (run->exit_reason == KVM_EXIT_MMIO && > + run->mmio.phys_addr == UCALL_MMIO_GPA && > + run->mmio.len == sizeof(gva_t) && > + run->mmio.is_write) > + return (void *)(*((gva_t *)run->mmio.data)); > + return NULL; > + } > + > if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) { > struct kvm_regs regs; > >