Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+30 -198
+9 -67
arch/s390/include/asm/alternative-asm.h
··· 5 5 #ifdef __ASSEMBLY__ 6 6 7 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 - 20 - /* 21 8 * Issue one struct alt_instr descriptor entry (need to put it into 22 9 * the section .altinstructions, see below). This entry contains 23 10 * enough information for the alternatives patching code to patch an ··· 15 28 .long \alt_start - . 16 29 .word \feature 17 30 .byte \orig_end - \orig_start 18 - .byte \alt_end - \alt_start 19 - .endm 20 - 21 - /* 22 - * Fill up @bytes with nops. The macro emits 6-byte nop instructions 23 - * for the bulk of the area, possibly followed by a 4-byte and/or 24 - * a 2-byte nop if the size of the area is not divisible by 6. 25 - */ 26 - .macro alt_pad_fill bytes 27 - .rept ( \bytes ) / 6 28 - brcl 0,0 29 - .endr 30 - .rept ( \bytes ) % 6 / 4 31 - nop 32 - .endr 33 - .rept ( \bytes ) % 6 % 4 / 2 34 - nopr 35 - .endr 36 - .endm 37 - 38 - /* 39 - * Fill up @bytes with nops. If the number of bytes is larger 40 - * than 6, emit a jg instruction to branch over all nops, then 41 - * fill an area of size (@bytes - 6) with nop instructions. 42 - */ 43 - .macro alt_pad bytes 44 - .if ( \bytes > 0 ) 45 - .if ( \bytes > 6 ) 46 - jg . + \bytes 47 - alt_pad_fill \bytes - 6 48 - .else 49 - alt_pad_fill \bytes 50 - .endif 51 - .endif 31 + .org . - ( \orig_end - \orig_start ) + ( \alt_end - \alt_start ) 32 + .org . - ( \alt_end - \alt_start ) + ( \orig_end - \orig_start ) 52 33 .endm 53 34 54 35 /* 55 36 * Define an alternative between two instructions. If @feature is 56 37 * present, early code in apply_alternatives() replaces @oldinstr with 57 - * @newinstr. ".skip" directive takes care of proper instruction padding 58 - * in case @newinstr is longer than @oldinstr. 38 + * @newinstr. 59 39 */ 60 40 .macro ALTERNATIVE oldinstr, newinstr, feature 61 41 .pushsection .altinstr_replacement,"ax" 62 42 770: \newinstr 63 43 771: .popsection 64 44 772: \oldinstr 65 - 773: alt_len_check 770b, 771b 66 - alt_len_check 772b, 773b 67 - alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) ) 68 - 774: .pushsection .altinstructions,"a" 69 - alt_entry 772b, 774b, 770b, 771b, \feature 45 + 773: .pushsection .altinstructions,"a" 46 + alt_entry 772b, 773b, 770b, 771b, \feature 70 47 .popsection 71 48 .endm 72 49 73 50 /* 74 51 * Define an alternative between two instructions. If @feature is 75 52 * present, early code in apply_alternatives() replaces @oldinstr with 76 - * @newinstr. ".skip" directive takes care of proper instruction padding 77 - * in case @newinstr is longer than @oldinstr. 53 + * @newinstr. 78 54 */ 79 55 .macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2 80 56 .pushsection .altinstr_replacement,"ax" ··· 45 95 771: \newinstr2 46 96 772: .popsection 47 97 773: \oldinstr 48 - 774: alt_len_check 770b, 771b 49 - alt_len_check 771b, 772b 50 - alt_len_check 773b, 774b 51 - .if ( 771b - 770b > 772b - 771b ) 52 - alt_pad ( ( 771b - 770b ) - ( 774b - 773b ) ) 53 - .else 54 - alt_pad ( ( 772b - 771b ) - ( 774b - 773b ) ) 55 - .endif 56 - 775: .pushsection .altinstructions,"a" 57 - alt_entry 773b, 775b, 770b, 771b,\feature1 58 - alt_entry 773b, 775b, 771b, 772b,\feature2 98 + 774: .pushsection .altinstructions,"a" 99 + alt_entry 773b, 774b, 770b, 771b,\feature1 100 + alt_entry 773b, 774b, 771b, 772b,\feature2 59 101 .popsection 60 102 .endm 61 103
+19 -72
arch/s390/include/asm/alternative.h
··· 13 13 s32 repl_offset; /* offset to replacement instruction */ 14 14 u16 facility; /* facility bit set for replacement */ 15 15 u8 instrlen; /* length of original instruction */ 16 - u8 replacementlen; /* length of new instruction */ 17 16 } __packed; 18 17 19 18 void apply_alternative_instructions(void); 20 19 void apply_alternatives(struct alt_instr *start, struct alt_instr *end); 21 20 22 21 /* 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 + * +---------------------------------+ 32 26 * 33 27 * .altinstr_replacement section 34 - * +---------------------+-----------+ 28 + * +---------------------------------+ 35 29 * |6641: |6651: 36 30 * | alternative instr 1 | 37 - * +-----------+---------+- - - - - -+ 38 - * |6642: |6652: | 39 - * | alternative instr 2 | padding 40 - * +---------------------+- - - - - -+ 41 - * ^ runtime ^ 31 + * +---------------------------------+ 32 + * |6642: |6652: 33 + * | alternative instr 2 | 34 + * +---------------------------------+ 42 35 * 43 36 * .altinstructions section 44 37 * +---------------------------------+ ··· 40 47 * +---------------------------------+ 41 48 */ 42 49 43 - #define b_altinstr(num) "664"#num 44 - #define e_altinstr(num) "665"#num 45 - 46 - #define e_oldinstr_pad_end "663" 50 + #define b_altinstr(num) "664"#num 51 + #define e_altinstr(num) "665"#num 47 52 #define oldinstr_len "662b-661b" 48 - #define oldinstr_total_len e_oldinstr_pad_end"b-661b" 49 53 #define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b" 50 - #define oldinstr_pad_len(num) \ 51 - "-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \ 52 - "((" altinstr_len(num) ")-(" oldinstr_len "))" 53 54 54 - #define INSTR_LEN_SANITY_CHECK(len) \ 55 - ".if " len " > 254\n" \ 56 - "\t.error \"cpu alternatives does not support instructions " \ 57 - "blocks > 254 bytes\"\n" \ 58 - ".endif\n" \ 59 - ".if (" len ") %% 2\n" \ 60 - "\t.error \"cpu alternatives instructions length is odd\"\n" \ 61 - ".endif\n" 62 - 63 - #define OLDINSTR_PADDING(oldinstr, num) \ 64 - ".if " oldinstr_pad_len(num) " > 6\n" \ 65 - "\tjg " e_oldinstr_pad_end "f\n" \ 66 - "6620:\n" \ 67 - "\t.rept (" oldinstr_pad_len(num) " - (6620b-662b)) / 2\n" \ 68 - "\tnopr\n" \ 69 - ".else\n" \ 70 - "\t.rept " oldinstr_pad_len(num) " / 6\n" \ 71 - "\t.brcl 0,0\n" \ 72 - "\t.endr\n" \ 73 - "\t.rept " oldinstr_pad_len(num) " %% 6 / 4\n" \ 74 - "\tnop\n" \ 75 - "\t.endr\n" \ 76 - "\t.rept " oldinstr_pad_len(num) " %% 6 %% 4 / 2\n" \ 77 - "\tnopr\n" \ 78 - ".endr\n" \ 79 - ".endif\n" 80 - 81 - #define OLDINSTR(oldinstr, num) \ 82 - "661:\n\t" oldinstr "\n662:\n" \ 83 - OLDINSTR_PADDING(oldinstr, num) \ 84 - e_oldinstr_pad_end ":\n" \ 85 - INSTR_LEN_SANITY_CHECK(oldinstr_len) 86 - 87 - #define OLDINSTR_2(oldinstr, num1, num2) \ 88 - "661:\n\t" oldinstr "\n662:\n" \ 89 - ".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \ 90 - OLDINSTR_PADDING(oldinstr, num2) \ 91 - ".else\n" \ 92 - OLDINSTR_PADDING(oldinstr, num1) \ 93 - ".endif\n" \ 94 - e_oldinstr_pad_end ":\n" \ 95 - INSTR_LEN_SANITY_CHECK(oldinstr_len) 55 + #define OLDINSTR(oldinstr) \ 56 + "661:\n\t" oldinstr "\n662:\n" 96 57 97 58 #define ALTINSTR_ENTRY(facility, num) \ 98 59 "\t.long 661b - .\n" /* old instruction */ \ 99 60 "\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \ 100 61 "\t.word " __stringify(facility) "\n" /* facility bit */ \ 101 - "\t.byte " oldinstr_total_len "\n" /* source len */ \ 102 - "\t.byte " altinstr_len(num) "\n" /* alt instruction len */ 62 + "\t.byte " oldinstr_len "\n" /* instruction len */ \ 63 + "\t.org . - (" oldinstr_len ") + (" altinstr_len(num) ")\n" \ 64 + "\t.org . - (" altinstr_len(num) ") + (" oldinstr_len ")\n" 103 65 104 66 #define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \ 105 - b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \ 106 - INSTR_LEN_SANITY_CHECK(altinstr_len(num)) 67 + b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" 107 68 108 69 /* alternative assembly primitive: */ 109 70 #define ALTERNATIVE(oldinstr, altinstr, facility) \ 110 71 ".pushsection .altinstr_replacement, \"ax\"\n" \ 111 72 ALTINSTR_REPLACEMENT(altinstr, 1) \ 112 73 ".popsection\n" \ 113 - OLDINSTR(oldinstr, 1) \ 74 + OLDINSTR(oldinstr) \ 114 75 ".pushsection .altinstructions,\"a\"\n" \ 115 76 ALTINSTR_ENTRY(facility, 1) \ 116 77 ".popsection\n" ··· 74 127 ALTINSTR_REPLACEMENT(altinstr1, 1) \ 75 128 ALTINSTR_REPLACEMENT(altinstr2, 2) \ 76 129 ".popsection\n" \ 77 - OLDINSTR_2(oldinstr, 1, 2) \ 130 + OLDINSTR(oldinstr) \ 78 131 ".pushsection .altinstructions,\"a\"\n" \ 79 132 ALTINSTR_ENTRY(facility1, 1) \ 80 133 ALTINSTR_ENTRY(facility2, 2) \
+2 -59
arch/s390/kernel/alternative.c
··· 7 7 #include <asm/facility.h> 8 8 #include <asm/nospec-branch.h> 9 9 10 - #define MAX_PATCH_LEN (255 - 1) 11 - 12 10 static int __initdata_or_module alt_instr_disabled; 13 11 14 12 static int __init disable_alternative_instructions(char *str) ··· 17 19 18 20 early_param("noaltinstr", disable_alternative_instructions); 19 21 20 - struct brcl_insn { 21 - u16 opc; 22 - s32 disp; 23 - } __packed; 24 - 25 - static u16 __initdata_or_module nop16 = 0x0700; 26 - static u32 __initdata_or_module nop32 = 0x47000000; 27 - static struct brcl_insn __initdata_or_module nop48 = { 28 - 0xc004, 0 29 - }; 30 - 31 - static const void *nops[] __initdata_or_module = { 32 - &nop16, 33 - &nop32, 34 - &nop48 35 - }; 36 - 37 - static void __init_or_module add_jump_padding(void *insns, unsigned int len) 38 - { 39 - struct brcl_insn brcl = { 40 - 0xc0f4, 41 - len / 2 42 - }; 43 - 44 - memcpy(insns, &brcl, sizeof(brcl)); 45 - insns += sizeof(brcl); 46 - len -= sizeof(brcl); 47 - 48 - while (len > 0) { 49 - memcpy(insns, &nop16, 2); 50 - insns += 2; 51 - len -= 2; 52 - } 53 - } 54 - 55 - static void __init_or_module add_padding(void *insns, unsigned int len) 56 - { 57 - if (len > 6) 58 - add_jump_padding(insns, len); 59 - else if (len >= 2) 60 - memcpy(insns, nops[len / 2 - 1], len); 61 - } 62 - 63 22 static void __init_or_module __apply_alternatives(struct alt_instr *start, 64 23 struct alt_instr *end) 65 24 { 66 25 struct alt_instr *a; 67 26 u8 *instr, *replacement; 68 - u8 insnbuf[MAX_PATCH_LEN]; 69 27 70 28 /* 71 29 * The scan order should be from start to end. A later scanned 72 30 * alternative code can overwrite previously scanned alternative code. 73 31 */ 74 32 for (a = start; a < end; a++) { 75 - int insnbuf_sz = 0; 76 - 77 33 instr = (u8 *)&a->instr_offset + a->instr_offset; 78 34 replacement = (u8 *)&a->repl_offset + a->repl_offset; 79 35 80 36 if (!__test_facility(a->facility, alt_stfle_fac_list)) 81 37 continue; 82 38 83 - if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) { 39 + if (unlikely(a->instrlen % 2)) { 84 40 WARN_ONCE(1, "cpu alternatives instructions length is " 85 41 "odd, skipping patching\n"); 86 42 continue; 87 43 } 88 44 89 - memcpy(insnbuf, replacement, a->replacementlen); 90 - insnbuf_sz = a->replacementlen; 91 - 92 - if (a->instrlen > a->replacementlen) { 93 - add_padding(insnbuf + a->replacementlen, 94 - a->instrlen - a->replacementlen); 95 - insnbuf_sz += a->instrlen - a->replacementlen; 96 - } 97 - 98 - s390_kernel_write(instr, insnbuf, insnbuf_sz); 45 + s390_kernel_write(instr, replacement, a->instrlen); 99 46 } 100 47 } 101 48