unaligned.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /* $Id: unaligned.c,v 1.23 2001/12/21 00:54:31 davem Exp $
  2. * unaligned.c: Unaligned load/store trap handling with special
  3. * cases for the kernel to do them more quickly.
  4. *
  5. * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  6. * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  7. */
  8. #include <linux/kernel.h>
  9. #include <linux/sched.h>
  10. #include <linux/mm.h>
  11. #include <linux/module.h>
  12. #include <asm/ptrace.h>
  13. #include <asm/processor.h>
  14. #include <asm/system.h>
  15. #include <asm/uaccess.h>
  16. #include <linux/smp.h>
  17. #include <linux/smp_lock.h>
  18. /* #define DEBUG_MNA */
  19. enum direction {
  20. load, /* ld, ldd, ldh, ldsh */
  21. store, /* st, std, sth, stsh */
  22. both, /* Swap, ldstub, etc. */
  23. fpload,
  24. fpstore,
  25. invalid,
  26. };
  27. #ifdef DEBUG_MNA
  28. static char *dirstrings[] = {
  29. "load", "store", "both", "fpload", "fpstore", "invalid"
  30. };
  31. #endif
  32. static inline enum direction decode_direction(unsigned int insn)
  33. {
  34. unsigned long tmp = (insn >> 21) & 1;
  35. if(!tmp)
  36. return load;
  37. else {
  38. if(((insn>>19)&0x3f) == 15)
  39. return both;
  40. else
  41. return store;
  42. }
  43. }
  44. /* 8 = double-word, 4 = word, 2 = half-word */
  45. static inline int decode_access_size(unsigned int insn)
  46. {
  47. insn = (insn >> 19) & 3;
  48. if(!insn)
  49. return 4;
  50. else if(insn == 3)
  51. return 8;
  52. else if(insn == 2)
  53. return 2;
  54. else {
  55. printk("Impossible unaligned trap. insn=%08x\n", insn);
  56. die_if_kernel("Byte sized unaligned access?!?!", current->thread.kregs);
  57. return 4; /* just to keep gcc happy. */
  58. }
  59. }
  60. /* 0x400000 = signed, 0 = unsigned */
  61. static inline int decode_signedness(unsigned int insn)
  62. {
  63. return (insn & 0x400000);
  64. }
  65. static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
  66. unsigned int rd)
  67. {
  68. if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
  69. /* Wheee... */
  70. __asm__ __volatile__("save %sp, -0x40, %sp\n\t"
  71. "save %sp, -0x40, %sp\n\t"
  72. "save %sp, -0x40, %sp\n\t"
  73. "save %sp, -0x40, %sp\n\t"
  74. "save %sp, -0x40, %sp\n\t"
  75. "save %sp, -0x40, %sp\n\t"
  76. "save %sp, -0x40, %sp\n\t"
  77. "restore; restore; restore; restore;\n\t"
  78. "restore; restore; restore;\n\t");
  79. }
  80. }
  81. static inline int sign_extend_imm13(int imm)
  82. {
  83. return imm << 19 >> 19;
  84. }
  85. static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
  86. {
  87. struct reg_window *win;
  88. if(reg < 16)
  89. return (!reg ? 0 : regs->u_regs[reg]);
  90. /* Ho hum, the slightly complicated case. */
  91. win = (struct reg_window *) regs->u_regs[UREG_FP];
  92. return win->locals[reg - 16]; /* yes, I know what this does... */
  93. }
  94. static inline unsigned long safe_fetch_reg(unsigned int reg, struct pt_regs *regs)
  95. {
  96. struct reg_window __user *win;
  97. unsigned long ret;
  98. if (reg < 16)
  99. return (!reg ? 0 : regs->u_regs[reg]);
  100. /* Ho hum, the slightly complicated case. */
  101. win = (struct reg_window __user *) regs->u_regs[UREG_FP];
  102. if ((unsigned long)win & 3)
  103. return -1;
  104. if (get_user(ret, &win->locals[reg - 16]))
  105. return -1;
  106. return ret;
  107. }
  108. static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
  109. {
  110. struct reg_window *win;
  111. if(reg < 16)
  112. return &regs->u_regs[reg];
  113. win = (struct reg_window *) regs->u_regs[UREG_FP];
  114. return &win->locals[reg - 16];
  115. }
  116. static unsigned long compute_effective_address(struct pt_regs *regs,
  117. unsigned int insn)
  118. {
  119. unsigned int rs1 = (insn >> 14) & 0x1f;
  120. unsigned int rs2 = insn & 0x1f;
  121. unsigned int rd = (insn >> 25) & 0x1f;
  122. if(insn & 0x2000) {
  123. maybe_flush_windows(rs1, 0, rd);
  124. return (fetch_reg(rs1, regs) + sign_extend_imm13(insn));
  125. } else {
  126. maybe_flush_windows(rs1, rs2, rd);
  127. return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs));
  128. }
  129. }
  130. unsigned long safe_compute_effective_address(struct pt_regs *regs,
  131. unsigned int insn)
  132. {
  133. unsigned int rs1 = (insn >> 14) & 0x1f;
  134. unsigned int rs2 = insn & 0x1f;
  135. unsigned int rd = (insn >> 25) & 0x1f;
  136. if(insn & 0x2000) {
  137. maybe_flush_windows(rs1, 0, rd);
  138. return (safe_fetch_reg(rs1, regs) + sign_extend_imm13(insn));
  139. } else {
  140. maybe_flush_windows(rs1, rs2, rd);
  141. return (safe_fetch_reg(rs1, regs) + safe_fetch_reg(rs2, regs));
  142. }
  143. }
  144. /* This is just to make gcc think panic does return... */
  145. static void unaligned_panic(char *str)
  146. {
  147. panic(str);
  148. }
  149. #define do_integer_load(dest_reg, size, saddr, is_signed, errh) ({ \
  150. __asm__ __volatile__ ( \
  151. "cmp %1, 8\n\t" \
  152. "be 9f\n\t" \
  153. " cmp %1, 4\n\t" \
  154. "be 6f\n" \
  155. "4:\t" " ldub [%2], %%l1\n" \
  156. "5:\t" "ldub [%2 + 1], %%l2\n\t" \
  157. "sll %%l1, 8, %%l1\n\t" \
  158. "tst %3\n\t" \
  159. "be 3f\n\t" \
  160. " add %%l1, %%l2, %%l1\n\t" \
  161. "sll %%l1, 16, %%l1\n\t" \
  162. "sra %%l1, 16, %%l1\n" \
  163. "3:\t" "b 0f\n\t" \
  164. " st %%l1, [%0]\n" \
  165. "6:\t" "ldub [%2 + 1], %%l2\n\t" \
  166. "sll %%l1, 24, %%l1\n" \
  167. "7:\t" "ldub [%2 + 2], %%g7\n\t" \
  168. "sll %%l2, 16, %%l2\n" \
  169. "8:\t" "ldub [%2 + 3], %%g1\n\t" \
  170. "sll %%g7, 8, %%g7\n\t" \
  171. "or %%l1, %%l2, %%l1\n\t" \
  172. "or %%g7, %%g1, %%g7\n\t" \
  173. "or %%l1, %%g7, %%l1\n\t" \
  174. "b 0f\n\t" \
  175. " st %%l1, [%0]\n" \
  176. "9:\t" "ldub [%2], %%l1\n" \
  177. "10:\t" "ldub [%2 + 1], %%l2\n\t" \
  178. "sll %%l1, 24, %%l1\n" \
  179. "11:\t" "ldub [%2 + 2], %%g7\n\t" \
  180. "sll %%l2, 16, %%l2\n" \
  181. "12:\t" "ldub [%2 + 3], %%g1\n\t" \
  182. "sll %%g7, 8, %%g7\n\t" \
  183. "or %%l1, %%l2, %%l1\n\t" \
  184. "or %%g7, %%g1, %%g7\n\t" \
  185. "or %%l1, %%g7, %%g7\n" \
  186. "13:\t" "ldub [%2 + 4], %%l1\n\t" \
  187. "st %%g7, [%0]\n" \
  188. "14:\t" "ldub [%2 + 5], %%l2\n\t" \
  189. "sll %%l1, 24, %%l1\n" \
  190. "15:\t" "ldub [%2 + 6], %%g7\n\t" \
  191. "sll %%l2, 16, %%l2\n" \
  192. "16:\t" "ldub [%2 + 7], %%g1\n\t" \
  193. "sll %%g7, 8, %%g7\n\t" \
  194. "or %%l1, %%l2, %%l1\n\t" \
  195. "or %%g7, %%g1, %%g7\n\t" \
  196. "or %%l1, %%g7, %%g7\n\t" \
  197. "st %%g7, [%0 + 4]\n" \
  198. "0:\n\n\t" \
  199. ".section __ex_table,#alloc\n\t" \
  200. ".word 4b, " #errh "\n\t" \
  201. ".word 5b, " #errh "\n\t" \
  202. ".word 6b, " #errh "\n\t" \
  203. ".word 7b, " #errh "\n\t" \
  204. ".word 8b, " #errh "\n\t" \
  205. ".word 9b, " #errh "\n\t" \
  206. ".word 10b, " #errh "\n\t" \
  207. ".word 11b, " #errh "\n\t" \
  208. ".word 12b, " #errh "\n\t" \
  209. ".word 13b, " #errh "\n\t" \
  210. ".word 14b, " #errh "\n\t" \
  211. ".word 15b, " #errh "\n\t" \
  212. ".word 16b, " #errh "\n\n\t" \
  213. ".previous\n\t" \
  214. : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed) \
  215. : "l1", "l2", "g7", "g1", "cc"); \
  216. })
  217. #define store_common(dst_addr, size, src_val, errh) ({ \
  218. __asm__ __volatile__ ( \
  219. "ld [%2], %%l1\n" \
  220. "cmp %1, 2\n\t" \
  221. "be 2f\n\t" \
  222. " cmp %1, 4\n\t" \
  223. "be 1f\n\t" \
  224. " srl %%l1, 24, %%l2\n\t" \
  225. "srl %%l1, 16, %%g7\n" \
  226. "4:\t" "stb %%l2, [%0]\n\t" \
  227. "srl %%l1, 8, %%l2\n" \
  228. "5:\t" "stb %%g7, [%0 + 1]\n\t" \
  229. "ld [%2 + 4], %%g7\n" \
  230. "6:\t" "stb %%l2, [%0 + 2]\n\t" \
  231. "srl %%g7, 24, %%l2\n" \
  232. "7:\t" "stb %%l1, [%0 + 3]\n\t" \
  233. "srl %%g7, 16, %%l1\n" \
  234. "8:\t" "stb %%l2, [%0 + 4]\n\t" \
  235. "srl %%g7, 8, %%l2\n" \
  236. "9:\t" "stb %%l1, [%0 + 5]\n" \
  237. "10:\t" "stb %%l2, [%0 + 6]\n\t" \
  238. "b 0f\n" \
  239. "11:\t" " stb %%g7, [%0 + 7]\n" \
  240. "1:\t" "srl %%l1, 16, %%g7\n" \
  241. "12:\t" "stb %%l2, [%0]\n\t" \
  242. "srl %%l1, 8, %%l2\n" \
  243. "13:\t" "stb %%g7, [%0 + 1]\n" \
  244. "14:\t" "stb %%l2, [%0 + 2]\n\t" \
  245. "b 0f\n" \
  246. "15:\t" " stb %%l1, [%0 + 3]\n" \
  247. "2:\t" "srl %%l1, 8, %%l2\n" \
  248. "16:\t" "stb %%l2, [%0]\n" \
  249. "17:\t" "stb %%l1, [%0 + 1]\n" \
  250. "0:\n\n\t" \
  251. ".section __ex_table,#alloc\n\t" \
  252. ".word 4b, " #errh "\n\t" \
  253. ".word 5b, " #errh "\n\t" \
  254. ".word 6b, " #errh "\n\t" \
  255. ".word 7b, " #errh "\n\t" \
  256. ".word 8b, " #errh "\n\t" \
  257. ".word 9b, " #errh "\n\t" \
  258. ".word 10b, " #errh "\n\t" \
  259. ".word 11b, " #errh "\n\t" \
  260. ".word 12b, " #errh "\n\t" \
  261. ".word 13b, " #errh "\n\t" \
  262. ".word 14b, " #errh "\n\t" \
  263. ".word 15b, " #errh "\n\t" \
  264. ".word 16b, " #errh "\n\t" \
  265. ".word 17b, " #errh "\n\n\t" \
  266. ".previous\n\t" \
  267. : : "r" (dst_addr), "r" (size), "r" (src_val) \
  268. : "l1", "l2", "g7", "g1", "cc"); \
  269. })
  270. #define do_integer_store(reg_num, size, dst_addr, regs, errh) ({ \
  271. unsigned long *src_val; \
  272. static unsigned long zero[2] = { 0, }; \
  273. \
  274. if (reg_num) src_val = fetch_reg_addr(reg_num, regs); \
  275. else { \
  276. src_val = &zero[0]; \
  277. if (size == 8) \
  278. zero[1] = fetch_reg(1, regs); \
  279. } \
  280. store_common(dst_addr, size, src_val, errh); \
  281. })
  282. extern void smp_capture(void);
  283. extern void smp_release(void);
  284. #define do_atomic(srcdest_reg, mem, errh) ({ \
  285. unsigned long flags, tmp; \
  286. \
  287. smp_capture(); \
  288. local_irq_save(flags); \
  289. tmp = *srcdest_reg; \
  290. do_integer_load(srcdest_reg, 4, mem, 0, errh); \
  291. store_common(mem, 4, &tmp, errh); \
  292. local_irq_restore(flags); \
  293. smp_release(); \
  294. })
  295. static inline void advance(struct pt_regs *regs)
  296. {
  297. regs->pc = regs->npc;
  298. regs->npc += 4;
  299. }
  300. static inline int floating_point_load_or_store_p(unsigned int insn)
  301. {
  302. return (insn >> 24) & 1;
  303. }
  304. static inline int ok_for_kernel(unsigned int insn)
  305. {
  306. return !floating_point_load_or_store_p(insn);
  307. }
  308. void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault");
  309. void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
  310. {
  311. unsigned long g2 = regs->u_regs [UREG_G2];
  312. unsigned long fixup = search_extables_range(regs->pc, &g2);
  313. if (!fixup) {
  314. unsigned long address = compute_effective_address(regs, insn);
  315. if(address < PAGE_SIZE) {
  316. printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
  317. } else
  318. printk(KERN_ALERT "Unable to handle kernel paging request in mna handler");
  319. printk(KERN_ALERT " at virtual address %08lx\n",address);
  320. printk(KERN_ALERT "current->{mm,active_mm}->context = %08lx\n",
  321. (current->mm ? current->mm->context :
  322. current->active_mm->context));
  323. printk(KERN_ALERT "current->{mm,active_mm}->pgd = %08lx\n",
  324. (current->mm ? (unsigned long) current->mm->pgd :
  325. (unsigned long) current->active_mm->pgd));
  326. die_if_kernel("Oops", regs);
  327. /* Not reached */
  328. }
  329. regs->pc = fixup;
  330. regs->npc = regs->pc + 4;
  331. regs->u_regs [UREG_G2] = g2;
  332. }
  333. asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
  334. {
  335. enum direction dir = decode_direction(insn);
  336. int size = decode_access_size(insn);
  337. if(!ok_for_kernel(insn) || dir == both) {
  338. printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n",
  339. regs->pc);
  340. unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store.");
  341. __asm__ __volatile__ ("\n"
  342. "kernel_unaligned_trap_fault:\n\t"
  343. "mov %0, %%o0\n\t"
  344. "call kernel_mna_trap_fault\n\t"
  345. " mov %1, %%o1\n\t"
  346. :
  347. : "r" (regs), "r" (insn)
  348. : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
  349. "g1", "g2", "g3", "g4", "g5", "g7", "cc");
  350. } else {
  351. unsigned long addr = compute_effective_address(regs, insn);
  352. #ifdef DEBUG_MNA
  353. printk("KMNA: pc=%08lx [dir=%s addr=%08lx size=%d] retpc[%08lx]\n",
  354. regs->pc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]);
  355. #endif
  356. switch(dir) {
  357. case load:
  358. do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
  359. size, (unsigned long *) addr,
  360. decode_signedness(insn),
  361. kernel_unaligned_trap_fault);
  362. break;
  363. case store:
  364. do_integer_store(((insn>>25)&0x1f), size,
  365. (unsigned long *) addr, regs,
  366. kernel_unaligned_trap_fault);
  367. break;
  368. #if 0 /* unsupported */
  369. case both:
  370. do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
  371. (unsigned long *) addr,
  372. kernel_unaligned_trap_fault);
  373. break;
  374. #endif
  375. default:
  376. panic("Impossible kernel unaligned trap.");
  377. /* Not reached... */
  378. }
  379. advance(regs);
  380. }
  381. }
  382. static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
  383. enum direction dir)
  384. {
  385. unsigned int reg;
  386. int check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
  387. int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
  388. if ((regs->pc | regs->npc) & 3)
  389. return 0;
  390. /* Must access_ok() in all the necessary places. */
  391. #define WINREG_ADDR(regnum) \
  392. ((void __user *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
  393. reg = (insn >> 25) & 0x1f;
  394. if (reg >= 16) {
  395. if (!access_ok(check, WINREG_ADDR(reg - 16), size))
  396. return -EFAULT;
  397. }
  398. reg = (insn >> 14) & 0x1f;
  399. if (reg >= 16) {
  400. if (!access_ok(check, WINREG_ADDR(reg - 16), size))
  401. return -EFAULT;
  402. }
  403. if (!(insn & 0x2000)) {
  404. reg = (insn & 0x1f);
  405. if (reg >= 16) {
  406. if (!access_ok(check, WINREG_ADDR(reg - 16), size))
  407. return -EFAULT;
  408. }
  409. }
  410. #undef WINREG_ADDR
  411. return 0;
  412. }
  413. void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault");
  414. void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
  415. {
  416. siginfo_t info;
  417. info.si_signo = SIGBUS;
  418. info.si_errno = 0;
  419. info.si_code = BUS_ADRALN;
  420. info.si_addr = (void __user *)safe_compute_effective_address(regs, insn);
  421. info.si_trapno = 0;
  422. send_sig_info(SIGBUS, &info, current);
  423. }
  424. asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
  425. {
  426. enum direction dir;
  427. lock_kernel();
  428. if(!(current->thread.flags & SPARC_FLAG_UNALIGNED) ||
  429. (((insn >> 30) & 3) != 3))
  430. goto kill_user;
  431. dir = decode_direction(insn);
  432. if(!ok_for_user(regs, insn, dir)) {
  433. goto kill_user;
  434. } else {
  435. int size = decode_access_size(insn);
  436. unsigned long addr;
  437. if(floating_point_load_or_store_p(insn)) {
  438. printk("User FPU load/store unaligned unsupported.\n");
  439. goto kill_user;
  440. }
  441. addr = compute_effective_address(regs, insn);
  442. switch(dir) {
  443. case load:
  444. do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
  445. size, (unsigned long *) addr,
  446. decode_signedness(insn),
  447. user_unaligned_trap_fault);
  448. break;
  449. case store:
  450. do_integer_store(((insn>>25)&0x1f), size,
  451. (unsigned long *) addr, regs,
  452. user_unaligned_trap_fault);
  453. break;
  454. case both:
  455. #if 0 /* unsupported */
  456. do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
  457. (unsigned long *) addr,
  458. user_unaligned_trap_fault);
  459. #else
  460. /*
  461. * This was supported in 2.4. However, we question
  462. * the value of SWAP instruction across word boundaries.
  463. */
  464. printk("Unaligned SWAP unsupported.\n");
  465. goto kill_user;
  466. #endif
  467. break;
  468. default:
  469. unaligned_panic("Impossible user unaligned trap.");
  470. __asm__ __volatile__ ("\n"
  471. "user_unaligned_trap_fault:\n\t"
  472. "mov %0, %%o0\n\t"
  473. "call user_mna_trap_fault\n\t"
  474. " mov %1, %%o1\n\t"
  475. :
  476. : "r" (regs), "r" (insn)
  477. : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
  478. "g1", "g2", "g3", "g4", "g5", "g7", "cc");
  479. goto out;
  480. }
  481. advance(regs);
  482. goto out;
  483. }
  484. kill_user:
  485. user_mna_trap_fault(regs, insn);
  486. out:
  487. unlock_kernel();
  488. }