Merge branch 'for-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
[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 struct brcl_insn {
19         u16 opc;
20         s32 disp;
21 } __packed;
22
23 static u16 __initdata_or_module nop16 = 0x0700;
24 static u32 __initdata_or_module nop32 = 0x47000000;
25 static struct brcl_insn __initdata_or_module nop48 = {
26         0xc004, 0
27 };
28
29 static const void *nops[] __initdata_or_module = {
30         &nop16,
31         &nop32,
32         &nop48
33 };
34
35 static void __init_or_module add_jump_padding(void *insns, unsigned int len)
36 {
37         struct brcl_insn brcl = {
38                 0xc0f4,
39                 len / 2
40         };
41
42         memcpy(insns, &brcl, sizeof(brcl));
43         insns += sizeof(brcl);
44         len -= sizeof(brcl);
45
46         while (len > 0) {
47                 memcpy(insns, &nop16, 2);
48                 insns += 2;
49                 len -= 2;
50         }
51 }
52
53 static void __init_or_module add_padding(void *insns, unsigned int len)
54 {
55         if (len > 6)
56                 add_jump_padding(insns, len);
57         else if (len >= 2)
58                 memcpy(insns, nops[len / 2 - 1], len);
59 }
60
61 static void __init_or_module __apply_alternatives(struct alt_instr *start,
62                                                   struct alt_instr *end)
63 {
64         struct alt_instr *a;
65         u8 *instr, *replacement;
66         u8 insnbuf[MAX_PATCH_LEN];
67
68         /*
69          * The scan order should be from start to end. A later scanned
70          * alternative code can overwrite previously scanned alternative code.
71          */
72         for (a = start; a < end; a++) {
73                 int insnbuf_sz = 0;
74
75                 instr = (u8 *)&a->instr_offset + a->instr_offset;
76                 replacement = (u8 *)&a->repl_offset + a->repl_offset;
77
78                 if (!test_facility(a->facility))
79                         continue;
80
81                 if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
82                         WARN_ONCE(1, "cpu alternatives instructions length is "
83                                      "odd, skipping patching\n");
84                         continue;
85                 }
86
87                 memcpy(insnbuf, replacement, a->replacementlen);
88                 insnbuf_sz = a->replacementlen;
89
90                 if (a->instrlen > a->replacementlen) {
91                         add_padding(insnbuf + a->replacementlen,
92                                     a->instrlen - a->replacementlen);
93                         insnbuf_sz += a->instrlen - a->replacementlen;
94                 }
95
96                 s390_kernel_write(instr, insnbuf, insnbuf_sz);
97         }
98 }
99
100 void __init_or_module apply_alternatives(struct alt_instr *start,
101                                          struct alt_instr *end)
102 {
103         if (!alt_instr_disabled)
104                 __apply_alternatives(start, end);
105 }
106
107 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
108 void __init apply_alternative_instructions(void)
109 {
110         apply_alternatives(__alt_instructions, __alt_instructions_end);
111 }