s390/alternatives: remove padding generation code
authorHeiko Carstens <hca@linux.ibm.com>
Wed, 11 May 2022 12:05:26 +0000 (14:05 +0200)
committerHeiko Carstens <hca@linux.ibm.com>
Tue, 17 May 2022 13:16:28 +0000 (15:16 +0200)
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>
arch/s390/include/asm/alternative-asm.h
arch/s390/include/asm/alternative.h
arch/s390/kernel/alternative.c

index bb3837d7387ce037cff3d8026b253ee462fb8fb7..7db046596b93e81e1b45a0e2537f602556747d39 100644 (file)
@@ -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
        .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"
 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
 
index 3f2856ed680831f2f574425dc9ed19672b75f7eb..904dd049f954767e4c5812358d6d403884638008 100644 (file)
@@ -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)                                    \
index cce0ddee2d02d2731ef36635a18082f9b13f76c3..e7bca29f9c3485bead01a70b6935f3f9041b7f8e 100644 (file)
@@ -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);
        }
 }