Re: [PATCH v15 15/23] KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
From: Binbin Wu
Date: Sat Oct 10 2026 - 00:32:07 EST
On 10/2/2026 3:37 AM, Lisa Wang wrote:
> From: Sagi Shahar <sagis@xxxxxxxxxx>
>
> TDX VMs need to issue the KVM_TDX_INIT_VCPU ioctl for each vcpu after
> vcpu creation.
>
> KVM_TDX_INIT_VCPU has a strict prerequisite for the CPUID state. To
> satisfy this requirement, call KVM_TDX_GET_CPUID and KVM_SET_CPUID2 to
> pull the CPUID configuration from the TDCS and commit it into KVM,
> allowing KVM_TDX_INIT_VCPU to succeed.
>
> Additionally, unlike tdx_vm_ioctl(), tdx_vcpu_ioctl() doesn't check
> hw_error. KVM's vCPU-scoped TDX ioctl handlers don't propagate SEAMCALL
> errors into hw_error: the error is handled in the kernel and only an
> errno is returned. Checking the ioctl's return value and errno is
> therefore sufficient.
>
> Signed-off-by: Sagi Shahar <sagis@xxxxxxxxxx>
> Signed-off-by: Lisa Wang <wyihan@xxxxxxxxxx>
> ---
> .../selftests/kvm/include/x86/tdx/tdx_util.h | 20 +++++++++
> tools/testing/selftests/kvm/lib/x86/processor.c | 48 ++++++++++++++++++----
> 2 files changed, 60 insertions(+), 8 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
> index e529c587aeae..f5b2f32f2118 100644
> --- a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
> +++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
> @@ -46,6 +46,26 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)
> (unsigned long long)hw_error); \
> })
>
> +#define __tdx_vcpu_ioctl(vcpu, cmd, _flags, arg) \
> +({ \
> + union { \
> + struct kvm_tdx_cmd c; \
> + unsigned long raw; \
> + } tdx_cmd = { .c = { \
> + .id = (cmd), \
> + .flags = (u32)(_flags), \
> + .data = (u64)(arg), \
> + } }; \
> + \
> + __vcpu_ioctl(vcpu, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \
> +})
> +
> +#define tdx_vcpu_ioctl(vcpu, cmd, flags, arg) \
> +({ \
> + int ret = __tdx_vcpu_ioctl(vcpu, cmd, flags, arg); \
> + TEST_ASSERT(!ret, "%s failed, errno: %d (%s)", \
> + #cmd, errno, strerror(errno)); \
> +})
tdx_vm_ioctl() prints ret, tdx_vcpu_ioctl() doesn't.
Better to be consistent.
> void tdx_init_vm(struct kvm_vm *vm);
> void tdx_vm_setup_boot_code_region(struct kvm_vm *vm);
> void tdx_vm_setup_boot_parameters_region(struct kvm_vm *vm, u32 nr_runnable_vcpus);
> diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
> index 4a753fb43007..4af9cbf3fabb 100644
> --- a/tools/testing/selftests/kvm/lib/x86/processor.c
> +++ b/tools/testing/selftests/kvm/lib/x86/processor.c
> @@ -876,16 +876,48 @@ gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm)
> "__vm_alloc() did not provide a page-aligned address");
> stack_gva -= 8;
>
> + return stack_gva;
> +}
> +
> +static void tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
> +{
> + struct kvm_cpuid2 *cpuid;
> +
> + cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
> + tdx_vcpu_ioctl(vcpu, KVM_TDX_GET_CPUID, 0, cpuid);
> + vcpu_init_cpuid(vcpu, cpuid);
> + free(cpuid);
> + tdx_vcpu_ioctl(vcpu, KVM_TDX_INIT_VCPU, 0, NULL);
> +}
Should this function better to be in tdx_util.c, like other tdx specific helpers?
> +
> +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
> +{
> + struct kvm_mp_state mp_state;
> + struct kvm_vcpu *vcpu;
> + struct kvm_regs regs;
> +
> vcpu = __vm_vcpu_add(vm, vcpu_id);
> - vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
> - vcpu_init_sregs(vm, vcpu);
> - vcpu_init_xcrs(vm, vcpu);
>
> - /* Setup guest general purpose registers */
> - vcpu_regs_get(vcpu, ®s);
> - regs.rflags = regs.rflags | 0x2;
> - regs.rsp = kvm_allocate_vcpu_stack(vm);
> - vcpu_regs_set(vcpu, ®s);
> + /*
> + * Both kvm_get_supported_cpuid() (for legacy VMs) and KVM_TDX_GET_CPUID
> + * (for TDX VMs) return VM-scoped CPUID. e.g. the APIC ID isn't
> + * populated per vCPU. This is fine because KVM selftests don't
> + * currently test CPUID topology enumeration.
> + */
> + if (is_tdx_vm(vm)) {
> + tdx_vcpu_init(vm, vcpu);
> + } else {
> + vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
> +
> + vcpu_init_sregs(vm, vcpu);
> + vcpu_init_xcrs(vm, vcpu);
> +
> + /* Setup guest general purpose registers */
> + vcpu_regs_get(vcpu, ®s);
> + regs.rflags = regs.rflags | 0x2;
> + regs.rsp = kvm_allocate_vcpu_stack(vm);
> + vcpu_regs_set(vcpu, ®s);
> + }
>
> /* Setup the MP state */
> mp_state.mp_state = 0;
>