cop2-ex.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * This file is subject to the terms and conditions of the GNU General Public
  3. * License. See the file "COPYING" in the main directory of this archive
  4. * for more details.
  5. *
  6. * Copyright (C) 2014 Lemote Corporation.
  7. * written by Huacai Chen <[email protected]>
  8. *
  9. * based on arch/mips/cavium-octeon/cpu.c
  10. * Copyright (C) 2009 Wind River Systems,
  11. * written by Ralf Baechle <[email protected]>
  12. */
  13. #include <linux/init.h>
  14. #include <linux/sched.h>
  15. #include <linux/notifier.h>
  16. #include <linux/ptrace.h>
  17. #include <linux/uaccess.h>
  18. #include <linux/sched/signal.h>
  19. #include <asm/fpu.h>
  20. #include <asm/cop2.h>
  21. #include <asm/inst.h>
  22. #include <asm/branch.h>
  23. #include <asm/current.h>
  24. #include <asm/mipsregs.h>
  25. #include <asm/unaligned-emul.h>
  26. static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
  27. void *data)
  28. {
  29. unsigned int res, fpu_owned;
  30. unsigned long ra, value, value_next;
  31. union mips_instruction insn;
  32. int fr = !test_thread_flag(TIF_32BIT_FPREGS);
  33. struct pt_regs *regs = (struct pt_regs *)data;
  34. void __user *addr = (void __user *)regs->cp0_badvaddr;
  35. unsigned int __user *pc = (unsigned int __user *)exception_epc(regs);
  36. ra = regs->regs[31];
  37. __get_user(insn.word, pc);
  38. switch (action) {
  39. case CU2_EXCEPTION:
  40. preempt_disable();
  41. fpu_owned = __is_fpu_owner();
  42. if (!fr)
  43. set_c0_status(ST0_CU1 | ST0_CU2);
  44. else
  45. set_c0_status(ST0_CU1 | ST0_CU2 | ST0_FR);
  46. enable_fpu_hazard();
  47. KSTK_STATUS(current) |= (ST0_CU1 | ST0_CU2);
  48. if (fr)
  49. KSTK_STATUS(current) |= ST0_FR;
  50. else
  51. KSTK_STATUS(current) &= ~ST0_FR;
  52. /* If FPU is owned, we needn't init or restore fp */
  53. if (!fpu_owned) {
  54. set_thread_flag(TIF_USEDFPU);
  55. init_fp_ctx(current);
  56. _restore_fp(current);
  57. }
  58. preempt_enable();
  59. return NOTIFY_STOP; /* Don't call default notifier */
  60. case CU2_LWC2_OP:
  61. if (insn.loongson3_lswc2_format.ls == 0)
  62. goto sigbus;
  63. if (insn.loongson3_lswc2_format.fr == 0) { /* gslq */
  64. if (!access_ok(addr, 16))
  65. goto sigbus;
  66. LoadDW(addr, value, res);
  67. if (res)
  68. goto fault;
  69. LoadDW(addr + 8, value_next, res);
  70. if (res)
  71. goto fault;
  72. regs->regs[insn.loongson3_lswc2_format.rt] = value;
  73. regs->regs[insn.loongson3_lswc2_format.rq] = value_next;
  74. compute_return_epc(regs);
  75. } else { /* gslqc1 */
  76. if (!access_ok(addr, 16))
  77. goto sigbus;
  78. lose_fpu(1);
  79. LoadDW(addr, value, res);
  80. if (res)
  81. goto fault;
  82. LoadDW(addr + 8, value_next, res);
  83. if (res)
  84. goto fault;
  85. set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0, value);
  86. set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0, value_next);
  87. compute_return_epc(regs);
  88. own_fpu(1);
  89. }
  90. return NOTIFY_STOP; /* Don't call default notifier */
  91. case CU2_SWC2_OP:
  92. if (insn.loongson3_lswc2_format.ls == 0)
  93. goto sigbus;
  94. if (insn.loongson3_lswc2_format.fr == 0) { /* gssq */
  95. if (!access_ok(addr, 16))
  96. goto sigbus;
  97. /* write upper 8 bytes first */
  98. value_next = regs->regs[insn.loongson3_lswc2_format.rq];
  99. StoreDW(addr + 8, value_next, res);
  100. if (res)
  101. goto fault;
  102. value = regs->regs[insn.loongson3_lswc2_format.rt];
  103. StoreDW(addr, value, res);
  104. if (res)
  105. goto fault;
  106. compute_return_epc(regs);
  107. } else { /* gssqc1 */
  108. if (!access_ok(addr, 16))
  109. goto sigbus;
  110. lose_fpu(1);
  111. value_next = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0);
  112. StoreDW(addr + 8, value_next, res);
  113. if (res)
  114. goto fault;
  115. value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0);
  116. StoreDW(addr, value, res);
  117. if (res)
  118. goto fault;
  119. compute_return_epc(regs);
  120. own_fpu(1);
  121. }
  122. return NOTIFY_STOP; /* Don't call default notifier */
  123. case CU2_LDC2_OP:
  124. switch (insn.loongson3_lsdc2_format.opcode1) {
  125. /*
  126. * Loongson-3 overridden ldc2 instructions.
  127. * opcode1 instruction
  128. * 0x1 gslhx: load 2 bytes to GPR
  129. * 0x2 gslwx: load 4 bytes to GPR
  130. * 0x3 gsldx: load 8 bytes to GPR
  131. * 0x6 gslwxc1: load 4 bytes to FPR
  132. * 0x7 gsldxc1: load 8 bytes to FPR
  133. */
  134. case 0x1:
  135. if (!access_ok(addr, 2))
  136. goto sigbus;
  137. LoadHW(addr, value, res);
  138. if (res)
  139. goto fault;
  140. compute_return_epc(regs);
  141. regs->regs[insn.loongson3_lsdc2_format.rt] = value;
  142. break;
  143. case 0x2:
  144. if (!access_ok(addr, 4))
  145. goto sigbus;
  146. LoadW(addr, value, res);
  147. if (res)
  148. goto fault;
  149. compute_return_epc(regs);
  150. regs->regs[insn.loongson3_lsdc2_format.rt] = value;
  151. break;
  152. case 0x3:
  153. if (!access_ok(addr, 8))
  154. goto sigbus;
  155. LoadDW(addr, value, res);
  156. if (res)
  157. goto fault;
  158. compute_return_epc(regs);
  159. regs->regs[insn.loongson3_lsdc2_format.rt] = value;
  160. break;
  161. case 0x6:
  162. die_if_kernel("Unaligned FP access in kernel code", regs);
  163. BUG_ON(!used_math());
  164. if (!access_ok(addr, 4))
  165. goto sigbus;
  166. lose_fpu(1);
  167. LoadW(addr, value, res);
  168. if (res)
  169. goto fault;
  170. set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
  171. compute_return_epc(regs);
  172. own_fpu(1);
  173. break;
  174. case 0x7:
  175. die_if_kernel("Unaligned FP access in kernel code", regs);
  176. BUG_ON(!used_math());
  177. if (!access_ok(addr, 8))
  178. goto sigbus;
  179. lose_fpu(1);
  180. LoadDW(addr, value, res);
  181. if (res)
  182. goto fault;
  183. set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
  184. compute_return_epc(regs);
  185. own_fpu(1);
  186. break;
  187. }
  188. return NOTIFY_STOP; /* Don't call default notifier */
  189. case CU2_SDC2_OP:
  190. switch (insn.loongson3_lsdc2_format.opcode1) {
  191. /*
  192. * Loongson-3 overridden sdc2 instructions.
  193. * opcode1 instruction
  194. * 0x1 gsshx: store 2 bytes from GPR
  195. * 0x2 gsswx: store 4 bytes from GPR
  196. * 0x3 gssdx: store 8 bytes from GPR
  197. * 0x6 gsswxc1: store 4 bytes from FPR
  198. * 0x7 gssdxc1: store 8 bytes from FPR
  199. */
  200. case 0x1:
  201. if (!access_ok(addr, 2))
  202. goto sigbus;
  203. compute_return_epc(regs);
  204. value = regs->regs[insn.loongson3_lsdc2_format.rt];
  205. StoreHW(addr, value, res);
  206. if (res)
  207. goto fault;
  208. break;
  209. case 0x2:
  210. if (!access_ok(addr, 4))
  211. goto sigbus;
  212. compute_return_epc(regs);
  213. value = regs->regs[insn.loongson3_lsdc2_format.rt];
  214. StoreW(addr, value, res);
  215. if (res)
  216. goto fault;
  217. break;
  218. case 0x3:
  219. if (!access_ok(addr, 8))
  220. goto sigbus;
  221. compute_return_epc(regs);
  222. value = regs->regs[insn.loongson3_lsdc2_format.rt];
  223. StoreDW(addr, value, res);
  224. if (res)
  225. goto fault;
  226. break;
  227. case 0x6:
  228. die_if_kernel("Unaligned FP access in kernel code", regs);
  229. BUG_ON(!used_math());
  230. if (!access_ok(addr, 4))
  231. goto sigbus;
  232. lose_fpu(1);
  233. value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
  234. StoreW(addr, value, res);
  235. if (res)
  236. goto fault;
  237. compute_return_epc(regs);
  238. own_fpu(1);
  239. break;
  240. case 0x7:
  241. die_if_kernel("Unaligned FP access in kernel code", regs);
  242. BUG_ON(!used_math());
  243. if (!access_ok(addr, 8))
  244. goto sigbus;
  245. lose_fpu(1);
  246. value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
  247. StoreDW(addr, value, res);
  248. if (res)
  249. goto fault;
  250. compute_return_epc(regs);
  251. own_fpu(1);
  252. break;
  253. }
  254. return NOTIFY_STOP; /* Don't call default notifier */
  255. }
  256. return NOTIFY_OK; /* Let default notifier send signals */
  257. fault:
  258. /* roll back jump/branch */
  259. regs->regs[31] = ra;
  260. regs->cp0_epc = (unsigned long)pc;
  261. /* Did we have an exception handler installed? */
  262. if (fixup_exception(regs))
  263. return NOTIFY_STOP; /* Don't call default notifier */
  264. die_if_kernel("Unhandled kernel unaligned access", regs);
  265. force_sig(SIGSEGV);
  266. return NOTIFY_STOP; /* Don't call default notifier */
  267. sigbus:
  268. die_if_kernel("Unhandled kernel unaligned access", regs);
  269. force_sig(SIGBUS);
  270. return NOTIFY_STOP; /* Don't call default notifier */
  271. }
  272. static int __init loongson_cu2_setup(void)
  273. {
  274. return cu2_notifier(loongson_cu2_call, 0);
  275. }
  276. early_initcall(loongson_cu2_setup);