alignment.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
  3. #include <linux/kernel.h>
  4. #include <linux/uaccess.h>
  5. #include <linux/ptrace.h>
  6. static int align_enable = 1;
  7. static int align_count;
  8. static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx)
  9. {
  10. return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx);
  11. }
  12. static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val)
  13. {
  14. if (rx == 15)
  15. regs->lr = val;
  16. else
  17. *((uint32_t *)&(regs->a0) - 2 + rx) = val;
  18. }
  19. /*
  20. * Get byte-value from addr and set it to *valp.
  21. *
  22. * Success: return 0
  23. * Failure: return 1
  24. */
  25. static int ldb_asm(uint32_t addr, uint32_t *valp)
  26. {
  27. uint32_t val;
  28. int err;
  29. if (!access_ok(VERIFY_READ, (void *)addr, 1))
  30. return 1;
  31. asm volatile (
  32. "movi %0, 0\n"
  33. "1:\n"
  34. "ldb %1, (%2)\n"
  35. "br 3f\n"
  36. "2:\n"
  37. "movi %0, 1\n"
  38. "br 3f\n"
  39. ".section __ex_table,\"a\"\n"
  40. ".align 2\n"
  41. ".long 1b, 2b\n"
  42. ".previous\n"
  43. "3:\n"
  44. : "=&r"(err), "=r"(val)
  45. : "r" (addr)
  46. );
  47. *valp = val;
  48. return err;
  49. }
  50. /*
  51. * Put byte-value to addr.
  52. *
  53. * Success: return 0
  54. * Failure: return 1
  55. */
  56. static int stb_asm(uint32_t addr, uint32_t val)
  57. {
  58. int err;
  59. if (!access_ok(VERIFY_WRITE, (void *)addr, 1))
  60. return 1;
  61. asm volatile (
  62. "movi %0, 0\n"
  63. "1:\n"
  64. "stb %1, (%2)\n"
  65. "br 3f\n"
  66. "2:\n"
  67. "movi %0, 1\n"
  68. "br 3f\n"
  69. ".section __ex_table,\"a\"\n"
  70. ".align 2\n"
  71. ".long 1b, 2b\n"
  72. ".previous\n"
  73. "3:\n"
  74. : "=&r"(err)
  75. : "r"(val), "r" (addr)
  76. );
  77. return err;
  78. }
  79. /*
  80. * Get half-word from [rx + imm]
  81. *
  82. * Success: return 0
  83. * Failure: return 1
  84. */
  85. static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
  86. {
  87. uint32_t byte0, byte1;
  88. if (ldb_asm(addr, &byte0))
  89. return 1;
  90. addr += 1;
  91. if (ldb_asm(addr, &byte1))
  92. return 1;
  93. byte0 |= byte1 << 8;
  94. put_ptreg(regs, rz, byte0);
  95. return 0;
  96. }
  97. /*
  98. * Store half-word to [rx + imm]
  99. *
  100. * Success: return 0
  101. * Failure: return 1
  102. */
  103. static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
  104. {
  105. uint32_t byte0, byte1;
  106. byte0 = byte1 = get_ptreg(regs, rz);
  107. byte0 &= 0xff;
  108. if (stb_asm(addr, byte0))
  109. return 1;
  110. addr += 1;
  111. byte1 = (byte1 >> 8) & 0xff;
  112. if (stb_asm(addr, byte1))
  113. return 1;
  114. return 0;
  115. }
  116. /*
  117. * Get word from [rx + imm]
  118. *
  119. * Success: return 0
  120. * Failure: return 1
  121. */
  122. static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
  123. {
  124. uint32_t byte0, byte1, byte2, byte3;
  125. if (ldb_asm(addr, &byte0))
  126. return 1;
  127. addr += 1;
  128. if (ldb_asm(addr, &byte1))
  129. return 1;
  130. addr += 1;
  131. if (ldb_asm(addr, &byte2))
  132. return 1;
  133. addr += 1;
  134. if (ldb_asm(addr, &byte3))
  135. return 1;
  136. byte0 |= byte1 << 8;
  137. byte0 |= byte2 << 16;
  138. byte0 |= byte3 << 24;
  139. put_ptreg(regs, rz, byte0);
  140. return 0;
  141. }
  142. /*
  143. * Store word to [rx + imm]
  144. *
  145. * Success: return 0
  146. * Failure: return 1
  147. */
  148. static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
  149. {
  150. uint32_t byte0, byte1, byte2, byte3;
  151. byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz);
  152. byte0 &= 0xff;
  153. if (stb_asm(addr, byte0))
  154. return 1;
  155. addr += 1;
  156. byte1 = (byte1 >> 8) & 0xff;
  157. if (stb_asm(addr, byte1))
  158. return 1;
  159. addr += 1;
  160. byte2 = (byte2 >> 16) & 0xff;
  161. if (stb_asm(addr, byte2))
  162. return 1;
  163. addr += 1;
  164. byte3 = (byte3 >> 24) & 0xff;
  165. if (stb_asm(addr, byte3))
  166. return 1;
  167. align_count++;
  168. return 0;
  169. }
  170. extern int fixup_exception(struct pt_regs *regs);
  171. #define OP_LDH 0xc000
  172. #define OP_STH 0xd000
  173. #define OP_LDW 0x8000
  174. #define OP_STW 0x9000
  175. void csky_alignment(struct pt_regs *regs)
  176. {
  177. int ret;
  178. uint16_t tmp;
  179. uint32_t opcode = 0;
  180. uint32_t rx = 0;
  181. uint32_t rz = 0;
  182. uint32_t imm = 0;
  183. uint32_t addr = 0;
  184. if (!user_mode(regs))
  185. goto bad_area;
  186. ret = get_user(tmp, (uint16_t *)instruction_pointer(regs));
  187. if (ret) {
  188. pr_err("%s get_user failed.\n", __func__);
  189. goto bad_area;
  190. }
  191. opcode = (uint32_t)tmp;
  192. rx = opcode & 0xf;
  193. imm = (opcode >> 4) & 0xf;
  194. rz = (opcode >> 8) & 0xf;
  195. opcode &= 0xf000;
  196. if (rx == 0 || rx == 1 || rz == 0 || rz == 1)
  197. goto bad_area;
  198. switch (opcode) {
  199. case OP_LDH:
  200. addr = get_ptreg(regs, rx) + (imm << 1);
  201. ret = ldh_c(regs, rz, addr);
  202. break;
  203. case OP_LDW:
  204. addr = get_ptreg(regs, rx) + (imm << 2);
  205. ret = ldw_c(regs, rz, addr);
  206. break;
  207. case OP_STH:
  208. addr = get_ptreg(regs, rx) + (imm << 1);
  209. ret = sth_c(regs, rz, addr);
  210. break;
  211. case OP_STW:
  212. addr = get_ptreg(regs, rx) + (imm << 2);
  213. ret = stw_c(regs, rz, addr);
  214. break;
  215. }
  216. if (ret)
  217. goto bad_area;
  218. regs->pc += 2;
  219. return;
  220. bad_area:
  221. if (!user_mode(regs)) {
  222. if (fixup_exception(regs))
  223. return;
  224. bust_spinlocks(1);
  225. pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n",
  226. __func__, opcode, rz, rx, imm, addr);
  227. show_regs(regs);
  228. bust_spinlocks(0);
  229. do_exit(SIGKILL);
  230. }
  231. force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr, current);
  232. }
  233. static struct ctl_table alignment_tbl[4] = {
  234. {
  235. .procname = "enable",
  236. .data = &align_enable,
  237. .maxlen = sizeof(align_enable),
  238. .mode = 0666,
  239. .proc_handler = &proc_dointvec
  240. },
  241. {
  242. .procname = "count",
  243. .data = &align_count,
  244. .maxlen = sizeof(align_count),
  245. .mode = 0666,
  246. .proc_handler = &proc_dointvec
  247. },
  248. {}
  249. };
  250. static struct ctl_table sysctl_table[2] = {
  251. {
  252. .procname = "csky_alignment",
  253. .mode = 0555,
  254. .child = alignment_tbl},
  255. {}
  256. };
  257. static struct ctl_path sysctl_path[2] = {
  258. {.procname = "csky"},
  259. {}
  260. };
  261. static int __init csky_alignment_init(void)
  262. {
  263. register_sysctl_paths(sysctl_path, sysctl_table);
  264. return 0;
  265. }
  266. arch_initcall(csky_alignment_init);