Commit e6ed91fd authored by Heiko Carstens's avatar Heiko Carstens

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: default avatarVasily Gorbik <gor@linux.ibm.com>
Tested-by: default avatarNathan Chancellor <nathan@kernel.org>
Tested-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Link: https://github.com/ClangBuiltLinux/linux/issues/1356
Link: https://lore.kernel.org/r/20220511120532.2228616-3-hca@linux.ibm.comSigned-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent fad442d3
...@@ -4,19 +4,6 @@ ...@@ -4,19 +4,6 @@
#ifdef __ASSEMBLY__ #ifdef __ASSEMBLY__
/*
* Check the length of an instruction sequence. The length may not be larger
* than 254 bytes and it has to be divisible by 2.
*/
.macro alt_len_check start,end
.if ( \end - \start ) > 254
.error "cpu alternatives does not support instructions blocks > 254 bytes\n"
.endif
.if ( \end - \start ) % 2
.error "cpu alternatives instructions length is odd\n"
.endif
.endm
/* /*
* Issue one struct alt_instr descriptor entry (need to put it into * Issue one struct alt_instr descriptor entry (need to put it into
* the section .altinstructions, see below). This entry contains * the section .altinstructions, see below). This entry contains
...@@ -28,66 +15,29 @@ ...@@ -28,66 +15,29 @@
.long \alt_start - . .long \alt_start - .
.word \feature .word \feature
.byte \orig_end - \orig_start .byte \orig_end - \orig_start
.byte \alt_end - \alt_start .org . - ( \orig_end - \orig_start ) + ( \alt_end - \alt_start )
.endm .org . - ( \alt_end - \alt_start ) + ( \orig_end - \orig_start )
/*
* Fill up @bytes with nops. The macro emits 6-byte nop instructions
* for the bulk of the area, possibly followed by a 4-byte and/or
* a 2-byte nop if the size of the area is not divisible by 6.
*/
.macro alt_pad_fill bytes
.rept ( \bytes ) / 6
brcl 0,0
.endr
.rept ( \bytes ) % 6 / 4
nop
.endr
.rept ( \bytes ) % 6 % 4 / 2
nopr
.endr
.endm
/*
* Fill up @bytes with nops. If the number of bytes is larger
* than 6, emit a jg instruction to branch over all nops, then
* fill an area of size (@bytes - 6) with nop instructions.
*/
.macro alt_pad bytes
.if ( \bytes > 0 )
.if ( \bytes > 6 )
jg . + \bytes
alt_pad_fill \bytes - 6
.else
alt_pad_fill \bytes
.endif
.endif
.endm .endm
/* /*
* Define an alternative between two instructions. If @feature is * Define an alternative between two instructions. If @feature is
* present, early code in apply_alternatives() replaces @oldinstr with * present, early code in apply_alternatives() replaces @oldinstr with
* @newinstr. ".skip" directive takes care of proper instruction padding * @newinstr.
* in case @newinstr is longer than @oldinstr.
*/ */
.macro ALTERNATIVE oldinstr, newinstr, feature .macro ALTERNATIVE oldinstr, newinstr, feature
.pushsection .altinstr_replacement,"ax" .pushsection .altinstr_replacement,"ax"
770: \newinstr 770: \newinstr
771: .popsection 771: .popsection
772: \oldinstr 772: \oldinstr
773: alt_len_check 770b, 771b 773: .pushsection .altinstructions,"a"
alt_len_check 772b, 773b alt_entry 772b, 773b, 770b, 771b, \feature
alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) )
774: .pushsection .altinstructions,"a"
alt_entry 772b, 774b, 770b, 771b, \feature
.popsection .popsection
.endm .endm
/* /*
* Define an alternative between two instructions. If @feature is * Define an alternative between two instructions. If @feature is
* present, early code in apply_alternatives() replaces @oldinstr with * present, early code in apply_alternatives() replaces @oldinstr with
* @newinstr. ".skip" directive takes care of proper instruction padding * @newinstr.
* in case @newinstr is longer than @oldinstr.
*/ */
.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2 .macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
.pushsection .altinstr_replacement,"ax" .pushsection .altinstr_replacement,"ax"
...@@ -95,17 +45,9 @@ ...@@ -95,17 +45,9 @@
771: \newinstr2 771: \newinstr2
772: .popsection 772: .popsection
773: \oldinstr 773: \oldinstr
774: alt_len_check 770b, 771b 774: .pushsection .altinstructions,"a"
alt_len_check 771b, 772b alt_entry 773b, 774b, 770b, 771b,\feature1
alt_len_check 773b, 774b alt_entry 773b, 774b, 771b, 772b,\feature2
.if ( 771b - 770b > 772b - 771b )
alt_pad ( ( 771b - 770b ) - ( 774b - 773b ) )
.else
alt_pad ( ( 772b - 771b ) - ( 774b - 773b ) )
.endif
775: .pushsection .altinstructions,"a"
alt_entry 773b, 775b, 770b, 771b,\feature1
alt_entry 773b, 775b, 771b, 772b,\feature2
.popsection .popsection
.endm .endm
......
...@@ -13,32 +13,25 @@ struct alt_instr { ...@@ -13,32 +13,25 @@ struct alt_instr {
s32 repl_offset; /* offset to replacement instruction */ s32 repl_offset; /* offset to replacement instruction */
u16 facility; /* facility bit set for replacement */ u16 facility; /* facility bit set for replacement */
u8 instrlen; /* length of original instruction */ u8 instrlen; /* length of original instruction */
u8 replacementlen; /* length of new instruction */
} __packed; } __packed;
void apply_alternative_instructions(void); void apply_alternative_instructions(void);
void apply_alternatives(struct alt_instr *start, struct alt_instr *end); void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
/* /*
* |661: |662: |6620 |663: * +---------------------------------+
* +-----------+---------------------+ * |661: |662:
* | oldinstr | oldinstr_padding | * | oldinstr |
* | +----------+----------+ * +---------------------------------+
* | | | |
* | | >6 bytes |6/4/2 nops|
* | |6 bytes jg----------->
* +-----------+---------------------+
* ^^ static padding ^^
* *
* .altinstr_replacement section * .altinstr_replacement section
* +---------------------+-----------+ * +---------------------------------+
* |6641: |6651: * |6641: |6651:
* | alternative instr 1 | * | alternative instr 1 |
* +-----------+---------+- - - - - -+ * +---------------------------------+
* |6642: |6652: | * |6642: |6652:
* | alternative instr 2 | padding * | alternative instr 2 |
* +---------------------+- - - - - -+ * +---------------------------------+
* ^ runtime ^
* *
* .altinstructions section * .altinstructions section
* +---------------------------------+ * +---------------------------------+
...@@ -49,75 +42,29 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end); ...@@ -49,75 +42,29 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
#define b_altinstr(num) "664"#num #define b_altinstr(num) "664"#num
#define e_altinstr(num) "665"#num #define e_altinstr(num) "665"#num
#define e_oldinstr_pad_end "663"
#define oldinstr_len "662b-661b" #define oldinstr_len "662b-661b"
#define oldinstr_total_len e_oldinstr_pad_end"b-661b"
#define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b" #define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b"
#define oldinstr_pad_len(num) \
"-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \ #define OLDINSTR(oldinstr) \
"((" altinstr_len(num) ")-(" oldinstr_len "))" "661:\n\t" oldinstr "\n662:\n"
#define INSTR_LEN_SANITY_CHECK(len) \
".if " len " > 254\n" \
"\t.error \"cpu alternatives does not support instructions " \
"blocks > 254 bytes\"\n" \
".endif\n" \
".if (" len ") %% 2\n" \
"\t.error \"cpu alternatives instructions length is odd\"\n" \
".endif\n"
#define OLDINSTR_PADDING(oldinstr, num) \
".if " oldinstr_pad_len(num) " > 6\n" \
"\tjg " e_oldinstr_pad_end "f\n" \
"6620:\n" \
"\t.rept (" oldinstr_pad_len(num) " - (6620b-662b)) / 2\n" \
"\tnopr\n" \
".else\n" \
"\t.rept " oldinstr_pad_len(num) " / 6\n" \
"\t.brcl 0,0\n" \
"\t.endr\n" \
"\t.rept " oldinstr_pad_len(num) " %% 6 / 4\n" \
"\tnop\n" \
"\t.endr\n" \
"\t.rept " oldinstr_pad_len(num) " %% 6 %% 4 / 2\n" \
"\tnopr\n" \
".endr\n" \
".endif\n"
#define OLDINSTR(oldinstr, num) \
"661:\n\t" oldinstr "\n662:\n" \
OLDINSTR_PADDING(oldinstr, num) \
e_oldinstr_pad_end ":\n" \
INSTR_LEN_SANITY_CHECK(oldinstr_len)
#define OLDINSTR_2(oldinstr, num1, num2) \
"661:\n\t" oldinstr "\n662:\n" \
".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \
OLDINSTR_PADDING(oldinstr, num2) \
".else\n" \
OLDINSTR_PADDING(oldinstr, num1) \
".endif\n" \
e_oldinstr_pad_end ":\n" \
INSTR_LEN_SANITY_CHECK(oldinstr_len)
#define ALTINSTR_ENTRY(facility, num) \ #define ALTINSTR_ENTRY(facility, num) \
"\t.long 661b - .\n" /* old instruction */ \ "\t.long 661b - .\n" /* old instruction */ \
"\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \ "\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \
"\t.word " __stringify(facility) "\n" /* facility bit */ \ "\t.word " __stringify(facility) "\n" /* facility bit */ \
"\t.byte " oldinstr_total_len "\n" /* source len */ \ "\t.byte " oldinstr_len "\n" /* instruction len */ \
"\t.byte " altinstr_len(num) "\n" /* alt instruction len */ "\t.org . - (" oldinstr_len ") + (" altinstr_len(num) ")\n" \
"\t.org . - (" altinstr_len(num) ") + (" oldinstr_len ")\n"
#define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \ #define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \
b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \ b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n"
INSTR_LEN_SANITY_CHECK(altinstr_len(num))
/* alternative assembly primitive: */ /* alternative assembly primitive: */
#define ALTERNATIVE(oldinstr, altinstr, facility) \ #define ALTERNATIVE(oldinstr, altinstr, facility) \
".pushsection .altinstr_replacement, \"ax\"\n" \ ".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(altinstr, 1) \ ALTINSTR_REPLACEMENT(altinstr, 1) \
".popsection\n" \ ".popsection\n" \
OLDINSTR(oldinstr, 1) \ OLDINSTR(oldinstr) \
".pushsection .altinstructions,\"a\"\n" \ ".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(facility, 1) \ ALTINSTR_ENTRY(facility, 1) \
".popsection\n" ".popsection\n"
...@@ -127,7 +74,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end); ...@@ -127,7 +74,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
ALTINSTR_REPLACEMENT(altinstr1, 1) \ ALTINSTR_REPLACEMENT(altinstr1, 1) \
ALTINSTR_REPLACEMENT(altinstr2, 2) \ ALTINSTR_REPLACEMENT(altinstr2, 2) \
".popsection\n" \ ".popsection\n" \
OLDINSTR_2(oldinstr, 1, 2) \ OLDINSTR(oldinstr) \
".pushsection .altinstructions,\"a\"\n" \ ".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(facility1, 1) \ ALTINSTR_ENTRY(facility1, 1) \
ALTINSTR_ENTRY(facility2, 2) \ ALTINSTR_ENTRY(facility2, 2) \
......
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
#include <asm/facility.h> #include <asm/facility.h>
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
#define MAX_PATCH_LEN (255 - 1)
static int __initdata_or_module alt_instr_disabled; static int __initdata_or_module alt_instr_disabled;
static int __init disable_alternative_instructions(char *str) static int __init disable_alternative_instructions(char *str)
...@@ -19,85 +17,30 @@ static int __init disable_alternative_instructions(char *str) ...@@ -19,85 +17,30 @@ static int __init disable_alternative_instructions(char *str)
early_param("noaltinstr", disable_alternative_instructions); early_param("noaltinstr", disable_alternative_instructions);
struct brcl_insn {
u16 opc;
s32 disp;
} __packed;
static u16 __initdata_or_module nop16 = 0x0700;
static u32 __initdata_or_module nop32 = 0x47000000;
static struct brcl_insn __initdata_or_module nop48 = {
0xc004, 0
};
static const void *nops[] __initdata_or_module = {
&nop16,
&nop32,
&nop48
};
static void __init_or_module add_jump_padding(void *insns, unsigned int len)
{
struct brcl_insn brcl = {
0xc0f4,
len / 2
};
memcpy(insns, &brcl, sizeof(brcl));
insns += sizeof(brcl);
len -= sizeof(brcl);
while (len > 0) {
memcpy(insns, &nop16, 2);
insns += 2;
len -= 2;
}
}
static void __init_or_module add_padding(void *insns, unsigned int len)
{
if (len > 6)
add_jump_padding(insns, len);
else if (len >= 2)
memcpy(insns, nops[len / 2 - 1], len);
}
static void __init_or_module __apply_alternatives(struct alt_instr *start, static void __init_or_module __apply_alternatives(struct alt_instr *start,
struct alt_instr *end) struct alt_instr *end)
{ {
struct alt_instr *a; struct alt_instr *a;
u8 *instr, *replacement; u8 *instr, *replacement;
u8 insnbuf[MAX_PATCH_LEN];
/* /*
* The scan order should be from start to end. A later scanned * The scan order should be from start to end. A later scanned
* alternative code can overwrite previously scanned alternative code. * alternative code can overwrite previously scanned alternative code.
*/ */
for (a = start; a < end; a++) { for (a = start; a < end; a++) {
int insnbuf_sz = 0;
instr = (u8 *)&a->instr_offset + a->instr_offset; instr = (u8 *)&a->instr_offset + a->instr_offset;
replacement = (u8 *)&a->repl_offset + a->repl_offset; replacement = (u8 *)&a->repl_offset + a->repl_offset;
if (!__test_facility(a->facility, alt_stfle_fac_list)) if (!__test_facility(a->facility, alt_stfle_fac_list))
continue; continue;
if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) { if (unlikely(a->instrlen % 2)) {
WARN_ONCE(1, "cpu alternatives instructions length is " WARN_ONCE(1, "cpu alternatives instructions length is "
"odd, skipping patching\n"); "odd, skipping patching\n");
continue; continue;
} }
memcpy(insnbuf, replacement, a->replacementlen); s390_kernel_write(instr, replacement, a->instrlen);
insnbuf_sz = a->replacementlen;
if (a->instrlen > a->replacementlen) {
add_padding(insnbuf + a->replacementlen,
a->instrlen - a->replacementlen);
insnbuf_sz += a->instrlen - a->replacementlen;
}
s390_kernel_write(instr, insnbuf, insnbuf_sz);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment