^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #include <asm/alternative.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #include <asm/facility.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) #include <asm/nospec-branch.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #define MAX_PATCH_LEN (255 - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) static int __initdata_or_module alt_instr_disabled;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) static int __init disable_alternative_instructions(char *str)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) alt_instr_disabled = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) early_param("noaltinstr", disable_alternative_instructions);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) struct brcl_insn {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) u16 opc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) s32 disp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) } __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static u16 __initdata_or_module nop16 = 0x0700;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static u32 __initdata_or_module nop32 = 0x47000000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static struct brcl_insn __initdata_or_module nop48 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) 0xc004, 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static const void *nops[] __initdata_or_module = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) &nop16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) &nop32,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) &nop48
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static void __init_or_module add_jump_padding(void *insns, unsigned int len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct brcl_insn brcl = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) 0xc0f4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) len / 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) memcpy(insns, &brcl, sizeof(brcl));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) insns += sizeof(brcl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) len -= sizeof(brcl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) while (len > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) memcpy(insns, &nop16, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) insns += 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) len -= 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static void __init_or_module add_padding(void *insns, unsigned int len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (len > 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) add_jump_padding(insns, len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) else if (len >= 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) memcpy(insns, nops[len / 2 - 1], len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static void __init_or_module __apply_alternatives(struct alt_instr *start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) struct alt_instr *end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) struct alt_instr *a;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) u8 *instr, *replacement;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) u8 insnbuf[MAX_PATCH_LEN];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * The scan order should be from start to end. A later scanned
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * alternative code can overwrite previously scanned alternative code.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) for (a = start; a < end; a++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) int insnbuf_sz = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) instr = (u8 *)&a->instr_offset + a->instr_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) replacement = (u8 *)&a->repl_offset + a->repl_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) if (!__test_facility(a->facility,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) S390_lowcore.alt_stfle_fac_list))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) WARN_ONCE(1, "cpu alternatives instructions length is "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) "odd, skipping patching\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) memcpy(insnbuf, replacement, a->replacementlen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) insnbuf_sz = a->replacementlen;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) if (a->instrlen > a->replacementlen) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) add_padding(insnbuf + a->replacementlen,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) a->instrlen - a->replacementlen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) insnbuf_sz += a->instrlen - a->replacementlen;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) s390_kernel_write(instr, insnbuf, insnbuf_sz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) void __init_or_module apply_alternatives(struct alt_instr *start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct alt_instr *end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (!alt_instr_disabled)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) __apply_alternatives(start, end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) void __init apply_alternative_instructions(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) apply_alternatives(__alt_instructions, __alt_instructions_end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }