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