1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <assert.h>
9 #include <lk/compiler.h>
10 #include <lk/trace.h>
11 #include <arch/riscv.h>
12 #include <kernel/thread.h>
13 #include <platform.h>
14 #include <arch/riscv/iframe.h>
15
16 #define LOCAL_TRACE 0
17
cause_to_string(long cause)18 static const char *cause_to_string(long cause) {
19 switch (cause) {
20 case RISCV_EXCEPTION_IADDR_MISALIGN:
21 return "Instruction address misaligned";
22 case RISCV_EXCEPTION_IACCESS_FAULT:
23 return "Instruction access fault";
24 case RISCV_EXCEPTION_ILLEGAL_INS:
25 return "Illegal instruction";
26 case RISCV_EXCEPTION_BREAKPOINT:
27 return "Breakpoint";
28 case RISCV_EXCEPTION_LOAD_ADDR_MISALIGN:
29 return "Load address misaligned";
30 case RISCV_EXCEPTION_LOAD_ACCESS_FAULT:
31 return "Load access fault";
32 case RISCV_EXCEPTION_STORE_ADDR_MISALIGN:
33 return "Store/AMO address misaligned";
34 case RISCV_EXCEPTION_STORE_ACCESS_FAULT:
35 return "Store/AMO access fault";
36 case RISCV_EXCEPTION_ENV_CALL_U_MODE:
37 return "Environment call from U-mode";
38 case RISCV_EXCEPTION_ENV_CALL_S_MODE:
39 return "Environment call from S-mode";
40 case RISCV_EXCEPTION_ENV_CALL_M_MODE:
41 return "Environment call from M-mode";
42 case RISCV_EXCEPTION_INS_PAGE_FAULT:
43 return "Instruction page fault";
44 case RISCV_EXCEPTION_LOAD_PAGE_FAULT:
45 return "Load page fault";
46 case RISCV_EXCEPTION_STORE_PAGE_FAULT:
47 return "Store/AMO page fault";
48 }
49 return "Unknown";
50 }
51
dump_iframe(struct riscv_short_iframe * frame,bool kernel)52 static void dump_iframe(struct riscv_short_iframe *frame, bool kernel) {
53 printf("a0 %#16lx a1 %#16lx a2 %#16lx a3 %#16lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
54 printf("a4 %#16lx a5 %#16lx a6 %#16lx a7 %#16lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
55 printf("t0 %#16lx t1 %#16lx t2 %#16lx t3 %#16lx\n", frame->t0, frame->t1, frame->t2, frame->t3);
56 printf("t5 %#16lx t6 %#16lx\n", frame->t5, frame->t6);
57 if (!kernel) {
58 printf("gp %#16lx tp %#16lx sp %#lx\n", frame->gp, frame->tp, frame->sp);
59 }
60 }
61
62 __NO_RETURN __NO_INLINE
fatal_exception(long cause,ulong epc,struct riscv_short_iframe * frame,bool kernel)63 static void fatal_exception(long cause, ulong epc, struct riscv_short_iframe *frame, bool kernel) {
64 if (cause < 0) {
65 printf("unhandled interrupt cause %#lx, epc %#lx, tval %#lx\n", cause, epc,
66 riscv_csr_read(RISCV_CSR_XTVAL));
67 } else {
68 printf("unhandled exception cause %#lx (%s), epc %#lx, tval %#lx\n", cause,
69 cause_to_string(cause), epc, riscv_csr_read(RISCV_CSR_XTVAL));
70 }
71
72 dump_iframe(frame, kernel);
73 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
74 }
75
76 // weak reference, can override this somewhere else
77 __WEAK
riscv_syscall_handler(struct riscv_short_iframe * frame)78 void riscv_syscall_handler(struct riscv_short_iframe *frame) {
79 printf("unhandled syscall handler\n");
80 dump_iframe(frame, false);
81 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
82 }
83
84 // called from assembly
85 void riscv_exception_handler(long cause, ulong epc, struct riscv_short_iframe *frame, bool kernel);
riscv_exception_handler(long cause,ulong epc,struct riscv_short_iframe * frame,bool kernel)86 void riscv_exception_handler(long cause, ulong epc, struct riscv_short_iframe *frame, bool kernel) {
87 LTRACEF("hart %u cause %#lx epc %#lx status %#lx kernel %d\n",
88 riscv_current_hart(), cause, epc, frame->status, kernel);
89
90 enum handler_return ret = INT_NO_RESCHEDULE;
91
92 // top bit of the cause register determines if it's an interrupt or not
93 if (cause < 0) {
94 switch (cause & LONG_MAX) {
95 #if WITH_SMP
96 case RISCV_INTERRUPT_XSWI: // machine software interrupt
97 ret = riscv_software_exception();
98 break;
99 #endif
100 case RISCV_INTERRUPT_XTIM: // machine timer interrupt
101 ret = riscv_timer_exception();
102 break;
103 case RISCV_INTERRUPT_XEXT: // machine external interrupt
104 ret = riscv_platform_irq();
105 break;
106 default:
107 fatal_exception(cause, epc, frame, kernel);
108 }
109 } else {
110 // all synchronous traps go here
111 switch (cause) {
112 case RISCV_EXCEPTION_ENV_CALL_U_MODE: // ecall from user mode
113 riscv_syscall_handler(frame);
114 break;
115 default:
116 fatal_exception(cause, epc, frame, kernel);
117 }
118 }
119
120 if (ret == INT_RESCHEDULE) {
121 thread_preempt();
122 }
123 }
124