1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/module.h> 3 #include <asm/alternative.h> 4 #include <asm/facility.h> 5 6 #define MAX_PATCH_LEN (255 - 1) 7 8 static int __initdata_or_module alt_instr_disabled; 9 10 static int __init disable_alternative_instructions(char *str) 11 { 12 alt_instr_disabled = 1; 13 return 0; 14 } 15 16 early_param("noaltinstr", disable_alternative_instructions); 17 18 static int __init nobp_setup_early(char *str) 19 { 20 bool enabled; 21 int rc; 22 23 rc = kstrtobool(str, &enabled); 24 if (rc) 25 return rc; 26 if (enabled && test_facility(82)) 27 __set_facility(82, S390_lowcore.alt_stfle_fac_list); 28 else 29 __clear_facility(82, S390_lowcore.alt_stfle_fac_list); 30 return 0; 31 } 32 early_param("nobp", nobp_setup_early); 33 34 static int __init nospec_setup_early(char *str) 35 { 36 __clear_facility(82, S390_lowcore.alt_stfle_fac_list); 37 return 0; 38 } 39 early_param("nospec", nospec_setup_early); 40 41 struct brcl_insn { 42 u16 opc; 43 s32 disp; 44 } __packed; 45 46 static u16 __initdata_or_module nop16 = 0x0700; 47 static u32 __initdata_or_module nop32 = 0x47000000; 48 static struct brcl_insn __initdata_or_module nop48 = { 49 0xc004, 0 50 }; 51 52 static const void *nops[] __initdata_or_module = { 53 &nop16, 54 &nop32, 55 &nop48 56 }; 57 58 static void __init_or_module add_jump_padding(void *insns, unsigned int len) 59 { 60 struct brcl_insn brcl = { 61 0xc0f4, 62 len / 2 63 }; 64 65 memcpy(insns, &brcl, sizeof(brcl)); 66 insns += sizeof(brcl); 67 len -= sizeof(brcl); 68 69 while (len > 0) { 70 memcpy(insns, &nop16, 2); 71 insns += 2; 72 len -= 2; 73 } 74 } 75 76 static void __init_or_module add_padding(void *insns, unsigned int len) 77 { 78 if (len > 6) 79 add_jump_padding(insns, len); 80 else if (len >= 2) 81 memcpy(insns, nops[len / 2 - 1], len); 82 } 83 84 static void __init_or_module __apply_alternatives(struct alt_instr *start, 85 struct alt_instr *end) 86 { 87 struct alt_instr *a; 88 u8 *instr, *replacement; 89 u8 insnbuf[MAX_PATCH_LEN]; 90 91 /* 92 * The scan order should be from start to end. A later scanned 93 * alternative code can overwrite previously scanned alternative code. 94 */ 95 for (a = start; a < end; a++) { 96 int insnbuf_sz = 0; 97 98 instr = (u8 *)&a->instr_offset + a->instr_offset; 99 replacement = (u8 *)&a->repl_offset + a->repl_offset; 100 101 if (!__test_facility(a->facility, 102 S390_lowcore.alt_stfle_fac_list)) 103 continue; 104 105 if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) { 106 WARN_ONCE(1, "cpu alternatives instructions length is " 107 "odd, skipping patching\n"); 108 continue; 109 } 110 111 memcpy(insnbuf, replacement, a->replacementlen); 112 insnbuf_sz = a->replacementlen; 113 114 if (a->instrlen > a->replacementlen) { 115 add_padding(insnbuf + a->replacementlen, 116 a->instrlen - a->replacementlen); 117 insnbuf_sz += a->instrlen - a->replacementlen; 118 } 119 120 s390_kernel_write(instr, insnbuf, insnbuf_sz); 121 } 122 } 123 124 void __init_or_module apply_alternatives(struct alt_instr *start, 125 struct alt_instr *end) 126 { 127 if (!alt_instr_disabled) 128 __apply_alternatives(start, end); 129 } 130 131 extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 132 void __init apply_alternative_instructions(void) 133 { 134 apply_alternatives(__alt_instructions, __alt_instructions_end); 135 } 136