162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Handle detection, reporting and mitigation of Spectre v1, v2, v3a and v4, as 462306a36Sopenharmony_ci * detailed at: 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This code was originally written hastily under an awful lot of stress and so 962306a36Sopenharmony_ci * aspects of it are somewhat hacky. Unfortunately, changing anything in here 1062306a36Sopenharmony_ci * instantly makes me feel ill. Thanks, Jann. Thann. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (C) 2018 ARM Ltd, All Rights Reserved. 1362306a36Sopenharmony_ci * Copyright (C) 2020 Google LLC 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * "If there's something strange in your neighbourhood, who you gonna call?" 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Authors: Will Deacon <will@kernel.org> and Marc Zyngier <maz@kernel.org> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/arm-smccc.h> 2162306a36Sopenharmony_ci#include <linux/bpf.h> 2262306a36Sopenharmony_ci#include <linux/cpu.h> 2362306a36Sopenharmony_ci#include <linux/device.h> 2462306a36Sopenharmony_ci#include <linux/nospec.h> 2562306a36Sopenharmony_ci#include <linux/prctl.h> 2662306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/debug-monitors.h> 2962306a36Sopenharmony_ci#include <asm/insn.h> 3062306a36Sopenharmony_ci#include <asm/spectre.h> 3162306a36Sopenharmony_ci#include <asm/traps.h> 3262306a36Sopenharmony_ci#include <asm/vectors.h> 3362306a36Sopenharmony_ci#include <asm/virt.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * We try to ensure that the mitigation state can never change as the result of 3762306a36Sopenharmony_ci * onlining a late CPU. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic void update_mitigation_state(enum mitigation_state *oldp, 4062306a36Sopenharmony_ci enum mitigation_state new) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci enum mitigation_state state; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci do { 4562306a36Sopenharmony_ci state = READ_ONCE(*oldp); 4662306a36Sopenharmony_ci if (new <= state) 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Userspace almost certainly can't deal with this. */ 5062306a36Sopenharmony_ci if (WARN_ON(system_capabilities_finalized())) 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } while (cmpxchg_relaxed(oldp, state, new) != state); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * Spectre v1. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * The kernel can't protect userspace for this one: it's each person for 5962306a36Sopenharmony_ci * themselves. Advertise what we're doing and be done with it. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cissize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, 6262306a36Sopenharmony_ci char *buf) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci return sprintf(buf, "Mitigation: __user pointer sanitization\n"); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * Spectre v2. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * This one sucks. A CPU is either: 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * - Mitigated in hardware and advertised by ID_AA64PFR0_EL1.CSV2. 7362306a36Sopenharmony_ci * - Mitigated in hardware and listed in our "safe list". 7462306a36Sopenharmony_ci * - Mitigated in software by firmware. 7562306a36Sopenharmony_ci * - Mitigated in software by a CPU-specific dance in the kernel and a 7662306a36Sopenharmony_ci * firmware call at EL2. 7762306a36Sopenharmony_ci * - Vulnerable. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * It's not unlikely for different CPUs in a big.LITTLE system to fall into 8062306a36Sopenharmony_ci * different camps. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic enum mitigation_state spectre_v2_state; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic bool __read_mostly __nospectre_v2; 8562306a36Sopenharmony_cistatic int __init parse_spectre_v2_param(char *str) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci __nospectre_v2 = true; 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciearly_param("nospectre_v2", parse_spectre_v2_param); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic bool spectre_v2_mitigations_off(void) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci bool ret = __nospectre_v2 || cpu_mitigations_off(); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ret) 9762306a36Sopenharmony_ci pr_info_once("spectre-v2 mitigation disabled by command line option\n"); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const char *get_bhb_affected_string(enum mitigation_state bhb_state) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci switch (bhb_state) { 10562306a36Sopenharmony_ci case SPECTRE_UNAFFECTED: 10662306a36Sopenharmony_ci return ""; 10762306a36Sopenharmony_ci default: 10862306a36Sopenharmony_ci case SPECTRE_VULNERABLE: 10962306a36Sopenharmony_ci return ", but not BHB"; 11062306a36Sopenharmony_ci case SPECTRE_MITIGATED: 11162306a36Sopenharmony_ci return ", BHB"; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic bool _unprivileged_ebpf_enabled(void) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 11862306a36Sopenharmony_ci return !sysctl_unprivileged_bpf_disabled; 11962306a36Sopenharmony_ci#else 12062306a36Sopenharmony_ci return false; 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cissize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, 12562306a36Sopenharmony_ci char *buf) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci enum mitigation_state bhb_state = arm64_get_spectre_bhb_state(); 12862306a36Sopenharmony_ci const char *bhb_str = get_bhb_affected_string(bhb_state); 12962306a36Sopenharmony_ci const char *v2_str = "Branch predictor hardening"; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci switch (spectre_v2_state) { 13262306a36Sopenharmony_ci case SPECTRE_UNAFFECTED: 13362306a36Sopenharmony_ci if (bhb_state == SPECTRE_UNAFFECTED) 13462306a36Sopenharmony_ci return sprintf(buf, "Not affected\n"); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Platforms affected by Spectre-BHB can't report 13862306a36Sopenharmony_ci * "Not affected" for Spectre-v2. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci v2_str = "CSV2"; 14162306a36Sopenharmony_ci fallthrough; 14262306a36Sopenharmony_ci case SPECTRE_MITIGATED: 14362306a36Sopenharmony_ci if (bhb_state == SPECTRE_MITIGATED && _unprivileged_ebpf_enabled()) 14462306a36Sopenharmony_ci return sprintf(buf, "Vulnerable: Unprivileged eBPF enabled\n"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return sprintf(buf, "Mitigation: %s%s\n", v2_str, bhb_str); 14762306a36Sopenharmony_ci case SPECTRE_VULNERABLE: 14862306a36Sopenharmony_ci fallthrough; 14962306a36Sopenharmony_ci default: 15062306a36Sopenharmony_ci return sprintf(buf, "Vulnerable\n"); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic enum mitigation_state spectre_v2_get_cpu_hw_mitigation_state(void) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci u64 pfr0; 15762306a36Sopenharmony_ci static const struct midr_range spectre_v2_safe_list[] = { 15862306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A35), 15962306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A53), 16062306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), 16162306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), 16262306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), 16362306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_2XX_SILVER), 16462306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER), 16562306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER), 16662306a36Sopenharmony_ci { /* sentinel */ } 16762306a36Sopenharmony_ci }; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* If the CPU has CSV2 set, we're safe */ 17062306a36Sopenharmony_ci pfr0 = read_cpuid(ID_AA64PFR0_EL1); 17162306a36Sopenharmony_ci if (cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL1_CSV2_SHIFT)) 17262306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Alternatively, we have a list of unaffected CPUs */ 17562306a36Sopenharmony_ci if (is_midr_in_range_list(read_cpuid_id(), spectre_v2_safe_list)) 17662306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic enum mitigation_state spectre_v2_get_cpu_fw_mitigation_state(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci struct arm_smccc_res res; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 18762306a36Sopenharmony_ci ARM_SMCCC_ARCH_WORKAROUND_1, &res); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ret = res.a0; 19062306a36Sopenharmony_ci switch (ret) { 19162306a36Sopenharmony_ci case SMCCC_RET_SUCCESS: 19262306a36Sopenharmony_ci return SPECTRE_MITIGATED; 19362306a36Sopenharmony_ci case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED: 19462306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 19562306a36Sopenharmony_ci default: 19662306a36Sopenharmony_ci fallthrough; 19762306a36Sopenharmony_ci case SMCCC_RET_NOT_SUPPORTED: 19862306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cibool has_spectre_v2(const struct arm64_cpu_capabilities *entry, int scope) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible()); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (spectre_v2_get_cpu_hw_mitigation_state() == SPECTRE_UNAFFECTED) 20762306a36Sopenharmony_ci return false; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (spectre_v2_get_cpu_fw_mitigation_state() == SPECTRE_UNAFFECTED) 21062306a36Sopenharmony_ci return false; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return true; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cienum mitigation_state arm64_get_spectre_v2_state(void) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci return spectre_v2_state; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciDEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void install_bp_hardening_cb(bp_hardening_cb_t fn) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci __this_cpu_write(bp_hardening_data.fn, fn); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Vinz Clortho takes the hyp_vecs start/end "keys" at 22862306a36Sopenharmony_ci * the door when we're a guest. Skip the hyp-vectors work. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if (!is_hyp_mode_available()) 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci __this_cpu_write(bp_hardening_data.slot, HYP_VECTOR_SPECTRE_DIRECT); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* Called during entry so must be noinstr */ 23762306a36Sopenharmony_cistatic noinstr void call_smc_arch_workaround_1(void) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* Called during entry so must be noinstr */ 24362306a36Sopenharmony_cistatic noinstr void call_hvc_arch_workaround_1(void) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* Called during entry so must be noinstr */ 24962306a36Sopenharmony_cistatic noinstr void qcom_link_stack_sanitisation(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci u64 tmp; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci asm volatile("mov %0, x30 \n" 25462306a36Sopenharmony_ci ".rept 16 \n" 25562306a36Sopenharmony_ci "bl . + 4 \n" 25662306a36Sopenharmony_ci ".endr \n" 25762306a36Sopenharmony_ci "mov x30, %0 \n" 25862306a36Sopenharmony_ci : "=&r" (tmp)); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic bp_hardening_cb_t spectre_v2_get_sw_mitigation_cb(void) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci u32 midr = read_cpuid_id(); 26462306a36Sopenharmony_ci if (((midr & MIDR_CPU_MODEL_MASK) != MIDR_QCOM_FALKOR) && 26562306a36Sopenharmony_ci ((midr & MIDR_CPU_MODEL_MASK) != MIDR_QCOM_FALKOR_V1)) 26662306a36Sopenharmony_ci return NULL; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return qcom_link_stack_sanitisation; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic enum mitigation_state spectre_v2_enable_fw_mitigation(void) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci bp_hardening_cb_t cb; 27462306a36Sopenharmony_ci enum mitigation_state state; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci state = spectre_v2_get_cpu_fw_mitigation_state(); 27762306a36Sopenharmony_ci if (state != SPECTRE_MITIGATED) 27862306a36Sopenharmony_ci return state; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (spectre_v2_mitigations_off()) 28162306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci switch (arm_smccc_1_1_get_conduit()) { 28462306a36Sopenharmony_ci case SMCCC_CONDUIT_HVC: 28562306a36Sopenharmony_ci cb = call_hvc_arch_workaround_1; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci case SMCCC_CONDUIT_SMC: 28962306a36Sopenharmony_ci cb = call_smc_arch_workaround_1; 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * Prefer a CPU-specific workaround if it exists. Note that we 29862306a36Sopenharmony_ci * still rely on firmware for the mitigation at EL2. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci cb = spectre_v2_get_sw_mitigation_cb() ?: cb; 30162306a36Sopenharmony_ci install_bp_hardening_cb(cb); 30262306a36Sopenharmony_ci return SPECTRE_MITIGATED; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_civoid spectre_v2_enable_mitigation(const struct arm64_cpu_capabilities *__unused) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci enum mitigation_state state; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci WARN_ON(preemptible()); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci state = spectre_v2_get_cpu_hw_mitigation_state(); 31262306a36Sopenharmony_ci if (state == SPECTRE_VULNERABLE) 31362306a36Sopenharmony_ci state = spectre_v2_enable_fw_mitigation(); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci update_mitigation_state(&spectre_v2_state, state); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * Spectre-v3a. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * Phew, there's not an awful lot to do here! We just instruct EL2 to use 32262306a36Sopenharmony_ci * an indirect trampoline for the hyp vectors so that guests can't read 32362306a36Sopenharmony_ci * VBAR_EL2 to defeat randomisation of the hypervisor VA layout. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_cibool has_spectre_v3a(const struct arm64_cpu_capabilities *entry, int scope) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci static const struct midr_range spectre_v3a_unsafe_list[] = { 32862306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), 32962306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), 33062306a36Sopenharmony_ci {}, 33162306a36Sopenharmony_ci }; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible()); 33462306a36Sopenharmony_ci return is_midr_in_range_list(read_cpuid_id(), spectre_v3a_unsafe_list); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_civoid spectre_v3a_enable_mitigation(const struct arm64_cpu_capabilities *__unused) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct bp_hardening_data *data = this_cpu_ptr(&bp_hardening_data); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (this_cpu_has_cap(ARM64_SPECTRE_V3A)) 34262306a36Sopenharmony_ci data->slot += HYP_VECTOR_INDIRECT; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * Spectre v4. 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * If you thought Spectre v2 was nasty, wait until you see this mess. A CPU is 34962306a36Sopenharmony_ci * either: 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * - Mitigated in hardware and listed in our "safe list". 35262306a36Sopenharmony_ci * - Mitigated in hardware via PSTATE.SSBS. 35362306a36Sopenharmony_ci * - Mitigated in software by firmware (sometimes referred to as SSBD). 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * Wait, that doesn't sound so bad, does it? Keep reading... 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * A major source of headaches is that the software mitigation is enabled both 35862306a36Sopenharmony_ci * on a per-task basis, but can also be forced on for the kernel, necessitating 35962306a36Sopenharmony_ci * both context-switch *and* entry/exit hooks. To make it even worse, some CPUs 36062306a36Sopenharmony_ci * allow EL0 to toggle SSBS directly, which can end up with the prctl() state 36162306a36Sopenharmony_ci * being stale when re-entering the kernel. The usual big.LITTLE caveats apply, 36262306a36Sopenharmony_ci * so you can have systems that have both firmware and SSBS mitigations. This 36362306a36Sopenharmony_ci * means we actually have to reject late onlining of CPUs with mitigations if 36462306a36Sopenharmony_ci * all of the currently onlined CPUs are safelisted, as the mitigation tends to 36562306a36Sopenharmony_ci * be opt-in for userspace. Yes, really, the cure is worse than the disease. 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * The only good part is that if the firmware mitigation is present, then it is 36862306a36Sopenharmony_ci * present for all CPUs, meaning we don't have to worry about late onlining of a 36962306a36Sopenharmony_ci * vulnerable CPU if one of the boot CPUs is using the firmware mitigation. 37062306a36Sopenharmony_ci * 37162306a36Sopenharmony_ci * Give me a VAX-11/780 any day of the week... 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic enum mitigation_state spectre_v4_state; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* This is the per-cpu state tracking whether we need to talk to firmware */ 37662306a36Sopenharmony_ciDEFINE_PER_CPU_READ_MOSTLY(u64, arm64_ssbd_callback_required); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cienum spectre_v4_policy { 37962306a36Sopenharmony_ci SPECTRE_V4_POLICY_MITIGATION_DYNAMIC, 38062306a36Sopenharmony_ci SPECTRE_V4_POLICY_MITIGATION_ENABLED, 38162306a36Sopenharmony_ci SPECTRE_V4_POLICY_MITIGATION_DISABLED, 38262306a36Sopenharmony_ci}; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic enum spectre_v4_policy __read_mostly __spectre_v4_policy; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic const struct spectre_v4_param { 38762306a36Sopenharmony_ci const char *str; 38862306a36Sopenharmony_ci enum spectre_v4_policy policy; 38962306a36Sopenharmony_ci} spectre_v4_params[] = { 39062306a36Sopenharmony_ci { "force-on", SPECTRE_V4_POLICY_MITIGATION_ENABLED, }, 39162306a36Sopenharmony_ci { "force-off", SPECTRE_V4_POLICY_MITIGATION_DISABLED, }, 39262306a36Sopenharmony_ci { "kernel", SPECTRE_V4_POLICY_MITIGATION_DYNAMIC, }, 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_cistatic int __init parse_spectre_v4_param(char *str) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int i; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (!str || !str[0]) 39962306a36Sopenharmony_ci return -EINVAL; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(spectre_v4_params); i++) { 40262306a36Sopenharmony_ci const struct spectre_v4_param *param = &spectre_v4_params[i]; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (strncmp(str, param->str, strlen(param->str))) 40562306a36Sopenharmony_ci continue; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci __spectre_v4_policy = param->policy; 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return -EINVAL; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ciearly_param("ssbd", parse_spectre_v4_param); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/* 41662306a36Sopenharmony_ci * Because this was all written in a rush by people working in different silos, 41762306a36Sopenharmony_ci * we've ended up with multiple command line options to control the same thing. 41862306a36Sopenharmony_ci * Wrap these up in some helpers, which prefer disabling the mitigation if faced 41962306a36Sopenharmony_ci * with contradictory parameters. The mitigation is always either "off", 42062306a36Sopenharmony_ci * "dynamic" or "on". 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistatic bool spectre_v4_mitigations_off(void) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci bool ret = cpu_mitigations_off() || 42562306a36Sopenharmony_ci __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_DISABLED; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (ret) 42862306a36Sopenharmony_ci pr_info_once("spectre-v4 mitigation disabled by command-line option\n"); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* Do we need to toggle the mitigation state on entry to/exit from the kernel? */ 43462306a36Sopenharmony_cistatic bool spectre_v4_mitigations_dynamic(void) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci return !spectre_v4_mitigations_off() && 43762306a36Sopenharmony_ci __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_DYNAMIC; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic bool spectre_v4_mitigations_on(void) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci return !spectre_v4_mitigations_off() && 44362306a36Sopenharmony_ci __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_ENABLED; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cissize_t cpu_show_spec_store_bypass(struct device *dev, 44762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci switch (spectre_v4_state) { 45062306a36Sopenharmony_ci case SPECTRE_UNAFFECTED: 45162306a36Sopenharmony_ci return sprintf(buf, "Not affected\n"); 45262306a36Sopenharmony_ci case SPECTRE_MITIGATED: 45362306a36Sopenharmony_ci return sprintf(buf, "Mitigation: Speculative Store Bypass disabled via prctl\n"); 45462306a36Sopenharmony_ci case SPECTRE_VULNERABLE: 45562306a36Sopenharmony_ci fallthrough; 45662306a36Sopenharmony_ci default: 45762306a36Sopenharmony_ci return sprintf(buf, "Vulnerable\n"); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cienum mitigation_state arm64_get_spectre_v4_state(void) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci return spectre_v4_state; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic enum mitigation_state spectre_v4_get_cpu_hw_mitigation_state(void) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci static const struct midr_range spectre_v4_safe_list[] = { 46962306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A35), 47062306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A53), 47162306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), 47262306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), 47362306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER), 47462306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER), 47562306a36Sopenharmony_ci { /* sentinel */ }, 47662306a36Sopenharmony_ci }; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (is_midr_in_range_list(read_cpuid_id(), spectre_v4_safe_list)) 47962306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* CPU features are detected first */ 48262306a36Sopenharmony_ci if (this_cpu_has_cap(ARM64_SSBS)) 48362306a36Sopenharmony_ci return SPECTRE_MITIGATED; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic enum mitigation_state spectre_v4_get_cpu_fw_mitigation_state(void) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int ret; 49162306a36Sopenharmony_ci struct arm_smccc_res res; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 49462306a36Sopenharmony_ci ARM_SMCCC_ARCH_WORKAROUND_2, &res); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = res.a0; 49762306a36Sopenharmony_ci switch (ret) { 49862306a36Sopenharmony_ci case SMCCC_RET_SUCCESS: 49962306a36Sopenharmony_ci return SPECTRE_MITIGATED; 50062306a36Sopenharmony_ci case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED: 50162306a36Sopenharmony_ci fallthrough; 50262306a36Sopenharmony_ci case SMCCC_RET_NOT_REQUIRED: 50362306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 50462306a36Sopenharmony_ci default: 50562306a36Sopenharmony_ci fallthrough; 50662306a36Sopenharmony_ci case SMCCC_RET_NOT_SUPPORTED: 50762306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cibool has_spectre_v4(const struct arm64_cpu_capabilities *cap, int scope) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci enum mitigation_state state; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible()); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci state = spectre_v4_get_cpu_hw_mitigation_state(); 51862306a36Sopenharmony_ci if (state == SPECTRE_VULNERABLE) 51962306a36Sopenharmony_ci state = spectre_v4_get_cpu_fw_mitigation_state(); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return state != SPECTRE_UNAFFECTED; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cibool try_emulate_el1_ssbs(struct pt_regs *regs, u32 instr) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci const u32 instr_mask = ~(1U << PSTATE_Imm_shift); 52762306a36Sopenharmony_ci const u32 instr_val = 0xd500401f | PSTATE_SSBS; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if ((instr & instr_mask) != instr_val) 53062306a36Sopenharmony_ci return false; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (instr & BIT(PSTATE_Imm_shift)) 53362306a36Sopenharmony_ci regs->pstate |= PSR_SSBS_BIT; 53462306a36Sopenharmony_ci else 53562306a36Sopenharmony_ci regs->pstate &= ~PSR_SSBS_BIT; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci arm64_skip_faulting_instruction(regs, 4); 53862306a36Sopenharmony_ci return true; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic enum mitigation_state spectre_v4_enable_hw_mitigation(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci enum mitigation_state state; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * If the system is mitigated but this CPU doesn't have SSBS, then 54762306a36Sopenharmony_ci * we must be on the safelist and there's nothing more to do. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci state = spectre_v4_get_cpu_hw_mitigation_state(); 55062306a36Sopenharmony_ci if (state != SPECTRE_MITIGATED || !this_cpu_has_cap(ARM64_SSBS)) 55162306a36Sopenharmony_ci return state; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (spectre_v4_mitigations_off()) { 55462306a36Sopenharmony_ci sysreg_clear_set(sctlr_el1, 0, SCTLR_ELx_DSSBS); 55562306a36Sopenharmony_ci set_pstate_ssbs(1); 55662306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* SCTLR_EL1.DSSBS was initialised to 0 during boot */ 56062306a36Sopenharmony_ci set_pstate_ssbs(0); 56162306a36Sopenharmony_ci return SPECTRE_MITIGATED; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* 56562306a36Sopenharmony_ci * Patch a branch over the Spectre-v4 mitigation code with a NOP so that 56662306a36Sopenharmony_ci * we fallthrough and check whether firmware needs to be called on this CPU. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_civoid __init spectre_v4_patch_fw_mitigation_enable(struct alt_instr *alt, 56962306a36Sopenharmony_ci __le32 *origptr, 57062306a36Sopenharmony_ci __le32 *updptr, int nr_inst) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci BUG_ON(nr_inst != 1); /* Branch -> NOP */ 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (spectre_v4_mitigations_off()) 57562306a36Sopenharmony_ci return; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (cpus_have_cap(ARM64_SSBS)) 57862306a36Sopenharmony_ci return; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (spectre_v4_mitigations_dynamic()) 58162306a36Sopenharmony_ci *updptr = cpu_to_le32(aarch64_insn_gen_nop()); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* 58562306a36Sopenharmony_ci * Patch a NOP in the Spectre-v4 mitigation code with an SMC/HVC instruction 58662306a36Sopenharmony_ci * to call into firmware to adjust the mitigation state. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_civoid __init smccc_patch_fw_mitigation_conduit(struct alt_instr *alt, 58962306a36Sopenharmony_ci __le32 *origptr, 59062306a36Sopenharmony_ci __le32 *updptr, int nr_inst) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci u32 insn; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci BUG_ON(nr_inst != 1); /* NOP -> HVC/SMC */ 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (arm_smccc_1_1_get_conduit()) { 59762306a36Sopenharmony_ci case SMCCC_CONDUIT_HVC: 59862306a36Sopenharmony_ci insn = aarch64_insn_get_hvc_value(); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case SMCCC_CONDUIT_SMC: 60162306a36Sopenharmony_ci insn = aarch64_insn_get_smc_value(); 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci return; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci *updptr = cpu_to_le32(insn); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic enum mitigation_state spectre_v4_enable_fw_mitigation(void) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci enum mitigation_state state; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci state = spectre_v4_get_cpu_fw_mitigation_state(); 61562306a36Sopenharmony_ci if (state != SPECTRE_MITIGATED) 61662306a36Sopenharmony_ci return state; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (spectre_v4_mitigations_off()) { 61962306a36Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_WORKAROUND_2, false, NULL); 62062306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_WORKAROUND_2, true, NULL); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (spectre_v4_mitigations_dynamic()) 62662306a36Sopenharmony_ci __this_cpu_write(arm64_ssbd_callback_required, 1); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return SPECTRE_MITIGATED; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_civoid spectre_v4_enable_mitigation(const struct arm64_cpu_capabilities *__unused) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci enum mitigation_state state; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci WARN_ON(preemptible()); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci state = spectre_v4_enable_hw_mitigation(); 63862306a36Sopenharmony_ci if (state == SPECTRE_VULNERABLE) 63962306a36Sopenharmony_ci state = spectre_v4_enable_fw_mitigation(); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci update_mitigation_state(&spectre_v4_state, state); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic void __update_pstate_ssbs(struct pt_regs *regs, bool state) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci u64 bit = compat_user_mode(regs) ? PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (state) 64962306a36Sopenharmony_ci regs->pstate |= bit; 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci regs->pstate &= ~bit; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_civoid spectre_v4_enable_task_mitigation(struct task_struct *tsk) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct pt_regs *regs = task_pt_regs(tsk); 65762306a36Sopenharmony_ci bool ssbs = false, kthread = tsk->flags & PF_KTHREAD; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (spectre_v4_mitigations_off()) 66062306a36Sopenharmony_ci ssbs = true; 66162306a36Sopenharmony_ci else if (spectre_v4_mitigations_dynamic() && !kthread) 66262306a36Sopenharmony_ci ssbs = !test_tsk_thread_flag(tsk, TIF_SSBD); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci __update_pstate_ssbs(regs, ssbs); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * The Spectre-v4 mitigation can be controlled via a prctl() from userspace. 66962306a36Sopenharmony_ci * This is interesting because the "speculation disabled" behaviour can be 67062306a36Sopenharmony_ci * configured so that it is preserved across exec(), which means that the 67162306a36Sopenharmony_ci * prctl() may be necessary even when PSTATE.SSBS can be toggled directly 67262306a36Sopenharmony_ci * from userspace. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_cistatic void ssbd_prctl_enable_mitigation(struct task_struct *task) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci task_clear_spec_ssb_noexec(task); 67762306a36Sopenharmony_ci task_set_spec_ssb_disable(task); 67862306a36Sopenharmony_ci set_tsk_thread_flag(task, TIF_SSBD); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void ssbd_prctl_disable_mitigation(struct task_struct *task) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci task_clear_spec_ssb_noexec(task); 68462306a36Sopenharmony_ci task_clear_spec_ssb_disable(task); 68562306a36Sopenharmony_ci clear_tsk_thread_flag(task, TIF_SSBD); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci switch (ctrl) { 69162306a36Sopenharmony_ci case PR_SPEC_ENABLE: 69262306a36Sopenharmony_ci /* Enable speculation: disable mitigation */ 69362306a36Sopenharmony_ci /* 69462306a36Sopenharmony_ci * Force disabled speculation prevents it from being 69562306a36Sopenharmony_ci * re-enabled. 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci if (task_spec_ssb_force_disable(task)) 69862306a36Sopenharmony_ci return -EPERM; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * If the mitigation is forced on, then speculation is forced 70262306a36Sopenharmony_ci * off and we again prevent it from being re-enabled. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci if (spectre_v4_mitigations_on()) 70562306a36Sopenharmony_ci return -EPERM; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ssbd_prctl_disable_mitigation(task); 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case PR_SPEC_FORCE_DISABLE: 71062306a36Sopenharmony_ci /* Force disable speculation: force enable mitigation */ 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * If the mitigation is forced off, then speculation is forced 71362306a36Sopenharmony_ci * on and we prevent it from being disabled. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci if (spectre_v4_mitigations_off()) 71662306a36Sopenharmony_ci return -EPERM; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci task_set_spec_ssb_force_disable(task); 71962306a36Sopenharmony_ci fallthrough; 72062306a36Sopenharmony_ci case PR_SPEC_DISABLE: 72162306a36Sopenharmony_ci /* Disable speculation: enable mitigation */ 72262306a36Sopenharmony_ci /* Same as PR_SPEC_FORCE_DISABLE */ 72362306a36Sopenharmony_ci if (spectre_v4_mitigations_off()) 72462306a36Sopenharmony_ci return -EPERM; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ssbd_prctl_enable_mitigation(task); 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci case PR_SPEC_DISABLE_NOEXEC: 72962306a36Sopenharmony_ci /* Disable speculation until execve(): enable mitigation */ 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci * If the mitigation state is forced one way or the other, then 73262306a36Sopenharmony_ci * we must fail now before we try to toggle it on execve(). 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (task_spec_ssb_force_disable(task) || 73562306a36Sopenharmony_ci spectre_v4_mitigations_off() || 73662306a36Sopenharmony_ci spectre_v4_mitigations_on()) { 73762306a36Sopenharmony_ci return -EPERM; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci ssbd_prctl_enable_mitigation(task); 74162306a36Sopenharmony_ci task_set_spec_ssb_noexec(task); 74262306a36Sopenharmony_ci break; 74362306a36Sopenharmony_ci default: 74462306a36Sopenharmony_ci return -ERANGE; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci spectre_v4_enable_task_mitigation(task); 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ciint arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, 75262306a36Sopenharmony_ci unsigned long ctrl) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci switch (which) { 75562306a36Sopenharmony_ci case PR_SPEC_STORE_BYPASS: 75662306a36Sopenharmony_ci return ssbd_prctl_set(task, ctrl); 75762306a36Sopenharmony_ci default: 75862306a36Sopenharmony_ci return -ENODEV; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int ssbd_prctl_get(struct task_struct *task) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci switch (spectre_v4_state) { 76562306a36Sopenharmony_ci case SPECTRE_UNAFFECTED: 76662306a36Sopenharmony_ci return PR_SPEC_NOT_AFFECTED; 76762306a36Sopenharmony_ci case SPECTRE_MITIGATED: 76862306a36Sopenharmony_ci if (spectre_v4_mitigations_on()) 76962306a36Sopenharmony_ci return PR_SPEC_NOT_AFFECTED; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (spectre_v4_mitigations_dynamic()) 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Mitigations are disabled, so we're vulnerable. */ 77562306a36Sopenharmony_ci fallthrough; 77662306a36Sopenharmony_ci case SPECTRE_VULNERABLE: 77762306a36Sopenharmony_ci fallthrough; 77862306a36Sopenharmony_ci default: 77962306a36Sopenharmony_ci return PR_SPEC_ENABLE; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* Check the mitigation state for this task */ 78362306a36Sopenharmony_ci if (task_spec_ssb_force_disable(task)) 78462306a36Sopenharmony_ci return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (task_spec_ssb_noexec(task)) 78762306a36Sopenharmony_ci return PR_SPEC_PRCTL | PR_SPEC_DISABLE_NOEXEC; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (task_spec_ssb_disable(task)) 79062306a36Sopenharmony_ci return PR_SPEC_PRCTL | PR_SPEC_DISABLE; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return PR_SPEC_PRCTL | PR_SPEC_ENABLE; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ciint arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci switch (which) { 79862306a36Sopenharmony_ci case PR_SPEC_STORE_BYPASS: 79962306a36Sopenharmony_ci return ssbd_prctl_get(task); 80062306a36Sopenharmony_ci default: 80162306a36Sopenharmony_ci return -ENODEV; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/* 80662306a36Sopenharmony_ci * Spectre BHB. 80762306a36Sopenharmony_ci * 80862306a36Sopenharmony_ci * A CPU is either: 80962306a36Sopenharmony_ci * - Mitigated by a branchy loop a CPU specific number of times, and listed 81062306a36Sopenharmony_ci * in our "loop mitigated list". 81162306a36Sopenharmony_ci * - Mitigated in software by the firmware Spectre v2 call. 81262306a36Sopenharmony_ci * - Has the ClearBHB instruction to perform the mitigation. 81362306a36Sopenharmony_ci * - Has the 'Exception Clears Branch History Buffer' (ECBHB) feature, so no 81462306a36Sopenharmony_ci * software mitigation in the vectors is needed. 81562306a36Sopenharmony_ci * - Has CSV2.3, so is unaffected. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_cistatic enum mitigation_state spectre_bhb_state; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cienum mitigation_state arm64_get_spectre_bhb_state(void) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci return spectre_bhb_state; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cienum bhb_mitigation_bits { 82562306a36Sopenharmony_ci BHB_LOOP, 82662306a36Sopenharmony_ci BHB_FW, 82762306a36Sopenharmony_ci BHB_HW, 82862306a36Sopenharmony_ci BHB_INSN, 82962306a36Sopenharmony_ci}; 83062306a36Sopenharmony_cistatic unsigned long system_bhb_mitigations; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci/* 83362306a36Sopenharmony_ci * This must be called with SCOPE_LOCAL_CPU for each type of CPU, before any 83462306a36Sopenharmony_ci * SCOPE_SYSTEM call will give the right answer. 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_ciu8 spectre_bhb_loop_affected(int scope) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci u8 k = 0; 83962306a36Sopenharmony_ci static u8 max_bhb_k; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (scope == SCOPE_LOCAL_CPU) { 84262306a36Sopenharmony_ci static const struct midr_range spectre_bhb_k32_list[] = { 84362306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A78), 84462306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A78AE), 84562306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A78C), 84662306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_X1), 84762306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A710), 84862306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_X2), 84962306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), 85062306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), 85162306a36Sopenharmony_ci {}, 85262306a36Sopenharmony_ci }; 85362306a36Sopenharmony_ci static const struct midr_range spectre_bhb_k24_list[] = { 85462306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A76), 85562306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A77), 85662306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), 85762306a36Sopenharmony_ci {}, 85862306a36Sopenharmony_ci }; 85962306a36Sopenharmony_ci static const struct midr_range spectre_bhb_k11_list[] = { 86062306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_AMPERE1), 86162306a36Sopenharmony_ci {}, 86262306a36Sopenharmony_ci }; 86362306a36Sopenharmony_ci static const struct midr_range spectre_bhb_k8_list[] = { 86462306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), 86562306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), 86662306a36Sopenharmony_ci {}, 86762306a36Sopenharmony_ci }; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k32_list)) 87062306a36Sopenharmony_ci k = 32; 87162306a36Sopenharmony_ci else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k24_list)) 87262306a36Sopenharmony_ci k = 24; 87362306a36Sopenharmony_ci else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k11_list)) 87462306a36Sopenharmony_ci k = 11; 87562306a36Sopenharmony_ci else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k8_list)) 87662306a36Sopenharmony_ci k = 8; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci max_bhb_k = max(max_bhb_k, k); 87962306a36Sopenharmony_ci } else { 88062306a36Sopenharmony_ci k = max_bhb_k; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return k; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic enum mitigation_state spectre_bhb_get_cpu_fw_mitigation_state(void) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci int ret; 88962306a36Sopenharmony_ci struct arm_smccc_res res; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, 89262306a36Sopenharmony_ci ARM_SMCCC_ARCH_WORKAROUND_3, &res); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = res.a0; 89562306a36Sopenharmony_ci switch (ret) { 89662306a36Sopenharmony_ci case SMCCC_RET_SUCCESS: 89762306a36Sopenharmony_ci return SPECTRE_MITIGATED; 89862306a36Sopenharmony_ci case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED: 89962306a36Sopenharmony_ci return SPECTRE_UNAFFECTED; 90062306a36Sopenharmony_ci default: 90162306a36Sopenharmony_ci fallthrough; 90262306a36Sopenharmony_ci case SMCCC_RET_NOT_SUPPORTED: 90362306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic bool is_spectre_bhb_fw_affected(int scope) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci static bool system_affected; 91062306a36Sopenharmony_ci enum mitigation_state fw_state; 91162306a36Sopenharmony_ci bool has_smccc = arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_NONE; 91262306a36Sopenharmony_ci static const struct midr_range spectre_bhb_firmware_mitigated_list[] = { 91362306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), 91462306a36Sopenharmony_ci MIDR_ALL_VERSIONS(MIDR_CORTEX_A75), 91562306a36Sopenharmony_ci {}, 91662306a36Sopenharmony_ci }; 91762306a36Sopenharmony_ci bool cpu_in_list = is_midr_in_range_list(read_cpuid_id(), 91862306a36Sopenharmony_ci spectre_bhb_firmware_mitigated_list); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (scope != SCOPE_LOCAL_CPU) 92162306a36Sopenharmony_ci return system_affected; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci fw_state = spectre_bhb_get_cpu_fw_mitigation_state(); 92462306a36Sopenharmony_ci if (cpu_in_list || (has_smccc && fw_state == SPECTRE_MITIGATED)) { 92562306a36Sopenharmony_ci system_affected = true; 92662306a36Sopenharmony_ci return true; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return false; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic bool supports_ecbhb(int scope) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u64 mmfr1; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (scope == SCOPE_LOCAL_CPU) 93762306a36Sopenharmony_ci mmfr1 = read_sysreg_s(SYS_ID_AA64MMFR1_EL1); 93862306a36Sopenharmony_ci else 93962306a36Sopenharmony_ci mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci return cpuid_feature_extract_unsigned_field(mmfr1, 94262306a36Sopenharmony_ci ID_AA64MMFR1_EL1_ECBHB_SHIFT); 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cibool is_spectre_bhb_affected(const struct arm64_cpu_capabilities *entry, 94662306a36Sopenharmony_ci int scope) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible()); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (supports_csv2p3(scope)) 95162306a36Sopenharmony_ci return false; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (supports_clearbhb(scope)) 95462306a36Sopenharmony_ci return true; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (spectre_bhb_loop_affected(scope)) 95762306a36Sopenharmony_ci return true; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (is_spectre_bhb_fw_affected(scope)) 96062306a36Sopenharmony_ci return true; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return false; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic void this_cpu_set_vectors(enum arm64_bp_harden_el1_vectors slot) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci const char *v = arm64_get_bp_hardening_vector(slot); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci __this_cpu_write(this_cpu_vector, v); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * When KPTI is in use, the vectors are switched when exiting to 97362306a36Sopenharmony_ci * user-space. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_ci if (arm64_kernel_unmapped_at_el0()) 97662306a36Sopenharmony_ci return; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci write_sysreg(v, vbar_el1); 97962306a36Sopenharmony_ci isb(); 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic bool __read_mostly __nospectre_bhb; 98362306a36Sopenharmony_cistatic int __init parse_spectre_bhb_param(char *str) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci __nospectre_bhb = true; 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ciearly_param("nospectre_bhb", parse_spectre_bhb_param); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_civoid spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci bp_hardening_cb_t cpu_cb; 99362306a36Sopenharmony_ci enum mitigation_state fw_state, state = SPECTRE_VULNERABLE; 99462306a36Sopenharmony_ci struct bp_hardening_data *data = this_cpu_ptr(&bp_hardening_data); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (!is_spectre_bhb_affected(entry, SCOPE_LOCAL_CPU)) 99762306a36Sopenharmony_ci return; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (arm64_get_spectre_v2_state() == SPECTRE_VULNERABLE) { 100062306a36Sopenharmony_ci /* No point mitigating Spectre-BHB alone. */ 100162306a36Sopenharmony_ci } else if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY)) { 100262306a36Sopenharmony_ci pr_info_once("spectre-bhb mitigation disabled by compile time option\n"); 100362306a36Sopenharmony_ci } else if (cpu_mitigations_off() || __nospectre_bhb) { 100462306a36Sopenharmony_ci pr_info_once("spectre-bhb mitigation disabled by command line option\n"); 100562306a36Sopenharmony_ci } else if (supports_ecbhb(SCOPE_LOCAL_CPU)) { 100662306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 100762306a36Sopenharmony_ci set_bit(BHB_HW, &system_bhb_mitigations); 100862306a36Sopenharmony_ci } else if (supports_clearbhb(SCOPE_LOCAL_CPU)) { 100962306a36Sopenharmony_ci /* 101062306a36Sopenharmony_ci * Ensure KVM uses the indirect vector which will have ClearBHB 101162306a36Sopenharmony_ci * added. 101262306a36Sopenharmony_ci */ 101362306a36Sopenharmony_ci if (!data->slot) 101462306a36Sopenharmony_ci data->slot = HYP_VECTOR_INDIRECT; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci this_cpu_set_vectors(EL1_VECTOR_BHB_CLEAR_INSN); 101762306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 101862306a36Sopenharmony_ci set_bit(BHB_INSN, &system_bhb_mitigations); 101962306a36Sopenharmony_ci } else if (spectre_bhb_loop_affected(SCOPE_LOCAL_CPU)) { 102062306a36Sopenharmony_ci /* 102162306a36Sopenharmony_ci * Ensure KVM uses the indirect vector which will have the 102262306a36Sopenharmony_ci * branchy-loop added. A57/A72-r0 will already have selected 102362306a36Sopenharmony_ci * the spectre-indirect vector, which is sufficient for BHB 102462306a36Sopenharmony_ci * too. 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ci if (!data->slot) 102762306a36Sopenharmony_ci data->slot = HYP_VECTOR_INDIRECT; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci this_cpu_set_vectors(EL1_VECTOR_BHB_LOOP); 103062306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 103162306a36Sopenharmony_ci set_bit(BHB_LOOP, &system_bhb_mitigations); 103262306a36Sopenharmony_ci } else if (is_spectre_bhb_fw_affected(SCOPE_LOCAL_CPU)) { 103362306a36Sopenharmony_ci fw_state = spectre_bhb_get_cpu_fw_mitigation_state(); 103462306a36Sopenharmony_ci if (fw_state == SPECTRE_MITIGATED) { 103562306a36Sopenharmony_ci /* 103662306a36Sopenharmony_ci * Ensure KVM uses one of the spectre bp_hardening 103762306a36Sopenharmony_ci * vectors. The indirect vector doesn't include the EL3 103862306a36Sopenharmony_ci * call, so needs upgrading to 103962306a36Sopenharmony_ci * HYP_VECTOR_SPECTRE_INDIRECT. 104062306a36Sopenharmony_ci */ 104162306a36Sopenharmony_ci if (!data->slot || data->slot == HYP_VECTOR_INDIRECT) 104262306a36Sopenharmony_ci data->slot += 1; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci this_cpu_set_vectors(EL1_VECTOR_BHB_FW); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* 104762306a36Sopenharmony_ci * The WA3 call in the vectors supersedes the WA1 call 104862306a36Sopenharmony_ci * made during context-switch. Uninstall any firmware 104962306a36Sopenharmony_ci * bp_hardening callback. 105062306a36Sopenharmony_ci */ 105162306a36Sopenharmony_ci cpu_cb = spectre_v2_get_sw_mitigation_cb(); 105262306a36Sopenharmony_ci if (__this_cpu_read(bp_hardening_data.fn) != cpu_cb) 105362306a36Sopenharmony_ci __this_cpu_write(bp_hardening_data.fn, NULL); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci state = SPECTRE_MITIGATED; 105662306a36Sopenharmony_ci set_bit(BHB_FW, &system_bhb_mitigations); 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci update_mitigation_state(&spectre_bhb_state, state); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci/* Patched to NOP when enabled */ 106462306a36Sopenharmony_civoid noinstr spectre_bhb_patch_loop_mitigation_enable(struct alt_instr *alt, 106562306a36Sopenharmony_ci __le32 *origptr, 106662306a36Sopenharmony_ci __le32 *updptr, int nr_inst) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci BUG_ON(nr_inst != 1); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (test_bit(BHB_LOOP, &system_bhb_mitigations)) 107162306a36Sopenharmony_ci *updptr++ = cpu_to_le32(aarch64_insn_gen_nop()); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci/* Patched to NOP when enabled */ 107562306a36Sopenharmony_civoid noinstr spectre_bhb_patch_fw_mitigation_enabled(struct alt_instr *alt, 107662306a36Sopenharmony_ci __le32 *origptr, 107762306a36Sopenharmony_ci __le32 *updptr, int nr_inst) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci BUG_ON(nr_inst != 1); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (test_bit(BHB_FW, &system_bhb_mitigations)) 108262306a36Sopenharmony_ci *updptr++ = cpu_to_le32(aarch64_insn_gen_nop()); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci/* Patched to correct the immediate */ 108662306a36Sopenharmony_civoid noinstr spectre_bhb_patch_loop_iter(struct alt_instr *alt, 108762306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci u8 rd; 109062306a36Sopenharmony_ci u32 insn; 109162306a36Sopenharmony_ci u16 loop_count = spectre_bhb_loop_affected(SCOPE_SYSTEM); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci BUG_ON(nr_inst != 1); /* MOV -> MOV */ 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY)) 109662306a36Sopenharmony_ci return; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci insn = le32_to_cpu(*origptr); 109962306a36Sopenharmony_ci rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, insn); 110062306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(rd, loop_count, 0, 110162306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 110262306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_ZERO); 110362306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci/* Patched to mov WA3 when supported */ 110762306a36Sopenharmony_civoid noinstr spectre_bhb_patch_wa3(struct alt_instr *alt, 110862306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci u8 rd; 111162306a36Sopenharmony_ci u32 insn; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci BUG_ON(nr_inst != 1); /* MOV -> MOV */ 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY) || 111662306a36Sopenharmony_ci !test_bit(BHB_FW, &system_bhb_mitigations)) 111762306a36Sopenharmony_ci return; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci insn = le32_to_cpu(*origptr); 112062306a36Sopenharmony_ci rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, insn); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_ORR, 112362306a36Sopenharmony_ci AARCH64_INSN_VARIANT_32BIT, 112462306a36Sopenharmony_ci AARCH64_INSN_REG_ZR, rd, 112562306a36Sopenharmony_ci ARM_SMCCC_ARCH_WORKAROUND_3); 112662306a36Sopenharmony_ci if (WARN_ON_ONCE(insn == AARCH64_BREAK_FAULT)) 112762306a36Sopenharmony_ci return; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci/* Patched to NOP when not supported */ 113362306a36Sopenharmony_civoid __init spectre_bhb_patch_clearbhb(struct alt_instr *alt, 113462306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci BUG_ON(nr_inst != 2); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (test_bit(BHB_INSN, &system_bhb_mitigations)) 113962306a36Sopenharmony_ci return; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci *updptr++ = cpu_to_le32(aarch64_insn_gen_nop()); 114262306a36Sopenharmony_ci *updptr++ = cpu_to_le32(aarch64_insn_gen_nop()); 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 114662306a36Sopenharmony_ci#define EBPF_WARN "Unprivileged eBPF is enabled, data leaks possible via Spectre v2 BHB attacks!\n" 114762306a36Sopenharmony_civoid unpriv_ebpf_notify(int new_state) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci if (spectre_v2_state == SPECTRE_VULNERABLE || 115062306a36Sopenharmony_ci spectre_bhb_state != SPECTRE_MITIGATED) 115162306a36Sopenharmony_ci return; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (!new_state) 115462306a36Sopenharmony_ci pr_err("WARNING: %s", EBPF_WARN); 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci#endif 1157