alternative.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. #ifndef _ASM_S390_ALTERNATIVE_H
  3. #define _ASM_S390_ALTERNATIVE_H
  4. #ifndef __ASSEMBLY__
  5. #include <linux/types.h>
  6. #include <linux/stddef.h>
  7. #include <linux/stringify.h>
  8. struct alt_instr {
  9. s32 instr_offset; /* original instruction */
  10. s32 repl_offset; /* offset to replacement instruction */
  11. u16 facility; /* facility bit set for replacement */
  12. u8 instrlen; /* length of original instruction */
  13. u8 replacementlen; /* length of new instruction */
  14. } __packed;
  15. void apply_alternative_instructions(void);
  16. void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
  17. /*
  18. * |661: |662: |6620 |663:
  19. * +-----------+---------------------+
  20. * | oldinstr | oldinstr_padding |
  21. * | +----------+----------+
  22. * | | | |
  23. * | | >6 bytes |6/4/2 nops|
  24. * | |6 bytes jg----------->
  25. * +-----------+---------------------+
  26. * ^^ static padding ^^
  27. *
  28. * .altinstr_replacement section
  29. * +---------------------+-----------+
  30. * |6641: |6651:
  31. * | alternative instr 1 |
  32. * +-----------+---------+- - - - - -+
  33. * |6642: |6652: |
  34. * | alternative instr 2 | padding
  35. * +---------------------+- - - - - -+
  36. * ^ runtime ^
  37. *
  38. * .altinstructions section
  39. * +---------------------------------+
  40. * | alt_instr entries for each |
  41. * | alternative instr |
  42. * +---------------------------------+
  43. */
  44. #define b_altinstr(num) "664"#num
  45. #define e_altinstr(num) "665"#num
  46. #define e_oldinstr_pad_end "663"
  47. #define oldinstr_len "662b-661b"
  48. #define oldinstr_total_len e_oldinstr_pad_end"b-661b"
  49. #define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b"
  50. #define oldinstr_pad_len(num) \
  51. "-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \
  52. "((" altinstr_len(num) ")-(" oldinstr_len "))"
  53. #define INSTR_LEN_SANITY_CHECK(len) \
  54. ".if " len " > 254\n" \
  55. "\t.error \"cpu alternatives does not support instructions " \
  56. "blocks > 254 bytes\"\n" \
  57. ".endif\n" \
  58. ".if (" len ") %% 2\n" \
  59. "\t.error \"cpu alternatives instructions length is odd\"\n" \
  60. ".endif\n"
  61. #define OLDINSTR_PADDING(oldinstr, num) \
  62. ".if " oldinstr_pad_len(num) " > 6\n" \
  63. "\tjg " e_oldinstr_pad_end "f\n" \
  64. "6620:\n" \
  65. "\t.fill (" oldinstr_pad_len(num) " - (6620b-662b)) / 2, 2, 0x0700\n" \
  66. ".else\n" \
  67. "\t.fill " oldinstr_pad_len(num) " / 6, 6, 0xc0040000\n" \
  68. "\t.fill " oldinstr_pad_len(num) " %% 6 / 4, 4, 0x47000000\n" \
  69. "\t.fill " oldinstr_pad_len(num) " %% 6 %% 4 / 2, 2, 0x0700\n" \
  70. ".endif\n"
  71. #define OLDINSTR(oldinstr, num) \
  72. "661:\n\t" oldinstr "\n662:\n" \
  73. OLDINSTR_PADDING(oldinstr, num) \
  74. e_oldinstr_pad_end ":\n" \
  75. INSTR_LEN_SANITY_CHECK(oldinstr_len)
  76. #define OLDINSTR_2(oldinstr, num1, num2) \
  77. "661:\n\t" oldinstr "\n662:\n" \
  78. ".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \
  79. OLDINSTR_PADDING(oldinstr, num2) \
  80. ".else\n" \
  81. OLDINSTR_PADDING(oldinstr, num1) \
  82. ".endif\n" \
  83. e_oldinstr_pad_end ":\n" \
  84. INSTR_LEN_SANITY_CHECK(oldinstr_len)
  85. #define ALTINSTR_ENTRY(facility, num) \
  86. "\t.long 661b - .\n" /* old instruction */ \
  87. "\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \
  88. "\t.word " __stringify(facility) "\n" /* facility bit */ \
  89. "\t.byte " oldinstr_total_len "\n" /* source len */ \
  90. "\t.byte " altinstr_len(num) "\n" /* alt instruction len */
  91. #define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \
  92. b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \
  93. INSTR_LEN_SANITY_CHECK(altinstr_len(num))
  94. /* alternative assembly primitive: */
  95. #define ALTERNATIVE(oldinstr, altinstr, facility) \
  96. ".pushsection .altinstr_replacement, \"ax\"\n" \
  97. ALTINSTR_REPLACEMENT(altinstr, 1) \
  98. ".popsection\n" \
  99. OLDINSTR(oldinstr, 1) \
  100. ".pushsection .altinstructions,\"a\"\n" \
  101. ALTINSTR_ENTRY(facility, 1) \
  102. ".popsection\n"
  103. #define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2)\
  104. ".pushsection .altinstr_replacement, \"ax\"\n" \
  105. ALTINSTR_REPLACEMENT(altinstr1, 1) \
  106. ALTINSTR_REPLACEMENT(altinstr2, 2) \
  107. ".popsection\n" \
  108. OLDINSTR_2(oldinstr, 1, 2) \
  109. ".pushsection .altinstructions,\"a\"\n" \
  110. ALTINSTR_ENTRY(facility1, 1) \
  111. ALTINSTR_ENTRY(facility2, 2) \
  112. ".popsection\n"
  113. /*
  114. * Alternative instructions for different CPU types or capabilities.
  115. *
  116. * This allows to use optimized instructions even on generic binary
  117. * kernels.
  118. *
  119. * oldinstr is padded with jump and nops at compile time if altinstr is
  120. * longer. altinstr is padded with jump and nops at run-time during patching.
  121. *
  122. * For non barrier like inlines please define new variants
  123. * without volatile and memory clobber.
  124. */
  125. #define alternative(oldinstr, altinstr, facility) \
  126. asm volatile(ALTERNATIVE(oldinstr, altinstr, facility) : : : "memory")
  127. #define alternative_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \
  128. asm volatile(ALTERNATIVE_2(oldinstr, altinstr1, facility1, \
  129. altinstr2, facility2) ::: "memory")
  130. #endif /* __ASSEMBLY__ */
  131. #endif /* _ASM_S390_ALTERNATIVE_H */