linux/arch/s390/kernel/nospec-branch.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/module.h>
   3#include <asm/nospec-branch.h>
   4
   5int nospec_call_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
   6int nospec_return_disable = !IS_ENABLED(CONFIG_EXPOLINE_FULL);
   7
   8static int __init nospectre_v2_setup_early(char *str)
   9{
  10        nospec_call_disable = 1;
  11        nospec_return_disable = 1;
  12        return 0;
  13}
  14early_param("nospectre_v2", nospectre_v2_setup_early);
  15
  16static int __init spectre_v2_setup_early(char *str)
  17{
  18        if (str && !strncmp(str, "on", 2)) {
  19                nospec_call_disable = 0;
  20                nospec_return_disable = 0;
  21        }
  22        if (str && !strncmp(str, "off", 3)) {
  23                nospec_call_disable = 1;
  24                nospec_return_disable = 1;
  25        }
  26        if (str && !strncmp(str, "auto", 4)) {
  27                nospec_call_disable = 0;
  28                nospec_return_disable = 1;
  29        }
  30        return 0;
  31}
  32early_param("spectre_v2", spectre_v2_setup_early);
  33
  34static void __init_or_module __nospec_revert(s32 *start, s32 *end)
  35{
  36        enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type;
  37        u8 *instr, *thunk, *br;
  38        u8 insnbuf[6];
  39        s32 *epo;
  40
  41        /* Second part of the instruction replace is always a nop */
  42        memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x00, 0x00 }, 4);
  43        for (epo = start; epo < end; epo++) {
  44                instr = (u8 *) epo + *epo;
  45                if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
  46                        type = BRCL_EXPOLINE;   /* brcl instruction */
  47                else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05)
  48                        type = BRASL_EXPOLINE;  /* brasl instruction */
  49                else
  50                        continue;
  51                thunk = instr + (*(int *)(instr + 2)) * 2;
  52                if (thunk[0] == 0xc6 && thunk[1] == 0x00)
  53                        /* exrl %r0,<target-br> */
  54                        br = thunk + (*(int *)(thunk + 2)) * 2;
  55                else if (thunk[0] == 0xc0 && (thunk[1] & 0x0f) == 0x00 &&
  56                         thunk[6] == 0x44 && thunk[7] == 0x00 &&
  57                         (thunk[8] & 0x0f) == 0x00 && thunk[9] == 0x00 &&
  58                         (thunk[1] & 0xf0) == (thunk[8] & 0xf0))
  59                        /* larl %rx,<target br> + ex %r0,0(%rx) */
  60                        br = thunk + (*(int *)(thunk + 2)) * 2;
  61                else
  62                        continue;
  63                if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
  64                        continue;
  65                switch (type) {
  66                case BRCL_EXPOLINE:
  67                        /* brcl to thunk, replace with br + nop */
  68                        insnbuf[0] = br[0];
  69                        insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
  70                        break;
  71                case BRASL_EXPOLINE:
  72                        /* brasl to thunk, replace with basr + nop */
  73                        insnbuf[0] = 0x0d;
  74                        insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
  75                        break;
  76                }
  77
  78                s390_kernel_write(instr, insnbuf, 6);
  79        }
  80}
  81
  82void __init_or_module nospec_call_revert(s32 *start, s32 *end)
  83{
  84        if (nospec_call_disable)
  85                __nospec_revert(start, end);
  86}
  87
  88void __init_or_module nospec_return_revert(s32 *start, s32 *end)
  89{
  90        if (nospec_return_disable)
  91                __nospec_revert(start, end);
  92}
  93
  94extern s32 __nospec_call_start[], __nospec_call_end[];
  95extern s32 __nospec_return_start[], __nospec_return_end[];
  96void __init nospec_init_branches(void)
  97{
  98        nospec_call_revert(__nospec_call_start, __nospec_call_end);
  99        nospec_return_revert(__nospec_return_start, __nospec_return_end);
 100}
 101