xref: /openbmc/linux/arch/s390/kernel/alternative.c (revision e6ed91fd)
19fa1db4cSMartin Schwidefsky // SPDX-License-Identifier: GPL-2.0
2686140a1SVasily Gorbik #include <linux/module.h>
3e16d02eeSHeiko Carstens #include <linux/cpu.h>
4e16d02eeSHeiko Carstens #include <linux/smp.h>
5e16d02eeSHeiko Carstens #include <asm/text-patching.h>
6686140a1SVasily Gorbik #include <asm/alternative.h>
7686140a1SVasily Gorbik #include <asm/facility.h>
86e179d64SMartin Schwidefsky #include <asm/nospec-branch.h>
9686140a1SVasily Gorbik 
10686140a1SVasily Gorbik static int __initdata_or_module alt_instr_disabled;
11686140a1SVasily Gorbik 
disable_alternative_instructions(char * str)12686140a1SVasily Gorbik static int __init disable_alternative_instructions(char *str)
13686140a1SVasily Gorbik {
14686140a1SVasily Gorbik 	alt_instr_disabled = 1;
15686140a1SVasily Gorbik 	return 0;
16686140a1SVasily Gorbik }
17686140a1SVasily Gorbik 
18686140a1SVasily Gorbik early_param("noaltinstr", disable_alternative_instructions);
19686140a1SVasily Gorbik 
__apply_alternatives(struct alt_instr * start,struct alt_instr * end)20686140a1SVasily Gorbik static void __init_or_module __apply_alternatives(struct alt_instr *start,
21686140a1SVasily Gorbik 						  struct alt_instr *end)
22686140a1SVasily Gorbik {
23686140a1SVasily Gorbik 	struct alt_instr *a;
24686140a1SVasily Gorbik 	u8 *instr, *replacement;
25686140a1SVasily Gorbik 
26686140a1SVasily Gorbik 	/*
27686140a1SVasily Gorbik 	 * The scan order should be from start to end. A later scanned
28686140a1SVasily Gorbik 	 * alternative code can overwrite previously scanned alternative code.
29686140a1SVasily Gorbik 	 */
30686140a1SVasily Gorbik 	for (a = start; a < end; a++) {
31686140a1SVasily Gorbik 		instr = (u8 *)&a->instr_offset + a->instr_offset;
32686140a1SVasily Gorbik 		replacement = (u8 *)&a->repl_offset + a->repl_offset;
33686140a1SVasily Gorbik 
3417e89e13SSven Schnelle 		if (!__test_facility(a->facility, alt_stfle_fac_list))
35686140a1SVasily Gorbik 			continue;
36686140a1SVasily Gorbik 
37*e6ed91fdSHeiko Carstens 		if (unlikely(a->instrlen % 2)) {
38686140a1SVasily Gorbik 			WARN_ONCE(1, "cpu alternatives instructions length is "
39686140a1SVasily Gorbik 				     "odd, skipping patching\n");
40686140a1SVasily Gorbik 			continue;
41686140a1SVasily Gorbik 		}
42686140a1SVasily Gorbik 
43*e6ed91fdSHeiko Carstens 		s390_kernel_write(instr, replacement, a->instrlen);
44686140a1SVasily Gorbik 	}
45686140a1SVasily Gorbik }
46686140a1SVasily Gorbik 
apply_alternatives(struct alt_instr * start,struct alt_instr * end)47686140a1SVasily Gorbik void __init_or_module apply_alternatives(struct alt_instr *start,
48686140a1SVasily Gorbik 					 struct alt_instr *end)
49686140a1SVasily Gorbik {
50686140a1SVasily Gorbik 	if (!alt_instr_disabled)
51686140a1SVasily Gorbik 		__apply_alternatives(start, end);
52686140a1SVasily Gorbik }
53686140a1SVasily Gorbik 
54686140a1SVasily Gorbik extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
apply_alternative_instructions(void)55686140a1SVasily Gorbik void __init apply_alternative_instructions(void)
56686140a1SVasily Gorbik {
57686140a1SVasily Gorbik 	apply_alternatives(__alt_instructions, __alt_instructions_end);
58686140a1SVasily Gorbik }
59e16d02eeSHeiko Carstens 
do_sync_core(void * info)60e16d02eeSHeiko Carstens static void do_sync_core(void *info)
61e16d02eeSHeiko Carstens {
62e16d02eeSHeiko Carstens 	sync_core();
63e16d02eeSHeiko Carstens }
64e16d02eeSHeiko Carstens 
text_poke_sync(void)65e16d02eeSHeiko Carstens void text_poke_sync(void)
66e16d02eeSHeiko Carstens {
67e16d02eeSHeiko Carstens 	on_each_cpu(do_sync_core, NULL, 1);
68e16d02eeSHeiko Carstens }
69e16d02eeSHeiko Carstens 
text_poke_sync_lock(void)70e16d02eeSHeiko Carstens void text_poke_sync_lock(void)
71e16d02eeSHeiko Carstens {
72e16d02eeSHeiko Carstens 	cpus_read_lock();
73e16d02eeSHeiko Carstens 	text_poke_sync();
74e16d02eeSHeiko Carstens 	cpus_read_unlock();
75e16d02eeSHeiko Carstens }
76