162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/arm-smccc.h> 362306a36Sopenharmony_ci#include <linux/kernel.h> 462306a36Sopenharmony_ci#include <linux/smp.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <asm/cp15.h> 762306a36Sopenharmony_ci#include <asm/cputype.h> 862306a36Sopenharmony_ci#include <asm/proc-fns.h> 962306a36Sopenharmony_ci#include <asm/spectre.h> 1062306a36Sopenharmony_ci#include <asm/system_misc.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#ifdef CONFIG_ARM_PSCI 1362306a36Sopenharmony_cistatic int __maybe_unused spectre_v2_get_cpu_fw_mitigation_state(void) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct arm_smccc_res res; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 1862306a36Sopenharmony_ci ARM_SMCCC_ARCH_WORKAROUND_1, &res); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci switch ((int)res.a0) { 2162306a36Sopenharmony_ci case SMCCC_RET_SUCCESS: 2262306a36Sopenharmony_ci return SPECTRE_MITIGATED; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED: 2562306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci default: 2862306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci#else 3262306a36Sopenharmony_cistatic int __maybe_unused spectre_v2_get_cpu_fw_mitigation_state(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR 3962306a36Sopenharmony_ciDEFINE_PER_CPU(harden_branch_predictor_fn_t, harden_branch_predictor_fn); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciextern void cpu_v7_iciallu_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 4262306a36Sopenharmony_ciextern void cpu_v7_bpiall_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 4362306a36Sopenharmony_ciextern void cpu_v7_smc_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 4462306a36Sopenharmony_ciextern void cpu_v7_hvc_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void harden_branch_predictor_bpiall(void) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci write_sysreg(0, BPIALL); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void harden_branch_predictor_iciallu(void) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci write_sysreg(0, ICIALLU); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void __maybe_unused call_smc_arch_workaround_1(void) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void __maybe_unused call_hvc_arch_workaround_1(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic unsigned int spectre_v2_install_workaround(unsigned int method) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci const char *spectre_v2_method = NULL; 6962306a36Sopenharmony_ci int cpu = smp_processor_id(); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (per_cpu(harden_branch_predictor_fn, cpu)) 7262306a36Sopenharmony_ci return SPECTRE_MITIGATED; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci switch (method) { 7562306a36Sopenharmony_ci case SPECTRE_V2_METHOD_BPIALL: 7662306a36Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 7762306a36Sopenharmony_ci harden_branch_predictor_bpiall; 7862306a36Sopenharmony_ci spectre_v2_method = "BPIALL"; 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci case SPECTRE_V2_METHOD_ICIALLU: 8262306a36Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 8362306a36Sopenharmony_ci harden_branch_predictor_iciallu; 8462306a36Sopenharmony_ci spectre_v2_method = "ICIALLU"; 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci case SPECTRE_V2_METHOD_HVC: 8862306a36Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 8962306a36Sopenharmony_ci call_hvc_arch_workaround_1; 9062306a36Sopenharmony_ci cpu_do_switch_mm = cpu_v7_hvc_switch_mm; 9162306a36Sopenharmony_ci spectre_v2_method = "hypervisor"; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci case SPECTRE_V2_METHOD_SMC: 9562306a36Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 9662306a36Sopenharmony_ci call_smc_arch_workaround_1; 9762306a36Sopenharmony_ci cpu_do_switch_mm = cpu_v7_smc_switch_mm; 9862306a36Sopenharmony_ci spectre_v2_method = "firmware"; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (spectre_v2_method) 10362306a36Sopenharmony_ci pr_info("CPU%u: Spectre v2: using %s workaround\n", 10462306a36Sopenharmony_ci smp_processor_id(), spectre_v2_method); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return SPECTRE_MITIGATED; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci#else 10962306a36Sopenharmony_cistatic unsigned int spectre_v2_install_workaround(unsigned int method) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci pr_info_once("Spectre V2: workarounds disabled by configuration\n"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci#endif 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void cpu_v7_spectre_v2_init(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci unsigned int state, method = 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci switch (read_cpuid_part()) { 12262306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A8: 12362306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A9: 12462306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A12: 12562306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A17: 12662306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A73: 12762306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A75: 12862306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 12962306a36Sopenharmony_ci method = SPECTRE_V2_METHOD_BPIALL; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A15: 13362306a36Sopenharmony_ci case ARM_CPU_PART_BRAHMA_B15: 13462306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 13562306a36Sopenharmony_ci method = SPECTRE_V2_METHOD_ICIALLU; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci case ARM_CPU_PART_BRAHMA_B53: 13962306a36Sopenharmony_ci /* Requires no workaround */ 14062306a36Sopenharmony_ci state = SPECTRE_UNAFFECTED; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci default: 14462306a36Sopenharmony_ci /* Other ARM CPUs require no workaround */ 14562306a36Sopenharmony_ci if (read_cpuid_implementor() == ARM_CPU_IMP_ARM) { 14662306a36Sopenharmony_ci state = SPECTRE_UNAFFECTED; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci fallthrough; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Cortex A57/A72 require firmware workaround */ 15362306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A57: 15462306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A72: 15562306a36Sopenharmony_ci state = spectre_v2_get_cpu_fw_mitigation_state(); 15662306a36Sopenharmony_ci if (state != SPECTRE_MITIGATED) 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci switch (arm_smccc_1_1_get_conduit()) { 16062306a36Sopenharmony_ci case SMCCC_CONDUIT_HVC: 16162306a36Sopenharmony_ci method = SPECTRE_V2_METHOD_HVC; 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci case SMCCC_CONDUIT_SMC: 16562306a36Sopenharmony_ci method = SPECTRE_V2_METHOD_SMC; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci default: 16962306a36Sopenharmony_ci state = SPECTRE_VULNERABLE; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (state == SPECTRE_MITIGATED) 17562306a36Sopenharmony_ci state = spectre_v2_install_workaround(method); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci spectre_v2_update_state(state, method); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#ifdef CONFIG_HARDEN_BRANCH_HISTORY 18162306a36Sopenharmony_cistatic int spectre_bhb_method; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const char *spectre_bhb_method_name(int method) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci switch (method) { 18662306a36Sopenharmony_ci case SPECTRE_V2_METHOD_LOOP8: 18762306a36Sopenharmony_ci return "loop"; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci case SPECTRE_V2_METHOD_BPIALL: 19062306a36Sopenharmony_ci return "BPIALL"; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci default: 19362306a36Sopenharmony_ci return "unknown"; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int spectre_bhb_install_workaround(int method) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci if (spectre_bhb_method != method) { 20062306a36Sopenharmony_ci if (spectre_bhb_method) { 20162306a36Sopenharmony_ci pr_err("CPU%u: Spectre BHB: method disagreement, system vulnerable\n", 20262306a36Sopenharmony_ci smp_processor_id()); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (spectre_bhb_update_vectors(method) == SPECTRE_VULNERABLE) 20862306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci spectre_bhb_method = method; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci pr_info("CPU%u: Spectre BHB: enabling %s workaround for all CPUs\n", 21362306a36Sopenharmony_ci smp_processor_id(), spectre_bhb_method_name(method)); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return SPECTRE_MITIGATED; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci#else 21962306a36Sopenharmony_cistatic int spectre_bhb_install_workaround(int method) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci#endif 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void cpu_v7_spectre_bhb_init(void) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci unsigned int state, method = 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci switch (read_cpuid_part()) { 23062306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A15: 23162306a36Sopenharmony_ci case ARM_CPU_PART_BRAHMA_B15: 23262306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A57: 23362306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A72: 23462306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 23562306a36Sopenharmony_ci method = SPECTRE_V2_METHOD_LOOP8; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A73: 23962306a36Sopenharmony_ci case ARM_CPU_PART_CORTEX_A75: 24062306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 24162306a36Sopenharmony_ci method = SPECTRE_V2_METHOD_BPIALL; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci default: 24562306a36Sopenharmony_ci state = SPECTRE_UNAFFECTED; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (state == SPECTRE_MITIGATED) 25062306a36Sopenharmony_ci state = spectre_bhb_install_workaround(method); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci spectre_v2_update_state(state, method); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic __maybe_unused bool cpu_v7_check_auxcr_set(bool *warned, 25662306a36Sopenharmony_ci u32 mask, const char *msg) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci u32 aux_cr; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (aux_cr)); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if ((aux_cr & mask) != mask) { 26362306a36Sopenharmony_ci if (!*warned) 26462306a36Sopenharmony_ci pr_err("CPU%u: %s", smp_processor_id(), msg); 26562306a36Sopenharmony_ci *warned = true; 26662306a36Sopenharmony_ci return false; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci return true; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic DEFINE_PER_CPU(bool, spectre_warned); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic bool check_spectre_auxcr(bool *warned, u32 bit) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci return IS_ENABLED(CONFIG_HARDEN_BRANCH_PREDICTOR) && 27662306a36Sopenharmony_ci cpu_v7_check_auxcr_set(warned, bit, 27762306a36Sopenharmony_ci "Spectre v2: firmware did not set auxiliary control register IBE bit, system vulnerable\n"); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_civoid cpu_v7_ca8_ibe(void) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(6))) 28362306a36Sopenharmony_ci cpu_v7_spectre_v2_init(); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_civoid cpu_v7_ca15_ibe(void) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(0))) 28962306a36Sopenharmony_ci cpu_v7_spectre_v2_init(); 29062306a36Sopenharmony_ci cpu_v7_spectre_bhb_init(); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_civoid cpu_v7_bugs_init(void) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci cpu_v7_spectre_v2_init(); 29662306a36Sopenharmony_ci cpu_v7_spectre_bhb_init(); 29762306a36Sopenharmony_ci} 298