1 // SPDX-License-Identifier: GPL-2.0 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 requested nobp=1, enable it and 20 * disable the expoline support. 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: etokens\n"); 43 if (nospec_uses_trampoline()) 44 pr_info("Spectre V2 mitigation: execute trampolines\n"); 45 if (nobp_enabled()) 46 pr_info("Spectre V2 mitigation: limited branch prediction\n"); 47 return 0; 48 } 49 arch_initcall(nospec_report); 50 51 #ifdef CONFIG_EXPOLINE 52 53 int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF); 54 55 static int __init nospectre_v2_setup_early(char *str) 56 { 57 nospec_disable = 1; 58 return 0; 59 } 60 early_param("nospectre_v2", nospectre_v2_setup_early); 61 62 void __init nospec_auto_detect(void) 63 { 64 if (test_facility(156) || cpu_mitigations_off()) { 65 /* 66 * The machine supports etokens. 67 * Disable expolines and disable nobp. 68 */ 69 if (__is_defined(CC_USING_EXPOLINE)) 70 nospec_disable = 1; 71 nobp = 0; 72 } else if (__is_defined(CC_USING_EXPOLINE)) { 73 /* 74 * The kernel has been compiled with expolines. 75 * Keep expolines enabled and disable nobp. 76 */ 77 nospec_disable = 0; 78 nobp = 0; 79 } 80 /* 81 * If the kernel has not been compiled with expolines the 82 * nobp setting decides what is done, this depends on the 83 * CONFIG_KERNEL_NP option and the nobp/nospec parameters. 84 */ 85 } 86 87 static int __init spectre_v2_setup_early(char *str) 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_early); 100 101 static void __init_or_module __nospec_revert(s32 *start, s32 *end) 102 { 103 enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type; 104 static const u8 branch[] = { 0x47, 0x00, 0x07, 0x00 }; 105 u8 *instr, *thunk, *br; 106 u8 insnbuf[6]; 107 s32 *epo; 108 109 /* Second part of the instruction replace is always a nop */ 110 memcpy(insnbuf + 2, branch, sizeof(branch)); 111 for (epo = start; epo < end; epo++) { 112 instr = (u8 *) epo + *epo; 113 if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04) 114 type = BRCL_EXPOLINE; /* brcl instruction */ 115 else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05) 116 type = BRASL_EXPOLINE; /* brasl instruction */ 117 else 118 continue; 119 thunk = instr + (long)(*(int *)(instr + 2)) * 2; 120 if (thunk[0] == 0xc6 && thunk[1] == 0x00) 121 /* exrl %r0,<target-br> */ 122 br = thunk + (long)(*(int *)(thunk + 2)) * 2; 123 else 124 continue; 125 if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0) 126 continue; 127 switch (type) { 128 case BRCL_EXPOLINE: 129 /* brcl to thunk, replace with br + nop */ 130 insnbuf[0] = br[0]; 131 insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); 132 break; 133 case BRASL_EXPOLINE: 134 /* brasl to thunk, replace with basr + nop */ 135 insnbuf[0] = 0x0d; 136 insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); 137 break; 138 } 139 140 s390_kernel_write(instr, insnbuf, 6); 141 } 142 } 143 144 void __init_or_module nospec_revert(s32 *start, s32 *end) 145 { 146 if (nospec_disable) 147 __nospec_revert(start, end); 148 } 149 150 extern s32 __nospec_call_start[], __nospec_call_end[]; 151 extern s32 __nospec_return_start[], __nospec_return_end[]; 152 void __init nospec_init_branches(void) 153 { 154 nospec_revert(__nospec_call_start, __nospec_call_end); 155 nospec_revert(__nospec_return_start, __nospec_return_end); 156 } 157 158 #endif /* CONFIG_EXPOLINE */ 159