Path: blob/master/arch/arm64/include/asm/alternative-macros.h
26481 views
/* SPDX-License-Identifier: GPL-2.0 */1#ifndef __ASM_ALTERNATIVE_MACROS_H2#define __ASM_ALTERNATIVE_MACROS_H34#include <linux/const.h>5#include <vdso/bits.h>67#include <asm/cpucaps.h>8#include <asm/insn-def.h>910/*11* Binutils 2.27.0 can't handle a 'UL' suffix on constants, so for the assembly12* macros below we must use we must use `(1 << ARM64_CB_SHIFT)`.13*/14#define ARM64_CB_SHIFT 1515#define ARM64_CB_BIT BIT(ARM64_CB_SHIFT)1617#if ARM64_NCAPS >= ARM64_CB_BIT18#error "cpucaps have overflown ARM64_CB_BIT"19#endif2021#ifndef __ASSEMBLY__2223#include <linux/stringify.h>2425#define ALTINSTR_ENTRY(cpucap) \26" .word 661b - .\n" /* label */ \27" .word 663f - .\n" /* new instruction */ \28" .hword " __stringify(cpucap) "\n" /* cpucap */ \29" .byte 662b-661b\n" /* source len */ \30" .byte 664f-663f\n" /* replacement len */3132#define ALTINSTR_ENTRY_CB(cpucap, cb) \33" .word 661b - .\n" /* label */ \34" .word " __stringify(cb) "- .\n" /* callback */ \35" .hword " __stringify(cpucap) "\n" /* cpucap */ \36" .byte 662b-661b\n" /* source len */ \37" .byte 664f-663f\n" /* replacement len */3839/*40* alternative assembly primitive:41*42* If any of these .org directive fail, it means that insn1 and insn243* don't have the same length. This used to be written as44*45* .if ((664b-663b) != (662b-661b))46* .error "Alternatives instruction length mismatch"47* .endif48*49* but most assemblers die if insn1 or insn2 have a .inst. This should50* be fixed in a binutils release posterior to 2.25.51.0.2 (anything51* containing commit 4e4d08cf7399b606 or c1baaddf8861).52*53* Alternatives with callbacks do not generate replacement instructions.54*/55#define __ALTERNATIVE_CFG(oldinstr, newinstr, cpucap, cfg_enabled) \56".if "__stringify(cfg_enabled)" == 1\n" \57"661:\n\t" \58oldinstr "\n" \59"662:\n" \60".pushsection .altinstructions,\"a\"\n" \61ALTINSTR_ENTRY(cpucap) \62".popsection\n" \63".subsection 1\n" \64"663:\n\t" \65newinstr "\n" \66"664:\n\t" \67".org . - (664b-663b) + (662b-661b)\n\t" \68".org . - (662b-661b) + (664b-663b)\n\t" \69".previous\n" \70".endif\n"7172#define __ALTERNATIVE_CFG_CB(oldinstr, cpucap, cfg_enabled, cb) \73".if "__stringify(cfg_enabled)" == 1\n" \74"661:\n\t" \75oldinstr "\n" \76"662:\n" \77".pushsection .altinstructions,\"a\"\n" \78ALTINSTR_ENTRY_CB(cpucap, cb) \79".popsection\n" \80"663:\n\t" \81"664:\n\t" \82".endif\n"8384#define _ALTERNATIVE_CFG(oldinstr, newinstr, cpucap, cfg, ...) \85__ALTERNATIVE_CFG(oldinstr, newinstr, cpucap, IS_ENABLED(cfg))8687#define ALTERNATIVE_CB(oldinstr, cpucap, cb) \88__ALTERNATIVE_CFG_CB(oldinstr, (1 << ARM64_CB_SHIFT) | (cpucap), 1, cb)89#else9091#include <asm/assembler.h>9293.macro altinstruction_entry orig_offset alt_offset cpucap orig_len alt_len94.word \orig_offset - .95.word \alt_offset - .96.hword (\cpucap)97.byte \orig_len98.byte \alt_len99.endm100101.macro alternative_insn insn1, insn2, cap, enable = 1102.if \enable103661: \insn1104662: .pushsection .altinstructions, "a"105altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f106.popsection107.subsection 1108663: \insn2109664: .org . - (664b-663b) + (662b-661b)110.org . - (662b-661b) + (664b-663b)111.previous112.endif113.endm114115/*116* Alternative sequences117*118* The code for the case where the capability is not present will be119* assembled and linked as normal. There are no restrictions on this120* code.121*122* The code for the case where the capability is present will be123* assembled into a special section to be used for dynamic patching.124* Code for that case must:125*126* 1. Be exactly the same length (in bytes) as the default code127* sequence.128*129* 2. Not contain a branch target that is used outside of the130* alternative sequence it is defined in (branches into an131* alternative sequence are not fixed up).132*/133134/*135* Begin an alternative code sequence.136*/137.macro alternative_if_not cap138.set .Lasm_alt_mode, 0139.pushsection .altinstructions, "a"140altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f141.popsection142661:143.endm144145.macro alternative_if cap146.set .Lasm_alt_mode, 1147.pushsection .altinstructions, "a"148altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f149.popsection150.subsection 1151.align 2 /* So GAS knows label 661 is suitably aligned */152661:153.endm154155.macro alternative_cb cap, cb156.set .Lasm_alt_mode, 0157.pushsection .altinstructions, "a"158altinstruction_entry 661f, \cb, (1 << ARM64_CB_SHIFT) | \cap, 662f-661f, 0159.popsection160661:161.endm162163/*164* Provide the other half of the alternative code sequence.165*/166.macro alternative_else167662:168.if .Lasm_alt_mode==0169.subsection 1170.else171.previous172.endif173663:174.endm175176/*177* Complete an alternative code sequence.178*/179.macro alternative_endif180664:181.org . - (664b-663b) + (662b-661b)182.org . - (662b-661b) + (664b-663b)183.if .Lasm_alt_mode==0184.previous185.endif186.endm187188/*189* Callback-based alternative epilogue190*/191.macro alternative_cb_end192662:193.endm194195/*196* Provides a trivial alternative or default sequence consisting solely197* of NOPs. The number of NOPs is chosen automatically to match the198* previous case.199*/200.macro alternative_else_nop_endif201alternative_else202nops (662b-661b) / AARCH64_INSN_SIZE203alternative_endif204.endm205206#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \207alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)208209#endif /* __ASSEMBLY__ */210211/*212* Usage: asm(ALTERNATIVE(oldinstr, newinstr, cpucap));213*214* Usage: asm(ALTERNATIVE(oldinstr, newinstr, cpucap, CONFIG_FOO));215* N.B. If CONFIG_FOO is specified, but not selected, the whole block216* will be omitted, including oldinstr.217*/218#define ALTERNATIVE(oldinstr, newinstr, ...) \219_ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1)220221#ifndef __ASSEMBLY__222223#include <linux/types.h>224225static __always_inline bool226alternative_has_cap_likely(const unsigned long cpucap)227{228if (!cpucap_is_possible(cpucap))229return false;230231asm goto(232#ifdef BUILD_VDSO233ALTERNATIVE("b %l[l_no]", "nop", %[cpucap])234#else235ALTERNATIVE_CB("b %l[l_no]", %[cpucap], alt_cb_patch_nops)236#endif237:238: [cpucap] "i" (cpucap)239:240: l_no);241242return true;243l_no:244return false;245}246247static __always_inline bool248alternative_has_cap_unlikely(const unsigned long cpucap)249{250if (!cpucap_is_possible(cpucap))251return false;252253asm goto(254ALTERNATIVE("nop", "b %l[l_yes]", %[cpucap])255:256: [cpucap] "i" (cpucap)257:258: l_yes);259260return false;261l_yes:262return true;263}264265#endif /* __ASSEMBLY__ */266267#endif /* __ASM_ALTERNATIVE_MACROS_H */268269270