alternative.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #ifndef __ASM_ALTERNATIVE_H
  2. #define __ASM_ALTERNATIVE_H
  3. #include <asm/cpufeature.h>
  4. #ifndef __ASSEMBLY__
  5. #include <linux/init.h>
  6. #include <linux/kconfig.h>
  7. #include <linux/types.h>
  8. #include <linux/stddef.h>
  9. #include <linux/stringify.h>
  10. struct alt_instr {
  11. s32 orig_offset; /* offset to original instruction */
  12. s32 alt_offset; /* offset to replacement instruction */
  13. u16 cpufeature; /* cpufeature bit set for replacement */
  14. u8 orig_len; /* size of original instruction(s) */
  15. u8 alt_len; /* size of new instruction(s), <= orig_len */
  16. };
  17. void __init apply_alternatives_all(void);
  18. void apply_alternatives(void *start, size_t length);
  19. #define ALTINSTR_ENTRY(feature) \
  20. " .word 661b - .\n" /* label */ \
  21. " .word 663f - .\n" /* new instruction */ \
  22. " .hword " __stringify(feature) "\n" /* feature bit */ \
  23. " .byte 662b-661b\n" /* source len */ \
  24. " .byte 664f-663f\n" /* replacement len */
  25. /*
  26. * alternative assembly primitive:
  27. *
  28. * If any of these .org directive fail, it means that insn1 and insn2
  29. * don't have the same length. This used to be written as
  30. *
  31. * .if ((664b-663b) != (662b-661b))
  32. * .error "Alternatives instruction length mismatch"
  33. * .endif
  34. *
  35. * but most assemblers die if insn1 or insn2 have a .inst. This should
  36. * be fixed in a binutils release posterior to 2.25.51.0.2 (anything
  37. * containing commit 4e4d08cf7399b606 or c1baaddf8861).
  38. */
  39. #define __ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg_enabled) \
  40. ".if "__stringify(cfg_enabled)" == 1\n" \
  41. "661:\n\t" \
  42. oldinstr "\n" \
  43. "662:\n" \
  44. ".pushsection .altinstructions,\"a\"\n" \
  45. ALTINSTR_ENTRY(feature) \
  46. ".popsection\n" \
  47. ".pushsection .altinstr_replacement, \"a\"\n" \
  48. "663:\n\t" \
  49. newinstr "\n" \
  50. "664:\n\t" \
  51. ".popsection\n\t" \
  52. ".org . - (664b-663b) + (662b-661b)\n\t" \
  53. ".org . - (662b-661b) + (664b-663b)\n" \
  54. ".endif\n"
  55. #define _ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg, ...) \
  56. __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg))
  57. #else
  58. #include <asm/assembler.h>
  59. .macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
  60. .word \orig_offset - .
  61. .word \alt_offset - .
  62. .hword \feature
  63. .byte \orig_len
  64. .byte \alt_len
  65. .endm
  66. .macro alternative_insn insn1, insn2, cap, enable = 1
  67. .if \enable
  68. 661: \insn1
  69. 662: .pushsection .altinstructions, "a"
  70. altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
  71. .popsection
  72. .pushsection .altinstr_replacement, "ax"
  73. 663: \insn2
  74. 664: .popsection
  75. .org . - (664b-663b) + (662b-661b)
  76. .org . - (662b-661b) + (664b-663b)
  77. .endif
  78. .endm
  79. /*
  80. * Begin an alternative code sequence.
  81. *
  82. * The code that follows this macro will be assembled and linked as
  83. * normal. There are no restrictions on this code.
  84. */
  85. .macro alternative_if_not cap, enable = 1
  86. .if \enable
  87. .pushsection .altinstructions, "a"
  88. altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
  89. .popsection
  90. 661:
  91. .endif
  92. .endm
  93. /*
  94. * Provide the alternative code sequence.
  95. *
  96. * The code that follows this macro is assembled into a special
  97. * section to be used for dynamic patching. Code that follows this
  98. * macro must:
  99. *
  100. * 1. Be exactly the same length (in bytes) as the default code
  101. * sequence.
  102. *
  103. * 2. Not contain a branch target that is used outside of the
  104. * alternative sequence it is defined in (branches into an
  105. * alternative sequence are not fixed up).
  106. */
  107. .macro alternative_else, enable = 1
  108. .if \enable
  109. 662: .pushsection .altinstr_replacement, "ax"
  110. 663:
  111. .endif
  112. .endm
  113. /*
  114. * Complete an alternative code sequence.
  115. */
  116. .macro alternative_endif, enable = 1
  117. .if \enable
  118. 664: .popsection
  119. .org . - (664b-663b) + (662b-661b)
  120. .org . - (662b-661b) + (664b-663b)
  121. .endif
  122. .endm
  123. #define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \
  124. alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
  125. /*
  126. * Generate the assembly for UAO alternatives with exception table entries.
  127. * This is complicated as there is no post-increment or pair versions of the
  128. * unprivileged instructions, and USER() only works for single instructions.
  129. */
  130. #ifdef CONFIG_ARM64_UAO
  131. .macro uao_ldp l, reg1, reg2, addr, post_inc
  132. alternative_if_not ARM64_HAS_UAO
  133. 8888: ldp \reg1, \reg2, [\addr], \post_inc;
  134. 8889: nop;
  135. nop;
  136. alternative_else
  137. ldtr \reg1, [\addr];
  138. ldtr \reg2, [\addr, #8];
  139. add \addr, \addr, \post_inc;
  140. alternative_endif
  141. _asm_extable 8888b,\l;
  142. _asm_extable 8889b,\l;
  143. .endm
  144. .macro uao_stp l, reg1, reg2, addr, post_inc
  145. alternative_if_not ARM64_HAS_UAO
  146. 8888: stp \reg1, \reg2, [\addr], \post_inc;
  147. 8889: nop;
  148. nop;
  149. alternative_else
  150. sttr \reg1, [\addr];
  151. sttr \reg2, [\addr, #8];
  152. add \addr, \addr, \post_inc;
  153. alternative_endif
  154. _asm_extable 8888b,\l;
  155. _asm_extable 8889b,\l;
  156. .endm
  157. .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
  158. alternative_if_not ARM64_HAS_UAO
  159. 8888: \inst \reg, [\addr], \post_inc;
  160. nop;
  161. alternative_else
  162. \alt_inst \reg, [\addr];
  163. add \addr, \addr, \post_inc;
  164. alternative_endif
  165. _asm_extable 8888b,\l;
  166. .endm
  167. #else
  168. .macro uao_ldp l, reg1, reg2, addr, post_inc
  169. USER(\l, ldp \reg1, \reg2, [\addr], \post_inc)
  170. .endm
  171. .macro uao_stp l, reg1, reg2, addr, post_inc
  172. USER(\l, stp \reg1, \reg2, [\addr], \post_inc)
  173. .endm
  174. .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
  175. USER(\l, \inst \reg, [\addr], \post_inc)
  176. .endm
  177. #endif
  178. #endif /* __ASSEMBLY__ */
  179. /*
  180. * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature));
  181. *
  182. * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature, CONFIG_FOO));
  183. * N.B. If CONFIG_FOO is specified, but not selected, the whole block
  184. * will be omitted, including oldinstr.
  185. */
  186. #define ALTERNATIVE(oldinstr, newinstr, ...) \
  187. _ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1)
  188. #endif /* __ASM_ALTERNATIVE_H */