at v5.1 579 lines 12 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2// Copyright (C) 2005-2017 Andes Technology Corporation 3 4#include <linux/proc_fs.h> 5#include <linux/uaccess.h> 6#include <linux/sysctl.h> 7#include <asm/unaligned.h> 8 9#define DEBUG(enable, tagged, ...) \ 10 do{ \ 11 if (enable) { \ 12 if (tagged) \ 13 pr_warn("[ %30s() ] ", __func__); \ 14 pr_warn(__VA_ARGS__); \ 15 } \ 16 } while (0) 17 18#define RT(inst) (((inst) >> 20) & 0x1FUL) 19#define RA(inst) (((inst) >> 15) & 0x1FUL) 20#define RB(inst) (((inst) >> 10) & 0x1FUL) 21#define SV(inst) (((inst) >> 8) & 0x3UL) 22#define IMM(inst) (((inst) >> 0) & 0x7FFFUL) 23 24#define RA3(inst) (((inst) >> 3) & 0x7UL) 25#define RT3(inst) (((inst) >> 6) & 0x7UL) 26#define IMM3U(inst) (((inst) >> 0) & 0x7UL) 27 28#define RA5(inst) (((inst) >> 0) & 0x1FUL) 29#define RT4(inst) (((inst) >> 5) & 0xFUL) 30 31#define GET_IMMSVAL(imm_value) \ 32 (((imm_value >> 14) & 0x1) ? (imm_value - 0x8000) : imm_value) 33 34#define __get8_data(val,addr,err) \ 35 __asm__( \ 36 "1: lbi.bi %1, [%2], #1\n" \ 37 "2:\n" \ 38 " .pushsection .text.fixup,\"ax\"\n" \ 39 " .align 2\n" \ 40 "3: movi %0, #1\n" \ 41 " j 2b\n" \ 42 " .popsection\n" \ 43 " .pushsection __ex_table,\"a\"\n" \ 44 " .align 3\n" \ 45 " .long 1b, 3b\n" \ 46 " .popsection\n" \ 47 : "=r" (err), "=&r" (val), "=r" (addr) \ 48 : "0" (err), "2" (addr)) 49 50#define get16_data(addr, val_ptr) \ 51 do { \ 52 unsigned int err = 0, v, a = addr; \ 53 __get8_data(v,a,err); \ 54 *val_ptr = v << 0; \ 55 __get8_data(v,a,err); \ 56 *val_ptr |= v << 8; \ 57 if (err) \ 58 goto fault; \ 59 *val_ptr = le16_to_cpu(*val_ptr); \ 60 } while(0) 61 62#define get32_data(addr, val_ptr) \ 63 do { \ 64 unsigned int err = 0, v, a = addr; \ 65 __get8_data(v,a,err); \ 66 *val_ptr = v << 0; \ 67 __get8_data(v,a,err); \ 68 *val_ptr |= v << 8; \ 69 __get8_data(v,a,err); \ 70 *val_ptr |= v << 16; \ 71 __get8_data(v,a,err); \ 72 *val_ptr |= v << 24; \ 73 if (err) \ 74 goto fault; \ 75 *val_ptr = le32_to_cpu(*val_ptr); \ 76 } while(0) 77 78#define get_data(addr, val_ptr, len) \ 79 if (len == 2) \ 80 get16_data(addr, val_ptr); \ 81 else \ 82 get32_data(addr, val_ptr); 83 84#define set16_data(addr, val) \ 85 do { \ 86 unsigned int err = 0, *ptr = addr ; \ 87 val = le32_to_cpu(val); \ 88 __asm__( \ 89 "1: sbi.bi %2, [%1], #1\n" \ 90 " srli %2, %2, #8\n" \ 91 "2: sbi %2, [%1]\n" \ 92 "3:\n" \ 93 " .pushsection .text.fixup,\"ax\"\n" \ 94 " .align 2\n" \ 95 "4: movi %0, #1\n" \ 96 " j 3b\n" \ 97 " .popsection\n" \ 98 " .pushsection __ex_table,\"a\"\n" \ 99 " .align 3\n" \ 100 " .long 1b, 4b\n" \ 101 " .long 2b, 4b\n" \ 102 " .popsection\n" \ 103 : "=r" (err), "+r" (ptr), "+r" (val) \ 104 : "0" (err) \ 105 ); \ 106 if (err) \ 107 goto fault; \ 108 } while(0) 109 110#define set32_data(addr, val) \ 111 do { \ 112 unsigned int err = 0, *ptr = addr ; \ 113 val = le32_to_cpu(val); \ 114 __asm__( \ 115 "1: sbi.bi %2, [%1], #1\n" \ 116 " srli %2, %2, #8\n" \ 117 "2: sbi.bi %2, [%1], #1\n" \ 118 " srli %2, %2, #8\n" \ 119 "3: sbi.bi %2, [%1], #1\n" \ 120 " srli %2, %2, #8\n" \ 121 "4: sbi %2, [%1]\n" \ 122 "5:\n" \ 123 " .pushsection .text.fixup,\"ax\"\n" \ 124 " .align 2\n" \ 125 "6: movi %0, #1\n" \ 126 " j 5b\n" \ 127 " .popsection\n" \ 128 " .pushsection __ex_table,\"a\"\n" \ 129 " .align 3\n" \ 130 " .long 1b, 6b\n" \ 131 " .long 2b, 6b\n" \ 132 " .long 3b, 6b\n" \ 133 " .long 4b, 6b\n" \ 134 " .popsection\n" \ 135 : "=r" (err), "+r" (ptr), "+r" (val) \ 136 : "0" (err) \ 137 ); \ 138 if (err) \ 139 goto fault; \ 140 } while(0) 141#define set_data(addr, val, len) \ 142 if (len == 2) \ 143 set16_data(addr, val); \ 144 else \ 145 set32_data(addr, val); 146#define NDS32_16BIT_INSTRUCTION 0x80000000 147 148extern pte_t va_present(struct mm_struct *mm, unsigned long addr); 149extern pte_t va_kernel_present(unsigned long addr); 150extern int va_readable(struct pt_regs *regs, unsigned long addr); 151extern int va_writable(struct pt_regs *regs, unsigned long addr); 152 153int unalign_access_mode = 0, unalign_access_debug = 0; 154 155static inline unsigned long *idx_to_addr(struct pt_regs *regs, int idx) 156{ 157 /* this should be consistent with ptrace.h */ 158 if (idx >= 0 && idx <= 25) /* R0-R25 */ 159 return &regs->uregs[0] + idx; 160 else if (idx >= 28 && idx <= 30) /* FP, GP, LP */ 161 return &regs->fp + (idx - 28); 162 else if (idx == 31) /* SP */ 163 return &regs->sp; 164 else 165 return NULL; /* cause a segfault */ 166} 167 168static inline unsigned long get_inst(unsigned long addr) 169{ 170 return be32_to_cpu(get_unaligned((u32 *) addr)); 171} 172 173static inline unsigned long sign_extend(unsigned long val, int len) 174{ 175 unsigned long ret = 0; 176 unsigned char *s, *t; 177 int i = 0; 178 179 val = cpu_to_le32(val); 180 181 s = (void *)&val; 182 t = (void *)&ret; 183 184 while (i++ < len) 185 *t++ = *s++; 186 187 if (((*(t - 1)) & 0x80) && (i < 4)) { 188 189 while (i++ <= 4) 190 *t++ = 0xff; 191 } 192 193 return le32_to_cpu(ret); 194} 195 196static inline int do_16(unsigned long inst, struct pt_regs *regs) 197{ 198 int imm, regular, load, len, addr_mode, idx_mode; 199 unsigned long unaligned_addr, target_val, source_idx, target_idx, 200 shift = 0; 201 switch ((inst >> 9) & 0x3F) { 202 203 case 0x12: /* LHI333 */ 204 imm = 1; 205 regular = 1; 206 load = 1; 207 len = 2; 208 addr_mode = 3; 209 idx_mode = 3; 210 break; 211 case 0x10: /* LWI333 */ 212 imm = 1; 213 regular = 1; 214 load = 1; 215 len = 4; 216 addr_mode = 3; 217 idx_mode = 3; 218 break; 219 case 0x11: /* LWI333.bi */ 220 imm = 1; 221 regular = 0; 222 load = 1; 223 len = 4; 224 addr_mode = 3; 225 idx_mode = 3; 226 break; 227 case 0x1A: /* LWI450 */ 228 imm = 0; 229 regular = 1; 230 load = 1; 231 len = 4; 232 addr_mode = 5; 233 idx_mode = 4; 234 break; 235 case 0x16: /* SHI333 */ 236 imm = 1; 237 regular = 1; 238 load = 0; 239 len = 2; 240 addr_mode = 3; 241 idx_mode = 3; 242 break; 243 case 0x14: /* SWI333 */ 244 imm = 1; 245 regular = 1; 246 load = 0; 247 len = 4; 248 addr_mode = 3; 249 idx_mode = 3; 250 break; 251 case 0x15: /* SWI333.bi */ 252 imm = 1; 253 regular = 0; 254 load = 0; 255 len = 4; 256 addr_mode = 3; 257 idx_mode = 3; 258 break; 259 case 0x1B: /* SWI450 */ 260 imm = 0; 261 regular = 1; 262 load = 0; 263 len = 4; 264 addr_mode = 5; 265 idx_mode = 4; 266 break; 267 268 default: 269 return -EFAULT; 270 } 271 272 if (addr_mode == 3) { 273 unaligned_addr = *idx_to_addr(regs, RA3(inst)); 274 source_idx = RA3(inst); 275 } else { 276 unaligned_addr = *idx_to_addr(regs, RA5(inst)); 277 source_idx = RA5(inst); 278 } 279 280 if (idx_mode == 3) 281 target_idx = RT3(inst); 282 else 283 target_idx = RT4(inst); 284 285 if (imm) 286 shift = IMM3U(inst) * len; 287 288 if (regular) 289 unaligned_addr += shift; 290 291 if (load) { 292 if (!access_ok((void *)unaligned_addr, len)) 293 return -EACCES; 294 295 get_data(unaligned_addr, &target_val, len); 296 *idx_to_addr(regs, target_idx) = target_val; 297 } else { 298 if (!access_ok((void *)unaligned_addr, len)) 299 return -EACCES; 300 target_val = *idx_to_addr(regs, target_idx); 301 set_data((void *)unaligned_addr, target_val, len); 302 } 303 304 if (!regular) 305 *idx_to_addr(regs, source_idx) = unaligned_addr + shift; 306 regs->ipc += 2; 307 308 return 0; 309fault: 310 return -EACCES; 311} 312 313static inline int do_32(unsigned long inst, struct pt_regs *regs) 314{ 315 int imm, regular, load, len, sign_ext; 316 unsigned long unaligned_addr, target_val, shift; 317 318 unaligned_addr = *idx_to_addr(regs, RA(inst)); 319 320 switch ((inst >> 25) << 1) { 321 322 case 0x02: /* LHI */ 323 imm = 1; 324 regular = 1; 325 load = 1; 326 len = 2; 327 sign_ext = 0; 328 break; 329 case 0x0A: /* LHI.bi */ 330 imm = 1; 331 regular = 0; 332 load = 1; 333 len = 2; 334 sign_ext = 0; 335 break; 336 case 0x22: /* LHSI */ 337 imm = 1; 338 regular = 1; 339 load = 1; 340 len = 2; 341 sign_ext = 1; 342 break; 343 case 0x2A: /* LHSI.bi */ 344 imm = 1; 345 regular = 0; 346 load = 1; 347 len = 2; 348 sign_ext = 1; 349 break; 350 case 0x04: /* LWI */ 351 imm = 1; 352 regular = 1; 353 load = 1; 354 len = 4; 355 sign_ext = 0; 356 break; 357 case 0x0C: /* LWI.bi */ 358 imm = 1; 359 regular = 0; 360 load = 1; 361 len = 4; 362 sign_ext = 0; 363 break; 364 case 0x12: /* SHI */ 365 imm = 1; 366 regular = 1; 367 load = 0; 368 len = 2; 369 sign_ext = 0; 370 break; 371 case 0x1A: /* SHI.bi */ 372 imm = 1; 373 regular = 0; 374 load = 0; 375 len = 2; 376 sign_ext = 0; 377 break; 378 case 0x14: /* SWI */ 379 imm = 1; 380 regular = 1; 381 load = 0; 382 len = 4; 383 sign_ext = 0; 384 break; 385 case 0x1C: /* SWI.bi */ 386 imm = 1; 387 regular = 0; 388 load = 0; 389 len = 4; 390 sign_ext = 0; 391 break; 392 393 default: 394 switch (inst & 0xff) { 395 396 case 0x01: /* LH */ 397 imm = 0; 398 regular = 1; 399 load = 1; 400 len = 2; 401 sign_ext = 0; 402 break; 403 case 0x05: /* LH.bi */ 404 imm = 0; 405 regular = 0; 406 load = 1; 407 len = 2; 408 sign_ext = 0; 409 break; 410 case 0x11: /* LHS */ 411 imm = 0; 412 regular = 1; 413 load = 1; 414 len = 2; 415 sign_ext = 1; 416 break; 417 case 0x15: /* LHS.bi */ 418 imm = 0; 419 regular = 0; 420 load = 1; 421 len = 2; 422 sign_ext = 1; 423 break; 424 case 0x02: /* LW */ 425 imm = 0; 426 regular = 1; 427 load = 1; 428 len = 4; 429 sign_ext = 0; 430 break; 431 case 0x06: /* LW.bi */ 432 imm = 0; 433 regular = 0; 434 load = 1; 435 len = 4; 436 sign_ext = 0; 437 break; 438 case 0x09: /* SH */ 439 imm = 0; 440 regular = 1; 441 load = 0; 442 len = 2; 443 sign_ext = 0; 444 break; 445 case 0x0D: /* SH.bi */ 446 imm = 0; 447 regular = 0; 448 load = 0; 449 len = 2; 450 sign_ext = 0; 451 break; 452 case 0x0A: /* SW */ 453 imm = 0; 454 regular = 1; 455 load = 0; 456 len = 4; 457 sign_ext = 0; 458 break; 459 case 0x0E: /* SW.bi */ 460 imm = 0; 461 regular = 0; 462 load = 0; 463 len = 4; 464 sign_ext = 0; 465 break; 466 467 default: 468 return -EFAULT; 469 } 470 } 471 472 if (imm) 473 shift = GET_IMMSVAL(IMM(inst)) * len; 474 else 475 shift = *idx_to_addr(regs, RB(inst)) << SV(inst); 476 477 if (regular) 478 unaligned_addr += shift; 479 480 if (load) { 481 482 if (!access_ok((void *)unaligned_addr, len)) 483 return -EACCES; 484 485 get_data(unaligned_addr, &target_val, len); 486 487 if (sign_ext) 488 *idx_to_addr(regs, RT(inst)) = 489 sign_extend(target_val, len); 490 else 491 *idx_to_addr(regs, RT(inst)) = target_val; 492 } else { 493 494 if (!access_ok((void *)unaligned_addr, len)) 495 return -EACCES; 496 497 target_val = *idx_to_addr(regs, RT(inst)); 498 set_data((void *)unaligned_addr, target_val, len); 499 } 500 501 if (!regular) 502 *idx_to_addr(regs, RA(inst)) = unaligned_addr + shift; 503 504 regs->ipc += 4; 505 506 return 0; 507fault: 508 return -EACCES; 509} 510 511int do_unaligned_access(unsigned long addr, struct pt_regs *regs) 512{ 513 unsigned long inst; 514 int ret = -EFAULT; 515 mm_segment_t seg = get_fs(); 516 517 inst = get_inst(regs->ipc); 518 519 DEBUG((unalign_access_debug > 0), 1, 520 "Faulting addr: 0x%08lx, pc: 0x%08lx [inst: 0x%08lx ]\n", addr, 521 regs->ipc, inst); 522 523 set_fs(USER_DS); 524 525 if (inst & NDS32_16BIT_INSTRUCTION) 526 ret = do_16((inst >> 16) & 0xffff, regs); 527 else 528 ret = do_32(inst, regs); 529 set_fs(seg); 530 531 return ret; 532} 533 534#ifdef CONFIG_PROC_FS 535 536static struct ctl_table alignment_tbl[3] = { 537 { 538 .procname = "enable", 539 .data = &unalign_access_mode, 540 .maxlen = sizeof(unalign_access_mode), 541 .mode = 0666, 542 .proc_handler = &proc_dointvec 543 } 544 , 545 { 546 .procname = "debug_info", 547 .data = &unalign_access_debug, 548 .maxlen = sizeof(unalign_access_debug), 549 .mode = 0644, 550 .proc_handler = &proc_dointvec 551 } 552 , 553 {} 554}; 555 556static struct ctl_table nds32_sysctl_table[2] = { 557 { 558 .procname = "unaligned_access", 559 .mode = 0555, 560 .child = alignment_tbl}, 561 {} 562}; 563 564static struct ctl_path nds32_path[2] = { 565 {.procname = "nds32"}, 566 {} 567}; 568 569/* 570 * Initialize nds32 alignment-correction interface 571 */ 572static int __init nds32_sysctl_init(void) 573{ 574 register_sysctl_paths(nds32_path, nds32_sysctl_table); 575 return 0; 576} 577 578__initcall(nds32_sysctl_init); 579#endif /* CONFIG_PROC_FS */