1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Handle unaligned accesses by emulation. 4 * 5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 6 * 7 * Derived from MIPS: 8 * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle 9 * Copyright (C) 1999 Silicon Graphics, Inc. 10 * Copyright (C) 2014 Imagination Technologies Ltd. 11 */ 12 #include <linux/mm.h> 13 #include <linux/sched.h> 14 #include <linux/signal.h> 15 #include <linux/debugfs.h> 16 #include <linux/perf_event.h> 17 18 #include <asm/asm.h> 19 #include <asm/branch.h> 20 #include <asm/fpu.h> 21 #include <asm/inst.h> 22 23 #include "access-helper.h" 24 25 #ifdef CONFIG_DEBUG_FS 26 static u32 unaligned_instructions_user; 27 static u32 unaligned_instructions_kernel; 28 #endif 29 30 static inline u64 read_fpr(unsigned int idx) 31 { 32 #ifdef CONFIG_64BIT 33 #define READ_FPR(idx, __value) \ 34 __asm__ __volatile__("movfr2gr.d %0, $f"#idx"\n\t" : "=r"(__value)); 35 #else 36 #define READ_FPR(idx, __value) \ 37 { \ 38 u32 __value_lo, __value_hi; \ 39 __asm__ __volatile__("movfr2gr.s %0, $f"#idx"\n\t" : "=r"(__value_lo)); \ 40 __asm__ __volatile__("movfrh2gr.s %0, $f"#idx"\n\t" : "=r"(__value_hi)); \ 41 __value = (__value_lo | ((u64)__value_hi << 32)); \ 42 } 43 #endif 44 u64 __value; 45 46 switch (idx) { 47 case 0: 48 READ_FPR(0, __value); 49 break; 50 case 1: 51 READ_FPR(1, __value); 52 break; 53 case 2: 54 READ_FPR(2, __value); 55 break; 56 case 3: 57 READ_FPR(3, __value); 58 break; 59 case 4: 60 READ_FPR(4, __value); 61 break; 62 case 5: 63 READ_FPR(5, __value); 64 break; 65 case 6: 66 READ_FPR(6, __value); 67 break; 68 case 7: 69 READ_FPR(7, __value); 70 break; 71 case 8: 72 READ_FPR(8, __value); 73 break; 74 case 9: 75 READ_FPR(9, __value); 76 break; 77 case 10: 78 READ_FPR(10, __value); 79 break; 80 case 11: 81 READ_FPR(11, __value); 82 break; 83 case 12: 84 READ_FPR(12, __value); 85 break; 86 case 13: 87 READ_FPR(13, __value); 88 break; 89 case 14: 90 READ_FPR(14, __value); 91 break; 92 case 15: 93 READ_FPR(15, __value); 94 break; 95 case 16: 96 READ_FPR(16, __value); 97 break; 98 case 17: 99 READ_FPR(17, __value); 100 break; 101 case 18: 102 READ_FPR(18, __value); 103 break; 104 case 19: 105 READ_FPR(19, __value); 106 break; 107 case 20: 108 READ_FPR(20, __value); 109 break; 110 case 21: 111 READ_FPR(21, __value); 112 break; 113 case 22: 114 READ_FPR(22, __value); 115 break; 116 case 23: 117 READ_FPR(23, __value); 118 break; 119 case 24: 120 READ_FPR(24, __value); 121 break; 122 case 25: 123 READ_FPR(25, __value); 124 break; 125 case 26: 126 READ_FPR(26, __value); 127 break; 128 case 27: 129 READ_FPR(27, __value); 130 break; 131 case 28: 132 READ_FPR(28, __value); 133 break; 134 case 29: 135 READ_FPR(29, __value); 136 break; 137 case 30: 138 READ_FPR(30, __value); 139 break; 140 case 31: 141 READ_FPR(31, __value); 142 break; 143 default: 144 panic("unexpected idx '%d'", idx); 145 } 146 #undef READ_FPR 147 return __value; 148 } 149 150 static inline void write_fpr(unsigned int idx, u64 value) 151 { 152 #ifdef CONFIG_64BIT 153 #define WRITE_FPR(idx, value) \ 154 __asm__ __volatile__("movgr2fr.d $f"#idx", %0\n\t" :: "r"(value)); 155 #else 156 #define WRITE_FPR(idx, value) \ 157 { \ 158 u32 value_lo = value; \ 159 u32 value_hi = value >> 32; \ 160 __asm__ __volatile__("movgr2fr.w $f"#idx", %0\n\t" :: "r"(value_lo)); \ 161 __asm__ __volatile__("movgr2frh.w $f"#idx", %0\n\t" :: "r"(value_hi)); \ 162 } 163 #endif 164 switch (idx) { 165 case 0: 166 WRITE_FPR(0, value); 167 break; 168 case 1: 169 WRITE_FPR(1, value); 170 break; 171 case 2: 172 WRITE_FPR(2, value); 173 break; 174 case 3: 175 WRITE_FPR(3, value); 176 break; 177 case 4: 178 WRITE_FPR(4, value); 179 break; 180 case 5: 181 WRITE_FPR(5, value); 182 break; 183 case 6: 184 WRITE_FPR(6, value); 185 break; 186 case 7: 187 WRITE_FPR(7, value); 188 break; 189 case 8: 190 WRITE_FPR(8, value); 191 break; 192 case 9: 193 WRITE_FPR(9, value); 194 break; 195 case 10: 196 WRITE_FPR(10, value); 197 break; 198 case 11: 199 WRITE_FPR(11, value); 200 break; 201 case 12: 202 WRITE_FPR(12, value); 203 break; 204 case 13: 205 WRITE_FPR(13, value); 206 break; 207 case 14: 208 WRITE_FPR(14, value); 209 break; 210 case 15: 211 WRITE_FPR(15, value); 212 break; 213 case 16: 214 WRITE_FPR(16, value); 215 break; 216 case 17: 217 WRITE_FPR(17, value); 218 break; 219 case 18: 220 WRITE_FPR(18, value); 221 break; 222 case 19: 223 WRITE_FPR(19, value); 224 break; 225 case 20: 226 WRITE_FPR(20, value); 227 break; 228 case 21: 229 WRITE_FPR(21, value); 230 break; 231 case 22: 232 WRITE_FPR(22, value); 233 break; 234 case 23: 235 WRITE_FPR(23, value); 236 break; 237 case 24: 238 WRITE_FPR(24, value); 239 break; 240 case 25: 241 WRITE_FPR(25, value); 242 break; 243 case 26: 244 WRITE_FPR(26, value); 245 break; 246 case 27: 247 WRITE_FPR(27, value); 248 break; 249 case 28: 250 WRITE_FPR(28, value); 251 break; 252 case 29: 253 WRITE_FPR(29, value); 254 break; 255 case 30: 256 WRITE_FPR(30, value); 257 break; 258 case 31: 259 WRITE_FPR(31, value); 260 break; 261 default: 262 panic("unexpected idx '%d'", idx); 263 } 264 #undef WRITE_FPR 265 } 266 267 void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc) 268 { 269 bool fp = false; 270 bool sign, write; 271 bool user = user_mode(regs); 272 unsigned int res, size = 0; 273 u64 value = 0; 274 union loongarch_instruction insn; 275 276 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); 277 278 __get_inst(&insn.word, pc, user); 279 280 switch (insn.reg2i12_format.opcode) { 281 case ldh_op: 282 size = 2; 283 sign = true; 284 write = false; 285 break; 286 case ldhu_op: 287 size = 2; 288 sign = false; 289 write = false; 290 break; 291 case sth_op: 292 size = 2; 293 sign = true; 294 write = true; 295 break; 296 case ldw_op: 297 size = 4; 298 sign = true; 299 write = false; 300 break; 301 case ldwu_op: 302 size = 4; 303 sign = false; 304 write = false; 305 break; 306 case stw_op: 307 size = 4; 308 sign = true; 309 write = true; 310 break; 311 case ldd_op: 312 size = 8; 313 sign = true; 314 write = false; 315 break; 316 case std_op: 317 size = 8; 318 sign = true; 319 write = true; 320 break; 321 case flds_op: 322 size = 4; 323 fp = true; 324 sign = true; 325 write = false; 326 break; 327 case fsts_op: 328 size = 4; 329 fp = true; 330 sign = true; 331 write = true; 332 break; 333 case fldd_op: 334 size = 8; 335 fp = true; 336 sign = true; 337 write = false; 338 break; 339 case fstd_op: 340 size = 8; 341 fp = true; 342 sign = true; 343 write = true; 344 break; 345 } 346 347 switch (insn.reg2i14_format.opcode) { 348 case ldptrw_op: 349 size = 4; 350 sign = true; 351 write = false; 352 break; 353 case stptrw_op: 354 size = 4; 355 sign = true; 356 write = true; 357 break; 358 case ldptrd_op: 359 size = 8; 360 sign = true; 361 write = false; 362 break; 363 case stptrd_op: 364 size = 8; 365 sign = true; 366 write = true; 367 break; 368 } 369 370 switch (insn.reg3_format.opcode) { 371 case ldxh_op: 372 size = 2; 373 sign = true; 374 write = false; 375 break; 376 case ldxhu_op: 377 size = 2; 378 sign = false; 379 write = false; 380 break; 381 case stxh_op: 382 size = 2; 383 sign = true; 384 write = true; 385 break; 386 case ldxw_op: 387 size = 4; 388 sign = true; 389 write = false; 390 break; 391 case ldxwu_op: 392 size = 4; 393 sign = false; 394 write = false; 395 break; 396 case stxw_op: 397 size = 4; 398 sign = true; 399 write = true; 400 break; 401 case ldxd_op: 402 size = 8; 403 sign = true; 404 write = false; 405 break; 406 case stxd_op: 407 size = 8; 408 sign = true; 409 write = true; 410 break; 411 case fldxs_op: 412 size = 4; 413 fp = true; 414 sign = true; 415 write = false; 416 break; 417 case fstxs_op: 418 size = 4; 419 fp = true; 420 sign = true; 421 write = true; 422 break; 423 case fldxd_op: 424 size = 8; 425 fp = true; 426 sign = true; 427 write = false; 428 break; 429 case fstxd_op: 430 size = 8; 431 fp = true; 432 sign = true; 433 write = true; 434 break; 435 } 436 437 if (!size) 438 goto sigbus; 439 if (user && !access_ok(addr, size)) 440 goto sigbus; 441 442 if (!write) { 443 res = unaligned_read(addr, &value, size, sign); 444 if (res) 445 goto fault; 446 447 /* Rd is the same field in any formats */ 448 if (!fp) 449 regs->regs[insn.reg3_format.rd] = value; 450 else { 451 if (is_fpu_owner()) 452 write_fpr(insn.reg3_format.rd, value); 453 else 454 set_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0, value); 455 } 456 } else { 457 /* Rd is the same field in any formats */ 458 if (!fp) 459 value = regs->regs[insn.reg3_format.rd]; 460 else { 461 if (is_fpu_owner()) 462 value = read_fpr(insn.reg3_format.rd); 463 else 464 value = get_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0); 465 } 466 467 res = unaligned_write(addr, value, size); 468 if (res) 469 goto fault; 470 } 471 472 #ifdef CONFIG_DEBUG_FS 473 if (user) 474 unaligned_instructions_user++; 475 else 476 unaligned_instructions_kernel++; 477 #endif 478 479 compute_return_era(regs); 480 481 return; 482 483 fault: 484 /* Did we have an exception handler installed? */ 485 if (fixup_exception(regs)) 486 return; 487 488 die_if_kernel("Unhandled kernel unaligned access", regs); 489 force_sig(SIGSEGV); 490 491 return; 492 493 sigbus: 494 die_if_kernel("Unhandled kernel unaligned access", regs); 495 force_sig(SIGBUS); 496 497 return; 498 } 499 500 #ifdef CONFIG_DEBUG_FS 501 static int __init debugfs_unaligned(void) 502 { 503 debugfs_create_u32("unaligned_instructions_user", 504 S_IRUGO, arch_debugfs_dir, &unaligned_instructions_user); 505 debugfs_create_u32("unaligned_instructions_kernel", 506 S_IRUGO, arch_debugfs_dir, &unaligned_instructions_kernel); 507 508 return 0; 509 } 510 arch_initcall(debugfs_unaligned); 511 #endif 512