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