linux/arch/arm64/include/asm/alternative-macros.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#ifndef __ASM_ALTERNATIVE_MACROS_H
   3#define __ASM_ALTERNATIVE_MACROS_H
   4
   5#include <asm/cpucaps.h>
   6#include <asm/insn-def.h>
   7
   8#define ARM64_CB_PATCH ARM64_NCAPS
   9
  10#ifndef __ASSEMBLY__
  11
  12#include <linux/stringify.h>
  13
  14#define ALTINSTR_ENTRY(feature)                                               \
  15        " .word 661b - .\n"                             /* label           */ \
  16        " .word 663f - .\n"                             /* new instruction */ \
  17        " .hword " __stringify(feature) "\n"            /* feature bit     */ \
  18        " .byte 662b-661b\n"                            /* source len      */ \
  19        " .byte 664f-663f\n"                            /* replacement len */
  20
  21#define ALTINSTR_ENTRY_CB(feature, cb)                                        \
  22        " .word 661b - .\n"                             /* label           */ \
  23        " .word " __stringify(cb) "- .\n"               /* callback */        \
  24        " .hword " __stringify(feature) "\n"            /* feature bit     */ \
  25        " .byte 662b-661b\n"                            /* source len      */ \
  26        " .byte 664f-663f\n"                            /* replacement len */
  27
  28/*
  29 * alternative assembly primitive:
  30 *
  31 * If any of these .org directive fail, it means that insn1 and insn2
  32 * don't have the same length. This used to be written as
  33 *
  34 * .if ((664b-663b) != (662b-661b))
  35 *      .error "Alternatives instruction length mismatch"
  36 * .endif
  37 *
  38 * but most assemblers die if insn1 or insn2 have a .inst. This should
  39 * be fixed in a binutils release posterior to 2.25.51.0.2 (anything
  40 * containing commit 4e4d08cf7399b606 or c1baaddf8861).
  41 *
  42 * Alternatives with callbacks do not generate replacement instructions.
  43 */
  44#define __ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg_enabled)     \
  45        ".if "__stringify(cfg_enabled)" == 1\n"                         \
  46        "661:\n\t"                                                      \
  47        oldinstr "\n"                                                   \
  48        "662:\n"                                                        \
  49        ".pushsection .altinstructions,\"a\"\n"                         \
  50        ALTINSTR_ENTRY(feature)                                         \
  51        ".popsection\n"                                                 \
  52        ".subsection 1\n"                                               \
  53        "663:\n\t"                                                      \
  54        newinstr "\n"                                                   \
  55        "664:\n\t"                                                      \
  56        ".org   . - (664b-663b) + (662b-661b)\n\t"                      \
  57        ".org   . - (662b-661b) + (664b-663b)\n\t"                      \
  58        ".previous\n"                                                   \
  59        ".endif\n"
  60
  61#define __ALTERNATIVE_CFG_CB(oldinstr, feature, cfg_enabled, cb)        \
  62        ".if "__stringify(cfg_enabled)" == 1\n"                         \
  63        "661:\n\t"                                                      \
  64        oldinstr "\n"                                                   \
  65        "662:\n"                                                        \
  66        ".pushsection .altinstructions,\"a\"\n"                         \
  67        ALTINSTR_ENTRY_CB(feature, cb)                                  \
  68        ".popsection\n"                                                 \
  69        "663:\n\t"                                                      \
  70        "664:\n\t"                                                      \
  71        ".endif\n"
  72
  73#define _ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg, ...) \
  74        __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg))
  75
  76#define ALTERNATIVE_CB(oldinstr, cb) \
  77        __ALTERNATIVE_CFG_CB(oldinstr, ARM64_CB_PATCH, 1, cb)
  78#else
  79
  80#include <asm/assembler.h>
  81
  82.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
  83        .word \orig_offset - .
  84        .word \alt_offset - .
  85        .hword \feature
  86        .byte \orig_len
  87        .byte \alt_len
  88.endm
  89
  90.macro alternative_insn insn1, insn2, cap, enable = 1
  91        .if \enable
  92661:    \insn1
  93662:    .pushsection .altinstructions, "a"
  94        altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
  95        .popsection
  96        .subsection 1
  97663:    \insn2
  98664:    .org    . - (664b-663b) + (662b-661b)
  99        .org    . - (662b-661b) + (664b-663b)
 100        .previous
 101        .endif
 102.endm
 103
 104/*
 105 * Alternative sequences
 106 *
 107 * The code for the case where the capability is not present will be
 108 * assembled and linked as normal. There are no restrictions on this
 109 * code.
 110 *
 111 * The code for the case where the capability is present will be
 112 * assembled into a special section to be used for dynamic patching.
 113 * Code for that case must:
 114 *
 115 * 1. Be exactly the same length (in bytes) as the default code
 116 *    sequence.
 117 *
 118 * 2. Not contain a branch target that is used outside of the
 119 *    alternative sequence it is defined in (branches into an
 120 *    alternative sequence are not fixed up).
 121 */
 122
 123/*
 124 * Begin an alternative code sequence.
 125 */
 126.macro alternative_if_not cap
 127        .set .Lasm_alt_mode, 0
 128        .pushsection .altinstructions, "a"
 129        altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
 130        .popsection
 131661:
 132.endm
 133
 134.macro alternative_if cap
 135        .set .Lasm_alt_mode, 1
 136        .pushsection .altinstructions, "a"
 137        altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f
 138        .popsection
 139        .subsection 1
 140        .align 2        /* So GAS knows label 661 is suitably aligned */
 141661:
 142.endm
 143
 144.macro alternative_cb cb
 145        .set .Lasm_alt_mode, 0
 146        .pushsection .altinstructions, "a"
 147        altinstruction_entry 661f, \cb, ARM64_CB_PATCH, 662f-661f, 0
 148        .popsection
 149661:
 150.endm
 151
 152/*
 153 * Provide the other half of the alternative code sequence.
 154 */
 155.macro alternative_else
 156662:
 157        .if .Lasm_alt_mode==0
 158        .subsection 1
 159        .else
 160        .previous
 161        .endif
 162663:
 163.endm
 164
 165/*
 166 * Complete an alternative code sequence.
 167 */
 168.macro alternative_endif
 169664:
 170        .org    . - (664b-663b) + (662b-661b)
 171        .org    . - (662b-661b) + (664b-663b)
 172        .if .Lasm_alt_mode==0
 173        .previous
 174        .endif
 175.endm
 176
 177/*
 178 * Callback-based alternative epilogue
 179 */
 180.macro alternative_cb_end
 181662:
 182.endm
 183
 184/*
 185 * Provides a trivial alternative or default sequence consisting solely
 186 * of NOPs. The number of NOPs is chosen automatically to match the
 187 * previous case.
 188 */
 189.macro alternative_else_nop_endif
 190alternative_else
 191        nops    (662b-661b) / AARCH64_INSN_SIZE
 192alternative_endif
 193.endm
 194
 195#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...)   \
 196        alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
 197
 198#endif  /*  __ASSEMBLY__  */
 199
 200/*
 201 * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature));
 202 *
 203 * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature, CONFIG_FOO));
 204 * N.B. If CONFIG_FOO is specified, but not selected, the whole block
 205 *      will be omitted, including oldinstr.
 206 */
 207#define ALTERNATIVE(oldinstr, newinstr, ...)   \
 208        _ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1)
 209
 210#endif /* __ASM_ALTERNATIVE_MACROS_H */
 211