alternative.c 3.0 KB

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