From: Ackerley Tng 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 Co-developed-by: Sagi Shahar Signed-off-by: Sagi Shahar Co-developed-by: Lisa Wang Signed-off-by: Lisa Wang --- 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 -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); + + if (is_tdx_vm(vm)) { + ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; + 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; -- 2.55.0.229.g6434b31f56-goog