Merge tag '4.16-minor-rc-SMB3-fixes' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / arch / s390 / kernel / alternative.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/module.h>
3 #include <asm/alternative.h>
4 #include <asm/facility.h>
5
6 #define MAX_PATCH_LEN (255 - 1)
7
8 static int __initdata_or_module alt_instr_disabled;
9
10 static int __init disable_alternative_instructions(char *str)
11 {
12         alt_instr_disabled = 1;
13         return 0;
14 }
15
16 early_param("noaltinstr", disable_alternative_instructions);
17
18 static int __init nobp_setup_early(char *str)
19 {
20         bool enabled;
21         int rc;
22
23         rc = kstrtobool(str, &enabled);
24         if (rc)
25                 return rc;
26         if (enabled && test_facility(82))
27                 __set_facility(82, S390_lowcore.alt_stfle_fac_list);
28         else
29                 __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
30         return 0;
31 }
32 early_param("nobp", nobp_setup_early);
33
34 static int __init nospec_setup_early(char *str)
35 {
36         __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
37         return 0;
38 }
39 early_param("nospec", nospec_setup_early);
40
41 struct brcl_insn {
42         u16 opc;
43         s32 disp;
44 } __packed;
45
46 static u16 __initdata_or_module nop16 = 0x0700;
47 static u32 __initdata_or_module nop32 = 0x47000000;
48 static struct brcl_insn __initdata_or_module nop48 = {
49         0xc004, 0
50 };
51
52 static const void *nops[] __initdata_or_module = {
53         &nop16,
54         &nop32,
55         &nop48
56 };
57
58 static void __init_or_module add_jump_padding(void *insns, unsigned int len)
59 {
60         struct brcl_insn brcl = {
61                 0xc0f4,
62                 len / 2
63         };
64
65         memcpy(insns, &brcl, sizeof(brcl));
66         insns += sizeof(brcl);
67         len -= sizeof(brcl);
68
69         while (len > 0) {
70                 memcpy(insns, &nop16, 2);
71                 insns += 2;
72                 len -= 2;
73         }
74 }
75
76 static void __init_or_module add_padding(void *insns, unsigned int len)
77 {
78         if (len > 6)
79                 add_jump_padding(insns, len);
80         else if (len >= 2)
81                 memcpy(insns, nops[len / 2 - 1], len);
82 }
83
84 static void __init_or_module __apply_alternatives(struct alt_instr *start,
85                                                   struct alt_instr *end)
86 {
87         struct alt_instr *a;
88         u8 *instr, *replacement;
89         u8 insnbuf[MAX_PATCH_LEN];
90
91         /*
92          * The scan order should be from start to end. A later scanned
93          * alternative code can overwrite previously scanned alternative code.
94          */
95         for (a = start; a < end; a++) {
96                 int insnbuf_sz = 0;
97
98                 instr = (u8 *)&a->instr_offset + a->instr_offset;
99                 replacement = (u8 *)&a->repl_offset + a->repl_offset;
100
101                 if (!__test_facility(a->facility,
102                                      S390_lowcore.alt_stfle_fac_list))
103                         continue;
104
105                 if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
106                         WARN_ONCE(1, "cpu alternatives instructions length is "
107                                      "odd, skipping patching\n");
108                         continue;
109                 }
110
111                 memcpy(insnbuf, replacement, a->replacementlen);
112                 insnbuf_sz = a->replacementlen;
113
114                 if (a->instrlen > a->replacementlen) {
115                         add_padding(insnbuf + a->replacementlen,
116                                     a->instrlen - a->replacementlen);
117                         insnbuf_sz += a->instrlen - a->replacementlen;
118                 }
119
120                 s390_kernel_write(instr, insnbuf, insnbuf_sz);
121         }
122 }
123
124 void __init_or_module apply_alternatives(struct alt_instr *start,
125                                          struct alt_instr *end)
126 {
127         if (!alt_instr_disabled)
128                 __apply_alternatives(start, end);
129 }
130
131 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
132 void __init apply_alternative_instructions(void)
133 {
134         apply_alternatives(__alt_instructions, __alt_instructions_end);
135 }