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