Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : #include <linux/arm-smccc.h> 3 : : #include <linux/kernel.h> 4 : : #include <linux/psci.h> 5 : : #include <linux/smp.h> 6 : : 7 : : #include <asm/cp15.h> 8 : : #include <asm/cputype.h> 9 : : #include <asm/proc-fns.h> 10 : : #include <asm/system_misc.h> 11 : : 12 : : #ifdef CONFIG_HARDEN_BRANCH_PREDICTOR 13 : : DEFINE_PER_CPU(harden_branch_predictor_fn_t, harden_branch_predictor_fn); 14 : : 15 : : extern void cpu_v7_iciallu_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 16 : : extern void cpu_v7_bpiall_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 17 : : extern void cpu_v7_smc_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 18 : : extern void cpu_v7_hvc_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 19 : : 20 : 0 : static void harden_branch_predictor_bpiall(void) 21 : : { 22 : 0 : write_sysreg(0, BPIALL); 23 : 0 : } 24 : : 25 : 0 : static void harden_branch_predictor_iciallu(void) 26 : : { 27 : 0 : write_sysreg(0, ICIALLU); 28 : 0 : } 29 : : 30 : : static void __maybe_unused call_smc_arch_workaround_1(void) 31 : : { 32 : : arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 33 : : } 34 : : 35 : : static void __maybe_unused call_hvc_arch_workaround_1(void) 36 : : { 37 : : arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 38 : : } 39 : : 40 : 3 : static void cpu_v7_spectre_init(void) 41 : : { 42 : : const char *spectre_v2_method = NULL; 43 : 3 : int cpu = smp_processor_id(); 44 : : 45 : 3 : if (per_cpu(harden_branch_predictor_fn, cpu)) 46 : 3 : return; 47 : : 48 : 3 : switch (read_cpuid_part()) { 49 : : case ARM_CPU_PART_CORTEX_A8: 50 : : case ARM_CPU_PART_CORTEX_A9: 51 : : case ARM_CPU_PART_CORTEX_A12: 52 : : case ARM_CPU_PART_CORTEX_A17: 53 : : case ARM_CPU_PART_CORTEX_A73: 54 : : case ARM_CPU_PART_CORTEX_A75: 55 : 0 : per_cpu(harden_branch_predictor_fn, cpu) = 56 : : harden_branch_predictor_bpiall; 57 : : spectre_v2_method = "BPIALL"; 58 : 0 : break; 59 : : 60 : : case ARM_CPU_PART_CORTEX_A15: 61 : : case ARM_CPU_PART_BRAHMA_B15: 62 : 0 : per_cpu(harden_branch_predictor_fn, cpu) = 63 : : harden_branch_predictor_iciallu; 64 : : spectre_v2_method = "ICIALLU"; 65 : 0 : break; 66 : : 67 : : #ifdef CONFIG_ARM_PSCI 68 : : case ARM_CPU_PART_BRAHMA_B53: 69 : : /* Requires no workaround */ 70 : : break; 71 : : default: 72 : : /* Other ARM CPUs require no workaround */ 73 : : if (read_cpuid_implementor() == ARM_CPU_IMP_ARM) 74 : : break; 75 : : /* fallthrough */ 76 : : /* Cortex A57/A72 require firmware workaround */ 77 : : case ARM_CPU_PART_CORTEX_A57: 78 : : case ARM_CPU_PART_CORTEX_A72: { 79 : : struct arm_smccc_res res; 80 : : 81 : : if (psci_ops.smccc_version == SMCCC_VERSION_1_0) 82 : : break; 83 : : 84 : : switch (psci_ops.conduit) { 85 : : case PSCI_CONDUIT_HVC: 86 : : arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 87 : : ARM_SMCCC_ARCH_WORKAROUND_1, &res); 88 : : if ((int)res.a0 != 0) 89 : : break; 90 : : per_cpu(harden_branch_predictor_fn, cpu) = 91 : : call_hvc_arch_workaround_1; 92 : : cpu_do_switch_mm = cpu_v7_hvc_switch_mm; 93 : : spectre_v2_method = "hypervisor"; 94 : : break; 95 : : 96 : : case PSCI_CONDUIT_SMC: 97 : : arm_smccc_1_1_smc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 98 : : ARM_SMCCC_ARCH_WORKAROUND_1, &res); 99 : : if ((int)res.a0 != 0) 100 : : break; 101 : : per_cpu(harden_branch_predictor_fn, cpu) = 102 : : call_smc_arch_workaround_1; 103 : : cpu_do_switch_mm = cpu_v7_smc_switch_mm; 104 : : spectre_v2_method = "firmware"; 105 : : break; 106 : : 107 : : default: 108 : : break; 109 : : } 110 : : } 111 : : #endif 112 : : } 113 : : 114 : 3 : if (spectre_v2_method) 115 : 0 : pr_info("CPU%u: Spectre v2: using %s workaround\n", 116 : : smp_processor_id(), spectre_v2_method); 117 : : } 118 : : #else 119 : : static void cpu_v7_spectre_init(void) 120 : : { 121 : : } 122 : : #endif 123 : : 124 : 0 : static __maybe_unused bool cpu_v7_check_auxcr_set(bool *warned, 125 : : u32 mask, const char *msg) 126 : : { 127 : : u32 aux_cr; 128 : : 129 : 0 : asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (aux_cr)); 130 : : 131 : 0 : if ((aux_cr & mask) != mask) { 132 : 0 : if (!*warned) 133 : 0 : pr_err("CPU%u: %s", smp_processor_id(), msg); 134 : 0 : *warned = true; 135 : 0 : return false; 136 : : } 137 : : return true; 138 : : } 139 : : 140 : : static DEFINE_PER_CPU(bool, spectre_warned); 141 : : 142 : : static bool check_spectre_auxcr(bool *warned, u32 bit) 143 : : { 144 : : return IS_ENABLED(CONFIG_HARDEN_BRANCH_PREDICTOR) && 145 : 0 : cpu_v7_check_auxcr_set(warned, bit, 146 : : "Spectre v2: firmware did not set auxiliary control register IBE bit, system vulnerable\n"); 147 : : } 148 : : 149 : 0 : void cpu_v7_ca8_ibe(void) 150 : : { 151 : 0 : if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(6))) 152 : 0 : cpu_v7_spectre_init(); 153 : 0 : } 154 : : 155 : 0 : void cpu_v7_ca15_ibe(void) 156 : : { 157 : 0 : if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(0))) 158 : 0 : cpu_v7_spectre_init(); 159 : 0 : } 160 : : 161 : 3 : void cpu_v7_bugs_init(void) 162 : : { 163 : 3 : cpu_v7_spectre_init(); 164 : 3 : }