arm64: alternatives: have callbacks take a cap
authorMark Rutland <mark.rutland@arm.com>
Mon, 12 Sep 2022 16:22:08 +0000 (17:22 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 16 Sep 2022 16:15:03 +0000 (17:15 +0100)
Today, callback alternatives are special-cased within
__apply_alternatives(), and are applied alongside patching for system
capabilities as ARM64_NCAPS is not part of the boot_capabilities feature
mask.

This special-casing is less than ideal. Giving special meaning to
ARM64_NCAPS for this requires some structures and loops to use
ARM64_NCAPS + 1 (AKA ARM64_NPATCHABLE), while others use ARM64_NCAPS.
It's also not immediately clear callback alternatives are only applied
when applying alternatives for system-wide features.

To make this a bit clearer, changes the way that callback alternatives
are identified to remove the special-casing of ARM64_NCAPS, and to allow
callback alternatives to be associated with a cpucap as with all other
alternatives.

New cpucaps, ARM64_ALWAYS_BOOT and ARM64_ALWAYS_SYSTEM are added which
are always detected alongside boot cpu capabilities and system
capabilities respectively. All existing callback alternatives are made
to use ARM64_ALWAYS_SYSTEM, and so will be patched at the same point
during the boot flow as before.

Subsequent patches will make more use of these new cpucaps.

There should be no functional change as a result of this patch.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: James Morse <james.morse@arm.com>
Cc: Joey Gouly <joey.gouly@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20220912162210.3626215-7-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/include/asm/alternative-macros.h
arch/arm64/include/asm/assembler.h
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/kernel/alternative.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/entry.S
arch/arm64/kvm/hyp/hyp-entry.S
arch/arm64/tools/cpucaps

index 7e157ab6cd505376a9c809f6941658ae71a3c9f3..189c31be163ce3b50fd457e88496ebfd7dd3f43b 100644 (file)
@@ -2,10 +2,16 @@
 #ifndef __ASM_ALTERNATIVE_MACROS_H
 #define __ASM_ALTERNATIVE_MACROS_H
 
+#include <linux/const.h>
+
 #include <asm/cpucaps.h>
 #include <asm/insn-def.h>
 
-#define ARM64_CB_PATCH ARM64_NCAPS
+#define ARM64_CB_BIT   (UL(1) << 15)
+
+#if ARM64_NCAPS >= ARM64_CB_BIT
+#error "cpucaps have overflown ARM64_CB_BIT"
+#endif
 
 #ifndef __ASSEMBLY__
 
@@ -73,8 +79,8 @@
 #define _ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg, ...)        \
        __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg))
 
-#define ALTERNATIVE_CB(oldinstr, cb) \
-       __ALTERNATIVE_CFG_CB(oldinstr, ARM64_CB_PATCH, 1, cb)
+#define ALTERNATIVE_CB(oldinstr, feature, cb) \
+       __ALTERNATIVE_CFG_CB(oldinstr, ARM64_CB_BIT | (feature), 1, cb)
 #else
 
 #include <asm/assembler.h>
@@ -82,7 +88,7 @@
 .macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
        .word \orig_offset - .
        .word \alt_offset - .
-       .hword \feature
+       .hword (\feature)
        .byte \orig_len
        .byte \alt_len
 .endm
 661:
 .endm
 
-.macro alternative_cb cb
+.macro alternative_cb cap, cb
        .set .Lasm_alt_mode, 0
        .pushsection .altinstructions, "a"
-       altinstruction_entry 661f, \cb, ARM64_CB_PATCH, 662f-661f, 0
+       altinstruction_entry 661f, \cb, ARM64_CB_BIT | \cap, 662f-661f, 0
        .popsection
 661:
 .endm
index 5846145be523c64c4e5c5be4e57cd9e9c2104ad0..d851e27334395b5d7d36db00c7e9dc94dc6734f7 100644 (file)
@@ -293,7 +293,7 @@ alternative_endif
 alternative_if_not ARM64_KVM_PROTECTED_MODE
        ASM_BUG()
 alternative_else_nop_endif
-alternative_cb kvm_compute_final_ctr_el0
+alternative_cb ARM64_ALWAYS_SYSTEM, kvm_compute_final_ctr_el0
        movz    \reg, #0
        movk    \reg, #0, lsl #16
        movk    \reg, #0, lsl #32
@@ -877,7 +877,7 @@ alternative_endif
 
        .macro __mitigate_spectre_bhb_loop      tmp
 #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY
-alternative_cb  spectre_bhb_patch_loop_iter
+alternative_cb ARM64_ALWAYS_SYSTEM, spectre_bhb_patch_loop_iter
        mov     \tmp, #32               // Patched to correct the immediate
 alternative_cb_end
 .Lspectre_bhb_loop\@:
@@ -890,7 +890,7 @@ alternative_cb_end
 
        .macro mitigate_spectre_bhb_loop        tmp
 #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY
-alternative_cb spectre_bhb_patch_loop_mitigation_enable
+alternative_cb ARM64_ALWAYS_SYSTEM, spectre_bhb_patch_loop_mitigation_enable
        b       .L_spectre_bhb_loop_done\@      // Patched to NOP
 alternative_cb_end
        __mitigate_spectre_bhb_loop     \tmp
@@ -904,7 +904,7 @@ alternative_cb_end
        stp     x0, x1, [sp, #-16]!
        stp     x2, x3, [sp, #-16]!
        mov     w0, #ARM_SMCCC_ARCH_WORKAROUND_3
-alternative_cb smccc_patch_fw_mitigation_conduit
+alternative_cb ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit
        nop                                     // Patched to SMC/HVC #0
 alternative_cb_end
        ldp     x2, x3, [sp], #16
@@ -914,7 +914,7 @@ alternative_cb_end
 
        .macro mitigate_spectre_bhb_clear_insn
 #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY
-alternative_cb spectre_bhb_patch_clearbhb
+alternative_cb ARM64_ALWAYS_SYSTEM, spectre_bhb_patch_clearbhb
        /* Patched to NOP when not supported */
        clearbhb
        isb
index 630c337c777462ef863b00b7b2f3ae294e684eda..f5852ad6e8845e928b78042d0879e16c56748f44 100644 (file)
@@ -422,9 +422,7 @@ extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
 extern struct static_key_false cpu_hwcap_keys[ARM64_NCAPS];
 extern struct static_key_false arm64_const_caps_ready;
 
-/* ARM64 CAPS + alternative_cb */
-#define ARM64_NPATCHABLE (ARM64_NCAPS + 1)
-extern DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE);
+extern DECLARE_BITMAP(boot_capabilities, ARM64_NCAPS);
 
 #define for_each_available_cap(cap)            \
        for_each_set_bit(cap, cpu_hwcaps, ARM64_NCAPS)
index b208da3bebec82e7b59467f155561ce3a1cc3047..7784081088e78f84c7aad3aaf9553a276a6d1b8a 100644 (file)
@@ -63,7 +63,7 @@
  * specific registers encoded in the instructions).
  */
 .macro kern_hyp_va     reg
-alternative_cb kvm_update_va_mask
+alternative_cb ARM64_ALWAYS_SYSTEM, kvm_update_va_mask
        and     \reg, \reg, #1          /* mask with va_mask */
        ror     \reg, \reg, #1          /* rotate to the first tag bit */
        add     \reg, \reg, #0          /* insert the low 12 bits of the tag */
@@ -97,7 +97,7 @@ alternative_cb_end
        hyp_pa  \reg, \tmp
 
        /* Load kimage_voffset. */
-alternative_cb kvm_get_kimage_voffset
+alternative_cb ARM64_ALWAYS_SYSTEM, kvm_get_kimage_voffset
        movz    \tmp, #0
        movk    \tmp, #0, lsl #16
        movk    \tmp, #0, lsl #32
@@ -131,6 +131,7 @@ static __always_inline unsigned long __kern_hyp_va(unsigned long v)
                                    "add %0, %0, #0\n"
                                    "add %0, %0, #0, lsl 12\n"
                                    "ror %0, %0, #63\n",
+                                   ARM64_ALWAYS_SYSTEM,
                                    kvm_update_va_mask)
                     : "+r" (v));
        return v;
index 2e18c9c0f612b58d9ba06c20ee54b03f862b1184..9a071a5fcb674204fb4611f9163ab00625647b8d 100644 (file)
@@ -21,6 +21,9 @@
 #define ALT_ORIG_PTR(a)                __ALT_PTR(a, orig_offset)
 #define ALT_REPL_PTR(a)                __ALT_PTR(a, alt_offset)
 
+#define ALT_CAP(a)             ((a)->cpufeature & ~ARM64_CB_BIT)
+#define ALT_HAS_CB(a)          ((a)->cpufeature & ARM64_CB_BIT)
+
 /* Volatile, as we may be patching the guts of READ_ONCE() */
 static volatile int all_alternatives_applied;
 
@@ -143,16 +146,15 @@ static void __nocfi __apply_alternatives(const struct alt_region *region,
 
        for (alt = region->begin; alt < region->end; alt++) {
                int nr_inst;
+               int cap = ALT_CAP(alt);
 
-               if (!test_bit(alt->cpufeature, feature_mask))
+               if (!test_bit(cap, feature_mask))
                        continue;
 
-               /* Use ARM64_CB_PATCH as an unconditional patch */
-               if (alt->cpufeature < ARM64_CB_PATCH &&
-                   !cpus_have_cap(alt->cpufeature))
+               if (!cpus_have_cap(cap))
                        continue;
 
-               if (alt->cpufeature == ARM64_CB_PATCH)
+               if (ALT_HAS_CB(alt))
                        BUG_ON(alt->alt_len != 0);
                else
                        BUG_ON(alt->alt_len != alt->orig_len);
@@ -161,10 +163,10 @@ static void __nocfi __apply_alternatives(const struct alt_region *region,
                updptr = is_module ? origptr : lm_alias(origptr);
                nr_inst = alt->orig_len / AARCH64_INSN_SIZE;
 
-               if (alt->cpufeature < ARM64_CB_PATCH)
-                       alt_cb = patch_alternative;
-               else
+               if (ALT_HAS_CB(alt))
                        alt_cb  = ALT_REPL_PTR(alt);
+               else
+                       alt_cb = patch_alternative;
 
                alt_cb(alt, origptr, updptr, nr_inst);
 
@@ -208,10 +210,10 @@ static int __apply_alternatives_multi_stop(void *unused)
                        cpu_relax();
                isb();
        } else {
-               DECLARE_BITMAP(remaining_capabilities, ARM64_NPATCHABLE);
+               DECLARE_BITMAP(remaining_capabilities, ARM64_NCAPS);
 
                bitmap_complement(remaining_capabilities, boot_capabilities,
-                                 ARM64_NPATCHABLE);
+                                 ARM64_NCAPS);
 
                BUG_ON(all_alternatives_applied);
                __apply_alternatives(&kernel_alternatives, false,
@@ -254,9 +256,9 @@ void apply_alternatives_module(void *start, size_t length)
                .begin  = start,
                .end    = start + length,
        };
-       DECLARE_BITMAP(all_capabilities, ARM64_NPATCHABLE);
+       DECLARE_BITMAP(all_capabilities, ARM64_NCAPS);
 
-       bitmap_fill(all_capabilities, ARM64_NPATCHABLE);
+       bitmap_fill(all_capabilities, ARM64_NCAPS);
 
        __apply_alternatives(&region, true, &all_capabilities[0]);
 }
index af4de817d7123a3608fdcab8b2c1dc42bcdc42aa..68a0545285a1d30cba729062bd14f3d2d17ad28c 100644 (file)
@@ -108,8 +108,7 @@ DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
 EXPORT_SYMBOL(cpu_hwcaps);
 static struct arm64_cpu_capabilities const __ro_after_init *cpu_hwcaps_ptrs[ARM64_NCAPS];
 
-/* Need also bit for ARM64_CB_PATCH */
-DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE);
+DECLARE_BITMAP(boot_capabilities, ARM64_NCAPS);
 
 bool arm64_use_ng_mappings = false;
 EXPORT_SYMBOL(arm64_use_ng_mappings);
@@ -1391,6 +1390,12 @@ u64 __read_sysreg_by_encoding(u32 sys_id)
 
 #include <linux/irqchip/arm-gic-v3.h>
 
+static bool
+has_always(const struct arm64_cpu_capabilities *entry, int scope)
+{
+       return true;
+}
+
 static bool
 feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
 {
@@ -2087,6 +2092,16 @@ cpucap_panic_on_conflict(const struct arm64_cpu_capabilities *cap)
 }
 
 static const struct arm64_cpu_capabilities arm64_features[] = {
+       {
+               .capability = ARM64_ALWAYS_BOOT,
+               .type = ARM64_CPUCAP_BOOT_CPU_FEATURE,
+               .matches = has_always,
+       },
+       {
+               .capability = ARM64_ALWAYS_SYSTEM,
+               .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+               .matches = has_always,
+       },
        {
                .desc = "GIC system register CPU interface",
                .capability = ARM64_HAS_SYSREG_GIC_CPUIF,
index 2d73b3e793b2bc56878b8ef62cde5d98773b54db..e28137d64b7688e2a7f127eac9ce4f49f1118d6e 100644 (file)
         * them if required.
         */
        .macro  apply_ssbd, state, tmp1, tmp2
-alternative_cb spectre_v4_patch_fw_mitigation_enable
+alternative_cb ARM64_ALWAYS_SYSTEM, spectre_v4_patch_fw_mitigation_enable
        b       .L__asm_ssbd_skip\@             // Patched to NOP
 alternative_cb_end
        ldr_this_cpu    \tmp2, arm64_ssbd_callback_required, \tmp1
@@ -123,7 +123,7 @@ alternative_cb_end
        tbnz    \tmp2, #TIF_SSBD, .L__asm_ssbd_skip\@
        mov     w0, #ARM_SMCCC_ARCH_WORKAROUND_2
        mov     w1, #\state
-alternative_cb smccc_patch_fw_mitigation_conduit
+alternative_cb ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit
        nop                                     // Patched to SMC/HVC #0
 alternative_cb_end
 .L__asm_ssbd_skip\@:
@@ -175,7 +175,7 @@ alternative_else_nop_endif
 
        .macro mte_set_kernel_gcr, tmp, tmp2
 #ifdef CONFIG_KASAN_HW_TAGS
-alternative_cb kasan_hw_tags_enable
+alternative_cb ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable
        b       1f
 alternative_cb_end
        mov     \tmp, KERNEL_GCR_EL1
@@ -186,7 +186,7 @@ alternative_cb_end
 
        .macro mte_set_user_gcr, tsk, tmp, tmp2
 #ifdef CONFIG_KASAN_HW_TAGS
-alternative_cb kasan_hw_tags_enable
+alternative_cb ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable
        b       1f
 alternative_cb_end
        ldr     \tmp, [\tsk, #THREAD_MTE_CTRL]
index 7839d075729b1601f28fad70443f405e98ea9498..8f3f93fa119ed85145ad14a9529df9530c381149 100644 (file)
@@ -196,7 +196,7 @@ SYM_CODE_END(__kvm_hyp_vector)
        sub     sp, sp, #(8 * 4)
        stp     x2, x3, [sp, #(8 * 0)]
        stp     x0, x1, [sp, #(8 * 2)]
-       alternative_cb spectre_bhb_patch_wa3
+       alternative_cb ARM64_ALWAYS_SYSTEM, spectre_bhb_patch_wa3
        /* Patched to mov WA3 when supported */
        mov     w0, #ARM_SMCCC_ARCH_WORKAROUND_1
        alternative_cb_end
@@ -216,7 +216,7 @@ SYM_CODE_END(__kvm_hyp_vector)
        mitigate_spectre_bhb_clear_insn
        .endif
        .if \indirect != 0
-       alternative_cb  kvm_patch_vector_branch
+       alternative_cb ARM64_ALWAYS_SYSTEM, kvm_patch_vector_branch
        /*
         * For ARM64_SPECTRE_V3A configurations, these NOPs get replaced with:
         *
index 63b2484ce6c3d001e24f767d95c712b478450828..ff882ec175e73cf9d5d9fd0a3952a5f550009441 100644 (file)
@@ -2,6 +2,8 @@
 #
 # Internal CPU capabilities constants, keep this list sorted
 
+ALWAYS_BOOT
+ALWAYS_SYSTEM
 BTI
 # Unreliable: use system_supports_32bit_el0() instead.
 HAS_32BIT_EL0_DO_NOT_USE