| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Handle detection, reporting and mitigation of Spectre v1, v2, v3a and v4, as
- * detailed at:
- *
- * https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
- *
- * This code was originally written hastily under an awful lot of stress and so
- * aspects of it are somewhat hacky. Unfortunately, changing anything in here
- * instantly makes me feel ill. Thanks, Jann. Thann.
- *
- * Copyright (C) 2018 ARM Ltd, All Rights Reserved.
- * Copyright (C) 2020 Google LLC
- *
- * "If there's something strange in your neighbourhood, who you gonna call?"
- *
- * Authors: Will Deacon <will@kernel.org> and Marc Zyngier <maz@kernel.org>
- */
- #include <linux/arm-smccc.h>
- #include <linux/bpf.h>
- #include <linux/cpu.h>
- #include <linux/device.h>
- #include <linux/nospec.h>
- #include <linux/prctl.h>
- #include <linux/sched/task_stack.h>
- #include <asm/debug-monitors.h>
- #include <asm/insn.h>
- #include <asm/spectre.h>
- #include <asm/traps.h>
- #include <asm/vectors.h>
- #include <asm/virt.h>
- /*
- * We try to ensure that the mitigation state can never change as the result of
- * onlining a late CPU.
- */
- static void update_mitigation_state(enum mitigation_state *oldp,
- enum mitigation_state new)
- {
- enum mitigation_state state;
- do {
- state = READ_ONCE(*oldp);
- if (new <= state)
- break;
- /* Userspace almost certainly can't deal with this. */
- if (WARN_ON(system_capabilities_finalized()))
- break;
- } while (cmpxchg_relaxed(oldp, state, new) != state);
- }
- /*
- * Spectre v1.
- *
- * The kernel can't protect userspace for this one: it's each person for
- * themselves. Advertise what we're doing and be done with it.
- */
- ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- return sprintf(buf, "Mitigation: __user pointer sanitization\n");
- }
- /*
- * Spectre v2.
- *
- * This one sucks. A CPU is either:
- *
- * - Mitigated in hardware and advertised by ID_AA64PFR0_EL1.CSV2.
- * - Mitigated in hardware and listed in our "safe list".
- * - Mitigated in software by firmware.
- * - Mitigated in software by a CPU-specific dance in the kernel and a
- * firmware call at EL2.
- * - Vulnerable.
- *
- * It's not unlikely for different CPUs in a big.LITTLE system to fall into
- * different camps.
- */
- static enum mitigation_state spectre_v2_state;
- static bool __read_mostly __nospectre_v2;
- static int __init parse_spectre_v2_param(char *str)
- {
- __nospectre_v2 = true;
- return 0;
- }
- early_param("nospectre_v2", parse_spectre_v2_param);
- static bool spectre_v2_mitigations_off(void)
- {
- bool ret = __nospectre_v2 || cpu_mitigations_off();
- if (ret)
- pr_info_once("spectre-v2 mitigation disabled by command line option\n");
- return ret;
- }
- static const char *get_bhb_affected_string(enum mitigation_state bhb_state)
- {
- switch (bhb_state) {
- case SPECTRE_UNAFFECTED:
- return "";
- default:
- case SPECTRE_VULNERABLE:
- return ", but not BHB";
- case SPECTRE_MITIGATED:
- return ", BHB";
- }
- }
- static bool _unprivileged_ebpf_enabled(void)
- {
- #ifdef CONFIG_BPF_SYSCALL
- return !sysctl_unprivileged_bpf_disabled;
- #else
- return false;
- #endif
- }
- ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- enum mitigation_state bhb_state = arm64_get_spectre_bhb_state();
- const char *bhb_str = get_bhb_affected_string(bhb_state);
- const char *v2_str = "Branch predictor hardening";
- switch (spectre_v2_state) {
- case SPECTRE_UNAFFECTED:
- if (bhb_state == SPECTRE_UNAFFECTED)
- return sprintf(buf, "Not affected\n");
- /*
- * Platforms affected by Spectre-BHB can't report
- * "Not affected" for Spectre-v2.
- */
- v2_str = "CSV2";
- fallthrough;
- case SPECTRE_MITIGATED:
- if (bhb_state == SPECTRE_MITIGATED && _unprivileged_ebpf_enabled())
- return sprintf(buf, "Vulnerable: Unprivileged eBPF enabled\n");
- return sprintf(buf, "Mitigation: %s%s\n", v2_str, bhb_str);
- case SPECTRE_VULNERABLE:
- fallthrough;
- default:
- return sprintf(buf, "Vulnerable\n");
- }
- }
- static enum mitigation_state spectre_v2_get_cpu_hw_mitigation_state(void)
- {
- u64 pfr0;
- static const struct midr_range spectre_v2_safe_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A35),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A53),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A55),
- MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53),
- MIDR_ALL_VERSIONS(MIDR_HISI_TSV110),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_2XX_SILVER),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER),
- { /* sentinel */ }
- };
- /* If the CPU has CSV2 set, we're safe */
- pfr0 = read_cpuid(ID_AA64PFR0_EL1);
- if (cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL1_CSV2_SHIFT))
- return SPECTRE_UNAFFECTED;
- /* Alternatively, we have a list of unaffected CPUs */
- if (is_midr_in_range_list(read_cpuid_id(), spectre_v2_safe_list))
- return SPECTRE_UNAFFECTED;
- return SPECTRE_VULNERABLE;
- }
- static enum mitigation_state spectre_v2_get_cpu_fw_mitigation_state(void)
- {
- int ret;
- struct arm_smccc_res res;
- arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
- ARM_SMCCC_ARCH_WORKAROUND_1, &res);
- ret = res.a0;
- switch (ret) {
- case SMCCC_RET_SUCCESS:
- return SPECTRE_MITIGATED;
- case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED:
- return SPECTRE_UNAFFECTED;
- default:
- fallthrough;
- case SMCCC_RET_NOT_SUPPORTED:
- return SPECTRE_VULNERABLE;
- }
- }
- bool has_spectre_v2(const struct arm64_cpu_capabilities *entry, int scope)
- {
- WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
- if (spectre_v2_get_cpu_hw_mitigation_state() == SPECTRE_UNAFFECTED)
- return false;
- if (spectre_v2_get_cpu_fw_mitigation_state() == SPECTRE_UNAFFECTED)
- return false;
- return true;
- }
- enum mitigation_state arm64_get_spectre_v2_state(void)
- {
- return spectre_v2_state;
- }
- DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data);
- static void install_bp_hardening_cb(bp_hardening_cb_t fn)
- {
- __this_cpu_write(bp_hardening_data.fn, fn);
- /*
- * Vinz Clortho takes the hyp_vecs start/end "keys" at
- * the door when we're a guest. Skip the hyp-vectors work.
- */
- if (!is_hyp_mode_available())
- return;
- __this_cpu_write(bp_hardening_data.slot, HYP_VECTOR_SPECTRE_DIRECT);
- }
- /* Called during entry so must be noinstr */
- static noinstr void call_smc_arch_workaround_1(void)
- {
- arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
- }
- /* Called during entry so must be noinstr */
- static noinstr void call_hvc_arch_workaround_1(void)
- {
- arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
- }
- /* Called during entry so must be noinstr */
- static noinstr void qcom_link_stack_sanitisation(void)
- {
- u64 tmp;
- asm volatile("mov %0, x30 \n"
- ".rept 16 \n"
- "bl . + 4 \n"
- ".endr \n"
- "mov x30, %0 \n"
- : "=&r" (tmp));
- }
- static bp_hardening_cb_t spectre_v2_get_sw_mitigation_cb(void)
- {
- u32 midr = read_cpuid_id();
- if (((midr & MIDR_CPU_MODEL_MASK) != MIDR_QCOM_FALKOR) &&
- ((midr & MIDR_CPU_MODEL_MASK) != MIDR_QCOM_FALKOR_V1))
- return NULL;
- return qcom_link_stack_sanitisation;
- }
- static enum mitigation_state spectre_v2_enable_fw_mitigation(void)
- {
- bp_hardening_cb_t cb;
- enum mitigation_state state;
- state = spectre_v2_get_cpu_fw_mitigation_state();
- if (state != SPECTRE_MITIGATED)
- return state;
- if (spectre_v2_mitigations_off())
- return SPECTRE_VULNERABLE;
- switch (arm_smccc_1_1_get_conduit()) {
- case SMCCC_CONDUIT_HVC:
- cb = call_hvc_arch_workaround_1;
- break;
- case SMCCC_CONDUIT_SMC:
- cb = call_smc_arch_workaround_1;
- break;
- default:
- return SPECTRE_VULNERABLE;
- }
- /*
- * Prefer a CPU-specific workaround if it exists. Note that we
- * still rely on firmware for the mitigation at EL2.
- */
- cb = spectre_v2_get_sw_mitigation_cb() ?: cb;
- install_bp_hardening_cb(cb);
- return SPECTRE_MITIGATED;
- }
- void spectre_v2_enable_mitigation(const struct arm64_cpu_capabilities *__unused)
- {
- enum mitigation_state state;
- WARN_ON(preemptible());
- state = spectre_v2_get_cpu_hw_mitigation_state();
- if (state == SPECTRE_VULNERABLE)
- state = spectre_v2_enable_fw_mitigation();
- update_mitigation_state(&spectre_v2_state, state);
- }
- /*
- * Spectre-v3a.
- *
- * Phew, there's not an awful lot to do here! We just instruct EL2 to use
- * an indirect trampoline for the hyp vectors so that guests can't read
- * VBAR_EL2 to defeat randomisation of the hypervisor VA layout.
- */
- bool has_spectre_v3a(const struct arm64_cpu_capabilities *entry, int scope)
- {
- static const struct midr_range spectre_v3a_unsafe_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
- {},
- };
- WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
- return is_midr_in_range_list(read_cpuid_id(), spectre_v3a_unsafe_list);
- }
- void spectre_v3a_enable_mitigation(const struct arm64_cpu_capabilities *__unused)
- {
- struct bp_hardening_data *data = this_cpu_ptr(&bp_hardening_data);
- if (this_cpu_has_cap(ARM64_SPECTRE_V3A))
- data->slot += HYP_VECTOR_INDIRECT;
- }
- /*
- * Spectre v4.
- *
- * If you thought Spectre v2 was nasty, wait until you see this mess. A CPU is
- * either:
- *
- * - Mitigated in hardware and listed in our "safe list".
- * - Mitigated in hardware via PSTATE.SSBS.
- * - Mitigated in software by firmware (sometimes referred to as SSBD).
- *
- * Wait, that doesn't sound so bad, does it? Keep reading...
- *
- * A major source of headaches is that the software mitigation is enabled both
- * on a per-task basis, but can also be forced on for the kernel, necessitating
- * both context-switch *and* entry/exit hooks. To make it even worse, some CPUs
- * allow EL0 to toggle SSBS directly, which can end up with the prctl() state
- * being stale when re-entering the kernel. The usual big.LITTLE caveats apply,
- * so you can have systems that have both firmware and SSBS mitigations. This
- * means we actually have to reject late onlining of CPUs with mitigations if
- * all of the currently onlined CPUs are safelisted, as the mitigation tends to
- * be opt-in for userspace. Yes, really, the cure is worse than the disease.
- *
- * The only good part is that if the firmware mitigation is present, then it is
- * present for all CPUs, meaning we don't have to worry about late onlining of a
- * vulnerable CPU if one of the boot CPUs is using the firmware mitigation.
- *
- * Give me a VAX-11/780 any day of the week...
- */
- static enum mitigation_state spectre_v4_state;
- /* This is the per-cpu state tracking whether we need to talk to firmware */
- DEFINE_PER_CPU_READ_MOSTLY(u64, arm64_ssbd_callback_required);
- enum spectre_v4_policy {
- SPECTRE_V4_POLICY_MITIGATION_DYNAMIC,
- SPECTRE_V4_POLICY_MITIGATION_ENABLED,
- SPECTRE_V4_POLICY_MITIGATION_DISABLED,
- };
- static enum spectre_v4_policy __read_mostly __spectre_v4_policy;
- static const struct spectre_v4_param {
- const char *str;
- enum spectre_v4_policy policy;
- } spectre_v4_params[] = {
- { "force-on", SPECTRE_V4_POLICY_MITIGATION_ENABLED, },
- { "force-off", SPECTRE_V4_POLICY_MITIGATION_DISABLED, },
- { "kernel", SPECTRE_V4_POLICY_MITIGATION_DYNAMIC, },
- };
- static int __init parse_spectre_v4_param(char *str)
- {
- int i;
- if (!str || !str[0])
- return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(spectre_v4_params); i++) {
- const struct spectre_v4_param *param = &spectre_v4_params[i];
- if (strncmp(str, param->str, strlen(param->str)))
- continue;
- __spectre_v4_policy = param->policy;
- return 0;
- }
- return -EINVAL;
- }
- early_param("ssbd", parse_spectre_v4_param);
- /*
- * Because this was all written in a rush by people working in different silos,
- * we've ended up with multiple command line options to control the same thing.
- * Wrap these up in some helpers, which prefer disabling the mitigation if faced
- * with contradictory parameters. The mitigation is always either "off",
- * "dynamic" or "on".
- */
- static bool spectre_v4_mitigations_off(void)
- {
- bool ret = cpu_mitigations_off() ||
- __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_DISABLED;
- if (ret)
- pr_info_once("spectre-v4 mitigation disabled by command-line option\n");
- return ret;
- }
- /* Do we need to toggle the mitigation state on entry to/exit from the kernel? */
- static bool spectre_v4_mitigations_dynamic(void)
- {
- return !spectre_v4_mitigations_off() &&
- __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_DYNAMIC;
- }
- static bool spectre_v4_mitigations_on(void)
- {
- return !spectre_v4_mitigations_off() &&
- __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_ENABLED;
- }
- ssize_t cpu_show_spec_store_bypass(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- switch (spectre_v4_state) {
- case SPECTRE_UNAFFECTED:
- return sprintf(buf, "Not affected\n");
- case SPECTRE_MITIGATED:
- return sprintf(buf, "Mitigation: Speculative Store Bypass disabled via prctl\n");
- case SPECTRE_VULNERABLE:
- fallthrough;
- default:
- return sprintf(buf, "Vulnerable\n");
- }
- }
- enum mitigation_state arm64_get_spectre_v4_state(void)
- {
- return spectre_v4_state;
- }
- static enum mitigation_state spectre_v4_get_cpu_hw_mitigation_state(void)
- {
- static const struct midr_range spectre_v4_safe_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A35),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A53),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A55),
- MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER),
- { /* sentinel */ },
- };
- if (is_midr_in_range_list(read_cpuid_id(), spectre_v4_safe_list))
- return SPECTRE_UNAFFECTED;
- /* CPU features are detected first */
- if (this_cpu_has_cap(ARM64_SSBS))
- return SPECTRE_MITIGATED;
- return SPECTRE_VULNERABLE;
- }
- static enum mitigation_state spectre_v4_get_cpu_fw_mitigation_state(void)
- {
- int ret;
- struct arm_smccc_res res;
- arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
- ARM_SMCCC_ARCH_WORKAROUND_2, &res);
- ret = res.a0;
- switch (ret) {
- case SMCCC_RET_SUCCESS:
- return SPECTRE_MITIGATED;
- case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED:
- fallthrough;
- case SMCCC_RET_NOT_REQUIRED:
- return SPECTRE_UNAFFECTED;
- default:
- fallthrough;
- case SMCCC_RET_NOT_SUPPORTED:
- return SPECTRE_VULNERABLE;
- }
- }
- bool has_spectre_v4(const struct arm64_cpu_capabilities *cap, int scope)
- {
- enum mitigation_state state;
- WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
- state = spectre_v4_get_cpu_hw_mitigation_state();
- if (state == SPECTRE_VULNERABLE)
- state = spectre_v4_get_cpu_fw_mitigation_state();
- return state != SPECTRE_UNAFFECTED;
- }
- bool try_emulate_el1_ssbs(struct pt_regs *regs, u32 instr)
- {
- const u32 instr_mask = ~(1U << PSTATE_Imm_shift);
- const u32 instr_val = 0xd500401f | PSTATE_SSBS;
- if ((instr & instr_mask) != instr_val)
- return false;
- if (instr & BIT(PSTATE_Imm_shift))
- regs->pstate |= PSR_SSBS_BIT;
- else
- regs->pstate &= ~PSR_SSBS_BIT;
- arm64_skip_faulting_instruction(regs, 4);
- return true;
- }
- static enum mitigation_state spectre_v4_enable_hw_mitigation(void)
- {
- enum mitigation_state state;
- /*
- * If the system is mitigated but this CPU doesn't have SSBS, then
- * we must be on the safelist and there's nothing more to do.
- */
- state = spectre_v4_get_cpu_hw_mitigation_state();
- if (state != SPECTRE_MITIGATED || !this_cpu_has_cap(ARM64_SSBS))
- return state;
- if (spectre_v4_mitigations_off()) {
- sysreg_clear_set(sctlr_el1, 0, SCTLR_ELx_DSSBS);
- set_pstate_ssbs(1);
- return SPECTRE_VULNERABLE;
- }
- /* SCTLR_EL1.DSSBS was initialised to 0 during boot */
- set_pstate_ssbs(0);
- /*
- * SSBS is self-synchronizing and is intended to affect subsequent
- * speculative instructions, but some CPUs can speculate with a stale
- * value of SSBS.
- *
- * Mitigate this with an unconditional speculation barrier, as CPUs
- * could mis-speculate branches and bypass a conditional barrier.
- */
- if (IS_ENABLED(CONFIG_ARM64_ERRATUM_3194386))
- spec_bar();
- return SPECTRE_MITIGATED;
- }
- /*
- * Patch a branch over the Spectre-v4 mitigation code with a NOP so that
- * we fallthrough and check whether firmware needs to be called on this CPU.
- */
- void __init spectre_v4_patch_fw_mitigation_enable(struct alt_instr *alt,
- __le32 *origptr,
- __le32 *updptr, int nr_inst)
- {
- BUG_ON(nr_inst != 1); /* Branch -> NOP */
- if (spectre_v4_mitigations_off())
- return;
- if (cpus_have_cap(ARM64_SSBS))
- return;
- if (spectre_v4_mitigations_dynamic())
- *updptr = cpu_to_le32(aarch64_insn_gen_nop());
- }
- /*
- * Patch a NOP in the Spectre-v4 mitigation code with an SMC/HVC instruction
- * to call into firmware to adjust the mitigation state.
- */
- void __init smccc_patch_fw_mitigation_conduit(struct alt_instr *alt,
- __le32 *origptr,
- __le32 *updptr, int nr_inst)
- {
- u32 insn;
- BUG_ON(nr_inst != 1); /* NOP -> HVC/SMC */
- switch (arm_smccc_1_1_get_conduit()) {
- case SMCCC_CONDUIT_HVC:
- insn = aarch64_insn_get_hvc_value();
- break;
- case SMCCC_CONDUIT_SMC:
- insn = aarch64_insn_get_smc_value();
- break;
- default:
- return;
- }
- *updptr = cpu_to_le32(insn);
- }
- static enum mitigation_state spectre_v4_enable_fw_mitigation(void)
- {
- enum mitigation_state state;
- state = spectre_v4_get_cpu_fw_mitigation_state();
- if (state != SPECTRE_MITIGATED)
- return state;
- if (spectre_v4_mitigations_off()) {
- arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_WORKAROUND_2, false, NULL);
- return SPECTRE_VULNERABLE;
- }
- arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_WORKAROUND_2, true, NULL);
- if (spectre_v4_mitigations_dynamic())
- __this_cpu_write(arm64_ssbd_callback_required, 1);
- return SPECTRE_MITIGATED;
- }
- void spectre_v4_enable_mitigation(const struct arm64_cpu_capabilities *__unused)
- {
- enum mitigation_state state;
- WARN_ON(preemptible());
- state = spectre_v4_enable_hw_mitigation();
- if (state == SPECTRE_VULNERABLE)
- state = spectre_v4_enable_fw_mitigation();
- update_mitigation_state(&spectre_v4_state, state);
- }
- static void __update_pstate_ssbs(struct pt_regs *regs, bool state)
- {
- u64 bit = compat_user_mode(regs) ? PSR_AA32_SSBS_BIT : PSR_SSBS_BIT;
- if (state)
- regs->pstate |= bit;
- else
- regs->pstate &= ~bit;
- }
- void spectre_v4_enable_task_mitigation(struct task_struct *tsk)
- {
- struct pt_regs *regs = task_pt_regs(tsk);
- bool ssbs = false, kthread = tsk->flags & PF_KTHREAD;
- if (spectre_v4_mitigations_off())
- ssbs = true;
- else if (spectre_v4_mitigations_dynamic() && !kthread)
- ssbs = !test_tsk_thread_flag(tsk, TIF_SSBD);
- __update_pstate_ssbs(regs, ssbs);
- }
- /*
- * The Spectre-v4 mitigation can be controlled via a prctl() from userspace.
- * This is interesting because the "speculation disabled" behaviour can be
- * configured so that it is preserved across exec(), which means that the
- * prctl() may be necessary even when PSTATE.SSBS can be toggled directly
- * from userspace.
- */
- static void ssbd_prctl_enable_mitigation(struct task_struct *task)
- {
- task_clear_spec_ssb_noexec(task);
- task_set_spec_ssb_disable(task);
- set_tsk_thread_flag(task, TIF_SSBD);
- }
- static void ssbd_prctl_disable_mitigation(struct task_struct *task)
- {
- task_clear_spec_ssb_noexec(task);
- task_clear_spec_ssb_disable(task);
- clear_tsk_thread_flag(task, TIF_SSBD);
- }
- static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl)
- {
- switch (ctrl) {
- case PR_SPEC_ENABLE:
- /* Enable speculation: disable mitigation */
- /*
- * Force disabled speculation prevents it from being
- * re-enabled.
- */
- if (task_spec_ssb_force_disable(task))
- return -EPERM;
- /*
- * If the mitigation is forced on, then speculation is forced
- * off and we again prevent it from being re-enabled.
- */
- if (spectre_v4_mitigations_on())
- return -EPERM;
- ssbd_prctl_disable_mitigation(task);
- break;
- case PR_SPEC_FORCE_DISABLE:
- /* Force disable speculation: force enable mitigation */
- /*
- * If the mitigation is forced off, then speculation is forced
- * on and we prevent it from being disabled.
- */
- if (spectre_v4_mitigations_off())
- return -EPERM;
- task_set_spec_ssb_force_disable(task);
- fallthrough;
- case PR_SPEC_DISABLE:
- /* Disable speculation: enable mitigation */
- /* Same as PR_SPEC_FORCE_DISABLE */
- if (spectre_v4_mitigations_off())
- return -EPERM;
- ssbd_prctl_enable_mitigation(task);
- break;
- case PR_SPEC_DISABLE_NOEXEC:
- /* Disable speculation until execve(): enable mitigation */
- /*
- * If the mitigation state is forced one way or the other, then
- * we must fail now before we try to toggle it on execve().
- */
- if (task_spec_ssb_force_disable(task) ||
- spectre_v4_mitigations_off() ||
- spectre_v4_mitigations_on()) {
- return -EPERM;
- }
- ssbd_prctl_enable_mitigation(task);
- task_set_spec_ssb_noexec(task);
- break;
- default:
- return -ERANGE;
- }
- spectre_v4_enable_task_mitigation(task);
- return 0;
- }
- int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
- unsigned long ctrl)
- {
- switch (which) {
- case PR_SPEC_STORE_BYPASS:
- return ssbd_prctl_set(task, ctrl);
- default:
- return -ENODEV;
- }
- }
- static int ssbd_prctl_get(struct task_struct *task)
- {
- switch (spectre_v4_state) {
- case SPECTRE_UNAFFECTED:
- return PR_SPEC_NOT_AFFECTED;
- case SPECTRE_MITIGATED:
- if (spectre_v4_mitigations_on())
- return PR_SPEC_NOT_AFFECTED;
- if (spectre_v4_mitigations_dynamic())
- break;
- /* Mitigations are disabled, so we're vulnerable. */
- fallthrough;
- case SPECTRE_VULNERABLE:
- fallthrough;
- default:
- return PR_SPEC_ENABLE;
- }
- /* Check the mitigation state for this task */
- if (task_spec_ssb_force_disable(task))
- return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
- if (task_spec_ssb_noexec(task))
- return PR_SPEC_PRCTL | PR_SPEC_DISABLE_NOEXEC;
- if (task_spec_ssb_disable(task))
- return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
- return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
- }
- int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which)
- {
- switch (which) {
- case PR_SPEC_STORE_BYPASS:
- return ssbd_prctl_get(task);
- default:
- return -ENODEV;
- }
- }
- /*
- * Spectre BHB.
- *
- * A CPU is either:
- * - Mitigated by a branchy loop a CPU specific number of times, and listed
- * in our "loop mitigated list".
- * - Mitigated in software by the firmware Spectre v2 call.
- * - Has the ClearBHB instruction to perform the mitigation.
- * - Has the 'Exception Clears Branch History Buffer' (ECBHB) feature, so no
- * software mitigation in the vectors is needed.
- * - Has CSV2.3, so is unaffected.
- */
- static enum mitigation_state spectre_bhb_state;
- enum mitigation_state arm64_get_spectre_bhb_state(void)
- {
- return spectre_bhb_state;
- }
- enum bhb_mitigation_bits {
- BHB_LOOP,
- BHB_FW,
- BHB_HW,
- BHB_INSN,
- };
- static unsigned long system_bhb_mitigations;
- /*
- * This must be called with SCOPE_LOCAL_CPU for each type of CPU, before any
- * SCOPE_SYSTEM call will give the right answer.
- */
- static bool is_spectre_bhb_safe(int scope)
- {
- static const struct midr_range spectre_bhb_safe_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A35),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A53),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A55),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A510),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A520),
- MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_2XX_SILVER),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER),
- {},
- };
- static bool all_safe = true;
- if (scope != SCOPE_LOCAL_CPU)
- return all_safe;
- if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_safe_list))
- return true;
- all_safe = false;
- return false;
- }
- static u8 spectre_bhb_loop_affected(void)
- {
- u8 k = 0;
- static const struct midr_range spectre_bhb_k132_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_X3),
- MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2),
- {},
- };
- static const struct midr_range spectre_bhb_k38_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A715),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A720),
- {},
- };
- static const struct midr_range spectre_bhb_k32_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A78),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A78AE),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A78C),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_X1),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_X1C),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A710),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_X2),
- MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2),
- MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1),
- {},
- };
- static const struct midr_range spectre_bhb_k24_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A76),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A76AE),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A77),
- MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1),
- MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_GOLD),
- MIDR_ALL_VERSIONS(MIDR_HISI_HIP09),
- {},
- };
- static const struct midr_range spectre_bhb_k11_list[] = {
- MIDR_ALL_VERSIONS(MIDR_AMPERE1),
- {},
- };
- static const struct midr_range spectre_bhb_k8_list[] = {
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
- MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
- {},
- };
- if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k132_list))
- k = 132;
- else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k38_list))
- k = 38;
- else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k32_list))
- k = 32;
- else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k24_list))
- k = 24;
- else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k11_list))
- k = 11;
- else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k8_list))
- k = 8;
- return k;
- }
- static enum mitigation_state spectre_bhb_get_cpu_fw_mitigation_state(void)
- {
- int ret;
- struct arm_smccc_res res;
- arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
- ARM_SMCCC_ARCH_WORKAROUND_3, &res);
- ret = res.a0;
- switch (ret) {
- case SMCCC_RET_SUCCESS:
- return SPECTRE_MITIGATED;
- case SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED:
- return SPECTRE_UNAFFECTED;
- default:
- fallthrough;
- case SMCCC_RET_NOT_SUPPORTED:
- return SPECTRE_VULNERABLE;
- }
- }
- static bool has_spectre_bhb_fw_mitigation(void)
- {
- enum mitigation_state fw_state;
- bool has_smccc = arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_NONE;
- fw_state = spectre_bhb_get_cpu_fw_mitigation_state();
- return has_smccc && fw_state == SPECTRE_MITIGATED;
- }
- static bool supports_ecbhb(int scope)
- {
- u64 mmfr1;
- if (scope == SCOPE_LOCAL_CPU)
- mmfr1 = read_sysreg_s(SYS_ID_AA64MMFR1_EL1);
- else
- mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
- return cpuid_feature_extract_unsigned_field(mmfr1,
- ID_AA64MMFR1_EL1_ECBHB_SHIFT);
- }
- static u8 max_bhb_k;
- bool is_spectre_bhb_affected(const struct arm64_cpu_capabilities *entry,
- int scope)
- {
- WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
- if (supports_csv2p3(scope))
- return false;
- if (is_spectre_bhb_safe(scope))
- return false;
- /*
- * At this point the core isn't known to be "safe" so we're going to
- * assume it's vulnerable. We still need to update `max_bhb_k` though,
- * but only if we aren't mitigating with clearbhb though.
- */
- if (scope == SCOPE_LOCAL_CPU && !supports_clearbhb(SCOPE_LOCAL_CPU))
- max_bhb_k = max(max_bhb_k, spectre_bhb_loop_affected());
- return true;
- }
- u8 get_spectre_bhb_loop_value(void)
- {
- return max_bhb_k;
- }
- static void this_cpu_set_vectors(enum arm64_bp_harden_el1_vectors slot)
- {
- const char *v = arm64_get_bp_hardening_vector(slot);
- __this_cpu_write(this_cpu_vector, v);
- /*
- * When KPTI is in use, the vectors are switched when exiting to
- * user-space.
- */
- if (cpus_have_cap(ARM64_UNMAP_KERNEL_AT_EL0))
- return;
- write_sysreg(v, vbar_el1);
- isb();
- }
- bool __read_mostly __nospectre_bhb;
- static int __init parse_spectre_bhb_param(char *str)
- {
- __nospectre_bhb = true;
- return 0;
- }
- early_param("nospectre_bhb", parse_spectre_bhb_param);
- void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry)
- {
- bp_hardening_cb_t cpu_cb;
- enum mitigation_state state = SPECTRE_VULNERABLE;
- struct bp_hardening_data *data = this_cpu_ptr(&bp_hardening_data);
- if (!is_spectre_bhb_affected(entry, SCOPE_LOCAL_CPU))
- return;
- if (arm64_get_spectre_v2_state() == SPECTRE_VULNERABLE) {
- /* No point mitigating Spectre-BHB alone. */
- } else if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY)) {
- pr_info_once("spectre-bhb mitigation disabled by compile time option\n");
- } else if (cpu_mitigations_off() || __nospectre_bhb) {
- pr_info_once("spectre-bhb mitigation disabled by command line option\n");
- } else if (supports_ecbhb(SCOPE_LOCAL_CPU)) {
- state = SPECTRE_MITIGATED;
- set_bit(BHB_HW, &system_bhb_mitigations);
- } else if (supports_clearbhb(SCOPE_LOCAL_CPU)) {
- /*
- * Ensure KVM uses the indirect vector which will have ClearBHB
- * added.
- */
- if (!data->slot)
- data->slot = HYP_VECTOR_INDIRECT;
- this_cpu_set_vectors(EL1_VECTOR_BHB_CLEAR_INSN);
- state = SPECTRE_MITIGATED;
- set_bit(BHB_INSN, &system_bhb_mitigations);
- } else if (spectre_bhb_loop_affected()) {
- /*
- * Ensure KVM uses the indirect vector which will have the
- * branchy-loop added. A57/A72-r0 will already have selected
- * the spectre-indirect vector, which is sufficient for BHB
- * too.
- */
- if (!data->slot)
- data->slot = HYP_VECTOR_INDIRECT;
- this_cpu_set_vectors(EL1_VECTOR_BHB_LOOP);
- state = SPECTRE_MITIGATED;
- set_bit(BHB_LOOP, &system_bhb_mitigations);
- } else if (has_spectre_bhb_fw_mitigation()) {
- /*
- * Ensure KVM uses one of the spectre bp_hardening
- * vectors. The indirect vector doesn't include the EL3
- * call, so needs upgrading to
- * HYP_VECTOR_SPECTRE_INDIRECT.
- */
- if (!data->slot || data->slot == HYP_VECTOR_INDIRECT)
- data->slot += 1;
- this_cpu_set_vectors(EL1_VECTOR_BHB_FW);
- /*
- * The WA3 call in the vectors supersedes the WA1 call
- * made during context-switch. Uninstall any firmware
- * bp_hardening callback.
- */
- cpu_cb = spectre_v2_get_sw_mitigation_cb();
- if (__this_cpu_read(bp_hardening_data.fn) != cpu_cb)
- __this_cpu_write(bp_hardening_data.fn, NULL);
- state = SPECTRE_MITIGATED;
- set_bit(BHB_FW, &system_bhb_mitigations);
- }
- update_mitigation_state(&spectre_bhb_state, state);
- }
- bool is_spectre_bhb_fw_mitigated(void)
- {
- return test_bit(BHB_FW, &system_bhb_mitigations);
- }
- /* Patched to NOP when enabled */
- void noinstr spectre_bhb_patch_loop_mitigation_enable(struct alt_instr *alt,
- __le32 *origptr,
- __le32 *updptr, int nr_inst)
- {
- BUG_ON(nr_inst != 1);
- if (test_bit(BHB_LOOP, &system_bhb_mitigations))
- *updptr++ = cpu_to_le32(aarch64_insn_gen_nop());
- }
- /* Patched to NOP when enabled */
- void noinstr spectre_bhb_patch_fw_mitigation_enabled(struct alt_instr *alt,
- __le32 *origptr,
- __le32 *updptr, int nr_inst)
- {
- BUG_ON(nr_inst != 1);
- if (test_bit(BHB_FW, &system_bhb_mitigations))
- *updptr++ = cpu_to_le32(aarch64_insn_gen_nop());
- }
- /* Patched to correct the immediate */
- void noinstr spectre_bhb_patch_loop_iter(struct alt_instr *alt,
- __le32 *origptr, __le32 *updptr, int nr_inst)
- {
- u8 rd;
- u32 insn;
- BUG_ON(nr_inst != 1); /* MOV -> MOV */
- if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY))
- return;
- insn = le32_to_cpu(*origptr);
- rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, insn);
- insn = aarch64_insn_gen_movewide(rd, max_bhb_k, 0,
- AARCH64_INSN_VARIANT_64BIT,
- AARCH64_INSN_MOVEWIDE_ZERO);
- *updptr++ = cpu_to_le32(insn);
- }
- /* Patched to mov WA3 when supported */
- void noinstr spectre_bhb_patch_wa3(struct alt_instr *alt,
- __le32 *origptr, __le32 *updptr, int nr_inst)
- {
- u8 rd;
- u32 insn;
- BUG_ON(nr_inst != 1); /* MOV -> MOV */
- if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY) ||
- !test_bit(BHB_FW, &system_bhb_mitigations))
- return;
- insn = le32_to_cpu(*origptr);
- rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, insn);
- insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_ORR,
- AARCH64_INSN_VARIANT_32BIT,
- AARCH64_INSN_REG_ZR, rd,
- ARM_SMCCC_ARCH_WORKAROUND_3);
- if (WARN_ON_ONCE(insn == AARCH64_BREAK_FAULT))
- return;
- *updptr++ = cpu_to_le32(insn);
- }
- /* Patched to NOP when not supported */
- void __init spectre_bhb_patch_clearbhb(struct alt_instr *alt,
- __le32 *origptr, __le32 *updptr, int nr_inst)
- {
- BUG_ON(nr_inst != 2);
- if (test_bit(BHB_INSN, &system_bhb_mitigations))
- return;
- *updptr++ = cpu_to_le32(aarch64_insn_gen_nop());
- *updptr++ = cpu_to_le32(aarch64_insn_gen_nop());
- }
- #ifdef CONFIG_BPF_SYSCALL
- #define EBPF_WARN "Unprivileged eBPF is enabled, data leaks possible via Spectre v2 BHB attacks!\n"
- void unpriv_ebpf_notify(int new_state)
- {
- if (spectre_v2_state == SPECTRE_VULNERABLE ||
- spectre_bhb_state != SPECTRE_MITIGATED)
- return;
- if (!new_state)
- pr_err("WARNING: %s", EBPF_WARN);
- }
- #endif
|