On Thu, May 21, 2026, Lisa Wang wrote:
> diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c
> b/tools/testing/selftests/kvm/lib/x86/ucall.c
> index e7dd5791959b..c8e3418d53af 100644
> --- a/tools/testing/selftests/kvm/lib/x86/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/x86/ucall.c
> @@ -5,11 +5,34 @@
> * 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)
>
> +static u8 vm_type;
> +static gpa_t host_ucall_mmio_gpa;
> +static gpa_t ucall_mmio_gpa;
> +
> +void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)
I think we should use an x86-specific GPA, not the first address past memslot0.
Unlike other architectures, x86 has a nice swath of addresses that are pretty
much guaranteed to be unused, thanks to selftests creating a local APIC by
default.
On the other hand, the chances of a collision with a memslot just after memslot0
are decidedly non-zero.
Note, because CoCo VMS don't support read-only memslots, the TODO in
__vm_create()
can't be resolved for TDX using the suggested shenanigans.
I vote for either the I/O APIC (0xfec00000) or HPET(0xfed00000). We *know* TDX
doesn't support an in-kernel I/O APIC, and the odds of KVM selftests ever
implementing an I/O APIC are basically nil. Ditto for the HPET.
> +{
> + vm_type = vm->type;
> + sync_global_to_guest(vm, vm_type);
> +
> + if (is_tdx_vm(vm)) {
> + host_ucall_mmio_gpa = ucall_mmio_gpa = mmio_gpa;
Drop host_ucall_mmio_gpa entirely. "host GPA" is rather nonsensical, and KVM is
responsible for stripping the shared bit. You can actually drop ucall_mmio_gpa
as well if we go with a hardcoded magic address.
> + 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, MMIO_SIZE_8B, uc);
s/MMIO_SIZE_8B/sizeof(hva_t), because what you're writing is the address of a
pointer in the host virtual address space.
> + 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 +69,13 @@ 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 == host_ucall_mmio_gpa &&
> + run->mmio.len == MMIO_SIZE_8B && run->mmio.is_write)
> + return (void *)(*((u64 *)run->mmio.data));
This needs to return NULL. Either that or make this an if-elif. Falling
through to the normal KVM_EXIT_IO check is not what we want.