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