sbi_trap.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2019 Western Digital Corporation or its affiliates.
  5. *
  6. * Authors:
  7. * Anup Patel <anup.patel@wdc.com>
  8. */
  9. #include <sbi/riscv_asm.h>
  10. #include <sbi/riscv_encoding.h>
  11. #include <sbi/sbi_console.h>
  12. #include <sbi/sbi_ecall.h>
  13. #include <sbi/sbi_error.h>
  14. #include <sbi/sbi_hart.h>
  15. #include <sbi/sbi_illegal_insn.h>
  16. #include <sbi/sbi_ipi.h>
  17. #include <sbi/sbi_misaligned_ldst.h>
  18. #include <sbi/sbi_scratch.h>
  19. #include <sbi/sbi_timer.h>
  20. #include <sbi/sbi_trap.h>
  21. static void __noreturn sbi_trap_error(const char *msg, int rc,
  22. ulong mcause, ulong mtval, ulong mtval2,
  23. ulong mtinst, struct sbi_trap_regs *regs)
  24. {
  25. u32 hartid = current_hartid();
  26. sbi_printf("%s: hart%d: %s (error %d)\n", __func__, hartid, msg, rc);
  27. sbi_printf("%s: hart%d: mcause=0x%" PRILX " mtval=0x%" PRILX "\n",
  28. __func__, hartid, mcause, mtval);
  29. if (misa_extension('H')) {
  30. sbi_printf("%s: hart%d: mtval2=0x%" PRILX
  31. " mtinst=0x%" PRILX "\n",
  32. __func__, hartid, mtval2, mtinst);
  33. }
  34. sbi_printf("%s: hart%d: mepc=0x%" PRILX " mstatus=0x%" PRILX "\n",
  35. __func__, hartid, regs->mepc, regs->mstatus);
  36. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  37. hartid, "ra", regs->ra, "sp", regs->sp);
  38. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  39. hartid, "gp", regs->gp, "tp", regs->tp);
  40. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  41. hartid, "s0", regs->s0, "s1", regs->s1);
  42. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  43. hartid, "a0", regs->a0, "a1", regs->a1);
  44. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  45. hartid, "a2", regs->a2, "a3", regs->a3);
  46. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  47. hartid, "a4", regs->a4, "a5", regs->a5);
  48. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  49. hartid, "a6", regs->a6, "a7", regs->a7);
  50. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  51. hartid, "s2", regs->s2, "s3", regs->s3);
  52. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  53. hartid, "s4", regs->s4, "s5", regs->s5);
  54. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  55. hartid, "s6", regs->s6, "s7", regs->s7);
  56. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  57. hartid, "s8", regs->s8, "s9", regs->s9);
  58. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  59. hartid, "s10", regs->s10, "s11", regs->s11);
  60. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  61. hartid, "t0", regs->t0, "t1", regs->t1);
  62. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  63. hartid, "t2", regs->t2, "t3", regs->t3);
  64. sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
  65. hartid, "t4", regs->t4, "t5", regs->t5);
  66. sbi_printf("%s: hart%d: %s=0x%" PRILX "\n", __func__, hartid, "t6",
  67. regs->t6);
  68. sbi_hart_hang();
  69. }
  70. /**
  71. * Redirect trap to lower privledge mode (S-mode or U-mode)
  72. *
  73. * @param regs pointer to register state
  74. * @param trap pointer to trap details
  75. *
  76. * @return 0 on success and negative error code on failure
  77. */
  78. int sbi_trap_redirect(struct sbi_trap_regs *regs,
  79. struct sbi_trap_info *trap)
  80. {
  81. ulong hstatus, vsstatus, prev_mode;
  82. #if __riscv_xlen == 32
  83. bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
  84. #else
  85. bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
  86. #endif
  87. /* By default, we redirect to HS-mode */
  88. bool next_virt = FALSE;
  89. /* Sanity check on previous mode */
  90. prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
  91. if (prev_mode != PRV_S && prev_mode != PRV_U)
  92. return SBI_ENOTSUPP;
  93. /* For certain exceptions from VS/VU-mode we redirect to VS-mode */
  94. if (misa_extension('H') && prev_virt) {
  95. switch (trap->cause) {
  96. case CAUSE_FETCH_PAGE_FAULT:
  97. case CAUSE_LOAD_PAGE_FAULT:
  98. case CAUSE_STORE_PAGE_FAULT:
  99. next_virt = TRUE;
  100. break;
  101. default:
  102. break;
  103. };
  104. }
  105. /* Update MSTATUS MPV bits */
  106. #if __riscv_xlen == 32
  107. regs->mstatusH &= ~MSTATUSH_MPV;
  108. regs->mstatusH |= (next_virt) ? MSTATUSH_MPV : 0UL;
  109. #else
  110. regs->mstatus &= ~MSTATUS_MPV;
  111. regs->mstatus |= (next_virt) ? MSTATUS_MPV : 0UL;
  112. #endif
  113. /* Update HSTATUS for VS/VU-mode to HS-mode transition */
  114. if (misa_extension('H') && prev_virt && !next_virt) {
  115. /* Update HSTATUS SPVP and SPV bits */
  116. hstatus = csr_read(CSR_HSTATUS);
  117. hstatus &= ~HSTATUS_SPVP;
  118. hstatus |= (prev_mode == PRV_S) ? HSTATUS_SPVP : 0;
  119. hstatus &= ~HSTATUS_SPV;
  120. hstatus |= (prev_virt) ? HSTATUS_SPV : 0;
  121. csr_write(CSR_HSTATUS, hstatus);
  122. csr_write(CSR_HTVAL, trap->tval2);
  123. csr_write(CSR_HTINST, trap->tinst);
  124. }
  125. /* Update exception related CSRs */
  126. if (next_virt) {
  127. /* Update VS-mode exception info */
  128. csr_write(CSR_VSTVAL, trap->tval);
  129. csr_write(CSR_VSEPC, trap->epc);
  130. csr_write(CSR_VSCAUSE, trap->cause);
  131. /* Set MEPC to VS-mode exception vector base */
  132. regs->mepc = csr_read(CSR_VSTVEC);
  133. /* Set MPP to VS-mode */
  134. regs->mstatus &= ~MSTATUS_MPP;
  135. regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
  136. /* Get VS-mode SSTATUS CSR */
  137. vsstatus = csr_read(CSR_VSSTATUS);
  138. /* Set SPP for VS-mode */
  139. vsstatus &= ~SSTATUS_SPP;
  140. if (prev_mode == PRV_S)
  141. vsstatus |= (1UL << SSTATUS_SPP_SHIFT);
  142. /* Set SPIE for VS-mode */
  143. vsstatus &= ~SSTATUS_SPIE;
  144. if (vsstatus & SSTATUS_SIE)
  145. vsstatus |= (1UL << SSTATUS_SPIE_SHIFT);
  146. /* Clear SIE for VS-mode */
  147. vsstatus &= ~SSTATUS_SIE;
  148. /* Update VS-mode SSTATUS CSR */
  149. csr_write(CSR_VSSTATUS, vsstatus);
  150. } else {
  151. /* Update S-mode exception info */
  152. csr_write(CSR_STVAL, trap->tval);
  153. csr_write(CSR_SEPC, trap->epc);
  154. csr_write(CSR_SCAUSE, trap->cause);
  155. /* Set MEPC to S-mode exception vector base */
  156. regs->mepc = csr_read(CSR_STVEC);
  157. /* Set MPP to S-mode */
  158. regs->mstatus &= ~MSTATUS_MPP;
  159. regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
  160. /* Set SPP for S-mode */
  161. regs->mstatus &= ~MSTATUS_SPP;
  162. if (prev_mode == PRV_S)
  163. regs->mstatus |= (1UL << MSTATUS_SPP_SHIFT);
  164. /* Set SPIE for S-mode */
  165. regs->mstatus &= ~MSTATUS_SPIE;
  166. if (regs->mstatus & MSTATUS_SIE)
  167. regs->mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
  168. /* Clear SIE for S-mode */
  169. regs->mstatus &= ~MSTATUS_SIE;
  170. }
  171. return 0;
  172. }
  173. /**
  174. * Handle trap/interrupt
  175. *
  176. * This function is called by firmware linked to OpenSBI
  177. * library for handling trap/interrupt. It expects the
  178. * following:
  179. * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART
  180. * 2. The 'mcause' CSR is having exception/interrupt cause
  181. * 3. The 'mtval' CSR is having additional trap information
  182. * 4. The 'mtval2' CSR is having additional trap information
  183. * 5. The 'mtinst' CSR is having decoded trap instruction
  184. * 6. Stack pointer (SP) is setup for current HART
  185. * 7. Interrupts are disabled in MSTATUS CSR
  186. *
  187. * @param regs pointer to register state
  188. */
  189. struct sbi_trap_regs *sbi_trap_handler(struct sbi_trap_regs *regs)
  190. {
  191. int rc = SBI_ENOTSUPP;
  192. const char *msg = "trap handler failed";
  193. ulong mcause = csr_read(CSR_MCAUSE);
  194. ulong mtval = csr_read(CSR_MTVAL), mtval2 = 0, mtinst = 0;
  195. struct sbi_trap_info trap;
  196. if (misa_extension('H')) {
  197. mtval2 = csr_read(CSR_MTVAL2);
  198. mtinst = csr_read(CSR_MTINST);
  199. }
  200. if (mcause & (1UL << (__riscv_xlen - 1))) {
  201. mcause &= ~(1UL << (__riscv_xlen - 1));
  202. switch (mcause) {
  203. case IRQ_M_TIMER:
  204. sbi_timer_process();
  205. break;
  206. case IRQ_M_SOFT:
  207. sbi_ipi_process();
  208. break;
  209. default:
  210. msg = "unhandled external interrupt";
  211. goto trap_error;
  212. };
  213. return regs;
  214. }
  215. switch (mcause) {
  216. case CAUSE_ILLEGAL_INSTRUCTION:
  217. rc = sbi_illegal_insn_handler(mtval, regs);
  218. msg = "illegal instruction handler failed";
  219. break;
  220. case CAUSE_MISALIGNED_LOAD:
  221. rc = sbi_misaligned_load_handler(mtval, mtval2, mtinst, regs);
  222. msg = "misaligned load handler failed";
  223. break;
  224. case CAUSE_MISALIGNED_STORE:
  225. rc = sbi_misaligned_store_handler(mtval, mtval2, mtinst, regs);
  226. msg = "misaligned store handler failed";
  227. break;
  228. case CAUSE_SUPERVISOR_ECALL:
  229. case CAUSE_MACHINE_ECALL:
  230. rc = sbi_ecall_handler(regs);
  231. msg = "ecall handler failed";
  232. break;
  233. default:
  234. /* If the trap came from S or U mode, redirect it there */
  235. trap.epc = regs->mepc;
  236. trap.cause = mcause;
  237. trap.tval = mtval;
  238. trap.tval2 = mtval2;
  239. trap.tinst = mtinst;
  240. rc = sbi_trap_redirect(regs, &trap);
  241. break;
  242. };
  243. trap_error:
  244. if (rc)
  245. sbi_trap_error(msg, rc, mcause, mtval, mtval2, mtinst, regs);
  246. return regs;
  247. }
  248. typedef void (*trap_exit_t)(const struct sbi_trap_regs *regs);
  249. /**
  250. * Exit trap/interrupt handling
  251. *
  252. * This function is called by non-firmware code to abruptly exit
  253. * trap/interrupt handling and resume execution at context pointed
  254. * by given register state.
  255. *
  256. * @param regs pointer to register state
  257. */
  258. void __noreturn sbi_trap_exit(const struct sbi_trap_regs *regs)
  259. {
  260. struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
  261. ((trap_exit_t)scratch->trap_exit)(regs);
  262. __builtin_unreachable();
  263. }