-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LoongArch: Add alternative runtime patching mechanism
Introduce the "alternative" mechanism from ARM64 and x86 for LoongArch to apply runtime patching. The main purpose of this patch is to provide a framework. In future we can use this mechanism (i.e., the ALTERNATIVE and ALTERNATIVE_2 macros) to optimize hotspot functions according to cpu features. Signed-off-by: Jun Yi <yijun@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
- Loading branch information
Huacai Chen
committed
Dec 14, 2022
1 parent
61a6fcc
commit 19e5eb1
Showing
9 changed files
with
507 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
#ifndef _ASM_ALTERNATIVE_ASM_H | ||
#define _ASM_ALTERNATIVE_ASM_H | ||
|
||
#ifdef __ASSEMBLY__ | ||
|
||
#include <asm/asm.h> | ||
|
||
/* | ||
* Issue one struct alt_instr descriptor entry (need to put it into | ||
* the section .altinstructions, see below). This entry contains | ||
* enough information for the alternatives patching code to patch an | ||
* instruction. See apply_alternatives(). | ||
*/ | ||
.macro altinstruction_entry orig alt feature orig_len alt_len | ||
.long \orig - . | ||
.long \alt - . | ||
.short \feature | ||
.byte \orig_len | ||
.byte \alt_len | ||
.endm | ||
|
||
/* | ||
* Define an alternative between two instructions. If @feature is | ||
* present, early code in apply_alternatives() replaces @oldinstr with | ||
* @newinstr. ".fill" directive takes care of proper instruction padding | ||
* in case @newinstr is longer than @oldinstr. | ||
*/ | ||
.macro ALTERNATIVE oldinstr, newinstr, feature | ||
140 : | ||
\oldinstr | ||
141 : | ||
.fill - (((144f-143f)-(141b-140b)) > 0) * ((144f-143f)-(141b-140b)) / 4, 4, 0x03400000 | ||
142 : | ||
|
||
.pushsection .altinstructions, "a" | ||
altinstruction_entry 140b, 143f, \feature, 142b-140b, 144f-143f | ||
.popsection | ||
|
||
.subsection 1 | ||
143 : | ||
\newinstr | ||
144 : | ||
.previous | ||
.endm | ||
|
||
#define old_len (141b-140b) | ||
#define new_len1 (144f-143f) | ||
#define new_len2 (145f-144f) | ||
|
||
#define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b))))) | ||
|
||
/* | ||
* Same as ALTERNATIVE macro above but for two alternatives. If CPU | ||
* has @feature1, it replaces @oldinstr with @newinstr1. If CPU has | ||
* @feature2, it replaces @oldinstr with @feature2. | ||
*/ | ||
.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2 | ||
140 : | ||
\oldinstr | ||
141 : | ||
.fill - ((alt_max_short(new_len1, new_len2) - (old_len)) > 0) * \ | ||
(alt_max_short(new_len1, new_len2) - (old_len)) / 4, 4, 0x03400000 | ||
142 : | ||
|
||
.pushsection .altinstructions, "a" | ||
altinstruction_entry 140b, 143f, \feature1, 142b-140b, 144f-143f, 142b-141b | ||
altinstruction_entry 140b, 144f, \feature2, 142b-140b, 145f-144f, 142b-141b | ||
.popsection | ||
|
||
.subsection 1 | ||
143 : | ||
\newinstr1 | ||
144 : | ||
\newinstr2 | ||
145 : | ||
.previous | ||
.endm | ||
|
||
#endif /* __ASSEMBLY__ */ | ||
|
||
#endif /* _ASM_ALTERNATIVE_ASM_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
#ifndef _ASM_ALTERNATIVE_H | ||
#define _ASM_ALTERNATIVE_H | ||
|
||
#ifndef __ASSEMBLY__ | ||
|
||
#include <linux/types.h> | ||
#include <linux/stddef.h> | ||
#include <linux/stringify.h> | ||
#include <asm/asm.h> | ||
|
||
struct alt_instr { | ||
s32 instr_offset; /* offset to original instruction */ | ||
s32 replace_offset; /* offset to replacement instruction */ | ||
u16 feature; /* feature bit set for replacement */ | ||
u8 instrlen; /* length of original instruction */ | ||
u8 replacementlen; /* length of new instruction */ | ||
} __packed; | ||
|
||
/* | ||
* Debug flag that can be tested to see whether alternative | ||
* instructions were patched in already: | ||
*/ | ||
extern int alternatives_patched; | ||
extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | ||
|
||
extern void alternative_instructions(void); | ||
extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); | ||
|
||
#define b_replacement(num) "664"#num | ||
#define e_replacement(num) "665"#num | ||
|
||
#define alt_end_marker "663" | ||
#define alt_slen "662b-661b" | ||
#define alt_total_slen alt_end_marker"b-661b" | ||
#define alt_rlen(num) e_replacement(num)"f-"b_replacement(num)"f" | ||
|
||
#define __OLDINSTR(oldinstr, num) \ | ||
"661:\n\t" oldinstr "\n662:\n" \ | ||
".fill -(((" alt_rlen(num) ")-(" alt_slen ")) > 0) * " \ | ||
"((" alt_rlen(num) ")-(" alt_slen ")) / 4, 4, 0x03400000\n" | ||
|
||
#define OLDINSTR(oldinstr, num) \ | ||
__OLDINSTR(oldinstr, num) \ | ||
alt_end_marker ":\n" | ||
|
||
#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))" | ||
|
||
/* | ||
* Pad the second replacement alternative with additional NOPs if it is | ||
* additionally longer than the first replacement alternative. | ||
*/ | ||
#define OLDINSTR_2(oldinstr, num1, num2) \ | ||
"661:\n\t" oldinstr "\n662:\n" \ | ||
".fill -((" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) > 0) * " \ | ||
"(" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) / 4, " \ | ||
"4, 0x03400000\n" \ | ||
alt_end_marker ":\n" | ||
|
||
#define ALTINSTR_ENTRY(feature, num) \ | ||
" .long 661b - .\n" /* label */ \ | ||
" .long " b_replacement(num)"f - .\n" /* new instruction */ \ | ||
" .short " __stringify(feature) "\n" /* feature bit */ \ | ||
" .byte " alt_total_slen "\n" /* source len */ \ | ||
" .byte " alt_rlen(num) "\n" /* replacement len */ | ||
|
||
#define ALTINSTR_REPLACEMENT(newinstr, feature, num) /* replacement */ \ | ||
b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n\t" | ||
|
||
/* alternative assembly primitive: */ | ||
#define ALTERNATIVE(oldinstr, newinstr, feature) \ | ||
OLDINSTR(oldinstr, 1) \ | ||
".pushsection .altinstructions,\"a\"\n" \ | ||
ALTINSTR_ENTRY(feature, 1) \ | ||
".popsection\n" \ | ||
".subsection 1\n" \ | ||
ALTINSTR_REPLACEMENT(newinstr, feature, 1) \ | ||
".previous\n" | ||
|
||
#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\ | ||
OLDINSTR_2(oldinstr, 1, 2) \ | ||
".pushsection .altinstructions,\"a\"\n" \ | ||
ALTINSTR_ENTRY(feature1, 1) \ | ||
ALTINSTR_ENTRY(feature2, 2) \ | ||
".popsection\n" \ | ||
".subsection 1\n" \ | ||
ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \ | ||
ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \ | ||
".previous\n" | ||
|
||
/* | ||
* Alternative instructions for different CPU types or capabilities. | ||
* | ||
* This allows to use optimized instructions even on generic binary | ||
* kernels. | ||
* | ||
* length of oldinstr must be longer or equal the length of newinstr | ||
* It can be padded with nops as needed. | ||
* | ||
* For non barrier like inlines please define new variants | ||
* without volatile and memory clobber. | ||
*/ | ||
#define alternative(oldinstr, newinstr, feature) \ | ||
(asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory")) | ||
|
||
#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ | ||
(asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory")) | ||
|
||
#endif /* __ASSEMBLY__ */ | ||
|
||
#endif /* _ASM_ALTERNATIVE_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* This is included by init/main.c to check for architecture-dependent bugs. | ||
* | ||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited | ||
*/ | ||
#ifndef _ASM_BUGS_H | ||
#define _ASM_BUGS_H | ||
|
||
#include <asm/cpu.h> | ||
#include <asm/cpu-info.h> | ||
|
||
extern void check_bugs(void); | ||
|
||
#endif /* _ASM_BUGS_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.