AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/28 19:39 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds and modifies KVM selftests in tools/testing/selftests/kvm/. It does not modify any core kernel code or drivers. Since the fuzzing targets the kernel and not userspace testing tools, there are no functional kernel changes to fuzz.",
  "WorthFuzzing": false
}

1/1 2026/07/28 19:39 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit e294868ad09135af10e01e6604f3a97911fc54ea\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jul 28 19:39:41 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 88c6c8046ddec..e67b1e9893f68 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -112,6 +112,7 @@ TEST_GEN_PROGS_x86 += x86/set_sregs_test\n TEST_GEN_PROGS_x86 += x86/smaller_maxphyaddr_emulation_test\n TEST_GEN_PROGS_x86 += x86/smm_test\n TEST_GEN_PROGS_x86 += x86/state_test\n+TEST_GEN_PROGS_x86 += x86/save_restore_pf_stress_test\n TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test\n TEST_GEN_PROGS_x86 += x86/svm_vmcall_test\n TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test\ndiff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h\nindex d64c8a228207a..e8356ee54d7b9 100644\n--- a/tools/testing/selftests/kvm/include/test_util.h\n+++ b/tools/testing/selftests/kvm/include/test_util.h\n@@ -23,6 +23,7 @@\n \n #include \u003clinux/mman.h\u003e\n #include \u003clinux/types.h\u003e\n+#include \u003clinux/stringify.h\u003e\n \n #define msecs_to_usecs(msec)    ((msec) * 1000ULL)\n \ndiff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h\nindex be79bda024bf1..82a8ea6b661f0 100644\n--- a/tools/testing/selftests/kvm/include/x86/evmcs.h\n+++ b/tools/testing/selftests/kvm/include/x86/evmcs.h\n@@ -1207,30 +1207,23 @@ static inline int evmcs_vmlaunch(void)\n \n \tcurrent_evmcs-\u003ehv_clean_fields = 0;\n \n-\t__asm__ __volatile__(\"push %%rbp;\"\n-\t\t\t     \"push %%rcx;\"\n-\t\t\t     \"push %%rdx;\"\n-\t\t\t     \"push %%rsi;\"\n-\t\t\t     \"push %%rdi;\"\n-\t\t\t     \"push $0;\"\n+\t__asm__ __volatile__(\"push $0;\"\n \t\t\t     \"mov %%rsp, (%[host_rsp]);\"\n \t\t\t     \"lea 1f(%%rip), %%rax;\"\n \t\t\t     \"mov %%rax, (%[host_rip]);\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n \t\t\t     \"vmlaunch;\"\n \t\t\t     \"incq (%%rsp);\"\n-\t\t\t     \"1: pop %%rax;\"\n-\t\t\t     \"pop %%rdi;\"\n-\t\t\t     \"pop %%rsi;\"\n-\t\t\t     \"pop %%rdx;\"\n-\t\t\t     \"pop %%rcx;\"\n-\t\t\t     \"pop %%rbp;\"\n+\t\t\t     \"1: ;\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n+\t\t\t     \"pop %%rax;\"\n \t\t\t     : [ret]\"=\u0026a\"(ret)\n \t\t\t     : [host_rsp]\"r\"\n \t\t\t       ((u64)\u0026current_evmcs-\u003ehost_rsp),\n \t\t\t       [host_rip]\"r\"\n-\t\t\t       ((u64)\u0026current_evmcs-\u003ehost_rip)\n-\t\t\t     : \"memory\", \"cc\", \"rbx\", \"r8\", \"r9\", \"r10\",\n-\t\t\t       \"r11\", \"r12\", \"r13\", \"r14\", \"r15\");\n+\t\t\t       ((u64)\u0026current_evmcs-\u003ehost_rip),\n+\t\t\t       GUEST_REGS_OFFSETS\n+\t\t\t     : \"memory\", \"cc\");\n \treturn ret;\n }\n \n@@ -1246,30 +1239,23 @@ static inline int evmcs_vmresume(void)\n \t/* HOST_RSP */\n \tcurrent_evmcs-\u003ehv_clean_fields \u0026= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_POINTER;\n \n-\t__asm__ __volatile__(\"push %%rbp;\"\n-\t\t\t     \"push %%rcx;\"\n-\t\t\t     \"push %%rdx;\"\n-\t\t\t     \"push %%rsi;\"\n-\t\t\t     \"push %%rdi;\"\n-\t\t\t     \"push $0;\"\n+\t__asm__ __volatile__(\"push $0;\"\n \t\t\t     \"mov %%rsp, (%[host_rsp]);\"\n \t\t\t     \"lea 1f(%%rip), %%rax;\"\n \t\t\t     \"mov %%rax, (%[host_rip]);\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n \t\t\t     \"vmresume;\"\n \t\t\t     \"incq (%%rsp);\"\n-\t\t\t     \"1: pop %%rax;\"\n-\t\t\t     \"pop %%rdi;\"\n-\t\t\t     \"pop %%rsi;\"\n-\t\t\t     \"pop %%rdx;\"\n-\t\t\t     \"pop %%rcx;\"\n-\t\t\t     \"pop %%rbp;\"\n+\t\t\t     \"1: ;\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n+\t\t\t     \"pop %%rax;\"\n \t\t\t     : [ret]\"=\u0026a\"(ret)\n \t\t\t     : [host_rsp]\"r\"\n \t\t\t       ((u64)\u0026current_evmcs-\u003ehost_rsp),\n \t\t\t       [host_rip]\"r\"\n-\t\t\t       ((u64)\u0026current_evmcs-\u003ehost_rip)\n-\t\t\t     : \"memory\", \"cc\", \"rbx\", \"r8\", \"r9\", \"r10\",\n-\t\t\t       \"r11\", \"r12\", \"r13\", \"r14\", \"r15\");\n+\t\t\t       ((u64)\u0026current_evmcs-\u003ehost_rip),\n+\t\t\t       GUEST_REGS_OFFSETS\n+\t\t\t     : \"memory\", \"cc\");\n \treturn ret;\n }\n \ndiff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h\nindex b161174ece453..461c8aef7d37b 100644\n--- a/tools/testing/selftests/kvm/include/x86/processor.h\n+++ b/tools/testing/selftests/kvm/include/x86/processor.h\n@@ -24,6 +24,7 @@ extern bool host_cpu_is_amd;\n extern bool host_cpu_is_hygon;\n extern bool host_cpu_is_amd_compatible;\n extern u64 guest_tsc_khz;\n+extern struct kvm_mmu guest_mmu;\n \n #ifndef MAX_NR_CPUID_ENTRIES\n #define MAX_NR_CPUID_ENTRIES 100\n@@ -398,8 +399,7 @@ static inline unsigned int x86_model(unsigned int eax)\n #define PTE_GET_PA(pte)\t\t((pte) \u0026 PHYSICAL_PAGE_MASK)\n #define PTE_GET_PFN(pte)        (PTE_GET_PA(pte) \u003e\u003e PAGE_SHIFT)\n \n-/* General Registers in 64-Bit Mode */\n-struct gpr64_regs {\n+struct guest_regs {\n \tu64 rax;\n \tu64 rcx;\n \tu64 rdx;\n@@ -416,8 +416,38 @@ struct gpr64_regs {\n \tu64 r13;\n \tu64 r14;\n \tu64 r15;\n+\tu64 rflags;\n };\n \n+extern struct guest_regs guest_regs;\n+\n+#define GUEST_REG_OFFSET(name) \\\n+\t[off_##name] \"i\" (offsetof(struct guest_regs, name))\n+\n+#define GUEST_REGS_OFFSETS\t\\\n+\tGUEST_REG_OFFSET(rax),\t\\\n+\tGUEST_REG_OFFSET(rcx),\t\\\n+\tGUEST_REG_OFFSET(rdx),\t\\\n+\tGUEST_REG_OFFSET(rbx),\t\\\n+\tGUEST_REG_OFFSET(rsp),\t\\\n+\tGUEST_REG_OFFSET(rbp),\t\\\n+\tGUEST_REG_OFFSET(rsi),\t\\\n+\tGUEST_REG_OFFSET(rdi),\t\\\n+\tGUEST_REG_OFFSET(r8),\t\\\n+\tGUEST_REG_OFFSET(r9),\t\\\n+\tGUEST_REG_OFFSET(r10),\t\\\n+\tGUEST_REG_OFFSET(r11),\t\\\n+\tGUEST_REG_OFFSET(r12),\t\\\n+\tGUEST_REG_OFFSET(r13),\t\\\n+\tGUEST_REG_OFFSET(r14),\t\\\n+\tGUEST_REG_OFFSET(r15),\t\\\n+\tGUEST_REG_OFFSET(rflags)\n+\n+#define GUEST_REG(name) \"guest_regs + %c[off_\" #name \"]\"\n+\n+#define GUEST_SWITCH_GPR_ASM(name) \\\n+\t\"xchg %%\" #name \", \" GUEST_REG(name) \"\\n\\t\"\n+\n struct desc64 {\n \tu16 limit0;\n \tu16 base0;\n@@ -584,6 +614,15 @@ static inline void set_cr0(u64 val)\n \t__asm__ __volatile__(\"mov %0, %%cr0\" : : \"r\" (val) : \"memory\");\n }\n \n+static inline u64 get_cr2(void)\n+{\n+\tu64 cr2;\n+\n+\t__asm__ __volatile__(\"mov %%cr2, %[cr2]\"\n+\t\t\t     : /* output */ [cr2]\"=r\"(cr2));\n+\treturn cr2;\n+}\n+\n static inline u64 get_cr3(void)\n {\n \tu64 cr3;\n@@ -879,6 +918,11 @@ static inline void write_sse_reg(int reg, const sse128_t *data)\n \t}\n }\n \n+static inline void invlpg(u64 addr)\n+{\n+\t__asm__ __volatile__(\"invlpg (%0)\" : : \"r\"(addr) : \"memory\");\n+}\n+\n static inline void cpu_relax(void)\n {\n \tasm volatile(\"rep; nop\" ::: \"memory\");\n@@ -915,6 +959,11 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu);\n void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state);\n void kvm_x86_state_cleanup(struct kvm_x86_state *state);\n \n+static inline bool kvm_x86_state_is_guest_mode(struct kvm_x86_state *state)\n+{\n+\treturn state-\u003enested.size \u0026\u0026 (state-\u003enested.flags \u0026 KVM_STATE_NESTED_GUEST_MODE);\n+}\n+\n const struct kvm_msr_list *kvm_get_msr_index_list(void);\n const struct kvm_msr_list *kvm_get_feature_msr_index_list(void);\n bool kvm_msr_is_in_save_restore_list(u32 msr_index);\ndiff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h\nindex 4bcfd60e3aecb..04f5e34dea3ae 100644\n--- a/tools/testing/selftests/kvm/include/x86/vmx.h\n+++ b/tools/testing/selftests/kvm/include/x86/vmx.h\n@@ -290,6 +290,23 @@ struct vmx_msr_entry {\n \tu64 value;\n } __attribute__ ((aligned(16)));\n \n+#define VMX_SWITCH_GPRS_ASM \\\n+\tGUEST_SWITCH_GPR_ASM(rax) \\\n+\tGUEST_SWITCH_GPR_ASM(rbx) \\\n+\tGUEST_SWITCH_GPR_ASM(rcx) \\\n+\tGUEST_SWITCH_GPR_ASM(rdx) \\\n+\tGUEST_SWITCH_GPR_ASM(rbp) \\\n+\tGUEST_SWITCH_GPR_ASM(rsi) \\\n+\tGUEST_SWITCH_GPR_ASM(rdi) \\\n+\tGUEST_SWITCH_GPR_ASM(r8)  \\\n+\tGUEST_SWITCH_GPR_ASM(r9)  \\\n+\tGUEST_SWITCH_GPR_ASM(r10) \\\n+\tGUEST_SWITCH_GPR_ASM(r11) \\\n+\tGUEST_SWITCH_GPR_ASM(r12) \\\n+\tGUEST_SWITCH_GPR_ASM(r13) \\\n+\tGUEST_SWITCH_GPR_ASM(r14) \\\n+\tGUEST_SWITCH_GPR_ASM(r15)\n+\n #include \"evmcs.h\"\n \n static inline int vmxon(u64 phys)\n@@ -363,9 +380,6 @@ static inline u64 vmptrstz(void)\n \treturn value;\n }\n \n-/*\n- * No guest state (e.g. GPRs) is established by this vmlaunch.\n- */\n static inline int vmlaunch(void)\n {\n \tint ret;\n@@ -373,34 +387,24 @@ static inline int vmlaunch(void)\n \tif (enable_evmcs)\n \t\treturn evmcs_vmlaunch();\n \n-\t__asm__ __volatile__(\"push %%rbp;\"\n-\t\t\t     \"push %%rcx;\"\n-\t\t\t     \"push %%rdx;\"\n-\t\t\t     \"push %%rsi;\"\n-\t\t\t     \"push %%rdi;\"\n-\t\t\t     \"push $0;\"\n+\t__asm__ __volatile__(\"push $0;\"\n \t\t\t     \"vmwrite %%rsp, %[host_rsp];\"\n \t\t\t     \"lea 1f(%%rip), %%rax;\"\n \t\t\t     \"vmwrite %%rax, %[host_rip];\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n \t\t\t     \"vmlaunch;\"\n \t\t\t     \"incq (%%rsp);\"\n-\t\t\t     \"1: pop %%rax;\"\n-\t\t\t     \"pop %%rdi;\"\n-\t\t\t     \"pop %%rsi;\"\n-\t\t\t     \"pop %%rdx;\"\n-\t\t\t     \"pop %%rcx;\"\n-\t\t\t     \"pop %%rbp;\"\n+\t\t\t     \"1: ;\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n+\t\t\t     \"pop %%rax;\"\n \t\t\t     : [ret]\"=\u0026a\"(ret)\n \t\t\t     : [host_rsp]\"r\"((u64)HOST_RSP),\n-\t\t\t       [host_rip]\"r\"((u64)HOST_RIP)\n-\t\t\t     : \"memory\", \"cc\", \"rbx\", \"r8\", \"r9\", \"r10\",\n-\t\t\t       \"r11\", \"r12\", \"r13\", \"r14\", \"r15\");\n+\t\t\t       [host_rip]\"r\"((u64)HOST_RIP),\n+\t\t\t       GUEST_REGS_OFFSETS\n+\t\t\t     : \"memory\", \"cc\");\n \treturn ret;\n }\n \n-/*\n- * No guest state (e.g. GPRs) is established by this vmresume.\n- */\n static inline int vmresume(void)\n {\n \tint ret;\n@@ -408,28 +412,21 @@ static inline int vmresume(void)\n \tif (enable_evmcs)\n \t\treturn evmcs_vmresume();\n \n-\t__asm__ __volatile__(\"push %%rbp;\"\n-\t\t\t     \"push %%rcx;\"\n-\t\t\t     \"push %%rdx;\"\n-\t\t\t     \"push %%rsi;\"\n-\t\t\t     \"push %%rdi;\"\n-\t\t\t     \"push $0;\"\n+\t__asm__ __volatile__(\"push $0;\"\n \t\t\t     \"vmwrite %%rsp, %[host_rsp];\"\n \t\t\t     \"lea 1f(%%rip), %%rax;\"\n \t\t\t     \"vmwrite %%rax, %[host_rip];\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n \t\t\t     \"vmresume;\"\n \t\t\t     \"incq (%%rsp);\"\n-\t\t\t     \"1: pop %%rax;\"\n-\t\t\t     \"pop %%rdi;\"\n-\t\t\t     \"pop %%rsi;\"\n-\t\t\t     \"pop %%rdx;\"\n-\t\t\t     \"pop %%rcx;\"\n-\t\t\t     \"pop %%rbp;\"\n+\t\t\t     \"1: ;\"\n+\t\t\t     VMX_SWITCH_GPRS_ASM\n+\t\t\t     \"pop %%rax;\"\n \t\t\t     : [ret]\"=\u0026a\"(ret)\n \t\t\t     : [host_rsp]\"r\"((u64)HOST_RSP),\n-\t\t\t       [host_rip]\"r\"((u64)HOST_RIP)\n-\t\t\t     : \"memory\", \"cc\", \"rbx\", \"r8\", \"r9\", \"r10\",\n-\t\t\t       \"r11\", \"r12\", \"r13\", \"r14\", \"r15\");\n+\t\t\t       [host_rip]\"r\"((u64)HOST_RIP),\n+\t\t\t       GUEST_REGS_OFFSETS\n+\t\t\t     : \"memory\", \"cc\");\n \treturn ret;\n }\n \ndiff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c\nindex 1d72dcdfce3b6..3e353ac39eeb7 100644\n--- a/tools/testing/selftests/kvm/lib/assert.c\n+++ b/tools/testing/selftests/kvm/lib/assert.c\n@@ -74,7 +74,7 @@ test_assert(bool exp, const char *exp_str,\n \tif (!(exp)) {\n \t\tva_start(ap, fmt);\n \n-\t\tfprintf(stderr, \"==== Test Assertion Failure ====\\n\"\n+\t\tfprintf(stderr, \"\\n==== Test Assertion Failure ====\\n\"\n \t\t\t\"  %s:%u: %s\\n\"\n \t\t\t\"  pid=%d tid=%d errno=%d - %s\\n\",\n \t\t\tfile, line, exp_str, getpid(), kvm_gettid(),\ndiff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c\nindex ef56dcefe0119..d31fa81ea0756 100644\n--- a/tools/testing/selftests/kvm/lib/x86/processor.c\n+++ b/tools/testing/selftests/kvm/lib/x86/processor.c\n@@ -28,6 +28,9 @@ bool host_cpu_is_hygon;\n bool host_cpu_is_amd_compatible;\n bool is_forced_emulation_enabled;\n u64 guest_tsc_khz;\n+struct kvm_mmu guest_mmu;\n+\n+struct guest_regs guest_regs;\n \n const char *ex_str(int vector)\n {\n@@ -829,6 +832,17 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)\n \tTEST_ASSERT(r \u003e 0, \"KVM_GET_TSC_KHZ did not provide a valid TSC frequency.\");\n \tguest_tsc_khz = r;\n \tsync_global_to_guest(vm, guest_tsc_khz);\n+\n+\t/*\n+\t * The guest MMU is just a placeholder to provide access to PTE masks\n+\t * (for now). The guest does not have mappings for its own page tables\n+\t * by default, so any meaningful use of guest page tables requires\n+\t * explicit setup by the test. Zero the PGD to make it obvious the guest\n+\t * page tables are not immediately usable by guest code.\n+\t */\n+\tguest_mmu = vm-\u003emmu;\n+\tguest_mmu.pgd = 0;\n+\tsync_global_to_guest(vm, guest_mmu);\n }\n \n void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)\ndiff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/selftests/kvm/lib/x86/svm.c\nindex 1445b890986fd..b05be50f075d6 100644\n--- a/tools/testing/selftests/kvm/lib/x86/svm.c\n+++ b/tools/testing/selftests/kvm/lib/x86/svm.c\n@@ -13,9 +13,6 @@\n \n #define SEV_DEV_PATH \"/dev/sev\"\n \n-struct gpr64_regs guest_regs;\n-u64 rflags;\n-\n /* Allocate memory regions for nested SVM tests.\n  *\n  * Input Args:\n@@ -137,46 +134,47 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip)\n  * save/restore 64-bit general registers except rax, rip, rsp\n  * which are directly handed through the VMCB guest processor state\n  */\n-#define SAVE_GPR_C\t\t\t\t\\\n-\t\"xchg %%rbx, guest_regs+0x20\\n\\t\"\t\\\n-\t\"xchg %%rcx, guest_regs+0x10\\n\\t\"\t\\\n-\t\"xchg %%rdx, guest_regs+0x18\\n\\t\"\t\\\n-\t\"xchg %%rbp, guest_regs+0x30\\n\\t\"\t\\\n-\t\"xchg %%rsi, guest_regs+0x38\\n\\t\"\t\\\n-\t\"xchg %%rdi, guest_regs+0x40\\n\\t\"\t\\\n-\t\"xchg %%r8,  guest_regs+0x48\\n\\t\"\t\\\n-\t\"xchg %%r9,  guest_regs+0x50\\n\\t\"\t\\\n-\t\"xchg %%r10, guest_regs+0x58\\n\\t\"\t\\\n-\t\"xchg %%r11, guest_regs+0x60\\n\\t\"\t\\\n-\t\"xchg %%r12, guest_regs+0x68\\n\\t\"\t\\\n-\t\"xchg %%r13, guest_regs+0x70\\n\\t\"\t\\\n-\t\"xchg %%r14, guest_regs+0x78\\n\\t\"\t\\\n-\t\"xchg %%r15, guest_regs+0x80\\n\\t\"\n-\n-#define LOAD_GPR_C      SAVE_GPR_C\n+#define SVM_SWITCH_GPRS_ASM \\\n+\tGUEST_SWITCH_GPR_ASM(rbx) \\\n+\tGUEST_SWITCH_GPR_ASM(rcx) \\\n+\tGUEST_SWITCH_GPR_ASM(rdx) \\\n+\tGUEST_SWITCH_GPR_ASM(rbp) \\\n+\tGUEST_SWITCH_GPR_ASM(rsi) \\\n+\tGUEST_SWITCH_GPR_ASM(rdi) \\\n+\tGUEST_SWITCH_GPR_ASM(r8)  \\\n+\tGUEST_SWITCH_GPR_ASM(r9)  \\\n+\tGUEST_SWITCH_GPR_ASM(r10) \\\n+\tGUEST_SWITCH_GPR_ASM(r11) \\\n+\tGUEST_SWITCH_GPR_ASM(r12) \\\n+\tGUEST_SWITCH_GPR_ASM(r13) \\\n+\tGUEST_SWITCH_GPR_ASM(r14) \\\n+\tGUEST_SWITCH_GPR_ASM(r15)\n \n /*\n  * selftests do not use interrupts so we dropped clgi/sti/cli/stgi\n- * for now. registers involved in LOAD/SAVE_GPR_C are eventually\n+ * for now. Registers involved in SVM_SWITCH_GPRS_ASM are eventually\n  * unmodified so they do not need to be in the clobber list.\n  */\n void run_guest(struct vmcb *vmcb, u64 vmcb_gpa)\n {\n \tasm volatile (\n \t\t\"vmload %[vmcb_gpa]\\n\\t\"\n-\t\t\"mov rflags, %%r15\\n\\t\"\t// rflags\n-\t\t\"mov %%r15, 0x170(%[vmcb])\\n\\t\"\n-\t\t\"mov guest_regs, %%r15\\n\\t\"\t// rax\n-\t\t\"mov %%r15, 0x1f8(%[vmcb])\\n\\t\"\n-\t\tLOAD_GPR_C\n+\t\t\"mov \" GUEST_REG(rflags) \", %%r15\\n\\t\"\n+\t\t\"mov %%r15, %[vmcb_rflags]\\n\\t\"\n+\t\t\"mov \" GUEST_REG(rax) \", %%r15\\n\\t\"\n+\t\t\"mov %%r15, %[vmcb_rax]\\n\\t\"\n+\t\tSVM_SWITCH_GPRS_ASM\n \t\t\"vmrun %[vmcb_gpa]\\n\\t\"\n-\t\tSAVE_GPR_C\n-\t\t\"mov 0x170(%[vmcb]), %%r15\\n\\t\"\t// rflags\n-\t\t\"mov %%r15, rflags\\n\\t\"\n-\t\t\"mov 0x1f8(%[vmcb]), %%r15\\n\\t\"\t// rax\n-\t\t\"mov %%r15, guest_regs\\n\\t\"\n+\t\tSVM_SWITCH_GPRS_ASM\n+\t\t\"mov %[vmcb_rflags], %%r15\\n\\t\"\n+\t\t\"mov %%r15, \" GUEST_REG(rflags) \"\\n\\t\"\n+\t\t\"mov %[vmcb_rax], %%r15\\n\\t\"\t// rax\n+\t\t\"mov %%r15, \" GUEST_REG(rax) \"\\n\\t\"\n \t\t\"vmsave %[vmcb_gpa]\\n\\t\"\n-\t\t: : [vmcb] \"r\" (vmcb), [vmcb_gpa] \"a\" (vmcb_gpa)\n+\t\t: [vmcb_rflags] \"+m\" (vmcb-\u003esave.rflags),\n+\t\t  [vmcb_rax] \"+m\" (vmcb-\u003esave.rax)\n+\t\t: [vmcb_gpa] \"a\" (vmcb_gpa),\n+\t\t  GUEST_REGS_OFFSETS\n \t\t: \"r15\", \"memory\");\n }\n \ndiff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c\nindex e7dd5791959ba..38050c60a0670 100644\n--- a/tools/testing/selftests/kvm/lib/x86/ucall.c\n+++ b/tools/testing/selftests/kvm/lib/x86/ucall.c\n@@ -10,36 +10,8 @@\n \n void ucall_arch_do_ucall(gva_t uc)\n {\n-\t/*\n-\t * FIXME: Revert this hack (the entire commit that added it) once nVMX\n-\t * preserves L2 GPRs across a nested VM-Exit.  If a ucall from L2, e.g.\n-\t * to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be\n-\t * clobbered by L1.  Save and restore non-volatile GPRs (clobbering RBP\n-\t * in particular is problematic) along with RDX and RDI (which are\n-\t * inputs), and clobber volatile GPRs. *sigh*\n-\t */\n-#define HORRIFIC_L2_UCALL_CLOBBER_HACK\t\\\n-\t\"rcx\", \"rsi\", \"r8\", \"r9\", \"r10\", \"r11\"\n-\n-\tasm volatile(\"push %%rbp\\n\\t\"\n-\t\t     \"push %%r15\\n\\t\"\n-\t\t     \"push %%r14\\n\\t\"\n-\t\t     \"push %%r13\\n\\t\"\n-\t\t     \"push %%r12\\n\\t\"\n-\t\t     \"push %%rbx\\n\\t\"\n-\t\t     \"push %%rdx\\n\\t\"\n-\t\t     \"push %%rdi\\n\\t\"\n-\t\t     \"in %[port], %%al\\n\\t\"\n-\t\t     \"pop %%rdi\\n\\t\"\n-\t\t     \"pop %%rdx\\n\\t\"\n-\t\t     \"pop %%rbx\\n\\t\"\n-\t\t     \"pop %%r12\\n\\t\"\n-\t\t     \"pop %%r13\\n\\t\"\n-\t\t     \"pop %%r14\\n\\t\"\n-\t\t     \"pop %%r15\\n\\t\"\n-\t\t     \"pop %%rbp\\n\\t\"\n-\t\t: : [port] \"d\" (UCALL_PIO_PORT), \"D\" (uc) : \"rax\", \"memory\",\n-\t\t     HORRIFIC_L2_UCALL_CLOBBER_HACK);\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)\ndiff --git a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c\nindex 77ce87c41a868..aa7f3b405fd32 100644\n--- a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c\n+++ b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c\n@@ -22,9 +22,6 @@\n \n #define SYNC_PORT\t0xe\n \n-#define STR(x) #x\n-#define XSTR(s) STR(s)\n-\n /*\n  * SMI handler: runs in real-address mode.\n  * Reports SMRAM_STAGE via port IO, then does RSM.\n@@ -37,7 +34,7 @@ static u8 smi_handler[] = {\n \n static inline void sync_with_host(u64 phase)\n {\n-\tasm volatile(\"in $\" XSTR(SYNC_PORT) \", %%al \\n\"\n+\tasm volatile(\"in $\" __stringify(SYNC_PORT) \", %%al \\n\"\n \t\t     : \"+a\" (phase));\n }\n \ndiff --git a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c\nindex 753a0e730ea8d..4931ec22768ee 100644\n--- a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c\n+++ b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c\n@@ -6,7 +6,6 @@\n  */\n #include \u003casm/kvm_para.h\u003e\n #include \u003clinux/kvm_para.h\u003e\n-#include \u003clinux/stringify.h\u003e\n #include \u003cstdint.h\u003e\n \n #include \"kvm_test_harness.h\"\ndiff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c\nnew file mode 100644\nindex 0000000000000..ba1d723001e94\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c\n@@ -0,0 +1,291 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+#include \u003cstdio.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003cerrno.h\u003e\n+#include \u003csys/types.h\u003e\n+#include \u003ctime.h\u003e\n+#include \u003cpthread.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003cgetopt.h\u003e\n+\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+#include \"svm_util.h\"\n+#include \"vmx.h\"\n+\n+#define NR_ITERATIONS\t\t500\n+\n+#define PTRS_PER_PTE\t\t512\n+#define PXD_INDEX(vaddr, level)\t(((vaddr) \u003e\u003e PG_LEVEL_SHIFT(level)) \u0026 (PTRS_PER_PTE - 1))\n+\n+#define TEST_MEM_BASE_GVA\t0xc0000000ULL\n+#define TEST_PGTABLE_GVA_OFFSET\t0xd0000000ULL\n+#define PATTERN\t\t\t0xabcdefabcdefabcdULL\n+\n+static u64 expected_vaddr;\n+static u64 guest_faults;\n+\n+static u64 *guest_get_pte(u64 vaddr)\n+{\n+\tu64 pgtable_pa, pte;\n+\tu64 *pgtable;\n+\tint level;\n+\n+\tlevel = (get_cr4() \u0026 X86_CR4_LA57) ? PG_LEVEL_256T : PG_LEVEL_512G;\n+\n+\tpgtable_pa = get_cr3() \u0026 PHYSICAL_PAGE_MASK;\n+\tfor (; level \u003e PG_LEVEL_4K; level--) {\n+\t\tpgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);\n+\t\tpte = pgtable[PXD_INDEX(vaddr, level)];\n+\t\tGUEST_ASSERT(pte \u0026 PTE_PRESENT_MASK(\u0026guest_mmu));\n+\t\tGUEST_ASSERT(!(pte \u0026 PTE_HUGE_MASK(\u0026guest_mmu)));\n+\t\tpgtable_pa = PTE_GET_PA(pte);\n+\t}\n+\n+\tpgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);\n+\treturn \u0026pgtable[PXD_INDEX(vaddr, PG_LEVEL_4K)];\n+}\n+\n+static void guest_pf_handler(struct ex_regs *regs)\n+{\n+\tu64 fault_addr;\n+\tu64 *ptep;\n+\n+\tfault_addr = get_cr2();\n+\tGUEST_ASSERT_EQ(fault_addr, READ_ONCE(expected_vaddr));\n+\n+\tptep = guest_get_pte(fault_addr);\n+\tGUEST_ASSERT(ptep);\n+\tGUEST_ASSERT(!(*ptep \u0026 PTE_PRESENT_MASK(\u0026guest_mmu)));\n+\n+\t*ptep |= PTE_PRESENT_MASK(\u0026guest_mmu);\n+\tguest_faults++;\n+}\n+\n+static void guest_access_memory(void *arg)\n+{\n+\tu64 vaddr, val;\n+\tint i;\n+\n+\tfor (i = 0; ; i++) {\n+\t\tvaddr = TEST_MEM_BASE_GVA + (i % PTRS_PER_PTE) * PAGE_SIZE;\n+\t\tWRITE_ONCE(expected_vaddr, vaddr);\n+\n+\t\t/* Read to trigger #PF */\n+\t\tval = READ_ONCE(*(u64 *)vaddr);\n+\t\tGUEST_ASSERT_EQ(val, PATTERN);\n+\n+\t\t/* Clear the present bit again so it faults next time */\n+\t\t*guest_get_pte(vaddr) \u0026= ~PTE_PRESENT_MASK(\u0026guest_mmu);\n+\t\tinvlpg(vaddr);\n+\t}\n+}\n+\n+static void l1_svm_code(struct svm_test_data *svm)\n+{\n+\tgeneric_svm_setup(svm, guest_access_memory);\n+\tsvm-\u003evmcb-\u003econtrol.intercept_exceptions |= BIT(UD_VECTOR);\n+\n+\twhile (1) {\n+\t\trun_guest(svm-\u003evmcb, svm-\u003evmcb_gpa);\n+\t\tGUEST_ASSERT_EQ(svm-\u003evmcb-\u003econtrol.exit_code,\n+\t\t\t\t(SVM_EXIT_EXCP_BASE + UD_VECTOR));\n+\t}\n+}\n+\n+static void l1_vmx_code(struct vmx_pages *vmx)\n+{\n+\tGUEST_ASSERT(prepare_for_vmx_operation(vmx));\n+\tGUEST_ASSERT(load_vmcs(vmx));\n+\tprepare_vmcs(vmx, guest_access_memory);\n+\n+\t/* Intercept UD, ignore any #PF */\n+\tGUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR) | BIT(PF_VECTOR)));\n+\tGUEST_ASSERT(!vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0));\n+\tGUEST_ASSERT(!vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, -1));\n+\n+\tGUEST_ASSERT(!vmlaunch());\n+\twhile (1) {\n+\t\tGUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI);\n+\t\tGUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) \u0026 0xff, UD_VECTOR);\n+\t\tGUEST_ASSERT(!vmresume());\n+\t}\n+}\n+\n+static void l1_guest_code(void *test_data)\n+{\n+\tif (this_cpu_has(X86_FEATURE_SVM))\n+\t\tl1_svm_code(test_data);\n+\telse\n+\t\tl1_vmx_code(test_data);\n+}\n+\n+static void *sigusr_thread_fn(void *arg)\n+{\n+\tpthread_t vcpu_thread = (pthread_t)arg;\n+\n+\tfor (;;) {\n+\t\tpthread_testcancel();\n+\t\tpthread_kill(vcpu_thread, SIGUSR1);\n+\t\tusleep(msecs_to_usecs(1));\n+\t}\n+\treturn NULL;\n+}\n+\n+static void dummy_signal_handler(int signo) {}\n+static struct sigaction sa;\n+\n+static void vcpu_sigusr_listen(void)\n+{\n+\tsa.sa_handler = dummy_signal_handler;\n+\tsigaction(SIGUSR1, \u0026sa, NULL);\n+}\n+\n+static void vcpu_sigusr_ignore(void)\n+{\n+\tsa.sa_handler = SIG_IGN;\n+\tsigaction(SIGUSR1, \u0026sa, NULL);\n+}\n+\n+static void kvm_x86_state_queue_ud(struct kvm_x86_state *state)\n+{\n+\tif (state-\u003eevents.exception.pending || state-\u003eevents.exception.injected)\n+\t\treturn;\n+\n+\tstate-\u003eevents.flags |= KVM_VCPUEVENT_VALID_PAYLOAD;\n+\tstate-\u003eevents.exception.pending = true;\n+\tstate-\u003eevents.exception.injected = false;\n+\tstate-\u003eevents.exception.nr = UD_VECTOR;\n+\tstate-\u003eevents.exception.has_error_code = false;\n+\tstate-\u003eevents.exception_has_payload = false;\n+}\n+\n+static void run_test(bool nested)\n+{\n+\tstruct kvm_x86_state *state;\n+\tint r, i, level;\n+\tpthread_t sigusr_thread;\n+\tgpa_t gpa, pgtable_gpa;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct ucall uc;\n+\tu64 *pgtable;\n+\tgva_t gva;\n+\tu64 pte;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, nested ? l1_guest_code : guest_access_memory);\n+\tvm_install_exception_handler(vm, PF_VECTOR, guest_pf_handler);\n+\tvm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);\n+\n+\tif (nested) {\n+\t\tif (kvm_cpu_has(X86_FEATURE_SVM))\n+\t\t\tvcpu_alloc_svm(vm, \u0026gva);\n+\t\telse\n+\t\t\tvcpu_alloc_vmx(vm, \u0026gva);\n+\t\tvcpu_args_set(vcpu, 1, gva);\n+\t}\n+\n+\t/* Allocate a page and write the pattern to it */\n+\tgva = vm_alloc_page(vm);\n+\t*(u64 *)addr_gva2hva(vm, gva) = PATTERN;\n+\tgpa = addr_gva2gpa(vm, gva);\n+\n+\t/*\n+\t * Map all virtual addresses to the pattern page and clear the present\n+\t * bit such that guest accesses will cause a #PF.\n+\t */\n+\tfor (i = 0; i \u003c PTRS_PER_PTE; i++) {\n+\t\tgva = TEST_MEM_BASE_GVA + i * getpagesize();\n+\t\tvirt_pg_map(vm, gva, gpa);\n+\t\t*vm_get_pte(vm, gva) \u0026= ~PTE_PRESENT_MASK(\u0026vm-\u003emmu);\n+\t}\n+\n+\t/*\n+\t * Now create mappings for the page tables created above so that the\n+\t * guest #PF handler can walk them. All PTEs for test virtual addresses\n+\t * should lie on the same PTE page, so one page is mapped for each page\n+\t * table level.\n+\t *\n+\t * Use an offset for the GVA instead of creating identity mappings to\n+\t * avoid collision with existing mappings at low GVAs (e.g. ELF).\n+\t */\n+\tpgtable_gpa = vm-\u003emmu.pgd;\n+\tfor (level = vm-\u003emmu.pgtable_levels; level \u003e= PG_LEVEL_4K; level--) {\n+\t\tvirt_map(vm, pgtable_gpa + TEST_PGTABLE_GVA_OFFSET, pgtable_gpa, 1);\n+\t\tpgtable = addr_gpa2hva(vm, pgtable_gpa);\n+\t\tpte = pgtable[PXD_INDEX(TEST_MEM_BASE_GVA, level)];\n+\t\tpgtable_gpa = PTE_GET_PA(pte);\n+\t}\n+\n+\t/* Initialize the thread sending SIGUSR and install the handler */\n+\tvcpu_sigusr_ignore();\n+\tr = pthread_create(\u0026sigusr_thread, NULL, sigusr_thread_fn,\n+\t\t\t   (void *)pthread_self());\n+\tTEST_ASSERT(!r, \"pthread_create() failed: %d\", r);\n+\n+\tfor (i = 1; i \u003c= NR_ITERATIONS; i++) {\n+\t\t/*\n+\t\t * Only handle SIGUSR while the vCPU is running, otherwise\n+\t\t * ignore it to avoid interrupting other ioctls/syscalls.\n+\t\t */\n+\t\tvcpu_sigusr_listen();\n+\t\tr = __vcpu_run(vcpu);\n+\t\tif (r == -1)\n+\t\t\tTEST_ASSERT_EQ(errno, EINTR);\n+\t\tvcpu_sigusr_ignore();\n+\n+\t\t/* The guest only exits due to a signal or failed assertion */\n+\t\tif (!r) {\n+\t\t\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\t\t\tTEST_ASSERT_EQ(get_ucall(vcpu, \u0026uc), UCALL_ABORT);\n+\t\t\tREPORT_GUEST_ASSERT(uc);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tstate = vcpu_save_state(vcpu);\n+\n+\t\t/*\n+\t\t * If the vCPU is in guest mode, inject a #UD to trigger an\n+\t\t * L2-\u003eL1 VM-Exit every other iteration.\n+\t\t */\n+\t\tif (kvm_x86_state_is_guest_mode(state) \u0026\u0026 i % 2 == 0)\n+\t\t\tkvm_x86_state_queue_ud(state);\n+\n+\t\tkvm_vm_release(vm);\n+\t\tvcpu = vm_recreate_with_one_vcpu(vm);\n+\t\tvm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);\n+\t\tvcpu_load_state(vcpu, state);\n+\t\tkvm_x86_state_cleanup(state);\n+\n+\t\tpr_info(\"\\rSave+restore iterations: %d\", i);\n+\t}\n+\tpr_info(\"\\n\");\n+\n+\tsync_global_from_guest(vm, guest_faults);\n+\tTEST_ASSERT(guest_faults \u003e 0, \"No guest page faults triggered\");\n+\tpr_info(\"Guest page faults%s: %lu\\n\", nested ? \" (in L2)\" : \"\", guest_faults);\n+\n+\tpthread_cancel(sigusr_thread);\n+\tpthread_join(sigusr_thread, NULL);\n+\tkvm_vm_free(vm);\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tpr_info(\"Running save+restore stress test...\\n\");\n+\trun_test(/*nested=*/false);\n+\n+\tif (!kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD) ||\n+\t    !kvm_has_cap(KVM_CAP_NESTED_STATE) ||\n+\t    (!kvm_cpu_has(X86_FEATURE_SVM) \u0026\u0026 !kvm_cpu_has(X86_FEATURE_VMX))) {\n+\t\tpr_info(\"Nested virtualization not supported, skipping nested test\\n\");\n+\t\treturn 0;\n+\t}\n+\n+\tpr_info(\"Running save+restore stress test with a nested guest...\\n\");\n+\trun_test(/*nested=*/true);\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c\nindex e2542f4ced605..d1edafd5af755 100644\n--- a/tools/testing/selftests/kvm/x86/smm_test.c\n+++ b/tools/testing/selftests/kvm/x86/smm_test.c\n@@ -22,9 +22,6 @@\n #define SMRAM_GPA 0x1000000\n #define SMRAM_STAGE 0xfe\n \n-#define STR(x) #x\n-#define XSTR(s) STR(s)\n-\n #define SYNC_PORT 0xe\n #define DONE 0xff\n \n@@ -42,7 +39,7 @@ u8 smi_handler[] = {\n \n static inline void sync_with_host(u64 phase)\n {\n-\tasm volatile(\"in $\" XSTR(SYNC_PORT)\", %%al \\n\"\n+\tasm volatile(\"in $\" __stringify(SYNC_PORT)\", %%al \\n\"\n \t\t     : \"+a\" (phase));\n }\n \n"
}

2/1 2026/07/28 19:39 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds and modifies KVM selftests in tools/testing/selftests/kvm/. It does not modify any core kernel code or drivers. Since the fuzzing targets the kernel and not userspace testing tools, there are no functional kernel changes to fuzz.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a '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:
For your convenience, here is the diff of the changes:
commit e294868ad09135af10e01e6604f3a97911fc54ea
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jul 28 19:39:41 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 88c6c8046ddec..e67b1e9893f68 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -112,6 +112,7 @@ TEST_GEN_PROGS_x86 += x86/set_sregs_test
 TEST_GEN_PROGS_x86 += x86/smaller_maxphyaddr_emulation_test
 TEST_GEN_PROGS_x86 += x86/smm_test
 TEST_GEN_PROGS_x86 += x86/state_test
+TEST_GEN_PROGS_x86 += x86/save_restore_pf_stress_test
 TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test
 TEST_GEN_PROGS_x86 += x86/svm_vmcall_test
 TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index d64c8a228207a..e8356ee54d7b9 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -23,6 +23,7 @@
 
 #include <linux/mman.h>
 #include <linux/types.h>
+#include <linux/stringify.h>
 
 #define msecs_to_usecs(msec)    ((msec) * 1000ULL)
 
diff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h
index be79bda024bf1..82a8ea6b661f0 100644
--- a/tools/testing/selftests/kvm/include/x86/evmcs.h
+++ b/tools/testing/selftests/kvm/include/x86/evmcs.h
@@ -1207,30 +1207,23 @@ static inline int evmcs_vmlaunch(void)
 
 	current_evmcs->hv_clean_fields = 0;
 
-	__asm__ __volatile__("push %%rbp;"
-			     "push %%rcx;"
-			     "push %%rdx;"
-			     "push %%rsi;"
-			     "push %%rdi;"
-			     "push $0;"
+	__asm__ __volatile__("push $0;"
 			     "mov %%rsp, (%[host_rsp]);"
 			     "lea 1f(%%rip), %%rax;"
 			     "mov %%rax, (%[host_rip]);"
+			     VMX_SWITCH_GPRS_ASM
 			     "vmlaunch;"
 			     "incq (%%rsp);"
-			     "1: pop %%rax;"
-			     "pop %%rdi;"
-			     "pop %%rsi;"
-			     "pop %%rdx;"
-			     "pop %%rcx;"
-			     "pop %%rbp;"
+			     "1: ;"
+			     VMX_SWITCH_GPRS_ASM
+			     "pop %%rax;"
 			     : [ret]"=&a"(ret)
 			     : [host_rsp]"r"
 			       ((u64)&current_evmcs->host_rsp),
 			       [host_rip]"r"
-			       ((u64)&current_evmcs->host_rip)
-			     : "memory", "cc", "rbx", "r8", "r9", "r10",
-			       "r11", "r12", "r13", "r14", "r15");
+			       ((u64)&current_evmcs->host_rip),
+			       GUEST_REGS_OFFSETS
+			     : "memory", "cc");
 	return ret;
 }
 
@@ -1246,30 +1239,23 @@ static inline int evmcs_vmresume(void)
 	/* HOST_RSP */
 	current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_POINTER;
 
-	__asm__ __volatile__("push %%rbp;"
-			     "push %%rcx;"
-			     "push %%rdx;"
-			     "push %%rsi;"
-			     "push %%rdi;"
-			     "push $0;"
+	__asm__ __volatile__("push $0;"
 			     "mov %%rsp, (%[host_rsp]);"
 			     "lea 1f(%%rip), %%rax;"
 			     "mov %%rax, (%[host_rip]);"
+			     VMX_SWITCH_GPRS_ASM
 			     "vmresume;"
 			     "incq (%%rsp);"
-			     "1: pop %%rax;"
-			     "pop %%rdi;"
-			     "pop %%rsi;"
-			     "pop %%rdx;"
-			     "pop %%rcx;"
-			     "pop %%rbp;"
+			     "1: ;"
+			     VMX_SWITCH_GPRS_ASM
+			     "pop %%rax;"
 			     : [ret]"=&a"(ret)
 			     : [host_rsp]"r"
 			       ((u64)&current_evmcs->host_rsp),
 			       [host_rip]"r"
-			       ((u64)&current_evmcs->host_rip)
-			     : "memory", "cc", "rbx", "r8", "r9", "r10",
-			       "r11", "r12", "r13", "r14", "r15");
+			       ((u64)&current_evmcs->host_rip),
+			       GUEST_REGS_OFFSETS
+			     : "memory", "cc");
 	return ret;
 }
 
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index b161174ece453..461c8aef7d37b 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -24,6 +24,7 @@ extern bool host_cpu_is_amd;
 extern bool host_cpu_is_hygon;
 extern bool host_cpu_is_amd_compatible;
 extern u64 guest_tsc_khz;
+extern struct kvm_mmu guest_mmu;
 
 #ifndef MAX_NR_CPUID_ENTRIES
 #define MAX_NR_CPUID_ENTRIES 100
@@ -398,8 +399,7 @@ static inline unsigned int x86_model(unsigned int eax)
 #define PTE_GET_PA(pte)		((pte) & PHYSICAL_PAGE_MASK)
 #define PTE_GET_PFN(pte)        (PTE_GET_PA(pte) >> PAGE_SHIFT)
 
-/* General Registers in 64-Bit Mode */
-struct gpr64_regs {
+struct guest_regs {
 	u64 rax;
 	u64 rcx;
 	u64 rdx;
@@ -416,8 +416,38 @@ struct gpr64_regs {
 	u64 r13;
 	u64 r14;
 	u64 r15;
+	u64 rflags;
 };
 
+extern struct guest_regs guest_regs;
+
+#define GUEST_REG_OFFSET(name) \
+	[off_##name] "i" (offsetof(struct guest_regs, name))
+
+#define GUEST_REGS_OFFSETS	\
+	GUEST_REG_OFFSET(rax),	\
+	GUEST_REG_OFFSET(rcx),	\
+	GUEST_REG_OFFSET(rdx),	\
+	GUEST_REG_OFFSET(rbx),	\
+	GUEST_REG_OFFSET(rsp),	\
+	GUEST_REG_OFFSET(rbp),	\
+	GUEST_REG_OFFSET(rsi),	\
+	GUEST_REG_OFFSET(rdi),	\
+	GUEST_REG_OFFSET(r8),	\
+	GUEST_REG_OFFSET(r9),	\
+	GUEST_REG_OFFSET(r10),	\
+	GUEST_REG_OFFSET(r11),	\
+	GUEST_REG_OFFSET(r12),	\
+	GUEST_REG_OFFSET(r13),	\
+	GUEST_REG_OFFSET(r14),	\
+	GUEST_REG_OFFSET(r15),	\
+	GUEST_REG_OFFSET(rflags)
+
+#define GUEST_REG(name) "guest_regs + %c[off_" #name "]"
+
+#define GUEST_SWITCH_GPR_ASM(name) \
+	"xchg %%" #name ", " GUEST_REG(name) "\n\t"
+
 struct desc64 {
 	u16 limit0;
 	u16 base0;
@@ -584,6 +614,15 @@ static inline void set_cr0(u64 val)
 	__asm__ __volatile__("mov %0, %%cr0" : : "r" (val) : "memory");
 }
 
+static inline u64 get_cr2(void)
+{
+	u64 cr2;
+
+	__asm__ __volatile__("mov %%cr2, %[cr2]"
+			     : /* output */ [cr2]"=r"(cr2));
+	return cr2;
+}
+
 static inline u64 get_cr3(void)
 {
 	u64 cr3;
@@ -879,6 +918,11 @@ static inline void write_sse_reg(int reg, const sse128_t *data)
 	}
 }
 
+static inline void invlpg(u64 addr)
+{
+	__asm__ __volatile__("invlpg (%0)" : : "r"(addr) : "memory");
+}
+
 static inline void cpu_relax(void)
 {
 	asm volatile("rep; nop" ::: "memory");
@@ -915,6 +959,11 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu);
 void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state);
 void kvm_x86_state_cleanup(struct kvm_x86_state *state);
 
+static inline bool kvm_x86_state_is_guest_mode(struct kvm_x86_state *state)
+{
+	return state->nested.size && (state->nested.flags & KVM_STATE_NESTED_GUEST_MODE);
+}
+
 const struct kvm_msr_list *kvm_get_msr_index_list(void);
 const struct kvm_msr_list *kvm_get_feature_msr_index_list(void);
 bool kvm_msr_is_in_save_restore_list(u32 msr_index);
diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h
index 4bcfd60e3aecb..04f5e34dea3ae 100644
--- a/tools/testing/selftests/kvm/include/x86/vmx.h
+++ b/tools/testing/selftests/kvm/include/x86/vmx.h
@@ -290,6 +290,23 @@ struct vmx_msr_entry {
 	u64 value;
 } __attribute__ ((aligned(16)));
 
+#define VMX_SWITCH_GPRS_ASM \
+	GUEST_SWITCH_GPR_ASM(rax) \
+	GUEST_SWITCH_GPR_ASM(rbx) \
+	GUEST_SWITCH_GPR_ASM(rcx) \
+	GUEST_SWITCH_GPR_ASM(rdx) \
+	GUEST_SWITCH_GPR_ASM(rbp) \
+	GUEST_SWITCH_GPR_ASM(rsi) \
+	GUEST_SWITCH_GPR_ASM(rdi) \
+	GUEST_SWITCH_GPR_ASM(r8)  \
+	GUEST_SWITCH_GPR_ASM(r9)  \
+	GUEST_SWITCH_GPR_ASM(r10) \
+	GUEST_SWITCH_GPR_ASM(r11) \
+	GUEST_SWITCH_GPR_ASM(r12) \
+	GUEST_SWITCH_GPR_ASM(r13) \
+	GUEST_SWITCH_GPR_ASM(r14) \
+	GUEST_SWITCH_GPR_ASM(r15)
+
 #include "evmcs.h"
 
 static inline int vmxon(u64 phys)
@@ -363,9 +380,6 @@ static inline u64 vmptrstz(void)
 	return value;
 }
 
-/*
- * No guest state (e.g. GPRs) is established by this vmlaunch.
- */
 static inline int vmlaunch(void)
 {
 	int ret;
@@ -373,34 +387,24 @@ static inline int vmlaunch(void)
 	if (enable_evmcs)
 		return evmcs_vmlaunch();
 
-	__asm__ __volatile__("push %%rbp;"
-			     "push %%rcx;"
-			     "push %%rdx;"
-			     "push %%rsi;"
-			     "push %%rdi;"
-			     "push $0;"
+	__asm__ __volatile__("push $0;"
 			     "vmwrite %%rsp, %[host_rsp];"
 			     "lea 1f(%%rip), %%rax;"
 			     "vmwrite %%rax, %[host_rip];"
+			     VMX_SWITCH_GPRS_ASM
 			     "vmlaunch;"
 			     "incq (%%rsp);"
-			     "1: pop %%rax;"
-			     "pop %%rdi;"
-			     "pop %%rsi;"
-			     "pop %%rdx;"
-			     "pop %%rcx;"
-			     "pop %%rbp;"
+			     "1: ;"
+			     VMX_SWITCH_GPRS_ASM
+			     "pop %%rax;"
 			     : [ret]"=&a"(ret)
 			     : [host_rsp]"r"((u64)HOST_RSP),
-			       [host_rip]"r"((u64)HOST_RIP)
-			     : "memory", "cc", "rbx", "r8", "r9", "r10",
-			       "r11", "r12", "r13", "r14", "r15");
+			       [host_rip]"r"((u64)HOST_RIP),
+			       GUEST_REGS_OFFSETS
+			     : "memory", "cc");
 	return ret;
 }
 
-/*
- * No guest state (e.g. GPRs) is established by this vmresume.
- */
 static inline int vmresume(void)
 {
 	int ret;
@@ -408,28 +412,21 @@ static inline int vmresume(void)
 	if (enable_evmcs)
 		return evmcs_vmresume();
 
-	__asm__ __volatile__("push %%rbp;"
-			     "push %%rcx;"
-			     "push %%rdx;"
-			     "push %%rsi;"
-			     "push %%rdi;"
-			     "push $0;"
+	__asm__ __volatile__("push $0;"
 			     "vmwrite %%rsp, %[host_rsp];"
 			     "lea 1f(%%rip), %%rax;"
 			     "vmwrite %%rax, %[host_rip];"
+			     VMX_SWITCH_GPRS_ASM
 			     "vmresume;"
 			     "incq (%%rsp);"
-			     "1: pop %%rax;"
-			     "pop %%rdi;"
-			     "pop %%rsi;"
-			     "pop %%rdx;"
-			     "pop %%rcx;"
-			     "pop %%rbp;"
+			     "1: ;"
+			     VMX_SWITCH_GPRS_ASM
+			     "pop %%rax;"
 			     : [ret]"=&a"(ret)
 			     : [host_rsp]"r"((u64)HOST_RSP),
-			       [host_rip]"r"((u64)HOST_RIP)
-			     : "memory", "cc", "rbx", "r8", "r9", "r10",
-			       "r11", "r12", "r13", "r14", "r15");
+			       [host_rip]"r"((u64)HOST_RIP),
+			       GUEST_REGS_OFFSETS
+			     : "memory", "cc");
 	return ret;
 }
 
diff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c
index 1d72dcdfce3b6..3e353ac39eeb7 100644
--- a/tools/testing/selftests/kvm/lib/assert.c
+++ b/tools/testing/selftests/kvm/lib/assert.c
@@ -74,7 +74,7 @@ test_assert(bool exp, const char *exp_str,
 	if (!(exp)) {
 		va_start(ap, fmt);
 
-		fprintf(stderr, "==== Test Assertion Failure ====\n"
+		fprintf(stderr, "\n==== Test Assertion Failure ====\n"
 			"  %s:%u: %s\n"
 			"  pid=%d tid=%d errno=%d - %s\n",
 			file, line, exp_str, getpid(), kvm_gettid(),
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index ef56dcefe0119..d31fa81ea0756 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -28,6 +28,9 @@ bool host_cpu_is_hygon;
 bool host_cpu_is_amd_compatible;
 bool is_forced_emulation_enabled;
 u64 guest_tsc_khz;
+struct kvm_mmu guest_mmu;
+
+struct guest_regs guest_regs;
 
 const char *ex_str(int vector)
 {
@@ -829,6 +832,17 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
 	TEST_ASSERT(r > 0, "KVM_GET_TSC_KHZ did not provide a valid TSC frequency.");
 	guest_tsc_khz = r;
 	sync_global_to_guest(vm, guest_tsc_khz);
+
+	/*
+	 * The guest MMU is just a placeholder to provide access to PTE masks
+	 * (for now). The guest does not have mappings for its own page tables
+	 * by default, so any meaningful use of guest page tables requires
+	 * explicit setup by the test. Zero the PGD to make it obvious the guest
+	 * page tables are not immediately usable by guest code.
+	 */
+	guest_mmu = vm->mmu;
+	guest_mmu.pgd = 0;
+	sync_global_to_guest(vm, guest_mmu);
 }
 
 void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
diff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/selftests/kvm/lib/x86/svm.c
index 1445b890986fd..b05be50f075d6 100644
--- a/tools/testing/selftests/kvm/lib/x86/svm.c
+++ b/tools/testing/selftests/kvm/lib/x86/svm.c
@@ -13,9 +13,6 @@
 
 #define SEV_DEV_PATH "/dev/sev"
 
-struct gpr64_regs guest_regs;
-u64 rflags;
-
 /* Allocate memory regions for nested SVM tests.
  *
  * Input Args:
@@ -137,46 +134,47 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip)
  * save/restore 64-bit general registers except rax, rip, rsp
  * which are directly handed through the VMCB guest processor state
  */
-#define SAVE_GPR_C				\
-	"xchg %%rbx, guest_regs+0x20\n\t"	\
-	"xchg %%rcx, guest_regs+0x10\n\t"	\
-	"xchg %%rdx, guest_regs+0x18\n\t"	\
-	"xchg %%rbp, guest_regs+0x30\n\t"	\
-	"xchg %%rsi, guest_regs+0x38\n\t"	\
-	"xchg %%rdi, guest_regs+0x40\n\t"	\
-	"xchg %%r8,  guest_regs+0x48\n\t"	\
-	"xchg %%r9,  guest_regs+0x50\n\t"	\
-	"xchg %%r10, guest_regs+0x58\n\t"	\
-	"xchg %%r11, guest_regs+0x60\n\t"	\
-	"xchg %%r12, guest_regs+0x68\n\t"	\
-	"xchg %%r13, guest_regs+0x70\n\t"	\
-	"xchg %%r14, guest_regs+0x78\n\t"	\
-	"xchg %%r15, guest_regs+0x80\n\t"
-
-#define LOAD_GPR_C      SAVE_GPR_C
+#define SVM_SWITCH_GPRS_ASM \
+	GUEST_SWITCH_GPR_ASM(rbx) \
+	GUEST_SWITCH_GPR_ASM(rcx) \
+	GUEST_SWITCH_GPR_ASM(rdx) \
+	GUEST_SWITCH_GPR_ASM(rbp) \
+	GUEST_SWITCH_GPR_ASM(rsi) \
+	GUEST_SWITCH_GPR_ASM(rdi) \
+	GUEST_SWITCH_GPR_ASM(r8)  \
+	GUEST_SWITCH_GPR_ASM(r9)  \
+	GUEST_SWITCH_GPR_ASM(r10) \
+	GUEST_SWITCH_GPR_ASM(r11) \
+	GUEST_SWITCH_GPR_ASM(r12) \
+	GUEST_SWITCH_GPR_ASM(r13) \
+	GUEST_SWITCH_GPR_ASM(r14) \
+	GUEST_SWITCH_GPR_ASM(r15)
 
 /*
  * selftests do not use interrupts so we dropped clgi/sti/cli/stgi
- * for now. registers involved in LOAD/SAVE_GPR_C are eventually
+ * for now. Registers involved in SVM_SWITCH_GPRS_ASM are eventually
  * unmodified so they do not need to be in the clobber list.
  */
 void run_guest(struct vmcb *vmcb, u64 vmcb_gpa)
 {
 	asm volatile (
 		"vmload %[vmcb_gpa]\n\t"
-		"mov rflags, %%r15\n\t"	// rflags
-		"mov %%r15, 0x170(%[vmcb])\n\t"
-		"mov guest_regs, %%r15\n\t"	// rax
-		"mov %%r15, 0x1f8(%[vmcb])\n\t"
-		LOAD_GPR_C
+		"mov " GUEST_REG(rflags) ", %%r15\n\t"
+		"mov %%r15, %[vmcb_rflags]\n\t"
+		"mov " GUEST_REG(rax) ", %%r15\n\t"
+		"mov %%r15, %[vmcb_rax]\n\t"
+		SVM_SWITCH_GPRS_ASM
 		"vmrun %[vmcb_gpa]\n\t"
-		SAVE_GPR_C
-		"mov 0x170(%[vmcb]), %%r15\n\t"	// rflags
-		"mov %%r15, rflags\n\t"
-		"mov 0x1f8(%[vmcb]), %%r15\n\t"	// rax
-		"mov %%r15, guest_regs\n\t"
+		SVM_SWITCH_GPRS_ASM
+		"mov %[vmcb_rflags], %%r15\n\t"
+		"mov %%r15, " GUEST_REG(rflags) "\n\t"
+		"mov %[vmcb_rax], %%r15\n\t"	// rax
+		"mov %%r15, " GUEST_REG(rax) "\n\t"
 		"vmsave %[vmcb_gpa]\n\t"
-		: : [vmcb] "r" (vmcb), [vmcb_gpa] "a" (vmcb_gpa)
+		: [vmcb_rflags] "+m" (vmcb->save.rflags),
+		  [vmcb_rax] "+m" (vmcb->save.rax)
+		: [vmcb_gpa] "a" (vmcb_gpa),
+		  GUEST_REGS_OFFSETS
 		: "r15", "memory");
 }
 
diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c
index e7dd5791959ba..38050c60a0670 100644
--- a/tools/testing/selftests/kvm/lib/x86/ucall.c
+++ b/tools/testing/selftests/kvm/lib/x86/ucall.c
@@ -10,36 +10,8 @@
 
 void ucall_arch_do_ucall(gva_t uc)
 {
-	/*
-	 * FIXME: Revert this hack (the entire commit that added it) once nVMX
-	 * preserves L2 GPRs across a nested VM-Exit.  If a ucall from L2, e.g.
-	 * to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be
-	 * clobbered by L1.  Save and restore non-volatile GPRs (clobbering RBP
-	 * in particular is problematic) along with RDX and RDI (which are
-	 * inputs), and clobber volatile GPRs. *sigh*
-	 */
-#define HORRIFIC_L2_UCALL_CLOBBER_HACK	\
-	"rcx", "rsi", "r8", "r9", "r10", "r11"
-
-	asm volatile("push %%rbp\n\t"
-		     "push %%r15\n\t"
-		     "push %%r14\n\t"
-		     "push %%r13\n\t"
-		     "push %%r12\n\t"
-		     "push %%rbx\n\t"
-		     "push %%rdx\n\t"
-		     "push %%rdi\n\t"
-		     "in %[port], %%al\n\t"
-		     "pop %%rdi\n\t"
-		     "pop %%rdx\n\t"
-		     "pop %%rbx\n\t"
-		     "pop %%r12\n\t"
-		     "pop %%r13\n\t"
-		     "pop %%r14\n\t"
-		     "pop %%r15\n\t"
-		     "pop %%rbp\n\t"
-		: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory",
-		     HORRIFIC_L2_UCALL_CLOBBER_HACK);
+	asm volatile("in %[port], %%al"
+		: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
 }
 
 void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
diff --git a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c
index 77ce87c41a868..aa7f3b405fd32 100644
--- a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c
+++ b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c
@@ -22,9 +22,6 @@
 
 #define SYNC_PORT	0xe
 
-#define STR(x) #x
-#define XSTR(s) STR(s)
-
 /*
  * SMI handler: runs in real-address mode.
  * Reports SMRAM_STAGE via port IO, then does RSM.
@@ -37,7 +34,7 @@ static u8 smi_handler[] = {
 
 static inline void sync_with_host(u64 phase)
 {
-	asm volatile("in $" XSTR(SYNC_PORT) ", %%al \n"
+	asm volatile("in $" __stringify(SYNC_PORT) ", %%al \n"
 		     : "+a" (phase));
 }
 
diff --git a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
index 753a0e730ea8d..4931ec22768ee 100644
--- a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
+++ b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
@@ -6,7 +6,6 @@
  */
 #include <asm/kvm_para.h>
 #include <linux/kvm_para.h>
-#include <linux/stringify.h>
 #include <stdint.h>
 
 #include "kvm_test_harness.h"
diff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c
new file mode 100644
index 0000000000000..ba1d723001e94
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "svm_util.h"
+#include "vmx.h"
+
+#define NR_ITERATIONS		500
+
+#define PTRS_PER_PTE		512
+#define PXD_INDEX(vaddr, level)	(((vaddr) >> PG_LEVEL_SHIFT(level)) & (PTRS_PER_PTE - 1))
+
+#define TEST_MEM_BASE_GVA	0xc0000000ULL
+#define TEST_PGTABLE_GVA_OFFSET	0xd0000000ULL
+#define PATTERN			0xabcdefabcdefabcdULL
+
+static u64 expected_vaddr;
+static u64 guest_faults;
+
+static u64 *guest_get_pte(u64 vaddr)
+{
+	u64 pgtable_pa, pte;
+	u64 *pgtable;
+	int level;
+
+	level = (get_cr4() & X86_CR4_LA57) ? PG_LEVEL_256T : PG_LEVEL_512G;
+
+	pgtable_pa = get_cr3() & PHYSICAL_PAGE_MASK;
+	for (; level > PG_LEVEL_4K; level--) {
+		pgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);
+		pte = pgtable[PXD_INDEX(vaddr, level)];
+		GUEST_ASSERT(pte & PTE_PRESENT_MASK(&guest_mmu));
+		GUEST_ASSERT(!(pte & PTE_HUGE_MASK(&guest_mmu)));
+		pgtable_pa = PTE_GET_PA(pte);
+	}
+
+	pgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);
+	return &pgtable[PXD_INDEX(vaddr, PG_LEVEL_4K)];
+}
+
+static void guest_pf_handler(struct ex_regs *regs)
+{
+	u64 fault_addr;
+	u64 *ptep;
+
+	fault_addr = get_cr2();
+	GUEST_ASSERT_EQ(fault_addr, READ_ONCE(expected_vaddr));
+
+	ptep = guest_get_pte(fault_addr);
+	GUEST_ASSERT(ptep);
+	GUEST_ASSERT(!(*ptep & PTE_PRESENT_MASK(&guest_mmu)));
+
+	*ptep |= PTE_PRESENT_MASK(&guest_mmu);
+	guest_faults++;
+}
+
+static void guest_access_memory(void *arg)
+{
+	u64 vaddr, val;
+	int i;
+
+	for (i = 0; ; i++) {
+		vaddr = TEST_MEM_BASE_GVA + (i % PTRS_PER_PTE) * PAGE_SIZE;
+		WRITE_ONCE(expected_vaddr, vaddr);
+
+		/* Read to trigger #PF */
+		val = READ_ONCE(*(u64 *)vaddr);
+		GUEST_ASSERT_EQ(val, PATTERN);
+
+		/* Clear the present bit again so it faults next time */
+		*guest_get_pte(vaddr) &= ~PTE_PRESENT_MASK(&guest_mmu);
+		invlpg(vaddr);
+	}
+}
+
+static void l1_svm_code(struct svm_test_data *svm)
+{
+	generic_svm_setup(svm, guest_access_memory);
+	svm->vmcb->control.intercept_exceptions |= BIT(UD_VECTOR);
+
+	while (1) {
+		run_guest(svm->vmcb, svm->vmcb_gpa);
+		GUEST_ASSERT_EQ(svm->vmcb->control.exit_code,
+				(SVM_EXIT_EXCP_BASE + UD_VECTOR));
+	}
+}
+
+static void l1_vmx_code(struct vmx_pages *vmx)
+{
+	GUEST_ASSERT(prepare_for_vmx_operation(vmx));
+	GUEST_ASSERT(load_vmcs(vmx));
+	prepare_vmcs(vmx, guest_access_memory);
+
+	/* Intercept UD, ignore any #PF */
+	GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR) | BIT(PF_VECTOR)));
+	GUEST_ASSERT(!vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0));
+	GUEST_ASSERT(!vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, -1));
+
+	GUEST_ASSERT(!vmlaunch());
+	while (1) {
+		GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI);
+		GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR);
+		GUEST_ASSERT(!vmresume());
+	}
+}
+
+static void l1_guest_code(void *test_data)
+{
+	if (this_cpu_has(X86_FEATURE_SVM))
+		l1_svm_code(test_data);
+	else
+		l1_vmx_code(test_data);
+}
+
+static void *sigusr_thread_fn(void *arg)
+{
+	pthread_t vcpu_thread = (pthread_t)arg;
+
+	for (;;) {
+		pthread_testcancel();
+		pthread_kill(vcpu_thread, SIGUSR1);
+		usleep(msecs_to_usecs(1));
+	}
+	return NULL;
+}
+
+static void dummy_signal_handler(int signo) {}
+static struct sigaction sa;
+
+static void vcpu_sigusr_listen(void)
+{
+	sa.sa_handler = dummy_signal_handler;
+	sigaction(SIGUSR1, &sa, NULL);
+}
+
+static void vcpu_sigusr_ignore(void)
+{
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGUSR1, &sa, NULL);
+}
+
+static void kvm_x86_state_queue_ud(struct kvm_x86_state *state)
+{
+	if (state->events.exception.pending || state->events.exception.injected)
+		return;
+
+	state->events.flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
+	state->events.exception.pending = true;
+	state->events.exception.injected = false;
+	state->events.exception.nr = UD_VECTOR;
+	state->events.exception.has_error_code = false;
+	state->events.exception_has_payload = false;
+}
+
+static void run_test(bool nested)
+{
+	struct kvm_x86_state *state;
+	int r, i, level;
+	pthread_t sigusr_thread;
+	gpa_t gpa, pgtable_gpa;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	u64 *pgtable;
+	gva_t gva;
+	u64 pte;
+
+	vm = vm_create_with_one_vcpu(&vcpu, nested ? l1_guest_code : guest_access_memory);
+	vm_install_exception_handler(vm, PF_VECTOR, guest_pf_handler);
+	vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
+
+	if (nested) {
+		if (kvm_cpu_has(X86_FEATURE_SVM))
+			vcpu_alloc_svm(vm, &gva);
+		else
+			vcpu_alloc_vmx(vm, &gva);
+		vcpu_args_set(vcpu, 1, gva);
+	}
+
+	/* Allocate a page and write the pattern to it */
+	gva = vm_alloc_page(vm);
+	*(u64 *)addr_gva2hva(vm, gva) = PATTERN;
+	gpa = addr_gva2gpa(vm, gva);
+
+	/*
+	 * Map all virtual addresses to the pattern page and clear the present
+	 * bit such that guest accesses will cause a #PF.
+	 */
+	for (i = 0; i < PTRS_PER_PTE; i++) {
+		gva = TEST_MEM_BASE_GVA + i * getpagesize();
+		virt_pg_map(vm, gva, gpa);
+		*vm_get_pte(vm, gva) &= ~PTE_PRESENT_MASK(&vm->mmu);
+	}
+
+	/*
+	 * Now create mappings for the page tables created above so that the
+	 * guest #PF handler can walk them. All PTEs for test virtual addresses
+	 * should lie on the same PTE page, so one page is mapped for each page
+	 * table level.
+	 *
+	 * Use an offset for the GVA instead of creating identity mappings to
+	 * avoid collision with existing mappings at low GVAs (e.g. ELF).
+	 */
+	pgtable_gpa = vm->mmu.pgd;
+	for (level = vm->mmu.pgtable_levels; level >= PG_LEVEL_4K; level--) {
+		virt_map(vm, pgtable_gpa + TEST_PGTABLE_GVA_OFFSET, pgtable_gpa, 1);
+		pgtable = addr_gpa2hva(vm, pgtable_gpa);
+		pte = pgtable[PXD_INDEX(TEST_MEM_BASE_GVA, level)];
+		pgtable_gpa = PTE_GET_PA(pte);
+	}
+
+	/* Initialize the thread sending SIGUSR and install the handler */
+	vcpu_sigusr_ignore();
+	r = pthread_create(&sigusr_thread, NULL, sigusr_thread_fn,
+			   (void *)pthread_self());
+	TEST_ASSERT(!r, "pthread_create() failed: %d", r);
+
+	for (i = 1; i <= NR_ITERATIONS; i++) {
+		/*
+		 * Only handle SIGUSR while the vCPU is running, otherwise
+		 * ignore it to avoid interrupting other ioctls/syscalls.
+		 */
+		vcpu_sigusr_listen();
+		r = __vcpu_run(vcpu);
+		if (r == -1)
+			TEST_ASSERT_EQ(errno, EINTR);
+		vcpu_sigusr_ignore();
+
+		/* The guest only exits due to a signal or failed assertion */
+		if (!r) {
+			TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+			TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_ABORT);
+			REPORT_GUEST_ASSERT(uc);
+			break;
+		}
+
+		state = vcpu_save_state(vcpu);
+
+		/*
+		 * If the vCPU is in guest mode, inject a #UD to trigger an
+		 * L2->L1 VM-Exit every other iteration.
+		 */
+		if (kvm_x86_state_is_guest_mode(state) && i % 2 == 0)
+			kvm_x86_state_queue_ud(state);
+
+		kvm_vm_release(vm);
+		vcpu = vm_recreate_with_one_vcpu(vm);
+		vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
+		vcpu_load_state(vcpu, state);
+		kvm_x86_state_cleanup(state);
+
+		pr_info("\rSave+restore iterations: %d", i);
+	}
+	pr_info("\n");
+
+	sync_global_from_guest(vm, guest_faults);
+	TEST_ASSERT(guest_faults > 0, "No guest page faults triggered");
+	pr_info("Guest page faults%s: %lu\n", nested ? " (in L2)" : "", guest_faults);
+
+	pthread_cancel(sigusr_thread);
+	pthread_join(sigusr_thread, NULL);
+	kvm_vm_free(vm);
+}
+
+int main(int argc, char *argv[])
+{
+	pr_info("Running save+restore stress test...\n");
+	run_test(/*nested=*/false);
+
+	if (!kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD) ||
+	    !kvm_has_cap(KVM_CAP_NESTED_STATE) ||
+	    (!kvm_cpu_has(X86_FEATURE_SVM) && !kvm_cpu_has(X86_FEATURE_VMX))) {
+		pr_info("Nested virtualization not supported, skipping nested test\n");
+		return 0;
+	}
+
+	pr_info("Running save+restore stress test with a nested guest...\n");
+	run_test(/*nested=*/true);
+	return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c
index e2542f4ced605..d1edafd5af755 100644
--- a/tools/testing/selftests/kvm/x86/smm_test.c
+++ b/tools/testing/selftests/kvm/x86/smm_test.c
@@ -22,9 +22,6 @@
 #define SMRAM_GPA 0x1000000
 #define SMRAM_STAGE 0xfe
 
-#define STR(x) #x
-#define XSTR(s) STR(s)
-
 #define SYNC_PORT 0xe
 #define DONE 0xff
 
@@ -42,7 +39,7 @@ u8 smi_handler[] = {
 
 static inline void sync_with_host(u64 phase)
 {
-	asm volatile("in $" XSTR(SYNC_PORT)", %%al \n"
+	asm volatile("in $" __stringify(SYNC_PORT)", %%al \n"
 		     : "+a" (phase));
 }
 

3/2 2026/07/28 19:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13969
							output: 88
							thoughts: 917
						

Thoughts:
**KVM Selftest Patch Assessment**

Okay, so I'm looking at this patch for KVM selftests. It looks like it's introducing a new test, `save_restore_pf_stress_test.c`, and touching up some existing tests and headers. Crucially, though, it's all contained within `tools/testing/selftests/kvm/`.  I need to make sure this is understood.

Let me just quickly verify the file list again to be absolutely sure: `Makefile.kvm`, `test_util.h`, `evmcs.h`, `processor.h`, `vmx.h`, `assert.c`, `processor.c`, `svm.c`, `ucall.c`, `evmcs_smm_controls_test.c`, `fix_hypercall_test.c`, `save_restore_pf_stress_test.c`, and `smm_test.c`. Yep, all within the `tools/testing/selftests/kvm/` directory.

The key thing is, this is *purely* a selftest modification. It doesn't modify any kernel code at all. No changes to `arch/x86/kvm/`, `virt/kvm/`, or anywhere else in the kernel proper. This is important because syzkaller fuzzes the kernel itself, not the userspace tools that test it.

Let me see what the instructions say about this scenario.  The instructions specify that changes to `tools/` and selftests should be `WorthFuzzing=false` under these circumstances.

Right, so this falls squarely within the criteria for `WorthFuzzing=false`. It's a selftest, and it's not changing the kernel's behavior. The patch doesn't introduce any new kernel code, no new functionality, nothing that would be reached during fuzzing.

Therefore, my conclusion is that this patch doesn't warrant fuzzing.  I'm going to call `set-results` with `WorthFuzzing=false`. Just to be absolutely clear: the patch modifies KVM selftests in the `tools/` directory, which do not modify the kernel code that syzkaller fuzzer is designed to test.



4/2 2026/07/28 19:39 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only adds and modifies KVM selftests in tools/testing/selftests/kvm/. It does not modify any core kernel code or drivers. Since the fuzzing targets the kernel and not userspace testing tools, there are no functional kernel changes to fuzz.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds and modifies KVM selftests in tools/testing/selftests/kvm/. It does not modify any core kernel code or drivers. Since the fuzzing targets the kernel and not userspace testing tools, there are no functional kernel changes to fuzz.",
  "WorthFuzzing": false
}

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)