mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-16 17:23:55 +08:00
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 <gor@linux.ibm.com> Tested-by: Nathan Chancellor <nathan@kernel.org> Tested-by: Nick Desaulniers <ndesaulniers@google.com> Link: https://github.com/ClangBuiltLinux/linux/issues/1356 Link: https://lore.kernel.org/r/20220511120532.2228616-3-hca@linux.ibm.com Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
This commit is contained in:
parent
fad442d3ab
commit
e6ed91fd07
@ -4,19 +4,6 @@
|
||||
|
||||
#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
|
||||
* the section .altinstructions, see below). This entry contains
|
||||
@ -28,66 +15,29 @@
|
||||
.long \alt_start - .
|
||||
.word \feature
|
||||
.byte \orig_end - \orig_start
|
||||
.byte \alt_end - \alt_start
|
||||
.endm
|
||||
|
||||
/*
|
||||
* 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
|
||||
.org . - ( \orig_end - \orig_start ) + ( \alt_end - \alt_start )
|
||||
.org . - ( \alt_end - \alt_start ) + ( \orig_end - \orig_start )
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Define an alternative between two instructions. If @feature is
|
||||
* present, early code in apply_alternatives() replaces @oldinstr with
|
||||
* @newinstr. ".skip" directive takes care of proper instruction padding
|
||||
* in case @newinstr is longer than @oldinstr.
|
||||
* @newinstr.
|
||||
*/
|
||||
.macro ALTERNATIVE oldinstr, newinstr, feature
|
||||
.pushsection .altinstr_replacement,"ax"
|
||||
770: \newinstr
|
||||
771: .popsection
|
||||
772: \oldinstr
|
||||
773: alt_len_check 770b, 771b
|
||||
alt_len_check 772b, 773b
|
||||
alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) )
|
||||
774: .pushsection .altinstructions,"a"
|
||||
alt_entry 772b, 774b, 770b, 771b, \feature
|
||||
773: .pushsection .altinstructions,"a"
|
||||
alt_entry 772b, 773b, 770b, 771b, \feature
|
||||
.popsection
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Define an alternative between two instructions. If @feature is
|
||||
* present, early code in apply_alternatives() replaces @oldinstr with
|
||||
* @newinstr. ".skip" directive takes care of proper instruction padding
|
||||
* in case @newinstr is longer than @oldinstr.
|
||||
* @newinstr.
|
||||
*/
|
||||
.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
|
||||
.pushsection .altinstr_replacement,"ax"
|
||||
@ -95,17 +45,9 @@
|
||||
771: \newinstr2
|
||||
772: .popsection
|
||||
773: \oldinstr
|
||||
774: alt_len_check 770b, 771b
|
||||
alt_len_check 771b, 772b
|
||||
alt_len_check 773b, 774b
|
||||
.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
|
||||
774: .pushsection .altinstructions,"a"
|
||||
alt_entry 773b, 774b, 770b, 771b,\feature1
|
||||
alt_entry 773b, 774b, 771b, 772b,\feature2
|
||||
.popsection
|
||||
.endm
|
||||
|
||||
|
@ -13,32 +13,25 @@ struct alt_instr {
|
||||
s32 repl_offset; /* offset to replacement instruction */
|
||||
u16 facility; /* facility bit set for replacement */
|
||||
u8 instrlen; /* length of original instruction */
|
||||
u8 replacementlen; /* length of new instruction */
|
||||
} __packed;
|
||||
|
||||
void apply_alternative_instructions(void);
|
||||
void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
|
||||
|
||||
/*
|
||||
* |661: |662: |6620 |663:
|
||||
* +-----------+---------------------+
|
||||
* | oldinstr | oldinstr_padding |
|
||||
* | +----------+----------+
|
||||
* | | | |
|
||||
* | | >6 bytes |6/4/2 nops|
|
||||
* | |6 bytes jg----------->
|
||||
* +-----------+---------------------+
|
||||
* ^^ static padding ^^
|
||||
* +---------------------------------+
|
||||
* |661: |662:
|
||||
* | oldinstr |
|
||||
* +---------------------------------+
|
||||
*
|
||||
* .altinstr_replacement section
|
||||
* +---------------------+-----------+
|
||||
* +---------------------------------+
|
||||
* |6641: |6651:
|
||||
* | alternative instr 1 |
|
||||
* +-----------+---------+- - - - - -+
|
||||
* |6642: |6652: |
|
||||
* | alternative instr 2 | padding
|
||||
* +---------------------+- - - - - -+
|
||||
* ^ runtime ^
|
||||
* +---------------------------------+
|
||||
* |6642: |6652:
|
||||
* | alternative instr 2 |
|
||||
* +---------------------------------+
|
||||
*
|
||||
* .altinstructions section
|
||||
* +---------------------------------+
|
||||
@ -47,77 +40,31 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
|
||||
* +---------------------------------+
|
||||
*/
|
||||
|
||||
#define b_altinstr(num) "664"#num
|
||||
#define e_altinstr(num) "665"#num
|
||||
|
||||
#define e_oldinstr_pad_end "663"
|
||||
#define b_altinstr(num) "664"#num
|
||||
#define e_altinstr(num) "665"#num
|
||||
#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 oldinstr_pad_len(num) \
|
||||
"-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \
|
||||
"((" altinstr_len(num) ")-(" oldinstr_len "))"
|
||||
|
||||
#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 OLDINSTR(oldinstr) \
|
||||
"661:\n\t" oldinstr "\n662:\n"
|
||||
|
||||
#define ALTINSTR_ENTRY(facility, num) \
|
||||
"\t.long 661b - .\n" /* old instruction */ \
|
||||
"\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \
|
||||
"\t.word " __stringify(facility) "\n" /* facility bit */ \
|
||||
"\t.byte " oldinstr_total_len "\n" /* source len */ \
|
||||
"\t.byte " altinstr_len(num) "\n" /* alt instruction len */
|
||||
"\t.byte " oldinstr_len "\n" /* instruction len */ \
|
||||
"\t.org . - (" oldinstr_len ") + (" altinstr_len(num) ")\n" \
|
||||
"\t.org . - (" altinstr_len(num) ") + (" oldinstr_len ")\n"
|
||||
|
||||
#define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \
|
||||
b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \
|
||||
INSTR_LEN_SANITY_CHECK(altinstr_len(num))
|
||||
b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n"
|
||||
|
||||
/* alternative assembly primitive: */
|
||||
#define ALTERNATIVE(oldinstr, altinstr, facility) \
|
||||
".pushsection .altinstr_replacement, \"ax\"\n" \
|
||||
ALTINSTR_REPLACEMENT(altinstr, 1) \
|
||||
".popsection\n" \
|
||||
OLDINSTR(oldinstr, 1) \
|
||||
OLDINSTR(oldinstr) \
|
||||
".pushsection .altinstructions,\"a\"\n" \
|
||||
ALTINSTR_ENTRY(facility, 1) \
|
||||
".popsection\n"
|
||||
@ -127,7 +74,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
|
||||
ALTINSTR_REPLACEMENT(altinstr1, 1) \
|
||||
ALTINSTR_REPLACEMENT(altinstr2, 2) \
|
||||
".popsection\n" \
|
||||
OLDINSTR_2(oldinstr, 1, 2) \
|
||||
OLDINSTR(oldinstr) \
|
||||
".pushsection .altinstructions,\"a\"\n" \
|
||||
ALTINSTR_ENTRY(facility1, 1) \
|
||||
ALTINSTR_ENTRY(facility2, 2) \
|
||||
|
@ -7,8 +7,6 @@
|
||||
#include <asm/facility.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
|
||||
#define MAX_PATCH_LEN (255 - 1)
|
||||
|
||||
static int __initdata_or_module alt_instr_disabled;
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
struct alt_instr *end)
|
||||
{
|
||||
struct alt_instr *a;
|
||||
u8 *instr, *replacement;
|
||||
u8 insnbuf[MAX_PATCH_LEN];
|
||||
|
||||
/*
|
||||
* The scan order should be from start to end. A later scanned
|
||||
* alternative code can overwrite previously scanned alternative code.
|
||||
*/
|
||||
for (a = start; a < end; a++) {
|
||||
int insnbuf_sz = 0;
|
||||
|
||||
instr = (u8 *)&a->instr_offset + a->instr_offset;
|
||||
replacement = (u8 *)&a->repl_offset + a->repl_offset;
|
||||
|
||||
if (!__test_facility(a->facility, alt_stfle_fac_list))
|
||||
continue;
|
||||
|
||||
if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
|
||||
if (unlikely(a->instrlen % 2)) {
|
||||
WARN_ONCE(1, "cpu alternatives instructions length is "
|
||||
"odd, skipping patching\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(insnbuf, replacement, a->replacementlen);
|
||||
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);
|
||||
s390_kernel_write(instr, replacement, a->instrlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user