1 // SPDX-License-Identifier: GPL-2.0 1 2 #include <linux/module.h> 3 #include <linux/device.h> 4 #include <linux/cpu.h> 5 #include <asm/nospec-branch.h> 6 7 int nobp = IS_ENABLED(CONFIG_KERNEL_NOBP); 8 9 static int __init nobp_setup_early(char *str) 10 { 11 bool enabled; 12 int rc; 13 14 rc = kstrtobool(str, &enabled); 15 if (rc) 16 return rc; 17 if (enabled && test_facility(82)) { 18 /* 19 * The user explicitly request 20 * disable the expoline suppor 21 */ 22 nobp = 1; 23 if (IS_ENABLED(CONFIG_EXPOLINE 24 nospec_disable = 1; 25 } else { 26 nobp = 0; 27 } 28 return 0; 29 } 30 early_param("nobp", nobp_setup_early); 31 32 static int __init nospec_setup_early(char *str 33 { 34 nobp = 0; 35 return 0; 36 } 37 early_param("nospec", nospec_setup_early); 38 39 static int __init nospec_report(void) 40 { 41 if (test_facility(156)) 42 pr_info("Spectre V2 mitigation 43 if (nospec_uses_trampoline()) 44 pr_info("Spectre V2 mitigation 45 if (nobp_enabled()) 46 pr_info("Spectre V2 mitigation 47 return 0; 48 } 49 arch_initcall(nospec_report); 50 51 #ifdef CONFIG_EXPOLINE 52 53 int nospec_disable = IS_ENABLED(CONFIG_EXPOLIN 54 55 static int __init nospectre_v2_setup_early(cha 56 { 57 nospec_disable = 1; 58 return 0; 59 } 60 early_param("nospectre_v2", nospectre_v2_setup 61 62 void __init nospec_auto_detect(void) 63 { 64 if (test_facility(156) || cpu_mitigati 65 /* 66 * The machine supports etoken 67 * Disable expolines and disab 68 */ 69 if (__is_defined(CC_USING_EXPO 70 nospec_disable = 1; 71 nobp = 0; 72 } else if (__is_defined(CC_USING_EXPOL 73 /* 74 * The kernel has been compile 75 * Keep expolines enabled and 76 */ 77 nospec_disable = 0; 78 nobp = 0; 79 } 80 /* 81 * If the kernel has not been compiled 82 * nobp setting decides what is done, 83 * CONFIG_KERNEL_NP option and the nob 84 */ 85 } 86 87 static int __init spectre_v2_setup_early(char 88 { 89 if (str && !strncmp(str, "on", 2)) { 90 nospec_disable = 0; 91 nobp = 0; 92 } 93 if (str && !strncmp(str, "off", 3)) 94 nospec_disable = 1; 95 if (str && !strncmp(str, "auto", 4)) 96 nospec_auto_detect(); 97 return 0; 98 } 99 early_param("spectre_v2", spectre_v2_setup_ear 100 101 static void __init_or_module __nospec_revert(s 102 { 103 enum { BRCL_EXPOLINE, BRASL_EXPOLINE } 104 static const u8 branch[] = { 0x47, 0x0 105 u8 *instr, *thunk, *br; 106 u8 insnbuf[6]; 107 s32 *epo; 108 109 /* Second part of the instruction repl 110 memcpy(insnbuf + 2, branch, sizeof(bra 111 for (epo = start; epo < end; epo++) { 112 instr = (u8 *) epo + *epo; 113 if (instr[0] == 0xc0 && (instr 114 type = BRCL_EXPOLINE; 115 else if (instr[0] == 0xc0 && ( 116 type = BRASL_EXPOLINE; 117 else 118 continue; 119 thunk = instr + (long)(*(int * 120 if (thunk[0] == 0xc6 && thunk[ 121 /* exrl %r0,<target-br 122 br = thunk + (long)(*( 123 else 124 continue; 125 if (br[0] != 0x07 || (br[1] & 126 continue; 127 switch (type) { 128 case BRCL_EXPOLINE: 129 /* brcl to thunk, repl 130 insnbuf[0] = br[0]; 131 insnbuf[1] = (instr[1] 132 break; 133 case BRASL_EXPOLINE: 134 /* brasl to thunk, rep 135 insnbuf[0] = 0x0d; 136 insnbuf[1] = (instr[1] 137 break; 138 } 139 140 s390_kernel_write(instr, insnb 141 } 142 } 143 144 void __init_or_module nospec_revert(s32 *start 145 { 146 if (nospec_disable) 147 __nospec_revert(start, end); 148 } 149 150 extern s32 __nospec_call_start[], __nospec_cal 151 extern s32 __nospec_return_start[], __nospec_r 152 void __init nospec_init_branches(void) 153 { 154 nospec_revert(__nospec_call_start, __n 155 nospec_revert(__nospec_return_start, _ 156 } 157 158 #endif /* CONFIG_EXPOLINE */ 159
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.