unaligned.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright (C) 2011-2012 Synopsys (www.synopsys.com)
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * vineetg : May 2011
  9. * -Adapted (from .26 to .35)
  10. * -original contribution by Tim.yao@amlogic.com
  11. *
  12. */
  13. #include <linux/types.h>
  14. #include <linux/ptrace.h>
  15. #include <linux/uaccess.h>
  16. #include <asm/disasm.h>
  17. #define __get8_unaligned_check(val, addr, err) \
  18. __asm__( \
  19. "1: ldb.ab %1, [%2, 1]\n" \
  20. "2:\n" \
  21. " .section .fixup,\"ax\"\n" \
  22. " .align 4\n" \
  23. "3: mov %0, 1\n" \
  24. " b 2b\n" \
  25. " .previous\n" \
  26. " .section __ex_table,\"a\"\n" \
  27. " .align 4\n" \
  28. " .long 1b, 3b\n" \
  29. " .previous\n" \
  30. : "=r" (err), "=&r" (val), "=r" (addr) \
  31. : "0" (err), "2" (addr))
  32. #define get16_unaligned_check(val, addr) \
  33. do { \
  34. unsigned int err = 0, v, a = addr; \
  35. __get8_unaligned_check(v, a, err); \
  36. val = v ; \
  37. __get8_unaligned_check(v, a, err); \
  38. val |= v << 8; \
  39. if (err) \
  40. goto fault; \
  41. } while (0)
  42. #define get32_unaligned_check(val, addr) \
  43. do { \
  44. unsigned int err = 0, v, a = addr; \
  45. __get8_unaligned_check(v, a, err); \
  46. val = v << 0; \
  47. __get8_unaligned_check(v, a, err); \
  48. val |= v << 8; \
  49. __get8_unaligned_check(v, a, err); \
  50. val |= v << 16; \
  51. __get8_unaligned_check(v, a, err); \
  52. val |= v << 24; \
  53. if (err) \
  54. goto fault; \
  55. } while (0)
  56. #define put16_unaligned_check(val, addr) \
  57. do { \
  58. unsigned int err = 0, v = val, a = addr;\
  59. \
  60. __asm__( \
  61. "1: stb.ab %1, [%2, 1]\n" \
  62. " lsr %1, %1, 8\n" \
  63. "2: stb %1, [%2]\n" \
  64. "3:\n" \
  65. " .section .fixup,\"ax\"\n" \
  66. " .align 4\n" \
  67. "4: mov %0, 1\n" \
  68. " b 3b\n" \
  69. " .previous\n" \
  70. " .section __ex_table,\"a\"\n" \
  71. " .align 4\n" \
  72. " .long 1b, 4b\n" \
  73. " .long 2b, 4b\n" \
  74. " .previous\n" \
  75. : "=r" (err), "=&r" (v), "=&r" (a) \
  76. : "0" (err), "1" (v), "2" (a)); \
  77. \
  78. if (err) \
  79. goto fault; \
  80. } while (0)
  81. #define put32_unaligned_check(val, addr) \
  82. do { \
  83. unsigned int err = 0, v = val, a = addr;\
  84. __asm__( \
  85. \
  86. "1: stb.ab %1, [%2, 1]\n" \
  87. " lsr %1, %1, 8\n" \
  88. "2: stb.ab %1, [%2, 1]\n" \
  89. " lsr %1, %1, 8\n" \
  90. "3: stb.ab %1, [%2, 1]\n" \
  91. " lsr %1, %1, 8\n" \
  92. "4: stb %1, [%2]\n" \
  93. "5:\n" \
  94. " .section .fixup,\"ax\"\n" \
  95. " .align 4\n" \
  96. "6: mov %0, 1\n" \
  97. " b 5b\n" \
  98. " .previous\n" \
  99. " .section __ex_table,\"a\"\n" \
  100. " .align 4\n" \
  101. " .long 1b, 6b\n" \
  102. " .long 2b, 6b\n" \
  103. " .long 3b, 6b\n" \
  104. " .long 4b, 6b\n" \
  105. " .previous\n" \
  106. : "=r" (err), "=&r" (v), "=&r" (a) \
  107. : "0" (err), "1" (v), "2" (a)); \
  108. \
  109. if (err) \
  110. goto fault; \
  111. } while (0)
  112. /* sysctl hooks */
  113. int unaligned_enabled __read_mostly = 1; /* Enabled by default */
  114. int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */
  115. static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
  116. struct callee_regs *cregs)
  117. {
  118. int val;
  119. /* register write back */
  120. if ((state->aa == 1) || (state->aa == 2)) {
  121. set_reg(state->wb_reg, state->src1 + state->src2, regs, cregs);
  122. if (state->aa == 2)
  123. state->src2 = 0;
  124. }
  125. if (state->zz == 0) {
  126. get32_unaligned_check(val, state->src1 + state->src2);
  127. } else {
  128. get16_unaligned_check(val, state->src1 + state->src2);
  129. if (state->x)
  130. val = (val << 16) >> 16;
  131. }
  132. if (state->pref == 0)
  133. set_reg(state->dest, val, regs, cregs);
  134. return;
  135. fault: state->fault = 1;
  136. }
  137. static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
  138. struct callee_regs *cregs)
  139. {
  140. /* register write back */
  141. if ((state->aa == 1) || (state->aa == 2)) {
  142. set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs);
  143. if (state->aa == 3)
  144. state->src3 = 0;
  145. } else if (state->aa == 3) {
  146. if (state->zz == 2) {
  147. set_reg(state->wb_reg, state->src2 + (state->src3 << 1),
  148. regs, cregs);
  149. } else if (!state->zz) {
  150. set_reg(state->wb_reg, state->src2 + (state->src3 << 2),
  151. regs, cregs);
  152. } else {
  153. goto fault;
  154. }
  155. }
  156. /* write fix-up */
  157. if (!state->zz)
  158. put32_unaligned_check(state->src1, state->src2 + state->src3);
  159. else
  160. put16_unaligned_check(state->src1, state->src2 + state->src3);
  161. return;
  162. fault: state->fault = 1;
  163. }
  164. /*
  165. * Handle an unaligned access
  166. * Returns 0 if successfully handled, 1 if some error happened
  167. */
  168. int misaligned_fixup(unsigned long address, struct pt_regs *regs,
  169. unsigned long cause, struct callee_regs *cregs)
  170. {
  171. struct disasm_state state;
  172. char buf[TASK_COMM_LEN];
  173. /* handle user mode only and only if enabled by sysadmin */
  174. if (!user_mode(regs) || !unaligned_enabled)
  175. return 1;
  176. if (no_unaligned_warning) {
  177. pr_warn_once("%s(%d) made unaligned access which was emulated"
  178. " by kernel assist\n. This can degrade application"
  179. " performance significantly\n. To enable further"
  180. " logging of such instances, please \n"
  181. " echo 0 > /proc/sys/kernel/ignore-unaligned-usertrap\n",
  182. get_task_comm(buf, current), task_pid_nr(current));
  183. } else {
  184. /* Add rate limiting if it gets down to it */
  185. pr_warn("%s(%d): unaligned access to/from 0x%lx by PC: 0x%lx\n",
  186. get_task_comm(buf, current), task_pid_nr(current),
  187. address, regs->ret);
  188. }
  189. disasm_instr(regs->ret, &state, 1, regs, cregs);
  190. if (state.fault)
  191. goto fault;
  192. /* ldb/stb should not have unaligned exception */
  193. if ((state.zz == 1) || (state.di))
  194. goto fault;
  195. if (!state.write)
  196. fixup_load(&state, regs, cregs);
  197. else
  198. fixup_store(&state, regs, cregs);
  199. if (state.fault)
  200. goto fault;
  201. if (delay_mode(regs)) {
  202. regs->ret = regs->bta;
  203. regs->status32 &= ~STATUS_DE_MASK;
  204. } else {
  205. regs->ret += state.instr_len;
  206. }
  207. return 0;
  208. fault:
  209. pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n",
  210. state.words[0], address);
  211. return 1;
  212. }