sbi_trap.c 9.0 KB

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