1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * Early initialization code for riscv
4 */
5
6 #include <arch/encoding.h>
7 #include <arch/exception.h>
8 #include <console/console.h>
9 #include <vm.h>
10 #include <mcall.h>
11 #include <sbi.h>
12 #include <types.h>
13
14 static const char *const exception_names[] = {
15 "Instruction address misaligned",
16 "Instruction access fault",
17 "Illegal instruction",
18 "Breakpoint",
19 "Load address misaligned",
20 "Load access fault",
21 "Store address misaligned",
22 "Store access fault",
23 "Environment call from U-mode",
24 "Environment call from S-mode",
25 "Reserved (10)",
26 "Environment call from M-mode",
27 "Instruction page fault",
28 "Load page fault",
29 "Reserved (14)",
30 "Store page fault",
31 };
32
mstatus_to_previous_mode(uintptr_t ms)33 static const char *mstatus_to_previous_mode(uintptr_t ms)
34 {
35 switch (ms & MSTATUS_MPP) {
36 case 0x00000000:
37 return "user";
38 case 0x00000800:
39 return "supervisor";
40 case 0x00001000:
41 return "hypervisor";
42 case 0x00001800:
43 return "machine";
44 }
45
46 return "unknown";
47 }
48
print_trap_information(const struct trapframe * tf)49 static void print_trap_information(const struct trapframe *tf)
50 {
51 const char *previous_mode;
52 bool mprv = !!(tf->status & MSTATUS_MPRV);
53 int hart_id = read_csr(mhartid);
54
55 /* Leave some space around the trap message */
56 printk(BIOS_DEBUG, "\n");
57
58 if (tf->cause < ARRAY_SIZE(exception_names))
59 printk(BIOS_DEBUG, "Exception: %s\n", exception_names[tf->cause]);
60 else
61 printk(BIOS_DEBUG, "Trap: Unknown cause %p\n", (void *)tf->cause);
62
63 previous_mode = mstatus_to_previous_mode(read_csr(mstatus));
64 printk(BIOS_DEBUG, "Hart ID: %d\n", hart_id);
65 printk(BIOS_DEBUG, "Previous mode: %s%s\n", previous_mode, mprv ? " (MPRV)" : "");
66 printk(BIOS_DEBUG, "Bad instruction pc: %p\n", (void *)tf->epc);
67 printk(BIOS_DEBUG, "Bad address: %p\n", (void *)tf->badvaddr);
68 printk(BIOS_DEBUG, "Stored ra: %p\n", (void *)tf->gpr[1]);
69 printk(BIOS_DEBUG, "Stored sp: %p\n", (void *)tf->gpr[2]);
70 }
71
interrupt_handler(struct trapframe * tf)72 static void interrupt_handler(struct trapframe *tf)
73 {
74 uint64_t cause = tf->cause & ~0x8000000000000000ULL;
75
76 switch (cause) {
77 case IRQ_M_TIMER:
78 /*
79 * Set interrupt pending for supervisor mode and disable timer
80 * interrupt in machine mode.
81 * To receive another timer interrupt just set timecmp and
82 * enable machine mode timer interrupt again.
83 */
84
85 clear_csr(mie, MIP_MTIP);
86 set_csr(mip, MIP_STIP);
87
88 break;
89 case IRQ_M_SOFT:
90 if (HLS()->ipi_pending & IPI_SOFT) {
91 set_csr(mip, MIP_SSIP);
92 } else if (HLS()->ipi_pending & IPI_FENCE_I) {
93 asm volatile("fence.i");
94 } else if (HLS()->ipi_pending & IPI_SFENCE_VMA) {
95 asm volatile("sfence.vma");
96 } else if (HLS()->ipi_pending & IPI_SFENCE_VMA_ASID) {
97 asm volatile("sfence.vma");
98 } else if (HLS()->ipi_pending & IPI_SHUTDOWN) {
99 while (HLS()->ipi_pending & IPI_SHUTDOWN)
100 asm volatile("wfi");
101 }
102 break;
103 default:
104 printk(BIOS_EMERG, "======================================\n");
105 printk(BIOS_EMERG, "coreboot: Unknown machine interrupt: 0x%llx\n", cause);
106 printk(BIOS_EMERG, "======================================\n");
107 print_trap_information(tf);
108 break;
109 }
110 }
111
112 void (*trap_handler)(struct trapframe *tf) = default_trap_handler;
113
default_trap_handler(struct trapframe * tf)114 void default_trap_handler(struct trapframe *tf)
115 {
116 if (tf->cause & 0x8000000000000000ULL) {
117 interrupt_handler(tf);
118 return;
119 }
120
121 switch (tf->cause) {
122 case CAUSE_FETCH_ACCESS:
123 case CAUSE_ILLEGAL_INSTRUCTION:
124 case CAUSE_BREAKPOINT:
125 case CAUSE_LOAD_ACCESS:
126 case CAUSE_STORE_ACCESS:
127 case CAUSE_USER_ECALL:
128 case CAUSE_HYPERVISOR_ECALL:
129 case CAUSE_MACHINE_ECALL:
130 print_trap_information(tf);
131 break;
132 case CAUSE_SUPERVISOR_ECALL:
133 handle_sbi(tf);
134 return;
135 case CAUSE_MISALIGNED_FETCH:
136 case CAUSE_MISALIGNED_LOAD:
137 case CAUSE_MISALIGNED_STORE:
138 print_trap_information(tf);
139 return;
140 default:
141 printk(BIOS_EMERG, "================================\n");
142 printk(BIOS_EMERG, "coreboot: can not handle a trap:\n");
143 printk(BIOS_EMERG, "================================\n");
144 print_trap_information(tf);
145 break;
146 }
147
148 die("Can't recover from trap. Halting.\n");
149 }
150
151 /* This function used to redirect trap to s-mode. */
redirect_trap(void)152 void redirect_trap(void)
153 {
154 write_csr(stval, read_csr(mtval));
155 write_csr(sepc, read_csr(mepc));
156 write_csr(scause, read_csr(mcause));
157 write_csr(mepc, read_csr(stvec));
158
159 uintptr_t status = read_csr(mstatus);
160 uintptr_t mpp = EXTRACT_FIELD(status, MSTATUS_MPP);
161 status = INSERT_FIELD(status, MSTATUS_MPP, 1);
162 status = INSERT_FIELD(status, MSTATUS_SPP, mpp & 1);
163 write_csr(mstatus, status);
164 }
165