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