xref: /aosp_15_r20/external/coreboot/src/arch/riscv/trap_handler.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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