1 #ifndef __X86_ALTERNATIVE_H__
2 #define __X86_ALTERNATIVE_H__
3 
4 #include <asm/nops.h>
5 
6 #ifdef __ASSEMBLY__
7 .macro altinstruction_entry orig alt feature orig_len alt_len
8         .long \orig - .
9         .long \alt - .
10         .word \feature
11         .byte \orig_len
12         .byte \alt_len
13 .endm
14 #else
15 #include <xen/stringify.h>
16 #include <xen/types.h>
17 
18 struct alt_instr {
19     s32 instr_offset;       /* original instruction */
20     s32 repl_offset;        /* offset to replacement instruction */
21     u16 cpuid;              /* cpuid bit set for replacement */
22     u8  instrlen;           /* length of original instruction */
23     u8  replacementlen;     /* length of new instruction, <= instrlen */
24 };
25 
26 #define __ALT_PTR(a,f)      ((u8 *)((void *)&(a)->f + (a)->f))
27 #define ALT_ORIG_PTR(a)     __ALT_PTR(a, instr_offset)
28 #define ALT_REPL_PTR(a)     __ALT_PTR(a, repl_offset)
29 
30 extern void add_nops(void *insns, unsigned int len);
31 /* Similar to alternative_instructions except it can be run with IRQs enabled. */
32 extern void apply_alternatives(const struct alt_instr *start,
33                                const struct alt_instr *end);
34 extern void alternative_instructions(void);
35 
36 #define OLDINSTR(oldinstr)      "661:\n\t" oldinstr "\n662:\n"
37 
38 #define b_replacement(number)   "663"#number
39 #define e_replacement(number)   "664"#number
40 
41 #define alt_slen "662b-661b"
42 #define alt_rlen(number) e_replacement(number)"f-"b_replacement(number)"f"
43 
44 #define ALTINSTR_ENTRY(feature, number)                                       \
45         " .long 661b - .\n"                             /* label           */ \
46         " .long " b_replacement(number)"f - .\n"        /* new instruction */ \
47         " .word " __stringify(feature) "\n"             /* feature bit     */ \
48         " .byte " alt_slen "\n"                         /* source len      */ \
49         " .byte " alt_rlen(number) "\n"                 /* replacement len */
50 
51 #define DISCARD_ENTRY(number)                           /* rlen <= slen */    \
52         " .byte 0xff + (" alt_rlen(number) ") - (" alt_slen ")\n"
53 
54 #define ALTINSTR_REPLACEMENT(newinstr, feature, number) /* replacement */     \
55         b_replacement(number)":\n\t" newinstr "\n" e_replacement(number) ":\n\t"
56 
57 #define ALTERNATIVE_N(newinstr, feature, number)	\
58 	".pushsection .altinstructions,\"a\"\n"		\
59 	ALTINSTR_ENTRY(feature, number)			\
60 	".section .discard,\"a\",@progbits\n"		\
61 	DISCARD_ENTRY(number)				\
62 	".section .altinstr_replacement, \"ax\"\n"	\
63 	ALTINSTR_REPLACEMENT(newinstr, feature, number)	\
64 	".popsection\n"
65 
66 /* alternative assembly primitive: */
67 #define ALTERNATIVE(oldinstr, newinstr, feature)			  \
68 	OLDINSTR(oldinstr)						  \
69 	ALTERNATIVE_N(newinstr, feature, 1)
70 
71 #define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \
72 	ALTERNATIVE(oldinstr, newinstr1, feature1)			  \
73 	ALTERNATIVE_N(newinstr2, feature2, 2)
74 
75 #define ALTERNATIVE_3(oldinstr, newinstr1, feature1, newinstr2, feature2, \
76 		      newinstr3, feature3)				  \
77 	ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \
78 	ALTERNATIVE_N(newinstr3, feature3, 3)
79 
80 /*
81  * Alternative instructions for different CPU types or capabilities.
82  *
83  * This allows to use optimized instructions even on generic binary
84  * kernels.
85  *
86  * length of oldinstr must be longer or equal the length of newinstr
87  * It can be padded with nops as needed.
88  *
89  * For non barrier like inlines please define new variants
90  * without volatile and memory clobber.
91  */
92 #define alternative(oldinstr, newinstr, feature)                        \
93         asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory")
94 
95 /*
96  * Alternative inline assembly with input.
97  *
98  * Pecularities:
99  * No memory clobber here.
100  * Argument numbers start with 1.
101  * Best is to use constraints that are fixed size (like (%1) ... "r")
102  * If you use variable sized constraints like "m" or "g" in the
103  * replacement make sure to pad to the worst case length.
104  */
105 #define alternative_input(oldinstr, newinstr, feature, input...)	\
106 	asm volatile (ALTERNATIVE(oldinstr, newinstr, feature)		\
107 		      : : input)
108 
109 /* Like alternative_input, but with a single output argument */
110 #define alternative_io(oldinstr, newinstr, feature, output, input...)	\
111 	asm volatile (ALTERNATIVE(oldinstr, newinstr, feature)		\
112 		      : output : input)
113 
114 /*
115  * This is similar to alternative_io. But it has two features and
116  * respective instructions.
117  *
118  * If CPU has feature2, newinstr2 is used.
119  * Otherwise, if CPU has feature1, newinstr1 is used.
120  * Otherwise, oldinstr is used.
121  */
122 #define alternative_io_2(oldinstr, newinstr1, feature1, newinstr2,	\
123 			 feature2, output, input...)			\
124 	asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1,	\
125 				   newinstr2, feature2)			\
126 		     : output : input)
127 
128 /*
129  * This is similar to alternative_io. But it has three features and
130  * respective instructions.
131  *
132  * If CPU has feature3, newinstr3 is used.
133  * Otherwise, if CPU has feature2, newinstr2 is used.
134  * Otherwise, if CPU has feature1, newinstr1 is used.
135  * Otherwise, oldinstr is used.
136  */
137 #define alternative_io_3(oldinstr, newinstr1, feature1, newinstr2,	\
138 			 feature2, newinstr3, feature3, output,		\
139 			 input...)					\
140 	asm volatile(ALTERNATIVE_3(oldinstr, newinstr1, feature1,	\
141 				   newinstr2, feature2, newinstr3,	\
142 				   feature3)				\
143 		     : output : input)
144 
145 /* Use this macro(s) if you need more than one output parameter. */
146 #define ASM_OUTPUT2(a...) a
147 
148 #endif  /*  __ASSEMBLY__  */
149 
150 #endif /* __X86_ALTERNATIVE_H__ */
151