On 10/2/2026 3:37 AM, Lisa Wang wrote:
> From: Sagi Shahar <[email protected]>
> 
> 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 <[email protected]>
> Signed-off-by: Lisa Wang <[email protected]>
> ---
>  .../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, &regs);
> -     regs.rflags = regs.rflags | 0x2;
> -     regs.rsp = kvm_allocate_vcpu_stack(vm);
> -     vcpu_regs_set(vcpu, &regs);
> +     /*
> +      * 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, &regs);
> +             regs.rflags = regs.rflags | 0x2;
> +             regs.rsp = kvm_allocate_vcpu_stack(vm);
> +             vcpu_regs_set(vcpu, &regs);
> +     }
>  
>       /* Setup the MP state */
>       mp_state.mp_state = 0;
> 


Reply via email to