[PATCH 2/4] arm64: Convert VTCR_EL2 to sysreg infratructure
Alexandru Elisei
alexandru.elisei at arm.com
Wed Dec 3 03:43:14 PST 2025
Hi Marc,
On Sat, Nov 29, 2025 at 02:45:23PM +0000, Marc Zyngier wrote:
> Our definition of VTCR_EL2 is both partial (tons of fields are
> missing) and totally inconsistent (some constants are shifted,
> some are not). They are also expressed in terms of TCR, which is
> rather inconvenient.
>
> Replace the ad-hoc definitions with the the generated version.
> This results in a bunch of additional changes to make the code
> with the unshifted nature of generated enumerations.
>
> The register data was extracted from the BSD licenced AARCHMRS
> (AARCHMRS_OPENSOURCE_A_profile_FAT-2025-09_ASL0).
Looks correct to me:
Reviewed-by: Alexandru Elisei <alexandru.elisei at arm.com>
Thanks,
Alex
>
> Signed-off-by: Marc Zyngier <maz at kernel.org>
> ---
> arch/arm64/include/asm/kvm_arm.h | 52 +++++++----------------------
> arch/arm64/include/asm/sysreg.h | 1 -
> arch/arm64/kvm/hyp/pgtable.c | 8 ++---
> arch/arm64/kvm/nested.c | 8 ++---
> arch/arm64/tools/sysreg | 57 ++++++++++++++++++++++++++++++++
> 5 files changed, 76 insertions(+), 50 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
> index 1da290aeedce7..cd2dc378baee6 100644
> --- a/arch/arm64/include/asm/kvm_arm.h
> +++ b/arch/arm64/include/asm/kvm_arm.h
> @@ -123,37 +123,7 @@
> #define TCR_EL2_MASK (TCR_EL2_TG0_MASK | TCR_EL2_SH0_MASK | \
> TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK)
>
> -/* VTCR_EL2 Registers bits */
> -#define VTCR_EL2_DS TCR_EL2_DS
> -#define VTCR_EL2_RES1 (1U << 31)
> -#define VTCR_EL2_HD (1 << 22)
> -#define VTCR_EL2_HA (1 << 21)
> -#define VTCR_EL2_PS_SHIFT TCR_EL2_PS_SHIFT
> -#define VTCR_EL2_PS_MASK TCR_EL2_PS_MASK
> -#define VTCR_EL2_TG0_MASK TCR_TG0_MASK
> -#define VTCR_EL2_TG0_4K TCR_TG0_4K
> -#define VTCR_EL2_TG0_16K TCR_TG0_16K
> -#define VTCR_EL2_TG0_64K TCR_TG0_64K
> -#define VTCR_EL2_SH0_MASK TCR_SH0_MASK
> -#define VTCR_EL2_SH0_INNER TCR_SH0_INNER
> -#define VTCR_EL2_ORGN0_MASK TCR_ORGN0_MASK
> -#define VTCR_EL2_ORGN0_WBWA TCR_ORGN0_WBWA
> -#define VTCR_EL2_IRGN0_MASK TCR_IRGN0_MASK
> -#define VTCR_EL2_IRGN0_WBWA TCR_IRGN0_WBWA
> -#define VTCR_EL2_SL0_SHIFT 6
> -#define VTCR_EL2_SL0_MASK (3 << VTCR_EL2_SL0_SHIFT)
> -#define VTCR_EL2_T0SZ_MASK 0x3f
> -#define VTCR_EL2_VS_SHIFT 19
> -#define VTCR_EL2_VS_8BIT (0 << VTCR_EL2_VS_SHIFT)
> -#define VTCR_EL2_VS_16BIT (1 << VTCR_EL2_VS_SHIFT)
> -
> -#define VTCR_EL2_T0SZ(x) TCR_T0SZ(x)
> -
> /*
> - * We configure the Stage-2 page tables to always restrict the IPA space to be
> - * 40 bits wide (T0SZ = 24). Systems with a PARange smaller than 40 bits are
> - * not known to exist and will break with this configuration.
> - *
> * The VTCR_EL2 is configured per VM and is initialised in kvm_init_stage2_mmu.
> *
> * Note that when using 4K pages, we concatenate two first level page tables
> @@ -161,9 +131,6 @@
> *
> */
>
> -#define VTCR_EL2_COMMON_BITS (VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \
> - VTCR_EL2_IRGN0_WBWA | VTCR_EL2_RES1)
> -
> /*
> * VTCR_EL2:SL0 indicates the entry level for Stage2 translation.
> * Interestingly, it depends on the page size.
> @@ -195,30 +162,35 @@
> */
> #ifdef CONFIG_ARM64_64K_PAGES
>
> -#define VTCR_EL2_TGRAN VTCR_EL2_TG0_64K
> +#define VTCR_EL2_TGRAN 64K
> #define VTCR_EL2_TGRAN_SL0_BASE 3UL
>
> #elif defined(CONFIG_ARM64_16K_PAGES)
>
> -#define VTCR_EL2_TGRAN VTCR_EL2_TG0_16K
> +#define VTCR_EL2_TGRAN 16K
> #define VTCR_EL2_TGRAN_SL0_BASE 3UL
>
> #else /* 4K */
>
> -#define VTCR_EL2_TGRAN VTCR_EL2_TG0_4K
> +#define VTCR_EL2_TGRAN 4K
> #define VTCR_EL2_TGRAN_SL0_BASE 2UL
>
> #endif
>
> #define VTCR_EL2_LVLS_TO_SL0(levels) \
> - ((VTCR_EL2_TGRAN_SL0_BASE - (4 - (levels))) << VTCR_EL2_SL0_SHIFT)
> + FIELD_PREP(VTCR_EL2_SL0, (VTCR_EL2_TGRAN_SL0_BASE - (4 - (levels))))
> #define VTCR_EL2_SL0_TO_LVLS(sl0) \
> ((sl0) + 4 - VTCR_EL2_TGRAN_SL0_BASE)
> #define VTCR_EL2_LVLS(vtcr) \
> - VTCR_EL2_SL0_TO_LVLS(((vtcr) & VTCR_EL2_SL0_MASK) >> VTCR_EL2_SL0_SHIFT)
> + VTCR_EL2_SL0_TO_LVLS(FIELD_GET(VTCR_EL2_SL0, (vtcr)))
> +
> +#define VTCR_EL2_FLAGS (SYS_FIELD_PREP_ENUM(VTCR_EL2, SH0, INNER) | \
> + SYS_FIELD_PREP_ENUM(VTCR_EL2, ORGN0, WBWA) | \
> + SYS_FIELD_PREP_ENUM(VTCR_EL2, IRGN0, WBWA) | \
> + SYS_FIELD_PREP_ENUM(VTCR_EL2, TG0, VTCR_EL2_TGRAN) | \
> + VTCR_EL2_RES1)
>
> -#define VTCR_EL2_FLAGS (VTCR_EL2_COMMON_BITS | VTCR_EL2_TGRAN)
> -#define VTCR_EL2_IPA(vtcr) (64 - ((vtcr) & VTCR_EL2_T0SZ_MASK))
> +#define VTCR_EL2_IPA(vtcr) (64 - FIELD_GET(VTCR_EL2_T0SZ, (vtcr)))
>
> /*
> * ARM VMSAv8-64 defines an algorithm for finding the translation table
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index c231d2a3e5159..acad7a7621b9e 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -516,7 +516,6 @@
> #define SYS_TTBR1_EL2 sys_reg(3, 4, 2, 0, 1)
> #define SYS_TCR_EL2 sys_reg(3, 4, 2, 0, 2)
> #define SYS_VTTBR_EL2 sys_reg(3, 4, 2, 1, 0)
> -#define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2)
>
> #define SYS_HAFGRTR_EL2 sys_reg(3, 4, 3, 1, 6)
> #define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0)
> diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
> index 947ac1a951a5b..e0bd6a0172729 100644
> --- a/arch/arm64/kvm/hyp/pgtable.c
> +++ b/arch/arm64/kvm/hyp/pgtable.c
> @@ -583,8 +583,8 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift)
> u64 vtcr = VTCR_EL2_FLAGS;
> s8 lvls;
>
> - vtcr |= kvm_get_parange(mmfr0) << VTCR_EL2_PS_SHIFT;
> - vtcr |= VTCR_EL2_T0SZ(phys_shift);
> + vtcr |= FIELD_PREP(VTCR_EL2_PS, kvm_get_parange(mmfr0));
> + vtcr |= FIELD_PREP(VTCR_EL2_T0SZ, (UL(64) - phys_shift));
> /*
> * Use a minimum 2 level page table to prevent splitting
> * host PMD huge pages at stage2.
> @@ -624,9 +624,7 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift)
> vtcr |= VTCR_EL2_DS;
>
> /* Set the vmid bits */
> - vtcr |= (get_vmid_bits(mmfr1) == 16) ?
> - VTCR_EL2_VS_16BIT :
> - VTCR_EL2_VS_8BIT;
> + vtcr |= (get_vmid_bits(mmfr1) == 16) ? VTCR_EL2_VS : 0;
>
> return vtcr;
> }
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index 911fc99ed99d9..e1ef8930c97b3 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -377,7 +377,7 @@ static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi)
> {
> wi->t0sz = vtcr & TCR_EL2_T0SZ_MASK;
>
> - switch (vtcr & VTCR_EL2_TG0_MASK) {
> + switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
> case VTCR_EL2_TG0_4K:
> wi->pgshift = 12; break;
> case VTCR_EL2_TG0_16K:
> @@ -513,7 +513,7 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)
>
> lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock);
>
> - switch (vtcr & VTCR_EL2_TG0_MASK) {
> + switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
> case VTCR_EL2_TG0_4K:
> ttl = (TLBI_TTL_TG_4K << 2);
> break;
> @@ -530,7 +530,7 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)
>
> again:
> /* Iteratively compute the block sizes for a particular granule size */
> - switch (vtcr & VTCR_EL2_TG0_MASK) {
> + switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
> case VTCR_EL2_TG0_4K:
> if (sz < SZ_4K) sz = SZ_4K;
> else if (sz < SZ_2M) sz = SZ_2M;
> @@ -593,7 +593,7 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)
>
> if (!max_size) {
> /* Compute the maximum extent of the invalidation */
> - switch (mmu->tlb_vtcr & VTCR_EL2_TG0_MASK) {
> + switch (FIELD_GET(VTCR_EL2_TG0_MASK, mmu->tlb_vtcr)) {
> case VTCR_EL2_TG0_4K:
> max_size = SZ_1G;
> break;
> diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
> index 9d388f87d9a13..6f43b2ae5993b 100644
> --- a/arch/arm64/tools/sysreg
> +++ b/arch/arm64/tools/sysreg
> @@ -4400,6 +4400,63 @@ Field 56:12 BADDR
> Res0 11:0
> EndSysreg
>
> +Sysreg VTCR_EL2 3 4 2 1 2
> +Res0 63:46
> +Field 45 HDBSS
> +Field 44 HAFT
> +Res0 43:42
> +Field 41 TL0
> +Field 40 GCSH
> +Res0 39
> +Field 38 D128
> +Field 37 S2POE
> +Field 36 S2PIE
> +Field 35 TL1
> +Field 34 AssuredOnly
> +Field 33 SL2
> +Field 32 DS
> +Res1 31
> +Field 30 NSA
> +Field 29 NSW
> +Field 28 HWU62
> +Field 27 HWU61
> +Field 26 HWU60
> +Field 25 HWU59
> +Res0 24:23
> +Field 22 HD
> +Field 21 HA
> +Res0 20
> +Enum 19 VS
> + 0b0 8BIT
> + 0b1 16BIT
> +EndEnum
> +Field 18:16 PS
> +Enum 15:14 TG0
> + 0b00 4K
> + 0b01 64K
> + 0b10 16K
> +EndEnum
> +Enum 13:12 SH0
> + 0b00 NONE
> + 0b01 OUTER
> + 0b11 INNER
> +EndEnum
> +Enum 11:10 ORGN0
> + 0b00 NC
> + 0b01 WBWA
> + 0b10 WT
> + 0b11 WBnWA
> +EndEnum
> +Enum 9:8 IRGN0
> + 0b00 NC
> + 0b01 WBWA
> + 0b10 WT
> + 0b11 WBnWA
> +EndEnum
> +Field 7:6 SL0
> +Field 5:0 T0SZ
> +EndSysreg
> +
> Sysreg GCSCR_EL2 3 4 2 5 0
> Fields GCSCR_ELx
> EndSysreg
> --
> 2.47.3
>
More information about the linux-arm-kernel
mailing list