1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _ASM_S390_ALTERNATIVE_H
3 #define _ASM_S390_ALTERNATIVE_H
4 
5 /*
6  * Each alternative comes with a 32 bit feature field:
7  *	union {
8  *		u32 feature;
9  *		struct {
10  *			u32 ctx	 : 4;
11  *			u32 type : 8;
12  *			u32 data : 20;
13  *		};
14  *	}
15  *
16  * @ctx is a bitfield, where only one bit must be set. Each bit defines
17  * in which context an alternative is supposed to be applied to the
18  * kernel image:
19  *
20  * - from the decompressor before the kernel itself is executed
21  * - from early kernel code from within the kernel
22  *
23  * @type is a number which defines the type and with that the type
24  * specific alternative patching.
25  *
26  * @data is additional type specific information which defines if an
27  * alternative should be applied.
28  */
29 
30 #define ALT_CTX_EARLY		1
31 #define ALT_CTX_LATE		2
32 #define ALT_CTX_ALL		(ALT_CTX_EARLY | ALT_CTX_LATE)
33 
34 #define ALT_TYPE_FACILITY	0
35 #define ALT_TYPE_FEATURE	1
36 #define ALT_TYPE_SPEC		2
37 
38 #define ALT_DATA_SHIFT		0
39 #define ALT_TYPE_SHIFT		20
40 #define ALT_CTX_SHIFT		28
41 
42 #define ALT_FACILITY(facility)		(ALT_CTX_EARLY << ALT_CTX_SHIFT		| \
43 					 ALT_TYPE_FACILITY << ALT_TYPE_SHIFT	| \
44 					 (facility) << ALT_DATA_SHIFT)
45 
46 #define ALT_FEATURE(feature)		(ALT_CTX_EARLY << ALT_CTX_SHIFT		| \
47 					 ALT_TYPE_FEATURE << ALT_TYPE_SHIFT	| \
48 					 (feature) << ALT_DATA_SHIFT)
49 
50 #define ALT_SPEC(facility)		(ALT_CTX_LATE << ALT_CTX_SHIFT		| \
51 					 ALT_TYPE_SPEC << ALT_TYPE_SHIFT	| \
52 					 (facility) << ALT_DATA_SHIFT)
53 
54 #ifndef __ASSEMBLY__
55 
56 #include <linux/types.h>
57 #include <linux/stddef.h>
58 #include <linux/stringify.h>
59 
60 struct alt_instr {
61 	s32 instr_offset;	/* original instruction */
62 	s32 repl_offset;	/* offset to replacement instruction */
63 	union {
64 		u32 feature;	/* feature required for replacement */
65 		struct {
66 			u32 ctx	 : 4;  /* context */
67 			u32 type : 8;  /* type of alternative */
68 			u32 data : 20; /* patching information */
69 		};
70 	};
71 	u8  instrlen;		/* length of original instruction */
72 } __packed;
73 
74 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
75 
76 void __apply_alternatives(struct alt_instr *start, struct alt_instr *end, unsigned int ctx);
77 
apply_alternative_instructions(void)78 static inline void apply_alternative_instructions(void)
79 {
80 	__apply_alternatives(__alt_instructions, __alt_instructions_end, ALT_CTX_LATE);
81 }
82 
apply_alternatives(struct alt_instr * start,struct alt_instr * end)83 static inline void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
84 {
85 	__apply_alternatives(start, end, ALT_CTX_ALL);
86 }
87 
88 /*
89  * +---------------------------------+
90  * |661:			     |662:
91  * | oldinstr			     |
92  * +---------------------------------+
93  *
94  * .altinstr_replacement section
95  * +---------------------------------+
96  * |6641:			     |6651:
97  * | alternative instr 1	     |
98  * +---------------------------------+
99  * |6642:			     |6652:
100  * | alternative instr 2	     |
101  * +---------------------------------+
102  *
103  * .altinstructions section
104  * +---------------------------------+
105  * | alt_instr entries for each      |
106  * | alternative instr		     |
107  * +---------------------------------+
108  */
109 
110 #define b_altinstr(num)		"664"#num
111 #define e_altinstr(num)		"665"#num
112 #define oldinstr_len		"662b-661b"
113 #define altinstr_len(num)	e_altinstr(num)"b-"b_altinstr(num)"b"
114 
115 #define OLDINSTR(oldinstr) \
116 	"661:\n\t" oldinstr "\n662:\n"
117 
118 #define ALTINSTR_ENTRY(feature, num)					\
119 	"\t.long 661b - .\n"			/* old instruction */	\
120 	"\t.long " b_altinstr(num)"b - .\n"	/* alt instruction */	\
121 	"\t.long " __stringify(feature) "\n"	/* feature	   */	\
122 	"\t.byte " oldinstr_len "\n"		/* instruction len */	\
123 	"\t.org . - (" oldinstr_len ") & 1\n"				\
124 	"\t.org . - (" oldinstr_len ") + (" altinstr_len(num) ")\n"	\
125 	"\t.org . - (" altinstr_len(num) ") + (" oldinstr_len ")\n"
126 
127 #define ALTINSTR_REPLACEMENT(altinstr, num)	/* replacement */	\
128 	b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n"
129 
130 /* alternative assembly primitive: */
131 #define ALTERNATIVE(oldinstr, altinstr, feature) \
132 	".pushsection .altinstr_replacement, \"ax\"\n"			\
133 	ALTINSTR_REPLACEMENT(altinstr, 1)				\
134 	".popsection\n"							\
135 	OLDINSTR(oldinstr)						\
136 	".pushsection .altinstructions,\"a\"\n"				\
137 	ALTINSTR_ENTRY(feature, 1)					\
138 	".popsection\n"
139 
140 #define ALTERNATIVE_2(oldinstr, altinstr1, feature1, altinstr2, feature2)\
141 	".pushsection .altinstr_replacement, \"ax\"\n"			\
142 	ALTINSTR_REPLACEMENT(altinstr1, 1)				\
143 	ALTINSTR_REPLACEMENT(altinstr2, 2)				\
144 	".popsection\n"							\
145 	OLDINSTR(oldinstr)						\
146 	".pushsection .altinstructions,\"a\"\n"				\
147 	ALTINSTR_ENTRY(feature1, 1)					\
148 	ALTINSTR_ENTRY(feature2, 2)					\
149 	".popsection\n"
150 
151 /*
152  * Alternative instructions for different CPU types or capabilities.
153  *
154  * This allows to use optimized instructions even on generic binary
155  * kernels.
156  *
157  * oldinstr is padded with jump and nops at compile time if altinstr is
158  * longer. altinstr is padded with jump and nops at run-time during patching.
159  *
160  * For non barrier like inlines please define new variants
161  * without volatile and memory clobber.
162  */
163 #define alternative(oldinstr, altinstr, feature)			\
164 	asm_inline volatile(ALTERNATIVE(oldinstr, altinstr, feature) : : : "memory")
165 
166 #define alternative_2(oldinstr, altinstr1, feature1, altinstr2, feature2) \
167 	asm_inline volatile(ALTERNATIVE_2(oldinstr, altinstr1, feature1,   \
168 				   altinstr2, feature2) ::: "memory")
169 
170 /* Alternative inline assembly with input. */
171 #define alternative_input(oldinstr, newinstr, feature, input...)	\
172 	asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, feature)	\
173 		: : input)
174 
175 /* Like alternative_input, but with a single output argument */
176 #define alternative_io(oldinstr, altinstr, feature, output, input...)	\
177 	asm_inline volatile(ALTERNATIVE(oldinstr, altinstr, feature)	\
178 		: output : input)
179 
180 /* Use this macro if more than one output parameter is needed. */
181 #define ASM_OUTPUT2(a...) a
182 
183 /* Use this macro if clobbers are needed without inputs. */
184 #define ASM_NO_INPUT_CLOBBER(clobber...) : clobber
185 
186 #else  /* __ASSEMBLY__ */
187 
188 /*
189  * Issue one struct alt_instr descriptor entry (need to put it into
190  * the section .altinstructions, see below). This entry contains
191  * enough information for the alternatives patching code to patch an
192  * instruction. See apply_alternatives().
193  */
194 .macro alt_entry orig_start, orig_end, alt_start, alt_end, feature
195 	.long	\orig_start - .
196 	.long	\alt_start - .
197 	.long	\feature
198 	.byte	\orig_end - \orig_start
199 	.org	. - ( \orig_end - \orig_start ) & 1
200 	.org	. - ( \orig_end - \orig_start ) + ( \alt_end - \alt_start )
201 	.org	. - ( \alt_end - \alt_start ) + ( \orig_end - \orig_start )
202 .endm
203 
204 /*
205  * Define an alternative between two instructions. If @feature is
206  * present, early code in apply_alternatives() replaces @oldinstr with
207  * @newinstr.
208  */
209 .macro ALTERNATIVE oldinstr, newinstr, feature
210 	.pushsection .altinstr_replacement,"ax"
211 770:	\newinstr
212 771:	.popsection
213 772:	\oldinstr
214 773:	.pushsection .altinstructions,"a"
215 	alt_entry 772b, 773b, 770b, 771b, \feature
216 	.popsection
217 .endm
218 
219 /*
220  * Define an alternative between two instructions. If @feature is
221  * present, early code in apply_alternatives() replaces @oldinstr with
222  * @newinstr.
223  */
224 .macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
225 	.pushsection .altinstr_replacement,"ax"
226 770:	\newinstr1
227 771:	\newinstr2
228 772:	.popsection
229 773:	\oldinstr
230 774:	.pushsection .altinstructions,"a"
231 	alt_entry 773b, 774b, 770b, 771b,\feature1
232 	alt_entry 773b, 774b, 771b, 772b,\feature2
233 	.popsection
234 .endm
235 
236 #endif /* __ASSEMBLY__ */
237 
238 #endif /* _ASM_S390_ALTERNATIVE_H */
239