AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/10/02 15:27 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series only modifies user-space files under tools/ (specifically KVM selftests and tools/include/), which does not affect the compiled kernel image (vmlinux) or kernel modules.",
  "WorthFuzzing": false
}

1/1 2026/10/02 15:27 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit ca0265f290501b008991abdcc90a557449bf7eed\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Oct 2 15:27:32 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/include/linux/kbuild.h b/tools/include/linux/kbuild.h\nnew file mode 100644\nindex 0000000000000..957fd55cd159f\n--- /dev/null\n+++ b/tools/include/linux/kbuild.h\n@@ -0,0 +1,11 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef __TOOLS_LINUX_KBUILD_H\n+#define __TOOLS_LINUX_KBUILD_H\n+\n+#define DEFINE(sym, val) \\\n+\tasm volatile(\"\\n.ascii \\\"-\u003e\" #sym \" %0 \" #val \"\\\"\" : : \"i\" (val))\n+\n+#define OFFSET(sym, str, mem) \\\n+\tDEFINE(sym, __builtin_offsetof(struct str, mem))\n+\n+#endif /* __TOOLS_LINUX_KBUILD_H */\ndiff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore\nindex 1d41a046a7bfd..eef6055242b2e 100644\n--- a/tools/testing/selftests/kvm/.gitignore\n+++ b/tools/testing/selftests/kvm/.gitignore\n@@ -9,4 +9,5 @@\n !config\n !settings\n !Makefile\n-!Makefile.kvm\n\\ No newline at end of file\n+!Makefile.kvm\n+include/x86/**/*_offsets.h\n\\ No newline at end of file\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 239bc61ea384f..14cff95cf2913 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -20,6 +20,8 @@ LIBKVM += lib/userfaultfd_util.c\n \n LIBKVM_STRING += lib/string_override.c\n \n+LIBKVM_ASM_DEFS += lib/x86/tdx/td_boot_offsets.c\n+\n LIBKVM_x86 += lib/x86/apic.c\n LIBKVM_x86 += lib/x86/handlers.S\n LIBKVM_x86 += lib/x86/hyperv.c\n@@ -28,8 +30,11 @@ LIBKVM_x86 += lib/x86/pmu.c\n LIBKVM_x86 += lib/x86/processor.c\n LIBKVM_x86 += lib/x86/sev.c\n LIBKVM_x86 += lib/x86/svm.c\n+LIBKVM_x86 += lib/x86/tdx/tdx_util.c\n LIBKVM_x86 += lib/x86/ucall.c\n LIBKVM_x86 += lib/x86/vmx.c\n+LIBKVM_x86 += lib/x86/tdx/td_boot.S\n+LIBKVM_x86 += lib/x86/tdx/tdx.S\n \n LIBKVM_arm64 += lib/arm64/gic.c\n LIBKVM_arm64 += lib/arm64/gic_v3.c\n@@ -156,6 +161,7 @@ TEST_GEN_PROGS_x86 += x86/triple_fault_event_test\n TEST_GEN_PROGS_x86 += x86/recalc_apic_map_test\n TEST_GEN_PROGS_x86 += x86/aperfmperf_test\n TEST_GEN_PROGS_x86 += x86/guest_memfd_conversions_test\n+TEST_GEN_PROGS_x86 += x86/tdx_vm_test\n TEST_GEN_PROGS_x86 += access_tracking_perf_test\n TEST_GEN_PROGS_x86 += coalesced_io_test\n TEST_GEN_PROGS_x86 += dirty_log_perf_test\n@@ -268,6 +274,10 @@ OVERRIDE_TARGETS = 1\n include ../lib.mk\n include ../cgroup/lib/libcgroup.mk\n \n+# Enable Kbuild tools.\n+include $(top_srcdir)/scripts/Kbuild.include\n+include $(top_srcdir)/scripts/Makefile.lib\n+\n ifeq ($(ARCH),x86)\n include ../vfio/lib/libvfio.mk\n endif\n@@ -284,15 +294,24 @@ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \\\n \t-fno-stack-protector -fno-PIE -fno-strict-aliasing \\\n \t-I$(LINUX_TOOL_INCLUDE) -I$(LINUX_TOOL_ARCH_INCLUDE) \\\n \t-I$(LINUX_HDR_PATH) -Iinclude -I$(\u003cD) -Iinclude/$(ARCH) \\\n-\t-I ../rseq -I.. $(EXTRA_CFLAGS) $(KHDR_INCLUDES)\n+\t-I ../rseq -I.. -I$(OUTPUT)/include/$(ARCH) $(EXTRA_CFLAGS) $(KHDR_INCLUDES)\n ifeq ($(ARCH),s390)\n \tCFLAGS += -march=z10\n endif\n+\n ifeq ($(ARCH),x86)\n+\n ifeq ($(shell echo \"void foo(void) { }\" | $(CC) -march=x86-64-v2 -x c - -c -o /dev/null 2\u003e/dev/null; echo \"$$?\"),0)\n \tCFLAGS += -march=x86-64-v2\n endif\n+\n+KVM_GEN_HDRS := $(patsubst lib/x86/%.c, $(OUTPUT)/include/x86/%.h, $(filter lib/x86/%, $(LIBKVM_ASM_DEFS)))\n+$(shell mkdir -p $(sort $(dir $(KVM_GEN_HDRS))))\n+$(KVM_GEN_HDRS): GUARD = $(shell echo $(*F) | tr a-z A-Z | tr '.' '_')\n+$(KVM_GEN_HDRS): $(OUTPUT)/include/x86/%.h: $(OUTPUT)/lib/x86/%.s FORCE\n+\t$(call filechk,offsets,__$(GUARD)_H__)\n endif\n+\n ifeq ($(ARCH),arm64)\n tools_dir := $(top_srcdir)/tools\n arm64_tools_dir := $(tools_dir)/arch/arm64/tools/\n@@ -325,11 +344,8 @@ LIBKVM_S := $(filter %.S,$(LIBKVM))\n LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))\n LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))\n LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING))\n-LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ)\n-LIBKVM_OBJS += $(LIBCGROUP_O)\n-ifeq ($(ARCH),x86)\n-LIBKVM_OBJS += $(LIBVFIO_O)\n-endif\n+LIBKVM_ASM_DEFS_OBJ := $(patsubst %.c, $(OUTPUT)/%.s, $(LIBKVM_ASM_DEFS))\n+LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O) $(LIBVFIO_O)\n SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS))\n SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))\n \n@@ -354,7 +370,9 @@ $(SPLIT_TEST_GEN_OBJ): $(OUTPUT)/$(ARCH)/%.o: $(ARCH)/%.c\n \t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $\u003c -o $@\n \n EXTRA_CLEAN += $(GEN_HDRS) \\\n+\t       $(KVM_GEN_HDRS) \\\n \t       $(LIBKVM_OBJS) \\\n+\t       $(LIBKVM_ASM_DEFS_OBJ) \\\n \t       $(SPLIT_TEST_GEN_OBJ) \\\n \t       $(TEST_DEP_FILES) \\\n \t       $(TEST_GEN_OBJ) \\\n@@ -366,6 +384,9 @@ $(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c $(GEN_HDRS)\n $(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S $(GEN_HDRS)\n \t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $\u003c -o $@\n \n+$(LIBKVM_ASM_DEFS_OBJ): $(OUTPUT)/%.s: %.c FORCE\n+\t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -S $\u003c -o $@\n+\n # Compile the string overrides as freestanding to prevent the compiler from\n # generating self-referential code, e.g. without \"freestanding\" the compiler may\n # \"optimize\" memcmp() by invoking memcmp(), thus causing infinite recursion.\n@@ -374,11 +395,15 @@ $(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c\n \n $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))\n $(SPLIT_TEST_GEN_OBJ): $(GEN_HDRS)\n+$(LIBKVM_OBJS): $(KVM_GEN_HDRS)\n $(TEST_GEN_PROGS): $(LIBKVM_OBJS)\n $(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS)\n $(TEST_GEN_OBJ): $(GEN_HDRS)\n \n-cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib ..\n+FORCE:\n+\n+cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib .. \\\n+\t\t\t$(wildcard $(sort $(dir $(KVM_GEN_HDRS))))\n cscope:\n \t$(RM) cscope.*\n \t(find $(include_paths) -name '*.h' \\\ndiff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h\nindex 777fa3dbf88d6..426bc05fe62f3 100644\n--- a/tools/testing/selftests/kvm/include/kvm_util.h\n+++ b/tools/testing/selftests/kvm/include/kvm_util.h\n@@ -221,18 +221,6 @@ struct vm_shape {\n \n kvm_static_assert(sizeof(struct vm_shape) == sizeof(u64));\n \n-#define VM_TYPE_DEFAULT\t\t\t0\n-\n-#define VM_SHAPE(__mode)\t\t\t\\\n-({\t\t\t\t\t\t\\\n-\tstruct vm_shape shape = {\t\t\\\n-\t\t.mode = (__mode),\t\t\\\n-\t\t.type = VM_TYPE_DEFAULT\t\t\\\n-\t};\t\t\t\t\t\\\n-\t\t\t\t\t\t\\\n-\tshape;\t\t\t\t\t\\\n-})\n-\n extern enum vm_guest_mode vm_mode_default;\n \n #if defined(__aarch64__)\n@@ -270,8 +258,23 @@ extern enum vm_guest_mode vm_mode_default;\n \n #endif\n \n+#define VM_TYPE_DEFAULT\t\t\t0\n+\n+#define __VM_SHAPE(__mode, __type)\t\t\\\n+({\t\t\t\t\t\t\\\n+\tstruct vm_shape shape = {\t\t\\\n+\t\t.mode = (__mode),\t\t\\\n+\t\t.type = (__type),\t\t\\\n+\t};\t\t\t\t\t\\\n+\t\t\t\t\t\t\\\n+\tshape;\t\t\t\t\t\\\n+})\n+\n+#define VM_SHAPE(__mode)\t__VM_SHAPE(__mode, VM_TYPE_DEFAULT)\n #define VM_SHAPE_DEFAULT\tVM_SHAPE(VM_MODE_DEFAULT)\n \n+#define VM_TYPE(__type)\t\t__VM_SHAPE(VM_MODE_DEFAULT, __type)\n+\n #define MIN_PAGE_SIZE\t\t(1U \u003c\u003c MIN_PAGE_SHIFT)\n #define PTES_PER_MIN_PAGE\tptes_per_page(MIN_PAGE_SIZE)\n \ndiff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h\nindex 6e6f70035508a..61ae02d927c17 100644\n--- a/tools/testing/selftests/kvm/include/x86/processor.h\n+++ b/tools/testing/selftests/kvm/include/x86/processor.h\n@@ -30,6 +30,10 @@ extern struct kvm_mmu guest_mmu;\n #define MAX_NR_CPUID_ENTRIES 100\n #endif\n \n+#ifndef NUM_INTERRUPTS\n+#define NUM_INTERRUPTS 256\n+#endif\n+\n #define NONCANONICAL 0xaaaaaaaaaaaaaaaaull\n \n /* Forced emulation prefix, used to invoke the emulator unconditionally. */\n@@ -386,6 +390,11 @@ static inline unsigned int x86_model(unsigned int eax)\n \treturn ((eax \u003e\u003e 12) \u0026 0xf0) | ((eax \u003e\u003e 4) \u0026 0x0f);\n }\n \n+#define VM_SHAPE_SEV\t\tVM_TYPE(KVM_X86_SEV_VM)\n+#define VM_SHAPE_SEV_ES\t\tVM_TYPE(KVM_X86_SEV_ES_VM)\n+#define VM_SHAPE_SNP\t\tVM_TYPE(KVM_X86_SNP_VM)\n+#define VM_SHAPE_TDX\t\tVM_TYPE(KVM_X86_TDX_VM)\n+\n #define PHYSICAL_PAGE_MASK      GENMASK_ULL(51, 12)\n \n #define PAGE_SHIFT\t\t12\n@@ -1020,6 +1029,8 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs)\n \tvcpu_ioctl(vcpu, KVM_SET_XCRS, xcrs);\n }\n \n+const struct kvm_cpuid_entry2 *__get_cpuid_entry(const struct kvm_cpuid2 *cpuid,\n+\t\t\t\t\t\t u32 function, u32 index);\n const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,\n \t\t\t\t\t       u32 function, u32 index);\n const struct kvm_cpuid2 *kvm_get_supported_cpuid(void);\n@@ -1202,6 +1213,8 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu,\n \tvcpu_set_or_clear_cpuid_feature(vcpu, feature, false);\n }\n \n+gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm);\n+\n u64 vcpu_get_msr(struct kvm_vcpu *vcpu, u64 msr_index);\n int _vcpu_set_msr(struct kvm_vcpu *vcpu, u64 msr_index, u64 msr_value);\n \n@@ -1645,4 +1658,35 @@ u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa);\n \n bool sys_clocksource_is_based_on_tsc(void);\n \n+static inline u16 kvm_get_default_idt_limit(void)\n+{\n+\treturn NUM_INTERRUPTS * sizeof(struct idt_entry) - 1;\n+}\n+\n+static inline u16 kvm_get_default_gdt_limit(void)\n+{\n+\treturn getpagesize() - 1;\n+}\n+\n+static inline u64 kvm_get_default_cr0(void)\n+{\n+\treturn X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;\n+}\n+\n+static inline u64 kvm_get_default_cr4(int pgtable_levels)\n+{\n+\tu64 cr4 = X86_CR4_PAE | X86_CR4_OSFXSR;\n+\n+\tif (kvm_cpu_has(X86_FEATURE_XSAVE))\n+\t\tcr4 |= X86_CR4_OSXSAVE;\n+\tif (pgtable_levels == 5)\n+\t\tcr4 |= X86_CR4_LA57;\n+\treturn cr4;\n+}\n+\n+static inline u64 kvm_get_default_efer(void)\n+{\n+\treturn EFER_LME | EFER_LMA | EFER_NX;\n+}\n+\n #endif /* SELFTEST_KVM_PROCESSOR_H */\ndiff --git a/tools/testing/selftests/kvm/include/x86/processor_asm.h b/tools/testing/selftests/kvm/include/x86/processor_asm.h\nnew file mode 100644\nindex 0000000000000..713b6bc0aeb75\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/include/x86/processor_asm.h\n@@ -0,0 +1,12 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+/*\n+ * Used for storing defines used by both c and assembly code.\n+ */\n+#ifndef SELFTEST_KVM_PROCESSOR_ASM_H\n+#define SELFTEST_KVM_PROCESSOR_ASM_H\n+\n+#define KERNEL_CS\t0x8\n+#define KERNEL_DS\t0x10\n+#define KERNEL_TSS\t0x18\n+\n+#endif  /* SELFTEST_KVM_PROCESSOR_ASM_H */\ndiff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h\nindex dec383e59a47e..eb4ab573febfd 100644\n--- a/tools/testing/selftests/kvm/include/x86/sev.h\n+++ b/tools/testing/selftests/kvm/include/x86/sev.h\n@@ -53,8 +53,6 @@ void snp_vm_launch_start(struct kvm_vm *vm, u64 policy);\n void snp_vm_launch_update(struct kvm_vm *vm);\n void snp_vm_launch_finish(struct kvm_vm *vm);\n \n-struct kvm_vm *vm_sev_create_with_one_vcpu(u32 type, void *guest_code,\n-\t\t\t\t\t   struct kvm_vcpu **cpu);\n void vm_sev_launch(struct kvm_vm *vm, u64 policy, u8 *measurement);\n \n kvm_static_assert(SEV_RET_SUCCESS == 0);\ndiff --git a/tools/testing/selftests/kvm/include/x86/tdx/td_boot.h b/tools/testing/selftests/kvm/include/x86/tdx/td_boot.h\nnew file mode 100644\nindex 0000000000000..8f47a3893f59c\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/include/x86/tdx/td_boot.h\n@@ -0,0 +1,81 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+#ifndef SELFTEST_TDX_TD_BOOT_H\n+#define SELFTEST_TDX_TD_BOOT_H\n+\n+/*\n+ * Layout for boot section (not to scale)\n+ *\n+ *                                   GPA\n+ * _________________________________ 0x1_0000_0000 (4GB)\n+ * |   Boot code trampoline    |\n+ * |___________________________|____ 0x0_ffff_fff0: Reset vector (16B below 4GB)\n+ * |   Boot code               |\n+ * |___________________________|____ td_boot will be copied here, so that the\n+ * |                           |     jmp to td_boot is exactly at the reset vector\n+ * |   Empty space             |\n+ * |                           |\n+ * |───────────────────────────|\n+ * |                           |\n+ * |                           |\n+ * |   Boot parameters         |\n+ * |                           |\n+ * |                           |\n+ * |___________________________|____ 0x0_ffff_0000: TD_BOOT_PARAMETERS_GPA\n+ *\n+ * TD_BOOT_PARAMETERS_GPA is arbitrarily chosen to\n+ *\n+ * + be within the 4GB address space\n+ * + provide enough contiguous memory for the struct td_boot_parameters such\n+ *   that there is one struct td_per_vcpu_parameters for KVM_MAX_VCPUS\n+ */\n+#define TD_BOOT_PARAMETERS_GPA 0xffff0000\n+\n+#ifndef __ASSEMBLER__\n+\n+#include \u003clinux/compiler.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+/*\n+ * The exact memory layout for LGDT or LIDT instructions.\n+ */\n+struct __packed td_boot_parameters_dtr {\n+\tu16 limit;\n+\tu32 base;\n+};\n+\n+/*\n+ * Allows each vCPU to be initialized with different rip and esp.\n+ */\n+struct td_per_vcpu_parameters {\n+\tu32 esp_gva;\n+\tu64 guest_code;\n+};\n+\n+/*\n+ * Boot parameters for the TD.\n+ *\n+ * Unlike a regular VM, KVM cannot set registers such as esp, eip, etc\n+ * before boot, so to run selftests, these registers' values have to be\n+ * initialized by the TD.\n+ *\n+ * This struct is loaded in TD private memory at TD_BOOT_PARAMETERS_GPA.\n+ *\n+ * The TD boot code will read off parameters from this struct and set up the\n+ * vCPU for executing selftests.\n+ */\n+struct td_boot_parameters {\n+\tu32 cr0;\n+\tu32 cr3;\n+\tu32 cr4;\n+\tstruct td_boot_parameters_dtr gdtr;\n+\tstruct td_boot_parameters_dtr idtr;\n+\tstruct td_per_vcpu_parameters per_vcpu[];\n+};\n+\n+extern u8 td_boot[], td_boot_code_end[], td_boot_reset_vector_trampoline[];\n+\n+#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot)\n+\n+#endif /* #ifndef __ASSEMBLER__ */\n+\n+#endif /* SELFTEST_TDX_TD_BOOT_H */\ndiff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h\nnew file mode 100644\nindex 0000000000000..9982aaaba8859\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h\n@@ -0,0 +1,16 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+#ifndef SELFTEST_KVM_TDX_TDX_H\n+#define SELFTEST_KVM_TDX_TDX_H\n+\n+#include \u003clinux/types.h\u003e\n+\n+/* TDX hypercall Leaf IDs */\n+#define TDVMCALL_GET_TD_VM_CALL_INFO\t\t0x10000\n+#define TDVMCALL_MAP_GPA\t\t\t0x10001\n+#define TDVMCALL_GET_QUOTE\t\t\t0x10002\n+#define TDVMCALL_REPORT_FATAL_ERROR\t\t0x10003\n+#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT\t0x10004\n+\n+u64 __tdvmcall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15);\n+\n+#endif /* SELFTEST_KVM_TDX_TDX_H */\ndiff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h\nnew file mode 100644\nindex 0000000000000..85791fe16f688\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h\n@@ -0,0 +1,83 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+#ifndef SELFTESTS_TDX_TDX_UTIL_H\n+#define SELFTESTS_TDX_TDX_UTIL_H\n+\n+#include \u003cstdbool.h\u003e\n+\n+#include \"kvm_util.h\"\n+\n+static inline bool is_tdx_vm(struct kvm_vm *vm)\n+{\n+\treturn vm-\u003etype == KVM_X86_TDX_VM;\n+}\n+\n+static inline bool is_tdx_supported(void)\n+{\n+\treturn !!(kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(KVM_X86_TDX_VM));\n+}\n+\n+/*\n+ * TDX ioctls\n+ * Use underscores to avoid collisions with struct member names.\n+ */\n+#define __tdx_vm_ioctl(vm, cmd, _flags, arg, hw_err)\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tu64 *__hw_err = (hw_err);\t\t\t\t\t\\\n+\tint r;\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tunion {\t\t\t\t\t\t\t\t\\\n+\t\tstruct kvm_tdx_cmd c;\t\t\t\t\t\\\n+\t\tunsigned long raw;\t\t\t\t\t\\\n+\t} tdx_cmd = { .c = {\t\t\t\t\t\t\\\n+\t\t.id = (cmd),\t\t\t\t\t\t\\\n+\t\t.flags = (u32)(_flags),\t\t\t\t\t\\\n+\t\t.data = (u64)(arg),\t\t\t\t\t\\\n+\t} };\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tr = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, \u0026tdx_cmd.raw);\t\\\n+\tif (__hw_err)\t\t\t\t\t\t\t\\\n+\t\t*__hw_err = tdx_cmd.c.hw_error;\t\t\t\t\\\n+\tr;\t\t\t\t\t\t\t\t\\\n+})\n+\n+#define tdx_vm_ioctl(vm, cmd, flags, arg)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tu64 hw_error;\t\t\t\t\t\t\t\\\n+\tint ret = __tdx_vm_ioctl(vm, cmd, flags, arg, \u0026hw_error);\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tTEST_ASSERT(!ret,\t\t\t\t\t\t\\\n+\t\t    \"%s failed, rc: %d errno: %i (%s) hw_error: 0x%llx\",\\\n+\t\t    #cmd, ret, errno, strerror(errno),\t\t\t\\\n+\t\t    (unsigned long long)hw_error);\t\t\t\\\n+})\n+\n+#define __tdx_vcpu_ioctl(vcpu, cmd, _flags, arg)\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tunion {\t\t\t\t\t\t\t\t\\\n+\t\tstruct kvm_tdx_cmd c;\t\t\t\t\t\\\n+\t\tunsigned long raw;\t\t\t\t\t\\\n+\t} tdx_cmd = { .c = {\t\t\t\t\t\t\\\n+\t\t.id = (cmd),\t\t\t\t\t\t\\\n+\t\t.flags = (u32)(_flags),\t\t\t\t\t\\\n+\t\t.data = (u64)(arg),\t\t\t\t\t\\\n+\t} };\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\t__vcpu_ioctl(vcpu, KVM_MEMORY_ENCRYPT_OP, \u0026tdx_cmd.raw);\t\\\n+})\n+\n+#define tdx_vcpu_ioctl(vcpu, cmd, flags, arg)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tint ret = __tdx_vcpu_ioctl(vcpu, cmd, flags, arg);\t\t\\\n+\tTEST_ASSERT(!ret, \"%s failed, errno: %d (%s)\",\t\t\t\\\n+\t\t     #cmd, errno, strerror(errno));\t\t\t\\\n+})\n+void tdx_init_vm(struct kvm_vm *vm);\n+void tdx_vm_setup_boot_code_region(struct kvm_vm *vm);\n+void tdx_vm_setup_boot_parameters_region(struct kvm_vm *vm, u32 nr_runnable_vcpus);\n+void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm);\n+void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu);\n+void tdx_vcpu_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code);\n+\n+void tdx_vm_finalize(struct kvm_vm *vm);\n+\n+#endif /* SELFTESTS_TDX_TDX_UTIL_H */\ndiff --git a/tools/testing/selftests/kvm/include/x86/ucall.h b/tools/testing/selftests/kvm/include/x86/ucall.h\nindex 0e4950041e3ee..3639f03a4da97 100644\n--- a/tools/testing/selftests/kvm/include/x86/ucall.h\n+++ b/tools/testing/selftests/kvm/include/x86/ucall.h\n@@ -2,12 +2,8 @@\n #ifndef SELFTEST_KVM_UCALL_H\n #define SELFTEST_KVM_UCALL_H\n \n-#include \"kvm_util.h\"\n+#include \"linux/kvm.h\"\n \n #define UCALL_EXIT_REASON       KVM_EXIT_IO\n \n-static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)\n-{\n-}\n-\n #endif\ndiff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c\nindex 3f559f207408f..f474664b5d924 100644\n--- a/tools/testing/selftests/kvm/lib/kvm_util.c\n+++ b/tools/testing/selftests/kvm/lib/kvm_util.c\n@@ -482,7 +482,7 @@ void kvm_set_files_rlimit(u32 nr_vcpus)\n static bool is_guest_memfd_required(struct vm_shape shape)\n {\n #ifdef __x86_64__\n-\treturn shape.type == KVM_X86_SNP_VM;\n+\treturn shape.type == KVM_X86_SNP_VM || shape.type == KVM_X86_TDX_VM;\n #else\n \treturn false;\n #endif\n@@ -494,8 +494,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,\n \tu64 nr_pages = vm_nr_pages_required(shape.mode, nr_runnable_vcpus,\n \t\t\t\t\t\t nr_extra_pages);\n \tstruct userspace_mem_region *slot0;\n+\tu64 gmem_flags = 0;\n \tstruct kvm_vm *vm;\n-\tint i, flags;\n+\tint flags = 0;\n+\tint i;\n \n \tkvm_set_files_rlimit(nr_runnable_vcpus);\n \n@@ -505,14 +507,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,\n \tvm = ____vm_create(shape);\n \n \t/*\n-\t * Force GUEST_MEMFD for the primary memory region if necessary, e.g.\n-\t * for CoCo VMs that require GUEST_MEMFD backed private memory.\n+\t * Force GUEST_MEMFD for the primary memory region if necessary, and\n+\t * initialize it as shared if in-place conversion is supported, so the\n+\t * selftest framework can populate it exactly like other memory\n+\t * providers.\n \t */\n-\tflags = 0;\n-\tif (is_guest_memfd_required(shape))\n+\tif (is_guest_memfd_required(shape)) {\n \t\tflags |= KVM_MEM_GUEST_MEMFD;\n+\t\tif (kvm_has_gmem_attributes)\n+\t\t\tgmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED |\n+\t\t\t\t      GUEST_MEMFD_FLAG_MMAP;\n+\t}\n \n-\tvm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags);\n+\tvm_mem_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags,\n+\t\t   -1, 0, gmem_flags);\n \tfor (i = 0; i \u003c NR_MEM_REGIONS; i++)\n \t\tvm-\u003ememslots[i] = 0;\n \ndiff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c\nindex 029ce21f9f2fa..c03dfc7631726 100644\n--- a/tools/testing/selftests/kvm/lib/ucall_common.c\n+++ b/tools/testing/selftests/kvm/lib/ucall_common.c\n@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;\n \n void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa)\n {\n+\tstruct userspace_mem_region *region;\n \tstruct ucall_header *hdr;\n \tstruct ucall *uc;\n \tgva_t gva;\n@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa)\n \thdr = (struct ucall_header *)addr_gva2hva(vm, gva);\n \tmemset(hdr, 0, sizeof(*hdr));\n \n+\tregion = vm_get_mem_region(vm, MEM_REGION_DATA);\n+\tif (region-\u003eregion.flags \u0026 KVM_MEM_GUEST_MEMFD) {\n+\t\tsize_t aligned_sz = align_up(sizeof(*hdr), vm-\u003epage_size);\n+\n+\t\tvm_mem_set_shared(vm, addr_gva2gpa(vm, gva), aligned_sz);\n+\t}\n+\n \tfor (i = 0; i \u003c KVM_MAX_VCPUS; ++i) {\n \t\tuc = \u0026hdr-\u003eucalls[i];\n \t\tuc-\u003ehva = uc;\ndiff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c\nindex d31fa81ea0756..e1d0fd5ed1f68 100644\n--- a/tools/testing/selftests/kvm/lib/x86/processor.c\n+++ b/tools/testing/selftests/kvm/lib/x86/processor.c\n@@ -8,19 +8,13 @@\n #include \"kvm_util.h\"\n #include \"pmu.h\"\n #include \"processor.h\"\n+#include \"processor_asm.h\"\n #include \"smm.h\"\n #include \"svm_util.h\"\n #include \"sev.h\"\n+#include \"tdx/tdx_util.h\"\n #include \"vmx.h\"\n \n-#ifndef NUM_INTERRUPTS\n-#define NUM_INTERRUPTS 256\n-#endif\n-\n-#define KERNEL_CS\t0x8\n-#define KERNEL_DS\t0x10\n-#define KERNEL_TSS\t0x18\n-\n gva_t exception_handlers;\n bool host_cpu_is_amd;\n bool host_cpu_is_intel;\n@@ -652,17 +646,13 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu)\n \tvcpu_sregs_get(vcpu, \u0026sregs);\n \n \tsregs.idt.base = vm-\u003earch.idt;\n-\tsregs.idt.limit = NUM_INTERRUPTS * sizeof(struct idt_entry) - 1;\n+\tsregs.idt.limit = kvm_get_default_idt_limit();\n \tsregs.gdt.base = vm-\u003earch.gdt;\n-\tsregs.gdt.limit = getpagesize() - 1;\n+\tsregs.gdt.limit = kvm_get_default_gdt_limit();\n \n-\tsregs.cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;\n-\tsregs.cr4 |= X86_CR4_PAE | X86_CR4_OSFXSR;\n-\tif (kvm_cpu_has(X86_FEATURE_XSAVE))\n-\t\tsregs.cr4 |= X86_CR4_OSXSAVE;\n-\tif (vm-\u003emmu.pgtable_levels == 5)\n-\t\tsregs.cr4 |= X86_CR4_LA57;\n-\tsregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);\n+\tsregs.cr0 = kvm_get_default_cr0();\n+\tsregs.cr4 |= kvm_get_default_cr4(vm-\u003emmu.pgtable_levels);\n+\tsregs.efer |= kvm_get_default_efer();\n \n \tkvm_seg_set_unusable(\u0026sregs.ldt);\n \tkvm_seg_set_kernel_code_64bit(\u0026sregs.cs);\n@@ -828,6 +818,13 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)\n \t\tvm_sev_ioctl(vm, KVM_SEV_INIT2, \u0026init);\n \t}\n \n+\tif (is_tdx_vm(vm)) {\n+\t\ttdx_init_vm(vm);\n+\t\ttdx_vm_setup_boot_code_region(vm);\n+\t\ttdx_vm_setup_boot_parameters_region(vm, nr_vcpus);\n+\t\ttdx_vm_load_common_boot_parameters(vm);\n+\t}\n+\n \tr = __vm_ioctl(vm, KVM_GET_TSC_KHZ, NULL);\n \tTEST_ASSERT(r \u003e 0, \"KVM_GET_TSC_KHZ did not provide a valid TSC frequency.\");\n \tguest_tsc_khz = r;\n@@ -849,27 +846,84 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)\n {\n \tstruct kvm_regs regs;\n \n-\tvcpu_regs_get(vcpu, \u0026regs);\n-\tregs.rip = (unsigned long) guest_code;\n-\tvcpu_regs_set(vcpu, \u0026regs);\n+\tif (is_tdx_vm(vcpu-\u003evm)) {\n+\t\ttdx_vcpu_set_entry_point(vcpu, guest_code);\n+\t} else {\n+\t\tvcpu_regs_get(vcpu, \u0026regs);\n+\t\tregs.rip = (unsigned long)guest_code;\n+\t\tvcpu_regs_set(vcpu, \u0026regs);\n+\t}\n }\n \n-struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)\n+gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm)\n {\n \tstruct kvm_mp_state mp_state;\n \tstruct kvm_regs regs;\n+\tgva_t stack_gva;\n \tstruct kvm_vcpu *vcpu;\n \n+\tstack_gva = __vm_alloc(vm, DEFAULT_STACK_PGS * getpagesize(),\n+\t\t\t       DEFAULT_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA);\n+\n+\tstack_gva += DEFAULT_STACK_PGS * getpagesize();\n+\n+\t/*\n+\t * Align stack to match calling sequence requirements in section \"The\n+\t * Stack Frame\" of the System V ABI AMD64 Architecture Processor\n+\t * Supplement, which requires the value (%rsp + 8) to be a multiple of\n+\t * 16 when control is transferred to the function entry point.\n+\t *\n+\t * If this code is ever used to launch a vCPU with 32-bit entry point it\n+\t * may need to subtract 4 bytes instead of 8 bytes.\n+\t */\n+\tTEST_ASSERT(IS_ALIGNED(stack_gva, PAGE_SIZE),\n+\t\t    \"__vm_alloc() did not provide a page-aligned address\");\n+\tstack_gva -= 8;\n+\n+\treturn stack_gva;\n+}\n+\n+static void tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)\n+{\n+\tstruct kvm_cpuid2 *cpuid;\n+\n+\tcpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);\n+\ttdx_vcpu_ioctl(vcpu, KVM_TDX_GET_CPUID, 0, cpuid);\n+\tvcpu_init_cpuid(vcpu, cpuid);\n+\tfree(cpuid);\n+\ttdx_vcpu_ioctl(vcpu, KVM_TDX_INIT_VCPU, 0, NULL);\n+\n+\ttdx_vcpu_load_boot_parameters(vm, vcpu);\n+}\n+\n+struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)\n+{\n+\tstruct kvm_mp_state mp_state;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_regs regs;\n+\n \tvcpu = __vm_vcpu_add(vm, vcpu_id);\n-\tvcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());\n-\tvcpu_init_sregs(vm, vcpu);\n-\tvcpu_init_xcrs(vm, vcpu);\n \n-\t/* Setup guest general purpose registers */\n-\tvcpu_regs_get(vcpu, \u0026regs);\n-\tregs.rflags = regs.rflags | X86_EFLAGS_FIXED;\n-\tregs.rsp = vm_alloc_stack(vm, DEFAULT_STACK_PGS);\n-\tvcpu_regs_set(vcpu, \u0026regs);\n+\t/*\n+\t * Both kvm_get_supported_cpuid() (for legacy VMs) and KVM_TDX_GET_CPUID\n+\t * (for TDX VMs) return VM-scoped CPUID. e.g. the APIC ID isn't\n+\t * populated per vCPU. This is fine because KVM selftests don't\n+\t * currently test CPUID topology enumeration.\n+\t */\n+\tif (is_tdx_vm(vm)) {\n+\t\ttdx_vcpu_init(vm, vcpu);\n+\t} else {\n+\t\tvcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());\n+\n+\t\tvcpu_init_sregs(vm, vcpu);\n+\t\tvcpu_init_xcrs(vm, vcpu);\n+\n+\t\t/* Setup guest general purpose registers */\n+\t\tvcpu_regs_get(vcpu, \u0026regs);\n+\t\tregs.rflags = regs.rflags | 0x2;\n+\t\tregs.rsp = kvm_allocate_vcpu_stack(vm);\n+\t\tvcpu_regs_set(vcpu, \u0026regs);\n+\t}\n \n \t/* Setup the MP state */\n \tmp_state.mp_state = 0;\n@@ -1330,17 +1384,24 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)\n \n void kvm_init_vm_address_properties(struct kvm_vm *vm)\n {\n+\tu32 gpa_bits = kvm_cpu_property(X86_PROPERTY_GUEST_MAX_PHY_ADDR);\n+\n+\tvm-\u003earch.sev_fd = -1;\n+\n \tif (is_sev_vm(vm)) {\n \t\tvm-\u003earch.sev_fd = open_sev_dev_path_or_exit();\n \t\tvm-\u003earch.c_bit = BIT_ULL(this_cpu_property(X86_PROPERTY_SEV_C_BIT));\n \t\tvm-\u003egpa_tag_mask = vm-\u003earch.c_bit;\n-\t} else {\n-\t\tvm-\u003earch.sev_fd = -1;\n+\t} else if (is_tdx_vm(vm)) {\n+\t\tTEST_ASSERT(gpa_bits == 48 || gpa_bits == 52,\n+\t\t\t    \"TDX: bad X86_PROPERTY_GUEST_MAX_PHY_ADDR value: %u\", gpa_bits);\n+\t\tvm-\u003earch.s_bit = BIT_ULL(gpa_bits - 1);\n+\t\tvm-\u003egpa_tag_mask = vm-\u003earch.s_bit;\n \t}\n }\n \n-const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,\n-\t\t\t\t\t       u32 function, u32 index)\n+const struct kvm_cpuid_entry2 *__get_cpuid_entry(const struct kvm_cpuid2 *cpuid,\n+\t\t\t\t\t\t u32 function, u32 index)\n {\n \tint i;\n \n@@ -1350,11 +1411,21 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,\n \t\t\treturn \u0026cpuid-\u003eentries[i];\n \t}\n \n-\tTEST_FAIL(\"CPUID function 0x%x index 0x%x not found \", function, index);\n-\n \treturn NULL;\n }\n \n+const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,\n+\t\t\t\t\t       u32 function, u32 index)\n+{\n+\tconst struct kvm_cpuid_entry2 *entry;\n+\n+\tentry = __get_cpuid_entry(cpuid, function, index);\n+\tif (!entry)\n+\t\tTEST_FAIL(\"CPUID function 0x%x index 0x%x not found \", function, index);\n+\n+\treturn entry;\n+}\n+\n #define X86_HYPERCALL(inputs...)\t\t\t\t\t\\\n ({\t\t\t\t\t\t\t\t\t\\\n \tu64 r;\t\t\t\t\t\t\t\\\n@@ -1465,6 +1536,12 @@ bool kvm_arch_has_default_irqchip(void)\n \treturn true;\n }\n \n+void kvm_arch_vm_finalize_vcpus(struct kvm_vm *vm)\n+{\n+\tif (is_tdx_vm(vm))\n+\t\ttdx_vm_finalize(vm);\n+}\n+\n void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, u64 smram_gpa,\n \t\t const void *smi_handler, size_t handler_size)\n {\ndiff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c\nindex 93f9169034617..95d8520eea34a 100644\n--- a/tools/testing/selftests/kvm/lib/x86/sev.c\n+++ b/tools/testing/selftests/kvm/lib/x86/sev.c\n@@ -158,22 +158,6 @@ void snp_vm_launch_finish(struct kvm_vm *vm)\n \tvm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_FINISH, \u0026launch_finish);\n }\n \n-struct kvm_vm *vm_sev_create_with_one_vcpu(u32 type, void *guest_code,\n-\t\t\t\t\t   struct kvm_vcpu **cpu)\n-{\n-\tstruct vm_shape shape = {\n-\t\t.mode = VM_MODE_DEFAULT,\n-\t\t.type = type,\n-\t};\n-\tstruct kvm_vm *vm;\n-\tstruct kvm_vcpu *cpus[1];\n-\n-\tvm = __vm_create_with_vcpus(shape, 1, 0, guest_code, cpus);\n-\t*cpu = cpus[0];\n-\n-\treturn vm;\n-}\n-\n void vm_sev_launch(struct kvm_vm *vm, u64 policy, u8 *measurement)\n {\n \tif (is_sev_snp_vm(vm)) {\ndiff --git a/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S\nnew file mode 100644\nindex 0000000000000..a04011b91ae4e\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S\n@@ -0,0 +1,71 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+\n+#include \"tdx/td_boot.h\"\n+#include \"tdx/td_boot_offsets.h\"\n+#include \"processor_asm.h\"\n+\n+.code32\n+\n+.globl td_boot\n+td_boot:\n+\t/*\n+\t * Paging is turned off.\n+\t *\n+\t * Register usage throughout td_boot:\n+\t * ebx: Address of global boot parameters (Must be preserved)\n+\t * eax: Address of per-vCPU parameters (Must be preserved)\n+\t * edi, edx: Temporary scratch registers\n+\t */\n+\tcli\n+\n+\tmovl $TD_BOOT_PARAMETERS_GPA, %ebx\n+\n+\t/*\n+\t * Find the address of struct td_per_vcpu_parameters for this\n+\t * vCPU based on esi (TDX spec: initialized with vCPU id). Put\n+\t * struct address into eax for indirect addressing.\n+\t */\n+\tmovl $SIZEOF_TD_PER_VCPU_PARAMETERS, %eax\n+\tmul %esi\n+\tleal TD_BOOT_PARAMETERS_PER_VCPU(%ebx), %edi\n+\taddl %edi, %eax\n+\n+\t/* Setup stack. */\n+\tmovl TD_PER_VCPU_PARAMETERS_ESP_GVA(%eax), %esp\n+\n+\t/* Setup GDT. */\n+\tleal TD_BOOT_PARAMETERS_GDT(%ebx), %edi\n+\tlgdt (%edi)\n+\n+\t/* Setup IDT. */\n+\tleal TD_BOOT_PARAMETERS_IDT(%ebx), %edi\n+\tlidt (%edi)\n+\n+\t/*\n+\t * Set up control registers (x86 does not support moving memory\n+\t * directly to a control register).\n+\t */\n+\tmovl TD_BOOT_PARAMETERS_CR4(%ebx), %edi\n+\tmovl %edi, %cr4\n+\tmovl TD_BOOT_PARAMETERS_CR3(%ebx), %edi\n+\tmovl %edi, %cr3\n+\tmovl TD_BOOT_PARAMETERS_CR0(%ebx), %edi\n+\tmovl %edi, %cr0\n+\n+\t/* Switching to 64bit mode after ljmp and then jump to guest code */\n+\tljmp $(KERNEL_CS),$1f\n+1:\n+\tjmp *TD_PER_VCPU_PARAMETERS_GUEST_CODE(%eax)\n+\tint3\n+\n+.globl td_boot_reset_vector_trampoline\n+td_boot_reset_vector_trampoline:\n+\tjmp td_boot\n+\tint3\n+\n+/* Leave marker so size of td_boot code can be computed. */\n+.globl td_boot_code_end\n+td_boot_code_end:\n+\n+/* Disable executable stack. */\n+.section .note.GNU-stack,\"\",%progbits\ndiff --git a/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c\nnew file mode 100644\nindex 0000000000000..7f76a3585b99a\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c\n@@ -0,0 +1,21 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#define COMPILE_OFFSETS\n+\n+#include \u003clinux/kbuild.h\u003e\n+\n+#include \"tdx/td_boot.h\"\n+\n+static void __attribute__((used)) common(void)\n+{\n+\tOFFSET(TD_BOOT_PARAMETERS_CR0, td_boot_parameters, cr0);\n+\tOFFSET(TD_BOOT_PARAMETERS_CR3, td_boot_parameters, cr3);\n+\tOFFSET(TD_BOOT_PARAMETERS_CR4, td_boot_parameters, cr4);\n+\tOFFSET(TD_BOOT_PARAMETERS_GDT, td_boot_parameters, gdtr);\n+\tOFFSET(TD_BOOT_PARAMETERS_IDT, td_boot_parameters, idtr);\n+\tOFFSET(TD_BOOT_PARAMETERS_PER_VCPU, td_boot_parameters, per_vcpu);\n+\tOFFSET(TD_PER_VCPU_PARAMETERS_ESP_GVA, td_per_vcpu_parameters, esp_gva);\n+\tOFFSET(TD_PER_VCPU_PARAMETERS_GUEST_CODE, td_per_vcpu_parameters,\n+\t       guest_code);\n+\tDEFINE(SIZEOF_TD_PER_VCPU_PARAMETERS,\n+\t       sizeof(struct td_per_vcpu_parameters));\n+}\ndiff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx.S b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.S\nnew file mode 100644\nindex 0000000000000..e3f0f7f2232bf\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.S\n@@ -0,0 +1,39 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+\n+.globl __tdvmcall\n+/*\n+ * u64 __tdvmcall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15)\n+ */\n+__tdvmcall:\n+\tpush %r12\n+\tpush %r13\n+\tpush %r14\n+\tpush %r15\n+\n+\t/*\n+\t * Map C ABI fast arguments to TDX GHCI payload registers\n+\t * RDI=fn, RSI=r12, RDX=r13, RCX=r14, R8=r15\n+\t */\n+\tmov %rdi, %r11\n+\tmov %rsi, %r12\n+\tmov %rdx, %r13\n+\tmov %rcx, %r14\n+\tmov %r8,  %r15\n+\n+\tmov $0, %rax\t\t/* TDCALL leaf 0: TDG.VP.VMCALL */\n+\tmov $0xFC00, %rcx\t/* Expose R10-R15 */\n+\tmov $0, %r10\t\t/* Standard GHCI call */\n+\n+\t/* TDCALL instruction */\n+\t.byte 0x66, 0x0f, 0x01, 0xcc\n+\n+\t/* Extract status */\n+\tmov %r10, %rax\n+\n+\tpop %r15\n+\tpop %r14\n+\tpop %r13\n+\tpop %r12\n+\tret\n+\n+.section .note.GNU-stack,\"\",%progbits\ndiff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c\nnew file mode 100644\nindex 0000000000000..b8e1f848cf548\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c\n@@ -0,0 +1,292 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+#include \u003clinux/align.h\u003e\n+#include \u003clinux/sizes.h\u003e\n+\n+#include \"processor.h\"\n+#include \"tdx/td_boot.h\"\n+#include \"tdx/tdx_util.h\"\n+\n+/* Arbitrarily selected to avoid overlaps with anything else */\n+#define TD_BOOT_CODE_SLOT\t20\n+#define TD_BOOT_PARAMETERS_SLOT\t21\n+\n+#define X86_RESET_VECTOR\t0xfffffff0ul\n+\n+void tdx_vm_setup_boot_code_region(struct kvm_vm *vm)\n+{\n+\tconst size_t total_size = td_boot_code_end - td_boot;\n+\tconst size_t boot_code_size = td_boot_reset_vector_trampoline - td_boot;\n+\tconst gpa_t boot_code_gpa = X86_RESET_VECTOR - boot_code_size;\n+\tgpa_t alloc_gpa = round_down(boot_code_gpa, PAGE_SIZE);\n+\tsize_t nr_pages = (SZ_4G - alloc_gpa) / PAGE_SIZE;\n+\tu64 gmem_flags = 0;\n+\tgpa_t gpa;\n+\tu8 *hva;\n+\n+\tif (kvm_has_gmem_attributes)\n+\t\tgmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED | GUEST_MEMFD_FLAG_MMAP;\n+\tvm_mem_add(vm, VM_MEM_SRC_SHMEM, alloc_gpa, TD_BOOT_CODE_SLOT,\n+\t\t   nr_pages, KVM_MEM_GUEST_MEMFD, -1, 0, gmem_flags);\n+\n+\tgpa = vm_phy_pages_alloc(vm, nr_pages, alloc_gpa, TD_BOOT_CODE_SLOT);\n+\tTEST_ASSERT(gpa == alloc_gpa, \"Failed vm_phy_pages_alloc\\n\");\n+\n+\tvirt_map(vm, alloc_gpa, alloc_gpa, nr_pages);\n+\thva = addr_gpa2hva(vm, boot_code_gpa);\n+\tmemcpy(hva, td_boot, total_size);\n+\n+\thva += boot_code_size;\n+\tTEST_ASSERT(hva == addr_gpa2hva(vm, X86_RESET_VECTOR),\n+\t\t    \"Expected RESET vector at hva 0x%lx, got %lx\",\n+\t\t    (unsigned long)addr_gpa2hva(vm, X86_RESET_VECTOR), (unsigned long)hva);\n+}\n+\n+void tdx_vm_setup_boot_parameters_region(struct kvm_vm *vm, u32 nr_runnable_vcpus)\n+{\n+\tsize_t boot_params_size = sizeof(struct td_boot_parameters) +\n+\t\tnr_runnable_vcpus * sizeof(struct td_per_vcpu_parameters);\n+\tint npages = DIV_ROUND_UP(boot_params_size, PAGE_SIZE);\n+\tu64 gmem_flags = 0;\n+\tgpa_t gpa;\n+\n+\tif (kvm_has_gmem_attributes)\n+\t\tgmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED | GUEST_MEMFD_FLAG_MMAP;\n+\tvm_mem_add(vm, VM_MEM_SRC_SHMEM, TD_BOOT_PARAMETERS_GPA,\n+\t\t   TD_BOOT_PARAMETERS_SLOT, npages,\n+\t\t   KVM_MEM_GUEST_MEMFD, -1, 0, gmem_flags);\n+\tgpa = vm_phy_pages_alloc(vm, npages, TD_BOOT_PARAMETERS_GPA, TD_BOOT_PARAMETERS_SLOT);\n+\tTEST_ASSERT(gpa == TD_BOOT_PARAMETERS_GPA, \"Failed vm_phy_pages_alloc\\n\");\n+\n+\tvirt_map(vm, TD_BOOT_PARAMETERS_GPA, TD_BOOT_PARAMETERS_GPA, npages);\n+}\n+\n+void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm)\n+{\n+\tstruct td_boot_parameters *params = addr_gpa2hva(vm, TD_BOOT_PARAMETERS_GPA);\n+\tu32 cr4;\n+\n+\tcr4 = kvm_get_default_cr4(vm-\u003emmu.pgtable_levels);\n+\n+\t/* Per the TDX spec, CR4 bit MCE is fixed to 1 */\n+\tcr4 |= X86_CR4_MCE;\n+\n+\t/* Per the TDX spec, CR4 bit VMXE and SMXE are fixed to 0 */\n+\tcr4 \u0026= ~(X86_CR4_VMXE | X86_CR4_SMXE);\n+\n+\t/* Set parameters! */\n+\tparams-\u003ecr0 = kvm_get_default_cr0();\n+\tTEST_ASSERT(vm-\u003emmu.pgd \u003c (1ULL \u003c\u003c 32),\n+\t\t    \"PGD must be within 32-bit address space for 32-bit boot code\");\n+\tparams-\u003ecr3 = vm-\u003emmu.pgd;\n+\tparams-\u003ecr4 = cr4;\n+\tparams-\u003eidtr.base = vm-\u003earch.idt;\n+\tparams-\u003eidtr.limit = kvm_get_default_idt_limit();\n+\tparams-\u003egdtr.base = vm-\u003earch.gdt;\n+\tparams-\u003egdtr.limit = kvm_get_default_gdt_limit();\n+\n+\tTEST_ASSERT(params-\u003eidtr.base \u003c (1ULL \u003c\u003c 32),\n+\t\t    \"idt must be within 32-bit address space for 32-bit boot code\");\n+\tTEST_ASSERT(params-\u003egdtr.base \u003c (1ULL \u003c\u003c 32),\n+\t\t    \"gdt must be within 32-bit address space for 32-bit boot code\");\n+}\n+\n+void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu)\n+{\n+\tstruct td_boot_parameters *params =\n+\t\taddr_gpa2hva(vm, TD_BOOT_PARAMETERS_GPA);\n+\tstruct td_per_vcpu_parameters *vcpu_params =\n+\t\t\u0026params-\u003eper_vcpu[vcpu-\u003eid];\n+\n+\tvcpu_params-\u003eesp_gva = kvm_allocate_vcpu_stack(vm);\n+\tTEST_ASSERT(vcpu_params-\u003eesp_gva \u003c (1ULL \u003c\u003c 32),\n+\t\t    \"ESP GVA must be within 32-bit address space for 32-bit boot code\");\n+}\n+\n+void tdx_vcpu_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)\n+{\n+\tstruct td_boot_parameters *params =\n+\t\taddr_gpa2hva(vcpu-\u003evm, TD_BOOT_PARAMETERS_GPA);\n+\tstruct td_per_vcpu_parameters *vcpu_params =\n+\t\t\u0026params-\u003eper_vcpu[vcpu-\u003eid];\n+\n+\tvcpu_params-\u003eguest_code = (u64)guest_code;\n+}\n+\n+static const struct kvm_tdx_capabilities *tdx_read_capabilities(struct kvm_vm *vm)\n+{\n+\tstatic struct kvm_tdx_capabilities *tdx_cap;\n+\tint nr_cpuid_configs = 4;\n+\tint rc = -1;\n+\tint i;\n+\n+\tif (tdx_cap)\n+\t\treturn tdx_cap;\n+\n+\tdo {\n+\t\tnr_cpuid_configs *= 2;\n+\n+\t\ttdx_cap = realloc(tdx_cap, sizeof(*tdx_cap) +\n+\t\t\t\t\t   (sizeof(struct kvm_cpuid_entry2) * nr_cpuid_configs));\n+\t\tTEST_ASSERT(tdx_cap,\n+\t\t\t    \"Could not allocate memory for tdx capability nr_cpuid_configs %d\\n\",\n+\t\t\t    nr_cpuid_configs);\n+\n+\t\ttdx_cap-\u003ecpuid.nent = nr_cpuid_configs;\n+\t\trc = __tdx_vm_ioctl(vm, KVM_TDX_CAPABILITIES, 0, tdx_cap, NULL);\n+\t} while (rc \u003c 0 \u0026\u0026 errno == E2BIG);\n+\n+\tTEST_ASSERT(rc == 0, \"KVM_TDX_CAPABILITIES failed: %d %d\",\n+\t\t    rc, errno);\n+\n+\tpr_debug(\"tdx_cap: supported_attrs: 0x%016llx\\n\"\n+\t\t \"tdx_cap: supported_xfam 0x%016llx\\n\",\n+\t\t tdx_cap-\u003esupported_attrs, tdx_cap-\u003esupported_xfam);\n+\n+\tfor (i = 0; i \u003c tdx_cap-\u003ecpuid.nent; i++) {\n+\t\tconst struct kvm_cpuid_entry2 *config = \u0026tdx_cap-\u003ecpuid.entries[i];\n+\n+\t\tpr_debug(\"cpuid config[%d]: leaf 0x%x sub_leaf 0x%x eax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\\n\",\n+\t\t\t i, config-\u003efunction, config-\u003eindex,\n+\t\t\t config-\u003eeax, config-\u003eebx, config-\u003eecx, config-\u003eedx);\n+\t}\n+\n+\treturn tdx_cap;\n+}\n+\n+/*\n+ * Filter CPUID based on TDX supported capabilities\n+ *\n+ * Input Args:\n+ *   vm - Virtual Machine\n+ *   cpuid_data - CPUID fields to filter\n+ *\n+ * Output Args: None\n+ *\n+ * Return: None\n+ *\n+ * For each CPUID leaf, filter out unsupported bits based on the capabilities\n+ * reported by the TDX module\n+ */\n+static void tdx_filter_cpuid(struct kvm_vm *vm,\n+\t\t\t     struct kvm_cpuid2 *cpuid_data)\n+{\n+\tconst struct kvm_tdx_capabilities *tdx_cap;\n+\tconst struct kvm_cpuid_entry2 *config;\n+\tstruct kvm_cpuid_entry2 *e;\n+\tint i;\n+\n+\ttdx_cap = tdx_read_capabilities(vm);\n+\n+\ti = 0;\n+\twhile (i \u003c cpuid_data-\u003enent) {\n+\t\te = cpuid_data-\u003eentries + i;\n+\t\tconfig = __get_cpuid_entry(\u0026tdx_cap-\u003ecpuid, e-\u003efunction, e-\u003eindex);\n+\n+\t\tif (!config) {\n+\t\t\tint left = cpuid_data-\u003enent - i - 1;\n+\n+\t\t\tif (left \u003e 0)\n+\t\t\t\tmemmove(cpuid_data-\u003eentries + i,\n+\t\t\t\t\tcpuid_data-\u003eentries + i + 1,\n+\t\t\t\t\tsizeof(*cpuid_data-\u003eentries) * left);\n+\t\t\tcpuid_data-\u003enent--;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\te-\u003eeax \u0026= config-\u003eeax;\n+\t\te-\u003eebx \u0026= config-\u003eebx;\n+\t\te-\u003eecx \u0026= config-\u003eecx;\n+\t\te-\u003eedx \u0026= config-\u003eedx;\n+\n+\t\ti++;\n+\t}\n+}\n+\n+void tdx_init_vm(struct kvm_vm *vm)\n+{\n+\tstruct kvm_tdx_init_vm *init_vm;\n+\tconst struct kvm_cpuid2 *tmp;\n+\tstruct kvm_cpuid2 *cpuid;\n+\n+\ttmp = kvm_get_supported_cpuid();\n+\n+\tcpuid = allocate_kvm_cpuid2(tmp-\u003enent);\n+\tmemcpy(cpuid, tmp, kvm_cpuid2_size(tmp-\u003enent));\n+\ttdx_filter_cpuid(vm, cpuid);\n+\n+\tinit_vm = calloc(1, sizeof(*init_vm) +\n+\t\t\t sizeof(init_vm-\u003ecpuid.entries[0]) * cpuid-\u003enent);\n+\tTEST_ASSERT(init_vm, \"init_vm allocation failed\");\n+\n+\tmemcpy(\u0026init_vm-\u003ecpuid, cpuid, kvm_cpuid2_size(cpuid-\u003enent));\n+\tfree(cpuid);\n+\n+\tinit_vm-\u003eattributes = 0;\n+\tinit_vm-\u003exfam = 0;\n+\n+\ttdx_vm_ioctl(vm, KVM_TDX_INIT_VM, 0, init_vm);\n+\n+\tfree(init_vm);\n+}\n+\n+static void tdx_init_mem_region(struct kvm_vm *vm, void *source_pages,\n+\t\t\t\tu64 gpa, u64 size)\n+{\n+\tu32 flags = KVM_TDX_MEASURE_MEMORY_REGION;\n+\tstruct kvm_tdx_init_mem_region mem_region = {\n+\t\t.source_addr = (u64)source_pages,\n+\t\t.gpa = gpa,\n+\t\t.nr_pages = size / PAGE_SIZE,\n+\t};\n+\tstruct kvm_vcpu *vcpu;\n+\n+\tvcpu = list_first_entry_or_null(\u0026vm-\u003evcpus, struct kvm_vcpu, list);\n+\n+\tTEST_ASSERT(size \u0026\u0026 IS_ALIGNED(size, PAGE_SIZE),\n+\t\t    \"Cannot add partial pages to the guest memory.\\n\");\n+\tTEST_ASSERT(IS_ALIGNED((u64)source_pages, PAGE_SIZE),\n+\t\t    \"Source memory buffer is not page aligned\\n\");\n+\ttdx_vcpu_ioctl(vcpu, KVM_TDX_INIT_MEM_REGION, flags, \u0026mem_region);\n+}\n+\n+static void tdx_load_private_memory(struct kvm_vm *vm)\n+{\n+\tstruct userspace_mem_region *region;\n+\tint ctr;\n+\n+\thash_for_each(vm-\u003eregions.slot_hash, ctr, region, slot_node) {\n+\t\tconst struct sparsebit *protected_pages = region-\u003eprotected_phy_pages;\n+\t\tconst gpa_t gpa_base = region-\u003eregion.guest_phys_addr;\n+\t\tconst u64 hva_base = region-\u003eregion.userspace_addr;\n+\t\tconst sparsebit_idx_t lowest_page_in_region = gpa_base \u003e\u003e vm-\u003epage_shift;\n+\t\tvoid *source_pages = NULL;\n+\t\tsparsebit_idx_t i, j;\n+\n+\t\tif (!sparsebit_any_set(protected_pages))\n+\t\t\tcontinue;\n+\n+\t\tTEST_ASSERT(region-\u003eregion.guest_memfd != -1,\n+\t\t\t    \"TD private memory must be backed by guest_memfd\");\n+\n+\t\tsparsebit_for_each_set_range(protected_pages, i, j) {\n+\t\t\tconst u64 size_to_load = (j - i + 1) * vm-\u003epage_size;\n+\t\t\tconst u64 offset =\n+\t\t\t\t(i - lowest_page_in_region) * vm-\u003epage_size;\n+\t\t\tconst u64 hva = hva_base + offset;\n+\t\t\tconst u64 gpa = gpa_base + offset;\n+\n+\t\t\tif (!kvm_has_gmem_attributes)\n+\t\t\t\tsource_pages = (void *)hva;\n+\n+\t\t\tvm_mem_set_private(vm, gpa, size_to_load);\n+\t\t\ttdx_init_mem_region(vm, source_pages, gpa, size_to_load);\n+\t\t}\n+\t}\n+}\n+\n+void tdx_vm_finalize(struct kvm_vm *vm)\n+{\n+\ttdx_load_private_memory(vm);\n+\ttdx_vm_ioctl(vm, KVM_TDX_FINALIZE_VM, 0, NULL);\n+}\ndiff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c\nindex 38050c60a0670..c1cd32d684646 100644\n--- a/tools/testing/selftests/kvm/lib/x86/ucall.c\n+++ b/tools/testing/selftests/kvm/lib/x86/ucall.c\n@@ -5,16 +5,36 @@\n  * Copyright (C) 2018, Red Hat, Inc.\n  */\n #include \"kvm_util.h\"\n+#include \"tdx/tdx.h\"\n+#include \"tdx/tdx_util.h\"\n \n-#define UCALL_PIO_PORT ((u16)0x1000)\n+#define UCALL_PIO_PORT\t\t((u16)0x1000)\n+#define UCALL_TDX_MAGIC\t\t0xabacadabaULL\n \n-void ucall_arch_do_ucall(gva_t uc)\n+static void ucall_tdx_do_ucall(gva_t uc)\n+{\n+\t__tdvmcall(TDVMCALL_REPORT_FATAL_ERROR, UCALL_TDX_MAGIC, uc, 0, 0);\n+}\n+\n+static void *ucall_tdx_get_ucall(struct kvm_vcpu *vcpu)\n+{\n+\tstruct kvm_run *run = vcpu-\u003erun;\n+\n+\tif (run-\u003eexit_reason == KVM_EXIT_SYSTEM_EVENT \u0026\u0026\n+\t    run-\u003esystem_event.type == KVM_SYSTEM_EVENT_TDX_FATAL \u0026\u0026\n+\t    run-\u003esystem_event.data[12] == UCALL_TDX_MAGIC)\n+\t\treturn (void *)(run-\u003esystem_event.data[13]);\n+\n+\treturn NULL;\n+}\n+\n+static void ucall_x86_do_ucall(gva_t uc)\n {\n \tasm volatile(\"in %[port], %%al\"\n \t\t: : [port] \"d\" (UCALL_PIO_PORT), \"D\" (uc) : \"rax\", \"memory\");\n }\n \n-void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)\n+static void *ucall_x86_get_ucall(struct kvm_vcpu *vcpu)\n {\n \tstruct kvm_run *run = vcpu-\u003erun;\n \n@@ -26,3 +46,31 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)\n \t}\n \treturn NULL;\n }\n+\n+static struct {\n+\tvoid (*do_ucall)(gva_t uc);\n+\tvoid *(*get_ucall)(struct kvm_vcpu *vcpu);\n+} ucall_x86_ops = {\n+\t.do_ucall = ucall_x86_do_ucall,\n+\t.get_ucall = ucall_x86_get_ucall,\n+};\n+\n+void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)\n+{\n+\tif (is_tdx_vm(vm)) {\n+\t\tucall_x86_ops.do_ucall = ucall_tdx_do_ucall;\n+\t\tucall_x86_ops.get_ucall = ucall_tdx_get_ucall;\n+\t}\n+\n+\tsync_global_to_guest(vm, ucall_x86_ops);\n+}\n+\n+void ucall_arch_do_ucall(gva_t uc)\n+{\n+\treturn ucall_x86_ops.do_ucall(uc);\n+}\n+\n+void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)\n+{\n+\treturn ucall_x86_ops.get_ucall(vcpu);\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c\nindex eaa8201b937d3..998c1b1d59a18 100644\n--- a/tools/testing/selftests/kvm/x86/sev_dbg_test.c\n+++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c\n@@ -70,17 +70,14 @@ static void __test_sev_dbg(struct kvm_vm *vm, int nr_bytes)\n \t}\n }\n \n-static void test_sev_dbg(u32 type, u64 policy)\n+static void test_sev_dbg(struct vm_shape shape, u64 policy)\n {\n \tint sizes[] = { 1, 8, 15, 16, 17, 32, 33 };\n \tstruct kvm_vcpu *vcpu;\n \tstruct kvm_vm *vm;\n \tint i;\n \n-\tif (!(kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(type)))\n-\t\treturn;\n-\n-\tvm = vm_sev_create_with_one_vcpu(type, NULL, \u0026vcpu);\n+\tvm = vm_create_shape_with_one_vcpu(shape, \u0026vcpu, NULL);\n \n \tdata = addr_gva2hva(vm, vm_alloc(vm, BUFFER_SIZE, KVM_UTIL_MIN_VADDR));\n \tmemset(data, 0xaa, BUFFER_SIZE);\n@@ -112,7 +109,9 @@ int main(int argc, char *argv[])\n \tTEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV));\n \n \t/* Note, KVM doesn't support {de,en}crypt commands for SNP. */\n-\ttest_sev_dbg(KVM_X86_SEV_VM, 0);\n-\ttest_sev_dbg(KVM_X86_SEV_ES_VM, SEV_POLICY_ES);\n+\tif (kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(KVM_X86_SEV_VM))\n+\t\ttest_sev_dbg(VM_SHAPE_SEV, 0);\n+\tif (kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(KVM_X86_SEV_ES_VM))\n+\t\ttest_sev_dbg(VM_SHAPE_SEV_ES, SEV_POLICY_ES);\n \treturn 0;\n }\ndiff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c\nindex bf27b6187afac..5aebf7f52c445 100644\n--- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c\n+++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c\n@@ -104,7 +104,7 @@ static void compare_xsave(u8 *from_host, u8 *from_guest)\n \t\tabort();\n }\n \n-static void test_sync_vmsa(u32 type, u64 policy)\n+static void test_sync_vmsa(struct vm_shape shape, u64 policy)\n {\n \tstruct kvm_vcpu *vcpu;\n \tstruct kvm_vm *vm;\n@@ -114,7 +114,7 @@ static void test_sync_vmsa(u32 type, u64 policy)\n \tdouble x87val = M_PI;\n \tstruct kvm_xsave __attribute__((aligned(64))) xsave = { 0 };\n \n-\tvm = vm_sev_create_with_one_vcpu(type, guest_code_xsave, \u0026vcpu);\n+\tvm = vm_create_shape_with_one_vcpu(shape, \u0026vcpu, guest_code_xsave);\n \tgva = vm_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR,\n \t\t\t      MEM_REGION_TEST_DATA);\n \thva = addr_gva2hva(vm, gva);\n@@ -150,13 +150,13 @@ static void test_sync_vmsa(u32 type, u64 policy)\n \tkvm_vm_free(vm);\n }\n \n-static void test_sev(void *guest_code, u32 type, u64 policy)\n+static void test_sev(void *guest_code, struct vm_shape shape, u64 policy)\n {\n \tstruct kvm_vcpu *vcpu;\n \tstruct kvm_vm *vm;\n \tstruct ucall uc;\n \n-\tvm = vm_sev_create_with_one_vcpu(type, guest_code, \u0026vcpu);\n+\tvm = vm_create_shape_with_one_vcpu(shape, \u0026vcpu, guest_code);\n \n \t/* TODO: Validate the measurement is as expected. */\n \tvm_sev_launch(vm, policy, NULL);\n@@ -201,12 +201,12 @@ static void guest_shutdown_code(void)\n \t__asm__ __volatile__(\"ud2\");\n }\n \n-static void test_sev_shutdown(u32 type, u64 policy)\n+static void test_sev_shutdown(struct vm_shape shape, u64 policy)\n {\n \tstruct kvm_vcpu *vcpu;\n \tstruct kvm_vm *vm;\n \n-\tvm = vm_sev_create_with_one_vcpu(type, guest_shutdown_code, \u0026vcpu);\n+\tvm = vm_create_shape_with_one_vcpu(shape, \u0026vcpu, guest_shutdown_code);\n \n \tvm_sev_launch(vm, policy, NULL);\n \n@@ -218,28 +218,28 @@ static void test_sev_shutdown(u32 type, u64 policy)\n \tkvm_vm_free(vm);\n }\n \n-static void test_sev_smoke(void *guest, u32 type, u64 policy)\n+static void test_sev_smoke(void *guest, struct vm_shape shape, u64 policy)\n {\n \tconst u64 xf_mask = XFEATURE_MASK_X87_AVX;\n \n-\tif (type == KVM_X86_SNP_VM)\n-\t\ttest_sev(guest, type, policy | SNP_POLICY_DBG);\n+\tif (shape.type == KVM_X86_SNP_VM)\n+\t\ttest_sev(guest, shape, policy | SNP_POLICY_DBG);\n \telse\n-\t\ttest_sev(guest, type, policy | SEV_POLICY_NO_DBG);\n-\ttest_sev(guest, type, policy);\n+\t\ttest_sev(guest, shape, policy | SEV_POLICY_NO_DBG);\n+\ttest_sev(guest, shape, policy);\n \n-\tif (type == KVM_X86_SEV_VM)\n+\tif (shape.type == KVM_X86_SEV_VM)\n \t\treturn;\n \n-\ttest_sev_shutdown(type, policy);\n+\ttest_sev_shutdown(shape, policy);\n \n \tif (kvm_has_cap(KVM_CAP_XCRS) \u0026\u0026\n \t    (xgetbv(0) \u0026 kvm_cpu_supported_xcr0() \u0026 xf_mask) == xf_mask) {\n-\t\ttest_sync_vmsa(type, policy);\n-\t\tif (type == KVM_X86_SNP_VM)\n-\t\t\ttest_sync_vmsa(type, policy | SNP_POLICY_DBG);\n+\t\ttest_sync_vmsa(shape, policy);\n+\t\tif (shape.type == KVM_X86_SNP_VM)\n+\t\t\ttest_sync_vmsa(shape, policy | SNP_POLICY_DBG);\n \t\telse\n-\t\t\ttest_sync_vmsa(type, policy | SEV_POLICY_NO_DBG);\n+\t\t\ttest_sync_vmsa(shape, policy | SEV_POLICY_NO_DBG);\n \t}\n }\n \n@@ -254,13 +254,13 @@ int main(int argc, char *argv[])\n \t * though X86_FEATURE_SEV is set.  Gate every type on KVM_CAP_VM_TYPES.\n \t */\n \tif (kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(KVM_X86_SEV_VM))\n-\t\ttest_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0);\n+\t\ttest_sev_smoke(guest_sev_code, VM_SHAPE_SEV, 0);\n \n \tif (kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(KVM_X86_SEV_ES_VM))\n-\t\ttest_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES);\n+\t\ttest_sev_smoke(guest_sev_es_code, VM_SHAPE_SEV_ES, SEV_POLICY_ES);\n \n \tif (kvm_check_cap(KVM_CAP_VM_TYPES) \u0026 BIT(KVM_X86_SNP_VM))\n-\t\ttest_sev_smoke(guest_snp_code, KVM_X86_SNP_VM, snp_default_policy());\n+\t\ttest_sev_smoke(guest_snp_code, VM_SHAPE_SNP, snp_default_policy());\n \n \treturn 0;\n }\ndiff --git a/tools/testing/selftests/kvm/x86/tdx_vm_test.c b/tools/testing/selftests/kvm/x86/tdx_vm_test.c\nnew file mode 100644\nindex 0000000000000..7cdcaf33b585b\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/tdx_vm_test.c\n@@ -0,0 +1,33 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+#include \"processor.h\"\n+#include \"kvm_util.h\"\n+#include \"tdx/tdx_util.h\"\n+#include \"ucall_common.h\"\n+#include \"kselftest_harness.h\"\n+\n+static void guest_code_lifecycle(void)\n+{\n+\tGUEST_DONE();\n+}\n+\n+TEST(verify_td_lifecycle)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct ucall uc;\n+\n+\tvm = vm_create_shape_with_one_vcpu(VM_SHAPE_TDX, \u0026vcpu,\n+\t\t\t\t\t   guest_code_lifecycle);\n+\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_EQ(get_ucall(vcpu, \u0026uc), UCALL_DONE);\n+\n+\tkvm_vm_free(vm);\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\tTEST_REQUIRE(is_tdx_supported());\n+\treturn test_harness_run(argc, argv);\n+}\n"
}

2/1 2026/10/02 15:27 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series only modifies user-space files under tools/ (specifically KVM selftests and tools/include/), which does not affect the compiled kernel image (vmlinux) or kernel modules.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit ca0265f290501b008991abdcc90a557449bf7eed
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Oct 2 15:27:32 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/include/linux/kbuild.h b/tools/include/linux/kbuild.h
new file mode 100644
index 0000000000000..957fd55cd159f
--- /dev/null
+++ b/tools/include/linux/kbuild.h
@@ -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 */
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 1d41a046a7bfd..eef6055242b2e 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -9,4 +9,5 @@
 !config
 !settings
 !Makefile
-!Makefile.kvm
\ No newline at end of file
+!Makefile.kvm
+include/x86/**/*_offsets.h
\ No newline at end of file
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 239bc61ea384f..14cff95cf2913 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -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
@@ -28,8 +30,11 @@ 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
+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
@@ -156,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
@@ -268,6 +274,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
@@ -284,15 +294,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/
@@ -325,11 +344,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))
 
@@ -354,7 +370,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) \
@@ -366,6 +384,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.
@@ -374,11 +395,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' \
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 777fa3dbf88d6..426bc05fe62f3 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -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)
 
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 6e6f70035508a..61ae02d927c17 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -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. */
@@ -386,6 +390,11 @@ 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 VM_SHAPE_TDX		VM_TYPE(KVM_X86_TDX_VM)
+
 #define PHYSICAL_PAGE_MASK      GENMASK_ULL(51, 12)
 
 #define PAGE_SHIFT		12
@@ -1020,6 +1029,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);
@@ -1202,6 +1213,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);
 
@@ -1645,4 +1658,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 */
diff --git a/tools/testing/selftests/kvm/include/x86/processor_asm.h b/tools/testing/selftests/kvm/include/x86/processor_asm.h
new file mode 100644
index 0000000000000..713b6bc0aeb75
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86/processor_asm.h
@@ -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 */
diff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h
index dec383e59a47e..eb4ab573febfd 100644
--- a/tools/testing/selftests/kvm/include/x86/sev.h
+++ b/tools/testing/selftests/kvm/include/x86/sev.h
@@ -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);
diff --git a/tools/testing/selftests/kvm/include/x86/tdx/td_boot.h b/tools/testing/selftests/kvm/include/x86/tdx/td_boot.h
new file mode 100644
index 0000000000000..8f47a3893f59c
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86/tdx/td_boot.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_TDX_TD_BOOT_H
+#define SELFTEST_TDX_TD_BOOT_H
+
+/*
+ * Layout for boot section (not to scale)
+ *
+ *                                   GPA
+ * _________________________________ 0x1_0000_0000 (4GB)
+ * |   Boot code trampoline    |
+ * |___________________________|____ 0x0_ffff_fff0: Reset vector (16B below 4GB)
+ * |   Boot code               |
+ * |___________________________|____ td_boot will be copied here, so that the
+ * |                           |     jmp to td_boot is exactly at the reset vector
+ * |   Empty space             |
+ * |                           |
+ * |───────────────────────────|
+ * |                           |
+ * |                           |
+ * |   Boot parameters         |
+ * |                           |
+ * |                           |
+ * |___________________________|____ 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.
+ */
+struct __packed td_boot_parameters_dtr {
+	u16 limit;
+	u32 base;
+};
+
+/*
+ * Allows each vCPU to be initialized with different rip and esp.
+ */
+struct td_per_vcpu_parameters {
+	u32 esp_gva;
+	u64 guest_code;
+};
+
+/*
+ * Boot parameters for the TD.
+ *
+ * Unlike a regular VM, KVM cannot set registers such as esp, eip, etc
+ * before boot, so to run selftests, these registers' values have to be
+ * initialized by the TD.
+ *
+ * This struct is loaded in TD private memory at TD_BOOT_PARAMETERS_GPA.
+ *
+ * The TD boot code will read off parameters from this struct and set up the
+ * vCPU for executing selftests.
+ */
+struct td_boot_parameters {
+	u32 cr0;
+	u32 cr3;
+	u32 cr4;
+	struct td_boot_parameters_dtr gdtr;
+	struct td_boot_parameters_dtr idtr;
+	struct td_per_vcpu_parameters per_vcpu[];
+};
+
+extern u8 td_boot[], td_boot_code_end[], td_boot_reset_vector_trampoline[];
+
+#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot)
+
+#endif /* #ifndef __ASSEMBLER__ */
+
+#endif /* SELFTEST_TDX_TD_BOOT_H */
diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
new file mode 100644
index 0000000000000..9982aaaba8859
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_TDX_TDX_H
+#define SELFTEST_KVM_TDX_TDX_H
+
+#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 */
diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
new file mode 100644
index 0000000000000..85791fe16f688
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
@@ -0,0 +1,83 @@
+/* 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;
+}
+
+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.
+ */
+#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);			\
+})
+
+#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);
+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 */
diff --git a/tools/testing/selftests/kvm/include/x86/ucall.h b/tools/testing/selftests/kvm/include/x86/ucall.h
index 0e4950041e3ee..3639f03a4da97 100644
--- a/tools/testing/selftests/kvm/include/x86/ucall.h
+++ b/tools/testing/selftests/kvm/include/x86/ucall.h
@@ -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
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 3f559f207408f..f474664b5d924 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -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
@@ -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;
 
diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
index 029ce21f9f2fa..c03dfc7631726 100644
--- a/tools/testing/selftests/kvm/lib/ucall_common.c
+++ b/tools/testing/selftests/kvm/lib/ucall_common.c
@@ -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;
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index d31fa81ea0756..e1d0fd5ed1f68 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -8,19 +8,13 @@
 #include "kvm_util.h"
 #include "pmu.h"
 #include "processor.h"
+#include "processor_asm.h"
 #include "smm.h"
 #include "svm_util.h"
 #include "sev.h"
+#include "tdx/tdx_util.h"
 #include "vmx.h"
 
-#ifndef NUM_INTERRUPTS
-#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;
@@ -652,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.gdt.limit = kvm_get_default_gdt_limit();
 
-	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.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);
@@ -828,6 +818,13 @@ 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);
+		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);
 	TEST_ASSERT(r > 0, "KVM_GET_TSC_KHZ did not provide a valid TSC frequency.");
 	guest_tsc_khz = r;
@@ -849,27 +846,84 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
 {
 	struct kvm_regs regs;
 
-	vcpu_regs_get(vcpu, &regs);
-	regs.rip = (unsigned long) guest_code;
-	vcpu_regs_set(vcpu, &regs);
+	if (is_tdx_vm(vcpu->vm)) {
+		tdx_vcpu_set_entry_point(vcpu, guest_code);
+	} else {
+		vcpu_regs_get(vcpu, &regs);
+		regs.rip = (unsigned long)guest_code;
+		vcpu_regs_set(vcpu, &regs);
+	}
 }
 
-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;
+
+	return stack_gva;
+}
+
+static void tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
+{
+	struct kvm_cpuid2 *cpuid;
+
+	cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
+	tdx_vcpu_ioctl(vcpu, KVM_TDX_GET_CPUID, 0, cpuid);
+	vcpu_init_cpuid(vcpu, cpuid);
+	free(cpuid);
+	tdx_vcpu_ioctl(vcpu, KVM_TDX_INIT_VCPU, 0, NULL);
+
+	tdx_vcpu_load_boot_parameters(vm, vcpu);
+}
+
+struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
+{
+	struct kvm_mp_state mp_state;
+	struct kvm_vcpu *vcpu;
+	struct kvm_regs regs;
+
 	vcpu = __vm_vcpu_add(vm, vcpu_id);
-	vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
-	vcpu_init_sregs(vm, vcpu);
-	vcpu_init_xcrs(vm, vcpu);
 
-	/* Setup guest general purpose registers */
-	vcpu_regs_get(vcpu, &regs);
-	regs.rflags = regs.rflags | X86_EFLAGS_FIXED;
-	regs.rsp = vm_alloc_stack(vm, DEFAULT_STACK_PGS);
-	vcpu_regs_set(vcpu, &regs);
+	/*
+	 * Both kvm_get_supported_cpuid() (for legacy VMs) and KVM_TDX_GET_CPUID
+	 * (for TDX VMs) return VM-scoped CPUID. e.g. the APIC ID isn't
+	 * populated per vCPU. This is fine because KVM selftests don't
+	 * currently test CPUID topology enumeration.
+	 */
+	if (is_tdx_vm(vm)) {
+		tdx_vcpu_init(vm, vcpu);
+	} else {
+		vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
+
+		vcpu_init_sregs(vm, vcpu);
+		vcpu_init_xcrs(vm, vcpu);
+
+		/* Setup guest general purpose registers */
+		vcpu_regs_get(vcpu, &regs);
+		regs.rflags = regs.rflags | 0x2;
+		regs.rsp = kvm_allocate_vcpu_stack(vm);
+		vcpu_regs_set(vcpu, &regs);
+	}
 
 	/* Setup the MP state */
 	mp_state.mp_state = 0;
@@ -1330,17 +1384,24 @@ 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;
 	}
 }
 
-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;
 
@@ -1350,11 +1411,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;							\
@@ -1465,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)
 {
diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c
index 93f9169034617..95d8520eea34a 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -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)) {
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S
new file mode 100644
index 0000000000000..a04011b91ae4e
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "tdx/td_boot.h"
+#include "tdx/td_boot_offsets.h"
+#include "processor_asm.h"
+
+.code32
+
+.globl td_boot
+td_boot:
+	/*
+	 * Paging is turned off.
+	 *
+	 * Register usage throughout td_boot:
+	 * ebx: Address of global boot parameters (Must be preserved)
+	 * eax: Address of per-vCPU parameters (Must be preserved)
+	 * edi, edx: Temporary scratch registers
+	 */
+	cli
+
+	movl $TD_BOOT_PARAMETERS_GPA, %ebx
+
+	/*
+	 * 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
+	leal TD_BOOT_PARAMETERS_PER_VCPU(%ebx), %edi
+	addl %edi, %eax
+
+	/* Setup stack. */
+	movl TD_PER_VCPU_PARAMETERS_ESP_GVA(%eax), %esp
+
+	/* Setup GDT. */
+	leal TD_BOOT_PARAMETERS_GDT(%ebx), %edi
+	lgdt (%edi)
+
+	/* Setup IDT. */
+	leal TD_BOOT_PARAMETERS_IDT(%ebx), %edi
+	lidt (%edi)
+
+	/*
+	 * 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
+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
+td_boot_code_end:
+
+/* Disable executable stack. */
+.section .note.GNU-stack,"",%progbits
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c
new file mode 100644
index 0000000000000..7f76a3585b99a
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c
@@ -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));
+}
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx.S b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.S
new file mode 100644
index 0000000000000..e3f0f7f2232bf
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.S
@@ -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
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c
new file mode 100644
index 0000000000000..b8e1f848cf548
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/align.h>
+#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 TD_BOOT_PARAMETERS_SLOT	21
+
+#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;
+
+	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);
+
+	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);
+}
+
+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);
+	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);
+	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");
+
+	virt_map(vm, TD_BOOT_PARAMETERS_GPA, TD_BOOT_PARAMETERS_GPA, npages);
+}
+
+void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm)
+{
+	struct td_boot_parameters *params = addr_gpa2hva(vm, TD_BOOT_PARAMETERS_GPA);
+	u32 cr4;
+
+	cr4 = kvm_get_default_cr4(vm->mmu.pgtable_levels);
+
+	/* Per the TDX spec, CR4 bit MCE is fixed to 1 */
+	cr4 |= X86_CR4_MCE;
+
+	/* Per the TDX spec, CR4 bit VMXE and SMXE are fixed to 0 */
+	cr4 &= ~(X86_CR4_VMXE | X86_CR4_SMXE);
+
+	/* 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;
+	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");
+}
+
+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 =
+		&params->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");
+}
+
+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 =
+		&params->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;
+	int nr_cpuid_configs = 4;
+	int rc = -1;
+	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));
+		TEST_ASSERT(tdx_cap,
+			    "Could not allocate memory for tdx capability nr_cpuid_configs %d\n",
+			    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;
+}
+
+/*
+ * Filter CPUID based on TDX supported capabilities
+ *
+ * Input Args:
+ *   vm - Virtual Machine
+ *   cpuid_data - CPUID fields to filter
+ *
+ * Output Args: None
+ *
+ * Return: None
+ *
+ * For each CPUID leaf, filter out unsupported bits based on the capabilities
+ * reported by the TDX module
+ */
+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);
+
+	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;
+
+	tdx_vm_ioctl(vm, KVM_TDX_INIT_VM, 0, init_vm);
+
+	free(init_vm);
+}
+
+static void tdx_init_mem_region(struct kvm_vm *vm, void *source_pages,
+				u64 gpa, u64 size)
+{
+	u32 flags = KVM_TDX_MEASURE_MEMORY_REGION;
+	struct kvm_tdx_init_mem_region mem_region = {
+		.source_addr = (u64)source_pages,
+		.gpa = gpa,
+		.nr_pages = size / PAGE_SIZE,
+	};
+	struct kvm_vcpu *vcpu;
+
+	vcpu = list_first_entry_or_null(&vm->vcpus, struct kvm_vcpu, list);
+
+	TEST_ASSERT(size && IS_ALIGNED(size, PAGE_SIZE),
+		    "Cannot add partial pages to the guest memory.\n");
+	TEST_ASSERT(IS_ALIGNED((u64)source_pages, PAGE_SIZE),
+		    "Source memory buffer is not page aligned\n");
+	tdx_vcpu_ioctl(vcpu, KVM_TDX_INIT_MEM_REGION, flags, &mem_region);
+}
+
+static void tdx_load_private_memory(struct kvm_vm *vm)
+{
+	struct userspace_mem_region *region;
+	int ctr;
+
+	hash_for_each(vm->regions.slot_hash, ctr, region, slot_node) {
+		const struct sparsebit *protected_pages = region->protected_phy_pages;
+		const gpa_t gpa_base = region->region.guest_phys_addr;
+		const u64 hva_base = region->region.userspace_addr;
+		const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift;
+		void *source_pages = NULL;
+		sparsebit_idx_t i, j;
+
+		if (!sparsebit_any_set(protected_pages))
+			continue;
+
+		TEST_ASSERT(region->region.guest_memfd != -1,
+			    "TD private memory must be backed by guest_memfd");
+
+		sparsebit_for_each_set_range(protected_pages, i, j) {
+			const u64 size_to_load = (j - i + 1) * vm->page_size;
+			const u64 offset =
+				(i - lowest_page_in_region) * vm->page_size;
+			const u64 hva = hva_base + offset;
+			const u64 gpa = gpa_base + offset;
+
+			if (!kvm_has_gmem_attributes)
+				source_pages = (void *)hva;
+
+			vm_mem_set_private(vm, gpa, size_to_load);
+			tdx_init_mem_region(vm, source_pages, gpa, size_to_load);
+		}
+	}
+}
+
+void tdx_vm_finalize(struct kvm_vm *vm)
+{
+	tdx_load_private_memory(vm);
+	tdx_vm_ioctl(vm, KVM_TDX_FINALIZE_VM, 0, NULL);
+}
diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c
index 38050c60a0670..c1cd32d684646 100644
--- a/tools/testing/selftests/kvm/lib/x86/ucall.c
+++ b/tools/testing/selftests/kvm/lib/x86/ucall.c
@@ -5,16 +5,36 @@
  * 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
 
-void ucall_arch_do_ucall(gva_t uc)
+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)
 {
 	asm volatile("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 +46,31 @@ 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)
+{
+	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);
+}
+
+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);
+}
diff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c
index eaa8201b937d3..998c1b1d59a18 100644
--- a/tools/testing/selftests/kvm/x86/sev_dbg_test.c
+++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c
@@ -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;
 }
diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c
index bf27b6187afac..5aebf7f52c445 100644
--- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c
+++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c
@@ -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;
 }
diff --git a/tools/testing/selftests/kvm/x86/tdx_vm_test.c b/tools/testing/selftests/kvm/x86/tdx_vm_test.c
new file mode 100644
index 0000000000000..7cdcaf33b585b
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/tdx_vm_test.c
@@ -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);
+}

3/2 2026/10/02 15:27 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 25236
							output: 72
							thoughts: 1540
						

Thoughts:
**Analyzing the Patch for Fuzzing Eligibility**

Okay, let's break this patch down. My initial assessment is based on the file paths. I see a substantial touch within the `tools/` directory, specifically focusing on `tools/include/linux/kbuild.h` and the `tools/testing/selftests/kvm/` subtree. My understanding is that all changes are confined to this area.

Now, let's consult the triage guidelines. I'm looking specifically at the negative criteria for determining when a patch isn't suitable for fuzzing. The key phrases I'm scanning for are: "Non-kernel and non-executable changes" and anything mentioning user-space directories, self-tests, samples, or scripts that *don't* affect the compiled kernel image or modules. I know these tests like the back of my hand.

Given this, I have to determine if this change affects code that could be fuzzed for kernel security problems.

Let me go through the list carefully again.

*   `tools/include/linux/kbuild.h`
*   `tools/testing/selftests/kvm/.gitignore`
*   `tools/testing/selftests/kvm/Makefile.kvm`
*   `tools/testing/selftests/kvm/include/kvm_util.h`
*   `tools/testing/selftests/kvm/include/x86/processor.h`
*   `tools/testing/selftests/kvm/include/x86/processor_asm.h`
*   `tools/testing/selftests/kvm/include/x86/sev.h`
*   `tools/testing/selftests/kvm/include/x86/tdx/td_boot.h`
*   `tools/testing/selftests/kvm/include/x86/tdx/tdx.h`
*   `tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h`
*   `tools/testing/selftests/kvm/include/x86/ucall.h`
*   `tools/testing/selftests/kvm/lib/kvm_util.c`
*   `tools/testing/selftests/kvm/lib/ucall_common.c`
*   `tools/testing/selftests/kvm/lib/x86/processor.c`
*   `tools/testing/selftests/kvm/lib/x86/sev.c`
*   `tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S`
*   `tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c`
*   `tools/testing/selftests/kvm/lib/x86/tdx/tdx.S`
*   `tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c`
*   `tools/testing/selftests/kvm/lib/x86/ucall.c`
*   `tools/testing/selftests/kvm/x86/sev_dbg_test.c`
*   `tools/testing/selftests/kvm/x86/sev_smoke_test.c`
*   `tools/testing/selftests/kvm/x86/tdx_vm_test.c`

Yep. Everything is under `tools/`, indicating these are user-space tests and utilities. Nothing here is going to become part of the kernel image (vmlinux) or a loadable module.

Therefore, according to the triage criteria, the changes fall squarely into the "negative criteria" category. The modified code is entirely within user-space tools and self-tests. The change is not for the vmlinux or kernel modules.

So, *WorthFuzzing* is false.

My reasoning is straightforward: all changes are confined to user-space tools and self-tests, which do not affect the compiled kernel image or kernel modules.
```
WorthFuzzing: false
FocusSymbols: []
EnableConfigs: []
Reasoning: all modified files are user-space tools/selftests that don't affect kernel image/modules.
```
Let's now update the results accordingly.



4/2 2026/10/02 15:27 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series only modifies user-space files under tools/ (specifically KVM selftests and tools/include/), which does not affect the compiled kernel image (vmlinux) or kernel modules.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series only modifies user-space files under tools/ (specifically KVM selftests and tools/include/), which does not affect the compiled kernel image (vmlinux) or kernel modules.",
  "WorthFuzzing": false
}

5/1 2026/10/02 15:27 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)