xref: /linux/arch/s390/include/asm/alternative.h (revision 43347d56c8d9dd732cee2f8efd384ad21dd1f6c4)
1 #ifndef _ASM_S390_ALTERNATIVE_H
2 #define _ASM_S390_ALTERNATIVE_H
3 
4 #ifndef __ASSEMBLY__
5 
6 #include <linux/types.h>
7 #include <linux/stddef.h>
8 #include <linux/stringify.h>
9 
10 struct alt_instr {
11 	s32 instr_offset;	/* original instruction */
12 	s32 repl_offset;	/* offset to replacement instruction */
13 	u16 facility;		/* facility bit set for replacement */
14 	u8  instrlen;		/* length of original instruction */
15 	u8  replacementlen;	/* length of new instruction */
16 } __packed;
17 
18 #ifdef CONFIG_ALTERNATIVES
19 extern void apply_alternative_instructions(void);
20 extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
21 #else
22 static inline void apply_alternative_instructions(void) {};
23 static inline void apply_alternatives(struct alt_instr *start,
24 				      struct alt_instr *end) {};
25 #endif
26 /*
27  * |661:       |662:	  |6620      |663:
28  * +-----------+---------------------+
29  * | oldinstr  | oldinstr_padding    |
30  * |	       +----------+----------+
31  * |	       |	  |	     |
32  * |	       | >6 bytes |6/4/2 nops|
33  * |	       |6 bytes jg----------->
34  * +-----------+---------------------+
35  *		 ^^ static padding ^^
36  *
37  * .altinstr_replacement section
38  * +---------------------+-----------+
39  * |6641:			     |6651:
40  * | alternative instr 1	     |
41  * +-----------+---------+- - - - - -+
42  * |6642:		 |6652:      |
43  * | alternative instr 2 | padding
44  * +---------------------+- - - - - -+
45  *			  ^ runtime ^
46  *
47  * .altinstructions section
48  * +---------------------------------+
49  * | alt_instr entries for each      |
50  * | alternative instr		     |
51  * +---------------------------------+
52  */
53 
54 #define b_altinstr(num)	"664"#num
55 #define e_altinstr(num)	"665"#num
56 
57 #define e_oldinstr_pad_end	"663"
58 #define oldinstr_len		"662b-661b"
59 #define oldinstr_total_len	e_oldinstr_pad_end"b-661b"
60 #define altinstr_len(num)	e_altinstr(num)"b-"b_altinstr(num)"b"
61 #define oldinstr_pad_len(num) \
62 	"-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \
63 	"((" altinstr_len(num) ")-(" oldinstr_len "))"
64 
65 #define INSTR_LEN_SANITY_CHECK(len)					\
66 	".if " len " > 254\n"						\
67 	"\t.error \"cpu alternatives does not support instructions "	\
68 		"blocks > 254 bytes\"\n"				\
69 	".endif\n"							\
70 	".if (" len ") %% 2\n"						\
71 	"\t.error \"cpu alternatives instructions length is odd\"\n"	\
72 	".endif\n"
73 
74 #define OLDINSTR_PADDING(oldinstr, num)					\
75 	".if " oldinstr_pad_len(num) " > 6\n"				\
76 	"\tjg " e_oldinstr_pad_end "f\n"				\
77 	"6620:\n"							\
78 	"\t.fill (" oldinstr_pad_len(num) " - (6620b-662b)) / 2, 2, 0x0700\n" \
79 	".else\n"							\
80 	"\t.fill " oldinstr_pad_len(num) " / 6, 6, 0xc0040000\n"	\
81 	"\t.fill " oldinstr_pad_len(num) " %% 6 / 4, 4, 0x47000000\n"	\
82 	"\t.fill " oldinstr_pad_len(num) " %% 6 %% 4 / 2, 2, 0x0700\n"	\
83 	".endif\n"
84 
85 #define OLDINSTR(oldinstr, num)						\
86 	"661:\n\t" oldinstr "\n662:\n"					\
87 	OLDINSTR_PADDING(oldinstr, num)					\
88 	e_oldinstr_pad_end ":\n"					\
89 	INSTR_LEN_SANITY_CHECK(oldinstr_len)
90 
91 #define OLDINSTR_2(oldinstr, num1, num2)				\
92 	"661:\n\t" oldinstr "\n662:\n"					\
93 	".if " altinstr_len(num1) " < " altinstr_len(num2) "\n"		\
94 	OLDINSTR_PADDING(oldinstr, num2)				\
95 	".else\n"							\
96 	OLDINSTR_PADDING(oldinstr, num1)				\
97 	".endif\n"							\
98 	e_oldinstr_pad_end ":\n"					\
99 	INSTR_LEN_SANITY_CHECK(oldinstr_len)
100 
101 #define ALTINSTR_ENTRY(facility, num)					\
102 	"\t.long 661b - .\n"			/* old instruction */	\
103 	"\t.long " b_altinstr(num)"b - .\n"	/* alt instruction */	\
104 	"\t.word " __stringify(facility) "\n"	/* facility bit    */	\
105 	"\t.byte " oldinstr_total_len "\n"	/* source len	   */	\
106 	"\t.byte " altinstr_len(num) "\n"	/* alt instruction len */
107 
108 #define ALTINSTR_REPLACEMENT(altinstr, num)	/* replacement */	\
109 	b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n"	\
110 	INSTR_LEN_SANITY_CHECK(altinstr_len(num))
111 
112 #ifdef CONFIG_ALTERNATIVES
113 /* alternative assembly primitive: */
114 #define ALTERNATIVE(oldinstr, altinstr, facility) \
115 	".pushsection .altinstr_replacement, \"ax\"\n"			\
116 	ALTINSTR_REPLACEMENT(altinstr, 1)				\
117 	".popsection\n"							\
118 	OLDINSTR(oldinstr, 1)						\
119 	".pushsection .altinstructions,\"a\"\n"				\
120 	ALTINSTR_ENTRY(facility, 1)					\
121 	".popsection\n"
122 
123 #define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2)\
124 	".pushsection .altinstr_replacement, \"ax\"\n"			\
125 	ALTINSTR_REPLACEMENT(altinstr1, 1)				\
126 	ALTINSTR_REPLACEMENT(altinstr2, 2)				\
127 	".popsection\n"							\
128 	OLDINSTR_2(oldinstr, 1, 2)					\
129 	".pushsection .altinstructions,\"a\"\n"				\
130 	ALTINSTR_ENTRY(facility1, 1)					\
131 	ALTINSTR_ENTRY(facility2, 2)					\
132 	".popsection\n"
133 #else
134 /* Alternative instructions are disabled, let's put just oldinstr in */
135 #define ALTERNATIVE(oldinstr, altinstr, facility) \
136 	oldinstr "\n"
137 
138 #define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \
139 	oldinstr "\n"
140 #endif
141 
142 /*
143  * Alternative instructions for different CPU types or capabilities.
144  *
145  * This allows to use optimized instructions even on generic binary
146  * kernels.
147  *
148  * oldinstr is padded with jump and nops at compile time if altinstr is
149  * longer. altinstr is padded with jump and nops at run-time during patching.
150  *
151  * For non barrier like inlines please define new variants
152  * without volatile and memory clobber.
153  */
154 #define alternative(oldinstr, altinstr, facility)			\
155 	asm volatile(ALTERNATIVE(oldinstr, altinstr, facility) : : : "memory")
156 
157 #define alternative_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \
158 	asm volatile(ALTERNATIVE_2(oldinstr, altinstr1, facility1,	    \
159 				   altinstr2, facility2) ::: "memory")
160 
161 #endif /* __ASSEMBLY__ */
162 
163 #endif /* _ASM_S390_ALTERNATIVE_H */
164