cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nospec-branch.c (4065B)


      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
      7static int __init nobp_setup_early(char *str)
      8{
      9	bool enabled;
     10	int rc;
     11
     12	rc = kstrtobool(str, &enabled);
     13	if (rc)
     14		return rc;
     15	if (enabled && test_facility(82)) {
     16		/*
     17		 * The user explicitely requested nobp=1, enable it and
     18		 * disable the expoline support.
     19		 */
     20		__set_facility(82, alt_stfle_fac_list);
     21		if (IS_ENABLED(CONFIG_EXPOLINE))
     22			nospec_disable = 1;
     23	} else {
     24		__clear_facility(82, alt_stfle_fac_list);
     25	}
     26	return 0;
     27}
     28early_param("nobp", nobp_setup_early);
     29
     30static int __init nospec_setup_early(char *str)
     31{
     32	__clear_facility(82, alt_stfle_fac_list);
     33	return 0;
     34}
     35early_param("nospec", nospec_setup_early);
     36
     37static int __init nospec_report(void)
     38{
     39	if (test_facility(156))
     40		pr_info("Spectre V2 mitigation: etokens\n");
     41	if (nospec_uses_trampoline())
     42		pr_info("Spectre V2 mitigation: execute trampolines\n");
     43	if (__test_facility(82, alt_stfle_fac_list))
     44		pr_info("Spectre V2 mitigation: limited branch prediction\n");
     45	return 0;
     46}
     47arch_initcall(nospec_report);
     48
     49#ifdef CONFIG_EXPOLINE
     50
     51int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
     52
     53static int __init nospectre_v2_setup_early(char *str)
     54{
     55	nospec_disable = 1;
     56	return 0;
     57}
     58early_param("nospectre_v2", nospectre_v2_setup_early);
     59
     60void __init nospec_auto_detect(void)
     61{
     62	if (test_facility(156) || cpu_mitigations_off()) {
     63		/*
     64		 * The machine supports etokens.
     65		 * Disable expolines and disable nobp.
     66		 */
     67		if (__is_defined(CC_USING_EXPOLINE))
     68			nospec_disable = 1;
     69		__clear_facility(82, alt_stfle_fac_list);
     70	} else if (__is_defined(CC_USING_EXPOLINE)) {
     71		/*
     72		 * The kernel has been compiled with expolines.
     73		 * Keep expolines enabled and disable nobp.
     74		 */
     75		nospec_disable = 0;
     76		__clear_facility(82, alt_stfle_fac_list);
     77	}
     78	/*
     79	 * If the kernel has not been compiled with expolines the
     80	 * nobp setting decides what is done, this depends on the
     81	 * CONFIG_KERNEL_NP option and the nobp/nospec parameters.
     82	 */
     83}
     84
     85static int __init spectre_v2_setup_early(char *str)
     86{
     87	if (str && !strncmp(str, "on", 2)) {
     88		nospec_disable = 0;
     89		__clear_facility(82, alt_stfle_fac_list);
     90	}
     91	if (str && !strncmp(str, "off", 3))
     92		nospec_disable = 1;
     93	if (str && !strncmp(str, "auto", 4))
     94		nospec_auto_detect();
     95	return 0;
     96}
     97early_param("spectre_v2", spectre_v2_setup_early);
     98
     99static void __init_or_module __nospec_revert(s32 *start, s32 *end)
    100{
    101	enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type;
    102	static const u8 branch[] = { 0x47, 0x00, 0x07, 0x00 };
    103	u8 *instr, *thunk, *br;
    104	u8 insnbuf[6];
    105	s32 *epo;
    106
    107	/* Second part of the instruction replace is always a nop */
    108	memcpy(insnbuf + 2, branch, sizeof(branch));
    109	for (epo = start; epo < end; epo++) {
    110		instr = (u8 *) epo + *epo;
    111		if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
    112			type = BRCL_EXPOLINE;	/* brcl instruction */
    113		else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05)
    114			type = BRASL_EXPOLINE;	/* brasl instruction */
    115		else
    116			continue;
    117		thunk = instr + (*(int *)(instr + 2)) * 2;
    118		if (thunk[0] == 0xc6 && thunk[1] == 0x00)
    119			/* exrl %r0,<target-br> */
    120			br = thunk + (*(int *)(thunk + 2)) * 2;
    121		else
    122			continue;
    123		if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
    124			continue;
    125		switch (type) {
    126		case BRCL_EXPOLINE:
    127			/* brcl to thunk, replace with br + nop */
    128			insnbuf[0] = br[0];
    129			insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
    130			break;
    131		case BRASL_EXPOLINE:
    132			/* brasl to thunk, replace with basr + nop */
    133			insnbuf[0] = 0x0d;
    134			insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
    135			break;
    136		}
    137
    138		s390_kernel_write(instr, insnbuf, 6);
    139	}
    140}
    141
    142void __init_or_module nospec_revert(s32 *start, s32 *end)
    143{
    144	if (nospec_disable)
    145		__nospec_revert(start, end);
    146}
    147
    148extern s32 __nospec_call_start[], __nospec_call_end[];
    149extern s32 __nospec_return_start[], __nospec_return_end[];
    150void __init nospec_init_branches(void)
    151{
    152	nospec_revert(__nospec_call_start, __nospec_call_end);
    153	nospec_revert(__nospec_return_start, __nospec_return_end);
    154}
    155
    156#endif /* CONFIG_EXPOLINE */