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