| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/06/29 20:33 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies KVM selftests in tools/testing/selftests/kvm/. It does not make any functional changes to the kernel code itself, so there is nothing new to fuzz.",
"WorthFuzzing": false
} |
| 1/1 | 2026/06/29 20:33 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 39811cbaaa29ded790400a3bf6adb1cbde1fc700\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Jun 29 20:33:03 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 4ace12606e937b..c61d51a0c112fa 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -111,6 +111,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/stress_save_restore_pf_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 a56271c237ae9e..c55148ebfe934d 100644\n--- a/tools/testing/selftests/kvm/include/test_util.h\n+++ b/tools/testing/selftests/kvm/include/test_util.h\n@@ -240,4 +240,7 @@ char *strdup_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2), n\n \n char *sys_get_cur_clocksource(void);\n \n+#define STR(x) #x\n+#define XSTR(x) STR(x)\n+\n #endif /* SELFTEST_KVM_TEST_UTIL_H */\ndiff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h\nindex be79bda024bf16..98268be1aa5b26 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,22 @@ 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 : \"memory\", \"cc\");\n \treturn ret;\n }\n \n@@ -1246,30 +1238,22 @@ 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 : \"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 7d3a27bc0d842a..ec4dfd8176369b 100644\n--- a/tools/testing/selftests/kvm/include/x86/processor.h\n+++ b/tools/testing/selftests/kvm/include/x86/processor.h\n@@ -396,25 +396,28 @@ 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-\tu64 rax;\n-\tu64 rcx;\n-\tu64 rdx;\n-\tu64 rbx;\n-\tu64 rsp;\n-\tu64 rbp;\n-\tu64 rsi;\n-\tu64 rdi;\n-\tu64 r8;\n-\tu64 r9;\n-\tu64 r10;\n-\tu64 r11;\n-\tu64 r12;\n-\tu64 r13;\n-\tu64 r14;\n-\tu64 r15;\n-};\n+#define GUEST_REGS_RAX\t0\n+#define GUEST_REGS_RCX\t1\n+#define GUEST_REGS_RDX\t2\n+#define GUEST_REGS_RBX\t3\n+#define GUEST_REGS_RSP\t4\n+#define GUEST_REGS_RBP\t5\n+#define GUEST_REGS_RSI\t6\n+#define GUEST_REGS_RDI\t7\n+#define GUEST_REGS_R8\t8\n+#define GUEST_REGS_R9\t9\n+#define GUEST_REGS_R10\t10\n+#define GUEST_REGS_R11\t11\n+#define GUEST_REGS_R12\t12\n+#define GUEST_REGS_R13\t13\n+#define GUEST_REGS_R14\t14\n+#define GUEST_REGS_R15\t15\n+#define NR_GUEST_REGS\t(GUEST_REGS_R15 + 1)\n+\n+extern u64 guest_regs[NR_GUEST_REGS];\n+\n+#define GUEST_SWITCH_GPR_ASM(reg, idx) \\\n+\t\"xchg %%\" #reg \", guest_regs + 8 *\" XSTR(idx) \"\\n\\t\"\n \n struct desc64 {\n \tu16 limit0;\n@@ -582,6 +585,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@@ -877,6 +889,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\");\ndiff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h\nindex 4bcfd60e3aecb0..a808dc21c9f210 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, GUEST_REGS_RAX) \\\n+\tGUEST_SWITCH_GPR_ASM(rbx, GUEST_REGS_RBX) \\\n+\tGUEST_SWITCH_GPR_ASM(rcx, GUEST_REGS_RCX) \\\n+\tGUEST_SWITCH_GPR_ASM(rdx, GUEST_REGS_RDX) \\\n+\tGUEST_SWITCH_GPR_ASM(rbp, GUEST_REGS_RBP) \\\n+\tGUEST_SWITCH_GPR_ASM(rsi, GUEST_REGS_RSI) \\\n+\tGUEST_SWITCH_GPR_ASM(rdi, GUEST_REGS_RDI) \\\n+\tGUEST_SWITCH_GPR_ASM(r8, GUEST_REGS_R8) \\\n+\tGUEST_SWITCH_GPR_ASM(r9, GUEST_REGS_R9) \\\n+\tGUEST_SWITCH_GPR_ASM(r10, GUEST_REGS_R10) \\\n+\tGUEST_SWITCH_GPR_ASM(r11, GUEST_REGS_R11) \\\n+\tGUEST_SWITCH_GPR_ASM(r12, GUEST_REGS_R12) \\\n+\tGUEST_SWITCH_GPR_ASM(r13, GUEST_REGS_R13) \\\n+\tGUEST_SWITCH_GPR_ASM(r14, GUEST_REGS_R14) \\\n+\tGUEST_SWITCH_GPR_ASM(r15, GUEST_REGS_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,23 @@ 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 : \"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 +411,20 @@ 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 : \"memory\", \"cc\");\n \treturn ret;\n }\n \ndiff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c\nindex ef56dcefe0119e..0d66aff04939ba 100644\n--- a/tools/testing/selftests/kvm/lib/x86/processor.c\n+++ b/tools/testing/selftests/kvm/lib/x86/processor.c\n@@ -29,6 +29,8 @@ bool host_cpu_is_amd_compatible;\n bool is_forced_emulation_enabled;\n u64 guest_tsc_khz;\n \n+u64 guest_regs[NR_GUEST_REGS];\n+\n const char *ex_str(int vector)\n {\n \tswitch (vector) {\ndiff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/selftests/kvm/lib/x86/svm.c\nindex 1445b890986fd1..d07f10b5dc9633 100644\n--- a/tools/testing/selftests/kvm/lib/x86/svm.c\n+++ b/tools/testing/selftests/kvm/lib/x86/svm.c\n@@ -13,7 +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@@ -125,7 +124,7 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip)\n \n \tvmcb-\u003esave.rip = (u64)guest_rip;\n \tvmcb-\u003esave.rsp = (u64)svm-\u003estack;\n-\tguest_regs.rdi = (u64)svm;\n+\tguest_regs[GUEST_REGS_RDI] = (u64)svm;\n \n \tif (svm-\u003encr3_gpa) {\n \t\tctrl-\u003emisc_ctl |= SVM_MISC_ENABLE_NP;\n@@ -137,46 +136,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, GUEST_REGS_RBX) \\\n+\tGUEST_SWITCH_GPR_ASM(rcx, GUEST_REGS_RCX) \\\n+\tGUEST_SWITCH_GPR_ASM(rdx, GUEST_REGS_RDX) \\\n+\tGUEST_SWITCH_GPR_ASM(rbp, GUEST_REGS_RBP) \\\n+\tGUEST_SWITCH_GPR_ASM(rsi, GUEST_REGS_RSI) \\\n+\tGUEST_SWITCH_GPR_ASM(rdi, GUEST_REGS_RDI) \\\n+\tGUEST_SWITCH_GPR_ASM(r8, GUEST_REGS_R8) \\\n+\tGUEST_SWITCH_GPR_ASM(r9, GUEST_REGS_R9) \\\n+\tGUEST_SWITCH_GPR_ASM(r10, GUEST_REGS_R10) \\\n+\tGUEST_SWITCH_GPR_ASM(r11, GUEST_REGS_R11) \\\n+\tGUEST_SWITCH_GPR_ASM(r12, GUEST_REGS_R12) \\\n+\tGUEST_SWITCH_GPR_ASM(r13, GUEST_REGS_R13) \\\n+\tGUEST_SWITCH_GPR_ASM(r14, GUEST_REGS_R14) \\\n+\tGUEST_SWITCH_GPR_ASM(r15, GUEST_REGS_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 rflags, %%r15\\n\\t\"\n+\t\t\"mov %%r15, %[vmcb_rflags]\\n\\t\"\n+\t\t\"mov %[guest_regs_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\tSVM_SWITCH_GPRS_ASM\n+\t\t\"mov %[vmcb_rflags], %%r15\\n\\t\"\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\t\"mov %[vmcb_rax], %%r15\\n\\t\"\t// rax\n+\t\t\"mov %%r15, %[guest_regs_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 [guest_regs_rax] \"+rm\" (guest_regs[GUEST_REGS_RAX])\n+\t\t: [vmcb_gpa] \"a\" (vmcb_gpa)\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 e7dd5791959ba1..38050c60a06703 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 77ce87c41a868e..cf53bf4697e1e3 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.\ndiff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c\nindex e2542f4ced605f..21619e7582718f 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 \ndiff --git a/tools/testing/selftests/kvm/x86/stress_save_restore_pf_test.c b/tools/testing/selftests/kvm/x86/stress_save_restore_pf_test.c\nnew file mode 100644\nindex 00000000000000..2b76e56f744e78\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/stress_save_restore_pf_test.c\n@@ -0,0 +1,326 @@\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 GOTO_PREV_LINE\t\t\"\\033[A\\r\"\n+#define PRINT_ITER(s, x)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\\\n+\tif (x == 1)\t\t\t\t\t\t\\\n+\t\tprintf(s \"%d\\n\", x); \\\n+\telse\t\t\t\t\t\t\t\\\n+\t\tprintf(GOTO_PREV_LINE s \"%d\\n\", x);\t\t\\\n+\tfflush(stdout);\t\t\t\t\t\t\\\n+} while (0)\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 NR_TEST_ADDRS\t\tPTRS_PER_PTE\n+#define PATTERN\t\t\t0xabcdefabcdefabcdULL\n+\n+static u64 pte_present_mask;\n+static u64 pte_huge_mask;\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+\tbool la57;\n+\n+\tla57 = !!(get_cr4() \u0026 X86_CR4_LA57);\n+\tlevel = 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);\n+\t\tGUEST_ASSERT(!(pte \u0026 pte_huge_mask));\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));\n+\n+\t*ptep |= pte_present_mask;\n+\tinvlpg(fault_addr);\n+\n+\tguest_faults++;\n+}\n+\n+static void guest_access_memory(void *arg)\n+{\n+\tu64 vaddr, val;\n+\tint i = 0;\n+\n+\tfor (;; i++) {\n+\t\tvaddr = TEST_MEM_BASE_GVA + (i % NR_TEST_ADDRS) * 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;\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, (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 bool vcpu_state_is_guest_mode(struct kvm_x86_state *state)\n+{\n+\treturn !!(state-\u003enested.flags \u0026 KVM_STATE_NESTED_GUEST_MODE);\n+}\n+\n+static void vcpu_state_inject_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 bool parse_args_nested(int argc, char *argv[])\n+{\n+\tbool nested = false;\n+\tint opt;\n+\n+\twhile ((opt = getopt(argc, argv, \"n\")) != -1) {\n+\t\tswitch (opt) {\n+\t\tcase 'n':\n+\t\t\tnested = true;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tprintf(\"Usage: %s [-n]\\n\", argv[0]);\n+\t\t\texit(1);\n+\t\t}\n+\t}\n+\n+\treturn nested;\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tstruct kvm_x86_state *state;\n+\tint r, i, level, count = 0;\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+\tbool nested;\n+\tgva_t gva;\n+\tu64 pte;\n+\n+\tTEST_REQUIRE(kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD));\n+\n+\tnested = parse_args_nested(argc, argv);\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\tTEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX));\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+\tpte_present_mask = PTE_PRESENT_MASK(\u0026vm-\u003emmu);\n+\tpte_huge_mask = PTE_HUGE_MASK(\u0026vm-\u003emmu);\n+\tsync_global_to_guest(vm, pte_present_mask);\n+\tsync_global_to_guest(vm, pte_huge_mask);\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 NR_TEST_ADDRS; 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;\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+\twhile (count++ \u003c NR_ITERATIONS) {\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 (nested \u0026\u0026 vcpu_state_is_guest_mode(state) \u0026\u0026 count % 2 == 0)\n+\t\t\tvcpu_state_inject_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\tPRINT_ITER(\"Save+restore iterations: \", count);\n+\t}\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+\treturn 0;\n+}\n"
} |
| 2/1 | 2026/06/29 20:33 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies KVM selftests in tools/testing/selftests/kvm/. It does not make any functional changes to the kernel code itself, so there is nothing new 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). 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. 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. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. 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 39811cbaaa29ded790400a3bf6adb1cbde1fc700
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Jun 29 20:33:03 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 4ace12606e937b..c61d51a0c112fa 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -111,6 +111,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/stress_save_restore_pf_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 a56271c237ae9e..c55148ebfe934d 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -240,4 +240,7 @@ char *strdup_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2), n
char *sys_get_cur_clocksource(void);
+#define STR(x) #x
+#define XSTR(x) STR(x)
+
#endif /* SELFTEST_KVM_TEST_UTIL_H */
diff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h
index be79bda024bf16..98268be1aa5b26 100644
--- a/tools/testing/selftests/kvm/include/x86/evmcs.h
+++ b/tools/testing/selftests/kvm/include/x86/evmcs.h
@@ -1207,30 +1207,22 @@ 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)¤t_evmcs->host_rsp),
[host_rip]"r"
((u64)¤t_evmcs->host_rip)
- : "memory", "cc", "rbx", "r8", "r9", "r10",
- "r11", "r12", "r13", "r14", "r15");
+ : "memory", "cc");
return ret;
}
@@ -1246,30 +1238,22 @@ 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)¤t_evmcs->host_rsp),
[host_rip]"r"
((u64)¤t_evmcs->host_rip)
- : "memory", "cc", "rbx", "r8", "r9", "r10",
- "r11", "r12", "r13", "r14", "r15");
+ : "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 7d3a27bc0d842a..ec4dfd8176369b 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -396,25 +396,28 @@ 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 {
- u64 rax;
- u64 rcx;
- u64 rdx;
- u64 rbx;
- u64 rsp;
- u64 rbp;
- u64 rsi;
- u64 rdi;
- u64 r8;
- u64 r9;
- u64 r10;
- u64 r11;
- u64 r12;
- u64 r13;
- u64 r14;
- u64 r15;
-};
+#define GUEST_REGS_RAX 0
+#define GUEST_REGS_RCX 1
+#define GUEST_REGS_RDX 2
+#define GUEST_REGS_RBX 3
+#define GUEST_REGS_RSP 4
+#define GUEST_REGS_RBP 5
+#define GUEST_REGS_RSI 6
+#define GUEST_REGS_RDI 7
+#define GUEST_REGS_R8 8
+#define GUEST_REGS_R9 9
+#define GUEST_REGS_R10 10
+#define GUEST_REGS_R11 11
+#define GUEST_REGS_R12 12
+#define GUEST_REGS_R13 13
+#define GUEST_REGS_R14 14
+#define GUEST_REGS_R15 15
+#define NR_GUEST_REGS (GUEST_REGS_R15 + 1)
+
+extern u64 guest_regs[NR_GUEST_REGS];
+
+#define GUEST_SWITCH_GPR_ASM(reg, idx) \
+ "xchg %%" #reg ", guest_regs + 8 *" XSTR(idx) "\n\t"
struct desc64 {
u16 limit0;
@@ -582,6 +585,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;
@@ -877,6 +889,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");
diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h
index 4bcfd60e3aecb0..a808dc21c9f210 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_REGS_RAX) \
+ GUEST_SWITCH_GPR_ASM(rbx, GUEST_REGS_RBX) \
+ GUEST_SWITCH_GPR_ASM(rcx, GUEST_REGS_RCX) \
+ GUEST_SWITCH_GPR_ASM(rdx, GUEST_REGS_RDX) \
+ GUEST_SWITCH_GPR_ASM(rbp, GUEST_REGS_RBP) \
+ GUEST_SWITCH_GPR_ASM(rsi, GUEST_REGS_RSI) \
+ GUEST_SWITCH_GPR_ASM(rdi, GUEST_REGS_RDI) \
+ GUEST_SWITCH_GPR_ASM(r8, GUEST_REGS_R8) \
+ GUEST_SWITCH_GPR_ASM(r9, GUEST_REGS_R9) \
+ GUEST_SWITCH_GPR_ASM(r10, GUEST_REGS_R10) \
+ GUEST_SWITCH_GPR_ASM(r11, GUEST_REGS_R11) \
+ GUEST_SWITCH_GPR_ASM(r12, GUEST_REGS_R12) \
+ GUEST_SWITCH_GPR_ASM(r13, GUEST_REGS_R13) \
+ GUEST_SWITCH_GPR_ASM(r14, GUEST_REGS_R14) \
+ GUEST_SWITCH_GPR_ASM(r15, GUEST_REGS_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,23 @@ 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");
+ : "memory", "cc");
return ret;
}
-/*
- * No guest state (e.g. GPRs) is established by this vmresume.
- */
static inline int vmresume(void)
{
int ret;
@@ -408,28 +411,20 @@ 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");
+ : "memory", "cc");
return ret;
}
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index ef56dcefe0119e..0d66aff04939ba 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -29,6 +29,8 @@ bool host_cpu_is_amd_compatible;
bool is_forced_emulation_enabled;
u64 guest_tsc_khz;
+u64 guest_regs[NR_GUEST_REGS];
+
const char *ex_str(int vector)
{
switch (vector) {
diff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/selftests/kvm/lib/x86/svm.c
index 1445b890986fd1..d07f10b5dc9633 100644
--- a/tools/testing/selftests/kvm/lib/x86/svm.c
+++ b/tools/testing/selftests/kvm/lib/x86/svm.c
@@ -13,7 +13,6 @@
#define SEV_DEV_PATH "/dev/sev"
-struct gpr64_regs guest_regs;
u64 rflags;
/* Allocate memory regions for nested SVM tests.
@@ -125,7 +124,7 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip)
vmcb->save.rip = (u64)guest_rip;
vmcb->save.rsp = (u64)svm->stack;
- guest_regs.rdi = (u64)svm;
+ guest_regs[GUEST_REGS_RDI] = (u64)svm;
if (svm->ncr3_gpa) {
ctrl->misc_ctl |= SVM_MISC_ENABLE_NP;
@@ -137,46 +136,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_REGS_RBX) \
+ GUEST_SWITCH_GPR_ASM(rcx, GUEST_REGS_RCX) \
+ GUEST_SWITCH_GPR_ASM(rdx, GUEST_REGS_RDX) \
+ GUEST_SWITCH_GPR_ASM(rbp, GUEST_REGS_RBP) \
+ GUEST_SWITCH_GPR_ASM(rsi, GUEST_REGS_RSI) \
+ GUEST_SWITCH_GPR_ASM(rdi, GUEST_REGS_RDI) \
+ GUEST_SWITCH_GPR_ASM(r8, GUEST_REGS_R8) \
+ GUEST_SWITCH_GPR_ASM(r9, GUEST_REGS_R9) \
+ GUEST_SWITCH_GPR_ASM(r10, GUEST_REGS_R10) \
+ GUEST_SWITCH_GPR_ASM(r11, GUEST_REGS_R11) \
+ GUEST_SWITCH_GPR_ASM(r12, GUEST_REGS_R12) \
+ GUEST_SWITCH_GPR_ASM(r13, GUEST_REGS_R13) \
+ GUEST_SWITCH_GPR_ASM(r14, GUEST_REGS_R14) \
+ GUEST_SWITCH_GPR_ASM(r15, GUEST_REGS_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 rflags, %%r15\n\t"
+ "mov %%r15, %[vmcb_rflags]\n\t"
+ "mov %[guest_regs_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
+ SVM_SWITCH_GPRS_ASM
+ "mov %[vmcb_rflags], %%r15\n\t"
"mov %%r15, rflags\n\t"
- "mov 0x1f8(%[vmcb]), %%r15\n\t" // rax
- "mov %%r15, guest_regs\n\t"
+ "mov %[vmcb_rax], %%r15\n\t" // rax
+ "mov %%r15, %[guest_regs_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),
+ [guest_regs_rax] "+rm" (guest_regs[GUEST_REGS_RAX])
+ : [vmcb_gpa] "a" (vmcb_gpa)
: "r15", "memory");
}
diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c
index e7dd5791959ba1..38050c60a06703 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 77ce87c41a868e..cf53bf4697e1e3 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.
diff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c
index e2542f4ced605f..21619e7582718f 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
diff --git a/tools/testing/selftests/kvm/x86/stress_save_restore_pf_test.c b/tools/testing/selftests/kvm/x86/stress_save_restore_pf_test.c
new file mode 100644
index 00000000000000..2b76e56f744e78
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/stress_save_restore_pf_test.c
@@ -0,0 +1,326 @@
+// 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 GOTO_PREV_LINE "\033[A\r"
+#define PRINT_ITER(s, x) \
+do { \
+ if (x == 1) \
+ printf(s "%d\n", x); \
+ else \
+ printf(GOTO_PREV_LINE s "%d\n", x); \
+ fflush(stdout); \
+} while (0)
+
+#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 NR_TEST_ADDRS PTRS_PER_PTE
+#define PATTERN 0xabcdefabcdefabcdULL
+
+static u64 pte_present_mask;
+static u64 pte_huge_mask;
+
+static u64 expected_vaddr;
+static u64 guest_faults;
+
+static u64 *guest_get_pte(u64 vaddr)
+{
+ u64 pgtable_pa, pte;
+ u64 *pgtable;
+ int level;
+ bool la57;
+
+ la57 = !!(get_cr4() & X86_CR4_LA57);
+ level = 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_ASSERT(!(pte & pte_huge_mask));
+ 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));
+
+ *ptep |= pte_present_mask;
+ invlpg(fault_addr);
+
+ guest_faults++;
+}
+
+static void guest_access_memory(void *arg)
+{
+ u64 vaddr, val;
+ int i = 0;
+
+ for (;; i++) {
+ vaddr = TEST_MEM_BASE_GVA + (i % NR_TEST_ADDRS) * 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;
+ 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 bool vcpu_state_is_guest_mode(struct kvm_x86_state *state)
+{
+ return !!(state->nested.flags & KVM_STATE_NESTED_GUEST_MODE);
+}
+
+static void vcpu_state_inject_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 bool parse_args_nested(int argc, char *argv[])
+{
+ bool nested = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "n")) != -1) {
+ switch (opt) {
+ case 'n':
+ nested = true;
+ break;
+ default:
+ printf("Usage: %s [-n]\n", argv[0]);
+ exit(1);
+ }
+ }
+
+ return nested;
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_x86_state *state;
+ int r, i, level, count = 0;
+ pthread_t sigusr_thread;
+ gpa_t gpa, pgtable_gpa;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ u64 *pgtable;
+ bool nested;
+ gva_t gva;
+ u64 pte;
+
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD));
+
+ nested = parse_args_nested(argc, argv);
+
+ 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) {
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX));
+ if (kvm_cpu_has(X86_FEATURE_SVM))
+ vcpu_alloc_svm(vm, &gva);
+ else
+ vcpu_alloc_vmx(vm, &gva);
+ vcpu_args_set(vcpu, 1, gva);
+ }
+
+ pte_present_mask = PTE_PRESENT_MASK(&vm->mmu);
+ pte_huge_mask = PTE_HUGE_MASK(&vm->mmu);
+ sync_global_to_guest(vm, pte_present_mask);
+ sync_global_to_guest(vm, pte_huge_mask);
+
+ /* 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 < NR_TEST_ADDRS; i++) {
+ gva = TEST_MEM_BASE_GVA + i * getpagesize();
+ virt_pg_map(vm, gva, gpa);
+ *vm_get_pte(vm, gva) &= ~pte_present_mask;
+ }
+
+ /*
+ * 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);
+
+ while (count++ < NR_ITERATIONS) {
+ /*
+ * 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 (nested && vcpu_state_is_guest_mode(state) && count % 2 == 0)
+ vcpu_state_inject_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);
+
+ PRINT_ITER("Save+restore iterations: ", count);
+ }
+
+ 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);
+ return 0;
+}
|
| 3/2 | 2026/06/29 20:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13041 output: 73 thoughts: 642 Thoughts: **Assessing the Worthiness of Fuzzing this Patch** Okay, so I've got this patch series in front of me. The first thing that jumps out is that all the changes are within the `tools/testing/selftests/kvm/` directory. Wait, let me double-check that diff. Yep, it's all contained there: `Makefile.kvm`, some include files, and a few source files for various tests. Alright, looks like `evmcs_smm_controls_test.c`, `smm_test.c`, and `stress_save_restore_pf_test.c` are among the modified tests. Now, my initial reaction is, "This is a selftest patch." It isn't modifying core kernel logic, drivers, or anything fundamental to an architecture. These are essentially userspace tests, designed to validate the behavior of the KVM subsystem. The patch doesn't look like it touches the underlying kernel code that *needs* fuzzing. I'm remembering the instructions: Return `WorthFuzzing=false` if the patch has only documentation changes, Kconfig changes, comment changes, purely decorative changes, or changes to constants or macros that don't affect execution flow, or unreachable code paths. This looks like a perfect fit, right? Fuzzing the kernel based on changes to tests seems counter-intuitive. There are no changes to the kernel's execution path based on my assessment of the diff. I should verify though, let me double-check all the modified files to make sure there are *no* other changes hiding anywhere. No, everything still starts with `tools/testing/selftests/kvm/`. So, I'm confident in saying that the patch does not introduce any changes that necessitate fuzzing the kernel. Therefore, `WorthFuzzing=false`. The reasoning is that the patch is completely self-contained within the KVM selftests, and there are no functional changes to the kernel itself. Time to call `set-results`. |
| 4/2 | 2026/06/29 20:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies KVM selftests in tools/testing/selftests/kvm/. It does not make any functional changes to the kernel code itself, so there is nothing new to fuzz.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies KVM selftests in tools/testing/selftests/kvm/. It does not make any functional changes to the kernel code itself, so there is nothing new to fuzz.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|