|
@@ -3,6 +3,7 @@
|
|
|
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/stddef.h>
|
|
|
+#include <linux/stringify.h>
|
|
|
#include <asm/asm.h>
|
|
|
|
|
|
/*
|
|
@@ -74,6 +75,22 @@ static inline void alternatives_smp_switch(int smp) {}
|
|
|
|
|
|
const unsigned char *const *find_nop_table(void);
|
|
|
|
|
|
+/* alternative assembly primitive: */
|
|
|
+#define ALTERNATIVE(oldinstr, newinstr, feature) \
|
|
|
+ \
|
|
|
+ "661:\n\t" oldinstr "\n662:\n" \
|
|
|
+ ".section .altinstructions,\"a\"\n" \
|
|
|
+ _ASM_ALIGN "\n" \
|
|
|
+ _ASM_PTR "661b\n" /* label */ \
|
|
|
+ _ASM_PTR "663f\n" /* new instruction */ \
|
|
|
+ " .byte " __stringify(feature) "\n" /* feature bit */ \
|
|
|
+ " .byte 662b-661b\n" /* sourcelen */ \
|
|
|
+ " .byte 664f-663f\n" /* replacementlen */ \
|
|
|
+ ".previous\n" \
|
|
|
+ ".section .altinstr_replacement, \"ax\"\n" \
|
|
|
+ "663:\n\t" newinstr "\n664:\n" /* replacement */ \
|
|
|
+ ".previous"
|
|
|
+
|
|
|
/*
|
|
|
* Alternative instructions for different CPU types or capabilities.
|
|
|
*
|
|
@@ -87,18 +104,7 @@ const unsigned char *const *find_nop_table(void);
|
|
|
* without volatile and memory clobber.
|
|
|
*/
|
|
|
#define alternative(oldinstr, newinstr, feature) \
|
|
|
- asm volatile ("661:\n\t" oldinstr "\n662:\n" \
|
|
|
- ".section .altinstructions,\"a\"\n" \
|
|
|
- _ASM_ALIGN "\n" \
|
|
|
- _ASM_PTR "661b\n" /* label */ \
|
|
|
- _ASM_PTR "663f\n" /* new instruction */ \
|
|
|
- " .byte %c0\n" /* feature bit */ \
|
|
|
- " .byte 662b-661b\n" /* sourcelen */ \
|
|
|
- " .byte 664f-663f\n" /* replacementlen */ \
|
|
|
- ".previous\n" \
|
|
|
- ".section .altinstr_replacement,\"ax\"\n" \
|
|
|
- "663:\n\t" newinstr "\n664:\n" /* replacement */ \
|
|
|
- ".previous" :: "i" (feature) : "memory")
|
|
|
+ asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory")
|
|
|
|
|
|
/*
|
|
|
* Alternative inline assembly with input.
|
|
@@ -109,35 +115,16 @@ const unsigned char *const *find_nop_table(void);
|
|
|
* Best is to use constraints that are fixed size (like (%1) ... "r")
|
|
|
* If you use variable sized constraints like "m" or "g" in the
|
|
|
* replacement make sure to pad to the worst case length.
|
|
|
+ * Leaving an unused argument 0 to keep API compatibility.
|
|
|
*/
|
|
|
#define alternative_input(oldinstr, newinstr, feature, input...) \
|
|
|
- asm volatile ("661:\n\t" oldinstr "\n662:\n" \
|
|
|
- ".section .altinstructions,\"a\"\n" \
|
|
|
- _ASM_ALIGN "\n" \
|
|
|
- _ASM_PTR "661b\n" /* label */ \
|
|
|
- _ASM_PTR "663f\n" /* new instruction */ \
|
|
|
- " .byte %c0\n" /* feature bit */ \
|
|
|
- " .byte 662b-661b\n" /* sourcelen */ \
|
|
|
- " .byte 664f-663f\n" /* replacementlen */ \
|
|
|
- ".previous\n" \
|
|
|
- ".section .altinstr_replacement,\"ax\"\n" \
|
|
|
- "663:\n\t" newinstr "\n664:\n" /* replacement */ \
|
|
|
- ".previous" :: "i" (feature), ##input)
|
|
|
+ asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) \
|
|
|
+ : : "i" (0), ## input)
|
|
|
|
|
|
/* Like alternative_input, but with a single output argument */
|
|
|
#define alternative_io(oldinstr, newinstr, feature, output, input...) \
|
|
|
- asm volatile ("661:\n\t" oldinstr "\n662:\n" \
|
|
|
- ".section .altinstructions,\"a\"\n" \
|
|
|
- _ASM_ALIGN "\n" \
|
|
|
- _ASM_PTR "661b\n" /* label */ \
|
|
|
- _ASM_PTR "663f\n" /* new instruction */ \
|
|
|
- " .byte %c[feat]\n" /* feature bit */ \
|
|
|
- " .byte 662b-661b\n" /* sourcelen */ \
|
|
|
- " .byte 664f-663f\n" /* replacementlen */ \
|
|
|
- ".previous\n" \
|
|
|
- ".section .altinstr_replacement,\"ax\"\n" \
|
|
|
- "663:\n\t" newinstr "\n664:\n" /* replacement */ \
|
|
|
- ".previous" : output : [feat] "i" (feature), ##input)
|
|
|
+ asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) \
|
|
|
+ : output : "i" (0), ## input)
|
|
|
|
|
|
/*
|
|
|
* use this macro(s) if you need more than one output parameter
|