unaligned.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:14k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

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