TDX KVM selftests
Subsystems (4)
v15 cover TDX KVM selftests
This patch series focuses on setting up a TDX VM and adding all code necessary to run a basic lifecycle test.
Unlike standard KVM selftests can set up the VM through guest registers, TDX module protects TDs' register state from the host. This feature of TDX causes problems on VM boot state initialization and the ucall implementation.
In standard KVM selftests, the host directly initializes the guest state by manipulating Special Registers (SREGs) and General Purpose Registers (GPRs) via IOCTLs (KVM_SET_SREGS, etc.) before the first KVM_RUN.
To bypass direct register initialization by the host, we utilize the standard x86 reset vector as the default entry point.
The mechanism works as follows:
1. The host places register values into a specific memory region and inserts boot code at the VM's default starting point.
2. When the VM starts, it executes this boot code to "pull" values from memory and manually set up its own SREGs and GPRs.
3. Once the environment is ready, the boot code jumps to the guest code.
The standard x86 ucall() implementation uses PIO, but it does not actually transmit data through the 4-byte PIO data. Instead, it relies on the host reading the ucall address directly from the guest's RDI register.
TDX selftests cannot utilize the standard x86 ucall implementation because the host is unable to access the guest's RDI register. Between the two alternatives, TDVMCALL_MMIO and TDVMCALL_REPORT_FATAL_ERROR, TDVMCALL_MMIO has fragility concerns and requires hardcoding a magic address while ignoring mmio_gpa. Therefore, the Report Fatal Error TDVMCALL is the cleanest and most efficient solution for TDX ucalls.
Main Changes from v14[1]:
1. Include patches from Sean to transition TDX ucalls from TDVMCALL_MMIO to TDVMCALL_REPORT_FATAL_ERROR, and update the rest of the series to support this change.
2. Rename TDCALL to TDVMCALL and leaf to fn.
3. Update the commit message to clarify workflow of CPUIDs.
4. Replace manual JMP rel8 offset calculations with an assembly trampoline.
Thanks review from Ackerley, Binbin, Peter, Rick, Sean and Xiaoyao !
Series is organized by:
1. Patches 1 - 3: Initialize the TDX VM
2. Patches 4 - 7: Add the TDX boot code
3. Patches 8 - 13: Set up the boot region
4. Patches 14 - 17: Set up the vCPU
5. Patches 18 - 19: Finalize the TDX VM
6. Patches 20 - 23: Implement the ucall and run the TDX test
[1]: https://lore.kernel.org/all/[email protected]/
---
Ackerley Tng (1):
KVM: selftests: Add helpers to init TDX memory and finalize VM
Erdem Aktas (2):
KVM: selftests: Add TDX boot code
KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
Isaku Yamahata (1):
KVM: selftests: Update kvm_init_vm_address_properties() for TDX
Lisa Wang (3):
KVM: selftests: Set shared attributes for ucall guest_memfd pages
KVM: selftests: Require guest_memfd for TDX VMs
KVM: selftests: Support guest_memfd in-place conversion
Sagi Shahar (13):
KVM: selftests: Initialize the TDX VM
KVM: selftests: Expose segment definitions to assembly files
tools: include: Add kbuild.h for assembly structure offsets
KVM: selftests: Introduce structures for TDX guest boot parameters
KVM: selftests: Expose functions to get default sregs values
KVM: selftests: Set up TDX boot code region
KVM: selftests: Set up TDX boot parameters region
KVM: selftests: Expose function to allocate vCPU stack
KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
KVM: selftests: Set entry point for TDX guest code
KVM: selftests: Finalize TDX VM in kvm_arch_vm_finalize_vcpus()
KVM: selftests: Add TDX lifecycle test
Sean Christopherson (3):
KVM: selftests: Add macros to simplify creating VM shapes for non-default types
KVM: selftests: Add support for per-VM ucall ops on x86
KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR
tools/include/linux/kbuild.h | 11 + tools/testing/selftests/kvm/.gitignore | 3 +- tools/testing/selftests/kvm/Makefile.kvm | 39 ++- tools/testing/selftests/kvm/include/kvm_util.h | 27 +- .../testing/selftests/kvm/include/x86/processor.h | 44 ++++ .../selftests/kvm/include/x86/processor_asm.h | 12 + tools/testing/selftests/kvm/include/x86/sev.h | 2 - .../selftests/kvm/include/x86/tdx/td_boot.h | 81 ++++++ tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 16 ++ .../selftests/kvm/include/x86/tdx/tdx_util.h | 83 ++++++ tools/testing/selftests/kvm/include/x86/ucall.h | 6 +- tools/testing/selftests/kvm/lib/kvm_util.c | 22 +- tools/testing/selftests/kvm/lib/ucall_common.c | 8 + tools/testing/selftests/kvm/lib/x86/processor.c | 147 ++++++++--- tools/testing/selftests/kvm/lib/x86/sev.c | 16 -- tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S | 71 +++++ .../selftests/kvm/lib/x86/tdx/td_boot_offsets.c | 21 ++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 39 +++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 292 +++++++++++++++++++++ tools/testing/selftests/kvm/lib/x86/ucall.c | 54 +++- tools/testing/selftests/kvm/x86/sev_dbg_test.c | 13 +- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 40 +-- tools/testing/selftests/kvm/x86/tdx_vm_test.c | 33 +++ 23 files changed, 965 insertions(+), 115 deletions(-)
---
base-commit: 37e0791600e5f1e2837a270c885296a172f5825e
change-id: 20260722-tdx-selftests-1da5587cf047
Best regards,
Signature · 1 line
Lisa Wang <[email protected]>
PATCH v15 1/23 KVM: selftests: Add macros to simplify creating VM shapes for non-default types
From: Sean Christopherson <[email protected]>
Add VM_TYPE() and __VM_SHAPE() macros to create a vm_shape structure given a type (and mode), and use the macros to define VM_SHAPE_{SEV,SEV_ES,SNP} shapes for x86's SEV family of VM shapes. Providing common infrastructure will avoid having to copy+paste vm_sev_create_with_one_vcpu() for TDX.
Use the new SEV+ shapes and drop vm_sev_create_with_one_vcpu().
Opportunistically move the existing VM_SHAPE() (now __VM_SHAPE()) macro below the definitions of VM_MODE_DEFAULT so that all of the SHAPE/TYPE macros are bundled together.
No functional change intended.
--- tools/testing/selftests/kvm/include/kvm_util.h | 27 ++++++++------- .../testing/selftests/kvm/include/x86/processor.h | 4 +++ tools/testing/selftests/kvm/include/x86/sev.h | 2 -- tools/testing/selftests/kvm/lib/x86/sev.c | 16 --------- tools/testing/selftests/kvm/x86/sev_dbg_test.c | 13 ++++--- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 40 +++++++++++----------- 6 files changed, 45 insertions(+), 57 deletions(-)
tools/testing/selftests/kvm/include/kvm_util.h +15 -12
@@ -221,18 +221,6 @@ struct vm_shape {kvm_static_assert (sizeof (struct vm_shape ) ==sizeof (u64)); -#define VM_TYPE_DEFAULT 0 - -#define VM_SHAPE (__mode) \ -({ \ - struct vm_shape shape = { \ - .mode = (__mode), \ - .type = VM_TYPE_DEFAULT \ - }; \ - \ - shape; \ -}) -extern enum vm_guest_mode vm_mode_default;#if defined(__aarch64__)
@@ -270,8 +258,23 @@ extern enum vm_guest_mode vm_mode_default;#endif +#define VM_TYPE_DEFAULT 0 + +#define __VM_SHAPE (__mode, __type) \ +({ \ + struct vm_shape shape = { \ + .mode = (__mode), \ + .type = (__type), \ + }; \ + \ + shape; \ +}) + +#define VM_SHAPE (__mode) __VM_SHAPE(__mode, VM_TYPE_DEFAULT)#define VM_SHAPE_DEFAULT VM_SHAPE(VM_MODE_DEFAULT) +#define VM_TYPE (__type) __VM_SHAPE(VM_MODE_DEFAULT, __type) +#define MIN_PAGE_SIZE (1U << MIN_PAGE_SHIFT)#define PTES_PER_MIN_PAGE ptes_per_page(MIN_PAGE_SIZE)
tools/testing/selftests/kvm/include/x86/processor.h +4 -0
@@ -386,6 +386,10 @@ static inline unsigned int x86_model(unsigned int eax)return ((eax >>12 ) &0xf0 ) | ((eax >>4 ) &0x0f ); } +#define VM_SHAPE_SEV VM_TYPE(KVM_X86_SEV_VM) +#define VM_SHAPE_SEV_ES VM_TYPE(KVM_X86_SEV_ES_VM) +#define VM_SHAPE_SNP VM_TYPE(KVM_X86_SNP_VM) +#define PHYSICAL_PAGE_MASK GENMASK_ULL(51, 12)#define PAGE_SHIFT 12
tools/testing/selftests/kvm/include/x86/sev.h +0 -2
@@ -53,8 +53,6 @@ void snp_vm_launch_start(struct kvm_vm *vm, u64 policy);void snp_vm_launch_update (struct kvm_vm *vm);void snp_vm_launch_finish (struct kvm_vm *vm); -struct kvm_vm *vm_sev_create_with_one_vcpu (u32 type,void *guest_code, -struct kvm_vcpu **cpu);void vm_sev_launch (struct kvm_vm *vm,u64 policy,u8 *measurement);kvm_static_assert (SEV_RET_SUCCESS ==0 );
tools/testing/selftests/kvm/lib/x86/sev.c +0 -16
@@ -158,22 +158,6 @@ void snp_vm_launch_finish(struct kvm_vm *vm)vm_sev_ioctl (vm,KVM_SEV_SNP_LAUNCH_FINISH , &launch_finish); } -struct kvm_vm *vm_sev_create_with_one_vcpu (u32 type,void *guest_code, -struct kvm_vcpu **cpu) -{ -struct vm_shape shape = { - .mode =VM_MODE_DEFAULT , - .type = type, - }; -struct kvm_vm *vm; -struct kvm_vcpu *cpus[1 ]; - - vm =__vm_create_with_vcpus (shape,1 ,0 , guest_code, cpus); - *cpu = cpus[0 ]; - -return vm; -} -void vm_sev_launch (struct kvm_vm *vm,u64 policy,u8 *measurement) {if (is_sev_snp_vm (vm)) {
tools/testing/selftests/kvm/x86/sev_dbg_test.c +6 -7
@@ -70,17 +70,14 @@ static void __test_sev_dbg(struct kvm_vm *vm, int nr_bytes) } } -static void test_sev_dbg (u32 type,u64 policy) +static void test_sev_dbg (struct vm_shape shape,u64 policy) {int sizes[] = {1 ,8 ,15 ,16 ,17 ,32 ,33 };struct kvm_vcpu *vcpu;struct kvm_vm *vm;int i; -if (!(kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (type))) -return ; - - vm =vm_sev_create_with_one_vcpu (type,NULL , &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu,NULL ); data =addr_gva2hva (vm,vm_alloc (vm,BUFFER_SIZE ,KVM_UTIL_MIN_VADDR ));memset (data,0xaa ,BUFFER_SIZE );
@@ -112,7 +109,9 @@ int main(int argc, char *argv[])TEST_REQUIRE (kvm_cpu_has (X86_FEATURE_SEV ));/* Note, KVM doesn't support {de,en}crypt commands for SNP. */ -test_sev_dbg (KVM_X86_SEV_VM ,0 ); -test_sev_dbg (KVM_X86_SEV_ES_VM ,SEV_POLICY_ES ); +if (kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_SEV_VM )) +test_sev_dbg (VM_SHAPE_SEV ,0 ); +if (kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_SEV_ES_VM )) +test_sev_dbg (VM_SHAPE_SEV_ES ,SEV_POLICY_ES );return 0 ; }
tools/testing/selftests/kvm/x86/sev_smoke_test.c +20 -20
@@ -104,7 +104,7 @@ static void compare_xsave(u8 *from_host, u8 *from_guest)abort (); } -static void test_sync_vmsa (u32 type,u64 policy) +static void test_sync_vmsa (struct vm_shape shape,u64 policy) {struct kvm_vcpu *vcpu;struct kvm_vm *vm;
@@ -114,7 +114,7 @@ static void test_sync_vmsa(u32 type, u64 policy)double x87val =M_PI ;struct kvm_xsave __attribute__((aligned (64 ))) xsave = {0 }; - vm =vm_sev_create_with_one_vcpu (type, guest_code_xsave, &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu, guest_code_xsave); gva =vm_alloc_shared (vm,PAGE_SIZE ,KVM_UTIL_MIN_VADDR ,MEM_REGION_TEST_DATA ); hva =addr_gva2hva (vm, gva);
@@ -150,13 +150,13 @@ static void test_sync_vmsa(u32 type, u64 policy)kvm_vm_free (vm); } -static void test_sev (void *guest_code,u32 type,u64 policy) +static void test_sev (void *guest_code,struct vm_shape shape,u64 policy) {struct kvm_vcpu *vcpu;struct kvm_vm *vm;struct ucall uc; - vm =vm_sev_create_with_one_vcpu (type, guest_code, &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu, guest_code);/* TODO: Validate the measurement is as expected. */ vm_sev_launch (vm, policy,NULL );
@@ -201,12 +201,12 @@ static void guest_shutdown_code(void) __asm__ __volatile__("ud2" ); } -static void test_sev_shutdown (u32 type,u64 policy) +static void test_sev_shutdown (struct vm_shape shape,u64 policy) {struct kvm_vcpu *vcpu;struct kvm_vm *vm; - vm =vm_sev_create_with_one_vcpu (type, guest_shutdown_code, &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu, guest_shutdown_code);vm_sev_launch (vm, policy,NULL );
@@ -218,28 +218,28 @@ static void test_sev_shutdown(u32 type, u64 policy)kvm_vm_free (vm); } -static void test_sev_smoke (void *guest,u32 type,u64 policy) +static void test_sev_smoke (void *guest,struct vm_shape shape,u64 policy) {const u64 xf_mask =XFEATURE_MASK_X87_AVX ; -if (type ==KVM_X86_SNP_VM ) -test_sev (guest, type, policy |SNP_POLICY_DBG ); +if (shape.type ==KVM_X86_SNP_VM ) +test_sev (guest, shape, policy |SNP_POLICY_DBG );else -test_sev (guest, type, policy |SEV_POLICY_NO_DBG ); -test_sev (guest, type, policy); +test_sev (guest, shape, policy |SEV_POLICY_NO_DBG ); +test_sev (guest, shape, policy); -if (type ==KVM_X86_SEV_VM ) +if (shape.type ==KVM_X86_SEV_VM )return ; -test_sev_shutdown (type, policy); +test_sev_shutdown (shape, policy);if (kvm_has_cap (KVM_CAP_XCRS ) && (xgetbv (0 ) &kvm_cpu_supported_xcr0 () & xf_mask) == xf_mask) { -test_sync_vmsa (type, policy); -if (type ==KVM_X86_SNP_VM ) -test_sync_vmsa (type, policy |SNP_POLICY_DBG ); +test_sync_vmsa (shape, policy); +if (shape.type ==KVM_X86_SNP_VM ) +test_sync_vmsa (shape, policy |SNP_POLICY_DBG );else -test_sync_vmsa (type, policy |SEV_POLICY_NO_DBG ); +test_sync_vmsa (shape, policy |SEV_POLICY_NO_DBG ); } }
@@ -254,13 +254,13 @@ int main(int argc, char *argv[])* though X86_FEATURE_SEV is set. Gate every type on KVM_CAP_VM_TYPES. */ if (kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_SEV_VM )) -test_sev_smoke (guest_sev_code,KVM_X86_SEV_VM ,0 ); +test_sev_smoke (guest_sev_code,VM_SHAPE_SEV ,0 );if (kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_SEV_ES_VM )) -test_sev_smoke (guest_sev_es_code,KVM_X86_SEV_ES_VM ,SEV_POLICY_ES ); +test_sev_smoke (guest_sev_es_code,VM_SHAPE_SEV_ES ,SEV_POLICY_ES );if (kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_SNP_VM )) -test_sev_smoke (guest_snp_code,KVM_X86_SNP_VM ,snp_default_policy ()); +test_sev_smoke (guest_snp_code,VM_SHAPE_SNP ,snp_default_policy ());return 0 ; }
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
PATCH v15 2/23 KVM: selftests: Update kvm_init_vm_address_properties() for TDX
In reply to Lisa WangFrom: Isaku Yamahata <[email protected]>
Initialize the TDX S-bit and the GPA tag mask in kvm_init_vm_address_properties() for TDX VMs, similar to how the C-bit is initialized for SEV VMs.
The TDX S-bit is used to distinguish between shared and private guest physical addresses. Its position is determined by the guest physical address width, which is either 48 or 52 bits for current TDX implementations.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 14 ++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 12 ++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h new file +14 -0
@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTESTS_TDX_TDX_UTIL_H +#define SELFTESTS_TDX_TDX_UTIL_H + +#include <stdbool.h> + +#include "kvm_util.h" + +static inline bool is_tdx_vm (struct kvm_vm *vm) +{ +return vm->type ==KVM_X86_TDX_VM ; +} + +#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +10 -2
@@ -11,6 +11,7 @@#include "smm.h" #include "svm_util.h" #include "sev.h" +#include "tdx/tdx_util.h" #include "vmx.h" #ifndef NUM_INTERRUPTS
@@ -1330,12 +1331,19 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)void kvm_init_vm_address_properties (struct kvm_vm *vm) { +u32 gpa_bits =kvm_cpu_property (X86_PROPERTY_GUEST_MAX_PHY_ADDR ); + + vm->arch.sev_fd =-1 ; +if (is_sev_vm (vm)) { vm->arch.sev_fd =open_sev_dev_path_or_exit (); vm->arch.c_bit =BIT_ULL (this_cpu_property (X86_PROPERTY_SEV_C_BIT )); vm->gpa_tag_mask = vm->arch.c_bit; - }else { - vm->arch.sev_fd =-1 ; + }else if (is_tdx_vm (vm)) { +TEST_ASSERT (gpa_bits ==48 || gpa_bits ==52 , +"TDX: bad X86_PROPERTY_GUEST_MAX_PHY_ADDR value: %u" , gpa_bits); + vm->arch.s_bit =BIT_ULL (gpa_bits -1 ); + vm->gpa_tag_mask = vm->arch.s_bit; } }
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
PATCH v15 3/23 KVM: selftests: Initialize the TDX VM
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Add tdx_init_vm() to handle the mandatory VM-level initialization sequence required for Intel TDX.
For TDX, the VM's CPUID configuration must be "sealed" during KVM_TDX_INIT_VM before any vCPUs are created. This is necessary because the TDX module does not allow the host to create and initialize any vCPUs before the VM's CPUID configuration is accepted and sealed into the TDCS.
Additionally, to satisfy the strict requirements of the TDH.MNG.INIT SEAMCALL, the helper masks the host-supported CPUID (kvm_get_supported_cpuid()) against the "directly configurable" bits reported by KVM_TDX_CAPABILITIES.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 2 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 37 +++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 21 +++- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 121 +++++++++++++++++++++ 5 files changed, 178 insertions(+), 4 deletions(-)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -28,6 +28,7 @@ LIBKVM_x86 += lib/x86/pmu.c LIBKVM_x86 += lib/x86/processor.c LIBKVM_x86 += lib/x86/sev.c LIBKVM_x86 += lib/x86/svm.c +LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c
tools/testing/selftests/kvm/include/x86/processor.h +2 -0
@@ -1024,6 +1024,8 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs)vcpu_ioctl (vcpu,KVM_SET_XCRS , xcrs); } +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index);const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid,u32 function,u32 index);const struct kvm_cpuid2 *kvm_get_supported_cpuid (void );
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +37 -0
@@ -11,4 +11,41 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) return vm->type == KVM_X86_TDX_VM; } +/* +* TDX ioctls +* Use underscores to avoid collisions with struct member names. +*/ +#define __tdx_vm_ioctl (vm, cmd, _flags, arg, hw_err) \ +({ \ + u64 *__hw_err = (hw_err); \ + int r; \ + \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \ + if (__hw_err) \ + *__hw_err = tdx_cmd.c.hw_error; \ + r; \ +}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 hw_error; \ + int ret = __tdx_vm_ioctl(vm, cmd, flags, arg, &hw_error); \ + \ + TEST_ASSERT(!ret, \ + "%s failed, rc: %d errno: %i (%s) hw_error: 0x%llx",\ + #cmd, ret, errno, strerror(errno), \ + (unsigned long long)hw_error); \ +}) + +void tdx_init_vm (struct kvm_vm *vm); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +17 -4
@@ -829,6 +829,9 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)vm_sev_ioctl (vm,KVM_SEV_INIT2 , &init); } +if (is_tdx_vm (vm)) +tdx_init_vm (vm); + r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );TEST_ASSERT (r >0 ,"KVM_GET_TSC_KHZ did not provide a valid TSC frequency." ); guest_tsc_khz = r;
@@ -1347,8 +1350,8 @@ void kvm_init_vm_address_properties(struct kvm_vm *vm) } } -const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, -u32 function,u32 index) +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) {int i;
@@ -1358,11 +1361,21 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,return &cpuid->entries[i]; } -TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); -return NULL ; } +const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) +{ +const struct kvm_cpuid_entry2 *entry; + + entry =__get_cpuid_entry (cpuid, function, index); +if (!entry) +TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); + +return entry; +} + #define X86_HYPERCALL(inputs...) \ ({ \ u64 r; \
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +121 -0
121 lines not on this page · raw message
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 03/23] KVM: selftests: Initialize the TDX VM
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] selftests/kvm: uninitialized padding causes KVM_TDX_INIT_VM failure - [Medium] selftests/kvm…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/kvm: uninitialized padding causes KVM_TDX_INIT_VM failure
- [Medium] selftests/kvm: data race on static tdx_cap in tdx_read_capabilities() --
commit 3680cc9e7726e14977411713d50e23fbb8635ddc
Author: Sagi Shahar <[email protected]>
KVM: selftests: Initialize the TDX VM
Add tdx_init_vm() to handle the mandatory VM-level initialization sequence required for Intel TDX. For TDX, the VM's CPUID configuration must be "sealed" during KVM_TDX_INIT_VM before any vCPUs are created. This commit masks the host-supported CPUID against the configurable bits reported by KVM_TDX_CAPABILITIES.
Quoted 16 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0@@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "tdx/tdx_util.h" + +static const struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) +{ +static struct kvm_tdx_capabilities *tdx_cap; +int nr_cpuid_configs =4 ; +int rc =-1 ;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 20
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+int i; + +if (tdx_cap) +return tdx_cap; + + do { + nr_cpuid_configs *= 2; + + tdx_cap = realloc(tdx_cap, sizeof(*tdx_cap) + + (sizeof(struct kvm_cpuid_entry2) * nr_cpuid_configs));
[Severity: Medium]
Could this realloc() cause a data race if multiple threads attempt to create TDX VMs concurrently?
If tdx_read_capabilities() checks and reallocates the static pointer tdx_cap without locking, concurrent calls might race on the realloc(), potentially leading to memory corruption, a double free, or a use-after-free during test execution.
[ ... ]
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +7
-0+void tdx_init_vm (struct kvm_vm *vm) +{ +struct kvm_tdx_init_vm *init_vm; +const struct kvm_cpuid2 *tmp; +struct kvm_cpuid2 *cpuid; + + tmp =kvm_get_supported_cpuid ();
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 112
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+ + cpuid =allocate_kvm_cpuid2 (tmp->nent); +memcpy (cpuid, tmp,kvm_cpuid2_size (tmp->nent)); +tdx_filter_cpuid (vm, cpuid); + + init_vm =calloc (1 ,sizeof (*init_vm) + +sizeof (init_vm->cpuid.entries[0 ]) * cpuid->nent); +TEST_ASSERT (init_vm,"init_vm allocation failed" ); + +memcpy (&init_vm->cpuid, cpuid,kvm_cpuid2_size (cpuid->nent));
[Severity: Medium]
Does this memcpy() copy uninitialized padding into the ioctl payload?
Since allocate_kvm_cpuid2() allocates struct kvm_cpuid2 using malloc(), the padding field can be left uninitialized. The kernel's KVM_GET_SUPPORTED_CPUID ioctl does not clear this padding, and it is subsequently copied into init_vm->cpuid here.
Because the KVM_TDX_INIT_VM ioctl strictly validates that cpuid.padding is zero, could this cause KVM_TDX_INIT_VM to fail with -EINVAL if the padding contains garbage?
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +9
-0+free (cpuid); + + init_vm->attributes =0 ; + init_vm->xfam =0 ; + +tdx_vm_ioctl (vm,KVM_TDX_INIT_VM ,0 , init_vm); + +free (init_vm); +}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Re: [PATCH v15 03/23] KVM: selftests: Initialize the TDX VM
Quoted 21 lines
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +6
-0+ +static const struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) +{ +static struct kvm_tdx_capabilities *tdx_cap; +int nr_cpuid_configs =4 ; +int rc =-1 ;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 20
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+int i; + +if (tdx_cap) +return tdx_cap; + + do { + nr_cpuid_configs *= 2; + + tdx_cap = realloc(tdx_cap, sizeof(*tdx_cap) + + (sizeof(struct kvm_cpuid_entry2) * nr_cpuid_configs));[Severity: Medium]
Could thisrealloc()cause a data race if multiple threads attempt to create TDX VMs concurrently?
Quoted 4 lines
If
tdx_read_capabilities()checks and reallocates the static pointertdx_capwithout locking, concurrent calls might race on therealloc(), potentially leading to memory corruption, a double free, or a use-after-free during test execution.
In KVM selftests, VM creation is performed on the main thread during test setup, matching other cached helpers like kvm_get_supported_cpuid(). I don't think we should care about potential race here.
Quoted 22 lines
[ ... ]
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +7
-0+void tdx_init_vm (struct kvm_vm *vm) +{ +struct kvm_tdx_init_vm *init_vm; +const struct kvm_cpuid2 *tmp; +struct kvm_cpuid2 *cpuid; + + tmp =kvm_get_supported_cpuid ();tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 112
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+ + cpuid =allocate_kvm_cpuid2 (tmp->nent); +memcpy (cpuid, tmp,kvm_cpuid2_size (tmp->nent)); +tdx_filter_cpuid (vm, cpuid); + + init_vm =calloc (1 ,sizeof (*init_vm) + +sizeof (init_vm->cpuid.entries[0 ]) * cpuid->nent); +TEST_ASSERT (init_vm,"init_vm allocation failed" ); + +memcpy (&init_vm->cpuid, cpuid,kvm_cpuid2_size (cpuid->nent));[Severity: Medium]
Does thismemcpy()copy uninitialized padding into the ioctl payload?
Quoted 8 lines
Since
allocate_kvm_cpuid2()allocates structkvm_cpuid2usingmalloc(), the padding field can be left uninitialized. The kernel'sKVM_GET_SUPPORTED_CPUIDioctl does not clear this padding, and it is subsequently copied intoinit_vm->cpuidhere.Because the
KVM_TDX_INIT_VMioctl strictly validates that cpuid.padding is zero, could this causeKVM_TDX_INIT_VMto fail with-EINVALif the padding contains garbage?
This might not happen in practice, as this allocation happens at the beginning of VM creation. It would only happen if a previously freed chunk with non-zero data is reused by malloc(), polluting cpuid->padding.
Still, we can set cpuid->padding = 0 here, or change malloc() to calloc() in allocate_kvm_cpuid2().
Re: [PATCH v15 03/23] KVM: selftests: Initialize the TDX VM
In reply to Lisa Wangtools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 47
Quoted 10 lines · Lisa Wang
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 hw_error; \ + int ret = __tdx_vm_ioctl(vm, cmd, flags, arg, &hw_error); \ + \ + TEST_ASSERT(!ret, \ + "%s failed, rc: %d errno: %i (%s) hw_error: 0x%llx",\ + #cmd, ret, errno, strerror(errno), \ + (unsigned long long)hw_error); \ +})
The cast to (unsigned long long) catches me eye. Can we use
"hw_error: 0x%" PRIx64
instead?
Re: [PATCH v15 03/23] KVM: selftests: Initialize the TDX VM
In reply to Lisa WangQuoted 5 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add
tdx_init_vm()to handle the mandatory VM-level initialization sequence required for Intel TDX.
Quoted 8 lines
For TDX, the VM's CPUID configuration must be "sealed" during
KVM_TDX_INIT_VMbefore any vCPUs are created. This is necessary because the TDX module does not allow the host to create and initialize any vCPUs before the VM's CPUID configuration is accepted and sealed into the TDCS.Additionally, to satisfy the strict requirements of the TDH.MNG.INIT SEAMCALL, the helper masks the host-supported CPUID
^
KVM-supported is more accurate
Quoted 74 lines
(
kvm_get_supported_cpuid()) against the "directly configurable"bits reported byKVM_TDX_CAPABILITIES.Co-developed-by Isaku Yamahata <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Co-developed-by Rick Edgecombe <[email protected]>Signed-off-by Rick Edgecombe <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Ira Weiny <[email protected]>--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 2 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 37 +++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 21 +++- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 121 +++++++++++++++++++++ 5 files changed, 178 insertions(+), 4 deletions(-)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -28,6 +28,7 @@ LIBKVM_x86 += lib/x86/pmu.c LIBKVM_x86 += lib/x86/processor.c LIBKVM_x86 += lib/x86/sev.c LIBKVM_x86 += lib/x86/svm.c +LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.ctools/testing/selftests/kvm/include/x86/processor.h +2
-0@@ -1024,6 +1024,8 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs)vcpu_ioctl (vcpu,KVM_SET_XCRS , xcrs); } +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index);const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid,u32 function,u32 index);const struct kvm_cpuid2 *kvm_get_supported_cpuid (void );tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +23
-0@@ -11,4 +11,41 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)return vm->type ==KVM_X86_TDX_VM ; } +/* +* TDX ioctls +* Use underscores to avoid collisions with struct member names. +*/ +#define __tdx_vm_ioctl (vm, cmd, _flags, arg, hw_err) \ +({ \ + u64 *__hw_err = (hw_err); \ + int r; \ + \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \ + if (__hw_err) \ + *__hw_err = tdx_cmd.c.hw_error; \ + r; \ +})
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 46
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+ +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 hw_error; \ + int ret = __tdx_vm_ioctl(vm, cmd, flags, arg, &hw_error); \ + \ + TEST_ASSERT(!ret, \ + "%s failed, rc: %d errno: %i (%s) hw_error: 0x%llx",\ + #cmd, ret, errno, strerror(errno), \ + (unsigned long long)hw_error); \
hw_error is already u64, why cast it to unsigned long long? TDX cases can only be done in x86-64, just use 0x%lx for u64?
Quoted 72 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +4
-0+}) + +void tdx_init_vm (struct kvm_vm *vm); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/processor.c +17
-4@@ -829,6 +829,9 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)vm_sev_ioctl (vm,KVM_SEV_INIT2 , &init); } +if (is_tdx_vm (vm)) +tdx_init_vm (vm); + r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );TEST_ASSERT (r >0 ,"KVM_GET_TSC_KHZ did not provide a valid TSC frequency." ); guest_tsc_khz = r;@@ -1347,8 +1350,8 @@ void kvm_init_vm_address_properties(struct kvm_vm *vm) } } -const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, -u32 function,u32 index) +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) {int i;@@ -1358,11 +1361,21 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,return &cpuid->entries[i]; } -TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); -return NULL ; } +const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) +{ +const struct kvm_cpuid_entry2 *entry; + + entry =__get_cpuid_entry (cpuid, function, index); +if (!entry) +TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); + +return entry; +} + #define X86_HYPERCALL(inputs...) \ ({ \ u64 r; \tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +12
-0@@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "tdx/tdx_util.h" + +static const struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) +{ +static struct kvm_tdx_capabilities *tdx_cap; +int nr_cpuid_configs =4 ; +int rc =-1 ; +int i; +
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 22
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+if (tdx_cap) +return tdx_cap; + + do { + nr_cpuid_configs *= 2; + + tdx_cap = realloc(tdx_cap, sizeof(*tdx_cap) + + (sizeof(struct kvm_cpuid_entry2) * nr_cpuid_configs)); + TEST_ASSERT(tdx_cap, + "Could not allocate memory for tdx capability nr_cpuid_configs %d\n",
Nit: Trailing newline in the message could be dropped.
Quoted 22 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +22
-0+ nr_cpuid_configs); + + tdx_cap->cpuid.nent = nr_cpuid_configs; + rc =__tdx_vm_ioctl (vm,KVM_TDX_CAPABILITIES ,0 , tdx_cap,NULL ); + }while (rc <0 && errno ==E2BIG ); + +TEST_ASSERT (rc ==0 ,"KVM_TDX_CAPABILITIES failed: %d %d" , + rc, errno); + +pr_debug ("tdx_cap: supported_attrs: 0x%016llx\n" +"tdx_cap: supported_xfam 0x%016llx\n" , + tdx_cap->supported_attrs, tdx_cap->supported_xfam); + +for (i =0 ; i < tdx_cap->cpuid.nent; i++) { +const struct kvm_cpuid_entry2 *config = &tdx_cap->cpuid.entries[i]; + +pr_debug ("cpuid config[%d]: leaf 0x%x sub_leaf 0x%x eax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n" , + i, config->function, config->index, + config->eax, config->ebx, config->ecx, config->edx); + } + +return tdx_cap;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 54
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+} + +/* +* Filter CPUID based on TDX supported capabilities +* +* Input Args: +* vm - Virtual Machine +* cpuid_data - CPUID fields to filter +* +* Output Args: None
cpuid_data should be Input/Output Arg?
I am not sure Input Args / Output Args / Return needed though. It seems the selftests code in kvm/x86 doesn't use this format.
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 59
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +5
-0+* +* Return: None +* +* For each CPUID leaf, filter out unsupported bits based on the capabilities +* reported by the TDX module
KVM filters directly configurable features that it doesn't support. The capabilities reported are not the original version reported by the TDX module.
the TDX module -> KVM ?
Quoted 47 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +47
-0+*/ +static void tdx_filter_cpuid (struct kvm_vm *vm, +struct kvm_cpuid2 *cpuid_data) +{ +const struct kvm_tdx_capabilities *tdx_cap; +const struct kvm_cpuid_entry2 *config; +struct kvm_cpuid_entry2 *e; +int i; + + tdx_cap =tdx_read_capabilities (vm); + + i =0 ; +while (i < cpuid_data->nent) { + e = cpuid_data->entries + i; + config =__get_cpuid_entry (&tdx_cap->cpuid, e->function, e->index); + +if (!config) { +int left = cpuid_data->nent - i -1 ; + +if (left >0 ) +memmove (cpuid_data->entries + i, + cpuid_data->entries + i +1 , +sizeof (*cpuid_data->entries) * left); + cpuid_data->nent--; +continue ; + } + + e->eax &= config->eax; + e->ebx &= config->ebx; + e->ecx &= config->ecx; + e->edx &= config->edx; + + i++; + } +} + +void tdx_init_vm (struct kvm_vm *vm) +{ +struct kvm_tdx_init_vm *init_vm; +const struct kvm_cpuid2 *tmp; +struct kvm_cpuid2 *cpuid; + + tmp =kvm_get_supported_cpuid (); + + cpuid =allocate_kvm_cpuid2 (tmp->nent); +memcpy (cpuid, tmp,kvm_cpuid2_size (tmp->nent)); +tdx_filter_cpuid (vm, cpuid);
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 116
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+ + init_vm =calloc (1 ,sizeof (*init_vm) + +sizeof (init_vm->cpuid.entries[0 ]) * cpuid->nent); +TEST_ASSERT (init_vm,"init_vm allocation failed" ); + +memcpy (&init_vm->cpuid, cpuid,kvm_cpuid2_size (cpuid->nent)); +free (cpuid); + + init_vm->attributes =0 ; + init_vm->xfam =0 ;
May be some comments for setting attributes and xfam to 0. Especially why the test doesn't need SEPT_VE_DISABLE?
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +5
-0+ +tdx_vm_ioctl (vm,KVM_TDX_INIT_VM ,0 , init_vm); + +free (init_vm); +}
PATCH v15 4/23 KVM: selftests: Expose segment definitions to assembly files
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Extract kernel segment definitions into a separate header for assembly files.
--- tools/testing/selftests/kvm/include/x86/processor_asm.h | 12 ++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-)
tools/testing/selftests/kvm/include/x86/processor_asm.h new file +12 -0
@@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* Used for storing defines used by both c and assembly code. +*/ +#ifndef SELFTEST_KVM_PROCESSOR_ASM_H +#define SELFTEST_KVM_PROCESSOR_ASM_H + +#define KERNEL_CS 0x8 +#define KERNEL_DS 0x10 +#define KERNEL_TSS 0x18 + +#endif /* SELFTEST_KVM_PROCESSOR_ASM_H */
tools/testing/selftests/kvm/lib/x86/processor.c +1 -4
@@ -8,6 +8,7 @@#include "kvm_util.h" #include "pmu.h" #include "processor.h" +#include "processor_asm.h" #include "smm.h" #include "svm_util.h" #include "sev.h"
@@ -18,10 +19,6 @@#define NUM_INTERRUPTS 256#endif -#define KERNEL_CS 0x8 -#define KERNEL_DS 0x10 -#define KERNEL_TSS 0x18 -gva_t exception_handlers;bool host_cpu_is_amd;bool host_cpu_is_intel;
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
PATCH v15 5/23 tools: include: Add kbuild.h for assembly structure offsets
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Add the Kbuild macros needed to enable the filechk_offsets mechanism to generate C header files containing structure member offset information.
Tools depending on assembly code that operate on structures have to hardcode the offsets of structure members. The Kbuild infrastructure can instead generate C header files with these offsets automatically, allowing them to be included in assembly code as symbolic constants.
For example, the TDX guest boot code requires access to parameters passed in the C structure (struct td_boot_parameters). This header provides the macros needed to extract these offsets from C code and expose them to assembly, ensuring the two remain synchronized.
--- tools/include/linux/kbuild.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
tools/include/linux/kbuild.h new file +11 -0
@@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TOOLS_LINUX_KBUILD_H +#define __TOOLS_LINUX_KBUILD_H + +#define DEFINE (sym, val) \ + asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val)) + +#define OFFSET (sym, str, mem) \ + DEFINE(sym, __builtin_offsetof(struct str, mem)) + +#endif /* __TOOLS_LINUX_KBUILD_H */
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 05/23] tools: include: Add kbuild.h for assembly structure offsets
Quoted 15 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add the
Kbuildmacros needed to enable thefilechk_offsetsmechanism to generate C header files containing structure member offset information.Tools depending on assembly code that operate on structures have to hardcode the offsets of structure members. The
Kbuildinfrastructure can instead generate C header files with these offsets automatically, allowing them to be included in assembly code as symbolic constants.For example, the TDX guest boot code requires access to parameters passed in the C structure (struct
td_boot_parameters). This header provides the macros needed to extract these offsets from C code and expose them to assembly, ensuring the two remain synchronized.
Quoted 5 lines
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Ira Weiny <[email protected]>Reviewed-by Ackerley Tng <[email protected]>Reviewed-by Binbin Wu <[email protected]>
Quoted 22 lines
--- tools/include/linux/kbuild.h | 11 +++++++++++ 1 file changed, 11 insertions(+)tools/include/linux/kbuild.h new file +11
-0@@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TOOLS_LINUX_KBUILD_H +#define __TOOLS_LINUX_KBUILD_H + +#define DEFINE (sym, val) \ + asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val)) + +#define OFFSET (sym, str, mem) \ + DEFINE(sym, __builtin_offsetof(struct str, mem)) + +#endif /* __TOOLS_LINUX_KBUILD_H */
PATCH v15 6/23 KVM: selftests: Introduce structures for TDX guest boot parameters
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Introduce td_boot_parameters and td_per_vcpu_parameters, and export their offsets to assembly via the kbuild infrastructure.
TDX guest registers are private and must be initialized by guest-side assembly. These structures allow the assembly code to retrieve boot parameters and index into per-vCPU data based on the vCPU ID, while keeping host and guest definitions synchronized.
Use kbuild.h to expose the offsets into the structs from c code to assembly code.
--- tools/testing/selftests/kvm/.gitignore | 3 +- tools/testing/selftests/kvm/Makefile.kvm | 35 +++++++++--- .../selftests/kvm/include/x86/tdx/td_boot.h | 66 ++++++++++++++++++++++ .../selftests/kvm/lib/x86/tdx/td_boot_offsets.c | 21 +++++++ 4 files changed, 117 insertions(+), 8 deletions(-)
tools/testing/selftests/kvm/.gitignore +2 -1
@@ -9,4 +9,5 @@ !config !settings !Makefile -!Makefile.kvm +!Makefile.kvm +include/x86/**/*_offsets.h
tools/testing/selftests/kvm/Makefile.kvm +28 -7
@@ -20,6 +20,8 @@ LIBKVM += lib/userfaultfd_util.c LIBKVM_STRING += lib/string_override.c +LIBKVM_ASM_DEFS += lib/x86/tdx/td_boot_offsets.c + LIBKVM_x86 += lib/x86/apic.c LIBKVM_x86 += lib/x86/handlers.S LIBKVM_x86 += lib/x86/hyperv.c
@@ -269,6 +271,10 @@ OVERRIDE_TARGETS = 1 include ../lib.mk include ../cgroup/lib/libcgroup.mk +# Enable Kbuild tools. +include $(top_srcdir)/scripts/Kbuild.include +include $(top_srcdir)/scripts/Makefile.lib + ifeq ($(ARCH),x86) include ../vfio/lib/libvfio.mk endif
@@ -285,15 +291,24 @@ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ -fno-stack-protector -fno-PIE -fno-strict-aliasing \ -I$(LINUX_TOOL_INCLUDE) -I$(LINUX_TOOL_ARCH_INCLUDE) \ -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -Iinclude/$(ARCH) \ - -I ../rseq -I.. $(EXTRA_CFLAGS) $(KHDR_INCLUDES) + -I ../rseq -I.. -I$(OUTPUT)/include/$(ARCH) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) ifeq ($(ARCH),s390) CFLAGS += -march=z10 endif + ifeq ($(ARCH),x86) + ifeq ($(shell echo "void foo(void) { }" | $(CC) -march=x86-64-v2 -x c - -c -o /dev/null 2>/dev/null; echo "$$?"),0) CFLAGS += -march=x86-64-v2 endif + +KVM_GEN_HDRS := $(patsubst lib/x86/%.c, $(OUTPUT)/include/x86/%.h, $(filter lib/x86/%, $(LIBKVM_ASM_DEFS))) +$(shell mkdir -p $(sort $(dir $(KVM_GEN_HDRS)))) +$(KVM_GEN_HDRS): GUARD = $(shell echo $(*F) | tr a-z A-Z | tr '.' '_') +$(KVM_GEN_HDRS): $(OUTPUT)/include/x86/%.h: $(OUTPUT)/lib/x86/%.s FORCE + $(call filechk,offsets,__$(GUARD)_H__) endif + ifeq ($(ARCH),arm64) tools_dir := $(top_srcdir)/tools arm64_tools_dir := $(tools_dir)/arch/arm64/tools/
@@ -326,11 +341,8 @@ LIBKVM_S := $(filter %.S,$(LIBKVM)) LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C)) LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S)) LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING)) -LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) -LIBKVM_OBJS += $(LIBCGROUP_O) -ifeq ($(ARCH),x86) -LIBKVM_OBJS += $(LIBVFIO_O) -endif +LIBKVM_ASM_DEFS_OBJ := $(patsubst %.c, $(OUTPUT)/%.s, $(LIBKVM_ASM_DEFS)) +LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O) $(LIBVFIO_O) SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS)) SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))
@@ -355,7 +367,9 @@ $(SPLIT_TEST_GEN_OBJ): $(OUTPUT)/$(ARCH)/%.o: $(ARCH)/%.c $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ EXTRA_CLEAN += $(GEN_HDRS) \ + $(KVM_GEN_HDRS) \ $(LIBKVM_OBJS) \ + $(LIBKVM_ASM_DEFS_OBJ) \ $(SPLIT_TEST_GEN_OBJ) \ $(TEST_DEP_FILES) \ $(TEST_GEN_OBJ) \
@@ -367,6 +381,9 @@ $(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c $(GEN_HDRS) $(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S $(GEN_HDRS) $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ +$(LIBKVM_ASM_DEFS_OBJ): $(OUTPUT)/%.s: %.c FORCE + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -S $< -o $@ + # Compile the string overrides as freestanding to prevent the compiler from # generating self-referential code, e.g. without "freestanding" the compiler may # "optimize" memcmp() by invoking memcmp(), thus causing infinite recursion.
@@ -375,11 +392,15 @@ $(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS)))) $(SPLIT_TEST_GEN_OBJ): $(GEN_HDRS) +$(LIBKVM_OBJS): $(KVM_GEN_HDRS) $(TEST_GEN_PROGS): $(LIBKVM_OBJS) $(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS) $(TEST_GEN_OBJ): $(GEN_HDRS) -cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib .. +FORCE: + +cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib .. \ + $(wildcard $(sort $(dir $(KVM_GEN_HDRS)))) cscope: $(RM) cscope.* (find $(include_paths) -name '*.h' \
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h new file +66 -0
66 lines not on this page · raw message
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c new file +21 -0
@@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#define COMPILE_OFFSETS + +#include <linux/kbuild.h> + +#include "tdx/td_boot.h" + +static void __attribute__((used))common (void ) +{ +OFFSET (TD_BOOT_PARAMETERS_CR0 , td_boot_parameters, cr0); +OFFSET (TD_BOOT_PARAMETERS_CR3 , td_boot_parameters, cr3); +OFFSET (TD_BOOT_PARAMETERS_CR4 , td_boot_parameters, cr4); +OFFSET (TD_BOOT_PARAMETERS_GDT , td_boot_parameters, gdtr); +OFFSET (TD_BOOT_PARAMETERS_IDT , td_boot_parameters, idtr); +OFFSET (TD_BOOT_PARAMETERS_PER_VCPU , td_boot_parameters, per_vcpu); +OFFSET (TD_PER_VCPU_PARAMETERS_ESP_GVA , td_per_vcpu_parameters, esp_gva); +OFFSET (TD_PER_VCPU_PARAMETERS_GUEST_CODE , td_per_vcpu_parameters, + guest_code); +DEFINE (SIZEOF_TD_PER_VCPU_PARAMETERS , +sizeof (struct td_per_vcpu_parameters )); +}
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 06/23] KVM: selftests: Introduce structures for TDX guest boot parameters
On 10/2/2026 3:37 AM, Lisa Wang wrote:
<snip>
tools/testing/selftests/kvm/Makefile.kvm LIBKVM_S := $(filter %.S,$(LIBKVM)) · line 333
Quoted 9 lines
tools/testing/selftests/kvm/Makefile.kvm +0
-5@@ -326,11 +341,8 @@ LIBKVM_S := $(filter %.S,$(LIBKVM)) LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C)) LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S)) LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING)) -LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) -LIBKVM_OBJS += $(LIBCGROUP_O) -ifeq ($(ARCH),x86) -LIBKVM_OBJS += $(LIBVFIO_O) -endif
the check for x86 arch is removed below, is it intentional?
Other than it,
Quoted 4 lines
tools/testing/selftests/kvm/Makefile.kvm +2
-0+LIBKVM_ASM_DEFS_OBJ := $(patsubst %.c, $(OUTPUT)/%.s, $(LIBKVM_ASM_DEFS)) +LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O) $(LIBVFIO_O) SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS)) SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))
PATCH v15 7/23 KVM: selftests: Add TDX boot code
In reply to Lisa WangFrom: Erdem Aktas <[email protected]>
Add code to boot a TDX test VM. Since TDX registers are inaccessible to KVM, the boot code loads the relevant values from memory into the registers before jumping to the guest code.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/include/x86/tdx/td_boot.h | 21 ++++++- tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S | 65 ++++++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -33,6 +33,7 @@ LIBKVM_x86 += lib/x86/svm.c LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c +LIBKVM_x86 += lib/x86/tdx/td_boot.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.c
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +18 -3
@@ -2,9 +2,6 @@#ifndef SELFTEST_TDX_TD_BOOT_H #define SELFTEST_TDX_TD_BOOT_H -#include <linux/compiler.h> -#include <linux/types.h> -/* * Layout for boot section (not to scale) *
@@ -24,7 +21,19 @@* | | * | | * |___________________________|____ 0x0_ffff_0000: TD_BOOT_PARAMETERS_GPA +* +* TD_BOOT_PARAMETERS_GPA is arbitrarily chosen to +* +* + be within the 4GB address space +* + provide enough contiguous memory for the struct td_boot_parameters such +* that there is one struct td_per_vcpu_parameters for KVM_MAX_VCPUS */ +#define TD_BOOT_PARAMETERS_GPA 0xffff0000 + +#ifndef __ASSEMBLER__ + +#include <linux/compiler.h> +#include <linux/types.h> /* * The exact memory layout for LGDT or LIDT instructions.
@@ -63,4 +72,10 @@ struct td_boot_parameters {struct td_per_vcpu_parameters per_vcpu[]; }; +extern u8 td_boot[], td_boot_code_end[]; + +#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot) + +#endif /* #ifndef __ASSEMBLER__ */ +#endif /* SELFTEST_TDX_TD_BOOT_H */
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +65 -0
65 lines not on this page · raw message
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 07/23] KVM: selftests: Add TDX boot code
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] kvm: selftests: missing wrmsr to set EFER.LME in TDX boot code - [Medium] kvm: selftests…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] kvm: selftests: missing wrmsr to set EFER.LME in TDX boot code
- [Medium] kvm: selftests: incorrect register used for TDX vCPU index
--
commit 0050b7c1fa5f321a19fe7395b74ac22ea9e52f99
Author: Erdem Aktas <[email protected]>
KVM: selftests: Add TDX boot code
This commit adds assembly code to boot a TDX test VM. It loads relevant boot parameters from memory into registers before jumping to the guest code.
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +0
-0@@ -0,0 +1,65 @@
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S line 29
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +7
-0+ /* + * Find the address of struct td_per_vcpu_parameters for this + * vCPU based on esi (TDX spec: initialized with vCPU id). Put + * struct address into eax for indirect addressing. + */ + movl $SIZEOF_TD_PER_VCPU_PARAMETERS, %eax + mul %esi
[Severity: Medium]
Is it correct to read the vCPU index from %esi in td_boot here?
According to the TDX architecture specification (via TDH.VP.INIT), the vCPU index is initialized in the %ecx register, while %esi is initialized to 0.
If %esi is used, will all vCPUs evaluate to index 0, causing them to incorrectly share the same stack and guest code pointer? This seems like it could corrupt the SMP stack and fail multi-vCPU tests.
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S line 56
Quoted 13 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +13
-0+ /* + * Set up control registers (x86 does not support moving memory + * directly to a control register). + */ + movl TD_BOOT_PARAMETERS_CR4(%ebx), %edi + movl %edi, %cr4 + movl TD_BOOT_PARAMETERS_CR3(%ebx), %edi + movl %edi, %cr3 + movl TD_BOOT_PARAMETERS_CR0(%ebx), %edi + movl %edi, %cr0 + + /* Switching to 64bit mode after ljmp and then jump to guest code */ + ljmp $(KERNEL_CS),$1f
[Severity: Medium]
Does td_boot miss a wrmsr to set EFER.LME before enabling paging?
The TDX VM guest boot code enables paging (CR0.PG) and physical address extensions (CR4.PAE) here, but appears to omit writing the EFER MSR to set the Long Mode Enable (LME) bit.
According to the x86 architecture, enabling paging without setting EFER.LME puts the processor into 32-bit PAE paging mode instead of 64-bit IA-32e mode.
Will the subsequent far jump attempt to load a 64-bit code segment (KERNEL_CS has L=1), causing a #GP fault since it is invalid in 32-bit mode? It seems this might crash the guest VM unconditionally on every boot.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
Re: [PATCH v15 07/23] KVM: selftests: Add TDX boot code
Quoted 13 lines · On Fri, Oct 02, 2026 at 09:13:29AM +0000, [email protected] wrote:
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S line 29
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +7
-0+ /* + * Find the address of struct td_per_vcpu_parameters for this + * vCPU based on esi (TDX spec: initialized with vCPU id). Put + * struct address into eax for indirect addressing. + */ + movl $SIZEOF_TD_PER_VCPU_PARAMETERS, %eax + mul %esi[Severity: Medium]
Is it correct to read the vCPU index from %esi intd_boothere?
Quoted 6 lines
According to the TDX architecture specification (via TDH.VP.INIT), the vCPU index is initialized in the %ecx register, while %esi is initialized to 0.
If %esi is used, will all vCPUs evaluate to index 0, causing them to incorrectly share the same stack and guest code pointer? This seems like it could corrupt the SMP stack and fail multi-vCPU tests.
According to the TDX spec, %esi is the vCPU index.
Quoted 23 lines
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S line 56
Quoted 13 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +13
-0+ /* + * Set up control registers (x86 does not support moving memory + * directly to a control register). + */ + movl TD_BOOT_PARAMETERS_CR4(%ebx), %edi + movl %edi, %cr4 + movl TD_BOOT_PARAMETERS_CR3(%ebx), %edi + movl %edi, %cr3 + movl TD_BOOT_PARAMETERS_CR0(%ebx), %edi + movl %edi, %cr0 + + /* Switching to 64bit mode after ljmp and then jump to guest code */ + ljmp $(KERNEL_CS),$1f[Severity: Medium]
Doestd_bootmiss a wrmsr to set EFER.LME before enabling paging?The TDX VM guest boot code enables paging (CR0.PG) and physical address extensions (CR4.PAE) here, but appears to omit writing the EFER MSR to set the Long Mode Enable (LME) bit.
Quoted 7 lines
According to the x86 architecture, enabling paging without setting EFER.LME puts the processor into 32-bit PAE paging mode instead of 64-bit IA-32e mode.
Will the subsequent far jump attempt to load a 64-bit code segment (
KERNEL_CShas L=1), causing a #GP fault since it is invalid in 32-bit mode? It seems this might crash the guest VM unconditionally on every boot.
In TDX, TDH.VP.INIT initializes EFER.LME = 1 already.
Quoted 2 lines
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
PATCH v15 8/23 KVM: selftests: Expose functions to get default sregs values
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
TDX can't set sregs values directly using KVM_SET_SREGS. Expose the default values of certain sregs used by TDX VMs so they can be set manually.
--- .../testing/selftests/kvm/include/x86/processor.h | 35 ++++++++++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 20 ++++--------- 2 files changed, 41 insertions(+), 14 deletions(-)
tools/testing/selftests/kvm/include/x86/processor.h +35 -0
@@ -30,6 +30,10 @@ extern struct kvm_mmu guest_mmu;#define MAX_NR_CPUID_ENTRIES 100#endif +#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif +#define NONCANONICAL 0xaaaaaaaaaaaaaaaaull/* Forced emulation prefix, used to invoke the emulator unconditionally. */
@@ -1651,4 +1655,35 @@ u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa);bool sys_clocksource_is_based_on_tsc (void ); +static inline u16 kvm_get_default_idt_limit (void ) +{ +return NUM_INTERRUPTS *sizeof (struct idt_entry ) -1 ; +} + +static inline u16 kvm_get_default_gdt_limit (void ) +{ +return getpagesize () -1 ; +} + +static inline u64 kvm_get_default_cr0 (void ) +{ +return X86_CR0_PE |X86_CR0_NE |X86_CR0_PG ; +} + +static inline u64 kvm_get_default_cr4 (int pgtable_levels) +{ +u64 cr4 =X86_CR4_PAE |X86_CR4_OSFXSR ; + +if (kvm_cpu_has (X86_FEATURE_XSAVE )) + cr4 |=X86_CR4_OSXSAVE ; +if (pgtable_levels ==5 ) + cr4 |=X86_CR4_LA57 ; +return cr4; +} + +static inline u64 kvm_get_default_efer (void ) +{ +return EFER_LME |EFER_LMA |EFER_NX ; +} +#endif /* SELFTEST_KVM_PROCESSOR_H */
tools/testing/selftests/kvm/lib/x86/processor.c +6 -14
@@ -15,10 +15,6 @@#include "tdx/tdx_util.h" #include "vmx.h" -#ifndef NUM_INTERRUPTS -#define NUM_INTERRUPTS 256 -#endif -gva_t exception_handlers;bool host_cpu_is_amd;bool host_cpu_is_intel;
@@ -650,17 +646,13 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu)vcpu_sregs_get (vcpu, &sregs); sregs.idt.base = vm->arch.idt; - sregs.idt.limit =NUM_INTERRUPTS *sizeof (struct idt_entry ) -1 ; + sregs.idt.limit =kvm_get_default_idt_limit (); sregs.gdt.base = vm->arch.gdt; - sregs.gdt.limit =getpagesize () -1 ; - - sregs.cr0 =X86_CR0_PE |X86_CR0_NE |X86_CR0_PG ; - sregs.cr4 |=X86_CR4_PAE |X86_CR4_OSFXSR ; -if (kvm_cpu_has (X86_FEATURE_XSAVE )) - sregs.cr4 |=X86_CR4_OSXSAVE ; -if (vm->mmu.pgtable_levels ==5 ) - sregs.cr4 |=X86_CR4_LA57 ; - sregs.efer |= (EFER_LME |EFER_LMA |EFER_NX ); + sregs.gdt.limit =kvm_get_default_gdt_limit (); + + sregs.cr0 =kvm_get_default_cr0 (); + sregs.cr4 |=kvm_get_default_cr4 (vm->mmu.pgtable_levels); + sregs.efer |=kvm_get_default_efer ();kvm_seg_set_unusable (&sregs.ldt);kvm_seg_set_kernel_code_64bit (&sregs.cs);
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
PATCH v15 9/23 KVM: selftests: Set up TDX boot code region
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Because the reset vector is located just 16 bytes below the 4GB boundary, there is not enough space to fit the entire TDX boot code. To solve this, insert a small trampoline at the reset vector that jumps to the actual boot code.
Place the boot code immediately in front of the trampoline. Because they are contiguous, they can be copied and mapped with a single operation.
Use virt_map() to create an identity map in this memory region to allow for seamless transition from paging-disabled to paging-enabled code.
--- .../selftests/kvm/include/x86/tdx/td_boot.h | 2 +- .../selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 4 ++- tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S | 6 ++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 35 ++++++++++++++++++++++ 5 files changed, 46 insertions(+), 2 deletions(-)
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +1 -1
@@ -72,7 +72,7 @@ struct td_boot_parameters {struct td_per_vcpu_parameters per_vcpu[]; }; -extern u8 td_boot[], td_boot_code_end[]; +extern u8 td_boot[], td_boot_code_end[], td_boot_reset_vector_trampoline[];#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1 -0
@@ -47,5 +47,6 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) })void tdx_init_vm (struct kvm_vm *vm); +void tdx_vm_setup_boot_code_region (struct kvm_vm *vm);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +3 -1
@@ -818,8 +818,10 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)vm_sev_ioctl (vm,KVM_SEV_INIT2 , &init); } -if (is_tdx_vm (vm)) +if (is_tdx_vm (vm)) {tdx_init_vm (vm); +tdx_vm_setup_boot_code_region (vm); + } r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );TEST_ASSERT (r >0 ,"KVM_GET_TSC_KHZ did not provide a valid TSC frequency." );
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S +6 -0
@@ -56,6 +56,12 @@ td_boot: ljmp $(KERNEL_CS),$1f 1: jmp *TD_PER_VCPU_PARAMETERS_GUEST_CODE(%eax) + int3 + +.globl td_boot_reset_vector_trampoline +td_boot_reset_vector_trampoline: + jmp td_boot + int3 /* Leave marker so size of td_boot code can be computed. */ .globl td_boot_code_end
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +35 -0
@@ -1,8 +1,43 @@// SPDX-License-Identifier: GPL-2.0-only +#include <linux/sizes.h> +#include "processor.h" +#include "tdx/td_boot.h" #include "tdx/tdx_util.h" +/* Arbitrarily selected to avoid overlaps with anything else */ +#define TD_BOOT_CODE_SLOT 20 + +#define X86_RESET_VECTOR 0xfffffff0ul + +void tdx_vm_setup_boot_code_region (struct kvm_vm *vm) +{ +const size_t total_size = td_boot_code_end - td_boot; +const size_t boot_code_size = td_boot_reset_vector_trampoline - td_boot; +const gpa_t boot_code_gpa =X86_RESET_VECTOR - boot_code_size; +gpa_t alloc_gpa =round_down (boot_code_gpa,PAGE_SIZE ); +size_t nr_pages = (SZ_4G - alloc_gpa) /PAGE_SIZE ; +u64 gmem_flags =0 ; +gpa_t gpa; +u8 *hva; + +vm_mem_add (vm,VM_MEM_SRC_SHMEM , alloc_gpa,TD_BOOT_CODE_SLOT , + nr_pages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags); + + gpa =vm_phy_pages_alloc (vm, nr_pages, alloc_gpa,TD_BOOT_CODE_SLOT ); +TEST_ASSERT (gpa == alloc_gpa,"Failed vm_phy_pages_alloc\n" ); + +virt_map (vm, alloc_gpa, alloc_gpa, nr_pages); + hva =addr_gpa2hva (vm, boot_code_gpa); +memcpy (hva, td_boot, total_size); + + hva += boot_code_size; +TEST_ASSERT (hva ==addr_gpa2hva (vm,X86_RESET_VECTOR ), +"Expected RESET vector at hva 0x%lx, got %lx" , + (unsigned long )addr_gpa2hva (vm,X86_RESET_VECTOR ), (unsigned long )hva); +} +static const struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
PATCH v15 10/23 KVM: selftests: Set up TDX boot parameters region
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 48 ++++++++++++++++++++++ 3 files changed, 52 insertions(+)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2 -0
@@ -48,5 +48,7 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)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); +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +2 -0
@@ -821,6 +821,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)if (is_tdx_vm (vm)) {tdx_init_vm (vm);tdx_vm_setup_boot_code_region (vm); +tdx_vm_setup_boot_parameters_region (vm, nr_vcpus); +tdx_vm_load_common_boot_parameters (vm); } r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +48 -0
60 lines not on this page · raw message
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 10/23] KVM: selftests: Set up TDX boot parameters region
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] selftests: ineffective TEST_ASSERT due to prior 32-bit truncation --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] selftests: ineffective TEST_ASSERT due to prior 32-bit truncation --
commit 818beb22776783ed2680d4f875f92a2f1dad9ff5
Author: Sagi Shahar <[email protected]>
KVM: selftests: Set up TDX boot parameters region
This commit allocates memory for TDX boot parameters and defines the utility functions necessary to fill this memory with the boot parameters.
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0
[ ... ]
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0@@ -38,6 +39,53 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm)
[ ... ]
Quoted 2 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm) +{
[ ... ]
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +5
-0+/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" ); + params->cr3 = vm->mmu.pgd;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 86
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt; + params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->idtr.base < (1ULL <<32 ), +"idt must be within 32-bit address space for 32-bit boot code" ); +TEST_ASSERT (params->gdtr.base < (1ULL <<32 ), +"gdt must be within 32-bit address space for 32-bit boot code" );
[Severity: Low]
Does this assignment silently truncate the 64-bit vm->arch.idt and vm->arch.gdt addresses?
Since params->idtr.base and params->gdtr.base are 32-bit fields, the subsequent TEST_ASSERT checks evaluate the already-truncated values. If a test allocates an IDT or GDT address beyond 4GB, the assertions will always be trivially true because the upper 32 bits are lost before the check.
This could lead to confusing test failures during guest execution instead of properly failing the assertion here.
Should these assertions check vm->arch.idt and vm->arch.gdt directly before assigning them to the base fields?
Quoted 1 line
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
Re: [PATCH v15 10/23] KVM: selftests: Set up TDX boot parameters region
Quoted 27 lines · On Fri, Oct 02, 2026 at 09:13:29AM +0000, [email protected] wrote:
[ ... ]
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +5
-0+/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" ); + params->cr3 = vm->mmu.pgd;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 86
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt; + params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->idtr.base < (1ULL <<32 ), +"idt must be within 32-bit address space for 32-bit boot code" ); +TEST_ASSERT (params->gdtr.base < (1ULL <<32 ), +"gdt must be within 32-bit address space for 32-bit boot code" );[Severity: Low]
Does this assignment silently truncate the 64-bitvm->arch.idtandvm->arch.gdtaddresses?Since
params->idtr.baseandparams->gdtr.baseare 32-bit fields, the subsequentTEST_ASSERTchecks evaluate the already-truncated values. If a test allocates an IDT or GDT address beyond 4GB, the assertions will always be trivially true because the upper 32 bits are lost before the check.
Quoted 5 lines
This could lead to confusing test failures during guest execution instead of properly failing the assertion here.
Should these assertions check
vm->arch.idtandvm->arch.gdtdirectly before assigning them to the base fields?
Will update the assertions to check vm->arch.idt and vm->arch.gdt directly.
Re: [PATCH v15 10/23] KVM: selftests: Set up TDX boot parameters region
In reply to Lisa WangQuoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Re: [PATCH v15 10/23] KVM: selftests: Set up TDX boot parameters region
In reply to Lisa WangQuoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
One nit below and the issue pointed by the sashiko.
Otherwise,
[...]
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 54
Quoted 13 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +13
-0+void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus) +{ +size_t boot_params_size =sizeof (struct td_boot_parameters ) + + nr_runnable_vcpus *sizeof (struct td_per_vcpu_parameters ); +int npages =DIV_ROUND_UP (boot_params_size,PAGE_SIZE ); +const u64 gmem_flags =0 ; +gpa_t gpa; + +vm_mem_add (vm,VM_MEM_SRC_SHMEM ,TD_BOOT_PARAMETERS_GPA , +TD_BOOT_PARAMETERS_SLOT , npages, +KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags); + gpa =vm_phy_pages_alloc (vm, npages,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_SLOT ); +TEST_ASSERT (gpa ==TD_BOOT_PARAMETERS_GPA ,"Failed vm_phy_pages_alloc\n" );
Nit: the tailing newline can be dropped.
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+ +virt_map (vm,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_GPA , npages); +} +
PATCH v15 11/23 KVM: selftests: Set shared attributes for ucall guest_memfd pages
In reply to Lisa WangThe default attributes for guest_memfd pages without in-place conversion is private. However, the ucall pages need to be shared between the host and the guest. Thus, set them as shared.
--- tools/testing/selftests/kvm/lib/ucall_common.c | 8 ++++++++ 1 file changed, 8 insertions(+)
tools/testing/selftests/kvm/lib/ucall_common.c +8 -0
@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;void ucall_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +struct userspace_mem_region *region;struct ucall_header *hdr;struct ucall *uc;gva_t gva;
@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) hdr = (struct ucall_header *)addr_gva2hva (vm, gva);memset (hdr,0 ,sizeof (*hdr)); + region =vm_get_mem_region (vm,MEM_REGION_DATA ); +if (region->region.flags &KVM_MEM_GUEST_MEMFD ) { +size_t aligned_sz =align_up (sizeof (*hdr), vm->page_size); + +vm_mem_set_shared (vm,addr_gva2gpa (vm, gva), aligned_sz); + } +for (i =0 ; i <KVM_MAX_VCPUS ; ++i) { uc = &hdr->ucalls[i]; uc->hva = uc;
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 11/23] KVM: selftests: Set shared attributes for ucall guest_memfd pages
Quoted 2 lines · Lisa Wang
The default attributes for
guest_memfdpages without in-place conversion is private.
This is wrong. The default memory attribute is of a @gfn is shared without in-place conversion, i.e., with per-vm memory attribute.
We don't need this patch actually. And I have tested with reverting this patch, with both out of place conversion and in-place conversion. Both worked.
Quoted 35 lines
However, the ucall pages need to be shared between the host and the guest. Thus, set them as shared.
Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/ucall_common.c | 8 ++++++++ 1 file changed, 8 insertions(+)tools/testing/selftests/kvm/lib/ucall_common.c +8
-0@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;void ucall_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +struct userspace_mem_region *region;struct ucall_header *hdr;struct ucall *uc;gva_t gva;@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) hdr = (struct ucall_header *)addr_gva2hva (vm, gva);memset (hdr,0 ,sizeof (*hdr)); + region =vm_get_mem_region (vm,MEM_REGION_DATA ); +if (region->region.flags &KVM_MEM_GUEST_MEMFD ) { +size_t aligned_sz =align_up (sizeof (*hdr), vm->page_size); + +vm_mem_set_shared (vm,addr_gva2gpa (vm, gva), aligned_sz); + } +for (i =0 ; i <KVM_MAX_VCPUS ; ++i) { uc = &hdr->ucalls[i]; uc->hva = uc;
Re: [PATCH v15 11/23] KVM: selftests: Set shared attributes for ucall guest_memfd pages
Quoted 10 lines · Xiaoyao Li
Quoted 2 lines · Lisa Wang
The default attributes for
guest_memfdpages without in-place conversion is private.This is wrong. The default memory attribute is of a @gfn is shared without in-place conversion, i.e., with per-vm memory attribute.
We don't need this patch actually. And I have tested with reverting this patch, with both out of place conversion and in-place conversion. Both worked.
oh yes, I misunderstood the default memory attribute without in-place conversion. I will drop this patch in the next series. Thanks!
Lisa
PATCH v15 12/23 KVM: selftests: Require guest_memfd for TDX VMs
In reply to Lisa WangForce guest_memfd for TDX VMs.
--- tools/testing/selftests/kvm/lib/kvm_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/kvm/lib/kvm_util.c +1 -1
@@ -482,7 +482,7 @@ void kvm_set_files_rlimit(u32 nr_vcpus)static bool is_guest_memfd_required (struct vm_shape shape) {#ifdef __x86_64__ -return shape.type ==KVM_X86_SNP_VM ; +return shape.type ==KVM_X86_SNP_VM || shape.type ==KVM_X86_TDX_VM ;#else return false;#endif
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 12/23] KVM: selftests: Require guest_memfd for TDX VMs
Quoted 4 lines · Lisa Wang
Force
guest_memfdfor TDX VMs.Signed-off-by Lisa Wang <[email protected]>Reviewed-by Binbin Wu <[email protected]>
Quoted 18 lines
--- tools/testing/selftests/kvm/lib/kvm_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)tools/testing/selftests/kvm/lib/kvm_util.c +1
-1@@ -482,7 +482,7 @@ void kvm_set_files_rlimit(u32 nr_vcpus)static bool is_guest_memfd_required (struct vm_shape shape) {#ifdef __x86_64__ -return shape.type ==KVM_X86_SNP_VM ; +return shape.type ==KVM_X86_SNP_VM || shape.type ==KVM_X86_TDX_VM ;#else return false;#endif
PATCH v15 13/23 KVM: selftests: Support guest_memfd in-place conversion
In reply to Lisa WangWith guest_memfd in-place conversion, the memory region can be set as shared, eliminating the need for dual-backed memory.
By setting the region to shared initially, the KVM selftest framework can populate the mmap()-ed guest_memfd memory just like any standard memory provider.
For CoCo VMs, pages that need to be private are explicitly set to private before executing the VM.
--- tools/testing/selftests/kvm/lib/kvm_util.c | 20 ++++++++++++++------ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 6 +++++- 2 files changed, 19 insertions(+), 7 deletions(-)
tools/testing/selftests/kvm/lib/kvm_util.c +14 -6
@@ -494,8 +494,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm; -int i, flags; +int flags =0 ; +int i;kvm_set_files_rlimit (nr_runnable_vcpus);
@@ -505,14 +507,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is supported, so the +* selftest framework can populate it exactly like other memory +* providers. */ - flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags, +-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +5 -1
@@ -23,6 +23,8 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm)gpa_t gpa;u8 *hva; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED |GUEST_MEMFD_FLAG_MMAP ;vm_mem_add (vm,VM_MEM_SRC_SHMEM , alloc_gpa,TD_BOOT_CODE_SLOT , nr_pages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);
@@ -44,9 +46,11 @@ void tdx_vm_setup_boot_parameters_region(struct kvm_vm *vm, u32 nr_runnable_vcpusize_t boot_params_size =sizeof (struct td_boot_parameters ) + nr_runnable_vcpus *sizeof (struct td_per_vcpu_parameters );int npages =DIV_ROUND_UP (boot_params_size,PAGE_SIZE ); -const u64 gmem_flags =0 ; +u64 gmem_flags =0 ;gpa_t gpa; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED |GUEST_MEMFD_FLAG_MMAP ;vm_mem_add (vm,VM_MEM_SRC_SHMEM ,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_SLOT , npages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 13/23] KVM: selftests: Support guest_memfd in-place conversion
Quoted 45 lines · Lisa Wang
With
guest_memfdin-place conversion, the memory region can be set as shared, eliminating the need for dual-backed memory.By setting the region to shared initially, the KVM selftest framework can populate the mmap()-ed
guest_memfdmemory just like any standard memory provider.For CoCo VMs, pages that need to be private are explicitly set to private before executing the VM.
Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/kvm_util.c | 20 ++++++++++++++------ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 6 +++++- 2 files changed, 19 insertions(+), 7 deletions(-)tools/testing/selftests/kvm/lib/kvm_util.c +7
-5@@ -494,8 +494,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm; -int i, flags; +int flags =0 ; +int i;kvm_set_files_rlimit (nr_runnable_vcpus);@@ -505,14 +507,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is supported, so the +* selftest framework can populate it exactly like other memory +* providers. */ - flags =0 ; -if (is_guest_memfd_required (shape))
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 523
Quoted 10 lines
tools/testing/selftests/kvm/lib/kvm_util.c +7
-1+if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags, +-1 ,0 , gmem_flags);
This is common for both TDX and SNP, does it break SNP?
Quoted 30 lines
tools/testing/selftests/kvm/lib/kvm_util.c +0
-0for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +5
-1@@ -23,6 +23,8 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm)gpa_t gpa;u8 *hva; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED |GUEST_MEMFD_FLAG_MMAP ;vm_mem_add (vm,VM_MEM_SRC_SHMEM , alloc_gpa,TD_BOOT_CODE_SLOT , nr_pages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);@@ -44,9 +46,11 @@ void tdx_vm_setup_boot_parameters_region(struct kvm_vm *vm, u32 nr_runnable_vcpusize_t boot_params_size =sizeof (struct td_boot_parameters ) + nr_runnable_vcpus *sizeof (struct td_per_vcpu_parameters );int npages =DIV_ROUND_UP (boot_params_size,PAGE_SIZE ); -const u64 gmem_flags =0 ; +u64 gmem_flags =0 ;gpa_t gpa; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED |GUEST_MEMFD_FLAG_MMAP ;vm_mem_add (vm,VM_MEM_SRC_SHMEM ,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_SLOT , npages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);
Re: [PATCH v15 13/23] KVM: selftests: Support guest_memfd in-place conversion
Quoted 53 lines · Binbin Wu
Quoted 52 lines · Lisa Wang
With
guest_memfdin-place conversion, the memory region can be set as shared, eliminating the need for dual-backed memory.By setting the region to shared initially, the KVM selftest framework can populate the mmap()-ed
guest_memfdmemory just like any standard memory provider.For CoCo VMs, pages that need to be private are explicitly set to private before executing the VM.
Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/kvm_util.c | 20 ++++++++++++++------ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 6 +++++- 2 files changed, 19 insertions(+), 7 deletions(-)tools/testing/selftests/kvm/lib/kvm_util.c +12
-5@@ -494,8 +494,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm; -int i, flags; +int flags =0 ; +int i;kvm_set_files_rlimit (nr_runnable_vcpus);@@ -505,14 +507,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is supported, so the +* selftest framework can populate it exactly like other memory +* providers. */ - flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + }
Quoted 4 lines
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 523
Quoted 3 lines
tools/testing/selftests/kvm/lib/kvm_util.c +2
-1-vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags, +-1 ,0 , gmem_flags);This is common for both TDX and SNP, does it break SNP?
As I commented in the v14[1], I still believe this part belongs to gmem in-place conversion series. Without this part, I think the SNP selftests will fail if in-place conversion is enabled.
Ackerley, did you test SNP selftests?
PATCH v15 14/23 KVM: selftests: Expose function to allocate vCPU stack
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Introduce kvm_allocate_vcpu_stack() to allocate a vCPU's stack in preparation for TDX to allocate a vCPU's stack and initialize its stack pointer.
TDX VMs' registers are protected state and cannot be initialized using the KVM_SET_REGS ioctl() that is used for normal VMs. A TDX vCPU's stack address will be a property of the TDX specific boot code that initializes the vCPUs' stack pointers at boot.
--- .../testing/selftests/kvm/include/x86/processor.h | 2 ++ tools/testing/selftests/kvm/lib/x86/processor.c | 25 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-)
tools/testing/selftests/kvm/include/x86/processor.h +2 -0
@@ -1212,6 +1212,8 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu,vcpu_set_or_clear_cpuid_feature (vcpu, feature, false); } +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm); +u64 vcpu_get_msr (struct kvm_vcpu *vcpu,u64 msr_index);int _vcpu_set_msr (struct kvm_vcpu *vcpu,u64 msr_index,u64 msr_value);
tools/testing/selftests/kvm/lib/x86/processor.c +22 -3
@@ -851,12 +851,31 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)vcpu_regs_set (vcpu, ®s); } -struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm) {struct kvm_mp_state mp_state;struct kvm_regs regs; +gva_t stack_gva;struct kvm_vcpu *vcpu; + stack_gva =__vm_alloc (vm,DEFAULT_STACK_PGS *getpagesize (), +DEFAULT_GUEST_STACK_VADDR_MIN ,MEM_REGION_DATA ); + + stack_gva +=DEFAULT_STACK_PGS *getpagesize (); + +/* +* Align stack to match calling sequence requirements in section "The +* Stack Frame" of the System V ABI AMD64 Architecture Processor +* Supplement, which requires the value (%rsp + 8) to be a multiple of +* 16 when control is transferred to the function entry point. +* +* If this code is ever used to launch a vCPU with 32-bit entry point it +* may need to subtract 4 bytes instead of 8 bytes. +*/ +TEST_ASSERT (IS_ALIGNED (stack_gva,PAGE_SIZE ), +"__vm_alloc() did not provide a page-aligned address" ); + stack_gva -=8 ; + vcpu =__vm_vcpu_add (vm, vcpu_id);vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ());vcpu_init_sregs (vm, vcpu);
@@ -864,8 +883,8 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)/* Setup guest general purpose registers */ vcpu_regs_get (vcpu, ®s); - regs.rflags = regs.rflags |X86_EFLAGS_FIXED ; - regs.rsp =vm_alloc_stack (vm,DEFAULT_STACK_PGS ); + regs.rflags = regs.rflags |0x2 ; + regs.rsp =kvm_allocate_vcpu_stack (vm);vcpu_regs_set (vcpu, ®s);/* Setup the MP state */
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 14/23] KVM: selftests: Expose function to allocate vCPU stack
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] selftests: replace descriptive X86_EFLAGS_FIXED macro with magic number 0x2 --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] selftests: replace descriptive X86_EFLAGS_FIXED macro with magic number 0x2 --
commit 450df5680580dea1fd0ac5294cee549f78bbf604
Author: Sagi Shahar <[email protected]>
KVM: selftests: Expose function to allocate vCPU stack
This commit introduces kvm_allocate_vcpu_stack to handle vCPU stack allocation separately. This is done in preparation for TDX, where the vCPU stack address is a property of the specific boot code rather than being initialized via the KVM_SET_REGS ioctl.
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/processor.c +0
-0
[ ... ]
tools/testing/selftests/kvm/lib/x86/processor.c struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) · line 886
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/processor.c +1
-2@@ -864,8 +883,8 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)/* Setup guest general purpose registers */ vcpu_regs_get (vcpu, ®s); - regs.rflags = regs.rflags |X86_EFLAGS_FIXED ; - regs.rsp =vm_alloc_stack (vm,DEFAULT_STACK_PGS ); + regs.rflags = regs.rflags |0x2 ;
[Severity: Low]
This isn't a bug, but was it intentional to replace the descriptive X86_EFLAGS_FIXED macro with the magic number 0x2 here?
Using the macro helps document that this sets the mandatory fixed bit in RFLAGS, maintaining code readability during vCPU register initialization.
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/processor.c +1
-0+ regs.rsp =kvm_allocate_vcpu_stack (vm);vcpu_regs_set (vcpu, ®s);/* Setup the MP state */
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=14
Re: [PATCH v15 14/23] KVM: selftests: Expose function to allocate vCPU stack
In reply to Lisa WangQuoted 39 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Introduce
kvm_allocate_vcpu_stack()to allocate a vCPU's stack in preparation for TDX to allocate a vCPU's stack and initialize its stack pointer.TDX VMs' registers are protected state and cannot be initialized using the
KVM_SET_REGSioctl()that is used for normal VMs. A TDX vCPU's stack address will be a property of the TDX specific boot code that initializes the vCPUs' stack pointers at boot.Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Binbin Wu <[email protected]>Reviewed-by Ira Weiny <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>--- .../testing/selftests/kvm/include/x86/processor.h | 2 ++ tools/testing/selftests/kvm/lib/x86/processor.c | 25 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-)tools/testing/selftests/kvm/include/x86/processor.h +2
-0@@ -1212,6 +1212,8 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu,vcpu_set_or_clear_cpuid_feature (vcpu, feature, false); } +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm); +u64 vcpu_get_msr (struct kvm_vcpu *vcpu,u64 msr_index);int _vcpu_set_msr (struct kvm_vcpu *vcpu,u64 msr_index,u64 msr_value);tools/testing/selftests/kvm/lib/x86/processor.c +0
-0
tools/testing/selftests/kvm/lib/x86/processor.c void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) · line 854
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/processor.c +1
-1@@ -851,12 +851,31 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)vcpu_regs_set (vcpu, ®s); } -struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm)
Besides what Sashiko pointed out[1], this patch was just messed up during the rebase and it even doesn't compile.
Quoted 39 lines
tools/testing/selftests/kvm/lib/x86/processor.c +21
-2{struct kvm_mp_state mp_state;struct kvm_regs regs; +gva_t stack_gva;struct kvm_vcpu *vcpu; + stack_gva =__vm_alloc (vm,DEFAULT_STACK_PGS *getpagesize (), +DEFAULT_GUEST_STACK_VADDR_MIN ,MEM_REGION_DATA ); + + stack_gva +=DEFAULT_STACK_PGS *getpagesize (); + +/* +* Align stack to match calling sequence requirements in section "The +* Stack Frame" of the System V ABI AMD64 Architecture Processor +* Supplement, which requires the value (%rsp + 8) to be a multiple of +* 16 when control is transferred to the function entry point. +* +* If this code is ever used to launch a vCPU with 32-bit entry point it +* may need to subtract 4 bytes instead of 8 bytes. +*/ +TEST_ASSERT (IS_ALIGNED (stack_gva,PAGE_SIZE ), +"__vm_alloc() did not provide a page-aligned address" ); + stack_gva -=8 ; + vcpu =__vm_vcpu_add (vm, vcpu_id);vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ());vcpu_init_sregs (vm, vcpu);@@ -864,8 +883,8 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)/* Setup guest general purpose registers */ vcpu_regs_get (vcpu, ®s); - regs.rflags = regs.rflags |X86_EFLAGS_FIXED ; - regs.rsp =vm_alloc_stack (vm,DEFAULT_STACK_PGS ); + regs.rflags = regs.rflags |0x2 ; + regs.rsp =kvm_allocate_vcpu_stack (vm);vcpu_regs_set (vcpu, ®s);/* Setup the MP state */
Re: [PATCH v15 14/23] KVM: selftests: Expose function to allocate vCPU stack
Quoted 43 lines · Xiaoyao Li
Quoted 42 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Introduce
kvm_allocate_vcpu_stack()to allocate a vCPU's stack in preparation for TDX to allocate a vCPU's stack and initialize its stack pointer.TDX VMs' registers are protected state and cannot be initialized using the
KVM_SET_REGSioctl()that is used for normal VMs. A TDX vCPU's stack address will be a property of the TDX specific boot code that initializes the vCPUs' stack pointers at boot.Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Binbin Wu <[email protected]>Reviewed-by Ira Weiny <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>--- .../testing/selftests/kvm/include/x86/processor.h | 2 ++ tools/testing/selftests/kvm/lib/x86/processor.c | 25 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-)tools/testing/selftests/kvm/include/x86/processor.h +2
-0@@ -1212,6 +1212,8 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu,vcpu_set_or_clear_cpuid_feature (vcpu, feature, false); } +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm); +u64 vcpu_get_msr (struct kvm_vcpu *vcpu,u64 msr_index);int _vcpu_set_msr (struct kvm_vcpu *vcpu,u64 msr_index,u64 msr_value);tools/testing/selftests/kvm/lib/x86/processor.c +0
-0@@ -851,12 +851,31 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)vcpu_regs_set (vcpu, ®s); }
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/processor.c void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) · line 854
Quoted 2 lines
tools/testing/selftests/kvm/lib/x86/processor.c +1
-1-struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm)Besides what Sashiko pointed out[1], this patch was just messed up during the rebase and it even doesn't compile.
Yes.
And since the upstream code already has vm_alloc_stack(), which basically does the same thing.
I think this patch can be dropped.
Quoted 43 lines
[1] https://lore.kernel.org/all/[email protected]/
Quoted 39 lines
tools/testing/selftests/kvm/lib/x86/processor.c +21
-2{struct kvm_mp_state mp_state;struct kvm_regs regs; +gva_t stack_gva;struct kvm_vcpu *vcpu; + stack_gva =__vm_alloc (vm,DEFAULT_STACK_PGS *getpagesize (), +DEFAULT_GUEST_STACK_VADDR_MIN ,MEM_REGION_DATA ); + + stack_gva +=DEFAULT_STACK_PGS *getpagesize (); + +/* +* Align stack to match calling sequence requirements in section "The +* Stack Frame" of the System V ABI AMD64 Architecture Processor +* Supplement, which requires the value (%rsp + 8) to be a multiple of +* 16 when control is transferred to the function entry point. +* +* If this code is ever used to launch a vCPU with 32-bit entry point it +* may need to subtract 4 bytes instead of 8 bytes. +*/ +TEST_ASSERT (IS_ALIGNED (stack_gva,PAGE_SIZE ), +"__vm_alloc() did not provide a page-aligned address" ); + stack_gva -=8 ; + vcpu =__vm_vcpu_add (vm, vcpu_id);vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ());vcpu_init_sregs (vm, vcpu);@@ -864,8 +883,8 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)/* Setup guest general purpose registers */ vcpu_regs_get (vcpu, ®s); - regs.rflags = regs.rflags |X86_EFLAGS_FIXED ; - regs.rsp =vm_alloc_stack (vm,DEFAULT_STACK_PGS ); + regs.rflags = regs.rflags |0x2 ; + regs.rsp =kvm_allocate_vcpu_stack (vm);vcpu_regs_set (vcpu, ®s);/* Setup the MP state */
Re: [PATCH v15 14/23] KVM: selftests: Expose function to allocate vCPU stack
Quoted 8 lines · Binbin Wu
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/processor.c void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) · line 854
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/processor.c +1
-1-struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm)Besides what Sashiko pointed out[1], this patch was just messed up during the rebase and it even doesn't compile.
Quoted 9 lines
Yes.
And since the upstream code already has
vm_alloc_stack(), which basically does the same thing.I think this patch can be dropped.
Quoted 2 lines
Yes.
vm_alloc_stack() can replace kvm_allocate_vcpu_stack(). I will drop this patch in the next version.
Thanks!
PATCH v15 15/23 KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
In reply to Lisa WangFrom: 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.
--- .../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(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +20 -0
@@ -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)); \ +})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);
tools/testing/selftests/kvm/lib/x86/processor.c +40 -8
56 lines not on this page · raw message
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 15/23] KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
tools/testing/selftests/kvm/lib/x86/processor.c gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm) · line 898
Quoted 6 lines · Lisa Wang
tools/testing/selftests/kvm/lib/x86/processor.c +6
-0+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; +
rebase mess up due to the previous patch.
Quoted 33 lines
tools/testing/selftests/kvm/lib/x86/processor.c +20
-8vcpu =__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 ;
Re: [PATCH v15 15/23] KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
In reply to Lisa WangQuoted 41 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
TDX VMs need to issue the
KVM_TDX_INIT_VCPUioctl for each vcpu after vcpu creation.
KVM_TDX_INIT_VCPUhas a strict prerequisite for the CPUID state. To satisfy this requirement, callKVM_TDX_GET_CPUIDandKVM_SET_CPUID2to pull the CPUID configuration from the TDCS and commit it into KVM, allowingKVM_TDX_INIT_VCPUto succeed.Additionally, unlike
tdx_vm_ioctl(),tdx_vcpu_ioctl()doesn't checkhw_error. KVM's vCPU-scoped TDX ioctl handlers don't propagate SEAMCALL errors intohw_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(-)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0@@ -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), \ + } }; \
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 68
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+ \ +__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.
Quoted 14 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +0
-0void 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);tools/testing/selftests/kvm/lib/x86/processor.c +3
-0@@ -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; +} +
tools/testing/selftests/kvm/lib/x86/processor.c gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm) · line 891
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/processor.c +10
-0+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?
Quoted 41 lines
tools/testing/selftests/kvm/lib/x86/processor.c +27
-8+ +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 ;
PATCH v15 16/23 KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Allocate a guest stack for each vCPU and record the GVA in the TDX boot parameters region to allow proper vCPU initialization.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 ++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 12 ++++++++++++ 3 files changed, 15 insertions(+)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1 -0
@@ -70,5 +70,6 @@ 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);void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm); +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +2 -0
@@ -888,6 +888,8 @@ static void tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)vcpu_init_cpuid (vcpu, cpuid);free (cpuid);tdx_vcpu_ioctl (vcpu,KVM_TDX_INIT_VCPU ,0 ,NULL ); + +tdx_vcpu_load_boot_parameters (vm, vcpu); }struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id)
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +12 -0
@@ -90,6 +90,18 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) "gdt must be within 32-bit address space for 32-bit boot code"); } +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); +TEST_ASSERT (vcpu_params->esp_gva < (1ULL <<32 ), +"ESP GVA must be within 32-bit address space for 32-bit boot code" ); +} +static const struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 16/23] KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] kvm: selftests: OOB array access in tdx_vcpu_load_boot_parameters() - [Medium] kvm: selftests…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] kvm: selftests: OOB array access in tdx_vcpu_load_boot_parameters()
- [Medium] kvm: selftests: silent integer truncation bypasses bounds check in tdx_vcpu_load_boot_parameters() --
commit cc6d621273aee216e856ad9b309ad6da83f69649
Author: Sagi Shahar <[email protected]>
KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
This commit allocates a guest stack for each vCPU and records its GVA in the TDX boot parameters region. This allows proper vCPU initialization.
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) · line 98
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +6
-0@@ -90,6 +90,18 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) "gdt must be within 32-bit address space for 32-bit boot code"); } +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id];
[Severity: Medium]
Will this lead to an out-of-bounds array access in tdx_vcpu_load_boot_parameters() if a test uses a sparse vcpu->id?
The boot parameters region appears to allocate memory for exactly nr_runnable_vcpus instances of struct td_per_vcpu_parameters in tdx_vm_setup_boot_parameters_region():
size_t boot_params_size = sizeof(struct td_boot_parameters) +
nr_runnable_vcpus * sizeof(struct td_per_vcpu_parameters);
Since vcpu->id represents the KVM vCPU ID (which can be the APIC ID on x86) and can be larger than or equal to nr_runnable_vcpus, could this write beyond the allocated bounds and corrupt adjacent host memory and guest physical pages?
This could be triggered if a selftest creating a TDX VM specifies a sparse vcpu->id that is larger than or equal to the total number of runnable vCPUs passed to vm_create().
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) · line 102
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+ + vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); +TEST_ASSERT (vcpu_params->esp_gva < (1ULL <<32 ), +"ESP GVA must be within 32-bit address space for 32-bit boot code" );
[Severity: Medium]
Does this bounds check in tdx_vcpu_load_boot_parameters() actually work, or is the value silently truncated before the check?
Because kvm_allocate_vcpu_stack() returns a 64-bit gva_t, assigning it to vcpu_params->esp_gva truncates the value to 32 bits since esp_gva is declared as a u32.
The TEST_ASSERT then evaluates the already truncated 32-bit value against (1ULL << 32), which will always be true. Could this bypass the intended safety check and cause silent stack corruption and test failures instead of an explicit assertion failure?
This could be triggered if a selftest allocates enough memory such that the guest stack virtual address exceeds 4GB.
Quoted 2 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+} +
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=16
Re: [PATCH v15 16/23] KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
Quoted 18 lines · On Fri, Oct 02, 2026 at 09:13:36AM +0000, [email protected] wrote:
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) · line 98
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +6
-0+void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id];[Severity: Medium]
Will this lead to an out-of-bounds array access intdx_vcpu_load_boot_parameters()if a test uses a sparsevcpu->id?The boot parameters region appears to allocate memory for exactly
nr_runnable_vcpusinstances of structtd_per_vcpu_parametersintdx_vm_setup_boot_parameters_region():
size_tboot_params_size=sizeof(struct td_boot_parameters)+
nr_runnable_vcpus*sizeof(struct td_per_vcpu_parameters);
Quoted 7 lines
Since
vcpu->idrepresents the KVM vCPU ID (which can be the APIC ID on x86) and can be larger than or equal tonr_runnable_vcpus, could this write beyond the allocated bounds and corrupt adjacent host memory and guest physical pages?This could be triggered if a selftest creating a TDX VM specifies a sparse
vcpu->idthat is larger than or equal to the total number of runnable vCPUs passed tovm_create().
In KVM selftests, VMs already create vCPUs sequentially with vcpu->id (via __vm_create_with_vcpus()). TDX selftests will not test sparse vCPU IDs either, because TDH.VP.INIT always initializes %esi to the sequential 0-based vCPU index.
Quoted 13 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) · line 102
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+ + vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); +TEST_ASSERT (vcpu_params->esp_gva < (1ULL <<32 ), +"ESP GVA must be within 32-bit address space for 32-bit boot code" );[Severity: Medium]
Does this bounds check intdx_vcpu_load_boot_parameters()actually work, or is the value silently truncated before the check?Because
kvm_allocate_vcpu_stack()returns a 64-bitgva_t, assigning it tovcpu_params->esp_gvatruncates the value to 32 bits sinceesp_gvais declared as a u32.
Quoted 7 lines
The
TEST_ASSERTthen evaluates the already truncated 32-bit value against (1ULL << 32), which will always be true. Could this bypass the intended safety check and cause silent stack corruption and test failures instead of an explicit assertion failure?This could be triggered if a selftest allocates enough memory such that the guest stack virtual address exceeds 4GB.
Yes, will fix in the next series.
PATCH v15 17/23 KVM: selftests: Set entry point for TDX guest code
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Since the rip register is inaccessible for TDX VMs, we need a different way to set the guest entry point for TDX VMs. This is done by writing the guest code address to a predefined location in the guest memory and loading it into rip as part of the TDX boot code.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 10 +++++++--- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1 -0
@@ -71,5 +71,6 @@ 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);void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu); +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +7 -3
@@ -846,9 +846,13 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) {struct kvm_regs regs; -vcpu_regs_get (vcpu, ®s); - regs.rip = (unsigned long ) guest_code; -vcpu_regs_set (vcpu, ®s); +if (is_tdx_vm (vcpu->vm)) { +tdx_vcpu_set_entry_point (vcpu, guest_code); + }else { +vcpu_regs_get (vcpu, ®s); + regs.rip = (unsigned long )guest_code; +vcpu_regs_set (vcpu, ®s); + } }gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm)
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10 -0
@@ -102,6 +102,16 @@ void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu) "ESP GVA must be within 32-bit address space for 32-bit boot code"); } +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vcpu->vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->guest_code = (u64 )guest_code; +} +static const struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 17/23] KVM: selftests: Set entry point for TDX guest code
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] selftests/tdx: out-of-bounds write via unvalidated vcpu->id in boot parameters --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] selftests/tdx: out-of-bounds write via unvalidated vcpu->id in boot parameters --
commit 5aefdec609eb9d20918c0aee05259719b0c3c0d2
Author: Sagi Shahar <[email protected]>
KVM: selftests: Set entry point for TDX guest code
This commit introduces a mechanism to set the guest entry point for TDX VMs, as the rip register is inaccessible. It accomplishes this by writing the guest code address to a predefined location in the guest memory, which is then loaded into rip during the TDX boot process.
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu) · line 110
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +6
-0@@ -102,6 +102,16 @@ void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu) "ESP GVA must be within 32-bit address space for 32-bit boot code"); } +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vcpu->vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id];
[Severity: Medium]
Does this out-of-bounds array access risk memory corruption?
If a test creates a TDX VM with a sparse or high vcpu->id (which is common for topology testing), could this index into params->per_vcpu exceed the memory allocated by tdx_vm_setup_boot_parameters_region()?
For context, tdx_vm_setup_boot_parameters_region() allocates memory strictly bounded by nr_runnable_vcpus rather than KVM_MAX_VCPUS:
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c:tdx_vm_setup_boot_parameters_region() {
...
size_t boot_params_size = sizeof(struct td_boot_parameters) +
nr_runnable_vcpus * sizeof(struct td_per_vcpu_parameters);
...
}Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+ + vcpu_params->guest_code = (u64 )guest_code; +} +
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=17
PATCH v15 18/23 KVM: selftests: Add helpers to init TDX memory and finalize VM
In reply to Lisa WangFrom: Ackerley Tng <[email protected]>
TDX protected memory needs to be measured and encrypted before it can be used by the guest. Traverse the VM's memory regions and initialize all the protected ranges by calling KVM_TDX_INIT_MEM_REGION.
Once all the memory is initialized, the VM can be finalized by calling KVM_TDX_FINALIZE_VM.
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 62 ++++++++++++++++++++++ 2 files changed, 64 insertions(+)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2 -0
@@ -73,4 +73,6 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm);void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu);void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code); +void tdx_vm_finalize (struct kvm_vm *vm); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +62 -0
70 lines not on this page · raw message
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 18/23] KVM: selftests: Add helpers to init TDX memory and finalize VM
Quoted 9 lines · Lisa Wang
From: Ackerley Tng <[email protected]>
TDX protected memory needs to be measured and encrypted before it can be used by the guest. Traverse the VM's memory regions and initialize all the protected ranges by calling
KVM_TDX_INIT_MEM_REGION.Once all the memory is initialized, the VM can be finalized by calling
KVM_TDX_FINALIZE_VM.
Quoted 9 lines
Co-developed-by Erdem Aktas <[email protected]>Signed-off-by Erdem Aktas <[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]>Signed-off-by Ackerley Tng <[email protected]>Reviewed-by Binbin Wu <[email protected]>Reviewed-by Ira Weiny <[email protected]>
PATCH v15 19/23 KVM: selftests: Finalize TDX VM in kvm_arch_vm_finalize_vcpus()
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Finalize TDX VM after VM and VCPU creation and memory initialization.
To integrate TDX VM finalization seamlessly, the finalization is hooked into kvm_arch_vm_finalize_vcpus(). This approach avoids the need for every TDX selftest to manually finalize the VM. While it does prevent TDX selftests from customizing memory before it is locked, no current selftests require this.
In TDX, finalization is a mandatory step to make the TD runnable. It also finalizes the MRTD of the VM's initial memory and state, locking them in for future attestation and preventing any further modifications.
--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)
tools/testing/selftests/kvm/lib/x86/processor.c +6 -0
@@ -1536,6 +1536,12 @@ bool kvm_arch_has_default_irqchip(void)return true; } +void kvm_arch_vm_finalize_vcpus (struct kvm_vm *vm) +{ +if (is_tdx_vm (vm)) +tdx_vm_finalize (vm); +} +void setup_smram (struct kvm_vm *vm,struct kvm_vcpu *vcpu,u64 smram_gpa,const void *smi_handler,size_t handler_size) {
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 19/23] KVM: selftests: Finalize TDX VM in kvm_arch_vm_finalize_vcpus()
Quoted 10 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Finalize TDX VM after VM and VCPU creation and memory initialization.
To integrate TDX VM finalization seamlessly, the finalization is hooked into
kvm_arch_vm_finalize_vcpus(). This approach avoids the need for every TDX selftest to manually finalize the VM. While it does prevent TDX selftests from customizing memory before it is locked, no current selftests require this.
Quoted 7 lines
In TDX, finalization is a mandatory step to make the TD runnable. It also finalizes the MRTD of the VM's initial memory and state, locking them in for future attestation and preventing any further modifications.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Ira Weiny <[email protected]>
Quoted 22 lines
--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)tools/testing/selftests/kvm/lib/x86/processor.c +6
-0@@ -1536,6 +1536,12 @@ bool kvm_arch_has_default_irqchip(void)return true; } +void kvm_arch_vm_finalize_vcpus (struct kvm_vm *vm) +{ +if (is_tdx_vm (vm)) +tdx_vm_finalize (vm); +} +void setup_smram (struct kvm_vm *vm,struct kvm_vcpu *vcpu,u64 smram_gpa,const void *smi_handler,size_t handler_size) {
Re: [PATCH v15 19/23] KVM: selftests: Finalize TDX VM in kvm_arch_vm_finalize_vcpus()
In reply to Lisa WangQuoted 10 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Finalize TDX VM after VM and VCPU creation and memory initialization.
To integrate TDX VM finalization seamlessly, the finalization is hooked into
kvm_arch_vm_finalize_vcpus(). This approach avoids the need for every TDX selftest to manually finalize the VM. While it does prevent TDX selftests from customizing memory before it is locked, no current selftests require this.
Quoted 7 lines
In TDX, finalization is a mandatory step to make the TD runnable. It also finalizes the MRTD of the VM's initial memory and state, locking them in for future attestation and preventing any further modifications.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Ira Weiny <[email protected]>
tdx_vm_finalize() does more than finalization.
It also calls tdx_load_private_memory() to populates the initial private memory.
otherwise,
Quoted 22 lines
--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)tools/testing/selftests/kvm/lib/x86/processor.c +6
-0@@ -1536,6 +1536,12 @@ bool kvm_arch_has_default_irqchip(void)return true; } +void kvm_arch_vm_finalize_vcpus (struct kvm_vm *vm) +{ +if (is_tdx_vm (vm)) +tdx_vm_finalize (vm); +} +void setup_smram (struct kvm_vm *vm,struct kvm_vcpu *vcpu,u64 smram_gpa,const void *smi_handler,size_t handler_size) {
PATCH v15 20/23 KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
In reply to Lisa WangFrom: Erdem Aktas <[email protected]>
TDX VMs need to notify the host, e.g. ucall. Add tdvmcall() to allow VMs to issue TDG.VP.VMCALL with a single function call instead of open-coding TDVMCALL registers setup at every call site.
The helper makes standard GHCI TDVMCALLs: the caller passes the GHCI sub-function and up to four arguments and returns the TDVMCALL status from R10.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 9 ++++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 39 +++++++++++++++++++++++ 3 files changed, 49 insertions(+)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -34,6 +34,7 @@ LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c LIBKVM_x86 += lib/x86/tdx/td_boot.S +LIBKVM_x86 += lib/x86/tdx/tdx.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.c
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +9 -0
@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +u64 __tdvmcall (u64 fn,u64 r12,u64 r13,u64 r14,u64 r15); + +#endif /* SELFTEST_KVM_TDX_TDX_H */
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +39 -0
@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +.globl __tdvmcall +/* + * u64 __tdvmcall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) + */ +__tdvmcall: + push %r12 + push %r13 + push %r14 + push %r15 + + /* + * Map C ABI fast arguments to TDX GHCI payload registers + * RDI=fn, RSI=r12, RDX=r13, RCX=r14, R8=r15 + */ + mov %rdi, %r11 + mov %rsi, %r12 + mov %rdx, %r13 + mov %rcx, %r14 + mov %r8, %r15 + + mov $0, %rax /* TDCALL leaf 0: TDG.VP.VMCALL */ + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 /* Standard GHCI call */ + + /* TDCALL instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret + +.section .note.GNU-stack,"",%progbits
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 20/23] KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] kvm: selftests: __tdvmcall silently masks TDCALL hardware failures --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] kvm: selftests: __tdvmcall silently masks TDCALL hardware failures --
commit 7709c4e900a0fe6433017aebbf708ee2946d3f19
Author: Erdem Aktas <[email protected]>
KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
This commit introduces a helper function __tdvmcall() to allow TDX VMs to issue TDG.VP.VMCALL with a single function call, abstracting the register setup. The function passes the GHCI sub-function and arguments, and returns the TDVMCALL status.
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +0
-0@@ -0,0 +1,39 @@
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S line 31
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +9
-0+ mov $0, %rax /* TDCALL leaf 0: TDG.VP.VMCALL */ + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 /* Standard GHCI call */ + + /* TDCALL instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax
[Severity: Medium]
Does this code inadvertently discard the TDCALL hardware failure status?
The TDCALL instruction returns its module-level execution status in %rax. If TDCALL fails (e.g., due to invalid arguments or incorrect VM state), %rax contains a non-zero error code and output registers like %r10 are left unmodified (which was initialized to 0 prior to the call).
By unconditionally overwriting %rax with %r10, any TDCALL failure status appears to be silently masked and converted into a false success. Should this code check or preserve the error code in %rax before overwriting it?
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +6
-0+ + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=20
Re: [PATCH v15 20/23] KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
Quoted 34 lines · On 10/2/2026 5:13 PM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] kvm: selftests:__tdvmcallsilently masks TDCALL hardware failures --commit
7709c4e900a0fe6433017aebbf708ee2946d3f19
Author: Erdem Aktas <[email protected]>KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
This commit introduces a helper function
__tdvmcall()to allow TDX VMs to issue TDG.VP.VMCALL with a single function call, abstracting the register setup. The function passes the GHCI sub-function and arguments, and returns the TDVMCALL status.Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +0
-0@@ -0,0 +1,39 @@[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S line 31
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +9
-0+ mov $0, %rax /* TDCALL leaf 0: TDG.VP.VMCALL */ + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 /* Standard GHCI call */ + + /* TDCALL instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax[Severity: Medium]
Does this code inadvertently discard the TDCALL hardware failure status?
Quoted 8 lines
The TDCALL instruction returns its module-level execution status in %rax. If TDCALL fails (e.g., due to invalid arguments or incorrect VM state), %rax contains a non-zero error code and output registers like %r10 are left unmodified (which was initialized to 0 prior to the call).
By unconditionally overwriting %rax with %r10, any TDCALL failure status appears to be silently masked and converted into a false success. Should this code check or preserve the error code in %rax before overwriting it?
This is a wrong comment from Sashiko. RAX returned from TDG.VP.VMCALL is always 0.
Quoted 7 lines
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +6
-0+ + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret
Re: [PATCH v15 20/23] KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
In reply to Lisa WangQuoted 10 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
TDX VMs need to notify the host, e.g. ucall. Add
tdvmcall()to allow VMs to issue TDG.VP.VMCALL with a single function call instead of open-coding TDVMCALL registers setup at every call site.The helper makes standard GHCI TDVMCALLs: the caller passes the GHCI sub-function and up to four arguments and returns the TDVMCALL status from R10.
Quoted 6 lines
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]>Signed-off-by Erdem Aktas <[email protected]>Reviewed-by Ackerley Tng <[email protected]>
Quoted 79 lines
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 9 ++++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 39 +++++++++++++++++++++++ 3 files changed, 49 insertions(+)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -34,6 +34,7 @@ LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c LIBKVM_x86 += lib/x86/tdx/td_boot.S +LIBKVM_x86 += lib/x86/tdx/tdx.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.ctools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +9
-0@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +u64 __tdvmcall (u64 fn,u64 r12,u64 r13,u64 r14,u64 r15); + +#endif /* SELFTEST_KVM_TDX_TDX_H */ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +39
-0@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +.globl __tdvmcall +/* + * u64 __tdvmcall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) + */ +__tdvmcall: + push %r12 + push %r13 + push %r14 + push %r15 + + /* + * Map C ABI fast arguments to TDX GHCI payload registers + * RDI=fn, RSI=r12, RDX=r13, RCX=r14, R8=r15 + */ + mov %rdi, %r11 + mov %rsi, %r12 + mov %rdx, %r13 + mov %rcx, %r14 + mov %r8, %r15 + + mov $0, %rax /* TDCALL leaf 0: TDG.VP.VMCALL */ + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 /* Standard GHCI call */ + + /* TDCALL instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret + +.section .note.GNU-stack,"",%progbits
Re: [PATCH v15 20/23] KVM: selftests: Add a helper to issue TDVMCALLs from the TDX vm
In reply to Lisa WangQuoted 3 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
TDX VMs need to notify the host, e.g. ucall. Add
tdvmcall()to allow VMs
^
The patch actually adds __tdvmcall().
Quoted 7 lines
to issue TDG.VP.VMCALL with a single function call instead of open-coding TDVMCALL registers setup at every call site.
The helper makes standard GHCI TDVMCALLs: the caller passes the GHCI sub-function and up to four arguments and returns the TDVMCALL status from R10.
Quoted 6 lines
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]>Signed-off-by Erdem Aktas <[email protected]>Reviewed-by Ackerley Tng <[email protected]>
Also one nit below.
Otherwise,
Quoted 54 lines
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 9 ++++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 39 +++++++++++++++++++++++ 3 files changed, 49 insertions(+)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -34,6 +34,7 @@ LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c LIBKVM_x86 += lib/x86/tdx/td_boot.S +LIBKVM_x86 += lib/x86/tdx/tdx.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.ctools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +9
-0@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +u64 __tdvmcall (u64 fn,u64 r12,u64 r13,u64 r14,u64 r15); + +#endif /* SELFTEST_KVM_TDX_TDX_H */ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +15
-0@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +.globl __tdvmcall +/* + * u64 __tdvmcall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) + */ +__tdvmcall: + push %r12 + push %r13 + push %r14 + push %r15 + + /* + * Map C ABI fast arguments to TDX GHCI payload registers + * RDI=fn, RSI=r12, RDX=r13, RCX=r14, R8=r15
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S line 25
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +10
-0+ */ + mov %rdi, %r11 + mov %rsi, %r12 + mov %rdx, %r13 + mov %rcx, %r14 + mov %r8, %r15 + + mov $0, %rax /* TDCALL leaf 0: TDG.VP.VMCALL */ + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 /* Standard GHCI call */
Nit:
xorl is more used then moving 0 to a register.
xorl %eax, %eax
...
xorl %r10d, %r10d
Quoted 15 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +14
-0+ + /* TDCALL instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret + +.section .note.GNU-stack,"",%progbits
PATCH v15 21/23 KVM: selftests: Add support for per-VM ucall ops on x86
In reply to Lisa WangFrom: Sean Christopherson <[email protected]>
Add a layer of indirection to x86's ucall infrastructure to allow wiring up a different set of {do,get}_ucall() operations for TDX VMs. TDX can't use port I/O (at least, not robustly), as the TDX ABI doesn't allow the guest to share arbitrary register state with the host on a PIO exit, and the PIO data payload is limited to 4 bytes, i.e. would potentially truncate the address of the per-ucall structure.
No functional change intended, as the ops are still hardwired to the common x86 ucall functions.
--- tools/testing/selftests/kvm/include/x86/ucall.h | 6 +----- tools/testing/selftests/kvm/lib/x86/ucall.c | 27 +++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-)
tools/testing/selftests/kvm/include/x86/ucall.h +1 -5
@@ -2,12 +2,8 @@#ifndef SELFTEST_KVM_UCALL_H #define SELFTEST_KVM_UCALL_H -#include "kvm_util.h" +#include "linux/kvm.h" #define UCALL_EXIT_REASON KVM_EXIT_IO -static inline void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) -{ -} -#endif
tools/testing/selftests/kvm/lib/x86/ucall.c +25 -2
@@ -8,13 +8,13 @@#define UCALL_PIO_PORT ((u16)0x1000) -void ucall_arch_do_ucall (gva_t uc) +static void ucall_x86_do_ucall (gva_t uc) { asmvolatile ("in %[port], %%al" : : [port]"d" (UCALL_PIO_PORT ),"D" (uc) :"rax" ,"memory" ); } -void *ucall_arch_get_ucall (struct kvm_vcpu *vcpu) +static void *ucall_x86_get_ucall (struct kvm_vcpu *vcpu) {struct kvm_run *run = vcpu->run;
@@ -26,3 +26,26 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) }return NULL ; } + +static struct { +void (*do_ucall)(gva_t uc); +void *(*get_ucall)(struct kvm_vcpu *vcpu); +} ucall_x86_ops = { + .do_ucall = ucall_x86_do_ucall, + .get_ucall = ucall_x86_get_ucall, +}; + +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ +sync_global_to_guest (vm, ucall_x86_ops); +} + +void ucall_arch_do_ucall (gva_t uc) +{ +return ucall_x86_ops.do_ucall (uc); +} + +void *ucall_arch_get_ucall (struct kvm_vcpu *vcpu) +{ +return ucall_x86_ops.get_ucall (vcpu); +}
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 21/23] KVM: selftests: Add support for per-VM ucall ops on x86
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] kvm: selftests: host-side ucall_x86_ops is a process-wide global, breaking multi-VM isolation --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] kvm: selftests: host-side ucall_x86_ops is a process-wide global, breaking multi-VM isolation --
commit abf66b381d3f3c93b178cddc07865810aae03f24
Author: Sean Christopherson <[email protected]>
KVM: selftests: Add support for per-VM ucall ops on x86
This commit introduces a layer of indirection for x86 ucall operations to support TDX VMs. It replaces hardcoded ucall functions with an operations structure that can be customized.
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +0
-0
[ ... ]
tools/testing/selftests/kvm/lib/x86/ucall.c void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) · line 36
Quoted 12 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +8
-0@@ -26,3 +26,26 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) }return NULL ; } + +static struct { +void (*do_ucall)(gva_t uc); +void *(*get_ucall)(struct kvm_vcpu *vcpu); +} ucall_x86_ops = { + .do_ucall = ucall_x86_do_ucall, + .get_ucall = ucall_x86_get_ucall, +};
[Severity: Medium]
The commit message mentions adding support for per-VM ucall ops, but ucall_x86_ops is defined as a static global variable. Doesn't this mean it will act as a process-wide shared variable on the host side, rather than being isolated per-VM?
If a selftest process initializes a TDX VM (which presumably overrides these operations) followed by a non-TDX VM, or runs multiple VMs concurrently, will the non-TDX VMs incorrectly end up using the TDX ucall callbacks?
Quoted 15 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +15
-0+ +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ +sync_global_to_guest (vm, ucall_x86_ops); +} + +void ucall_arch_do_ucall (gva_t uc) +{ +return ucall_x86_ops.do_ucall (uc); +} + +void *ucall_arch_get_ucall (struct kvm_vcpu *vcpu) +{ +return ucall_x86_ops.get_ucall (vcpu); +}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=21
Re: [PATCH v15 21/23] KVM: selftests: Add support for per-VM ucall ops on x86
In reply to Lisa WangQuoted 24 lines · Lisa Wang
From: Sean Christopherson <[email protected]>
Add a layer of indirection to x86's ucall infrastructure to allow wiring up a different set of {do,get}_ucall() operations for TDX VMs. TDX can't use port I/O (at least, not robustly), as the TDX ABI doesn't allow the guest to share arbitrary register state with the host on a PIO exit, and the PIO data payload is limited to 4 bytes, i.e. would potentially truncate the address of the per-ucall structure.
No functional change intended, as the ops are still hardwired to the common x86 ucall functions.
Signed-off-by Sean Christopherson <[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 | 27 +++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-)tools/testing/selftests/kvm/include/x86/ucall.h +0
-0
tools/testing/selftests/kvm/include/x86/ucall.h line 5
Quoted 5 lines
tools/testing/selftests/kvm/include/x86/ucall.h +1
-1@@ -2,12 +2,8 @@#ifndef SELFTEST_KVM_UCALL_H #define SELFTEST_KVM_UCALL_H -#include "kvm_util.h" +#include "linux/kvm.h"
Nit:
#include <linux/kvm.h>
[...]
tools/testing/selftests/kvm/include/x86/ucall.h line 9
Quoted 4 lines
tools/testing/selftests/kvm/include/x86/ucall.h +4
-0+ +void ucall_arch_do_ucall (gva_t uc) +{ +return ucall_x86_ops.do_ucall (uc);
return should be dropped.
Quoted 7 lines
tools/testing/selftests/kvm/include/x86/ucall.h +6
-0+} + +void *ucall_arch_get_ucall (struct kvm_vcpu *vcpu) +{ +return ucall_x86_ops.get_ucall (vcpu); +}
PATCH v15 22/23 KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR
In reply to Lisa WangFrom: Sean Christopherson <[email protected]>
Add support for doing ucalls on TDX by abusing TDVMCALL_REPORT_FATAL_ERROR to pass the address of the payload to the host. The "fatal error" TDVMCALL is perfectly suited for passing information to host userspace, is both the TDX Module and KVM allow the guest to pass (almost) all registers to the host, i.e. provide enough of a data payload to make a collision with a real fatal error practically impossible.
TDX can't use port I/O, as the TDX ABI doesn't allow the guest to share arbitrary register state with the host on a port I/O exit, and the port I/O data payload is limited to 4 bytes, i.e. would potentially truncate the ucall address.
Alternatively, TDX could use MMIO, but using a magic emulated MMIO address is fragile (see the TODO in __vm_create()), especially for TDX since TDX doesn't support read-only memslots, i.e. doesn't have line of sight towards addressing the TODO. E.g. TDX could hardcode the address to something that is all but guaranteed to be unused on x86, e.g. the I/O APIC base address or the HPET address, but that doesn't truly address the fragility concerns, and it's ugly because ucall_arch_init() would completely ignore the passed in @mmio_gpa despite obviously utilizing emulated MMIO.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 7 ++++++ tools/testing/selftests/kvm/lib/x86/ucall.c | 27 ++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx.h +7 -0
@@ -4,6 +4,13 @@#include <linux/types.h> +/* TDX hypercall Leaf IDs */ +#define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 +#define TDVMCALL_MAP_GPA 0x10001 +#define TDVMCALL_GET_QUOTE 0x10002 +#define TDVMCALL_REPORT_FATAL_ERROR 0x10003 +#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004 +u64 __tdvmcall (u64 fn,u64 r12,u64 r13,u64 r14,u64 r15);#endif /* SELFTEST_KVM_TDX_TDX_H */
tools/testing/selftests/kvm/lib/x86/ucall.c +26 -1
@@ -5,8 +5,28 @@* 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) +#define UCALL_PIO_PORT ((u16)0x1000) +#define UCALL_TDX_MAGIC 0xabacadabaULL + +static void ucall_tdx_do_ucall (gva_t uc) +{ +__tdvmcall (TDVMCALL_REPORT_FATAL_ERROR ,UCALL_TDX_MAGIC , uc,0 ,0 ); +} + +static void *ucall_tdx_get_ucall (struct kvm_vcpu *vcpu) +{ +struct kvm_run *run = vcpu->run; + +if (run->exit_reason ==KVM_EXIT_SYSTEM_EVENT && + run->system_event.type ==KVM_SYSTEM_EVENT_TDX_FATAL && + run->system_event.data[12 ] ==UCALL_TDX_MAGIC ) +return (void *)(run->system_event.data[13 ]); + +return NULL ; +}static void ucall_x86_do_ucall (gva_t uc) {
@@ -37,6 +57,11 @@ static struct {void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +if (is_tdx_vm (vm)) { + ucall_x86_ops.do_ucall = ucall_tdx_do_ucall; + ucall_x86_ops.get_ucall = ucall_tdx_get_ucall; + } +sync_global_to_guest (vm, ucall_x86_ops); }
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
[email protected]
AI reviewRe: [PATCH v15 22/23] KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] kvm: selftests: ucall global state poisoning breaks mixed-VM testing --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] kvm: selftests: ucall global state poisoning breaks mixed-VM testing --
commit 742d058040571d70e8a67c18a52e7026f4abe945
Author: Sean Christopherson <[email protected]>
KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR
This commit adds support for performing ucalls on TDX by utilizing the TDVMCALL_REPORT_FATAL_ERROR hypercall. It replaces the default ucall operations with TDX-specific handlers when a TDX VM is initialized.
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +0
-0
[ ... ]
tools/testing/selftests/kvm/lib/x86/ucall.c static struct { · line 64
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +5
-0@@ -37,6 +57,11 @@ static struct {void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +if (is_tdx_vm (vm)) { + ucall_x86_ops.do_ucall = ucall_tdx_do_ucall; + ucall_x86_ops.get_ucall = ucall_tdx_get_ucall; + } +
[Severity: Medium]
Since ucall_x86_ops is a process-wide global struct, does modifying it here without an else branch to restore the default handlers break mixed-VM testing?
If a process creates a TDX VM and later creates a non-TDX VM, the non-TDX VM will inherit these TDX handlers. When sync_global_to_guest() is called, this modified state syncs into the non-TDX VM's memory, which might cause the guest to incorrectly issue TDCALL for ucalls, leading to an Invalid Opcode exception in the non-TDX guest.
Additionally, host-side dispatch via ucall_arch_get_ucall() relies on this globally assigned pointer:
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
return ucall_x86_ops.get_ucall(vcpu);
}Will this cause the host to dispatch based on the last initialized VM rather than dynamically checking is_tdx_vm(vcpu->vm)?
Quoted 2 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +0
-0sync_global_to_guest (vm, ucall_x86_ops); }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=22
Re: [PATCH v15 22/23] KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR
Quoted 34 lines · On 10/2/2026 5:13 PM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] kvm: selftests: ucall global state poisoning breaks mixed-VM testing --commit
742d058040571d70e8a67c18a52e7026f4abe945
Author: Sean Christopherson <[email protected]>KVM: selftests: Add support for TDX ucalls, via
TDVMCALL_REPORT_FATAL_ERRORThis commit adds support for performing ucalls on TDX by utilizing the
TDVMCALL_REPORT_FATAL_ERRORhypercall. It replaces the default ucall operations with TDX-specific handlers when a TDX VM is initialized.Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +0
-0[ ... ]
tools/testing/selftests/kvm/lib/x86/ucall.c static struct { · line 64
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +5
-0@@ -37,6 +57,11 @@ static struct {void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +if (is_tdx_vm (vm)) { + ucall_x86_ops.do_ucall = ucall_tdx_do_ucall; + ucall_x86_ops.get_ucall = ucall_tdx_get_ucall; + } +[Severity: Medium]
Sinceucall_x86_opsis a process-wide global struct, does modifying it here without an else branch to restore the default handlers break mixed-VM testing?
Quoted 5 lines
If a process creates a TDX VM and later creates a non-TDX VM, the non-TDX VM will inherit these TDX handlers. When
sync_global_to_guest()is called, this modified state syncs into the non-TDX VM's memory, which might cause the guest to incorrectly issue TDCALL for ucalls, leading to an Invalid Opcode exception in the non-TDX guest.
Is it a possible scenario to test with TDX and non-TDX VMs in one process?
Quoted 15 lines
Additionally, host-side dispatch via
ucall_arch_get_ucall()relies on this globally assigned pointer:void *ucall_arch_get_ucall(struct
kvm_vcpu*vcpu){ return ucall_x86_ops.get_ucall(vcpu); }Will this cause the host to dispatch based on the last initialized VM rather than dynamically checking
is_tdx_vm(vcpu->vm)?Quoted 2 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +0
-0sync_global_to_guest (vm, ucall_x86_ops); }
Re: [PATCH v15 22/23] KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR
In reply to Lisa WangQuoted 5 lines · Lisa Wang
From: Sean Christopherson <[email protected]>
Add support for doing ucalls on TDX by abusing
TDVMCALL_REPORT_FATAL_ERRORto pass the address of the payload to the host. The "fatal error" TDVMCALL is perfectly suited for passing information to host userspace, is both the
^
asQuoted 18 lines
TDX Module and KVM allow the guest to pass (almost) all registers to the host, i.e. provide enough of a data payload to make a collision with a real fatal error practically impossible.
TDX can't use port I/O, as the TDX ABI doesn't allow the guest to share arbitrary register state with the host on a port I/O exit, and the port I/O data payload is limited to 4 bytes, i.e. would potentially truncate the ucall address.
Alternatively, TDX could use MMIO, but using a magic emulated MMIO address is fragile (see the TODO in
__vm_create()), especially for TDX since TDX doesn't support read-only memslots, i.e. doesn't have line of sight towards addressing the TODO. E.g. TDX could hardcode the address to something that is all but guaranteed to be unused on x86, e.g. the I/O APIC base address or the HPET address, but that doesn't truly address the fragility concerns, and it's ugly becauseucall_arch_init()would completely ignore the passed in @mmio_gpa despite obviously utilizing emulated MMIO.
Quoted 2 lines
Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Also one nit below.
otherwise,
Quoted 41 lines
--- tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 7 ++++++ tools/testing/selftests/kvm/lib/x86/ucall.c | 27 ++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-)tools/testing/selftests/kvm/include/x86/tdx/tdx.h +7
-0@@ -4,6 +4,13 @@#include <linux/types.h> +/* TDX hypercall Leaf IDs */ +#define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 +#define TDVMCALL_MAP_GPA 0x10001 +#define TDVMCALL_GET_QUOTE 0x10002 +#define TDVMCALL_REPORT_FATAL_ERROR 0x10003 +#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004 +u64 __tdvmcall (u64 fn,u64 r12,u64 r13,u64 r14,u64 r15);#endif /* SELFTEST_KVM_TDX_TDX_H */ tools/testing/selftests/kvm/lib/x86/ucall.c +8
-1@@ -5,8 +5,28 @@* 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) +#define UCALL_PIO_PORT ((u16)0x1000) +#define UCALL_TDX_MAGIC 0xabacadabaULL + +static void ucall_tdx_do_ucall (gva_t uc) +{ +__tdvmcall (TDVMCALL_REPORT_FATAL_ERROR ,UCALL_TDX_MAGIC , uc,0 ,0 );
tools/testing/selftests/kvm/lib/x86/ucall.c line 26
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +10
-0+} + +static void *ucall_tdx_get_ucall (struct kvm_vcpu *vcpu) +{ +struct kvm_run *run = vcpu->run; + +if (run->exit_reason ==KVM_EXIT_SYSTEM_EVENT && + run->system_event.type ==KVM_SYSTEM_EVENT_TDX_FATAL && + run->system_event.data[12 ] ==UCALL_TDX_MAGIC ) +return (void *)(run->system_event.data[13 ]);
Nit:
It's more readable to have comment for data[12] and data[13] to indicate they are the R12 and R13 passed.
Quoted 19 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +8
-0+ +return NULL ; +}static void ucall_x86_do_ucall (gva_t uc) {@@ -37,6 +57,11 @@ static struct {void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +if (is_tdx_vm (vm)) { + ucall_x86_ops.do_ucall = ucall_tdx_do_ucall; + ucall_x86_ops.get_ucall = ucall_tdx_get_ucall; + } +sync_global_to_guest (vm, ucall_x86_ops); }
PATCH v15 23/23 KVM: selftests: Add TDX lifecycle test
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Add a test to verify TDX lifecycle by creating a simple TDX VM.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 1 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 5 ++++ tools/testing/selftests/kvm/x86/tdx_vm_test.c | 33 ++++++++++++++++++++++ 4 files changed, 40 insertions(+)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -161,6 +161,7 @@ TEST_GEN_PROGS_x86 += x86/triple_fault_event_test TEST_GEN_PROGS_x86 += x86/recalc_apic_map_test TEST_GEN_PROGS_x86 += x86/aperfmperf_test TEST_GEN_PROGS_x86 += x86/guest_memfd_conversions_test +TEST_GEN_PROGS_x86 += x86/tdx_vm_test TEST_GEN_PROGS_x86 += access_tracking_perf_test TEST_GEN_PROGS_x86 += coalesced_io_test TEST_GEN_PROGS_x86 += dirty_log_perf_test
tools/testing/selftests/kvm/include/x86/processor.h +1 -0
@@ -393,6 +393,7 @@ static inline unsigned int x86_model(unsigned int eax)#define VM_SHAPE_SEV VM_TYPE(KVM_X86_SEV_VM)#define VM_SHAPE_SEV_ES VM_TYPE(KVM_X86_SEV_ES_VM)#define VM_SHAPE_SNP VM_TYPE(KVM_X86_SNP_VM) +#define VM_SHAPE_TDX VM_TYPE(KVM_X86_TDX_VM)#define PHYSICAL_PAGE_MASK GENMASK_ULL(51, 12)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +5 -0
@@ -11,6 +11,11 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)return vm->type ==KVM_X86_TDX_VM ; } +static inline bool is_tdx_supported (void ) +{ +return !!(kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_TDX_VM )); +} +/* * TDX ioctls * Use underscores to avoid collisions with struct member names.
tools/testing/selftests/kvm/x86/tdx_vm_test.c new file +33 -0
@@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "kvm_util.h" +#include "tdx/tdx_util.h" +#include "ucall_common.h" +#include "kselftest_harness.h" + +static void guest_code_lifecycle (void ) +{ +GUEST_DONE (); +} + +TEST (verify_td_lifecycle) +{ +struct kvm_vcpu *vcpu; +struct kvm_vm *vm; +struct ucall uc; + + vm =vm_create_shape_with_one_vcpu (VM_SHAPE_TDX , &vcpu, + guest_code_lifecycle); + +vcpu_run (vcpu); +TEST_ASSERT_EQ (get_ucall (vcpu, &uc),UCALL_DONE ); + +kvm_vm_free (vm); +} + +int main (int argc,char **argv) +{ +TEST_REQUIRE (is_tdx_supported ()); +return test_harness_run (argc, argv); +}
Signature · 1 line
2.56.0.rc1.315.gc6ed9934b7-goog
Re: [PATCH v15 23/23] KVM: selftests: Add TDX lifecycle test
Quoted 7 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add a test to verify TDX lifecycle by creating a simple TDX VM.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>