linux/arch/arm64/include/asm/alternative.h
<<
>>
Prefs
   1#ifndef __ASM_ALTERNATIVE_H
   2#define __ASM_ALTERNATIVE_H
   3
   4#include <asm/cpucaps.h>
   5#include <asm/insn.h>
   6
   7#ifndef __ASSEMBLY__
   8
   9#include <linux/init.h>
  10#include <linux/types.h>
  11#include <linux/stddef.h>
  12#include <linux/stringify.h>
  13
  14struct alt_instr {
  15        s32 orig_offset;        /* offset to original instruction */
  16        s32 alt_offset;         /* offset to replacement instruction */
  17        u16 cpufeature;         /* cpufeature bit set for replacement */
  18        u8  orig_len;           /* size of original instruction(s) */
  19        u8  alt_len;            /* size of new instruction(s), <= orig_len */
  20};
  21
  22void __init apply_alternatives_all(void);
  23void apply_alternatives(void *start, size_t length);
  24
  25#define ALTINSTR_ENTRY(feature)                                               \
  26        " .word 661b - .\n"                             /* label           */ \
  27        " .word 663f - .\n"                             /* new instruction */ \
  28        " .hword " __stringify(feature) "\n"            /* feature bit     */ \
  29        " .byte 662b-661b\n"                            /* source len      */ \
  30        " .byte 664f-663f\n"                            /* replacement len */
  31
  32/*
  33 * alternative assembly primitive:
  34 *
  35 * If any of these .org directive fail, it means that insn1 and insn2
  36 * don't have the same length. This used to be written as
  37 *
  38 * .if ((664b-663b) != (662b-661b))
  39 *      .error "Alternatives instruction length mismatch"
  40 * .endif
  41 *
  42 * but most assemblers die if insn1 or insn2 have a .inst. This should
  43 * be fixed in a binutils release posterior to 2.25.51.0.2 (anything
  44 * containing commit 4e4d08cf7399b606 or c1baaddf8861).
  45 */
  46#define __ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg_enabled)     \
  47        ".if "__stringify(cfg_enabled)" == 1\n"                         \
  48        "661:\n\t"                                                      \
  49        oldinstr "\n"                                                   \
  50        "662:\n"                                                        \
  51        ".pushsection .altinstructions,\"a\"\n"                         \
  52        ALTINSTR_ENTRY(feature)                                         \
  53        ".popsection\n"                                                 \
  54        ".pushsection .altinstr_replacement, \"a\"\n"                   \
  55        "663:\n\t"                                                      \
  56        newinstr "\n"                                                   \
  57        "664:\n\t"                                                      \
  58        ".popsection\n\t"                                               \
  59        ".org   . - (664b-663b) + (662b-661b)\n\t"                      \
  60        ".org   . - (662b-661b) + (664b-663b)\n"                        \
  61        ".endif\n"
  62
  63#define _ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg, ...) \
  64        __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg))
  65
  66#else
  67
  68#include <asm/assembler.h>
  69
  70.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
  71        .word \orig_offset - .
  72        .word \alt_offset - .
  73        .hword \feature
  74        .byte \orig_len
  75        .byte \alt_len
  76.endm
  77
  78.macro alternative_insn insn1, insn2, cap, enable = 1
  79        .if \enable
  80661:    \insn1
  81662:    .pushsection .altinstructions, "a"
  82        altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
  83        .popsection
  84        .pushsection .altinstr_replacement, "ax"
  85663:    \insn2
  86664:    .popsection
  87        .org    . - (664b-663b) + (662b-661b)
  88        .org    . - (662b-661b) + (664b-663b)
  89        .endif
  90.endm
  91
  92/*
  93 * Alternative sequences
  94 *
  95 * The code for the case where the capability is not present will be
  96 * assembled and linked as normal. There are no restrictions on this
  97 * code.
  98 *
  99 * The code for the case where the capability is present will be
 100 * assembled into a special section to be used for dynamic patching.
 101 * Code for that case must:
 102 *
 103 * 1. Be exactly the same length (in bytes) as the default code
 104 *    sequence.
 105 *
 106 * 2. Not contain a branch target that is used outside of the
 107 *    alternative sequence it is defined in (branches into an
 108 *    alternative sequence are not fixed up).
 109 */
 110
 111/*
 112 * Begin an alternative code sequence.
 113 */
 114.macro alternative_if_not cap
 115        .set .Lasm_alt_mode, 0
 116        .pushsection .altinstructions, "a"
 117        altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
 118        .popsection
 119661:
 120.endm
 121
 122.macro alternative_if cap
 123        .set .Lasm_alt_mode, 1
 124        .pushsection .altinstructions, "a"
 125        altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f
 126        .popsection
 127        .pushsection .altinstr_replacement, "ax"
 128        .align 2        /* So GAS knows label 661 is suitably aligned */
 129661:
 130.endm
 131
 132/*
 133 * Provide the other half of the alternative code sequence.
 134 */
 135.macro alternative_else
 136662:
 137        .if .Lasm_alt_mode==0
 138        .pushsection .altinstr_replacement, "ax"
 139        .else
 140        .popsection
 141        .endif
 142663:
 143.endm
 144
 145/*
 146 * Complete an alternative code sequence.
 147 */
 148.macro alternative_endif
 149664:
 150        .if .Lasm_alt_mode==0
 151        .popsection
 152        .endif
 153        .org    . - (664b-663b) + (662b-661b)
 154        .org    . - (662b-661b) + (664b-663b)
 155.endm
 156
 157/*
 158 * Provides a trivial alternative or default sequence consisting solely
 159 * of NOPs. The number of NOPs is chosen automatically to match the
 160 * previous case.
 161 */
 162.macro alternative_else_nop_endif
 163alternative_else
 164        nops    (662b-661b) / AARCH64_INSN_SIZE
 165alternative_endif
 166.endm
 167
 168#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...)   \
 169        alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
 170
 171.macro user_alt, label, oldinstr, newinstr, cond
 1729999:   alternative_insn "\oldinstr", "\newinstr", \cond
 173        _ASM_EXTABLE 9999b, \label
 174.endm
 175
 176/*
 177 * Generate the assembly for UAO alternatives with exception table entries.
 178 * This is complicated as there is no post-increment or pair versions of the
 179 * unprivileged instructions, and USER() only works for single instructions.
 180 */
 181#ifdef CONFIG_ARM64_UAO
 182        .macro uao_ldp l, reg1, reg2, addr, post_inc
 183                alternative_if_not ARM64_HAS_UAO
 1848888:                   ldp     \reg1, \reg2, [\addr], \post_inc;
 1858889:                   nop;
 186                        nop;
 187                alternative_else
 188                        ldtr    \reg1, [\addr];
 189                        ldtr    \reg2, [\addr, #8];
 190                        add     \addr, \addr, \post_inc;
 191                alternative_endif
 192
 193                _asm_extable    8888b,\l;
 194                _asm_extable    8889b,\l;
 195        .endm
 196
 197        .macro uao_stp l, reg1, reg2, addr, post_inc
 198                alternative_if_not ARM64_HAS_UAO
 1998888:                   stp     \reg1, \reg2, [\addr], \post_inc;
 2008889:                   nop;
 201                        nop;
 202                alternative_else
 203                        sttr    \reg1, [\addr];
 204                        sttr    \reg2, [\addr, #8];
 205                        add     \addr, \addr, \post_inc;
 206                alternative_endif
 207
 208                _asm_extable    8888b,\l;
 209                _asm_extable    8889b,\l;
 210        .endm
 211
 212        .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
 213                alternative_if_not ARM64_HAS_UAO
 2148888:                   \inst   \reg, [\addr], \post_inc;
 215                        nop;
 216                alternative_else
 217                        \alt_inst       \reg, [\addr];
 218                        add             \addr, \addr, \post_inc;
 219                alternative_endif
 220
 221                _asm_extable    8888b,\l;
 222        .endm
 223#else
 224        .macro uao_ldp l, reg1, reg2, addr, post_inc
 225                USER(\l, ldp \reg1, \reg2, [\addr], \post_inc)
 226        .endm
 227        .macro uao_stp l, reg1, reg2, addr, post_inc
 228                USER(\l, stp \reg1, \reg2, [\addr], \post_inc)
 229        .endm
 230        .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
 231                USER(\l, \inst \reg, [\addr], \post_inc)
 232        .endm
 233#endif
 234
 235#endif  /*  __ASSEMBLY__  */
 236
 237/*
 238 * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature));
 239 *
 240 * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature, CONFIG_FOO));
 241 * N.B. If CONFIG_FOO is specified, but not selected, the whole block
 242 *      will be omitted, including oldinstr.
 243 */
 244#define ALTERNATIVE(oldinstr, newinstr, ...)   \
 245        _ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1)
 246
 247#endif /* __ASM_ALTERNATIVE_H */
 248