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