From nobody Mon Jun 15 21:44:11 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3B044E54B for ; Tue, 14 Apr 2026 00:04:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125044; cv=none; b=ofp6eVFJ6vqv3jRAZ9LvYS8lN4GiQQ0ajESdbKb8goYqzwAIxxYepI56WTj6HLtWwtutsIh8wsYvo+Cfx+dt08bB/sLFgfJJIzkBkJnagNsF/gJRl5UoaNlZCsYaygOVU3baKJQurGMd6b45pQeYTPcTtYOjojkVh0Vm1nUKDtw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125044; c=relaxed/simple; bh=sNkeD3oItSsUYpcwRXopPrAGHGTV7rUHC2RBworXx6M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kWlOI6fzqI2eFRh2mlhkpn4+Vk68VnsG9v/5Chsx1vq3Ki86n8GnYje52D8c0hpbCeUWPdWfJ/eLkpcPICtxQFf7sD12xMLI/0oVeeYf+MKHKQnEI5cQq74RBRxibIVYcQSuqBxLDXkelCXDwrwA5ZURA9hRxyxY9243ts56jcQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=qzTpUOMt; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="qzTpUOMt" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 02A3E4E28; Mon, 13 Apr 2026 17:03:56 -0700 (PDT) Received: from workstation-e142269.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 07E4E3F7B4; Mon, 13 Apr 2026 17:03:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776125041; bh=sNkeD3oItSsUYpcwRXopPrAGHGTV7rUHC2RBworXx6M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qzTpUOMtt4Daa1iCNSkoG6aW71GBI52JmOBJuML2B5kaxm32WfnMgBniZyQU6tfLy wGJiXEDWZDjfG3XFWBAbi3+oo7PfuScZCQrVlsKy+nb33CUJI2CryYbapSVVRTBccG KKzE9uO+KkTyzyLE+t3FvSci7YdovJoyMiHr4MXI= From: Wei-Lin Chang To: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org Cc: Marc Zyngier , Oliver Upton , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon , Wei-Lin Chang Subject: [PATCH v2 1/4] KVM: arm64: nv: Rename vtcr_to_walk_info() to setup_s2_walk() Date: Tue, 14 Apr 2026 01:03:31 +0100 Message-ID: <20260414000334.3947257-2-weilin.chang@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414000334.3947257-1-weilin.chang@arm.com> References: <20260414000334.3947257-1-weilin.chang@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This rename aligns the stage-2 walker better with the stage-1 walker. Also set up other non-VTCR walk info in the function. Signed-off-by: Wei-Lin Chang --- arch/arm64/kvm/nested.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 2c43097248b2..f20402d0d7e5 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -378,9 +378,12 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, p= hys_addr_t ipa, return 0; } =20 -static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi) +static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) { - wi->t0sz =3D vtcr & TCR_EL2_T0SZ_MASK; + u64 vtcr =3D vcpu_read_sys_reg(vcpu, VTCR_EL2); + + wi->baddr =3D vcpu_read_sys_reg(vcpu, VTTBR_EL2); + wi->t0sz =3D vtcr & VTCR_EL2_T0SZ_MASK; =20 switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { case VTCR_EL2_TG0_4K: @@ -398,12 +401,12 @@ static void vtcr_to_walk_info(u64 vtcr, struct s2_wal= k_info *wi) ps_to_output_size(FIELD_GET(VTCR_EL2_PS_MASK, vtcr), false)); =20 wi->ha =3D vtcr & VTCR_EL2_HA; + wi->be =3D vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE; } =20 int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa, struct kvm_s2_trans *result) { - u64 vtcr =3D vcpu_read_sys_reg(vcpu, VTCR_EL2); struct s2_walk_info wi; int ret; =20 @@ -412,11 +415,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_add= r_t gipa, if (!vcpu_has_nv(vcpu)) return 0; =20 - wi.baddr =3D vcpu_read_sys_reg(vcpu, VTTBR_EL2); - - vtcr_to_walk_info(vtcr, &wi); - - wi.be =3D vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE; + setup_s2_walk(vcpu, &wi); =20 ret =3D walk_nested_s2_pgd(vcpu, gipa, &wi, result); if (ret) --=20 2.43.0 From nobody Mon Jun 15 21:44:11 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3935B3D561 for ; Tue, 14 Apr 2026 00:04:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125046; cv=none; b=pbHSO5Bv2br9Whz6qQq7UjErMH0F1zuX4K3zvroISKqnSF1elRCBgZrv9OYDo3Qy+Pc+nSwmf+swilJI4WLbLlnVnVCTXIxerxB1tTpz0cVvPqd5P3Vo2ywQSdylMLcMSaOZ3eRKAMyLcNg5UXsE2V3JG4Si2fV86xLim44faAY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125046; c=relaxed/simple; bh=hEJOa8RFZZTEyMjtJNuutXiH+ANECuK/KTknvYqeWN8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=O8pOBmJLRHf8gmspLSxPUDm5mJoJSRs6WLt+n/ULHMI4LS45kWLiVzOSo9Ma/H1vdRg6WhJSt+8jaJjxf9CI/sWcs/J6Yg32wcrMj/KweDC3aIuqzcrpVLAgnyDbrVyEBCKRCd2Mhd9xM+HNSy1YYczL+SRiZ5SoWKbJAlzyWJg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=GCi8IVOd; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="GCi8IVOd" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E9D014E2A; Mon, 13 Apr 2026 17:03:57 -0700 (PDT) Received: from workstation-e142269.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id F23833F7B4; Mon, 13 Apr 2026 17:04:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776125043; bh=hEJOa8RFZZTEyMjtJNuutXiH+ANECuK/KTknvYqeWN8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GCi8IVOd+MwfFd/o/V/cELURlvjgld91kDSixTrCEwUMLTtg5tVi0qidqNx+do0w2 cGYBki1EhhyQsz4Y1iuFEsIRz9BQVyednOaawsi92ZWYekkFQtR0ydcADA6yMTZnBY 0OZ0RbPyZijMMUkwgBDlQDYwZqOrKMg+VWqbLWAE= From: Wei-Lin Chang To: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org Cc: Marc Zyngier , Oliver Upton , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon , Wei-Lin Chang Subject: [PATCH v2 2/4] KVM: arm64: Factor out TG0/1 decoding of VTCR and TCR Date: Tue, 14 Apr 2026 01:03:32 +0100 Message-ID: <20260414000334.3947257-3-weilin.chang@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414000334.3947257-1-weilin.chang@arm.com> References: <20260414000334.3947257-1-weilin.chang@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The current code decodes TCR.TG0/TG1 and VTCR.TG0 inline at several places. Extract this logic into helpers so the granule size can be derived in one place. This enables us to alter the effective granule size in the same place, which we will do in a later patch. Signed-off-by: Wei-Lin Chang --- arch/arm64/kvm/at.c | 77 ++++++++++++++++++++++++++--------------- arch/arm64/kvm/nested.c | 27 +++++++++------ 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index a024d9a770dc..927226266081 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -135,14 +135,58 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, stru= ct s1_walk_info *wi) wi->e0poe =3D (wi->regime !=3D TR_EL2) && (val & TCR2_EL1_E0POE); } =20 +static unsigned int tcr_to_tg0_pgshift(u64 tcr) +{ + u64 tg0 =3D tcr & TCR_TG0_MASK; + + switch (tg0) { + case TCR_TG0_4K: + return 12; + case TCR_TG0_16K: + return 14; + case TCR_TG0_64K: + default: /* IMPDEF: treat any other value as 64k */ + return 16; + } +} + +static unsigned int tcr_to_tg1_pgshift(u64 tcr) +{ + u64 tg1 =3D tcr & TCR_TG1_MASK; + + switch (tg1) { + case TCR_TG1_4K: + return 12; + case TCR_TG1_16K: + return 14; + case TCR_TG1_64K: + default: /* IMPDEF: treat any other value as 64k */ + return 16; + } +} + +static unsigned int tcr_tg_pgshift(u64 tcr, bool upper_range) +{ + unsigned int shift; + + /* Someone was silly enough to encode TG0/TG1 differently */ + if (upper_range) + shift =3D tcr_to_tg1_pgshift(tcr); + else + shift =3D tcr_to_tg0_pgshift(tcr); + + return shift; +} + static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va) { - u64 hcr, sctlr, tcr, tg, ps, ia_bits, ttbr; + u64 hcr, sctlr, tcr, ps, ia_bits, ttbr; unsigned int stride, x; - bool va55, tbi, lva; + bool va55, tbi, lva, upper_range; =20 va55 =3D va & BIT(55); + upper_range =3D va55 && wi->regime !=3D TR_EL2; =20 if (vcpu_has_nv(vcpu)) { hcr =3D __vcpu_sys_reg(vcpu, HCR_EL2); @@ -173,35 +217,12 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struc= t s1_walk_info *wi, BUG(); } =20 - /* Someone was silly enough to encode TG0/TG1 differently */ - if (va55 && wi->regime !=3D TR_EL2) { + if (upper_range) wi->txsz =3D FIELD_GET(TCR_T1SZ_MASK, tcr); - tg =3D FIELD_GET(TCR_TG1_MASK, tcr); - - switch (tg << TCR_TG1_SHIFT) { - case TCR_TG1_4K: - wi->pgshift =3D 12; break; - case TCR_TG1_16K: - wi->pgshift =3D 14; break; - case TCR_TG1_64K: - default: /* IMPDEF: treat any other value as 64k */ - wi->pgshift =3D 16; break; - } - } else { + else wi->txsz =3D FIELD_GET(TCR_T0SZ_MASK, tcr); - tg =3D FIELD_GET(TCR_TG0_MASK, tcr); - - switch (tg << TCR_TG0_SHIFT) { - case TCR_TG0_4K: - wi->pgshift =3D 12; break; - case TCR_TG0_16K: - wi->pgshift =3D 14; break; - case TCR_TG0_64K: - default: /* IMPDEF: treat any other value as 64k */ - wi->pgshift =3D 16; break; - } - } =20 + wi->pgshift =3D tcr_tg_pgshift(tcr, upper_range); wi->pa52bit =3D has_52bit_pa(vcpu, wi, tcr); =20 ia_bits =3D get_ia_size(wi); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index f20402d0d7e5..40d52e9100d6 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -378,28 +378,33 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, = phys_addr_t ipa, return 0; } =20 -static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) -{ - u64 vtcr =3D vcpu_read_sys_reg(vcpu, VTCR_EL2); =20 - wi->baddr =3D vcpu_read_sys_reg(vcpu, VTTBR_EL2); - wi->t0sz =3D vtcr & VTCR_EL2_T0SZ_MASK; +static unsigned int vtcr_to_tg0_pgshift(u64 vtcr) +{ + u64 tg0 =3D FIELD_GET(VTCR_EL2_TG0_MASK, vtcr); =20 - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { + switch (tg0) { case VTCR_EL2_TG0_4K: - wi->pgshift =3D 12; break; + return 12; case VTCR_EL2_TG0_16K: - wi->pgshift =3D 14; break; + return 14; case VTCR_EL2_TG0_64K: - default: /* IMPDEF: treat any other value as 64k */ - wi->pgshift =3D 16; break; + default: /* IMPDEF: treat any other value as 64k */ + return 16; } +} + +static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) +{ + u64 vtcr =3D vcpu_read_sys_reg(vcpu, VTCR_EL2); =20 + wi->baddr =3D vcpu_read_sys_reg(vcpu, VTTBR_EL2); + wi->t0sz =3D vtcr & VTCR_EL2_T0SZ_MASK; + wi->pgshift =3D vtcr_to_tg0_pgshift(vtcr); wi->sl =3D FIELD_GET(VTCR_EL2_SL0_MASK, vtcr); /* Global limit for now, should eventually be per-VM */ wi->max_oa_bits =3D min(get_kvm_ipa_limit(), ps_to_output_size(FIELD_GET(VTCR_EL2_PS_MASK, vtcr), false)); - wi->ha =3D vtcr & VTCR_EL2_HA; wi->be =3D vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE; } --=20 2.43.0 From nobody Mon Jun 15 21:44:11 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 306DC14A8E for ; Tue, 14 Apr 2026 00:04:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125047; cv=none; b=C7q0zvybLKMS1RgF9KdVozFspafAK2k+WDWebQGleNJqstF/gexPzlXlxnsYwccV2UZ5/RXZ0YB47ih28yKqsDXRUfbW7J11lbEHdJBt5Us+LT/zvf0zcahD6+eWf8aOXAkTkYPhi+cFa6T+2nWBtOkwCFghxvmCSiSI5zNMWBc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125047; c=relaxed/simple; bh=rTwPoMoBGibDvIeg7vnDDP5LMIeeRhi6mWL8JuxGNWA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CiKKWYM1r7aCvdnJ5KCLBxTjGjJrOtCofTsS85dqxIPlK5/PhjwpGmM0BxrK5j2DHRnoJr5PCiDuzmXiiqKk6KC10+UVbH/y9dTLV/KqAeybMKi5Qv0wchbbiEkBrYFj62JEU/g9E5h+355zMhshCTXR9JJnJ6hfSToJY/5hL9A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=YT8o3hsS; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="YT8o3hsS" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E24294E2C; Mon, 13 Apr 2026 17:03:59 -0700 (PDT) Received: from workstation-e142269.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id E68863F7B4; Mon, 13 Apr 2026 17:04:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776125045; bh=rTwPoMoBGibDvIeg7vnDDP5LMIeeRhi6mWL8JuxGNWA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YT8o3hsSFlzjqWp2lvLm6XNX4RQqcecARDcEvZVudE1XIYx4yVik1u2x4Jr5H5wGB sE+q4RhQMznFJMtcxA0nslEDSM/L8r7Jg9NWYFaiGq64Zl5QvExJHolSg7gAONPQR9 Wmcaim/glfLIoRfnroudgdJyEOauW6OlhnvRFwlw= From: Wei-Lin Chang To: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org Cc: Marc Zyngier , Oliver Upton , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon , Wei-Lin Chang Subject: [PATCH v2 3/4] KVM: arm64: nv: Use literal granule size in TLBI range calculation Date: Tue, 14 Apr 2026 01:03:33 +0100 Message-ID: <20260414000334.3947257-4-weilin.chang@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414000334.3947257-1-weilin.chang@arm.com> References: <20260414000334.3947257-1-weilin.chang@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" TLBI handling derives the invalidation range from guest VTCR_EL2.TG0 in get_guest_mapping_ttl() and compute_tlb_inval_range(). Switch these to use a helper that returns the decoded VTCR_EL2.TG0 granule size instead of decoding it inline. This keeps the granule size derivation in one place and prepares for following changes that adjust the effective granule size. Signed-off-by: Wei-Lin Chang --- arch/arm64/kvm/nested.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 40d52e9100d6..a732d7b0bd5d 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -394,6 +394,11 @@ static unsigned int vtcr_to_tg0_pgshift(u64 vtcr) } } =20 +static size_t vtcr_to_tg0_pgsize(u64 vtcr) +{ + return BIT(vtcr_to_tg0_pgshift(vtcr)); +} + static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) { u64 vtcr =3D vcpu_read_sys_reg(vcpu, VTCR_EL2); @@ -516,20 +521,21 @@ static u8 pgshift_level_to_ttl(u16 shift, u8 level) */ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr) { - u64 tmp, sz =3D 0, vtcr =3D mmu->tlb_vtcr; + u64 tmp, sz =3D 0; kvm_pte_t pte; u8 ttl, level; + size_t tg0_size =3D vtcr_to_tg0_pgsize(mmu->tlb_vtcr); =20 lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock); =20 - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { - case VTCR_EL2_TG0_4K: + switch (tg0_size) { + case SZ_4K: ttl =3D (TLBI_TTL_TG_4K << 2); break; - case VTCR_EL2_TG0_16K: + case SZ_16K: ttl =3D (TLBI_TTL_TG_16K << 2); break; - case VTCR_EL2_TG0_64K: + case SZ_64K: default: /* IMPDEF: treat any other value as 64k */ ttl =3D (TLBI_TTL_TG_64K << 2); break; @@ -539,19 +545,19 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mm= u, u64 addr) =20 again: /* Iteratively compute the block sizes for a particular granule size */ - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { - case VTCR_EL2_TG0_4K: + switch (tg0_size) { + case SZ_4K: if (sz < SZ_4K) sz =3D SZ_4K; else if (sz < SZ_2M) sz =3D SZ_2M; else if (sz < SZ_1G) sz =3D SZ_1G; else sz =3D 0; break; - case VTCR_EL2_TG0_16K: + case SZ_16K: if (sz < SZ_16K) sz =3D SZ_16K; else if (sz < SZ_32M) sz =3D SZ_32M; else sz =3D 0; break; - case VTCR_EL2_TG0_64K: + case SZ_64K: default: /* IMPDEF: treat any other value as 64k */ if (sz < SZ_64K) sz =3D SZ_64K; else if (sz < SZ_512M) sz =3D SZ_512M; @@ -602,14 +608,14 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_m= mu *mmu, u64 val) =20 if (!max_size) { /* Compute the maximum extent of the invalidation */ - switch (FIELD_GET(VTCR_EL2_TG0_MASK, mmu->tlb_vtcr)) { - case VTCR_EL2_TG0_4K: + switch (vtcr_to_tg0_pgsize(mmu->tlb_vtcr)) { + case SZ_4K: max_size =3D SZ_1G; break; - case VTCR_EL2_TG0_16K: + case SZ_16K: max_size =3D SZ_32M; break; - case VTCR_EL2_TG0_64K: + case SZ_64K: default: /* IMPDEF: treat any other value as 64k */ /* * No, we do not support 52bit IPA in nested yet. Once --=20 2.43.0 From nobody Mon Jun 15 21:44:11 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1D7F040DFAF for ; Tue, 14 Apr 2026 00:04:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125052; cv=none; b=oxtU84eZxYHdvKca6Gg+WzBwP18RBA7+2k5Ft+MH36jtt6QglK3jvUSFWfeWwshVjmpM8iwDu3mvTU8NInPCL4qz1Mc8br5ZtXvRCQexiopv6H9vqSjWO+BBLn21vL1A0VDaw84HJx2Pfa+iwH9aot1SDAJsPHc34kYRsPSZZRE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776125052; c=relaxed/simple; bh=tZ2tAOa25uPuSCYx8KzsvxrVL7umOXNLkyBbeZjsolY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Z0vKDOBtrgqiEIZhHxLyKItmWh6OzEWrq2jfPvTI5fqJtI6K2XZV0bswSSbpibDYoY4PhY5VEWI5cBWpOc9L4kxh5ac1PUmdHiYtdE5URDLIqDf1bO6KFZcj03bdgvrYJcxr67XbRNKnKZTnIVd+DueOAq77V5JTjdXpNCUbpJE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=CzH/Jwqr; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="CzH/Jwqr" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D495B4E26; Mon, 13 Apr 2026 17:04:01 -0700 (PDT) Received: from workstation-e142269.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id DC1D23F7B4; Mon, 13 Apr 2026 17:04:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776125047; bh=tZ2tAOa25uPuSCYx8KzsvxrVL7umOXNLkyBbeZjsolY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CzH/JwqrxJ4V08fqKMdmzq1KRDmQ1m1VsMU1a/HWqfnLm1O3fSUtC/h2z8hu9ZwCM E3jW6NuHMwU/T40+kSmaGu+7S4GYuG1dlaLWTsrfOkj8ybkS+c8rKPgWhlAWyrEwhl D6K4Qw4vh+tNDuSI95tD+Uv+kUT/OB+vZ3GUf6mw= From: Wei-Lin Chang To: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org Cc: Marc Zyngier , Oliver Upton , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon , Wei-Lin Chang Subject: [PATCH v2 4/4] KVM: arm64: Fallback to a supported value for unsupported guest TGx Date: Tue, 14 Apr 2026 01:03:34 +0100 Message-ID: <20260414000334.3947257-5-weilin.chang@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414000334.3947257-1-weilin.chang@arm.com> References: <20260414000334.3947257-1-weilin.chang@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When KVM derives the translation granule for emulated stage-1 and stage-2 walks, it decodes TCR/VTCR.TGx and treats the granule as-is. This is wrong when the guest programs a granule size that is not advertised in the guest's ID_AA64MMFR0_EL1.TGRAN* fields. Architecturally, such a value must be treated as an implemented granule size. Choose an available one while prioritizing PAGE_SIZE. Signed-off-by: Wei-Lin Chang --- arch/arm64/kvm/at.c | 52 +++++++++++++++++++++- arch/arm64/kvm/nested.c | 98 +++++++++++++++++++++++++++++------------ 2 files changed, 121 insertions(+), 29 deletions(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 927226266081..702ce531afd5 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -135,6 +135,30 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, struc= t s1_walk_info *wi) wi->e0poe =3D (wi->regime !=3D TR_EL2) && (val & TCR2_EL1_E0POE); } =20 +#define _has_tgran(__r, __sz) \ + ({ \ + u64 _s1, _mmfr0 =3D __r; \ + \ + _s1 =3D SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz, _mmfr0); \ + \ + _s1 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_NI; \ + }) + +static bool has_tgran(u64 mmfr0, unsigned int shift) +{ + switch (shift) { + case 12: + return _has_tgran(mmfr0, 4); + case 14: + return _has_tgran(mmfr0, 16); + case 16: + return _has_tgran(mmfr0, 64); + default: + BUG(); + } +} + static unsigned int tcr_to_tg0_pgshift(u64 tcr) { u64 tg0 =3D tcr & TCR_TG0_MASK; @@ -165,8 +189,23 @@ static unsigned int tcr_to_tg1_pgshift(u64 tcr) } } =20 -static unsigned int tcr_tg_pgshift(u64 tcr, bool upper_range) +static unsigned int fallback_tgran_shift(u64 mmfr0) +{ + if (has_tgran(mmfr0, PAGE_SHIFT)) + return PAGE_SHIFT; + else if (has_tgran(mmfr0, 12)) + return 12; + else if (has_tgran(mmfr0, 14)) + return 14; + else if (has_tgran(mmfr0, 16)) + return 16; + else + return PAGE_SHIFT; +} + +static unsigned int tcr_tg_pgshift(struct kvm *kvm, u64 tcr, bool upper_ra= nge) { + u64 mmfr0 =3D kvm_read_vm_id_reg(kvm, SYS_ID_AA64MMFR0_EL1); unsigned int shift; =20 /* Someone was silly enough to encode TG0/TG1 differently */ @@ -175,6 +214,15 @@ static unsigned int tcr_tg_pgshift(u64 tcr, bool upper= _range) else shift =3D tcr_to_tg0_pgshift(tcr); =20 + /* + * If TGx is programmed to an unimplemented value (not advertised in + * ID_AA64MMFR0_EL1), we should treat it as if an implemented value is + * written, as per the architecture. Choose an available one while + * prioritizing PAGE_SIZE. + */ + if (!has_tgran(mmfr0, shift)) + return fallback_tgran_shift(mmfr0); + return shift; } =20 @@ -222,7 +270,7 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct = s1_walk_info *wi, else wi->txsz =3D FIELD_GET(TCR_T0SZ_MASK, tcr); =20 - wi->pgshift =3D tcr_tg_pgshift(tcr, upper_range); + wi->pgshift =3D tcr_tg_pgshift(vcpu->kvm, tcr, upper_range); wi->pa52bit =3D has_52bit_pa(vcpu, wi, tcr); =20 ia_bits =3D get_ia_size(wi); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index a732d7b0bd5d..327a6aaa45db 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -378,25 +378,83 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, = phys_addr_t ipa, return 0; } =20 +#define _has_tgran_2(__r, __sz) \ + ({ \ + u64 _s1, _s2, _mmfr0 =3D __r; \ + \ + _s2 =3D SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz##_2, _mmfr0); \ + \ + _s1 =3D SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz, _mmfr0); \ + \ + ((_s2 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_2_NI && \ + _s2 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz) || \ + (_s2 =3D=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz && \ + _s1 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_NI)); \ + }) + +static bool has_tgran_2(u64 mmfr0, unsigned int shift) +{ + switch (shift) { + case 12: + return _has_tgran_2(mmfr0, 4); + case 14: + return _has_tgran_2(mmfr0, 16); + case 16: + return _has_tgran_2(mmfr0, 64); + default: + BUG(); + } +} + +static unsigned int fallback_tgran2_shift(u64 mmfr0) +{ + if (has_tgran_2(mmfr0, PAGE_SHIFT)) + return PAGE_SHIFT; + else if (has_tgran_2(mmfr0, 12)) + return 12; + else if (has_tgran_2(mmfr0, 14)) + return 14; + else if (has_tgran_2(mmfr0, 16)) + return 16; + else + return PAGE_SHIFT; +} =20 -static unsigned int vtcr_to_tg0_pgshift(u64 vtcr) +static unsigned int vtcr_to_tg0_pgshift(struct kvm *kvm, u64 vtcr) { u64 tg0 =3D FIELD_GET(VTCR_EL2_TG0_MASK, vtcr); + u64 mmfr0 =3D kvm_read_vm_id_reg(kvm, SYS_ID_AA64MMFR0_EL1); + unsigned int shift; =20 switch (tg0) { case VTCR_EL2_TG0_4K: - return 12; + shift =3D 12; + break; case VTCR_EL2_TG0_16K: - return 14; + shift =3D 14; + break; case VTCR_EL2_TG0_64K: default: /* IMPDEF: treat any other value as 64k */ - return 16; + shift =3D 16; } + + /* + * If TGx is programmed to an unimplemented value (not advertised in + * ID_AA64MMFR0_EL1), we should treat it as if an implemented value is + * written, as per the architecture. Choose an available one while + * prioritizing PAGE_SIZE. + */ + if (!has_tgran_2(mmfr0, shift)) + return fallback_tgran2_shift(mmfr0); + + return shift; } =20 -static size_t vtcr_to_tg0_pgsize(u64 vtcr) +static size_t vtcr_to_tg0_pgsize(struct kvm *kvm, u64 vtcr) { - return BIT(vtcr_to_tg0_pgshift(vtcr)); + return BIT(vtcr_to_tg0_pgshift(kvm, vtcr)); } =20 static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) @@ -405,7 +463,7 @@ static void setup_s2_walk(struct kvm_vcpu *vcpu, struct= s2_walk_info *wi) =20 wi->baddr =3D vcpu_read_sys_reg(vcpu, VTTBR_EL2); wi->t0sz =3D vtcr & VTCR_EL2_T0SZ_MASK; - wi->pgshift =3D vtcr_to_tg0_pgshift(vtcr); + wi->pgshift =3D vtcr_to_tg0_pgshift(vcpu->kvm, vtcr); wi->sl =3D FIELD_GET(VTCR_EL2_SL0_MASK, vtcr); /* Global limit for now, should eventually be per-VM */ wi->max_oa_bits =3D min(get_kvm_ipa_limit(), @@ -524,7 +582,8 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu,= u64 addr) u64 tmp, sz =3D 0; kvm_pte_t pte; u8 ttl, level; - size_t tg0_size =3D vtcr_to_tg0_pgsize(mmu->tlb_vtcr); + struct kvm *kvm =3D kvm_s2_mmu_to_kvm(mmu); + size_t tg0_size =3D vtcr_to_tg0_pgsize(kvm, mmu->tlb_vtcr); =20 lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock); =20 @@ -608,7 +667,7 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu= *mmu, u64 val) =20 if (!max_size) { /* Compute the maximum extent of the invalidation */ - switch (vtcr_to_tg0_pgsize(mmu->tlb_vtcr)) { + switch (vtcr_to_tg0_pgsize(kvm, mmu->tlb_vtcr)) { case SZ_4K: max_size =3D SZ_1G; break; @@ -1504,21 +1563,6 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) } } =20 -#define has_tgran_2(__r, __sz) \ - ({ \ - u64 _s1, _s2, _mmfr0 =3D __r; \ - \ - _s2 =3D SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ - TGRAN##__sz##_2, _mmfr0); \ - \ - _s1 =3D SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ - TGRAN##__sz, _mmfr0); \ - \ - ((_s2 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_2_NI && \ - _s2 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz) || \ - (_s2 =3D=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz && \ - _s1 !=3D ID_AA64MMFR0_EL1_TGRAN##__sz##_NI)); \ - }) /* * Our emulated CPU doesn't support all the possible features. For the * sake of simplicity (and probably mental sanity), wipe out a number @@ -1600,15 +1644,15 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 v= al) */ switch (PAGE_SIZE) { case SZ_4K: - if (has_tgran_2(orig_val, 4)) + if (_has_tgran_2(orig_val, 4)) val |=3D SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN4_2, IMP); fallthrough; case SZ_16K: - if (has_tgran_2(orig_val, 16)) + if (_has_tgran_2(orig_val, 16)) val |=3D SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN16_2, IMP); fallthrough; case SZ_64K: - if (has_tgran_2(orig_val, 64)) + if (_has_tgran_2(orig_val, 64)) val |=3D SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN64_2, IMP); break; } --=20 2.43.0