18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/smp.h> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <asm/cp15.h> 78c2ecf20Sopenharmony_ci#include <asm/cputype.h> 88c2ecf20Sopenharmony_ci#include <asm/proc-fns.h> 98c2ecf20Sopenharmony_ci#include <asm/spectre.h> 108c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_PSCI 138c2ecf20Sopenharmony_cistatic int __maybe_unused spectre_v2_get_cpu_fw_mitigation_state(void) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct arm_smccc_res res; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 188c2ecf20Sopenharmony_ci ARM_SMCCC_ARCH_WORKAROUND_1, &res); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci switch ((int)res.a0) { 218c2ecf20Sopenharmony_ci case SMCCC_RET_SUCCESS: 228c2ecf20Sopenharmony_ci return SPECTRE_MITIGATED; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED: 258c2ecf20Sopenharmony_ci return SPECTRE_UNAFFECTED; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci default: 288c2ecf20Sopenharmony_ci return SPECTRE_VULNERABLE; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci#else 328c2ecf20Sopenharmony_cistatic int __maybe_unused spectre_v2_get_cpu_fw_mitigation_state(void) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return SPECTRE_VULNERABLE; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci#endif 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR 398c2ecf20Sopenharmony_ciDEFINE_PER_CPU(harden_branch_predictor_fn_t, harden_branch_predictor_fn); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciextern void cpu_v7_iciallu_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 428c2ecf20Sopenharmony_ciextern void cpu_v7_bpiall_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 438c2ecf20Sopenharmony_ciextern void cpu_v7_smc_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 448c2ecf20Sopenharmony_ciextern void cpu_v7_hvc_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void harden_branch_predictor_bpiall(void) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci write_sysreg(0, BPIALL); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void harden_branch_predictor_iciallu(void) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci write_sysreg(0, ICIALLU); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void __maybe_unused call_smc_arch_workaround_1(void) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void __maybe_unused call_hvc_arch_workaround_1(void) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic unsigned int spectre_v2_install_workaround(unsigned int method) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci const char *spectre_v2_method = NULL; 698c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (per_cpu(harden_branch_predictor_fn, cpu)) 728c2ecf20Sopenharmony_ci return SPECTRE_MITIGATED; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (method) { 758c2ecf20Sopenharmony_ci case SPECTRE_V2_METHOD_BPIALL: 768c2ecf20Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 778c2ecf20Sopenharmony_ci harden_branch_predictor_bpiall; 788c2ecf20Sopenharmony_ci spectre_v2_method = "BPIALL"; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci case SPECTRE_V2_METHOD_ICIALLU: 828c2ecf20Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 838c2ecf20Sopenharmony_ci harden_branch_predictor_iciallu; 848c2ecf20Sopenharmony_ci spectre_v2_method = "ICIALLU"; 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci case SPECTRE_V2_METHOD_HVC: 888c2ecf20Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 898c2ecf20Sopenharmony_ci call_hvc_arch_workaround_1; 908c2ecf20Sopenharmony_ci cpu_do_switch_mm = cpu_v7_hvc_switch_mm; 918c2ecf20Sopenharmony_ci spectre_v2_method = "hypervisor"; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci case SPECTRE_V2_METHOD_SMC: 958c2ecf20Sopenharmony_ci per_cpu(harden_branch_predictor_fn, cpu) = 968c2ecf20Sopenharmony_ci call_smc_arch_workaround_1; 978c2ecf20Sopenharmony_ci cpu_do_switch_mm = cpu_v7_smc_switch_mm; 988c2ecf20Sopenharmony_ci spectre_v2_method = "firmware"; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (spectre_v2_method) 1038c2ecf20Sopenharmony_ci pr_info("CPU%u: Spectre v2: using %s workaround\n", 1048c2ecf20Sopenharmony_ci smp_processor_id(), spectre_v2_method); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return SPECTRE_MITIGATED; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci#else 1098c2ecf20Sopenharmony_cistatic unsigned int spectre_v2_install_workaround(unsigned int method) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci pr_info_once("Spectre V2: workarounds disabled by configuration\n"); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return SPECTRE_VULNERABLE; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci#endif 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void cpu_v7_spectre_v2_init(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci unsigned int state, method = 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci switch (read_cpuid_part()) { 1228c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A8: 1238c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A9: 1248c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A12: 1258c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A17: 1268c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A73: 1278c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A75: 1288c2ecf20Sopenharmony_ci state = SPECTRE_MITIGATED; 1298c2ecf20Sopenharmony_ci method = SPECTRE_V2_METHOD_BPIALL; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A15: 1338c2ecf20Sopenharmony_ci case ARM_CPU_PART_BRAHMA_B15: 1348c2ecf20Sopenharmony_ci state = SPECTRE_MITIGATED; 1358c2ecf20Sopenharmony_ci method = SPECTRE_V2_METHOD_ICIALLU; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci case ARM_CPU_PART_BRAHMA_B53: 1398c2ecf20Sopenharmony_ci /* Requires no workaround */ 1408c2ecf20Sopenharmony_ci state = SPECTRE_UNAFFECTED; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci default: 1448c2ecf20Sopenharmony_ci /* Other ARM CPUs require no workaround */ 1458c2ecf20Sopenharmony_ci if (read_cpuid_implementor() == ARM_CPU_IMP_ARM) { 1468c2ecf20Sopenharmony_ci state = SPECTRE_UNAFFECTED; 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci fallthrough; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Cortex A57/A72 require firmware workaround */ 1538c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A57: 1548c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A72: 1558c2ecf20Sopenharmony_ci state = spectre_v2_get_cpu_fw_mitigation_state(); 1568c2ecf20Sopenharmony_ci if (state != SPECTRE_MITIGATED) 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci switch (arm_smccc_1_1_get_conduit()) { 1608c2ecf20Sopenharmony_ci case SMCCC_CONDUIT_HVC: 1618c2ecf20Sopenharmony_ci method = SPECTRE_V2_METHOD_HVC; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci case SMCCC_CONDUIT_SMC: 1658c2ecf20Sopenharmony_ci method = SPECTRE_V2_METHOD_SMC; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci default: 1698c2ecf20Sopenharmony_ci state = SPECTRE_VULNERABLE; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (state == SPECTRE_MITIGATED) 1758c2ecf20Sopenharmony_ci state = spectre_v2_install_workaround(method); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci spectre_v2_update_state(state, method); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#ifdef CONFIG_HARDEN_BRANCH_HISTORY 1818c2ecf20Sopenharmony_cistatic int spectre_bhb_method; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const char *spectre_bhb_method_name(int method) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci switch (method) { 1868c2ecf20Sopenharmony_ci case SPECTRE_V2_METHOD_LOOP8: 1878c2ecf20Sopenharmony_ci return "loop"; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci case SPECTRE_V2_METHOD_BPIALL: 1908c2ecf20Sopenharmony_ci return "BPIALL"; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci default: 1938c2ecf20Sopenharmony_ci return "unknown"; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int spectre_bhb_install_workaround(int method) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci if (spectre_bhb_method != method) { 2008c2ecf20Sopenharmony_ci if (spectre_bhb_method) { 2018c2ecf20Sopenharmony_ci pr_err("CPU%u: Spectre BHB: method disagreement, system vulnerable\n", 2028c2ecf20Sopenharmony_ci smp_processor_id()); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return SPECTRE_VULNERABLE; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (spectre_bhb_update_vectors(method) == SPECTRE_VULNERABLE) 2088c2ecf20Sopenharmony_ci return SPECTRE_VULNERABLE; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci spectre_bhb_method = method; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci pr_info("CPU%u: Spectre BHB: enabling %s workaround for all CPUs\n", 2138c2ecf20Sopenharmony_ci smp_processor_id(), spectre_bhb_method_name(method)); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return SPECTRE_MITIGATED; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci#else 2198c2ecf20Sopenharmony_cistatic int spectre_bhb_install_workaround(int method) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci return SPECTRE_VULNERABLE; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci#endif 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void cpu_v7_spectre_bhb_init(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci unsigned int state, method = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci switch (read_cpuid_part()) { 2308c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A15: 2318c2ecf20Sopenharmony_ci case ARM_CPU_PART_BRAHMA_B15: 2328c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A57: 2338c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A72: 2348c2ecf20Sopenharmony_ci state = SPECTRE_MITIGATED; 2358c2ecf20Sopenharmony_ci method = SPECTRE_V2_METHOD_LOOP8; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A73: 2398c2ecf20Sopenharmony_ci case ARM_CPU_PART_CORTEX_A75: 2408c2ecf20Sopenharmony_ci state = SPECTRE_MITIGATED; 2418c2ecf20Sopenharmony_ci method = SPECTRE_V2_METHOD_BPIALL; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci default: 2458c2ecf20Sopenharmony_ci state = SPECTRE_UNAFFECTED; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (state == SPECTRE_MITIGATED) 2508c2ecf20Sopenharmony_ci state = spectre_bhb_install_workaround(method); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci spectre_v2_update_state(state, method); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic __maybe_unused bool cpu_v7_check_auxcr_set(bool *warned, 2568c2ecf20Sopenharmony_ci u32 mask, const char *msg) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci u32 aux_cr; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (aux_cr)); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if ((aux_cr & mask) != mask) { 2638c2ecf20Sopenharmony_ci if (!*warned) 2648c2ecf20Sopenharmony_ci pr_err("CPU%u: %s", smp_processor_id(), msg); 2658c2ecf20Sopenharmony_ci *warned = true; 2668c2ecf20Sopenharmony_ci return false; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci return true; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(bool, spectre_warned); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic bool check_spectre_auxcr(bool *warned, u32 bit) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci return IS_ENABLED(CONFIG_HARDEN_BRANCH_PREDICTOR) && 2768c2ecf20Sopenharmony_ci cpu_v7_check_auxcr_set(warned, bit, 2778c2ecf20Sopenharmony_ci "Spectre v2: firmware did not set auxiliary control register IBE bit, system vulnerable\n"); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_civoid cpu_v7_ca8_ibe(void) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(6))) 2838c2ecf20Sopenharmony_ci cpu_v7_spectre_v2_init(); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_civoid cpu_v7_ca15_ibe(void) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(0))) 2898c2ecf20Sopenharmony_ci cpu_v7_spectre_v2_init(); 2908c2ecf20Sopenharmony_ci cpu_v7_spectre_bhb_init(); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_civoid cpu_v7_bugs_init(void) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci cpu_v7_spectre_v2_init(); 2968c2ecf20Sopenharmony_ci cpu_v7_spectre_bhb_init(); 2978c2ecf20Sopenharmony_ci} 298