From: Marc Zyngier <maz@kernel.org>
To: Wei-Lin Chang <weilin.chang@arm.com>
Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
linux-kernel@vger.kernel.org, Oliver Upton <oupton@kernel.org>,
Joey Gouly <joey.gouly@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>
Subject: Re: [PATCH 1/2] KVM: arm64: Factor out TG0/1 decoding of VTCR and TCR
Date: Tue, 07 Apr 2026 08:17:29 +0100 [thread overview]
Message-ID: <87y0izb5o6.wl-maz@kernel.org> (raw)
In-Reply-To: <20260406164618.3312473-2-weilin.chang@arm.com>
On Mon, 06 Apr 2026 17:46:17 +0100,
Wei-Lin Chang <weilin.chang@arm.com> wrote:
>
> The current code decodes TCR.TG0/TG1 and VTCR.TG0 inline at several
> places. Extract this logic into helpers so the granule size is derived
> in one place. This enables us to alter the effective granule size in
> the same place, which we will need in a later patch.
>
> Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
> ---
> arch/arm64/kvm/at.c | 73 +++++++++++++++++++++++++----------------
> arch/arm64/kvm/nested.c | 70 ++++++++++++++++++++++++---------------
> 2 files changed, 89 insertions(+), 54 deletions(-)
>
> diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
> index c5c5644b1878..ff8ba30e917b 100644
> --- a/arch/arm64/kvm/at.c
> +++ b/arch/arm64/kvm/at.c
> @@ -135,14 +135,54 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, struct s1_walk_info *wi)
> wi->e0poe = (wi->regime != TR_EL2) && (val & TCR2_EL1_E0POE);
> }
>
> +static unsigned int tg0_to_shift(u64 tg0)
> +{
It'd be better to abstract these helpers a bit more by making them
take the full TCR_ELx value, and to give them a slightly better name.
I'd suggest something like:
static unsigned int tcr_to_tg0_pgshift(u64 tcr)
{
u64 tg0 = tcr & TCR_TG0_MASK, tcr;
which makes it clear that the result is a page shift, as required by
wi->pgshift.
> + switch (tg0) {
> + case TCR_EL1_TG0_4K:
> + return 12;
> + case TCR_EL1_TG0_16K:
> + return 14;
> + case TCR_EL1_TG0_64K:
Please don't mix the _EL1 definition and those without _EL1 in the
same file. For a start, that's not always EL1. Also, this makes very
hard to reason about what is shifted and what is not.
> + default: /* IMPDEF: treat any other value as 64k */
> + return 16;
> + }
> +}
> +
> +static unsigned int tg1_to_shift(u64 tg1)
> +{
> + switch (tg1) {
> + case TCR_EL1_TG1_4K:
> + return 12;
> + case TCR_EL1_TG1_16K:
> + return 14;
> + case TCR_EL1_TG1_64K:
> + default: /* IMPDEF: treat any other value as 64k */
> + return 16;
> + }
> +}
> +
> +static u64 tcr_tg_shift(struct kvm *kvm, u64 tcr, bool upper_range)
> +{
> + unsigned int shift;
> +
> + /* Someone was silly enough to encode TG0/TG1 differently */
> + if (upper_range)
> + shift = tg1_to_shift(FIELD_GET(TCR_EL1_TG1_MASK, tcr));
> + else
> + shift = tg0_to_shift(FIELD_GET(TCR_EL1_TG0_MASK, 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;
>
> va55 = va & BIT(55);
> + upper_range = va55 && wi->regime != TR_EL2;
>
> if (vcpu_has_nv(vcpu)) {
> hcr = __vcpu_sys_reg(vcpu, HCR_EL2);
> @@ -173,35 +213,12 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
> BUG();
> }
>
> - /* Someone was silly enough to encode TG0/TG1 differently */
> - if (va55 && wi->regime != TR_EL2) {
> + if (upper_range)
> wi->txsz = FIELD_GET(TCR_T1SZ_MASK, tcr);
> - tg = FIELD_GET(TCR_TG1_MASK, tcr);
> -
> - switch (tg << TCR_TG1_SHIFT) {
> - case TCR_TG1_4K:
> - wi->pgshift = 12; break;
> - case TCR_TG1_16K:
> - wi->pgshift = 14; break;
> - case TCR_TG1_64K:
> - default: /* IMPDEF: treat any other value as 64k */
> - wi->pgshift = 16; break;
> - }
> - } else {
> + else
> wi->txsz = FIELD_GET(TCR_T0SZ_MASK, tcr);
> - tg = FIELD_GET(TCR_TG0_MASK, tcr);
> -
> - switch (tg << TCR_TG0_SHIFT) {
> - case TCR_TG0_4K:
> - wi->pgshift = 12; break;
> - case TCR_TG0_16K:
> - wi->pgshift = 14; break;
> - case TCR_TG0_64K:
> - default: /* IMPDEF: treat any other value as 64k */
> - wi->pgshift = 16; break;
> - }
> - }
>
> + wi->pgshift = tcr_tg_shift(vcpu->kvm, tcr, upper_range);
> wi->pa52bit = has_52bit_pa(vcpu, wi, tcr);
>
> ia_bits = get_ia_size(wi);
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index 883b6c1008fb..2bfab3007cb3 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -378,20 +378,36 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,
> return 0;
> }
>
> -static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi)
> +static unsigned int tg0_to_shift(u64 tg0)
Same comments as above.
> +{
> + switch (tg0) {
> + case VTCR_EL2_TG0_4K:
> + return 12;
> + case VTCR_EL2_TG0_16K:
> + return 14;
> + case VTCR_EL2_TG0_64K:
> + default: /* IMPDEF: treat any other value as 64k */
> + return 16;
> + }
> +}
> +
> +static u64 vtcr_tg0_shift(struct kvm *kvm, u64 vtcr)
> +{
> + u64 tg0 = FIELD_GET(VTCR_EL2_TG0_MASK, vtcr);
> + unsigned int shift = tg0_to_shift(tg0);
> +
> + return shift;
shift is an unsigned int. Why is the return value a u64? Try and make
sure that types are consistent, even if they cast nicely in C.
> +}
> +
> +static size_t vtcr_tg0_size(struct kvm *kvm, u64 vtcr)
> +{
> + return BIT(vtcr_tg0_shift(kvm, vtcr));
> +}
> +
> +static void vtcr_to_walk_info(struct kvm *kvm, u64 vtcr, struct s2_walk_info *wi)
This prototype reads bizarrely. vtcr is per CPU, the walk info is
evidently per CPU, and yet you pass a kvm struct.
Instead, rename this to:
static void setup_s2_walk(struct kvm_vcpu *vcpu,
struct s2_walk_info *wi)
{
u64 vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2);
and call that directly. You can then extract vcpu->kvm as needed. It
also aligns the naming on the s1 part, which isn't a bad thing to do.
> {
> wi->t0sz = vtcr & TCR_EL2_T0SZ_MASK;
> -
> - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
> - case VTCR_EL2_TG0_4K:
> - wi->pgshift = 12; break;
> - case VTCR_EL2_TG0_16K:
> - wi->pgshift = 14; break;
> - case VTCR_EL2_TG0_64K:
> - default: /* IMPDEF: treat any other value as 64k */
> - wi->pgshift = 16; break;
> - }
> -
> + wi->pgshift = vtcr_tg0_shift(kvm, vtcr);
> wi->sl = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr);
> /* Global limit for now, should eventually be per-VM */
> wi->max_oa_bits = min(get_kvm_ipa_limit(),
> @@ -414,7 +430,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,
>
> wi.baddr = vcpu_read_sys_reg(vcpu, VTTBR_EL2);
>
> - vtcr_to_walk_info(vtcr, &wi);
> + vtcr_to_walk_info(vcpu->kvm, vtcr, &wi);
>
> wi.be = vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE;
>
> @@ -515,17 +531,19 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)
> u64 tmp, sz = 0, vtcr = mmu->tlb_vtcr;
> kvm_pte_t pte;
> u8 ttl, level;
> + struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
> + size_t tg0_size = vtcr_tg0_size(kvm, vtcr);
>
> - lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock);
> + lockdep_assert_held_write(&kvm->mmu_lock);
>
> - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
> - case VTCR_EL2_TG0_4K:
> + switch (tg0_size) {
> + case SZ_4K:
> ttl = (TLBI_TTL_TG_4K << 2);
> break;
> - case VTCR_EL2_TG0_16K:
> + case SZ_16K:
> ttl = (TLBI_TTL_TG_16K << 2);
> break;
> - case VTCR_EL2_TG0_64K:
> + case SZ_64K:
All these unit changes make the patch harder to read than it should
be. Consider having a separate patch to do that.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
next prev parent reply other threads:[~2026-04-07 7:17 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-06 16:46 [PATCH 0/2] KVM: arm64: Handle unsupported guest translation granules Wei-Lin Chang
2026-04-06 16:46 ` [PATCH 1/2] KVM: arm64: Factor out TG0/1 decoding of VTCR and TCR Wei-Lin Chang
2026-04-07 7:17 ` Marc Zyngier [this message]
2026-04-09 12:54 ` Wei-Lin Chang
2026-04-06 16:46 ` [PATCH 2/2] KVM: arm64: Fallback to a supported value for unsupported guest TGx Wei-Lin Chang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87y0izb5o6.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=oupton@kernel.org \
--cc=suzuki.poulose@arm.com \
--cc=weilin.chang@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.