xref: /linux/arch/s390/kernel/nospec-branch.c (revision cada938a01586fc144902919e133354b1459db04)
1f19fbd5eSMartin Schwidefsky // SPDX-License-Identifier: GPL-2.0
2f19fbd5eSMartin Schwidefsky #include <linux/module.h>
3d424986fSMartin Schwidefsky #include <linux/device.h>
40336e04aSJosh Poimboeuf #include <linux/cpu.h>
5f19fbd5eSMartin Schwidefsky #include <asm/nospec-branch.h>
6f19fbd5eSMartin Schwidefsky 
7b2e2f43aSMartin Schwidefsky static int __init nobp_setup_early(char *str)
8b2e2f43aSMartin Schwidefsky {
9b2e2f43aSMartin Schwidefsky 	bool enabled;
10b2e2f43aSMartin Schwidefsky 	int rc;
11b2e2f43aSMartin Schwidefsky 
12b2e2f43aSMartin Schwidefsky 	rc = kstrtobool(str, &enabled);
13b2e2f43aSMartin Schwidefsky 	if (rc)
14b2e2f43aSMartin Schwidefsky 		return rc;
156e179d64SMartin Schwidefsky 	if (enabled && test_facility(82)) {
166e179d64SMartin Schwidefsky 		/*
17*cada938aSHeiko Carstens 		 * The user explicitly requested nobp=1, enable it and
186e179d64SMartin Schwidefsky 		 * disable the expoline support.
196e179d64SMartin Schwidefsky 		 */
2017e89e13SSven Schnelle 		__set_facility(82, alt_stfle_fac_list);
216e179d64SMartin Schwidefsky 		if (IS_ENABLED(CONFIG_EXPOLINE))
226e179d64SMartin Schwidefsky 			nospec_disable = 1;
236e179d64SMartin Schwidefsky 	} else {
2417e89e13SSven Schnelle 		__clear_facility(82, alt_stfle_fac_list);
256e179d64SMartin Schwidefsky 	}
26b2e2f43aSMartin Schwidefsky 	return 0;
27b2e2f43aSMartin Schwidefsky }
28b2e2f43aSMartin Schwidefsky early_param("nobp", nobp_setup_early);
29b2e2f43aSMartin Schwidefsky 
30b2e2f43aSMartin Schwidefsky static int __init nospec_setup_early(char *str)
31b2e2f43aSMartin Schwidefsky {
3217e89e13SSven Schnelle 	__clear_facility(82, alt_stfle_fac_list);
33b2e2f43aSMartin Schwidefsky 	return 0;
34b2e2f43aSMartin Schwidefsky }
35b2e2f43aSMartin Schwidefsky early_param("nospec", nospec_setup_early);
36b2e2f43aSMartin Schwidefsky 
37bc035599SMartin Schwidefsky static int __init nospec_report(void)
38bc035599SMartin Schwidefsky {
39aeaf7002SMartin Schwidefsky 	if (test_facility(156))
40aeaf7002SMartin Schwidefsky 		pr_info("Spectre V2 mitigation: etokens\n");
415d17d4edSSven Schnelle 	if (nospec_uses_trampoline())
42b7e7f505SMartin Schwidefsky 		pr_info("Spectre V2 mitigation: execute trampolines\n");
4317e89e13SSven Schnelle 	if (__test_facility(82, alt_stfle_fac_list))
44b7e7f505SMartin Schwidefsky 		pr_info("Spectre V2 mitigation: limited branch prediction\n");
45bc035599SMartin Schwidefsky 	return 0;
46bc035599SMartin Schwidefsky }
47bc035599SMartin Schwidefsky arch_initcall(nospec_report);
48bc035599SMartin Schwidefsky 
49b2e2f43aSMartin Schwidefsky #ifdef CONFIG_EXPOLINE
50b2e2f43aSMartin Schwidefsky 
516e179d64SMartin Schwidefsky int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
52f19fbd5eSMartin Schwidefsky 
53f19fbd5eSMartin Schwidefsky static int __init nospectre_v2_setup_early(char *str)
54f19fbd5eSMartin Schwidefsky {
556e179d64SMartin Schwidefsky 	nospec_disable = 1;
56f19fbd5eSMartin Schwidefsky 	return 0;
57f19fbd5eSMartin Schwidefsky }
58f19fbd5eSMartin Schwidefsky early_param("nospectre_v2", nospectre_v2_setup_early);
59f19fbd5eSMartin Schwidefsky 
606a3d1e81SMartin Schwidefsky void __init nospec_auto_detect(void)
616e179d64SMartin Schwidefsky {
620336e04aSJosh Poimboeuf 	if (test_facility(156) || cpu_mitigations_off()) {
63aeaf7002SMartin Schwidefsky 		/*
64aeaf7002SMartin Schwidefsky 		 * The machine supports etokens.
65aeaf7002SMartin Schwidefsky 		 * Disable expolines and disable nobp.
66aeaf7002SMartin Schwidefsky 		 */
67475c8e9eSJoe Perches 		if (__is_defined(CC_USING_EXPOLINE))
68aeaf7002SMartin Schwidefsky 			nospec_disable = 1;
6917e89e13SSven Schnelle 		__clear_facility(82, alt_stfle_fac_list);
70475c8e9eSJoe Perches 	} else if (__is_defined(CC_USING_EXPOLINE)) {
716e179d64SMartin Schwidefsky 		/*
726e179d64SMartin Schwidefsky 		 * The kernel has been compiled with expolines.
736e179d64SMartin Schwidefsky 		 * Keep expolines enabled and disable nobp.
746e179d64SMartin Schwidefsky 		 */
756e179d64SMartin Schwidefsky 		nospec_disable = 0;
7617e89e13SSven Schnelle 		__clear_facility(82, alt_stfle_fac_list);
776e179d64SMartin Schwidefsky 	}
786e179d64SMartin Schwidefsky 	/*
796e179d64SMartin Schwidefsky 	 * If the kernel has not been compiled with expolines the
806e179d64SMartin Schwidefsky 	 * nobp setting decides what is done, this depends on the
816e179d64SMartin Schwidefsky 	 * CONFIG_KERNEL_NP option and the nobp/nospec parameters.
826e179d64SMartin Schwidefsky 	 */
836e179d64SMartin Schwidefsky }
846e179d64SMartin Schwidefsky 
85f19fbd5eSMartin Schwidefsky static int __init spectre_v2_setup_early(char *str)
86f19fbd5eSMartin Schwidefsky {
87f19fbd5eSMartin Schwidefsky 	if (str && !strncmp(str, "on", 2)) {
886e179d64SMartin Schwidefsky 		nospec_disable = 0;
8917e89e13SSven Schnelle 		__clear_facility(82, alt_stfle_fac_list);
90f19fbd5eSMartin Schwidefsky 	}
916e179d64SMartin Schwidefsky 	if (str && !strncmp(str, "off", 3))
926e179d64SMartin Schwidefsky 		nospec_disable = 1;
936e179d64SMartin Schwidefsky 	if (str && !strncmp(str, "auto", 4))
946a3d1e81SMartin Schwidefsky 		nospec_auto_detect();
95f19fbd5eSMartin Schwidefsky 	return 0;
96f19fbd5eSMartin Schwidefsky }
97f19fbd5eSMartin Schwidefsky early_param("spectre_v2", spectre_v2_setup_early);
98f19fbd5eSMartin Schwidefsky 
99f19fbd5eSMartin Schwidefsky static void __init_or_module __nospec_revert(s32 *start, s32 *end)
100f19fbd5eSMartin Schwidefsky {
101f19fbd5eSMartin Schwidefsky 	enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type;
102c74d3c18SKees Cook 	static const u8 branch[] = { 0x47, 0x00, 0x07, 0x00 };
103f19fbd5eSMartin Schwidefsky 	u8 *instr, *thunk, *br;
104f19fbd5eSMartin Schwidefsky 	u8 insnbuf[6];
105f19fbd5eSMartin Schwidefsky 	s32 *epo;
106f19fbd5eSMartin Schwidefsky 
107f19fbd5eSMartin Schwidefsky 	/* Second part of the instruction replace is always a nop */
1082268169cSVasily Gorbik 	memcpy(insnbuf + 2, branch, sizeof(branch));
109f19fbd5eSMartin Schwidefsky 	for (epo = start; epo < end; epo++) {
110f19fbd5eSMartin Schwidefsky 		instr = (u8 *) epo + *epo;
111f19fbd5eSMartin Schwidefsky 		if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
112f19fbd5eSMartin Schwidefsky 			type = BRCL_EXPOLINE;	/* brcl instruction */
113f19fbd5eSMartin Schwidefsky 		else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05)
114f19fbd5eSMartin Schwidefsky 			type = BRASL_EXPOLINE;	/* brasl instruction */
115f19fbd5eSMartin Schwidefsky 		else
116f19fbd5eSMartin Schwidefsky 			continue;
117f19fbd5eSMartin Schwidefsky 		thunk = instr + (*(int *)(instr + 2)) * 2;
118f19fbd5eSMartin Schwidefsky 		if (thunk[0] == 0xc6 && thunk[1] == 0x00)
119f19fbd5eSMartin Schwidefsky 			/* exrl %r0,<target-br> */
120f19fbd5eSMartin Schwidefsky 			br = thunk + (*(int *)(thunk + 2)) * 2;
121f19fbd5eSMartin Schwidefsky 		else
122f19fbd5eSMartin Schwidefsky 			continue;
1232268169cSVasily Gorbik 		if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
124f19fbd5eSMartin Schwidefsky 			continue;
125f19fbd5eSMartin Schwidefsky 		switch (type) {
126f19fbd5eSMartin Schwidefsky 		case BRCL_EXPOLINE:
1272268169cSVasily Gorbik 			/* brcl to thunk, replace with br + nop */
128f19fbd5eSMartin Schwidefsky 			insnbuf[0] = br[0];
129f19fbd5eSMartin Schwidefsky 			insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
130f19fbd5eSMartin Schwidefsky 			break;
131f19fbd5eSMartin Schwidefsky 		case BRASL_EXPOLINE:
1322268169cSVasily Gorbik 			/* brasl to thunk, replace with basr + nop */
1336deaa3bbSMartin Schwidefsky 			insnbuf[0] = 0x0d;
1342268169cSVasily Gorbik 			insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
135f19fbd5eSMartin Schwidefsky 			break;
136f19fbd5eSMartin Schwidefsky 		}
137f19fbd5eSMartin Schwidefsky 
138f19fbd5eSMartin Schwidefsky 		s390_kernel_write(instr, insnbuf, 6);
139f19fbd5eSMartin Schwidefsky 	}
140f19fbd5eSMartin Schwidefsky }
141f19fbd5eSMartin Schwidefsky 
1426e179d64SMartin Schwidefsky void __init_or_module nospec_revert(s32 *start, s32 *end)
143f19fbd5eSMartin Schwidefsky {
1446e179d64SMartin Schwidefsky 	if (nospec_disable)
145f19fbd5eSMartin Schwidefsky 		__nospec_revert(start, end);
146f19fbd5eSMartin Schwidefsky }
147f19fbd5eSMartin Schwidefsky 
148f19fbd5eSMartin Schwidefsky extern s32 __nospec_call_start[], __nospec_call_end[];
149f19fbd5eSMartin Schwidefsky extern s32 __nospec_return_start[], __nospec_return_end[];
150f19fbd5eSMartin Schwidefsky void __init nospec_init_branches(void)
151f19fbd5eSMartin Schwidefsky {
1526e179d64SMartin Schwidefsky 	nospec_revert(__nospec_call_start, __nospec_call_end);
1536e179d64SMartin Schwidefsky 	nospec_revert(__nospec_return_start, __nospec_return_end);
154f19fbd5eSMartin Schwidefsky }
155b2e2f43aSMartin Schwidefsky 
156b2e2f43aSMartin Schwidefsky #endif /* CONFIG_EXPOLINE */
157