162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef __ASM_ALTERNATIVE_MACROS_H 362306a36Sopenharmony_ci#define __ASM_ALTERNATIVE_MACROS_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/const.h> 662306a36Sopenharmony_ci#include <vdso/bits.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/cpucaps.h> 962306a36Sopenharmony_ci#include <asm/insn-def.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Binutils 2.27.0 can't handle a 'UL' suffix on constants, so for the assembly 1362306a36Sopenharmony_ci * macros below we must use we must use `(1 << ARM64_CB_SHIFT)`. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci#define ARM64_CB_SHIFT 15 1662306a36Sopenharmony_ci#define ARM64_CB_BIT BIT(ARM64_CB_SHIFT) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#if ARM64_NCAPS >= ARM64_CB_BIT 1962306a36Sopenharmony_ci#error "cpucaps have overflown ARM64_CB_BIT" 2062306a36Sopenharmony_ci#endif 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/stringify.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define ALTINSTR_ENTRY(cpucap) \ 2762306a36Sopenharmony_ci " .word 661b - .\n" /* label */ \ 2862306a36Sopenharmony_ci " .word 663f - .\n" /* new instruction */ \ 2962306a36Sopenharmony_ci " .hword " __stringify(cpucap) "\n" /* cpucap */ \ 3062306a36Sopenharmony_ci " .byte 662b-661b\n" /* source len */ \ 3162306a36Sopenharmony_ci " .byte 664f-663f\n" /* replacement len */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define ALTINSTR_ENTRY_CB(cpucap, cb) \ 3462306a36Sopenharmony_ci " .word 661b - .\n" /* label */ \ 3562306a36Sopenharmony_ci " .word " __stringify(cb) "- .\n" /* callback */ \ 3662306a36Sopenharmony_ci " .hword " __stringify(cpucap) "\n" /* cpucap */ \ 3762306a36Sopenharmony_ci " .byte 662b-661b\n" /* source len */ \ 3862306a36Sopenharmony_ci " .byte 664f-663f\n" /* replacement len */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * alternative assembly primitive: 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * If any of these .org directive fail, it means that insn1 and insn2 4462306a36Sopenharmony_ci * don't have the same length. This used to be written as 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * .if ((664b-663b) != (662b-661b)) 4762306a36Sopenharmony_ci * .error "Alternatives instruction length mismatch" 4862306a36Sopenharmony_ci * .endif 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * but most assemblers die if insn1 or insn2 have a .inst. This should 5162306a36Sopenharmony_ci * be fixed in a binutils release posterior to 2.25.51.0.2 (anything 5262306a36Sopenharmony_ci * containing commit 4e4d08cf7399b606 or c1baaddf8861). 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * Alternatives with callbacks do not generate replacement instructions. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci#define __ALTERNATIVE_CFG(oldinstr, newinstr, cpucap, cfg_enabled) \ 5762306a36Sopenharmony_ci ".if "__stringify(cfg_enabled)" == 1\n" \ 5862306a36Sopenharmony_ci "661:\n\t" \ 5962306a36Sopenharmony_ci oldinstr "\n" \ 6062306a36Sopenharmony_ci "662:\n" \ 6162306a36Sopenharmony_ci ".pushsection .altinstructions,\"a\"\n" \ 6262306a36Sopenharmony_ci ALTINSTR_ENTRY(cpucap) \ 6362306a36Sopenharmony_ci ".popsection\n" \ 6462306a36Sopenharmony_ci ".subsection 1\n" \ 6562306a36Sopenharmony_ci "663:\n\t" \ 6662306a36Sopenharmony_ci newinstr "\n" \ 6762306a36Sopenharmony_ci "664:\n\t" \ 6862306a36Sopenharmony_ci ".org . - (664b-663b) + (662b-661b)\n\t" \ 6962306a36Sopenharmony_ci ".org . - (662b-661b) + (664b-663b)\n\t" \ 7062306a36Sopenharmony_ci ".previous\n" \ 7162306a36Sopenharmony_ci ".endif\n" 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define __ALTERNATIVE_CFG_CB(oldinstr, cpucap, cfg_enabled, cb) \ 7462306a36Sopenharmony_ci ".if "__stringify(cfg_enabled)" == 1\n" \ 7562306a36Sopenharmony_ci "661:\n\t" \ 7662306a36Sopenharmony_ci oldinstr "\n" \ 7762306a36Sopenharmony_ci "662:\n" \ 7862306a36Sopenharmony_ci ".pushsection .altinstructions,\"a\"\n" \ 7962306a36Sopenharmony_ci ALTINSTR_ENTRY_CB(cpucap, cb) \ 8062306a36Sopenharmony_ci ".popsection\n" \ 8162306a36Sopenharmony_ci "663:\n\t" \ 8262306a36Sopenharmony_ci "664:\n\t" \ 8362306a36Sopenharmony_ci ".endif\n" 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define _ALTERNATIVE_CFG(oldinstr, newinstr, cpucap, cfg, ...) \ 8662306a36Sopenharmony_ci __ALTERNATIVE_CFG(oldinstr, newinstr, cpucap, IS_ENABLED(cfg)) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define ALTERNATIVE_CB(oldinstr, cpucap, cb) \ 8962306a36Sopenharmony_ci __ALTERNATIVE_CFG_CB(oldinstr, (1 << ARM64_CB_SHIFT) | (cpucap), 1, cb) 9062306a36Sopenharmony_ci#else 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#include <asm/assembler.h> 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci.macro altinstruction_entry orig_offset alt_offset cpucap orig_len alt_len 9562306a36Sopenharmony_ci .word \orig_offset - . 9662306a36Sopenharmony_ci .word \alt_offset - . 9762306a36Sopenharmony_ci .hword (\cpucap) 9862306a36Sopenharmony_ci .byte \orig_len 9962306a36Sopenharmony_ci .byte \alt_len 10062306a36Sopenharmony_ci.endm 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci.macro alternative_insn insn1, insn2, cap, enable = 1 10362306a36Sopenharmony_ci .if \enable 10462306a36Sopenharmony_ci661: \insn1 10562306a36Sopenharmony_ci662: .pushsection .altinstructions, "a" 10662306a36Sopenharmony_ci altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f 10762306a36Sopenharmony_ci .popsection 10862306a36Sopenharmony_ci .subsection 1 10962306a36Sopenharmony_ci663: \insn2 11062306a36Sopenharmony_ci664: .org . - (664b-663b) + (662b-661b) 11162306a36Sopenharmony_ci .org . - (662b-661b) + (664b-663b) 11262306a36Sopenharmony_ci .previous 11362306a36Sopenharmony_ci .endif 11462306a36Sopenharmony_ci.endm 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * Alternative sequences 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * The code for the case where the capability is not present will be 12062306a36Sopenharmony_ci * assembled and linked as normal. There are no restrictions on this 12162306a36Sopenharmony_ci * code. 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * The code for the case where the capability is present will be 12462306a36Sopenharmony_ci * assembled into a special section to be used for dynamic patching. 12562306a36Sopenharmony_ci * Code for that case must: 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * 1. Be exactly the same length (in bytes) as the default code 12862306a36Sopenharmony_ci * sequence. 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * 2. Not contain a branch target that is used outside of the 13162306a36Sopenharmony_ci * alternative sequence it is defined in (branches into an 13262306a36Sopenharmony_ci * alternative sequence are not fixed up). 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Begin an alternative code sequence. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci.macro alternative_if_not cap 13962306a36Sopenharmony_ci .set .Lasm_alt_mode, 0 14062306a36Sopenharmony_ci .pushsection .altinstructions, "a" 14162306a36Sopenharmony_ci altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f 14262306a36Sopenharmony_ci .popsection 14362306a36Sopenharmony_ci661: 14462306a36Sopenharmony_ci.endm 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci.macro alternative_if cap 14762306a36Sopenharmony_ci .set .Lasm_alt_mode, 1 14862306a36Sopenharmony_ci .pushsection .altinstructions, "a" 14962306a36Sopenharmony_ci altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f 15062306a36Sopenharmony_ci .popsection 15162306a36Sopenharmony_ci .subsection 1 15262306a36Sopenharmony_ci .align 2 /* So GAS knows label 661 is suitably aligned */ 15362306a36Sopenharmony_ci661: 15462306a36Sopenharmony_ci.endm 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci.macro alternative_cb cap, cb 15762306a36Sopenharmony_ci .set .Lasm_alt_mode, 0 15862306a36Sopenharmony_ci .pushsection .altinstructions, "a" 15962306a36Sopenharmony_ci altinstruction_entry 661f, \cb, (1 << ARM64_CB_SHIFT) | \cap, 662f-661f, 0 16062306a36Sopenharmony_ci .popsection 16162306a36Sopenharmony_ci661: 16262306a36Sopenharmony_ci.endm 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * Provide the other half of the alternative code sequence. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci.macro alternative_else 16862306a36Sopenharmony_ci662: 16962306a36Sopenharmony_ci .if .Lasm_alt_mode==0 17062306a36Sopenharmony_ci .subsection 1 17162306a36Sopenharmony_ci .else 17262306a36Sopenharmony_ci .previous 17362306a36Sopenharmony_ci .endif 17462306a36Sopenharmony_ci663: 17562306a36Sopenharmony_ci.endm 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * Complete an alternative code sequence. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci.macro alternative_endif 18162306a36Sopenharmony_ci664: 18262306a36Sopenharmony_ci .org . - (664b-663b) + (662b-661b) 18362306a36Sopenharmony_ci .org . - (662b-661b) + (664b-663b) 18462306a36Sopenharmony_ci .if .Lasm_alt_mode==0 18562306a36Sopenharmony_ci .previous 18662306a36Sopenharmony_ci .endif 18762306a36Sopenharmony_ci.endm 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * Callback-based alternative epilogue 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci.macro alternative_cb_end 19362306a36Sopenharmony_ci662: 19462306a36Sopenharmony_ci.endm 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * Provides a trivial alternative or default sequence consisting solely 19862306a36Sopenharmony_ci * of NOPs. The number of NOPs is chosen automatically to match the 19962306a36Sopenharmony_ci * previous case. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci.macro alternative_else_nop_endif 20262306a36Sopenharmony_cialternative_else 20362306a36Sopenharmony_ci nops (662b-661b) / AARCH64_INSN_SIZE 20462306a36Sopenharmony_cialternative_endif 20562306a36Sopenharmony_ci.endm 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \ 20862306a36Sopenharmony_ci alternative_insn insn1, insn2, cap, IS_ENABLED(cfg) 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* 21362306a36Sopenharmony_ci * Usage: asm(ALTERNATIVE(oldinstr, newinstr, cpucap)); 21462306a36Sopenharmony_ci * 21562306a36Sopenharmony_ci * Usage: asm(ALTERNATIVE(oldinstr, newinstr, cpucap, CONFIG_FOO)); 21662306a36Sopenharmony_ci * N.B. If CONFIG_FOO is specified, but not selected, the whole block 21762306a36Sopenharmony_ci * will be omitted, including oldinstr. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci#define ALTERNATIVE(oldinstr, newinstr, ...) \ 22062306a36Sopenharmony_ci _ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1) 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci#include <linux/types.h> 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic __always_inline bool 22762306a36Sopenharmony_cialternative_has_cap_likely(const unsigned long cpucap) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci compiletime_assert(cpucap < ARM64_NCAPS, 23062306a36Sopenharmony_ci "cpucap must be < ARM64_NCAPS"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci asm goto( 23362306a36Sopenharmony_ci ALTERNATIVE_CB("b %l[l_no]", %[cpucap], alt_cb_patch_nops) 23462306a36Sopenharmony_ci : 23562306a36Sopenharmony_ci : [cpucap] "i" (cpucap) 23662306a36Sopenharmony_ci : 23762306a36Sopenharmony_ci : l_no); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return true; 24062306a36Sopenharmony_cil_no: 24162306a36Sopenharmony_ci return false; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic __always_inline bool 24562306a36Sopenharmony_cialternative_has_cap_unlikely(const unsigned long cpucap) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci compiletime_assert(cpucap < ARM64_NCAPS, 24862306a36Sopenharmony_ci "cpucap must be < ARM64_NCAPS"); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci asm goto( 25162306a36Sopenharmony_ci ALTERNATIVE("nop", "b %l[l_yes]", %[cpucap]) 25262306a36Sopenharmony_ci : 25362306a36Sopenharmony_ci : [cpucap] "i" (cpucap) 25462306a36Sopenharmony_ci : 25562306a36Sopenharmony_ci : l_yes); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return false; 25862306a36Sopenharmony_cil_yes: 25962306a36Sopenharmony_ci return true; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci#endif /* __ASM_ALTERNATIVE_MACROS_H */ 265