alternative.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #include <linux/module.h>
  2. #include <asm/alternative.h>
  3. #include <asm/facility.h>
  4. #include <asm/nospec-branch.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. struct brcl_insn {
  14. u16 opc;
  15. s32 disp;
  16. } __packed;
  17. static u16 __initdata_or_module nop16 = 0x0700;
  18. static u32 __initdata_or_module nop32 = 0x47000000;
  19. static struct brcl_insn __initdata_or_module nop48 = {
  20. 0xc004, 0
  21. };
  22. static const void *nops[] __initdata_or_module = {
  23. &nop16,
  24. &nop32,
  25. &nop48
  26. };
  27. static void __init_or_module add_jump_padding(void *insns, unsigned int len)
  28. {
  29. struct brcl_insn brcl = {
  30. 0xc0f4,
  31. len / 2
  32. };
  33. memcpy(insns, &brcl, sizeof(brcl));
  34. insns += sizeof(brcl);
  35. len -= sizeof(brcl);
  36. while (len > 0) {
  37. memcpy(insns, &nop16, 2);
  38. insns += 2;
  39. len -= 2;
  40. }
  41. }
  42. static void __init_or_module add_padding(void *insns, unsigned int len)
  43. {
  44. if (len > 6)
  45. add_jump_padding(insns, len);
  46. else if (len >= 2)
  47. memcpy(insns, nops[len / 2 - 1], len);
  48. }
  49. static void __init_or_module __apply_alternatives(struct alt_instr *start,
  50. struct alt_instr *end)
  51. {
  52. struct alt_instr *a;
  53. u8 *instr, *replacement;
  54. u8 insnbuf[MAX_PATCH_LEN];
  55. /*
  56. * The scan order should be from start to end. A later scanned
  57. * alternative code can overwrite previously scanned alternative code.
  58. */
  59. for (a = start; a < end; a++) {
  60. int insnbuf_sz = 0;
  61. instr = (u8 *)&a->instr_offset + a->instr_offset;
  62. replacement = (u8 *)&a->repl_offset + a->repl_offset;
  63. if (!__test_facility(a->facility,
  64. S390_lowcore.alt_stfle_fac_list))
  65. continue;
  66. if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
  67. WARN_ONCE(1, "cpu alternatives instructions length is "
  68. "odd, skipping patching\n");
  69. continue;
  70. }
  71. memcpy(insnbuf, replacement, a->replacementlen);
  72. insnbuf_sz = a->replacementlen;
  73. if (a->instrlen > a->replacementlen) {
  74. add_padding(insnbuf + a->replacementlen,
  75. a->instrlen - a->replacementlen);
  76. insnbuf_sz += a->instrlen - a->replacementlen;
  77. }
  78. s390_kernel_write(instr, insnbuf, insnbuf_sz);
  79. }
  80. }
  81. void __init_or_module apply_alternatives(struct alt_instr *start,
  82. struct alt_instr *end)
  83. {
  84. if (!alt_instr_disabled)
  85. __apply_alternatives(start, end);
  86. }
  87. extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
  88. void __init apply_alternative_instructions(void)
  89. {
  90. apply_alternatives(__alt_instructions, __alt_instructions_end);
  91. }