| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/02 12:58 | flow | patch-triage |
2mArgs:null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_handle_vncr_abort",
"kvm_handle_s1e2_tlbi",
"kvm_nested_s2_wp",
"kvm_nested_s2_unmap",
"compute_tlb_inval_range",
"kvm_vcpu_allocate_vncr_tlb"
],
"Reasoning": "The patch modifies core KVM ARM64 nested virtualization logic, specifically around VNCR (Virtual Nested Control Register) TLB handling and invalidation. It introduces new tracking mechanisms and modifies how TLB invalidation ranges are computed and applied. These are reachable core kernel paths that should be fuzzed to ensure correctness and prevent regressions.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/02 12:58 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e9d5ae9d48b814e8c8f67e43325c2efc28808bda\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Aug 2 12:58:28 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h\nindex bae2c4f92ef5c..108966a9db12b 100644\n--- a/arch/arm64/include/asm/kvm_host.h\n+++ b/arch/arm64/include/asm/kvm_host.h\n@@ -411,8 +411,8 @@ struct kvm_arch {\n \t/* Masks for VNCR-backed and general EL2 sysregs */\n \tstruct kvm_sysreg_masks\t*sysreg_masks;\n \n-\t/* Count the number of VNCR_EL2 currently mapped */\n-\tatomic_t vncr_map_count;\n+\t/* Count the number of VNCR_EL2 TLBs */\n+\tatomic_t vncr_tlb_count;\n \n \t/*\n \t * For an untrusted host VM, 'pkvm.handle' is used to lookup\ndiff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h\nindex 012d711034d17..cc48817a5bf7a 100644\n--- a/arch/arm64/include/asm/kvm_nested.h\n+++ b/arch/arm64/include/asm/kvm_nested.h\n@@ -388,6 +388,8 @@ struct s1_walk_result {\n \tbool\tfailed;\n };\n \n+#define S1_MMU_DISABLED\t\t(-127)\n+\n static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)\n {\n \twr-\u003efst\t\t= fst;\n@@ -396,6 +398,11 @@ static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)\n \twr-\u003efailed\t= true;\n }\n \n+static inline bool s1_mmu_disabled(struct s1_walk_result *wr)\n+{\n+\treturn wr-\u003elevel == S1_MMU_DISABLED;\n+}\n+\n int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,\n \t\t struct s1_walk_result *wr, u64 va);\n int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa,\ndiff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c\nindex 640f2dc00a8ba..0926426b87989 100644\n--- a/arch/arm64/kvm/at.c\n+++ b/arch/arm64/kvm/at.c\n@@ -11,8 +11,6 @@\n #include \u003casm/kvm_mmu.h\u003e\n #include \u003casm/lsui.h\u003e\n \n-#define S1_MMU_DISABLED\t\t(-127)\n-\n static int get_ia_size(struct s1_walk_info *wi)\n {\n \treturn 64 - wi-\u003etxsz;\ndiff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c\nindex bbe9cebd3d9d5..eb59549ec2172 100644\n--- a/arch/arm64/kvm/hyp/vhe/switch.c\n+++ b/arch/arm64/kvm/hyp/vhe/switch.c\n@@ -424,11 +424,11 @@ static bool kvm_hyp_handle_tlbi_el2(struct kvm_vcpu *vcpu, u64 *exit_code)\n \t\treturn false;\n \n \t/*\n-\t * If we have to check for any VNCR mapping being invalidated,\n+\t * If we have to check for any VNCR TLB being invalidated,\n \t * go back to the slow path for further processing.\n \t */\n \tif (vcpu_el2_e2h_is_set(vcpu) \u0026\u0026 vcpu_el2_tge_is_set(vcpu) \u0026\u0026\n-\t atomic_read(\u0026vcpu-\u003ekvm-\u003earch.vncr_map_count))\n+\t atomic_read(\u0026vcpu-\u003ekvm-\u003earch.vncr_tlb_count))\n \t\treturn false;\n \n \t__kvm_skip_instr(vcpu);\ndiff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c\nindex dfb96edbdc43c..1e3fd98f6589b 100644\n--- a/arch/arm64/kvm/nested.c\n+++ b/arch/arm64/kvm/nested.c\n@@ -48,7 +48,7 @@ void kvm_init_nested(struct kvm *kvm)\n {\n \tkvm-\u003earch.nested_mmus = NULL;\n \tkvm-\u003earch.nested_mmus_size = 0;\n-\tatomic_set(\u0026kvm-\u003earch.vncr_map_count, 0);\n+\tatomic_set(\u0026kvm-\u003earch.vncr_tlb_count, 0);\n }\n \n static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)\n@@ -506,7 +506,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,\n \treturn ret;\n }\n \n-static unsigned int ttl_to_size(u8 ttl)\n+static unsigned int __ttl_to_size(u8 ttl)\n {\n \tint level = ttl \u0026 3;\n \tint gran = (ttl \u003e\u003e 2) \u0026 3;\n@@ -562,10 +562,22 @@ static unsigned int ttl_to_size(u8 ttl)\n \treturn max_size;\n }\n \n-static u8 pgshift_level_to_ttl(u16 shift, u8 level)\n+static unsigned int ttl_to_size(u8 ttl)\n+{\n+\treturn __ttl_to_size(ttl) ?: SZ_1G;\n+}\n+\n+static u8 pgshift_level_to_ttl(u16 shift, s8 level)\n {\n \tu8 ttl;\n \n+\t/*\n+\t * If we don't have a proper level, fallback to the maximum\n+\t * size.\n+\t */\n+\tif (level \u003c 0)\n+\t\treturn 0;\n+\n \tswitch(shift) {\n \tcase 12:\n \t\tttl = TLBI_TTL_TG_4K;\n@@ -676,7 +688,11 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)\n \t\tttl = get_guest_mapping_ttl(mmu, addr);\n \t}\n \n-\tmax_size = ttl_to_size(ttl);\n+\t/*\n+\t * Don't use the default 1GB fallback, as we can adapt to the\n+\t * max mapping size we allow at S2.\n+\t */\n+\tmax_size = __ttl_to_size(ttl);\n \n \tif (!max_size) {\n \t\t/* Compute the maximum extent of the invalidation */\n@@ -879,6 +895,12 @@ void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu)\n \t}\n }\n \n+static void unmap_l1_vncr(struct vncr_tlb *vt)\n+{\n+\tclear_fixmap(vncr_fixmap(vt-\u003ecpu));\n+\tvt-\u003ecpu = -1;\n+}\n+\n static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu)\n {\n \tif (!host_data_test_flag(L1_VNCR_MAPPED))\n@@ -887,10 +909,8 @@ static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu)\n \tBUG_ON(vcpu-\u003earch.vncr_tlb-\u003ecpu != smp_processor_id());\n \tBUG_ON(is_hyp_ctxt(vcpu));\n \n-\tclear_fixmap(vncr_fixmap(vcpu-\u003earch.vncr_tlb-\u003ecpu));\n-\tvcpu-\u003earch.vncr_tlb-\u003ecpu = -1;\n+\tunmap_l1_vncr(vcpu-\u003earch.vncr_tlb);\n \thost_data_clear_flag(L1_VNCR_MAPPED);\n-\tatomic_dec(\u0026vcpu-\u003ekvm-\u003earch.vncr_map_count);\n }\n \n void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu)\n@@ -978,11 +998,27 @@ u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime)\n \treturn asid;\n }\n \n-static void invalidate_vncr(struct vncr_tlb *vt)\n+static void invalidate_vncr(struct kvm *kvm, struct vncr_tlb *vt)\n {\n+\tBUG_ON(!vt-\u003evalid);\n \tvt-\u003evalid = false;\n+\tatomic_dec(\u0026kvm-\u003earch.vncr_tlb_count);\n \tif (vt-\u003ecpu != -1)\n-\t\tclear_fixmap(vncr_fixmap(vt-\u003ecpu));\n+\t\tunmap_l1_vncr(vt);\n+}\n+\n+static bool vncr_tlb_intersects(struct vncr_tlb *vt, u64 addr,\n+\t\t\t\tu64 scope_start, u64 scope_size)\n+{\n+\tu64 tlb_size, tlb_start, tlb_end, scope_end;\n+\n+\ttlb_size = ttl_to_size(pgshift_level_to_ttl(vt-\u003ewi.pgshift, vt-\u003ewr.level));\n+\n+\ttlb_start = addr \u0026 ~(tlb_size - 1);\n+\ttlb_end = tlb_start + tlb_size - 1;\n+\tscope_end = scope_start + scope_size - 1;\n+\n+\treturn !(tlb_end \u003c scope_start || tlb_start \u003e scope_end);\n }\n \n /*\n@@ -1007,19 +1043,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)\n \tif (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY))\n \t\treturn;\n \n-\tkvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {\n-\t\tu64 ipa_start, ipa_end, ipa_size;\n-\n-\t\tipa_size = ttl_to_size(pgshift_level_to_ttl(vt-\u003ewi.pgshift,\n-\t\t\t\t\t\t\t vt-\u003ewr.level));\n-\t\tipa_start = vt-\u003ewr.pa \u0026 ~(ipa_size - 1);\n-\t\tipa_end = ipa_start + ipa_size;\n-\n-\t\tif (ipa_end \u003c= start || ipa_start \u003e= end)\n-\t\t\tcontinue;\n-\n-\t\tinvalidate_vncr(vt);\n-\t}\n+\tkvm_for_each_vncr_tlb(i, vcpu, vt, kvm)\n+\t\tif (vncr_tlb_intersects(vt, vt-\u003ewr.pa, start, end - start))\n+\t\t\tinvalidate_vncr(kvm, vt);\n }\n \n struct s1e2_tlbi_scope {\n@@ -1045,28 +1071,19 @@ static void invalidate_vncr_va(struct kvm *kvm,\n \tlockdep_assert_held_write(\u0026kvm-\u003emmu_lock);\n \n \tkvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {\n-\t\tu64 va_start, va_end, va_size;\n-\n-\t\tva_size = ttl_to_size(pgshift_level_to_ttl(vt-\u003ewi.pgshift,\n-\t\t\t\t\t\t\t vt-\u003ewr.level));\n-\t\tva_start = vt-\u003egva \u0026 ~(va_size - 1);\n-\t\tva_end = va_start + va_size;\n-\n \t\tswitch (scope-\u003etype) {\n \t\tcase TLBI_ALL:\n \t\t\tbreak;\n \n \t\tcase TLBI_VA:\n-\t\t\tif (va_end \u003c= scope-\u003eva ||\n-\t\t\t va_start \u003e= (scope-\u003eva + scope-\u003esize))\n+\t\t\tif (!vncr_tlb_intersects(vt, vt-\u003egva, scope-\u003eva, scope-\u003esize))\n \t\t\t\tcontinue;\n \t\t\tif (vt-\u003ewr.nG \u0026\u0026 vt-\u003ewr.asid != scope-\u003easid)\n \t\t\t\tcontinue;\n \t\t\tbreak;\n \n \t\tcase TLBI_VAA:\n-\t\t\tif (va_end \u003c= scope-\u003eva ||\n-\t\t\t va_start \u003e= (scope-\u003eva + scope-\u003esize))\n+\t\t\tif (!vncr_tlb_intersects(vt, vt-\u003egva, scope-\u003eva, scope-\u003esize))\n \t\t\t\tcontinue;\n \t\t\tbreak;\n \n@@ -1076,7 +1093,7 @@ static void invalidate_vncr_va(struct kvm *kvm,\n \t\t\tbreak;\n \t\t}\n \n-\t\tinvalidate_vncr(vt);\n+\t\tinvalidate_vncr(kvm, vt);\n \t}\n }\n \n@@ -1126,8 +1143,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,\n \tcase OP_TLBI_VALE1OSNXS:\n \t\tscope-\u003etype = TLBI_VA;\n \t\tscope-\u003esize = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val));\n-\t\tif (!scope-\u003esize)\n-\t\t\tscope-\u003esize = SZ_1G;\n \t\tscope-\u003eva = tlbi_va_s1_to_va(val) \u0026 ~(scope-\u003esize - 1);\n \t\tscope-\u003easid = FIELD_GET(TLBIR_ASID_MASK, val);\n \t\tbreak;\n@@ -1154,8 +1169,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,\n \tcase OP_TLBI_VAALE1OSNXS:\n \t\tscope-\u003etype = TLBI_VAA;\n \t\tscope-\u003esize = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val));\n-\t\tif (!scope-\u003esize)\n-\t\t\tscope-\u003esize = SZ_1G;\n \t\tscope-\u003eva = tlbi_va_s1_to_va(val) \u0026 ~(scope-\u003esize - 1);\n \t\tbreak;\n \tcase OP_TLBI_RVAE2:\n@@ -1316,13 +1329,20 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)\n * intersects with the TLBI request, invalidate it, and unmap the page\n * from the fixmap. Because we need to look at all the vcpu-private TLBs,\n * this requires some wide-ranging locking to ensure that nothing races\n- * against it. This may require some refcounting to avoid the search when\n- * no such TLB is present.\n+ * against it. This requires some refcounting to avoid the search when\n+ * no such TLB is present (see below).\n *\n * - On MMU notifiers, we must invalidate our TLB in a similar way, but\n * looking at the IPA instead. The funny part is that there may not be a\n * stage-2 mapping for this page if L1 hasn't accessed it using LD/ST\n * instructions.\n+ *\n+ * - vncr_tlb_count tracks the number of valid VNCR TLBs VM-wide. This isn't\n+ * the number of *mapped* L1 VNCR pages, which is likely be a subset (and\n+ * by definition, a TLBI handled from L1 runs with the canonical VNCR\n+ * page, not the L1's). The innermost trap handling code checks this to\n+ * find out whether to return to the guest ASAP (no L1 TLBs) or to visit\n+ * this part of the world for some extra invalidation work.\n */\n \n int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu)\n@@ -1377,7 +1397,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)\n \t */\n \tscoped_guard(write_lock, \u0026vcpu-\u003ekvm-\u003emmu_lock) {\n \t\tthis_cpu_reset_vncr_fixmap(vcpu);\n-\t\tvt-\u003evalid = false;\n+\t\tif (vt-\u003evalid)\n+\t\t\tinvalidate_vncr(vcpu-\u003ekvm, vt);\n \n \t\tvt-\u003ewi = (struct s1_walk_info) {\n \t\t\t.regime\t= TR_EL20,\n@@ -1449,6 +1470,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)\n \t\tvt-\u003evalid = true;\n \t\tvt-\u003ecpu = -1;\n \n+\t\tatomic_inc(\u0026vcpu-\u003ekvm-\u003earch.vncr_tlb_count);\n+\n \t\tkvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu);\n \t\tkvm_release_faultin_page(vcpu-\u003ekvm, page, false, vt-\u003ewr.pw \u0026\u0026 vt-\u003ehpa_writable);\n \t}\n@@ -1568,6 +1591,9 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)\n \tif (!vt-\u003evalid)\n \t\treturn;\n \n+\tif (!(vcpu_read_sys_reg(vcpu, SCTLR_EL2) \u0026 SCTLR_ELx_M) != s1_mmu_disabled(\u0026vt-\u003ewr))\n+\t\treturn;\n+\n \tif (read_vncr_el2(vcpu) != vt-\u003egva)\n \t\treturn;\n \n@@ -1592,7 +1618,6 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)\n \tif (pgprot_val(prot) != pgprot_val(PAGE_NONE)) {\n \t\t__set_fixmap(vncr_fixmap(vt-\u003ecpu), vt-\u003ehpa, prot);\n \t\thost_data_set_flag(L1_VNCR_MAPPED);\n-\t\tatomic_inc(\u0026vcpu-\u003ekvm-\u003earch.vncr_map_count);\n \t}\n }\n \n"
} |
| 2/1 | 2026/08/02 12:58 | agent | patch-evaluator |
2mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_handle_vncr_abort",
"kvm_handle_s1e2_tlbi",
"kvm_nested_s2_wp",
"kvm_nested_s2_unmap",
"compute_tlb_inval_range",
"kvm_vcpu_allocate_vncr_tlb"
],
"Reasoning": "The patch modifies core KVM ARM64 nested virtualization logic, specifically around VNCR (Virtual Nested Control Register) TLB handling and invalidation. It introduces new tracking mechanisms and modifies how TLB invalidation ranges are computed and applied. These are reachable core kernel paths that should be fuzzed to ensure correctness and prevent regressions.",
"WorthFuzzing": true
}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 e9d5ae9d48b814e8c8f67e43325c2efc28808bda
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Aug 2 12:58:28 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5c..108966a9db12b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -411,8 +411,8 @@ struct kvm_arch {
/* Masks for VNCR-backed and general EL2 sysregs */
struct kvm_sysreg_masks *sysreg_masks;
- /* Count the number of VNCR_EL2 currently mapped */
- atomic_t vncr_map_count;
+ /* Count the number of VNCR_EL2 TLBs */
+ atomic_t vncr_tlb_count;
/*
* For an untrusted host VM, 'pkvm.handle' is used to lookup
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index 012d711034d17..cc48817a5bf7a 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -388,6 +388,8 @@ struct s1_walk_result {
bool failed;
};
+#define S1_MMU_DISABLED (-127)
+
static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
{
wr->fst = fst;
@@ -396,6 +398,11 @@ static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
wr->failed = true;
}
+static inline bool s1_mmu_disabled(struct s1_walk_result *wr)
+{
+ return wr->level == S1_MMU_DISABLED;
+}
+
int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
struct s1_walk_result *wr, u64 va);
int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa,
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 640f2dc00a8ba..0926426b87989 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -11,8 +11,6 @@
#include <asm/kvm_mmu.h>
#include <asm/lsui.h>
-#define S1_MMU_DISABLED (-127)
-
static int get_ia_size(struct s1_walk_info *wi)
{
return 64 - wi->txsz;
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index bbe9cebd3d9d5..eb59549ec2172 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -424,11 +424,11 @@ static bool kvm_hyp_handle_tlbi_el2(struct kvm_vcpu *vcpu, u64 *exit_code)
return false;
/*
- * If we have to check for any VNCR mapping being invalidated,
+ * If we have to check for any VNCR TLB being invalidated,
* go back to the slow path for further processing.
*/
if (vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu) &&
- atomic_read(&vcpu->kvm->arch.vncr_map_count))
+ atomic_read(&vcpu->kvm->arch.vncr_tlb_count))
return false;
__kvm_skip_instr(vcpu);
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index dfb96edbdc43c..1e3fd98f6589b 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -48,7 +48,7 @@ void kvm_init_nested(struct kvm *kvm)
{
kvm->arch.nested_mmus = NULL;
kvm->arch.nested_mmus_size = 0;
- atomic_set(&kvm->arch.vncr_map_count, 0);
+ atomic_set(&kvm->arch.vncr_tlb_count, 0);
}
static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
@@ -506,7 +506,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,
return ret;
}
-static unsigned int ttl_to_size(u8 ttl)
+static unsigned int __ttl_to_size(u8 ttl)
{
int level = ttl & 3;
int gran = (ttl >> 2) & 3;
@@ -562,10 +562,22 @@ static unsigned int ttl_to_size(u8 ttl)
return max_size;
}
-static u8 pgshift_level_to_ttl(u16 shift, u8 level)
+static unsigned int ttl_to_size(u8 ttl)
+{
+ return __ttl_to_size(ttl) ?: SZ_1G;
+}
+
+static u8 pgshift_level_to_ttl(u16 shift, s8 level)
{
u8 ttl;
+ /*
+ * If we don't have a proper level, fallback to the maximum
+ * size.
+ */
+ if (level < 0)
+ return 0;
+
switch(shift) {
case 12:
ttl = TLBI_TTL_TG_4K;
@@ -676,7 +688,11 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)
ttl = get_guest_mapping_ttl(mmu, addr);
}
- max_size = ttl_to_size(ttl);
+ /*
+ * Don't use the default 1GB fallback, as we can adapt to the
+ * max mapping size we allow at S2.
+ */
+ max_size = __ttl_to_size(ttl);
if (!max_size) {
/* Compute the maximum extent of the invalidation */
@@ -879,6 +895,12 @@ void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu)
}
}
+static void unmap_l1_vncr(struct vncr_tlb *vt)
+{
+ clear_fixmap(vncr_fixmap(vt->cpu));
+ vt->cpu = -1;
+}
+
static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu)
{
if (!host_data_test_flag(L1_VNCR_MAPPED))
@@ -887,10 +909,8 @@ static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu)
BUG_ON(vcpu->arch.vncr_tlb->cpu != smp_processor_id());
BUG_ON(is_hyp_ctxt(vcpu));
- clear_fixmap(vncr_fixmap(vcpu->arch.vncr_tlb->cpu));
- vcpu->arch.vncr_tlb->cpu = -1;
+ unmap_l1_vncr(vcpu->arch.vncr_tlb);
host_data_clear_flag(L1_VNCR_MAPPED);
- atomic_dec(&vcpu->kvm->arch.vncr_map_count);
}
void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu)
@@ -978,11 +998,27 @@ u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime)
return asid;
}
-static void invalidate_vncr(struct vncr_tlb *vt)
+static void invalidate_vncr(struct kvm *kvm, struct vncr_tlb *vt)
{
+ BUG_ON(!vt->valid);
vt->valid = false;
+ atomic_dec(&kvm->arch.vncr_tlb_count);
if (vt->cpu != -1)
- clear_fixmap(vncr_fixmap(vt->cpu));
+ unmap_l1_vncr(vt);
+}
+
+static bool vncr_tlb_intersects(struct vncr_tlb *vt, u64 addr,
+ u64 scope_start, u64 scope_size)
+{
+ u64 tlb_size, tlb_start, tlb_end, scope_end;
+
+ tlb_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level));
+
+ tlb_start = addr & ~(tlb_size - 1);
+ tlb_end = tlb_start + tlb_size - 1;
+ scope_end = scope_start + scope_size - 1;
+
+ return !(tlb_end < scope_start || tlb_start > scope_end);
}
/*
@@ -1007,19 +1043,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)
if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY))
return;
- kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
- u64 ipa_start, ipa_end, ipa_size;
-
- ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
- vt->wr.level));
- ipa_start = vt->wr.pa & ~(ipa_size - 1);
- ipa_end = ipa_start + ipa_size;
-
- if (ipa_end <= start || ipa_start >= end)
- continue;
-
- invalidate_vncr(vt);
- }
+ kvm_for_each_vncr_tlb(i, vcpu, vt, kvm)
+ if (vncr_tlb_intersects(vt, vt->wr.pa, start, end - start))
+ invalidate_vncr(kvm, vt);
}
struct s1e2_tlbi_scope {
@@ -1045,28 +1071,19 @@ static void invalidate_vncr_va(struct kvm *kvm,
lockdep_assert_held_write(&kvm->mmu_lock);
kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
- u64 va_start, va_end, va_size;
-
- va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
- vt->wr.level));
- va_start = vt->gva & ~(va_size - 1);
- va_end = va_start + va_size;
-
switch (scope->type) {
case TLBI_ALL:
break;
case TLBI_VA:
- if (va_end <= scope->va ||
- va_start >= (scope->va + scope->size))
+ if (!vncr_tlb_intersects(vt, vt->gva, scope->va, scope->size))
continue;
if (vt->wr.nG && vt->wr.asid != scope->asid)
continue;
break;
case TLBI_VAA:
- if (va_end <= scope->va ||
- va_start >= (scope->va + scope->size))
+ if (!vncr_tlb_intersects(vt, vt->gva, scope->va, scope->size))
continue;
break;
@@ -1076,7 +1093,7 @@ static void invalidate_vncr_va(struct kvm *kvm,
break;
}
- invalidate_vncr(vt);
+ invalidate_vncr(kvm, vt);
}
}
@@ -1126,8 +1143,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,
case OP_TLBI_VALE1OSNXS:
scope->type = TLBI_VA;
scope->size = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val));
- if (!scope->size)
- scope->size = SZ_1G;
scope->va = tlbi_va_s1_to_va(val) & ~(scope->size - 1);
scope->asid = FIELD_GET(TLBIR_ASID_MASK, val);
break;
@@ -1154,8 +1169,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,
case OP_TLBI_VAALE1OSNXS:
scope->type = TLBI_VAA;
scope->size = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val));
- if (!scope->size)
- scope->size = SZ_1G;
scope->va = tlbi_va_s1_to_va(val) & ~(scope->size - 1);
break;
case OP_TLBI_RVAE2:
@@ -1316,13 +1329,20 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
* intersects with the TLBI request, invalidate it, and unmap the page
* from the fixmap. Because we need to look at all the vcpu-private TLBs,
* this requires some wide-ranging locking to ensure that nothing races
- * against it. This may require some refcounting to avoid the search when
- * no such TLB is present.
+ * against it. This requires some refcounting to avoid the search when
+ * no such TLB is present (see below).
*
* - On MMU notifiers, we must invalidate our TLB in a similar way, but
* looking at the IPA instead. The funny part is that there may not be a
* stage-2 mapping for this page if L1 hasn't accessed it using LD/ST
* instructions.
+ *
+ * - vncr_tlb_count tracks the number of valid VNCR TLBs VM-wide. This isn't
+ * the number of *mapped* L1 VNCR pages, which is likely be a subset (and
+ * by definition, a TLBI handled from L1 runs with the canonical VNCR
+ * page, not the L1's). The innermost trap handling code checks this to
+ * find out whether to return to the guest ASAP (no L1 TLBs) or to visit
+ * this part of the world for some extra invalidation work.
*/
int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu)
@@ -1377,7 +1397,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
*/
scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
this_cpu_reset_vncr_fixmap(vcpu);
- vt->valid = false;
+ if (vt->valid)
+ invalidate_vncr(vcpu->kvm, vt);
vt->wi = (struct s1_walk_info) {
.regime = TR_EL20,
@@ -1449,6 +1470,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
vt->valid = true;
vt->cpu = -1;
+ atomic_inc(&vcpu->kvm->arch.vncr_tlb_count);
+
kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu);
kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable);
}
@@ -1568,6 +1591,9 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
if (!vt->valid)
return;
+ if (!(vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_M) != s1_mmu_disabled(&vt->wr))
+ return;
+
if (read_vncr_el2(vcpu) != vt->gva)
return;
@@ -1592,7 +1618,6 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
if (pgprot_val(prot) != pgprot_val(PAGE_NONE)) {
__set_fixmap(vncr_fixmap(vt->cpu), vt->hpa, prot);
host_data_set_flag(L1_VNCR_MAPPED);
- atomic_inc(&vcpu->kvm->arch.vncr_map_count);
}
}
|
| 3/2 | 2026/08/02 12:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6699 output: 34 thoughts: 266 Thoughts: **My Analysis of this KVM ARM64 Nested Virtualization Patch** Okay, so I've got this patch here that's poking around in KVM's nested virtualization implementation for ARM64, specifically the VNCR (Virtual Nested Control Register) TLB handling. It's making changes that seem pretty fundamental, like renaming `vncr_map_count` to `vncr_tlb_count`, and adjusting how these VNCR TLBs are invalidated and tracked. I see new functions being introduced: `s1_mmu_disabled()` and `__ttl_to_size()`, plus some modifications to existing logic in `ttl_to_size()`. Then, we have changes in functions like `invalidate_vncr_va()`, `kvm_invalidate_vncr_ipa()`, `this_cpu_reset_vncr_fixmap()`, `invalidate_vncr()`, `kvm_translate_vncr()`, and `kvm_map_l1_vncr()`. These are all core KVM ARM64 nested virtualization functions. This is critical kernel logic, no doubt about it, and it definitely needs a thorough fuzzing campaign. We absolutely need to make sure this doesn't introduce any regressions or vulnerabilities. My first step is to nail down the focus symbols and required configuration options. I'm assuming we'll need `CONFIG_KVM` and `CONFIG_ARM64`, which is implicit given the architecture, of course. For nested virtualization specifically, there might be a more specific configuration parameter or two, but the standard KVM ARM64 config typically covers that. I should probably check `arch/arm64/kvm/Kconfig` to be sure. That's a good starting point to confirm all required config options. |
| 4/2 | 2026/08/02 12:58 | tool | grepper |
0mArgs:{
"Expression": "config KVM",
"PathPrefix": "arch/arm64/kvm/Kconfig"
}Results: {
"Output": "arch/arm64/kvm/Kconfig=18=if VIRTUALIZATION\narch/arm64/kvm/Kconfig-19-\narch/arm64/kvm/Kconfig:20:menuconfig KVM\narch/arm64/kvm/Kconfig-21-\tbool \"Kernel-based Virtual Machine (KVM) support\"\n"
} |
| 5/2 | 2026/08/02 12:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7478 output: 35 thoughts: 20 Thoughts: Okay, here's my summary, presented as a stream of thought: **Checking for ARM64 Nested KVM Configuration** Alright, let's dive in. First things first, before I go any further down this rabbit hole, I need to make sure I haven't missed something obvious. The goal is nested KVM on ARM64, right? So, the immediate, *must-check* item on my mental checklist is: Is there any specific or dedicated configuration guidance, perhaps specific quirks, or known gotchas, for running nested KVM on an ARM64 architecture that I should be aware of, before wasting valuable time reinventing a wheel? Let me go and confirm. |
| 6/2 | 2026/08/02 12:58 | tool | grepper |
0mError:no matches Args: {
"Expression": "config KVM_",
"PathPrefix": "arch/arm64/kvm/Kconfig"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/08/02 12:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7692 output: 33 thoughts: 25 Thoughts: Here's my thought process, summarized as if I were considering the situation: **Investigating KVM Nested Virtualization on ARM64** Okay, so I need to figure out the status of nested virtualization support in KVM on ARM64. Instead of manually sifting through the source code and documentation, let's be efficient. The quickest way to get a high-level view of available configurations is often to check the Kconfig files. Specifically, I should look in `arch/arm64/kvm/Kconfig`. I'm going to quickly search that file for anything related to the term "NESTED." That should highlight any relevant configuration options directly related to the feature. That's a much faster and focused approach than starting a deep dive from scratch. |
| 8/2 | 2026/08/02 12:59 | tool | grepper |
0mError:no matches Args: {
"Expression": "NESTED",
"PathPrefix": "arch/arm64/kvm/Kconfig"
}Results: {
"error": "no matches"
} |
| 9/2 | 2026/08/02 12:59 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7926 output: 33 thoughts: 51 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Nested Virtualization on ARM64 - Digging into the Code** Alright, so I'm thinking about nested virtualization on ARM64. The question is, does it have its own dedicated configuration option, or is it just implicitly enabled as part of the KVM setup on that architecture? I vaguely recall that nested virtualization on x86 has its own special knob, but I'm drawing a blank on ARM64. I should really brush up on my KVM internals again... To get a definitive answer, I'm going to head straight for the source code. Specifically, the `arch/arm64/kvm/nested.c` file should be the place to start. I'm hoping to find some conditional compilation directives in there, something like `#ifdef CONFIG_SOMETHING_NESTED_ARM64`. If it *is* conditionally compiled, that will give me my answer quickly. If not, it means nested virtualization might be a standard part of KVM's ARM64 support, which is still important to determine. Time to get my hands dirty. |
| 10/2 | 2026/08/02 12:59 | tool | grepper |
0mArgs:{
"Expression": "nested.o",
"PathPrefix": "arch/arm64/kvm/Makefile"
}Results: {
"Output": "arch/arm64/kvm/Makefile=16=kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \\\n--\narch/arm64/kvm/Makefile-19-\t vgic-sys-reg-v3.o fpsimd.o pkvm.o \\\narch/arm64/kvm/Makefile:20:\t arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \\\narch/arm64/kvm/Makefile-21-\t vgic/vgic.o vgic/vgic-init.o \\\n--\narch/arm64/kvm/Makefile-25-\t vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \\\narch/arm64/kvm/Makefile:26:\t vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \\\narch/arm64/kvm/Makefile-27-\t vgic/vgic-v5.o\n"
} |
| 11/2 | 2026/08/02 12:59 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8541 output: 41 thoughts: 360 Thoughts: **Analyzing the KVM Patch for Key Modifications** Okay, so this patch is clearly related to KVM, specifically the nested virtualization aspects, since it's unconditionally compiled when KVM is enabled. So, `KVM` is the primary configuration variable that drives this. Now, I need to pinpoint the core changes. Let's see, the code modifications are in the following: - `kvm_init_nested` - `ttl_to_size` - `pgshift_level_to_ttl` - `compute_tlb_inval_range` - `kvm_vcpu_load_hw_mmu` (not directly, just context in a function call) - `unmap_l1_vncr` - `this_cpu_reset_vncr_fixmap` - `invalidate_vncr` - `vncr_tlb_intersects` - `kvm_invalidate_vncr_ipa` - `invalidate_vncr_va` - `compute_s1_tlbi_range` - `kvm_vcpu_allocate_vncr_tlb` - `kvm_translate_vncr` - `kvm_map_l1_vncr` Now, to understand the API interactions, I need to focus on the entry points that are *not* static, or inlined. Those are the ones others are likely to call. So, I see: - `kvm_init_nested` - `kvm_walk_nested_s2` (context) - `compute_tlb_inval_range` - `kvm_vcpu_put_hw_mmu` (context) - `kvm_arch_flush_shadow_all` (context) - `kvm_vcpu_allocate_vncr_tlb` Wait, `kvm_invalidate_vncr_ipa` is static? I need to double-check that; there's a good chance I need to track down other callers. |
| 12/2 | 2026/08/02 12:59 | tool | grepper |
0mArgs:{
"Expression": "kvm_invalidate_vncr_ipa",
"PathPrefix": "arch/arm64/kvm/nested.c"
}Results: {
"Output": "arch/arm64/kvm/nested.c=1010=static bool vncr_tlb_intersects(struct vncr_tlb *vt, u64 addr,\n--\narch/arm64/kvm/nested.c-1034-\narch/arm64/kvm/nested.c:1035:static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)\narch/arm64/kvm/nested.c-1036-{\n--\narch/arm64/kvm/nested.c=1229=void kvm_nested_s2_wp(struct kvm *kvm)\n--\narch/arm64/kvm/nested.c-1244-\narch/arm64/kvm/nested.c:1245:\tkvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm-\u003earch.mmu.pgt-\u003eia_bits));\narch/arm64/kvm/nested.c-1246-}\n--\narch/arm64/kvm/nested.c=1248=void kvm_nested_s2_unmap(struct kvm *kvm, bool may_block)\n--\narch/arm64/kvm/nested.c-1263-\narch/arm64/kvm/nested.c:1264:\tkvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm-\u003earch.mmu.pgt-\u003eia_bits));\narch/arm64/kvm/nested.c-1265-}\n"
} |
| 13/2 | 2026/08/02 12:59 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9826 output: 39 thoughts: 29 Thoughts: Here's my thought process, summarized as you requested: **KVM Virtualization Inquiry** Okay, so I'm looking at this KVM code... and the first thing that jumps out is that `kvm_invalidate_vncr_ipa` is definitely a static function. No surprises there, makes sense in the context. Now, the real question is: What about `kvm_translate_vncr`? I need to quickly determine its scope and usage. Is it static as well, or is it exposed more broadly? Understanding that function's visibility will be key to figuring out how it interacts with other parts of the virtualization infrastructure. Need to keep digging... |
| 14/2 | 2026/08/02 12:59 | tool | grepper |
0mArgs:{
"Expression": "kvm_translate_vncr",
"PathPrefix": "arch/arm64/kvm/nested.c"
}Results: {
"Output": "arch/arm64/kvm/nested.c=1373=static u64 read_vncr_el2(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1377-\narch/arm64/kvm/nested.c:1378:static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)\narch/arm64/kvm/nested.c-1379-{\n--\narch/arm64/kvm/nested.c=1510=int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1527-\narch/arm64/kvm/nested.c:1528:\tret = kvm_translate_vncr(vcpu, \u0026is_gmem);\narch/arm64/kvm/nested.c-1529-\tswitch (ret) {\n"
} |
| 15/2 | 2026/08/02 12:59 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10301 output: 42 thoughts: 37 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into VNCR Abort Handling** Okay, so I'm looking at how Virtual Nested Code Re-entry (VNCR) aborts are handled within the KVM hypervisor. I see `kvm_handle_vncr_abort` is the function we're concerned with initially. The first thing that jumps out is that it calls `kvm_translate_vncr`. That makes sense - we need to translate the faulting address to figure out what went wrong. But, and this is where I need to take a closer look, what's the relationship with `kvm_map_l1_vncr`? We're dealing with nested paging here, and I suspect `kvm_map_l1_vncr` might be crucial in establishing or maintaining the L1 mappings that are relevant in this scenario. Is the abort happening because of an issue during the mapping process done by `kvm_map_l1_vncr`, or is this function more of a setup, and the actual translation done by `kvm_translate_vncr` is the culprit? I need to understand how these two functions interact to get a full picture of the abort handling flow. I'll need to trace back the call paths and consider the specific context of the VNCR abort to see how the L1 mappings play a role. I'm going to dig into the surrounding code now. |
| 16/2 | 2026/08/02 12:59 | tool | grepper |
0mArgs:{
"Expression": "kvm_map_l1_vncr",
"PathPrefix": "arch/arm64/kvm/nested.c"
}Results: {
"Output": "arch/arm64/kvm/nested.c=1510=int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1569-\narch/arm64/kvm/nested.c:1570:static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)\narch/arm64/kvm/nested.c-1571-{\n--\narch/arm64/kvm/nested.c=1957=void check_nested_vcpu_requests(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1970-\tif (kvm_check_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu))\narch/arm64/kvm/nested.c:1971:\t\tkvm_map_l1_vncr(vcpu);\narch/arm64/kvm/nested.c-1972-\n"
} |
| 17/2 | 2026/08/02 12:59 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10971 output: 38 thoughts: 36 Thoughts: Here's my take on the thought process, considering I'm an expert: **Navigating KVM Virtualization Internals** Okay, let me see... I'm tracing the flow of nested virtualization VCPU requests. Right, `check_nested_vcpu_requests` is ultimately using `kvm_map_l1_vncr`. That makes sense; it's the core mapping function for the L1 VNCr (Nested Virtual CPU Request) structure. But my mind drifts. That's fine. Hold on... I need to consider `invalidate_vncr_va`. That's critical too. My experience tells me that is the function responsible for clearing the virtual address mapping associated with the VNCR. This could happen on a VM exit, an invalidation of the guest page tables, or some other event that requires the old VNCR to be re-mapped on a re-entry event. I need to keep that one fresh in my head as I continue. Let me mentally link those two for now, just to make sure I don't lose the thread... `check_nested_vcpu_requests` with its dependency on `kvm_map_l1_vncr`, and then consider the invalidation logic of `invalidate_vncr_va`. That's the key. |
| 18/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "invalidate_vncr_va",
"PathPrefix": "arch/arm64/kvm/nested.c"
}Results: {
"Output": "arch/arm64/kvm/nested.c=1051=struct s1e2_tlbi_scope {\n--\narch/arm64/kvm/nested.c-1063-\narch/arm64/kvm/nested.c:1064:static void invalidate_vncr_va(struct kvm *kvm,\narch/arm64/kvm/nested.c-1065-\t\t\t struct s1e2_tlbi_scope *scope)\n--\narch/arm64/kvm/nested.c=1219=void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val)\n--\narch/arm64/kvm/nested.c-1225-\tguard(write_lock)(\u0026vcpu-\u003ekvm-\u003emmu_lock);\narch/arm64/kvm/nested.c:1226:\tinvalidate_vncr_va(vcpu-\u003ekvm, \u0026scope);\narch/arm64/kvm/nested.c-1227-}\n"
} |
| 19/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11603 output: 38 thoughts: 417 Thoughts: **Analyzing TLB Invalidation Changes** Okay, so `kvm_handle_s1e2_tlbi` ultimately calls `invalidate_vncr_va`. That's the starting point. My next move is to identify the functions that are either modified themselves, or call any static functions that *have* been changed. That gives me a good overview of the ripple effect. Let's see... the list I've got is: - `kvm_init_nested` - `compute_tlb_inval_range` - `kvm_vcpu_allocate_vncr_tlb` - `kvm_nested_s2_wp` - `kvm_nested_s2_unmap` - `kvm_handle_vncr_abort` - `check_nested_vcpu_requests` - `kvm_handle_s1e2_tlbi` - `kvm_vcpu_put_hw_mmu` - `kvm_vcpu_load_hw_mmu` These all look like potential candidates for inclusion in my `FocusSymbols` set. But I need to refine that initial list. I need to weed out anything that's too generic or, more importantly, in a hot path. Alright, `kvm_vcpu_put_hw_mmu` and `kvm_vcpu_load_hw_mmu` are definitely performance critical, so that is off the list. Then `check_nested_vcpu_requests` is called on every vCPU run - obviously a hot path. Now, that leaves `kvm_handle_vncr_abort` as a fault handler - that's good. And `kvm_handle_s1e2_tlbi` is an instruction handler, which I also like. Then `kvm_nested_s2_wp` and `kvm_nested_s2_unmap` look relevant, and finally `compute_tlb_inval_range` and `kvm_vcpu_allocate_vncr_tlb` are good picks. Next I'll quickly check if `kvm_handle_vncr_abort` and `kvm_handle_s1e2_tlbi` are exported, or simply non-static. |
| 20/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_handle_vncr_abort",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/handle_exit.c=376=static exit_handle_fn arm_exit_handlers[] = {\n--\narch/arm64/kvm/handle_exit.c-395-\t[ESR_ELx_EC_DABT_LOW]\t= kvm_handle_guest_abort,\narch/arm64/kvm/handle_exit.c:396:\t[ESR_ELx_EC_DABT_CUR]\t= kvm_handle_vncr_abort,\narch/arm64/kvm/handle_exit.c-397-\t[ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug,\n--\narch/arm64/kvm/nested.c=1485=static void handle_vncr_perm(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1509-\narch/arm64/kvm/nested.c:1510:int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)\narch/arm64/kvm/nested.c-1511-{\n"
} |
| 21/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12871 output: 41 thoughts: 26 Thoughts: Here's my take on those thoughts: **Analyzing Exit Handlers in KVM** Okay, so we're talking about exit handlers, confirmed. Good, that's the starting point. Now, to get to the specifics and understand the flow, I need to dig into the `kvm_handle_s1e2_tlbi` function. It seems like this might be a key handler. Time to delve into the code and see what it's responsible for, particularly in the context of TLBI operations. Let's see what interesting tidbits it might hold. I anticipate that this will be a worthwhile path to understanding. |
| 22/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_handle_s1e2_tlbi",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/nested.c=1102=static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,\n--\narch/arm64/kvm/nested.c-1218-\narch/arm64/kvm/nested.c:1219:void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val)\narch/arm64/kvm/nested.c-1220-{\n--\narch/arm64/kvm/sys_regs.c=4135=static bool handle_tlbi_el2(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n--\narch/arm64/kvm/sys_regs.c-4142-\narch/arm64/kvm/sys_regs.c:4143:\tkvm_handle_s1e2_tlbi(vcpu, sys_encoding, p-\u003eregval);\narch/arm64/kvm/sys_regs.c-4144-\treturn true;\n--\narch/arm64/kvm/sys_regs.c=4147=static bool handle_tlbi_el1(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n--\narch/arm64/kvm/sys_regs.c-4177-\tif (vcpu_el2_e2h_is_set(vcpu) \u0026\u0026 vcpu_el2_tge_is_set(vcpu)) {\narch/arm64/kvm/sys_regs.c:4178:\t\tkvm_handle_s1e2_tlbi(vcpu, sys_encoding, p-\u003eregval);\narch/arm64/kvm/sys_regs.c-4179-\t\treturn true;\n"
} |
| 23/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13561 output: 38 thoughts: 41 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating the System Register Handler Call** Alright, so the clue is that we're dealing with calls originating from the `sys_regs` handlers. That immediately narrows down the scope of what we need to look at. We're clearly dealing with something related to system register access, likely within a nested virtualization context. My first step is to focus on memory management with nested virtualization. The next thing to check is definitely going to be `kvm_nested_s2_wp` and `kvm_nested_s2_unmap`. These are highly likely candidates for being involved. The "WP" suggests "write protection," which is a common concern in hypervisor environments. We need to look into how the system handles write protection and memory unmapping with nested paging. These are the pieces of the puzzle that almost certainly define the behaviour and likely point to the root cause of whatever is happening. I need to understand how these functions interact with the guest's view of memory within the nested virtualization setup. |
| 24/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_nested_s2_wp",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/mmu.c=1267=static void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot)\n--\narch/arm64/kvm/mmu.c-1280-\tkvm_stage2_wp_range(\u0026kvm-\u003earch.mmu, start, end);\narch/arm64/kvm/mmu.c:1281:\tkvm_nested_s2_wp(kvm);\narch/arm64/kvm/mmu.c-1282-\twrite_unlock(\u0026kvm-\u003emmu_lock);\n--\narch/arm64/kvm/mmu.c=1325=void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,\n--\narch/arm64/kvm/mmu.c-1347-\narch/arm64/kvm/mmu.c:1348:\tkvm_nested_s2_wp(kvm);\narch/arm64/kvm/mmu.c-1349-}\n--\narch/arm64/kvm/nested.c=1219=void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val)\n--\narch/arm64/kvm/nested.c-1228-\narch/arm64/kvm/nested.c:1229:void kvm_nested_s2_wp(struct kvm *kvm)\narch/arm64/kvm/nested.c-1230-{\n"
} |
| 25/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14295 output: 38 thoughts: 24 Thoughts: Here's a first-person summary, tailored for an expert audience, based on the provided text: **Analyzing MMU Code and TLB Invalidation** Okay, so I'm digging into the MMU code. This likely means I'm dealing with performance bottlenecks or perhaps some subtle memory management issue, potentially related to context switching or concurrent access. Specifically, my focus is currently on the functions being called *from* the MMU code itself. This is always a critical area to examine, as efficiency here directly impacts overall system performance. Let's zoom in on `compute_tlb_inval_range`. This function name immediately clues me in to TLB invalidation, likely related to address space modifications (mappings being altered, unmapped, or invalidated due to memory protection changes, for example). `compute_tlb_inval_range` *suggests* it's responsible for determining the specific *range* of TLB entries that need invalidation – a key optimization. It will probably determine the minimally necessary entries, so we don't end up flushing too many TLB entries unnecessarily, but make sure to flush enough so as to not invalidate. Depending on the architecture, there could be different strategies for that, which is part of what I'd be looking at. I'll need to understand its logic very carefully. Is this the *only* part that deals with the process of invalidation, or is it combined with the instructions? Are the invalidations performed immediately, queued, or deferred? I need to understand how the process is working so that I can understand any potential issues in the context of the larger code. |
| 26/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "compute_tlb_inval_range",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/nested.c=606=static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)\n--\narch/arm64/kvm/nested.c-676-\narch/arm64/kvm/nested.c:677:unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)\narch/arm64/kvm/nested.c-678-{\n--\narch/arm64/kvm/sys_regs.c=4083=static void s2_mmu_unmap_ipa(struct kvm_s2_mmu *mmu,\n--\narch/arm64/kvm/sys_regs.c-4098-\tbase_addr = (info-\u003eipa.addr \u0026 GENMASK_ULL(35, 0)) \u003c\u003c 12;\narch/arm64/kvm/sys_regs.c:4099:\tmax_size = compute_tlb_inval_range(mmu, info-\u003eipa.addr);\narch/arm64/kvm/sys_regs.c-4100-\tbase_addr \u0026= ~(max_size - 1);\n"
} |
| 27/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15022 output: 42 thoughts: 46 Thoughts: Okay, here's my interpretation of those thoughts, framed as my own: **Analyzing a Potential Memory Management Issue** Alright, so we're looking at something that seems to originate from `s2_mmu_unmap_ipa`. This function is interesting because it's triggered from the system register handlers, which means it's pretty low-level and likely performance-sensitive. My immediate thought is to trace the path backward. Where does `s2_mmu_unmap_ipa` get called from? I need to understand the context. The next step is clear – I want to look at `kvm_vcpu_allocate_vncr_tlb`. I suspect there's a connection there, probably related to allocating or managing the Virtual Network Context Region (VNCR) TLB. This could be where the unmapping is actually initiated or triggered, or perhaps where the TLB entries related to the IPA mapping are allocated. This feels like a good lead in narrowing down where to look. |
| 28/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_vcpu_allocate_vncr_tlb",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/arm.c=921=int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/arm.c-951-\tif (vcpu_has_nv(vcpu)) {\narch/arm64/kvm/arm.c:952:\t\tret = kvm_vcpu_allocate_vncr_tlb(vcpu);\narch/arm64/kvm/arm.c-953-\t\tif (ret)\n--\narch/arm64/kvm/nested.c=1284=void kvm_arch_flush_shadow_all(struct kvm *kvm)\n--\narch/arm64/kvm/nested.c-1347-\narch/arm64/kvm/nested.c:1348:int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu)\narch/arm64/kvm/nested.c-1349-{\n"
} |
| 29/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15597 output: 39 thoughts: 169 Thoughts: **Analyzing the Recent Changes in the KVM Architecture** Okay, so this change stems from a call within `kvm_arch_vcpu_run_pid_change`. That narrows the focus considerably. I need to zero in on these symbols: `kvm_handle_vncr_abort`, `kvm_handle_s1e2_tlbi`, `kvm_nested_s2_wp`, `kvm_nested_s2_unmap`, `compute_tlb_inval_range`, and `kvm_vcpu_allocate_vncr_tlb`. That's the likely hot zone. Hold on... I noticed something. `kvm_nested_s2_unmap` itself wasn't directly touched. But, `kvm_invalidate_vncr_ipa`, which it calls, *was* modified. That changes things a bit. I should really confirm the interaction with `kvm_nested_s2_unmap` itself. Gotta make sure I'm not missing any subtle impacts of that indirect change. |
| 30/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_nested_s2_unmap",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/mmu.c=1104=void stage2_unmap_vm(struct kvm *kvm)\n--\narch/arm64/kvm/mmu.c-1117-\narch/arm64/kvm/mmu.c:1118:\tkvm_nested_s2_unmap(kvm, true);\narch/arm64/kvm/mmu.c-1119-\n--\narch/arm64/kvm/mmu.c=2434=bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)\n--\narch/arm64/kvm/mmu.c-2442-\narch/arm64/kvm/mmu.c:2443:\tkvm_nested_s2_unmap(kvm, range-\u003emay_block);\narch/arm64/kvm/mmu.c-2444-\treturn false;\n--\narch/arm64/kvm/mmu.c=2717=void kvm_arch_flush_shadow_memslot(struct kvm *kvm,\n--\narch/arm64/kvm/mmu.c-2724-\tkvm_stage2_unmap_range(\u0026kvm-\u003earch.mmu, gpa, size, true);\narch/arm64/kvm/mmu.c:2725:\tkvm_nested_s2_unmap(kvm, true);\narch/arm64/kvm/mmu.c-2726-\twrite_unlock(\u0026kvm-\u003emmu_lock);\n--\narch/arm64/kvm/nested.c=1229=void kvm_nested_s2_wp(struct kvm *kvm)\n--\narch/arm64/kvm/nested.c-1247-\narch/arm64/kvm/nested.c:1248:void kvm_nested_s2_unmap(struct kvm *kvm, bool may_block)\narch/arm64/kvm/nested.c-1249-{\n--\narch/arm64/kvm/sys_regs.c=3938=static bool handle_alle1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n--\narch/arm64/kvm/sys_regs.c-3951-\t */\narch/arm64/kvm/sys_regs.c:3952:\tkvm_nested_s2_unmap(vcpu-\u003ekvm, true);\narch/arm64/kvm/sys_regs.c-3953-\n"
} |
| 31/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16720 output: 35 thoughts: 27 Thoughts: Here's my take on the thought process, as if it were my own: **Navigating the KVM Initialization Landscape** Alright, so the call originated from the Memory Management Unit (MMU) and system registers, which is good context. Now, I need to understand where this fits into the broader picture of KVM initialization. My immediate thought is to zero in on `kvm_init_nested`. That function is crucial for nested virtualization, and it's likely where the pieces connecting the MMU and system register interactions come together in this context. I'll need to confirm if my intuition is correct and drill down into `kvm_init_nested` to see how it handles this specific call path. I'll also check if I have encountered this call previously, and refresh my memory on any tricky parts of the code. This will clarify the relationship between the MMU, the system registers, and the nested virtualization setup within KVM. |
| 32/2 | 2026/08/02 13:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_init_nested",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/arm.c=222=int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n--\narch/arm64/kvm/arm.c-238-\narch/arm64/kvm/arm.c:239:\tkvm_init_nested(kvm);\narch/arm64/kvm/arm.c-240-\n--\narch/arm64/kvm/mmu.c=981=int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type)\n--\narch/arm64/kvm/mmu.c-1036-\tif (kvm_is_nested_s2_mmu(kvm, mmu))\narch/arm64/kvm/mmu.c:1037:\t\tkvm_init_nested_s2_mmu(mmu);\narch/arm64/kvm/mmu.c-1038-\n--\narch/arm64/kvm/mmu.c=1125=void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)\n--\narch/arm64/kvm/mmu.c-1138-\tif (kvm_is_nested_s2_mmu(kvm, mmu))\narch/arm64/kvm/mmu.c:1139:\t\tkvm_init_nested_s2_mmu(mmu);\narch/arm64/kvm/mmu.c-1140-\n--\narch/arm64/kvm/nested.c=20=struct vncr_tlb {\n--\narch/arm64/kvm/nested.c-46-\narch/arm64/kvm/nested.c:47:void kvm_init_nested(struct kvm *kvm)\narch/arm64/kvm/nested.c-48-{\n--\narch/arm64/kvm/nested.c=805=static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-869-\narch/arm64/kvm/nested.c:870:void kvm_init_nested_s2_mmu(struct kvm_s2_mmu *mmu)\narch/arm64/kvm/nested.c-871-{\n"
} |
| 33/2 | 2026/08/02 13:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17632 output: 181 thoughts: 740 Thoughts: **My Analysis of This KVM ARM64 Patch** Okay, let's break this down. From what I'm seeing, `kvm_init_nested` is the initialization function called from `kvm_arch_init_vm`. It's basically the setup function for this specific nested virtualization context. My initial focus here, given the function names, is around how Virtual Nested Control Registers (VNCR) and TLB invalidation are handled. I've identified the key symbols this patch seems to touch: `kvm_handle_vncr_abort`, `kvm_handle_s1e2_tlbi`, `kvm_nested_s2_wp`, `kvm_nested_s2_unmap`, `compute_tlb_inval_range`, and `kvm_vcpu_allocate_vncr_tlb`. I'm going to quickly confirm these symbol names with the definitions to be absolutely sure. Yep, they check out. Alright, so the patch is modifying `arch/arm64/kvm/nested.c` and `arch/arm64/kvm/hyp/vhe/switch.c`. Clearly, this is core KVM ARM64 functionality, specifically related to nested virtualization. I'm noting that `CONFIG_KVM` is the encompassing config option. The key takeaway is that this patch is changing fundamental aspects of VNCR TLB handling and the computation/application of TLB invalidation ranges within the KVM ARM64 nested virtualization environment. This has the potential to introduce subtle, but critical, bugs. So, I need to flag this for fuzzing. These are reachable, kernel paths that need rigorous testing to prevent regressions or unexpected behavior. Therefore, I'm marking this for fuzzing, using `KVM` as the config. My analysis identifies the following symbols: - `kvm_handle_vncr_abort` - `kvm_handle_s1e2_tlbi` - `kvm_nested_s2_wp` - `kvm_nested_s2_unmap` - `compute_tlb_inval_range` - `kvm_vcpu_allocate_vncr_tlb` and I'll use the `set-results` tool to relay these findings. |
| 34/2 | 2026/08/02 13:01 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_handle_vncr_abort",
"kvm_handle_s1e2_tlbi",
"kvm_nested_s2_wp",
"kvm_nested_s2_unmap",
"compute_tlb_inval_range",
"kvm_vcpu_allocate_vncr_tlb"
],
"Reasoning": "The patch modifies core KVM ARM64 nested virtualization logic, specifically around VNCR (Virtual Nested Control Register) TLB handling and invalidation. It introduces new tracking mechanisms and modifies how TLB invalidation ranges are computed and applied. These are reachable core kernel paths that should be fuzzed to ensure correctness and prevent regressions.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_handle_vncr_abort",
"kvm_handle_s1e2_tlbi",
"kvm_nested_s2_wp",
"kvm_nested_s2_unmap",
"compute_tlb_inval_range",
"kvm_vcpu_allocate_vncr_tlb"
],
"Reasoning": "The patch modifies core KVM ARM64 nested virtualization logic, specifically around VNCR (Virtual Nested Control Register) TLB handling and invalidation. It introduces new tracking mechanisms and modifies how TLB invalidation ranges are computed and applied. These are reachable core kernel paths that should be fuzzed to ensure correctness and prevent regressions.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|