1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Channel subsystem I/O instructions. |
4 | */ |
5 | |
6 | #include <linux/export.h> |
7 | |
8 | #include <asm/asm-extable.h> |
9 | #include <asm/chpid.h> |
10 | #include <asm/schid.h> |
11 | #include <asm/crw.h> |
12 | |
13 | #include "ioasm.h" |
14 | #include "orb.h" |
15 | #include "cio.h" |
16 | #include "cio_inject.h" |
17 | |
18 | static inline int __stsch(struct subchannel_id schid, struct schib *addr) |
19 | { |
20 | unsigned long r1 = *(unsigned int *)&schid; |
21 | int ccode = -EIO; |
22 | |
23 | asm volatile( |
24 | " lgr 1,%[r1]\n" |
25 | " stsch %[addr]\n" |
26 | "0: ipm %[cc]\n" |
27 | " srl %[cc],28\n" |
28 | "1:\n" |
29 | EX_TABLE(0b, 1b) |
30 | : [cc] "+&d" (ccode), [addr] "=Q" (*addr) |
31 | : [r1] "d" (r1) |
32 | : "cc" , "1" ); |
33 | return ccode; |
34 | } |
35 | |
36 | int stsch(struct subchannel_id schid, struct schib *addr) |
37 | { |
38 | int ccode; |
39 | |
40 | ccode = __stsch(schid: schid, addr); |
41 | trace_s390_cio_stsch(schid: schid, schib: addr, cc: ccode); |
42 | |
43 | return ccode; |
44 | } |
45 | EXPORT_SYMBOL(stsch); |
46 | |
47 | static inline int __msch(struct subchannel_id schid, struct schib *addr) |
48 | { |
49 | unsigned long r1 = *(unsigned int *)&schid; |
50 | int ccode = -EIO; |
51 | |
52 | asm volatile( |
53 | " lgr 1,%[r1]\n" |
54 | " msch %[addr]\n" |
55 | "0: ipm %[cc]\n" |
56 | " srl %[cc],28\n" |
57 | "1:\n" |
58 | EX_TABLE(0b, 1b) |
59 | : [cc] "+&d" (ccode) |
60 | : [r1] "d" (r1), [addr] "Q" (*addr) |
61 | : "cc" , "1" ); |
62 | return ccode; |
63 | } |
64 | |
65 | int msch(struct subchannel_id schid, struct schib *addr) |
66 | { |
67 | int ccode; |
68 | |
69 | ccode = __msch(schid: schid, addr); |
70 | trace_s390_cio_msch(schid: schid, schib: addr, cc: ccode); |
71 | |
72 | return ccode; |
73 | } |
74 | |
75 | static inline int __tsch(struct subchannel_id schid, struct irb *addr) |
76 | { |
77 | unsigned long r1 = *(unsigned int *)&schid; |
78 | int ccode; |
79 | |
80 | asm volatile( |
81 | " lgr 1,%[r1]\n" |
82 | " tsch %[addr]\n" |
83 | " ipm %[cc]\n" |
84 | " srl %[cc],28" |
85 | : [cc] "=&d" (ccode), [addr] "=Q" (*addr) |
86 | : [r1] "d" (r1) |
87 | : "cc" , "1" ); |
88 | return ccode; |
89 | } |
90 | |
91 | int tsch(struct subchannel_id schid, struct irb *addr) |
92 | { |
93 | int ccode; |
94 | |
95 | ccode = __tsch(schid: schid, addr); |
96 | trace_s390_cio_tsch(schid: schid, irb: addr, cc: ccode); |
97 | |
98 | return ccode; |
99 | } |
100 | |
101 | static inline int __ssch(struct subchannel_id schid, union orb *addr) |
102 | { |
103 | unsigned long r1 = *(unsigned int *)&schid; |
104 | int ccode = -EIO; |
105 | |
106 | asm volatile( |
107 | " lgr 1,%[r1]\n" |
108 | " ssch %[addr]\n" |
109 | "0: ipm %[cc]\n" |
110 | " srl %[cc],28\n" |
111 | "1:\n" |
112 | EX_TABLE(0b, 1b) |
113 | : [cc] "+&d" (ccode) |
114 | : [r1] "d" (r1), [addr] "Q" (*addr) |
115 | : "cc" , "memory" , "1" ); |
116 | return ccode; |
117 | } |
118 | |
119 | int ssch(struct subchannel_id schid, union orb *addr) |
120 | { |
121 | int ccode; |
122 | |
123 | ccode = __ssch(schid: schid, addr); |
124 | trace_s390_cio_ssch(schid: schid, orb: addr, cc: ccode); |
125 | |
126 | return ccode; |
127 | } |
128 | EXPORT_SYMBOL(ssch); |
129 | |
130 | static inline int __csch(struct subchannel_id schid) |
131 | { |
132 | unsigned long r1 = *(unsigned int *)&schid; |
133 | int ccode; |
134 | |
135 | asm volatile( |
136 | " lgr 1,%[r1]\n" |
137 | " csch\n" |
138 | " ipm %[cc]\n" |
139 | " srl %[cc],28\n" |
140 | : [cc] "=&d" (ccode) |
141 | : [r1] "d" (r1) |
142 | : "cc" , "1" ); |
143 | return ccode; |
144 | } |
145 | |
146 | int csch(struct subchannel_id schid) |
147 | { |
148 | int ccode; |
149 | |
150 | ccode = __csch(schid: schid); |
151 | trace_s390_cio_csch(schid: schid, cc: ccode); |
152 | |
153 | return ccode; |
154 | } |
155 | EXPORT_SYMBOL(csch); |
156 | |
157 | int tpi(struct tpi_info *addr) |
158 | { |
159 | int ccode; |
160 | |
161 | asm volatile( |
162 | " tpi %[addr]\n" |
163 | " ipm %[cc]\n" |
164 | " srl %[cc],28" |
165 | : [cc] "=&d" (ccode), [addr] "=Q" (*addr) |
166 | : |
167 | : "cc" ); |
168 | trace_s390_cio_tpi(addr, cc: ccode); |
169 | |
170 | return ccode; |
171 | } |
172 | |
173 | int chsc(void *chsc_area) |
174 | { |
175 | typedef struct { char _[4096]; } addr_type; |
176 | int cc = -EIO; |
177 | |
178 | asm volatile( |
179 | " .insn rre,0xb25f0000,%[chsc_area],0\n" |
180 | "0: ipm %[cc]\n" |
181 | " srl %[cc],28\n" |
182 | "1:\n" |
183 | EX_TABLE(0b, 1b) |
184 | : [cc] "+&d" (cc), "+m" (*(addr_type *)chsc_area) |
185 | : [chsc_area] "d" (chsc_area) |
186 | : "cc" ); |
187 | trace_s390_cio_chsc(chsc: chsc_area, cc); |
188 | |
189 | return cc; |
190 | } |
191 | EXPORT_SYMBOL(chsc); |
192 | |
193 | static inline int __rsch(struct subchannel_id schid) |
194 | { |
195 | unsigned long r1 = *(unsigned int *)&schid; |
196 | int ccode; |
197 | |
198 | asm volatile( |
199 | " lgr 1,%[r1]\n" |
200 | " rsch\n" |
201 | " ipm %[cc]\n" |
202 | " srl %[cc],28\n" |
203 | : [cc] "=&d" (ccode) |
204 | : [r1] "d" (r1) |
205 | : "cc" , "memory" , "1" ); |
206 | return ccode; |
207 | } |
208 | |
209 | int rsch(struct subchannel_id schid) |
210 | { |
211 | int ccode; |
212 | |
213 | ccode = __rsch(schid: schid); |
214 | trace_s390_cio_rsch(schid: schid, cc: ccode); |
215 | |
216 | return ccode; |
217 | } |
218 | |
219 | static inline int __hsch(struct subchannel_id schid) |
220 | { |
221 | unsigned long r1 = *(unsigned int *)&schid; |
222 | int ccode; |
223 | |
224 | asm volatile( |
225 | " lgr 1,%[r1]\n" |
226 | " hsch\n" |
227 | " ipm %[cc]\n" |
228 | " srl %[cc],28\n" |
229 | : [cc] "=&d" (ccode) |
230 | : [r1] "d" (r1) |
231 | : "cc" , "1" ); |
232 | return ccode; |
233 | } |
234 | |
235 | int hsch(struct subchannel_id schid) |
236 | { |
237 | int ccode; |
238 | |
239 | ccode = __hsch(schid: schid); |
240 | trace_s390_cio_hsch(schid: schid, cc: ccode); |
241 | |
242 | return ccode; |
243 | } |
244 | EXPORT_SYMBOL(hsch); |
245 | |
246 | static inline int __xsch(struct subchannel_id schid) |
247 | { |
248 | unsigned long r1 = *(unsigned int *)&schid; |
249 | int ccode; |
250 | |
251 | asm volatile( |
252 | " lgr 1,%[r1]\n" |
253 | " xsch\n" |
254 | " ipm %[cc]\n" |
255 | " srl %[cc],28\n" |
256 | : [cc] "=&d" (ccode) |
257 | : [r1] "d" (r1) |
258 | : "cc" , "1" ); |
259 | return ccode; |
260 | } |
261 | |
262 | int xsch(struct subchannel_id schid) |
263 | { |
264 | int ccode; |
265 | |
266 | ccode = __xsch(schid: schid); |
267 | trace_s390_cio_xsch(schid: schid, cc: ccode); |
268 | |
269 | return ccode; |
270 | } |
271 | |
272 | static inline int __stcrw(struct crw *crw) |
273 | { |
274 | int ccode; |
275 | |
276 | asm volatile( |
277 | " stcrw %[crw]\n" |
278 | " ipm %[cc]\n" |
279 | " srl %[cc],28\n" |
280 | : [cc] "=&d" (ccode), [crw] "=Q" (*crw) |
281 | : |
282 | : "cc" ); |
283 | return ccode; |
284 | } |
285 | |
286 | static inline int _stcrw(struct crw *crw) |
287 | { |
288 | #ifdef CONFIG_CIO_INJECT |
289 | if (static_branch_unlikely(&cio_inject_enabled)) { |
290 | if (stcrw_get_injected(crw) == 0) |
291 | return 0; |
292 | } |
293 | #endif |
294 | |
295 | return __stcrw(crw); |
296 | } |
297 | |
298 | int stcrw(struct crw *crw) |
299 | { |
300 | int ccode; |
301 | |
302 | ccode = _stcrw(crw); |
303 | trace_s390_cio_stcrw(crw, cc: ccode); |
304 | |
305 | return ccode; |
306 | } |
307 | |