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 if (cause >= 0) {
20 switch (cause) {
21 case RISCV_EXCEPTION_IADDR_MISALIGN:
22 return "Instruction address misaligned";
23 case RISCV_EXCEPTION_IACCESS_FAULT:
24 return "Instruction access fault";
25 case RISCV_EXCEPTION_ILLEGAL_INS:
26 return "Illegal instruction";
27 case RISCV_EXCEPTION_BREAKPOINT:
28 return "Breakpoint";
29 case RISCV_EXCEPTION_LOAD_ADDR_MISALIGN:
30 return "Load address misaligned";
31 case RISCV_EXCEPTION_LOAD_ACCESS_FAULT:
32 return "Load access fault";
33 case RISCV_EXCEPTION_STORE_ADDR_MISALIGN:
34 return "Store/AMO address misaligned";
35 case RISCV_EXCEPTION_STORE_ACCESS_FAULT:
36 return "Store/AMO access fault";
37 case RISCV_EXCEPTION_ENV_CALL_U_MODE:
38 return "Environment call from U-mode";
39 case RISCV_EXCEPTION_ENV_CALL_S_MODE:
40 return "Environment call from S-mode";
41 case RISCV_EXCEPTION_ENV_CALL_M_MODE:
42 return "Environment call from M-mode";
43 case RISCV_EXCEPTION_INS_PAGE_FAULT:
44 return "Instruction page fault";
45 case RISCV_EXCEPTION_LOAD_PAGE_FAULT:
46 return "Load page fault";
47 case RISCV_EXCEPTION_STORE_PAGE_FAULT:
48 return "Store/AMO page fault";
49 }
50 return "Unknown exception";
51 } else {
52 switch (cause & LONG_MAX) {
53 case RISCV_INTERRUPT_USWI:
54 return "User software interrupt";
55 case RISCV_INTERRUPT_SSWI:
56 return "Supervisor software interrupt";
57 case RISCV_INTERRUPT_MSWI:
58 return "Machine software interrupt";
59 case RISCV_INTERRUPT_UTIM:
60 return "User timer interrupt";
61 case RISCV_INTERRUPT_STIM:
62 return "Supervisor timer interrupt";
63 case RISCV_INTERRUPT_MTIM:
64 return "Machine timer interrupt";
65 case RISCV_INTERRUPT_UEXT:
66 return "User external interrupt";
67 case RISCV_INTERRUPT_SEXT:
68 return "Supervisor external interrupt";
69 case RISCV_INTERRUPT_MEXT:
70 return "Machine external interrupt";
71 }
72 return "Unknown interrupt";
73 }
74 }
75
dump_iframe(struct riscv_short_iframe * frame,bool kernel)76 static void dump_iframe(struct riscv_short_iframe *frame, bool kernel) {
77 printf("a0 %#16lx a1 %#16lx a2 %#16lx a3 %#16lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
78 printf("a4 %#16lx a5 %#16lx a6 %#16lx a7 %#16lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
79 printf("t0 %#16lx t1 %#16lx t2 %#16lx t3 %#16lx\n", frame->t0, frame->t1, frame->t2, frame->t3);
80 printf("t5 %#16lx t6 %#16lx\n", frame->t5, frame->t6);
81 if (!kernel) {
82 printf("gp %#16lx tp %#16lx sp %#lx\n", frame->gp, frame->tp, frame->sp);
83 }
84 }
85
86 __NO_RETURN __NO_INLINE
fatal_exception(long cause,ulong epc,struct riscv_short_iframe * frame,bool kernel)87 static void fatal_exception(long cause, ulong epc, struct riscv_short_iframe *frame, bool kernel) {
88 if (cause < 0) {
89 printf("unhandled interrupt cause %#lx (%s), epc %#lx, tval %#lx\n", cause,
90 cause_to_string(cause), epc, riscv_csr_read(RISCV_CSR_XTVAL));
91 } else {
92 printf("unhandled exception cause %#lx (%s), epc %#lx, tval %#lx\n", cause,
93 cause_to_string(cause), epc, riscv_csr_read(RISCV_CSR_XTVAL));
94 }
95
96 dump_iframe(frame, kernel);
97 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
98 }
99
100 // weak reference, can override this somewhere else
101 __WEAK
riscv_syscall_handler(struct riscv_short_iframe * frame)102 void riscv_syscall_handler(struct riscv_short_iframe *frame) {
103 printf("unhandled syscall handler\n");
104 dump_iframe(frame, false);
105 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
106 }
107
108 // called from assembly
109 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)110 void riscv_exception_handler(long cause, ulong epc, struct riscv_short_iframe *frame, bool kernel) {
111 LTRACEF("hart %u cause %#lx epc %#lx status %#lx kernel %d\n",
112 riscv_current_hart(), cause, epc, frame->status, kernel);
113
114 enum handler_return ret = INT_NO_RESCHEDULE;
115
116 // top bit of the cause register determines if it's an interrupt or not
117 if (cause < 0) {
118 switch (cause & LONG_MAX) {
119 #if WITH_SMP
120 case RISCV_INTERRUPT_XSWI: // machine software interrupt
121 ret = riscv_software_exception();
122 break;
123 #endif
124 case RISCV_INTERRUPT_XTIM: // machine timer interrupt
125 ret = riscv_timer_exception();
126 break;
127 case RISCV_INTERRUPT_XEXT: // machine external interrupt
128 ret = riscv_platform_irq();
129 break;
130 default:
131 fatal_exception(cause, epc, frame, kernel);
132 }
133 } else {
134 // all synchronous traps go here
135 switch (cause) {
136 case RISCV_EXCEPTION_ENV_CALL_U_MODE: // ecall from user mode
137 riscv_syscall_handler(frame);
138 break;
139 default:
140 fatal_exception(cause, epc, frame, kernel);
141 }
142 }
143
144 if (ret == INT_RESCHEDULE) {
145 thread_preempt();
146 }
147 }
148