18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef __ASM_ALTERNATIVE_MACROS_H 38c2ecf20Sopenharmony_ci#define __ASM_ALTERNATIVE_MACROS_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <asm/cpucaps.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define ARM64_CB_PATCH ARM64_NCAPS 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* A64 instructions are always 32 bits. */ 108c2ecf20Sopenharmony_ci#define AARCH64_INSN_SIZE 4 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/stringify.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define ALTINSTR_ENTRY(feature) \ 178c2ecf20Sopenharmony_ci " .word 661b - .\n" /* label */ \ 188c2ecf20Sopenharmony_ci " .word 663f - .\n" /* new instruction */ \ 198c2ecf20Sopenharmony_ci " .hword " __stringify(feature) "\n" /* feature bit */ \ 208c2ecf20Sopenharmony_ci " .byte 662b-661b\n" /* source len */ \ 218c2ecf20Sopenharmony_ci " .byte 664f-663f\n" /* replacement len */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define ALTINSTR_ENTRY_CB(feature, cb) \ 248c2ecf20Sopenharmony_ci " .word 661b - .\n" /* label */ \ 258c2ecf20Sopenharmony_ci " .word " __stringify(cb) "- .\n" /* callback */ \ 268c2ecf20Sopenharmony_ci " .hword " __stringify(feature) "\n" /* feature bit */ \ 278c2ecf20Sopenharmony_ci " .byte 662b-661b\n" /* source len */ \ 288c2ecf20Sopenharmony_ci " .byte 664f-663f\n" /* replacement len */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * alternative assembly primitive: 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * If any of these .org directive fail, it means that insn1 and insn2 348c2ecf20Sopenharmony_ci * don't have the same length. This used to be written as 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * .if ((664b-663b) != (662b-661b)) 378c2ecf20Sopenharmony_ci * .error "Alternatives instruction length mismatch" 388c2ecf20Sopenharmony_ci * .endif 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * but most assemblers die if insn1 or insn2 have a .inst. This should 418c2ecf20Sopenharmony_ci * be fixed in a binutils release posterior to 2.25.51.0.2 (anything 428c2ecf20Sopenharmony_ci * containing commit 4e4d08cf7399b606 or c1baaddf8861). 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Alternatives with callbacks do not generate replacement instructions. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci#define __ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg_enabled) \ 478c2ecf20Sopenharmony_ci ".if "__stringify(cfg_enabled)" == 1\n" \ 488c2ecf20Sopenharmony_ci "661:\n\t" \ 498c2ecf20Sopenharmony_ci oldinstr "\n" \ 508c2ecf20Sopenharmony_ci "662:\n" \ 518c2ecf20Sopenharmony_ci ".pushsection .altinstructions,\"a\"\n" \ 528c2ecf20Sopenharmony_ci ALTINSTR_ENTRY(feature) \ 538c2ecf20Sopenharmony_ci ".popsection\n" \ 548c2ecf20Sopenharmony_ci ".subsection 1\n" \ 558c2ecf20Sopenharmony_ci "663:\n\t" \ 568c2ecf20Sopenharmony_ci newinstr "\n" \ 578c2ecf20Sopenharmony_ci "664:\n\t" \ 588c2ecf20Sopenharmony_ci ".org . - (664b-663b) + (662b-661b)\n\t" \ 598c2ecf20Sopenharmony_ci ".org . - (662b-661b) + (664b-663b)\n\t" \ 608c2ecf20Sopenharmony_ci ".previous\n" \ 618c2ecf20Sopenharmony_ci ".endif\n" 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define __ALTERNATIVE_CFG_CB(oldinstr, feature, cfg_enabled, cb) \ 648c2ecf20Sopenharmony_ci ".if "__stringify(cfg_enabled)" == 1\n" \ 658c2ecf20Sopenharmony_ci "661:\n\t" \ 668c2ecf20Sopenharmony_ci oldinstr "\n" \ 678c2ecf20Sopenharmony_ci "662:\n" \ 688c2ecf20Sopenharmony_ci ".pushsection .altinstructions,\"a\"\n" \ 698c2ecf20Sopenharmony_ci ALTINSTR_ENTRY_CB(feature, cb) \ 708c2ecf20Sopenharmony_ci ".popsection\n" \ 718c2ecf20Sopenharmony_ci "663:\n\t" \ 728c2ecf20Sopenharmony_ci "664:\n\t" \ 738c2ecf20Sopenharmony_ci ".endif\n" 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define _ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg, ...) \ 768c2ecf20Sopenharmony_ci __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg)) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define ALTERNATIVE_CB(oldinstr, cb) \ 798c2ecf20Sopenharmony_ci __ALTERNATIVE_CFG_CB(oldinstr, ARM64_CB_PATCH, 1, cb) 808c2ecf20Sopenharmony_ci#else 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#include <asm/assembler.h> 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len 858c2ecf20Sopenharmony_ci .word \orig_offset - . 868c2ecf20Sopenharmony_ci .word \alt_offset - . 878c2ecf20Sopenharmony_ci .hword \feature 888c2ecf20Sopenharmony_ci .byte \orig_len 898c2ecf20Sopenharmony_ci .byte \alt_len 908c2ecf20Sopenharmony_ci.endm 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci.macro alternative_insn insn1, insn2, cap, enable = 1 938c2ecf20Sopenharmony_ci .if \enable 948c2ecf20Sopenharmony_ci661: \insn1 958c2ecf20Sopenharmony_ci662: .pushsection .altinstructions, "a" 968c2ecf20Sopenharmony_ci altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f 978c2ecf20Sopenharmony_ci .popsection 988c2ecf20Sopenharmony_ci .subsection 1 998c2ecf20Sopenharmony_ci663: \insn2 1008c2ecf20Sopenharmony_ci664: .org . - (664b-663b) + (662b-661b) 1018c2ecf20Sopenharmony_ci .org . - (662b-661b) + (664b-663b) 1028c2ecf20Sopenharmony_ci .previous 1038c2ecf20Sopenharmony_ci .endif 1048c2ecf20Sopenharmony_ci.endm 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Alternative sequences 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * The code for the case where the capability is not present will be 1108c2ecf20Sopenharmony_ci * assembled and linked as normal. There are no restrictions on this 1118c2ecf20Sopenharmony_ci * code. 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * The code for the case where the capability is present will be 1148c2ecf20Sopenharmony_ci * assembled into a special section to be used for dynamic patching. 1158c2ecf20Sopenharmony_ci * Code for that case must: 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * 1. Be exactly the same length (in bytes) as the default code 1188c2ecf20Sopenharmony_ci * sequence. 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * 2. Not contain a branch target that is used outside of the 1218c2ecf20Sopenharmony_ci * alternative sequence it is defined in (branches into an 1228c2ecf20Sopenharmony_ci * alternative sequence are not fixed up). 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * Begin an alternative code sequence. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci.macro alternative_if_not cap 1298c2ecf20Sopenharmony_ci .set .Lasm_alt_mode, 0 1308c2ecf20Sopenharmony_ci .pushsection .altinstructions, "a" 1318c2ecf20Sopenharmony_ci altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f 1328c2ecf20Sopenharmony_ci .popsection 1338c2ecf20Sopenharmony_ci661: 1348c2ecf20Sopenharmony_ci.endm 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci.macro alternative_if cap 1378c2ecf20Sopenharmony_ci .set .Lasm_alt_mode, 1 1388c2ecf20Sopenharmony_ci .pushsection .altinstructions, "a" 1398c2ecf20Sopenharmony_ci altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f 1408c2ecf20Sopenharmony_ci .popsection 1418c2ecf20Sopenharmony_ci .subsection 1 1428c2ecf20Sopenharmony_ci .align 2 /* So GAS knows label 661 is suitably aligned */ 1438c2ecf20Sopenharmony_ci661: 1448c2ecf20Sopenharmony_ci.endm 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci.macro alternative_cb cb 1478c2ecf20Sopenharmony_ci .set .Lasm_alt_mode, 0 1488c2ecf20Sopenharmony_ci .pushsection .altinstructions, "a" 1498c2ecf20Sopenharmony_ci altinstruction_entry 661f, \cb, ARM64_CB_PATCH, 662f-661f, 0 1508c2ecf20Sopenharmony_ci .popsection 1518c2ecf20Sopenharmony_ci661: 1528c2ecf20Sopenharmony_ci.endm 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * Provide the other half of the alternative code sequence. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci.macro alternative_else 1588c2ecf20Sopenharmony_ci662: 1598c2ecf20Sopenharmony_ci .if .Lasm_alt_mode==0 1608c2ecf20Sopenharmony_ci .subsection 1 1618c2ecf20Sopenharmony_ci .else 1628c2ecf20Sopenharmony_ci .previous 1638c2ecf20Sopenharmony_ci .endif 1648c2ecf20Sopenharmony_ci663: 1658c2ecf20Sopenharmony_ci.endm 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * Complete an alternative code sequence. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci.macro alternative_endif 1718c2ecf20Sopenharmony_ci664: 1728c2ecf20Sopenharmony_ci .org . - (664b-663b) + (662b-661b) 1738c2ecf20Sopenharmony_ci .org . - (662b-661b) + (664b-663b) 1748c2ecf20Sopenharmony_ci .if .Lasm_alt_mode==0 1758c2ecf20Sopenharmony_ci .previous 1768c2ecf20Sopenharmony_ci .endif 1778c2ecf20Sopenharmony_ci.endm 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Callback-based alternative epilogue 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci.macro alternative_cb_end 1838c2ecf20Sopenharmony_ci662: 1848c2ecf20Sopenharmony_ci.endm 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * Provides a trivial alternative or default sequence consisting solely 1888c2ecf20Sopenharmony_ci * of NOPs. The number of NOPs is chosen automatically to match the 1898c2ecf20Sopenharmony_ci * previous case. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci.macro alternative_else_nop_endif 1928c2ecf20Sopenharmony_cialternative_else 1938c2ecf20Sopenharmony_ci nops (662b-661b) / AARCH64_INSN_SIZE 1948c2ecf20Sopenharmony_cialternative_endif 1958c2ecf20Sopenharmony_ci.endm 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \ 1988c2ecf20Sopenharmony_ci alternative_insn insn1, insn2, cap, IS_ENABLED(cfg) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci.macro user_alt, label, oldinstr, newinstr, cond 2018c2ecf20Sopenharmony_ci9999: alternative_insn "\oldinstr", "\newinstr", \cond 2028c2ecf20Sopenharmony_ci _asm_extable 9999b, \label 2038c2ecf20Sopenharmony_ci.endm 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* 2068c2ecf20Sopenharmony_ci * Generate the assembly for UAO alternatives with exception table entries. 2078c2ecf20Sopenharmony_ci * This is complicated as there is no post-increment or pair versions of the 2088c2ecf20Sopenharmony_ci * unprivileged instructions, and USER() only works for single instructions. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_UAO 2118c2ecf20Sopenharmony_ci .macro uao_ldp l, reg1, reg2, addr, post_inc 2128c2ecf20Sopenharmony_ci alternative_if_not ARM64_HAS_UAO 2138c2ecf20Sopenharmony_ci8888: ldp \reg1, \reg2, [\addr], \post_inc; 2148c2ecf20Sopenharmony_ci8889: nop; 2158c2ecf20Sopenharmony_ci nop; 2168c2ecf20Sopenharmony_ci alternative_else 2178c2ecf20Sopenharmony_ci ldtr \reg1, [\addr]; 2188c2ecf20Sopenharmony_ci ldtr \reg2, [\addr, #8]; 2198c2ecf20Sopenharmony_ci add \addr, \addr, \post_inc; 2208c2ecf20Sopenharmony_ci alternative_endif 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci _asm_extable 8888b,\l; 2238c2ecf20Sopenharmony_ci _asm_extable 8889b,\l; 2248c2ecf20Sopenharmony_ci .endm 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci .macro uao_stp l, reg1, reg2, addr, post_inc 2278c2ecf20Sopenharmony_ci alternative_if_not ARM64_HAS_UAO 2288c2ecf20Sopenharmony_ci8888: stp \reg1, \reg2, [\addr], \post_inc; 2298c2ecf20Sopenharmony_ci8889: nop; 2308c2ecf20Sopenharmony_ci nop; 2318c2ecf20Sopenharmony_ci alternative_else 2328c2ecf20Sopenharmony_ci sttr \reg1, [\addr]; 2338c2ecf20Sopenharmony_ci sttr \reg2, [\addr, #8]; 2348c2ecf20Sopenharmony_ci add \addr, \addr, \post_inc; 2358c2ecf20Sopenharmony_ci alternative_endif 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci _asm_extable 8888b,\l; 2388c2ecf20Sopenharmony_ci _asm_extable 8889b,\l; 2398c2ecf20Sopenharmony_ci .endm 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc 2428c2ecf20Sopenharmony_ci alternative_if_not ARM64_HAS_UAO 2438c2ecf20Sopenharmony_ci8888: \inst \reg, [\addr], \post_inc; 2448c2ecf20Sopenharmony_ci nop; 2458c2ecf20Sopenharmony_ci alternative_else 2468c2ecf20Sopenharmony_ci \alt_inst \reg, [\addr]; 2478c2ecf20Sopenharmony_ci add \addr, \addr, \post_inc; 2488c2ecf20Sopenharmony_ci alternative_endif 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci _asm_extable 8888b,\l; 2518c2ecf20Sopenharmony_ci .endm 2528c2ecf20Sopenharmony_ci#else 2538c2ecf20Sopenharmony_ci .macro uao_ldp l, reg1, reg2, addr, post_inc 2548c2ecf20Sopenharmony_ci USER(\l, ldp \reg1, \reg2, [\addr], \post_inc) 2558c2ecf20Sopenharmony_ci .endm 2568c2ecf20Sopenharmony_ci .macro uao_stp l, reg1, reg2, addr, post_inc 2578c2ecf20Sopenharmony_ci USER(\l, stp \reg1, \reg2, [\addr], \post_inc) 2588c2ecf20Sopenharmony_ci .endm 2598c2ecf20Sopenharmony_ci .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc 2608c2ecf20Sopenharmony_ci USER(\l, \inst \reg, [\addr], \post_inc) 2618c2ecf20Sopenharmony_ci .endm 2628c2ecf20Sopenharmony_ci#endif 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci#endif /* __ASSEMBLY__ */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature)); 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * Usage: asm(ALTERNATIVE(oldinstr, newinstr, feature, CONFIG_FOO)); 2708c2ecf20Sopenharmony_ci * N.B. If CONFIG_FOO is specified, but not selected, the whole block 2718c2ecf20Sopenharmony_ci * will be omitted, including oldinstr. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci#define ALTERNATIVE(oldinstr, newinstr, ...) \ 2748c2ecf20Sopenharmony_ci _ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1) 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#endif /* __ASM_ALTERNATIVE_MACROS_H */ 277