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