Re: [PATCH v2 4/8] KVM: arm64: Correctly handle end of VA space TLBI invalidation
flat view
From: Marc Zyngier <maz@kernel.org>
Date: 2026-08-09 18:13:48
Also in:
kvm, kvmarm, stable
Subsystem:
arm64 port (aarch64 architecture), kernel virtual machine for arm64 (kvm/arm64), the rest · Maintainers:
Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton, Linus Torvalds
On Sat, 08 Aug 2026 22:41:31 +0100, Wei-Lin Chang [off-list ref] wrote:
Hi Marc, On Thu, Aug 06, 2026 at 10:10:22AM +0100, Marc Zyngier wrote:quoted
Our TLB invalidation by VA code is based on comparing two ranges, one defined by the TLB, and one defined by the TLBI instruction. Each range is defined by a start and a size. However, the way the comparison is done doesn't account for address rollover, as it compares an address with (base + size). This works nicely until this expression represent the last page/block in the TTBR1 VA space, as the result is a big fat 0. And a failed TLB invalidation. Rewrite the comparison in a way that is immune to the address rollover (making the end address inclusive instead of exclusive), and move this into a common helper that is used by both VA and IPA invalidations, as suggested by Hyunwoo Kim (although the IPA version didn't suffer from this particular problem, obviously). Fixes: 4ffa72ad8f37e ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2") Reviewed-by: Yuan Yao <redacted> Signed-off-by: Marc Zyngier <maz@kernel.org> Cc: stable@vger.kernel.org --- arch/arm64/kvm/nested.c | 43 ++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 24 deletions(-)diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 27bc7ee4b3382..8a602d074dbb4 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c@@ -999,6 +999,20 @@ static void invalidate_vncr(struct vncr_tlb *vt) clear_fixmap(vncr_fixmap(vt->cpu)); } +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;I think if scope_end overflows,quoted
+ + return !(tlb_end < scope_start || tlb_start > scope_end);tlb_start > scope_end can evaluate to true and we return false even when there is overlap near the end of the TTBR1 address space.
I assume that you are implicitly talking about TLB Range Invalidation, right? Because we otherwise align base on size, making overflows impossible with this patch.
Therefore I think we need to saturate scope_end to ULONG_MAX when overflow happens. This is also what the architecture does when looking at the pseudocode J1.2.3.442 TLBIRange (M.c).
That's one possible implementation, because the pseudocode is dealing with start/end directly, but that's not how our scope works (it deals with start and size). For that we need to cap the *size*, similarly to what is done in handle_ripas2e1is() (see patch #6). Something like the hack below. M.
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index 21d0f4cbe07f1..5b8edb2e8a87d 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h@@ -305,6 +305,12 @@ static inline u64 decode_range_tlbi(u64 val, u64 *range, u16 *asid) num = FIELD_GET(GENMASK(43, 39), val); *range = __TLBI_RANGE_PAGES(num, scale) << shift; + /* Cap the range to the correct half of the address space */ + if (!(base & BIT(48))) + *range = min(*range, (BIT(48) - base)); + else + *range = min(*range, ~base + 1); + return base; }
--
Without deviation from the norm, progress is not possible.