Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
51c52e86 ME |
2 | /* |
3 | * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) | |
4 | * | |
5 | * Modifications for ppc64: | |
6 | * Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com> | |
7 | * | |
8 | * Copyright 2008 Michael Ellerman, IBM Corporation. | |
51c52e86 ME |
9 | */ |
10 | ||
3880ecb0 | 11 | #include <linux/types.h> |
309b315b | 12 | #include <linux/jump_label.h> |
51c52e86 | 13 | #include <linux/kernel.h> |
362e7701 ME |
14 | #include <linux/string.h> |
15 | #include <linux/init.h> | |
589ee628 | 16 | #include <linux/sched/mm.h> |
8ec7791b | 17 | #include <linux/stop_machine.h> |
51c52e86 | 18 | #include <asm/cputable.h> |
0c3beacf | 19 | #include <asm/text-patching.h> |
13799748 | 20 | #include <asm/interrupt.h> |
d715e433 AB |
21 | #include <asm/page.h> |
22 | #include <asm/sections.h> | |
9402c684 | 23 | #include <asm/setup.h> |
a048a07d | 24 | #include <asm/security_features.h> |
9402c684 | 25 | #include <asm/firmware.h> |
75346251 | 26 | #include <asm/inst.h> |
51c52e86 | 27 | |
676b2f99 NM |
28 | /* |
29 | * Used to generate warnings if mmu or cpu feature check functions that | |
30 | * use static keys before they are initialized. | |
31 | */ | |
32 | bool static_key_feature_checks_initialized __read_mostly; | |
33 | EXPORT_SYMBOL_GPL(static_key_feature_checks_initialized); | |
34 | ||
51c52e86 ME |
35 | struct fixup_entry { |
36 | unsigned long mask; | |
37 | unsigned long value; | |
38 | long start_off; | |
39 | long end_off; | |
fac23fe4 ME |
40 | long alt_start_off; |
41 | long alt_end_off; | |
51c52e86 ME |
42 | }; |
43 | ||
69d4d6e5 | 44 | static u32 *calc_addr(struct fixup_entry *fcur, long offset) |
51c52e86 | 45 | { |
9b1a735d ME |
46 | /* |
47 | * We store the offset to the code as a negative offset from | |
48 | * the start of the alt_entry, to support the VDSO. This | |
49 | * routine converts that back into an actual address. | |
50 | */ | |
69d4d6e5 | 51 | return (u32 *)((unsigned long)fcur + offset); |
9b1a735d ME |
52 | } |
53 | ||
69d4d6e5 | 54 | static int patch_alt_instruction(u32 *src, u32 *dest, u32 *alt_start, u32 *alt_end) |
9b1a735d | 55 | { |
7c95d889 | 56 | int err; |
c545b9f0 | 57 | ppc_inst_t instr; |
9b1a735d | 58 | |
f8faaffa | 59 | instr = ppc_inst_read(src); |
9b1a735d | 60 | |
18c85964 | 61 | if (instr_is_relative_branch(ppc_inst_read(src))) { |
69d4d6e5 | 62 | u32 *target = (u32 *)branch_target(src); |
9b1a735d ME |
63 | |
64 | /* Branch within the section doesn't need translating */ | |
b8858581 | 65 | if (target < alt_start || target > alt_end) { |
7c95d889 JN |
66 | err = translate_branch(&instr, dest, src); |
67 | if (err) | |
9b1a735d ME |
68 | return 1; |
69 | } | |
70 | } | |
71 | ||
8183d99f | 72 | raw_patch_instruction(dest, instr); |
9b1a735d ME |
73 | |
74 | return 0; | |
75 | } | |
76 | ||
6b289911 CL |
77 | static int patch_feature_section_mask(unsigned long value, unsigned long mask, |
78 | struct fixup_entry *fcur) | |
9b1a735d | 79 | { |
69d4d6e5 | 80 | u32 *start, *end, *alt_start, *alt_end, *src, *dest; |
9b1a735d ME |
81 | |
82 | start = calc_addr(fcur, fcur->start_off); | |
83 | end = calc_addr(fcur, fcur->end_off); | |
84 | alt_start = calc_addr(fcur, fcur->alt_start_off); | |
85 | alt_end = calc_addr(fcur, fcur->alt_end_off); | |
86 | ||
87 | if ((alt_end - alt_start) > (end - start)) | |
88 | return 1; | |
51c52e86 | 89 | |
6b289911 | 90 | if ((value & fcur->mask & mask) == (fcur->value & mask)) |
9b1a735d | 91 | return 0; |
51c52e86 | 92 | |
9b1a735d ME |
93 | src = alt_start; |
94 | dest = start; | |
51c52e86 | 95 | |
c5ff46d6 ME |
96 | for (; src < alt_end; src = ppc_inst_next(src, src), |
97 | dest = ppc_inst_next(dest, dest)) { | |
9b1a735d ME |
98 | if (patch_alt_instruction(src, dest, alt_start, alt_end)) |
99 | return 1; | |
51c52e86 | 100 | } |
9b1a735d | 101 | |
69d4d6e5 CL |
102 | for (; dest < end; dest++) |
103 | raw_patch_instruction(dest, ppc_inst(PPC_RAW_NOP())); | |
9b1a735d ME |
104 | |
105 | return 0; | |
51c52e86 ME |
106 | } |
107 | ||
6b289911 CL |
108 | static void do_feature_fixups_mask(unsigned long value, unsigned long mask, |
109 | void *fixup_start, void *fixup_end) | |
51c52e86 ME |
110 | { |
111 | struct fixup_entry *fcur, *fend; | |
112 | ||
113 | fcur = fixup_start; | |
114 | fend = fixup_end; | |
115 | ||
9b1a735d | 116 | for (; fcur < fend; fcur++) { |
6b289911 | 117 | if (patch_feature_section_mask(value, mask, fcur)) { |
1856c020 | 118 | WARN_ON(1); |
9b1a735d ME |
119 | printk("Unable to patch feature section at %p - %p" \ |
120 | " with %p - %p\n", | |
121 | calc_addr(fcur, fcur->start_off), | |
122 | calc_addr(fcur, fcur->end_off), | |
123 | calc_addr(fcur, fcur->alt_start_off), | |
124 | calc_addr(fcur, fcur->alt_end_off)); | |
125 | } | |
126 | } | |
51c52e86 | 127 | } |
362e7701 | 128 | |
6b289911 CL |
129 | void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) |
130 | { | |
131 | do_feature_fixups_mask(value, ~0, fixup_start, fixup_end); | |
132 | } | |
133 | ||
3d1dbbca | 134 | #ifdef CONFIG_PPC_BARRIER_NOSPEC |
b988e779 CL |
135 | static bool is_fixup_addr_valid(void *dest, size_t size) |
136 | { | |
137 | return system_state < SYSTEM_FREEING_INITMEM || | |
138 | !init_section_contains(dest, size); | |
139 | } | |
140 | ||
3d1dbbca CL |
141 | static int do_patch_fixups(long *start, long *end, unsigned int *instrs, int num) |
142 | { | |
143 | int i; | |
144 | ||
145 | for (i = 0; start < end; start++, i++) { | |
146 | int j; | |
147 | unsigned int *dest = (void *)start + *start; | |
148 | ||
b988e779 CL |
149 | if (!is_fixup_addr_valid(dest, sizeof(*instrs) * num)) |
150 | continue; | |
151 | ||
3d1dbbca CL |
152 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
153 | ||
154 | for (j = 0; j < num; j++) | |
155 | patch_instruction(dest + j, ppc_inst(instrs[j])); | |
156 | } | |
157 | return i; | |
158 | } | |
159 | #endif | |
160 | ||
aa8a5e00 | 161 | #ifdef CONFIG_PPC_BOOK3S_64 |
6076dc34 CL |
162 | static int do_patch_entry_fixups(long *start, long *end, unsigned int *instrs, |
163 | bool do_fallback, void *fallback) | |
164 | { | |
165 | int i; | |
166 | ||
167 | for (i = 0; start < end; start++, i++) { | |
168 | unsigned int *dest = (void *)start + *start; | |
169 | ||
b988e779 CL |
170 | if (!is_fixup_addr_valid(dest, sizeof(*instrs) * 3)) |
171 | continue; | |
172 | ||
6076dc34 CL |
173 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
174 | ||
175 | // See comment in do_entry_flush_fixups() RE order of patching | |
176 | if (do_fallback) { | |
177 | patch_instruction(dest, ppc_inst(instrs[0])); | |
178 | patch_instruction(dest + 2, ppc_inst(instrs[2])); | |
179 | patch_branch(dest + 1, (unsigned long)fallback, BRANCH_SET_LINK); | |
180 | } else { | |
181 | patch_instruction(dest + 1, ppc_inst(instrs[1])); | |
182 | patch_instruction(dest + 2, ppc_inst(instrs[2])); | |
183 | patch_instruction(dest, ppc_inst(instrs[0])); | |
184 | } | |
185 | } | |
186 | return i; | |
187 | } | |
188 | ||
3b30c6e8 | 189 | static void do_stf_entry_barrier_fixups(enum stf_barrier_type types) |
a048a07d | 190 | { |
6076dc34 | 191 | unsigned int instrs[3]; |
a048a07d NP |
192 | long *start, *end; |
193 | int i; | |
194 | ||
1fc0c27b | 195 | start = PTRRELOC(&__start___stf_entry_barrier_fixup); |
a048a07d NP |
196 | end = PTRRELOC(&__stop___stf_entry_barrier_fixup); |
197 | ||
ef909ba9 CL |
198 | instrs[0] = PPC_RAW_NOP(); |
199 | instrs[1] = PPC_RAW_NOP(); | |
200 | instrs[2] = PPC_RAW_NOP(); | |
a048a07d NP |
201 | |
202 | i = 0; | |
203 | if (types & STF_BARRIER_FALLBACK) { | |
ef909ba9 CL |
204 | instrs[i++] = PPC_RAW_MFLR(_R10); |
205 | instrs[i++] = PPC_RAW_NOP(); /* branch patched below */ | |
206 | instrs[i++] = PPC_RAW_MTLR(_R10); | |
a048a07d | 207 | } else if (types & STF_BARRIER_EIEIO) { |
ef909ba9 | 208 | instrs[i++] = PPC_RAW_EIEIO() | 0x02000000; /* eieio + bit 6 hint */ |
a048a07d | 209 | } else if (types & STF_BARRIER_SYNC_ORI) { |
ef909ba9 CL |
210 | instrs[i++] = PPC_RAW_SYNC(); |
211 | instrs[i++] = PPC_RAW_LD(_R10, _R13, 0); | |
212 | instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ | |
a048a07d NP |
213 | } |
214 | ||
6076dc34 CL |
215 | i = do_patch_entry_fixups(start, end, instrs, types & STF_BARRIER_FALLBACK, |
216 | &stf_barrier_fallback); | |
a048a07d NP |
217 | |
218 | printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i, | |
219 | (types == STF_BARRIER_NONE) ? "no" : | |
220 | (types == STF_BARRIER_FALLBACK) ? "fallback" : | |
221 | (types == STF_BARRIER_EIEIO) ? "eieio" : | |
222 | (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" | |
223 | : "unknown"); | |
224 | } | |
225 | ||
3b30c6e8 | 226 | static void do_stf_exit_barrier_fixups(enum stf_barrier_type types) |
a048a07d | 227 | { |
3d1dbbca | 228 | unsigned int instrs[6]; |
a048a07d NP |
229 | long *start, *end; |
230 | int i; | |
231 | ||
1fc0c27b | 232 | start = PTRRELOC(&__start___stf_exit_barrier_fixup); |
a048a07d NP |
233 | end = PTRRELOC(&__stop___stf_exit_barrier_fixup); |
234 | ||
ef909ba9 CL |
235 | instrs[0] = PPC_RAW_NOP(); |
236 | instrs[1] = PPC_RAW_NOP(); | |
237 | instrs[2] = PPC_RAW_NOP(); | |
238 | instrs[3] = PPC_RAW_NOP(); | |
239 | instrs[4] = PPC_RAW_NOP(); | |
240 | instrs[5] = PPC_RAW_NOP(); | |
a048a07d NP |
241 | |
242 | i = 0; | |
243 | if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) { | |
244 | if (cpu_has_feature(CPU_FTR_HVMODE)) { | |
ef909ba9 CL |
245 | instrs[i++] = PPC_RAW_MTSPR(SPRN_HSPRG1, _R13); |
246 | instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_HSPRG0); | |
a048a07d | 247 | } else { |
ef909ba9 CL |
248 | instrs[i++] = PPC_RAW_MTSPR(SPRN_SPRG2, _R13); |
249 | instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_SPRG1); | |
a048a07d | 250 | } |
ef909ba9 CL |
251 | instrs[i++] = PPC_RAW_SYNC(); |
252 | instrs[i++] = PPC_RAW_LD(_R13, _R13, 0); | |
253 | instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ | |
254 | if (cpu_has_feature(CPU_FTR_HVMODE)) | |
255 | instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_HSPRG1); | |
256 | else | |
257 | instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_SPRG2); | |
a048a07d | 258 | } else if (types & STF_BARRIER_EIEIO) { |
ef909ba9 | 259 | instrs[i++] = PPC_RAW_EIEIO() | 0x02000000; /* eieio + bit 6 hint */ |
a048a07d NP |
260 | } |
261 | ||
3d1dbbca | 262 | i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs)); |
a048a07d | 263 | |
a048a07d NP |
264 | printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i, |
265 | (types == STF_BARRIER_NONE) ? "no" : | |
266 | (types == STF_BARRIER_FALLBACK) ? "fallback" : | |
267 | (types == STF_BARRIER_EIEIO) ? "eieio" : | |
268 | (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" | |
269 | : "unknown"); | |
270 | } | |
271 | ||
13799748 NP |
272 | static bool stf_exit_reentrant = false; |
273 | static bool rfi_exit_reentrant = false; | |
3c12b4df | 274 | static DEFINE_MUTEX(exit_flush_lock); |
13799748 | 275 | |
8ec7791b ME |
276 | static int __do_stf_barrier_fixups(void *data) |
277 | { | |
278 | enum stf_barrier_type *types = data; | |
279 | ||
280 | do_stf_entry_barrier_fixups(*types); | |
281 | do_stf_exit_barrier_fixups(*types); | |
282 | ||
283 | return 0; | |
284 | } | |
a048a07d NP |
285 | |
286 | void do_stf_barrier_fixups(enum stf_barrier_type types) | |
287 | { | |
8ec7791b ME |
288 | /* |
289 | * The call to the fallback entry flush, and the fallback/sync-ori exit | |
13799748 NP |
290 | * flush can not be safely patched in/out while other CPUs are |
291 | * executing them. So call __do_stf_barrier_fixups() on one CPU while | |
292 | * all other CPUs spin in the stop machine core with interrupts hard | |
293 | * disabled. | |
294 | * | |
295 | * The branch to mark interrupt exits non-reentrant is enabled first, | |
296 | * then stop_machine runs which will ensure all CPUs are out of the | |
297 | * low level interrupt exit code before patching. After the patching, | |
298 | * if allowed, then flip the branch to allow fast exits. | |
8ec7791b | 299 | */ |
3c12b4df RC |
300 | |
301 | // Prevent static key update races with do_rfi_flush_fixups() | |
302 | mutex_lock(&exit_flush_lock); | |
13799748 NP |
303 | static_branch_enable(&interrupt_exit_not_reentrant); |
304 | ||
8ec7791b | 305 | stop_machine(__do_stf_barrier_fixups, &types, NULL); |
13799748 NP |
306 | |
307 | if ((types & STF_BARRIER_FALLBACK) || (types & STF_BARRIER_SYNC_ORI)) | |
308 | stf_exit_reentrant = false; | |
309 | else | |
310 | stf_exit_reentrant = true; | |
311 | ||
312 | if (stf_exit_reentrant && rfi_exit_reentrant) | |
313 | static_branch_disable(&interrupt_exit_not_reentrant); | |
3c12b4df RC |
314 | |
315 | mutex_unlock(&exit_flush_lock); | |
a048a07d NP |
316 | } |
317 | ||
9a32a7e7 NP |
318 | void do_uaccess_flush_fixups(enum l1d_flush_type types) |
319 | { | |
3d1dbbca | 320 | unsigned int instrs[4]; |
9a32a7e7 NP |
321 | long *start, *end; |
322 | int i; | |
323 | ||
324 | start = PTRRELOC(&__start___uaccess_flush_fixup); | |
325 | end = PTRRELOC(&__stop___uaccess_flush_fixup); | |
326 | ||
ef909ba9 CL |
327 | instrs[0] = PPC_RAW_NOP(); |
328 | instrs[1] = PPC_RAW_NOP(); | |
329 | instrs[2] = PPC_RAW_NOP(); | |
330 | instrs[3] = PPC_RAW_BLR(); | |
9a32a7e7 NP |
331 | |
332 | i = 0; | |
333 | if (types == L1D_FLUSH_FALLBACK) { | |
ef909ba9 | 334 | instrs[3] = PPC_RAW_NOP(); |
9a32a7e7 NP |
335 | /* fallthrough to fallback flush */ |
336 | } | |
337 | ||
338 | if (types & L1D_FLUSH_ORI) { | |
ef909ba9 CL |
339 | instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ |
340 | instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */ | |
9a32a7e7 NP |
341 | } |
342 | ||
343 | if (types & L1D_FLUSH_MTTRIG) | |
ef909ba9 | 344 | instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0); |
9a32a7e7 | 345 | |
3d1dbbca | 346 | i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs)); |
9a32a7e7 NP |
347 | |
348 | printk(KERN_DEBUG "uaccess-flush: patched %d locations (%s flush)\n", i, | |
349 | (types == L1D_FLUSH_NONE) ? "no" : | |
350 | (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : | |
351 | (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) | |
352 | ? "ori+mttrig type" | |
353 | : "ori type" : | |
354 | (types & L1D_FLUSH_MTTRIG) ? "mttrig type" | |
355 | : "unknown"); | |
356 | } | |
357 | ||
aec86b05 | 358 | static int __do_entry_flush_fixups(void *data) |
f7964378 | 359 | { |
aec86b05 | 360 | enum l1d_flush_type types = *(enum l1d_flush_type *)data; |
6076dc34 | 361 | unsigned int instrs[3]; |
f7964378 NP |
362 | long *start, *end; |
363 | int i; | |
364 | ||
ef909ba9 CL |
365 | instrs[0] = PPC_RAW_NOP(); |
366 | instrs[1] = PPC_RAW_NOP(); | |
367 | instrs[2] = PPC_RAW_NOP(); | |
f7964378 NP |
368 | |
369 | i = 0; | |
370 | if (types == L1D_FLUSH_FALLBACK) { | |
ef909ba9 CL |
371 | instrs[i++] = PPC_RAW_MFLR(_R10); |
372 | instrs[i++] = PPC_RAW_NOP(); /* branch patched below */ | |
373 | instrs[i++] = PPC_RAW_MTLR(_R10); | |
f7964378 NP |
374 | } |
375 | ||
376 | if (types & L1D_FLUSH_ORI) { | |
ef909ba9 CL |
377 | instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ |
378 | instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */ | |
f7964378 NP |
379 | } |
380 | ||
381 | if (types & L1D_FLUSH_MTTRIG) | |
ef909ba9 | 382 | instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0); |
f7964378 | 383 | |
49b39ec2 ME |
384 | /* |
385 | * If we're patching in or out the fallback flush we need to be careful about the | |
386 | * order in which we patch instructions. That's because it's possible we could | |
387 | * take a page fault after patching one instruction, so the sequence of | |
388 | * instructions must be safe even in a half patched state. | |
389 | * | |
390 | * To make that work, when patching in the fallback flush we patch in this order: | |
391 | * - the mflr (dest) | |
392 | * - the mtlr (dest + 2) | |
393 | * - the branch (dest + 1) | |
394 | * | |
395 | * That ensures the sequence is safe to execute at any point. In contrast if we | |
396 | * patch the mtlr last, it's possible we could return from the branch and not | |
397 | * restore LR, leading to a crash later. | |
398 | * | |
399 | * When patching out the fallback flush (either with nops or another flush type), | |
400 | * we patch in this order: | |
401 | * - the branch (dest + 1) | |
402 | * - the mtlr (dest + 2) | |
403 | * - the mflr (dest) | |
404 | * | |
405 | * Note we are protected by stop_machine() from other CPUs executing the code in a | |
406 | * semi-patched state. | |
407 | */ | |
408 | ||
08685be7 NP |
409 | start = PTRRELOC(&__start___entry_flush_fixup); |
410 | end = PTRRELOC(&__stop___entry_flush_fixup); | |
6076dc34 CL |
411 | i = do_patch_entry_fixups(start, end, instrs, types == L1D_FLUSH_FALLBACK, |
412 | &entry_flush_fallback); | |
f7964378 | 413 | |
08685be7 NP |
414 | start = PTRRELOC(&__start___scv_entry_flush_fixup); |
415 | end = PTRRELOC(&__stop___scv_entry_flush_fixup); | |
6076dc34 CL |
416 | i += do_patch_entry_fixups(start, end, instrs, types == L1D_FLUSH_FALLBACK, |
417 | &scv_entry_flush_fallback); | |
08685be7 | 418 | |
f7964378 NP |
419 | printk(KERN_DEBUG "entry-flush: patched %d locations (%s flush)\n", i, |
420 | (types == L1D_FLUSH_NONE) ? "no" : | |
421 | (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : | |
422 | (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) | |
423 | ? "ori+mttrig type" | |
424 | : "ori type" : | |
425 | (types & L1D_FLUSH_MTTRIG) ? "mttrig type" | |
426 | : "unknown"); | |
aec86b05 ME |
427 | |
428 | return 0; | |
429 | } | |
430 | ||
431 | void do_entry_flush_fixups(enum l1d_flush_type types) | |
432 | { | |
433 | /* | |
434 | * The call to the fallback flush can not be safely patched in/out while | |
435 | * other CPUs are executing it. So call __do_entry_flush_fixups() on one | |
436 | * CPU while all other CPUs spin in the stop machine core with interrupts | |
437 | * hard disabled. | |
438 | */ | |
439 | stop_machine(__do_entry_flush_fixups, &types, NULL); | |
f7964378 NP |
440 | } |
441 | ||
13799748 | 442 | static int __do_rfi_flush_fixups(void *data) |
aa8a5e00 | 443 | { |
13799748 | 444 | enum l1d_flush_type types = *(enum l1d_flush_type *)data; |
3d1dbbca | 445 | unsigned int instrs[3]; |
aa8a5e00 ME |
446 | long *start, *end; |
447 | int i; | |
448 | ||
1fc0c27b | 449 | start = PTRRELOC(&__start___rfi_flush_fixup); |
aa8a5e00 ME |
450 | end = PTRRELOC(&__stop___rfi_flush_fixup); |
451 | ||
ef909ba9 CL |
452 | instrs[0] = PPC_RAW_NOP(); |
453 | instrs[1] = PPC_RAW_NOP(); | |
454 | instrs[2] = PPC_RAW_NOP(); | |
aa8a5e00 ME |
455 | |
456 | if (types & L1D_FLUSH_FALLBACK) | |
457 | /* b .+16 to fallback flush */ | |
4390a58e | 458 | instrs[0] = PPC_RAW_BRANCH(16); |
aa8a5e00 ME |
459 | |
460 | i = 0; | |
461 | if (types & L1D_FLUSH_ORI) { | |
ef909ba9 CL |
462 | instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ |
463 | instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */ | |
aa8a5e00 ME |
464 | } |
465 | ||
466 | if (types & L1D_FLUSH_MTTRIG) | |
ef909ba9 | 467 | instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0); |
aa8a5e00 | 468 | |
3d1dbbca | 469 | i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs)); |
aa8a5e00 | 470 | |
0063d61c MFO |
471 | printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i, |
472 | (types == L1D_FLUSH_NONE) ? "no" : | |
473 | (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : | |
474 | (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) | |
475 | ? "ori+mttrig type" | |
476 | : "ori type" : | |
477 | (types & L1D_FLUSH_MTTRIG) ? "mttrig type" | |
478 | : "unknown"); | |
13799748 NP |
479 | |
480 | return 0; | |
481 | } | |
482 | ||
483 | void do_rfi_flush_fixups(enum l1d_flush_type types) | |
484 | { | |
485 | /* | |
486 | * stop_machine gets all CPUs out of the interrupt exit handler same | |
487 | * as do_stf_barrier_fixups. do_rfi_flush_fixups patching can run | |
488 | * without stop_machine, so this could be achieved with a broadcast | |
489 | * IPI instead, but this matches the stf sequence. | |
490 | */ | |
3c12b4df RC |
491 | |
492 | // Prevent static key update races with do_stf_barrier_fixups() | |
493 | mutex_lock(&exit_flush_lock); | |
13799748 NP |
494 | static_branch_enable(&interrupt_exit_not_reentrant); |
495 | ||
496 | stop_machine(__do_rfi_flush_fixups, &types, NULL); | |
497 | ||
498 | if (types & L1D_FLUSH_FALLBACK) | |
499 | rfi_exit_reentrant = false; | |
500 | else | |
501 | rfi_exit_reentrant = true; | |
502 | ||
503 | if (stf_exit_reentrant && rfi_exit_reentrant) | |
504 | static_branch_disable(&interrupt_exit_not_reentrant); | |
3c12b4df RC |
505 | |
506 | mutex_unlock(&exit_flush_lock); | |
aa8a5e00 | 507 | } |
2eea7f06 | 508 | |
815069ca | 509 | void do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end) |
2eea7f06 | 510 | { |
3d1dbbca | 511 | unsigned int instr; |
2eea7f06 MS |
512 | long *start, *end; |
513 | int i; | |
514 | ||
815069ca MS |
515 | start = fixup_start; |
516 | end = fixup_end; | |
2eea7f06 | 517 | |
ef909ba9 | 518 | instr = PPC_RAW_NOP(); |
2eea7f06 MS |
519 | |
520 | if (enable) { | |
521 | pr_info("barrier-nospec: using ORI speculation barrier\n"); | |
ef909ba9 | 522 | instr = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ |
2eea7f06 MS |
523 | } |
524 | ||
3d1dbbca | 525 | i = do_patch_fixups(start, end, &instr, 1); |
2eea7f06 MS |
526 | |
527 | printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); | |
528 | } | |
529 | ||
179ab1cb ME |
530 | #endif /* CONFIG_PPC_BOOK3S_64 */ |
531 | ||
532 | #ifdef CONFIG_PPC_BARRIER_NOSPEC | |
815069ca MS |
533 | void do_barrier_nospec_fixups(bool enable) |
534 | { | |
535 | void *start, *end; | |
536 | ||
1fc0c27b | 537 | start = PTRRELOC(&__start___barrier_nospec_fixup); |
815069ca MS |
538 | end = PTRRELOC(&__stop___barrier_nospec_fixup); |
539 | ||
540 | do_barrier_nospec_fixups_range(enable, start, end); | |
541 | } | |
179ab1cb | 542 | #endif /* CONFIG_PPC_BARRIER_NOSPEC */ |
aa8a5e00 | 543 | |
3e731858 | 544 | #ifdef CONFIG_PPC_E500 |
ebcd1bfc DC |
545 | void do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end) |
546 | { | |
3d1dbbca | 547 | unsigned int instr[2]; |
ebcd1bfc DC |
548 | long *start, *end; |
549 | int i; | |
550 | ||
551 | start = fixup_start; | |
552 | end = fixup_end; | |
553 | ||
ef909ba9 CL |
554 | instr[0] = PPC_RAW_NOP(); |
555 | instr[1] = PPC_RAW_NOP(); | |
ebcd1bfc DC |
556 | |
557 | if (enable) { | |
558 | pr_info("barrier-nospec: using isync; sync as speculation barrier\n"); | |
ef909ba9 CL |
559 | instr[0] = PPC_RAW_ISYNC(); |
560 | instr[1] = PPC_RAW_SYNC(); | |
ebcd1bfc DC |
561 | } |
562 | ||
3d1dbbca | 563 | i = do_patch_fixups(start, end, instr, ARRAY_SIZE(instr)); |
ebcd1bfc DC |
564 | |
565 | printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); | |
566 | } | |
76a5eaa3 | 567 | |
ce0c6be9 | 568 | static void __init patch_btb_flush_section(long *curr) |
76a5eaa3 DC |
569 | { |
570 | unsigned int *start, *end; | |
571 | ||
572 | start = (void *)curr + *curr; | |
573 | end = (void *)curr + *(curr + 1); | |
574 | for (; start < end; start++) { | |
575 | pr_devel("patching dest %lx\n", (unsigned long)start); | |
69d4d6e5 | 576 | patch_instruction(start, ppc_inst(PPC_RAW_NOP())); |
76a5eaa3 DC |
577 | } |
578 | } | |
579 | ||
ce0c6be9 | 580 | void __init do_btb_flush_fixups(void) |
76a5eaa3 DC |
581 | { |
582 | long *start, *end; | |
583 | ||
584 | start = PTRRELOC(&__start__btb_flush_fixup); | |
585 | end = PTRRELOC(&__stop__btb_flush_fixup); | |
586 | ||
587 | for (; start < end; start += 2) | |
588 | patch_btb_flush_section(start); | |
589 | } | |
3e731858 | 590 | #endif /* CONFIG_PPC_E500 */ |
ebcd1bfc | 591 | |
2d1b2027 KG |
592 | void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end) |
593 | { | |
3d98ffbf | 594 | long *start, *end; |
69d4d6e5 | 595 | u32 *dest; |
2d1b2027 KG |
596 | |
597 | if (!(value & CPU_FTR_LWSYNC)) | |
598 | return ; | |
599 | ||
600 | start = fixup_start; | |
601 | end = fixup_end; | |
602 | ||
603 | for (; start < end; start++) { | |
604 | dest = (void *)start + *start; | |
75346251 | 605 | raw_patch_instruction(dest, ppc_inst(PPC_INST_LWSYNC)); |
2d1b2027 KG |
606 | } |
607 | } | |
608 | ||
ce0c6be9 | 609 | static void __init do_final_fixups(void) |
d715e433 AB |
610 | { |
611 | #if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE) | |
c545b9f0 | 612 | ppc_inst_t inst; |
69d4d6e5 | 613 | u32 *src, *dest, *end; |
d715e433 AB |
614 | |
615 | if (PHYSICAL_START == 0) | |
616 | return; | |
617 | ||
69d4d6e5 CL |
618 | src = (u32 *)(KERNELBASE + PHYSICAL_START); |
619 | dest = (u32 *)KERNELBASE; | |
622cf6f4 | 620 | end = (void *)src + (__end_interrupts - _stext); |
d715e433 | 621 | |
622cf6f4 JN |
622 | while (src < end) { |
623 | inst = ppc_inst_read(src); | |
624 | raw_patch_instruction(dest, inst); | |
c5ff46d6 ME |
625 | src = ppc_inst_next(src, src); |
626 | dest = ppc_inst_next(dest, dest); | |
d715e433 AB |
627 | } |
628 | #endif | |
629 | } | |
630 | ||
a28e46f1 ME |
631 | static unsigned long __initdata saved_cpu_features; |
632 | static unsigned int __initdata saved_mmu_features; | |
633 | #ifdef CONFIG_PPC64 | |
634 | static unsigned long __initdata saved_firmware_features; | |
635 | #endif | |
636 | ||
637 | void __init apply_feature_fixups(void) | |
9402c684 | 638 | { |
2c0f9951 | 639 | struct cpu_spec *spec = PTRRELOC(*PTRRELOC(&cur_cpu_spec)); |
9402c684 | 640 | |
a28e46f1 ME |
641 | *PTRRELOC(&saved_cpu_features) = spec->cpu_features; |
642 | *PTRRELOC(&saved_mmu_features) = spec->mmu_features; | |
643 | ||
9402c684 BH |
644 | /* |
645 | * Apply the CPU-specific and firmware specific fixups to kernel text | |
646 | * (nop out sections not relevant to this CPU or this firmware). | |
647 | */ | |
648 | do_feature_fixups(spec->cpu_features, | |
649 | PTRRELOC(&__start___ftr_fixup), | |
650 | PTRRELOC(&__stop___ftr_fixup)); | |
651 | ||
652 | do_feature_fixups(spec->mmu_features, | |
653 | PTRRELOC(&__start___mmu_ftr_fixup), | |
654 | PTRRELOC(&__stop___mmu_ftr_fixup)); | |
655 | ||
656 | do_lwsync_fixups(spec->cpu_features, | |
657 | PTRRELOC(&__start___lwsync_fixup), | |
658 | PTRRELOC(&__stop___lwsync_fixup)); | |
659 | ||
660 | #ifdef CONFIG_PPC64 | |
a28e46f1 | 661 | saved_firmware_features = powerpc_firmware_features; |
9402c684 BH |
662 | do_feature_fixups(powerpc_firmware_features, |
663 | &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); | |
664 | #endif | |
665 | do_final_fixups(); | |
97f6e0cc | 666 | } |
309b315b | 667 | |
6b289911 CL |
668 | void __init update_mmu_feature_fixups(unsigned long mask) |
669 | { | |
670 | saved_mmu_features &= ~mask; | |
671 | saved_mmu_features |= cur_cpu_spec->mmu_features & mask; | |
672 | ||
673 | do_feature_fixups_mask(cur_cpu_spec->mmu_features, mask, | |
674 | PTRRELOC(&__start___mmu_ftr_fixup), | |
675 | PTRRELOC(&__stop___mmu_ftr_fixup)); | |
676 | mmu_feature_keys_init(); | |
677 | } | |
678 | ||
97f6e0cc BH |
679 | void __init setup_feature_keys(void) |
680 | { | |
309b315b AK |
681 | /* |
682 | * Initialise jump label. This causes all the cpu/mmu_has_feature() | |
683 | * checks to take on their correct polarity based on the current set of | |
684 | * CPU/MMU features. | |
685 | */ | |
686 | jump_label_init(); | |
4db73271 | 687 | cpu_feature_keys_init(); |
c12e6f24 | 688 | mmu_feature_keys_init(); |
676b2f99 | 689 | static_key_feature_checks_initialized = true; |
9402c684 BH |
690 | } |
691 | ||
a28e46f1 ME |
692 | static int __init check_features(void) |
693 | { | |
694 | WARN(saved_cpu_features != cur_cpu_spec->cpu_features, | |
695 | "CPU features changed after feature patching!\n"); | |
696 | WARN(saved_mmu_features != cur_cpu_spec->mmu_features, | |
697 | "MMU features changed after feature patching!\n"); | |
698 | #ifdef CONFIG_PPC64 | |
699 | WARN(saved_firmware_features != powerpc_firmware_features, | |
700 | "Firmware features changed after feature patching!\n"); | |
701 | #endif | |
702 | ||
703 | return 0; | |
704 | } | |
705 | late_initcall(check_features); | |
706 | ||
362e7701 ME |
707 | #ifdef CONFIG_FTR_FIXUP_SELFTEST |
708 | ||
709 | #define check(x) \ | |
710 | if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__); | |
711 | ||
6b289911 CL |
712 | static int patch_feature_section(unsigned long value, struct fixup_entry *fcur) |
713 | { | |
714 | return patch_feature_section_mask(value, ~0, fcur); | |
715 | } | |
716 | ||
362e7701 ME |
717 | /* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */ |
718 | static struct fixup_entry fixup; | |
719 | ||
ce0c6be9 | 720 | static long __init calc_offset(struct fixup_entry *entry, unsigned int *p) |
362e7701 ME |
721 | { |
722 | return (unsigned long)p - (unsigned long)entry; | |
723 | } | |
724 | ||
ce0c6be9 | 725 | static void __init test_basic_patching(void) |
362e7701 | 726 | { |
c69a48cd DA |
727 | extern unsigned int ftr_fixup_test1[]; |
728 | extern unsigned int end_ftr_fixup_test1[]; | |
729 | extern unsigned int ftr_fixup_test1_orig[]; | |
730 | extern unsigned int ftr_fixup_test1_expected[]; | |
cad0e390 | 731 | int size = 4 * (end_ftr_fixup_test1 - ftr_fixup_test1); |
362e7701 ME |
732 | |
733 | fixup.value = fixup.mask = 8; | |
c69a48cd DA |
734 | fixup.start_off = calc_offset(&fixup, ftr_fixup_test1 + 1); |
735 | fixup.end_off = calc_offset(&fixup, ftr_fixup_test1 + 2); | |
362e7701 ME |
736 | fixup.alt_start_off = fixup.alt_end_off = 0; |
737 | ||
738 | /* Sanity check */ | |
c69a48cd | 739 | check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0); |
362e7701 ME |
740 | |
741 | /* Check we don't patch if the value matches */ | |
742 | patch_feature_section(8, &fixup); | |
c69a48cd | 743 | check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0); |
362e7701 ME |
744 | |
745 | /* Check we do patch if the value doesn't match */ | |
746 | patch_feature_section(0, &fixup); | |
c69a48cd | 747 | check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0); |
362e7701 ME |
748 | |
749 | /* Check we do patch if the mask doesn't match */ | |
c69a48cd DA |
750 | memcpy(ftr_fixup_test1, ftr_fixup_test1_orig, size); |
751 | check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0); | |
362e7701 | 752 | patch_feature_section(~8, &fixup); |
c69a48cd | 753 | check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0); |
362e7701 ME |
754 | } |
755 | ||
ce0c6be9 | 756 | static void __init test_alternative_patching(void) |
362e7701 | 757 | { |
c69a48cd DA |
758 | extern unsigned int ftr_fixup_test2[]; |
759 | extern unsigned int end_ftr_fixup_test2[]; | |
760 | extern unsigned int ftr_fixup_test2_orig[]; | |
761 | extern unsigned int ftr_fixup_test2_alt[]; | |
762 | extern unsigned int ftr_fixup_test2_expected[]; | |
cad0e390 | 763 | int size = 4 * (end_ftr_fixup_test2 - ftr_fixup_test2); |
362e7701 ME |
764 | |
765 | fixup.value = fixup.mask = 0xF; | |
c69a48cd DA |
766 | fixup.start_off = calc_offset(&fixup, ftr_fixup_test2 + 1); |
767 | fixup.end_off = calc_offset(&fixup, ftr_fixup_test2 + 2); | |
768 | fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test2_alt); | |
769 | fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test2_alt + 1); | |
362e7701 ME |
770 | |
771 | /* Sanity check */ | |
c69a48cd | 772 | check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0); |
362e7701 ME |
773 | |
774 | /* Check we don't patch if the value matches */ | |
775 | patch_feature_section(0xF, &fixup); | |
c69a48cd | 776 | check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0); |
362e7701 ME |
777 | |
778 | /* Check we do patch if the value doesn't match */ | |
779 | patch_feature_section(0, &fixup); | |
c69a48cd | 780 | check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0); |
362e7701 ME |
781 | |
782 | /* Check we do patch if the mask doesn't match */ | |
c69a48cd DA |
783 | memcpy(ftr_fixup_test2, ftr_fixup_test2_orig, size); |
784 | check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0); | |
362e7701 | 785 | patch_feature_section(~0xF, &fixup); |
c69a48cd | 786 | check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0); |
362e7701 ME |
787 | } |
788 | ||
ce0c6be9 | 789 | static void __init test_alternative_case_too_big(void) |
362e7701 | 790 | { |
c69a48cd DA |
791 | extern unsigned int ftr_fixup_test3[]; |
792 | extern unsigned int end_ftr_fixup_test3[]; | |
793 | extern unsigned int ftr_fixup_test3_orig[]; | |
794 | extern unsigned int ftr_fixup_test3_alt[]; | |
cad0e390 | 795 | int size = 4 * (end_ftr_fixup_test3 - ftr_fixup_test3); |
362e7701 ME |
796 | |
797 | fixup.value = fixup.mask = 0xC; | |
c69a48cd DA |
798 | fixup.start_off = calc_offset(&fixup, ftr_fixup_test3 + 1); |
799 | fixup.end_off = calc_offset(&fixup, ftr_fixup_test3 + 2); | |
800 | fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test3_alt); | |
801 | fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test3_alt + 2); | |
362e7701 ME |
802 | |
803 | /* Sanity check */ | |
c69a48cd | 804 | check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); |
362e7701 ME |
805 | |
806 | /* Expect nothing to be patched, and the error returned to us */ | |
807 | check(patch_feature_section(0xF, &fixup) == 1); | |
c69a48cd | 808 | check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); |
362e7701 | 809 | check(patch_feature_section(0, &fixup) == 1); |
c69a48cd | 810 | check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); |
362e7701 | 811 | check(patch_feature_section(~0xF, &fixup) == 1); |
c69a48cd | 812 | check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); |
362e7701 ME |
813 | } |
814 | ||
ce0c6be9 | 815 | static void __init test_alternative_case_too_small(void) |
362e7701 | 816 | { |
c69a48cd DA |
817 | extern unsigned int ftr_fixup_test4[]; |
818 | extern unsigned int end_ftr_fixup_test4[]; | |
819 | extern unsigned int ftr_fixup_test4_orig[]; | |
820 | extern unsigned int ftr_fixup_test4_alt[]; | |
821 | extern unsigned int ftr_fixup_test4_expected[]; | |
cad0e390 | 822 | int size = 4 * (end_ftr_fixup_test4 - ftr_fixup_test4); |
362e7701 ME |
823 | unsigned long flag; |
824 | ||
825 | /* Check a high-bit flag */ | |
826 | flag = 1UL << ((sizeof(unsigned long) - 1) * 8); | |
827 | fixup.value = fixup.mask = flag; | |
c69a48cd DA |
828 | fixup.start_off = calc_offset(&fixup, ftr_fixup_test4 + 1); |
829 | fixup.end_off = calc_offset(&fixup, ftr_fixup_test4 + 5); | |
830 | fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test4_alt); | |
831 | fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test4_alt + 2); | |
362e7701 ME |
832 | |
833 | /* Sanity check */ | |
c69a48cd | 834 | check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0); |
362e7701 ME |
835 | |
836 | /* Check we don't patch if the value matches */ | |
837 | patch_feature_section(flag, &fixup); | |
c69a48cd | 838 | check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0); |
362e7701 ME |
839 | |
840 | /* Check we do patch if the value doesn't match */ | |
841 | patch_feature_section(0, &fixup); | |
c69a48cd | 842 | check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0); |
362e7701 ME |
843 | |
844 | /* Check we do patch if the mask doesn't match */ | |
c69a48cd DA |
845 | memcpy(ftr_fixup_test4, ftr_fixup_test4_orig, size); |
846 | check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0); | |
362e7701 | 847 | patch_feature_section(~flag, &fixup); |
c69a48cd | 848 | check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0); |
362e7701 ME |
849 | } |
850 | ||
851 | static void test_alternative_case_with_branch(void) | |
852 | { | |
c69a48cd DA |
853 | extern unsigned int ftr_fixup_test5[]; |
854 | extern unsigned int end_ftr_fixup_test5[]; | |
855 | extern unsigned int ftr_fixup_test5_expected[]; | |
cad0e390 | 856 | int size = 4 * (end_ftr_fixup_test5 - ftr_fixup_test5); |
362e7701 | 857 | |
c69a48cd | 858 | check(memcmp(ftr_fixup_test5, ftr_fixup_test5_expected, size) == 0); |
362e7701 ME |
859 | } |
860 | ||
ce0c6be9 | 861 | static void __init test_alternative_case_with_external_branch(void) |
362e7701 | 862 | { |
c69a48cd DA |
863 | extern unsigned int ftr_fixup_test6[]; |
864 | extern unsigned int end_ftr_fixup_test6[]; | |
865 | extern unsigned int ftr_fixup_test6_expected[]; | |
cad0e390 | 866 | int size = 4 * (end_ftr_fixup_test6 - ftr_fixup_test6); |
362e7701 | 867 | |
c69a48cd | 868 | check(memcmp(ftr_fixup_test6, ftr_fixup_test6_expected, size) == 0); |
362e7701 ME |
869 | } |
870 | ||
ce0c6be9 | 871 | static void __init test_alternative_case_with_branch_to_end(void) |
6158faed ME |
872 | { |
873 | extern unsigned int ftr_fixup_test7[]; | |
874 | extern unsigned int end_ftr_fixup_test7[]; | |
875 | extern unsigned int ftr_fixup_test7_expected[]; | |
876 | int size = 4 * (end_ftr_fixup_test7 - ftr_fixup_test7); | |
877 | ||
878 | check(memcmp(ftr_fixup_test7, ftr_fixup_test7_expected, size) == 0); | |
879 | } | |
880 | ||
ce0c6be9 | 881 | static void __init test_cpu_macros(void) |
362e7701 | 882 | { |
c69a48cd DA |
883 | extern u8 ftr_fixup_test_FTR_macros[]; |
884 | extern u8 ftr_fixup_test_FTR_macros_expected[]; | |
885 | unsigned long size = ftr_fixup_test_FTR_macros_expected - | |
886 | ftr_fixup_test_FTR_macros; | |
362e7701 ME |
887 | |
888 | /* The fixups have already been done for us during boot */ | |
c69a48cd DA |
889 | check(memcmp(ftr_fixup_test_FTR_macros, |
890 | ftr_fixup_test_FTR_macros_expected, size) == 0); | |
362e7701 ME |
891 | } |
892 | ||
ce0c6be9 | 893 | static void __init test_fw_macros(void) |
362e7701 ME |
894 | { |
895 | #ifdef CONFIG_PPC64 | |
c69a48cd DA |
896 | extern u8 ftr_fixup_test_FW_FTR_macros[]; |
897 | extern u8 ftr_fixup_test_FW_FTR_macros_expected[]; | |
898 | unsigned long size = ftr_fixup_test_FW_FTR_macros_expected - | |
899 | ftr_fixup_test_FW_FTR_macros; | |
362e7701 ME |
900 | |
901 | /* The fixups have already been done for us during boot */ | |
c69a48cd DA |
902 | check(memcmp(ftr_fixup_test_FW_FTR_macros, |
903 | ftr_fixup_test_FW_FTR_macros_expected, size) == 0); | |
362e7701 ME |
904 | #endif |
905 | } | |
906 | ||
ce0c6be9 | 907 | static void __init test_lwsync_macros(void) |
2d1b2027 | 908 | { |
c69a48cd DA |
909 | extern u8 lwsync_fixup_test[]; |
910 | extern u8 end_lwsync_fixup_test[]; | |
911 | extern u8 lwsync_fixup_test_expected_LWSYNC[]; | |
912 | extern u8 lwsync_fixup_test_expected_SYNC[]; | |
913 | unsigned long size = end_lwsync_fixup_test - | |
914 | lwsync_fixup_test; | |
2d1b2027 KG |
915 | |
916 | /* The fixups have already been done for us during boot */ | |
917 | if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) { | |
c69a48cd DA |
918 | check(memcmp(lwsync_fixup_test, |
919 | lwsync_fixup_test_expected_LWSYNC, size) == 0); | |
2d1b2027 | 920 | } else { |
c69a48cd DA |
921 | check(memcmp(lwsync_fixup_test, |
922 | lwsync_fixup_test_expected_SYNC, size) == 0); | |
2d1b2027 KG |
923 | } |
924 | } | |
925 | ||
785b79d1 JN |
926 | #ifdef CONFIG_PPC64 |
927 | static void __init test_prefix_patching(void) | |
928 | { | |
929 | extern unsigned int ftr_fixup_prefix1[]; | |
930 | extern unsigned int end_ftr_fixup_prefix1[]; | |
931 | extern unsigned int ftr_fixup_prefix1_orig[]; | |
932 | extern unsigned int ftr_fixup_prefix1_expected[]; | |
933 | int size = sizeof(unsigned int) * (end_ftr_fixup_prefix1 - ftr_fixup_prefix1); | |
934 | ||
935 | fixup.value = fixup.mask = 8; | |
936 | fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix1 + 1); | |
937 | fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix1 + 3); | |
938 | fixup.alt_start_off = fixup.alt_end_off = 0; | |
939 | ||
940 | /* Sanity check */ | |
941 | check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) == 0); | |
942 | ||
943 | patch_feature_section(0, &fixup); | |
944 | check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_expected, size) == 0); | |
945 | check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) != 0); | |
946 | } | |
947 | ||
948 | static void __init test_prefix_alt_patching(void) | |
949 | { | |
950 | extern unsigned int ftr_fixup_prefix2[]; | |
951 | extern unsigned int end_ftr_fixup_prefix2[]; | |
952 | extern unsigned int ftr_fixup_prefix2_orig[]; | |
953 | extern unsigned int ftr_fixup_prefix2_expected[]; | |
954 | extern unsigned int ftr_fixup_prefix2_alt[]; | |
955 | int size = sizeof(unsigned int) * (end_ftr_fixup_prefix2 - ftr_fixup_prefix2); | |
956 | ||
957 | fixup.value = fixup.mask = 8; | |
958 | fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix2 + 1); | |
959 | fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix2 + 3); | |
960 | fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix2_alt); | |
961 | fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix2_alt + 2); | |
962 | /* Sanity check */ | |
963 | check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) == 0); | |
964 | ||
965 | patch_feature_section(0, &fixup); | |
966 | check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_expected, size) == 0); | |
967 | check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) != 0); | |
968 | } | |
969 | ||
970 | static void __init test_prefix_word_alt_patching(void) | |
971 | { | |
972 | extern unsigned int ftr_fixup_prefix3[]; | |
973 | extern unsigned int end_ftr_fixup_prefix3[]; | |
974 | extern unsigned int ftr_fixup_prefix3_orig[]; | |
975 | extern unsigned int ftr_fixup_prefix3_expected[]; | |
976 | extern unsigned int ftr_fixup_prefix3_alt[]; | |
977 | int size = sizeof(unsigned int) * (end_ftr_fixup_prefix3 - ftr_fixup_prefix3); | |
978 | ||
979 | fixup.value = fixup.mask = 8; | |
980 | fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix3 + 1); | |
981 | fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix3 + 4); | |
982 | fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix3_alt); | |
983 | fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix3_alt + 3); | |
984 | /* Sanity check */ | |
985 | check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) == 0); | |
986 | ||
987 | patch_feature_section(0, &fixup); | |
988 | check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_expected, size) == 0); | |
989 | patch_feature_section(0, &fixup); | |
990 | check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) != 0); | |
991 | } | |
992 | #else | |
993 | static inline void test_prefix_patching(void) {} | |
994 | static inline void test_prefix_alt_patching(void) {} | |
995 | static inline void test_prefix_word_alt_patching(void) {} | |
996 | #endif /* CONFIG_PPC64 */ | |
997 | ||
362e7701 ME |
998 | static int __init test_feature_fixups(void) |
999 | { | |
1000 | printk(KERN_DEBUG "Running feature fixup self-tests ...\n"); | |
1001 | ||
1002 | test_basic_patching(); | |
1003 | test_alternative_patching(); | |
1004 | test_alternative_case_too_big(); | |
1005 | test_alternative_case_too_small(); | |
1006 | test_alternative_case_with_branch(); | |
1007 | test_alternative_case_with_external_branch(); | |
6158faed | 1008 | test_alternative_case_with_branch_to_end(); |
362e7701 ME |
1009 | test_cpu_macros(); |
1010 | test_fw_macros(); | |
2d1b2027 | 1011 | test_lwsync_macros(); |
785b79d1 JN |
1012 | test_prefix_patching(); |
1013 | test_prefix_alt_patching(); | |
1014 | test_prefix_word_alt_patching(); | |
362e7701 ME |
1015 | |
1016 | return 0; | |
1017 | } | |
1018 | late_initcall(test_feature_fixups); | |
1019 | ||
1020 | #endif /* CONFIG_FTR_FIXUP_SELFTEST */ |