arm64: mm: Handle LVA support as a CPU feature
authorArd Biesheuvel <ardb@kernel.org>
Wed, 14 Feb 2024 12:29:11 +0000 (13:29 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 16 Feb 2024 12:42:36 +0000 (12:42 +0000)
Currently, we detect CPU support for 52-bit virtual addressing (LVA)
extremely early, before creating the kernel page tables or enabling the
MMU. We cannot override the feature this early, and so large virtual
addressing is always enabled on CPUs that implement support for it if
the software support for it was enabled at build time. It also means we
rely on non-trivial code in asm to deal with this feature.

Given that both the ID map and the TTBR1 mapping of the kernel image are
guaranteed to be 48-bit addressable, it is not actually necessary to
enable support this early, and instead, we can model it as a CPU
feature. That way, we can rely on code patching to get the correct
TCR.T1SZ values programmed on secondary boot and resume from suspend.

On the primary boot path, we simply enable the MMU with 48-bit virtual
addressing initially, and update TCR.T1SZ if LVA is supported from C
code, right before creating the kernel mapping. Given that TTBR1 still
points to reserved_pg_dir at this point, updating TCR.T1SZ should be
safe without the need for explicit TLB maintenance.

Since this gets rid of all accesses to the vabits_actual variable from
asm code that occurred before TCR.T1SZ had been programmed, we no longer
have a need for this variable, and we can replace it with a C expression
that produces the correct value directly, based on the value of TCR.T1SZ.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20240214122845.2033971-70-ardb+git@google.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/memory.h
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/head.S
arch/arm64/kernel/image-vars.h
arch/arm64/kernel/pi/map_kernel.c
arch/arm64/kernel/sleep.S
arch/arm64/mm/mmu.c
arch/arm64/mm/proc.S
arch/arm64/tools/cpucaps

index e3edae1825f37497dc4860a7ead444866f79647e..4f4dc5496ee3a745e7860f641ffd6883dceda290 100644 (file)
@@ -995,6 +995,15 @@ static inline bool cpu_has_pac(void)
                                            &id_aa64isar2_override);
 }
 
+static inline bool cpu_has_lva(void)
+{
+       u64 mmfr2;
+
+       mmfr2 = read_sysreg_s(SYS_ID_AA64MMFR2_EL1);
+       return cpuid_feature_extract_unsigned_field(mmfr2,
+                                                   ID_AA64MMFR2_EL1_VARange_SHIFT);
+}
+
 #endif /* __ASSEMBLY__ */
 
 #endif
index 60904a6c4b42b5960c408f783f3e108c46aef644..9680d7444b3b5a2aff9a8fe35603c512a179ca88 100644 (file)
 #include <asm/boot.h>
 #include <asm/bug.h>
 #include <asm/sections.h>
+#include <asm/sysreg.h>
+
+static inline u64 __pure read_tcr(void)
+{
+       u64  tcr;
+
+       // read_sysreg() uses asm volatile, so avoid it here
+       asm("mrs %0, tcr_el1" : "=r"(tcr));
+       return tcr;
+}
 
 #if VA_BITS > 48
-extern u64                     vabits_actual;
+// For reasons of #include hell, we can't use TCR_T1SZ_OFFSET/TCR_T1SZ_MASK here
+#define vabits_actual          (64 - ((read_tcr() >> 16) & 63))
 #else
 #define vabits_actual          ((u64)VA_BITS)
 #endif
index 7064cf13f226ac3e8224b6099536f68c5ac49662..8eb8c7f7b317b0bb21b2de5832134372c2f465f9 100644 (file)
@@ -2692,6 +2692,19 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                .type = ARM64_CPUCAP_SYSTEM_FEATURE,
                .matches = has_lpa2,
        },
+#ifdef CONFIG_ARM64_VA_BITS_52
+       {
+               .desc = "52-bit Virtual Addressing (LVA)",
+               .capability = ARM64_HAS_VA52,
+               .type = ARM64_CPUCAP_BOOT_CPU_FEATURE,
+               .sys_reg = SYS_ID_AA64MMFR2_EL1,
+               .sign = FTR_UNSIGNED,
+               .field_width = 4,
+               .field_pos = ID_AA64MMFR2_EL1_VARange_SHIFT,
+               .matches = has_cpuid_feature,
+               .min_field_value = ID_AA64MMFR2_EL1_VARange_52,
+       },
+#endif
        {},
 };
 
index 545b5d8976f4bf9770985b95b4b6915a5e838f86..e25351addfd0077e7f5fee002fab536e0cdf43c6 100644 (file)
@@ -80,7 +80,6 @@
         *  x19        primary_entry() .. start_kernel()        whether we entered with the MMU on
         *  x20        primary_entry() .. __primary_switch()    CPU boot mode
         *  x21        primary_entry() .. start_kernel()        FDT pointer passed at boot in x0
-        *  x25        primary_entry() .. start_kernel()        supported VA size
         */
 SYM_CODE_START(primary_entry)
        bl      record_mmu_state
@@ -125,14 +124,6 @@ SYM_CODE_START(primary_entry)
         * On return, the CPU will be ready for the MMU to be turned on and
         * the TCR will have been set.
         */
-#if VA_BITS > 48
-       mrs_s   x0, SYS_ID_AA64MMFR2_EL1
-       tst     x0, ID_AA64MMFR2_EL1_VARange_MASK
-       mov     x0, #VA_BITS
-       mov     x25, #VA_BITS_MIN
-       csel    x25, x25, x0, eq
-       mov     x0, x25
-#endif
        bl      __cpu_setup                     // initialise processor
        b       __primary_switch
 SYM_CODE_END(primary_entry)
@@ -242,11 +233,6 @@ SYM_FUNC_START_LOCAL(__primary_switched)
        mov     x0, x20
        bl      set_cpu_boot_mode_flag
 
-#if VA_BITS > 48
-       adr_l   x8, vabits_actual               // Set this early so KASAN early init
-       str     x25, [x8]                       // ... observes the correct value
-       dc      civac, x8                       // Make visible to booting secondaries
-#endif
 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
        bl      kasan_early_init
 #endif
@@ -376,10 +362,13 @@ SYM_FUNC_START_LOCAL(secondary_startup)
         * Common entry point for secondary CPUs.
         */
        mov     x20, x0                         // preserve boot mode
+
+#ifdef CONFIG_ARM64_VA_BITS_52
+alternative_if ARM64_HAS_VA52
        bl      __cpu_secondary_check52bitva
-#if VA_BITS > 48
-       ldr_l   x0, vabits_actual
+alternative_else_nop_endif
 #endif
+
        bl      __cpu_setup                     // initialise processor
        adrp    x1, swapper_pg_dir
        adrp    x2, idmap_pg_dir
@@ -482,12 +471,8 @@ SYM_FUNC_START(__enable_mmu)
        ret
 SYM_FUNC_END(__enable_mmu)
 
+#ifdef CONFIG_ARM64_VA_BITS_52
 SYM_FUNC_START(__cpu_secondary_check52bitva)
-#if VA_BITS > 48
-       ldr_l   x0, vabits_actual
-       cmp     x0, #52
-       b.ne    2f
-
        mrs_s   x0, SYS_ID_AA64MMFR2_EL1
        and     x0, x0, ID_AA64MMFR2_EL1_VARange_MASK
        cbnz    x0, 2f
@@ -498,9 +483,9 @@ SYM_FUNC_START(__cpu_secondary_check52bitva)
        wfi
        b       1b
 
-#endif
 2:     ret
 SYM_FUNC_END(__cpu_secondary_check52bitva)
+#endif
 
 SYM_FUNC_START_LOCAL(__no_granule_support)
        /* Indicate that this CPU can't boot and is stuck in the kernel */
index e140c5bda90b684b553554c531a262f8c5e3c3f4..2b9d702abe0f8b05e698e727257191acd60a8ac6 100644 (file)
@@ -36,7 +36,6 @@ PROVIDE(__pi___memcpy                 = __pi_memcpy);
 PROVIDE(__pi___memmove                 = __pi_memmove);
 PROVIDE(__pi___memset                  = __pi_memset);
 
-PROVIDE(__pi_vabits_actual             = vabits_actual);
 PROVIDE(__pi_id_aa64isar1_override     = id_aa64isar1_override);
 PROVIDE(__pi_id_aa64isar2_override     = id_aa64isar2_override);
 PROVIDE(__pi_id_aa64mmfr1_override     = id_aa64mmfr1_override);
index 4b76a007a50dc1a2fae1d4abec48f8d703611494..1853825aa29d0d61c2ea259f64a9530164f5c350 100644 (file)
@@ -165,6 +165,9 @@ asmlinkage void __init early_map_kernel(u64 boot_status, void *fdt)
        chosen = fdt_path_offset(fdt, chosen_str);
        init_feature_override(boot_status, fdt, chosen);
 
+       if (VA_BITS > VA_BITS_MIN && cpu_has_lva())
+               sysreg_clear_set(tcr_el1, TCR_T1SZ_MASK, TCR_T1SZ(VA_BITS));
+
        /*
         * The virtual KASLR displacement modulo 2MiB is decided by the
         * physical placement of the image, as otherwise, we might not be able
index 2aa5129d825377c980517a9b94db63f2020d4d58..f093cdf71be111ae0839937bd745af52a2628151 100644 (file)
@@ -102,9 +102,6 @@ SYM_CODE_START(cpu_resume)
        mov     x0, xzr
        bl      init_kernel_el
        mov     x19, x0                 // preserve boot mode
-#if VA_BITS > 48
-       ldr_l   x0, vabits_actual
-#endif
        bl      __cpu_setup
        /* enable the MMU early - so we can access sleep_save_stash by va */
        adrp    x1, swapper_pg_dir
index a3d23da92d87fece0c1ddc10645ae889662d6bb8..ba00d0205447735b6ffd9d48ed065fc78727ee1e 100644 (file)
 #define NO_CONT_MAPPINGS       BIT(1)
 #define NO_EXEC_MAPPINGS       BIT(2)  /* assumes FEAT_HPDS is not used */
 
-#if VA_BITS > 48
-u64 vabits_actual __ro_after_init = VA_BITS_MIN;
-EXPORT_SYMBOL(vabits_actual);
-#endif
-
 u64 kimage_voffset __ro_after_init;
 EXPORT_SYMBOL(kimage_voffset);
 
index 55c366dbda8f8e2ce6711e67dffb40b3a31bc642..d104ddab26a4124f414d4550b682f557da4678ca 100644 (file)
@@ -397,8 +397,6 @@ SYM_FUNC_END(idmap_kpti_install_ng_mappings)
  *
  *     Initialise the processor for turning the MMU on.
  *
- * Input:
- *     x0 - actual number of VA bits (ignored unless VA_BITS > 48)
  * Output:
  *     Return in x0 the value of the SCTLR_EL1 register.
  */
@@ -422,16 +420,17 @@ SYM_FUNC_START(__cpu_setup)
        mair    .req    x17
        tcr     .req    x16
        mov_q   mair, MAIR_EL1_SET
-       mov_q   tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS) | TCR_CACHE_FLAGS | \
+       mov_q   tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \
                     TCR_SMP_FLAGS | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
                     TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS
 
        tcr_clear_errata_bits tcr, x9, x5
 
 #ifdef CONFIG_ARM64_VA_BITS_52
-       sub             x9, xzr, x0
-       add             x9, x9, #64
+       mov             x9, #64 - VA_BITS
+alternative_if ARM64_HAS_VA52
        tcr_set_t1sz    tcr, x9
+alternative_else_nop_endif
 #endif
 
        /*
index b912b1409fc09aaf08705b8d75b5d221ae0d020e..b370d808b3ecee326ed450b49a4ff62a249e488d 100644 (file)
@@ -50,6 +50,7 @@ HAS_STAGE2_FWB
 HAS_TCR2
 HAS_TIDCP1
 HAS_TLB_RANGE
+HAS_VA52
 HAS_VIRT_HOST_EXTN
 HAS_WFXT
 HW_DBM