162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef _ASM_ALTERNATIVE_H 362306a36Sopenharmony_ci#define _ASM_ALTERNATIVE_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/types.h> 862306a36Sopenharmony_ci#include <linux/stddef.h> 962306a36Sopenharmony_ci#include <linux/stringify.h> 1062306a36Sopenharmony_ci#include <asm/asm.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistruct alt_instr { 1362306a36Sopenharmony_ci s32 instr_offset; /* offset to original instruction */ 1462306a36Sopenharmony_ci s32 replace_offset; /* offset to replacement instruction */ 1562306a36Sopenharmony_ci u16 feature; /* feature bit set for replacement */ 1662306a36Sopenharmony_ci u8 instrlen; /* length of original instruction */ 1762306a36Sopenharmony_ci u8 replacementlen; /* length of new instruction */ 1862306a36Sopenharmony_ci} __packed; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Debug flag that can be tested to see whether alternative 2262306a36Sopenharmony_ci * instructions were patched in already: 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ciextern int alternatives_patched; 2562306a36Sopenharmony_ciextern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciextern void alternative_instructions(void); 2862306a36Sopenharmony_ciextern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define b_replacement(num) "664"#num 3162306a36Sopenharmony_ci#define e_replacement(num) "665"#num 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define alt_end_marker "663" 3462306a36Sopenharmony_ci#define alt_slen "662b-661b" 3562306a36Sopenharmony_ci#define alt_total_slen alt_end_marker"b-661b" 3662306a36Sopenharmony_ci#define alt_rlen(num) e_replacement(num)"f-"b_replacement(num)"f" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define __OLDINSTR(oldinstr, num) \ 3962306a36Sopenharmony_ci "661:\n\t" oldinstr "\n662:\n" \ 4062306a36Sopenharmony_ci ".fill -(((" alt_rlen(num) ")-(" alt_slen ")) > 0) * " \ 4162306a36Sopenharmony_ci "((" alt_rlen(num) ")-(" alt_slen ")) / 4, 4, 0x03400000\n" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define OLDINSTR(oldinstr, num) \ 4462306a36Sopenharmony_ci __OLDINSTR(oldinstr, num) \ 4562306a36Sopenharmony_ci alt_end_marker ":\n" 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * Pad the second replacement alternative with additional NOPs if it is 5162306a36Sopenharmony_ci * additionally longer than the first replacement alternative. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci#define OLDINSTR_2(oldinstr, num1, num2) \ 5462306a36Sopenharmony_ci "661:\n\t" oldinstr "\n662:\n" \ 5562306a36Sopenharmony_ci ".fill -((" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) > 0) * " \ 5662306a36Sopenharmony_ci "(" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) / 4, " \ 5762306a36Sopenharmony_ci "4, 0x03400000\n" \ 5862306a36Sopenharmony_ci alt_end_marker ":\n" 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define ALTINSTR_ENTRY(feature, num) \ 6162306a36Sopenharmony_ci " .long 661b - .\n" /* label */ \ 6262306a36Sopenharmony_ci " .long " b_replacement(num)"f - .\n" /* new instruction */ \ 6362306a36Sopenharmony_ci " .short " __stringify(feature) "\n" /* feature bit */ \ 6462306a36Sopenharmony_ci " .byte " alt_total_slen "\n" /* source len */ \ 6562306a36Sopenharmony_ci " .byte " alt_rlen(num) "\n" /* replacement len */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define ALTINSTR_REPLACEMENT(newinstr, feature, num) /* replacement */ \ 6862306a36Sopenharmony_ci b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n\t" 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* alternative assembly primitive: */ 7162306a36Sopenharmony_ci#define ALTERNATIVE(oldinstr, newinstr, feature) \ 7262306a36Sopenharmony_ci OLDINSTR(oldinstr, 1) \ 7362306a36Sopenharmony_ci ".pushsection .altinstructions,\"a\"\n" \ 7462306a36Sopenharmony_ci ALTINSTR_ENTRY(feature, 1) \ 7562306a36Sopenharmony_ci ".popsection\n" \ 7662306a36Sopenharmony_ci ".subsection 1\n" \ 7762306a36Sopenharmony_ci ALTINSTR_REPLACEMENT(newinstr, feature, 1) \ 7862306a36Sopenharmony_ci ".previous\n" 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\ 8162306a36Sopenharmony_ci OLDINSTR_2(oldinstr, 1, 2) \ 8262306a36Sopenharmony_ci ".pushsection .altinstructions,\"a\"\n" \ 8362306a36Sopenharmony_ci ALTINSTR_ENTRY(feature1, 1) \ 8462306a36Sopenharmony_ci ALTINSTR_ENTRY(feature2, 2) \ 8562306a36Sopenharmony_ci ".popsection\n" \ 8662306a36Sopenharmony_ci ".subsection 1\n" \ 8762306a36Sopenharmony_ci ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \ 8862306a36Sopenharmony_ci ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \ 8962306a36Sopenharmony_ci ".previous\n" 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * Alternative instructions for different CPU types or capabilities. 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * This allows to use optimized instructions even on generic binary 9562306a36Sopenharmony_ci * kernels. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * length of oldinstr must be longer or equal the length of newinstr 9862306a36Sopenharmony_ci * It can be padded with nops as needed. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * For non barrier like inlines please define new variants 10162306a36Sopenharmony_ci * without volatile and memory clobber. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci#define alternative(oldinstr, newinstr, feature) \ 10462306a36Sopenharmony_ci (asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory")) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ 10762306a36Sopenharmony_ci (asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory")) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#endif /* _ASM_ALTERNATIVE_H */ 112