1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Support for Floating Point and Vector Instructions
4 *
5 */
6
7 #ifndef __ASM_S390_FPU_INSN_H
8 #define __ASM_S390_FPU_INSN_H
9
10 #include <asm/fpu-insn-asm.h>
11
12 #ifndef __ASSEMBLY__
13
14 #include <linux/instrumented.h>
15 #include <asm/asm-extable.h>
16
17 asm(".include \"asm/fpu-insn-asm.h\"\n");
18
19 /*
20 * Various small helper functions, which can and should be used within
21 * kernel fpu code sections. Each function represents only one floating
22 * point or vector instruction (except for helper functions which require
23 * exception handling).
24 *
25 * This allows to use floating point and vector instructions like C
26 * functions, which has the advantage that all supporting code, like
27 * e.g. loops, can be written in easy to read C code.
28 *
29 * Each of the helper functions provides support for code instrumentation,
30 * like e.g. KASAN. Therefore instrumentation is also covered automatically
31 * when using these functions.
32 *
33 * In order to ensure that code generated with the helper functions stays
34 * within kernel fpu sections, which are guarded with kernel_fpu_begin()
35 * and kernel_fpu_end() calls, each function has a mandatory "memory"
36 * barrier.
37 */
38
fpu_cefbr(u8 f1,s32 val)39 static __always_inline void fpu_cefbr(u8 f1, s32 val)
40 {
41 asm volatile("cefbr %[f1],%[val]\n"
42 :
43 : [f1] "I" (f1), [val] "d" (val)
44 : "memory");
45 }
46
fpu_cgebr(u8 f2,u8 mode)47 static __always_inline unsigned long fpu_cgebr(u8 f2, u8 mode)
48 {
49 unsigned long val;
50
51 asm volatile("cgebr %[val],%[mode],%[f2]\n"
52 : [val] "=d" (val)
53 : [f2] "I" (f2), [mode] "I" (mode)
54 : "memory");
55 return val;
56 }
57
fpu_debr(u8 f1,u8 f2)58 static __always_inline void fpu_debr(u8 f1, u8 f2)
59 {
60 asm volatile("debr %[f1],%[f2]\n"
61 :
62 : [f1] "I" (f1), [f2] "I" (f2)
63 : "memory");
64 }
65
fpu_ld(unsigned short fpr,freg_t * reg)66 static __always_inline void fpu_ld(unsigned short fpr, freg_t *reg)
67 {
68 instrument_read(reg, sizeof(*reg));
69 asm volatile("ld %[fpr],%[reg]\n"
70 :
71 : [fpr] "I" (fpr), [reg] "Q" (reg->ui)
72 : "memory");
73 }
74
fpu_ldgr(u8 f1,u32 val)75 static __always_inline void fpu_ldgr(u8 f1, u32 val)
76 {
77 asm volatile("ldgr %[f1],%[val]\n"
78 :
79 : [f1] "I" (f1), [val] "d" (val)
80 : "memory");
81 }
82
fpu_lfpc(unsigned int * fpc)83 static __always_inline void fpu_lfpc(unsigned int *fpc)
84 {
85 instrument_read(fpc, sizeof(*fpc));
86 asm volatile("lfpc %[fpc]"
87 :
88 : [fpc] "Q" (*fpc)
89 : "memory");
90 }
91
92 /**
93 * fpu_lfpc_safe - Load floating point control register safely.
94 * @fpc: new value for floating point control register
95 *
96 * Load floating point control register. This may lead to an exception,
97 * since a saved value may have been modified by user space (ptrace,
98 * signal return, kvm registers) to an invalid value. In such a case
99 * set the floating point control register to zero.
100 */
fpu_lfpc_safe(unsigned int * fpc)101 static inline void fpu_lfpc_safe(unsigned int *fpc)
102 {
103 u32 tmp;
104
105 instrument_read(fpc, sizeof(*fpc));
106 asm volatile("\n"
107 "0: lfpc %[fpc]\n"
108 "1: nopr %%r7\n"
109 ".pushsection .fixup, \"ax\"\n"
110 "2: lghi %[tmp],0\n"
111 " sfpc %[tmp]\n"
112 " jg 1b\n"
113 ".popsection\n"
114 EX_TABLE(1b, 2b)
115 : [tmp] "=d" (tmp)
116 : [fpc] "Q" (*fpc)
117 : "memory");
118 }
119
fpu_std(unsigned short fpr,freg_t * reg)120 static __always_inline void fpu_std(unsigned short fpr, freg_t *reg)
121 {
122 instrument_write(reg, sizeof(*reg));
123 asm volatile("std %[fpr],%[reg]\n"
124 : [reg] "=Q" (reg->ui)
125 : [fpr] "I" (fpr)
126 : "memory");
127 }
128
fpu_sfpc(unsigned int fpc)129 static __always_inline void fpu_sfpc(unsigned int fpc)
130 {
131 asm volatile("sfpc %[fpc]"
132 :
133 : [fpc] "d" (fpc)
134 : "memory");
135 }
136
fpu_stfpc(unsigned int * fpc)137 static __always_inline void fpu_stfpc(unsigned int *fpc)
138 {
139 instrument_write(fpc, sizeof(*fpc));
140 asm volatile("stfpc %[fpc]"
141 : [fpc] "=Q" (*fpc)
142 :
143 : "memory");
144 }
145
fpu_vab(u8 v1,u8 v2,u8 v3)146 static __always_inline void fpu_vab(u8 v1, u8 v2, u8 v3)
147 {
148 asm volatile("VAB %[v1],%[v2],%[v3]"
149 :
150 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
151 : "memory");
152 }
153
fpu_vcksm(u8 v1,u8 v2,u8 v3)154 static __always_inline void fpu_vcksm(u8 v1, u8 v2, u8 v3)
155 {
156 asm volatile("VCKSM %[v1],%[v2],%[v3]"
157 :
158 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
159 : "memory");
160 }
161
fpu_vesravb(u8 v1,u8 v2,u8 v3)162 static __always_inline void fpu_vesravb(u8 v1, u8 v2, u8 v3)
163 {
164 asm volatile("VESRAVB %[v1],%[v2],%[v3]"
165 :
166 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
167 : "memory");
168 }
169
fpu_vgfmag(u8 v1,u8 v2,u8 v3,u8 v4)170 static __always_inline void fpu_vgfmag(u8 v1, u8 v2, u8 v3, u8 v4)
171 {
172 asm volatile("VGFMAG %[v1],%[v2],%[v3],%[v4]"
173 :
174 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3), [v4] "I" (v4)
175 : "memory");
176 }
177
fpu_vgfmg(u8 v1,u8 v2,u8 v3)178 static __always_inline void fpu_vgfmg(u8 v1, u8 v2, u8 v3)
179 {
180 asm volatile("VGFMG %[v1],%[v2],%[v3]"
181 :
182 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
183 : "memory");
184 }
185
186 #ifdef CONFIG_CC_IS_CLANG
187
fpu_vl(u8 v1,const void * vxr)188 static __always_inline void fpu_vl(u8 v1, const void *vxr)
189 {
190 instrument_read(vxr, sizeof(__vector128));
191 asm volatile("\n"
192 " la 1,%[vxr]\n"
193 " VL %[v1],0,,1\n"
194 :
195 : [vxr] "R" (*(__vector128 *)vxr),
196 [v1] "I" (v1)
197 : "memory", "1");
198 }
199
200 #else /* CONFIG_CC_IS_CLANG */
201
fpu_vl(u8 v1,const void * vxr)202 static __always_inline void fpu_vl(u8 v1, const void *vxr)
203 {
204 instrument_read(vxr, sizeof(__vector128));
205 asm volatile("VL %[v1],%O[vxr],,%R[vxr]\n"
206 :
207 : [vxr] "Q" (*(__vector128 *)vxr),
208 [v1] "I" (v1)
209 : "memory");
210 }
211
212 #endif /* CONFIG_CC_IS_CLANG */
213
fpu_vleib(u8 v,s16 val,u8 index)214 static __always_inline void fpu_vleib(u8 v, s16 val, u8 index)
215 {
216 asm volatile("VLEIB %[v],%[val],%[index]"
217 :
218 : [v] "I" (v), [val] "K" (val), [index] "I" (index)
219 : "memory");
220 }
221
fpu_vleig(u8 v,s16 val,u8 index)222 static __always_inline void fpu_vleig(u8 v, s16 val, u8 index)
223 {
224 asm volatile("VLEIG %[v],%[val],%[index]"
225 :
226 : [v] "I" (v), [val] "K" (val), [index] "I" (index)
227 : "memory");
228 }
229
fpu_vlgvf(u8 v,u16 index)230 static __always_inline u64 fpu_vlgvf(u8 v, u16 index)
231 {
232 u64 val;
233
234 asm volatile("VLGVF %[val],%[v],%[index]"
235 : [val] "=d" (val)
236 : [v] "I" (v), [index] "L" (index)
237 : "memory");
238 return val;
239 }
240
241 #ifdef CONFIG_CC_IS_CLANG
242
fpu_vll(u8 v1,u32 index,const void * vxr)243 static __always_inline void fpu_vll(u8 v1, u32 index, const void *vxr)
244 {
245 unsigned int size;
246
247 size = min(index + 1, sizeof(__vector128));
248 instrument_read(vxr, size);
249 asm volatile("\n"
250 " la 1,%[vxr]\n"
251 " VLL %[v1],%[index],0,1\n"
252 :
253 : [vxr] "R" (*(u8 *)vxr),
254 [index] "d" (index),
255 [v1] "I" (v1)
256 : "memory", "1");
257 }
258
259 #else /* CONFIG_CC_IS_CLANG */
260
fpu_vll(u8 v1,u32 index,const void * vxr)261 static __always_inline void fpu_vll(u8 v1, u32 index, const void *vxr)
262 {
263 unsigned int size;
264
265 size = min(index + 1, sizeof(__vector128));
266 instrument_read(vxr, size);
267 asm volatile("VLL %[v1],%[index],%O[vxr],%R[vxr]\n"
268 :
269 : [vxr] "Q" (*(u8 *)vxr),
270 [index] "d" (index),
271 [v1] "I" (v1)
272 : "memory");
273 }
274
275 #endif /* CONFIG_CC_IS_CLANG */
276
277 #ifdef CONFIG_CC_IS_CLANG
278
279 #define fpu_vlm(_v1, _v3, _vxrs) \
280 ({ \
281 unsigned int size = ((_v3) - (_v1) + 1) * sizeof(__vector128); \
282 struct { \
283 __vector128 _v[(_v3) - (_v1) + 1]; \
284 } *_v = (void *)(_vxrs); \
285 \
286 instrument_read(_v, size); \
287 asm volatile("\n" \
288 " la 1,%[vxrs]\n" \
289 " VLM %[v1],%[v3],0,1\n" \
290 : \
291 : [vxrs] "R" (*_v), \
292 [v1] "I" (_v1), [v3] "I" (_v3) \
293 : "memory", "1"); \
294 (_v3) - (_v1) + 1; \
295 })
296
297 #else /* CONFIG_CC_IS_CLANG */
298
299 #define fpu_vlm(_v1, _v3, _vxrs) \
300 ({ \
301 unsigned int size = ((_v3) - (_v1) + 1) * sizeof(__vector128); \
302 struct { \
303 __vector128 _v[(_v3) - (_v1) + 1]; \
304 } *_v = (void *)(_vxrs); \
305 \
306 instrument_read(_v, size); \
307 asm volatile("VLM %[v1],%[v3],%O[vxrs],%R[vxrs]\n" \
308 : \
309 : [vxrs] "Q" (*_v), \
310 [v1] "I" (_v1), [v3] "I" (_v3) \
311 : "memory"); \
312 (_v3) - (_v1) + 1; \
313 })
314
315 #endif /* CONFIG_CC_IS_CLANG */
316
fpu_vlr(u8 v1,u8 v2)317 static __always_inline void fpu_vlr(u8 v1, u8 v2)
318 {
319 asm volatile("VLR %[v1],%[v2]"
320 :
321 : [v1] "I" (v1), [v2] "I" (v2)
322 : "memory");
323 }
324
fpu_vlvgf(u8 v,u32 val,u16 index)325 static __always_inline void fpu_vlvgf(u8 v, u32 val, u16 index)
326 {
327 asm volatile("VLVGF %[v],%[val],%[index]"
328 :
329 : [v] "I" (v), [val] "d" (val), [index] "L" (index)
330 : "memory");
331 }
332
fpu_vn(u8 v1,u8 v2,u8 v3)333 static __always_inline void fpu_vn(u8 v1, u8 v2, u8 v3)
334 {
335 asm volatile("VN %[v1],%[v2],%[v3]"
336 :
337 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
338 : "memory");
339 }
340
fpu_vperm(u8 v1,u8 v2,u8 v3,u8 v4)341 static __always_inline void fpu_vperm(u8 v1, u8 v2, u8 v3, u8 v4)
342 {
343 asm volatile("VPERM %[v1],%[v2],%[v3],%[v4]"
344 :
345 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3), [v4] "I" (v4)
346 : "memory");
347 }
348
fpu_vrepib(u8 v1,s16 i2)349 static __always_inline void fpu_vrepib(u8 v1, s16 i2)
350 {
351 asm volatile("VREPIB %[v1],%[i2]"
352 :
353 : [v1] "I" (v1), [i2] "K" (i2)
354 : "memory");
355 }
356
fpu_vsrlb(u8 v1,u8 v2,u8 v3)357 static __always_inline void fpu_vsrlb(u8 v1, u8 v2, u8 v3)
358 {
359 asm volatile("VSRLB %[v1],%[v2],%[v3]"
360 :
361 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
362 : "memory");
363 }
364
365 #ifdef CONFIG_CC_IS_CLANG
366
fpu_vst(u8 v1,const void * vxr)367 static __always_inline void fpu_vst(u8 v1, const void *vxr)
368 {
369 instrument_write(vxr, sizeof(__vector128));
370 asm volatile("\n"
371 " la 1,%[vxr]\n"
372 " VST %[v1],0,,1\n"
373 : [vxr] "=R" (*(__vector128 *)vxr)
374 : [v1] "I" (v1)
375 : "memory", "1");
376 }
377
378 #else /* CONFIG_CC_IS_CLANG */
379
fpu_vst(u8 v1,const void * vxr)380 static __always_inline void fpu_vst(u8 v1, const void *vxr)
381 {
382 instrument_write(vxr, sizeof(__vector128));
383 asm volatile("VST %[v1],%O[vxr],,%R[vxr]\n"
384 : [vxr] "=Q" (*(__vector128 *)vxr)
385 : [v1] "I" (v1)
386 : "memory");
387 }
388
389 #endif /* CONFIG_CC_IS_CLANG */
390
391 #ifdef CONFIG_CC_IS_CLANG
392
fpu_vstl(u8 v1,u32 index,const void * vxr)393 static __always_inline void fpu_vstl(u8 v1, u32 index, const void *vxr)
394 {
395 unsigned int size;
396
397 size = min(index + 1, sizeof(__vector128));
398 instrument_write(vxr, size);
399 asm volatile("\n"
400 " la 1,%[vxr]\n"
401 " VSTL %[v1],%[index],0,1\n"
402 : [vxr] "=R" (*(u8 *)vxr)
403 : [index] "d" (index), [v1] "I" (v1)
404 : "memory", "1");
405 }
406
407 #else /* CONFIG_CC_IS_CLANG */
408
fpu_vstl(u8 v1,u32 index,const void * vxr)409 static __always_inline void fpu_vstl(u8 v1, u32 index, const void *vxr)
410 {
411 unsigned int size;
412
413 size = min(index + 1, sizeof(__vector128));
414 instrument_write(vxr, size);
415 asm volatile("VSTL %[v1],%[index],%O[vxr],%R[vxr]\n"
416 : [vxr] "=Q" (*(u8 *)vxr)
417 : [index] "d" (index), [v1] "I" (v1)
418 : "memory");
419 }
420
421 #endif /* CONFIG_CC_IS_CLANG */
422
423 #ifdef CONFIG_CC_IS_CLANG
424
425 #define fpu_vstm(_v1, _v3, _vxrs) \
426 ({ \
427 unsigned int size = ((_v3) - (_v1) + 1) * sizeof(__vector128); \
428 struct { \
429 __vector128 _v[(_v3) - (_v1) + 1]; \
430 } *_v = (void *)(_vxrs); \
431 \
432 instrument_write(_v, size); \
433 asm volatile("\n" \
434 " la 1,%[vxrs]\n" \
435 " VSTM %[v1],%[v3],0,1\n" \
436 : [vxrs] "=R" (*_v) \
437 : [v1] "I" (_v1), [v3] "I" (_v3) \
438 : "memory", "1"); \
439 (_v3) - (_v1) + 1; \
440 })
441
442 #else /* CONFIG_CC_IS_CLANG */
443
444 #define fpu_vstm(_v1, _v3, _vxrs) \
445 ({ \
446 unsigned int size = ((_v3) - (_v1) + 1) * sizeof(__vector128); \
447 struct { \
448 __vector128 _v[(_v3) - (_v1) + 1]; \
449 } *_v = (void *)(_vxrs); \
450 \
451 instrument_write(_v, size); \
452 asm volatile("VSTM %[v1],%[v3],%O[vxrs],%R[vxrs]\n" \
453 : [vxrs] "=Q" (*_v) \
454 : [v1] "I" (_v1), [v3] "I" (_v3) \
455 : "memory"); \
456 (_v3) - (_v1) + 1; \
457 })
458
459 #endif /* CONFIG_CC_IS_CLANG */
460
fpu_vupllf(u8 v1,u8 v2)461 static __always_inline void fpu_vupllf(u8 v1, u8 v2)
462 {
463 asm volatile("VUPLLF %[v1],%[v2]"
464 :
465 : [v1] "I" (v1), [v2] "I" (v2)
466 : "memory");
467 }
468
fpu_vx(u8 v1,u8 v2,u8 v3)469 static __always_inline void fpu_vx(u8 v1, u8 v2, u8 v3)
470 {
471 asm volatile("VX %[v1],%[v2],%[v3]"
472 :
473 : [v1] "I" (v1), [v2] "I" (v2), [v3] "I" (v3)
474 : "memory");
475 }
476
fpu_vzero(u8 v)477 static __always_inline void fpu_vzero(u8 v)
478 {
479 asm volatile("VZERO %[v]"
480 :
481 : [v] "I" (v)
482 : "memory");
483 }
484
485 #endif /* __ASSEMBLY__ */
486 #endif /* __ASM_S390_FPU_INSN_H */
487