Skip to content

Commit e6ed91f

Browse files
committed
s390/alternatives: remove padding generation code
clang fails to handle ".if" statements in inline assembly which are heavily used in the alternatives code. To work around this remove this code, and enforce that users of alternatives must specify original and alternative instruction sequences which have identical sizes. Add a compile time check with two ".org" statements similar to arm64. In result not only clang can handle this, but also quite a lot of code can be removed. Acked-by: Vasily Gorbik <[email protected]> Tested-by: Nathan Chancellor <[email protected]> Tested-by: Nick Desaulniers <[email protected]> Link: #1356 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Heiko Carstens <[email protected]>
1 parent fad442d commit e6ed91f

File tree

3 files changed

+31
-199
lines changed

3 files changed

+31
-199
lines changed

arch/s390/include/asm/alternative-asm.h

Lines changed: 9 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,6 @@
44

55
#ifdef __ASSEMBLY__
66

7-
/*
8-
* Check the length of an instruction sequence. The length may not be larger
9-
* than 254 bytes and it has to be divisible by 2.
10-
*/
11-
.macro alt_len_check start,end
12-
.if ( \end - \start ) > 254
13-
.error "cpu alternatives does not support instructions blocks > 254 bytes\n"
14-
.endif
15-
.if ( \end - \start ) % 2
16-
.error "cpu alternatives instructions length is odd\n"
17-
.endif
18-
.endm
19-
207
/*
218
* Issue one struct alt_instr descriptor entry (need to put it into
229
* the section .altinstructions, see below). This entry contains
@@ -28,84 +15,39 @@
2815
.long \alt_start - .
2916
.word \feature
3017
.byte \orig_end - \orig_start
31-
.byte \alt_end - \alt_start
32-
.endm
33-
34-
/*
35-
* Fill up @bytes with nops. The macro emits 6-byte nop instructions
36-
* for the bulk of the area, possibly followed by a 4-byte and/or
37-
* a 2-byte nop if the size of the area is not divisible by 6.
38-
*/
39-
.macro alt_pad_fill bytes
40-
.rept ( \bytes ) / 6
41-
brcl 0,0
42-
.endr
43-
.rept ( \bytes ) % 6 / 4
44-
nop
45-
.endr
46-
.rept ( \bytes ) % 6 % 4 / 2
47-
nopr
48-
.endr
49-
.endm
50-
51-
/*
52-
* Fill up @bytes with nops. If the number of bytes is larger
53-
* than 6, emit a jg instruction to branch over all nops, then
54-
* fill an area of size (@bytes - 6) with nop instructions.
55-
*/
56-
.macro alt_pad bytes
57-
.if ( \bytes > 0 )
58-
.if ( \bytes > 6 )
59-
jg . + \bytes
60-
alt_pad_fill \bytes - 6
61-
.else
62-
alt_pad_fill \bytes
63-
.endif
64-
.endif
18+
.org . - ( \orig_end - \orig_start ) + ( \alt_end - \alt_start )
19+
.org . - ( \alt_end - \alt_start ) + ( \orig_end - \orig_start )
6520
.endm
6621

6722
/*
6823
* Define an alternative between two instructions. If @feature is
6924
* present, early code in apply_alternatives() replaces @oldinstr with
70-
* @newinstr. ".skip" directive takes care of proper instruction padding
71-
* in case @newinstr is longer than @oldinstr.
25+
* @newinstr.
7226
*/
7327
.macro ALTERNATIVE oldinstr, newinstr, feature
7428
.pushsection .altinstr_replacement,"ax"
7529
770: \newinstr
7630
771: .popsection
7731
772: \oldinstr
78-
773: alt_len_check 770b, 771b
79-
alt_len_check 772b, 773b
80-
alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) )
81-
774: .pushsection .altinstructions,"a"
82-
alt_entry 772b, 774b, 770b, 771b, \feature
32+
773: .pushsection .altinstructions,"a"
33+
alt_entry 772b, 773b, 770b, 771b, \feature
8334
.popsection
8435
.endm
8536

8637
/*
8738
* Define an alternative between two instructions. If @feature is
8839
* present, early code in apply_alternatives() replaces @oldinstr with
89-
* @newinstr. ".skip" directive takes care of proper instruction padding
90-
* in case @newinstr is longer than @oldinstr.
40+
* @newinstr.
9141
*/
9242
.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
9343
.pushsection .altinstr_replacement,"ax"
9444
770: \newinstr1
9545
771: \newinstr2
9646
772: .popsection
9747
773: \oldinstr
98-
774: alt_len_check 770b, 771b
99-
alt_len_check 771b, 772b
100-
alt_len_check 773b, 774b
101-
.if ( 771b - 770b > 772b - 771b )
102-
alt_pad ( ( 771b - 770b ) - ( 774b - 773b ) )
103-
.else
104-
alt_pad ( ( 772b - 771b ) - ( 774b - 773b ) )
105-
.endif
106-
775: .pushsection .altinstructions,"a"
107-
alt_entry 773b, 775b, 770b, 771b,\feature1
108-
alt_entry 773b, 775b, 771b, 772b,\feature2
48+
774: .pushsection .altinstructions,"a"
49+
alt_entry 773b, 774b, 770b, 771b,\feature1
50+
alt_entry 773b, 774b, 771b, 772b,\feature2
10951
.popsection
11052
.endm
11153

arch/s390/include/asm/alternative.h

Lines changed: 20 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,25 @@ struct alt_instr {
1313
s32 repl_offset; /* offset to replacement instruction */
1414
u16 facility; /* facility bit set for replacement */
1515
u8 instrlen; /* length of original instruction */
16-
u8 replacementlen; /* length of new instruction */
1716
} __packed;
1817

1918
void apply_alternative_instructions(void);
2019
void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
2120

2221
/*
23-
* |661: |662: |6620 |663:
24-
* +-----------+---------------------+
25-
* | oldinstr | oldinstr_padding |
26-
* | +----------+----------+
27-
* | | | |
28-
* | | >6 bytes |6/4/2 nops|
29-
* | |6 bytes jg----------->
30-
* +-----------+---------------------+
31-
* ^^ static padding ^^
22+
* +---------------------------------+
23+
* |661: |662:
24+
* | oldinstr |
25+
* +---------------------------------+
3226
*
3327
* .altinstr_replacement section
34-
* +---------------------+-----------+
28+
* +---------------------------------+
3529
* |6641: |6651:
3630
* | alternative instr 1 |
37-
* +-----------+---------+- - - - - -+
38-
* |6642: |6652: |
39-
* | alternative instr 2 | padding
40-
* +---------------------+- - - - - -+
41-
* ^ runtime ^
31+
* +---------------------------------+
32+
* |6642: |6652:
33+
* | alternative instr 2 |
34+
* +---------------------------------+
4235
*
4336
* .altinstructions section
4437
* +---------------------------------+
@@ -47,77 +40,31 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
4740
* +---------------------------------+
4841
*/
4942

50-
#define b_altinstr(num) "664"#num
51-
#define e_altinstr(num) "665"#num
52-
53-
#define e_oldinstr_pad_end "663"
43+
#define b_altinstr(num) "664"#num
44+
#define e_altinstr(num) "665"#num
5445
#define oldinstr_len "662b-661b"
55-
#define oldinstr_total_len e_oldinstr_pad_end"b-661b"
5646
#define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b"
57-
#define oldinstr_pad_len(num) \
58-
"-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \
59-
"((" altinstr_len(num) ")-(" oldinstr_len "))"
60-
61-
#define INSTR_LEN_SANITY_CHECK(len) \
62-
".if " len " > 254\n" \
63-
"\t.error \"cpu alternatives does not support instructions " \
64-
"blocks > 254 bytes\"\n" \
65-
".endif\n" \
66-
".if (" len ") %% 2\n" \
67-
"\t.error \"cpu alternatives instructions length is odd\"\n" \
68-
".endif\n"
69-
70-
#define OLDINSTR_PADDING(oldinstr, num) \
71-
".if " oldinstr_pad_len(num) " > 6\n" \
72-
"\tjg " e_oldinstr_pad_end "f\n" \
73-
"6620:\n" \
74-
"\t.rept (" oldinstr_pad_len(num) " - (6620b-662b)) / 2\n" \
75-
"\tnopr\n" \
76-
".else\n" \
77-
"\t.rept " oldinstr_pad_len(num) " / 6\n" \
78-
"\t.brcl 0,0\n" \
79-
"\t.endr\n" \
80-
"\t.rept " oldinstr_pad_len(num) " %% 6 / 4\n" \
81-
"\tnop\n" \
82-
"\t.endr\n" \
83-
"\t.rept " oldinstr_pad_len(num) " %% 6 %% 4 / 2\n" \
84-
"\tnopr\n" \
85-
".endr\n" \
86-
".endif\n"
87-
88-
#define OLDINSTR(oldinstr, num) \
89-
"661:\n\t" oldinstr "\n662:\n" \
90-
OLDINSTR_PADDING(oldinstr, num) \
91-
e_oldinstr_pad_end ":\n" \
92-
INSTR_LEN_SANITY_CHECK(oldinstr_len)
93-
94-
#define OLDINSTR_2(oldinstr, num1, num2) \
95-
"661:\n\t" oldinstr "\n662:\n" \
96-
".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \
97-
OLDINSTR_PADDING(oldinstr, num2) \
98-
".else\n" \
99-
OLDINSTR_PADDING(oldinstr, num1) \
100-
".endif\n" \
101-
e_oldinstr_pad_end ":\n" \
102-
INSTR_LEN_SANITY_CHECK(oldinstr_len)
47+
48+
#define OLDINSTR(oldinstr) \
49+
"661:\n\t" oldinstr "\n662:\n"
10350

10451
#define ALTINSTR_ENTRY(facility, num) \
10552
"\t.long 661b - .\n" /* old instruction */ \
10653
"\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \
10754
"\t.word " __stringify(facility) "\n" /* facility bit */ \
108-
"\t.byte " oldinstr_total_len "\n" /* source len */ \
109-
"\t.byte " altinstr_len(num) "\n" /* alt instruction len */
55+
"\t.byte " oldinstr_len "\n" /* instruction len */ \
56+
"\t.org . - (" oldinstr_len ") + (" altinstr_len(num) ")\n" \
57+
"\t.org . - (" altinstr_len(num) ") + (" oldinstr_len ")\n"
11058

11159
#define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \
112-
b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \
113-
INSTR_LEN_SANITY_CHECK(altinstr_len(num))
60+
b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n"
11461

11562
/* alternative assembly primitive: */
11663
#define ALTERNATIVE(oldinstr, altinstr, facility) \
11764
".pushsection .altinstr_replacement, \"ax\"\n" \
11865
ALTINSTR_REPLACEMENT(altinstr, 1) \
11966
".popsection\n" \
120-
OLDINSTR(oldinstr, 1) \
67+
OLDINSTR(oldinstr) \
12168
".pushsection .altinstructions,\"a\"\n" \
12269
ALTINSTR_ENTRY(facility, 1) \
12370
".popsection\n"
@@ -127,7 +74,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
12774
ALTINSTR_REPLACEMENT(altinstr1, 1) \
12875
ALTINSTR_REPLACEMENT(altinstr2, 2) \
12976
".popsection\n" \
130-
OLDINSTR_2(oldinstr, 1, 2) \
77+
OLDINSTR(oldinstr) \
13178
".pushsection .altinstructions,\"a\"\n" \
13279
ALTINSTR_ENTRY(facility1, 1) \
13380
ALTINSTR_ENTRY(facility2, 2) \

arch/s390/kernel/alternative.c

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
#include <asm/facility.h>
88
#include <asm/nospec-branch.h>
99

10-
#define MAX_PATCH_LEN (255 - 1)
11-
1210
static int __initdata_or_module alt_instr_disabled;
1311

1412
static int __init disable_alternative_instructions(char *str)
@@ -19,85 +17,30 @@ static int __init disable_alternative_instructions(char *str)
1917

2018
early_param("noaltinstr", disable_alternative_instructions);
2119

22-
struct brcl_insn {
23-
u16 opc;
24-
s32 disp;
25-
} __packed;
26-
27-
static u16 __initdata_or_module nop16 = 0x0700;
28-
static u32 __initdata_or_module nop32 = 0x47000000;
29-
static struct brcl_insn __initdata_or_module nop48 = {
30-
0xc004, 0
31-
};
32-
33-
static const void *nops[] __initdata_or_module = {
34-
&nop16,
35-
&nop32,
36-
&nop48
37-
};
38-
39-
static void __init_or_module add_jump_padding(void *insns, unsigned int len)
40-
{
41-
struct brcl_insn brcl = {
42-
0xc0f4,
43-
len / 2
44-
};
45-
46-
memcpy(insns, &brcl, sizeof(brcl));
47-
insns += sizeof(brcl);
48-
len -= sizeof(brcl);
49-
50-
while (len > 0) {
51-
memcpy(insns, &nop16, 2);
52-
insns += 2;
53-
len -= 2;
54-
}
55-
}
56-
57-
static void __init_or_module add_padding(void *insns, unsigned int len)
58-
{
59-
if (len > 6)
60-
add_jump_padding(insns, len);
61-
else if (len >= 2)
62-
memcpy(insns, nops[len / 2 - 1], len);
63-
}
64-
6520
static void __init_or_module __apply_alternatives(struct alt_instr *start,
6621
struct alt_instr *end)
6722
{
6823
struct alt_instr *a;
6924
u8 *instr, *replacement;
70-
u8 insnbuf[MAX_PATCH_LEN];
7125

7226
/*
7327
* The scan order should be from start to end. A later scanned
7428
* alternative code can overwrite previously scanned alternative code.
7529
*/
7630
for (a = start; a < end; a++) {
77-
int insnbuf_sz = 0;
78-
7931
instr = (u8 *)&a->instr_offset + a->instr_offset;
8032
replacement = (u8 *)&a->repl_offset + a->repl_offset;
8133

8234
if (!__test_facility(a->facility, alt_stfle_fac_list))
8335
continue;
8436

85-
if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
37+
if (unlikely(a->instrlen % 2)) {
8638
WARN_ONCE(1, "cpu alternatives instructions length is "
8739
"odd, skipping patching\n");
8840
continue;
8941
}
9042

91-
memcpy(insnbuf, replacement, a->replacementlen);
92-
insnbuf_sz = a->replacementlen;
93-
94-
if (a->instrlen > a->replacementlen) {
95-
add_padding(insnbuf + a->replacementlen,
96-
a->instrlen - a->replacementlen);
97-
insnbuf_sz += a->instrlen - a->replacementlen;
98-
}
99-
100-
s390_kernel_write(instr, insnbuf, insnbuf_sz);
43+
s390_kernel_write(instr, replacement, a->instrlen);
10144
}
10245
}
10346

0 commit comments

Comments
 (0)