1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Western Digital Corporation or its affiliates.
5  *
6  * Authors:
7  *   Anup Patel <anup.patel@wdc.com>
8  */
9 
10 #include <sbi/riscv_asm.h>
11 #include <sbi/riscv_encoding.h>
12 #include <sbi/riscv_unpriv.h>
13 #include <sbi/sbi_console.h>
14 #include <sbi/sbi_ecall.h>
15 #include <sbi/sbi_error.h>
16 #include <sbi/sbi_hart.h>
17 #include <sbi/sbi_illegal_insn.h>
18 #include <sbi/sbi_ipi.h>
19 #include <sbi/sbi_misaligned_ldst.h>
20 #include <sbi/sbi_timer.h>
21 #include <sbi/sbi_trap.h>
22 
sbi_trap_error(const char * msg,int rc,u32 hartid,ulong mcause,ulong mtval,struct sbi_trap_regs * regs)23 static void __noreturn sbi_trap_error(const char *msg, int rc, u32 hartid,
24 				      ulong mcause, ulong mtval,
25 				      struct sbi_trap_regs *regs)
26 {
27 	sbi_printf("%s: hart%d: %s (error %d)\n", __func__, hartid, msg, rc);
28 	sbi_printf("%s: hart%d: mcause=0x%" PRILX " mtval=0x%" PRILX "\n",
29 		   __func__, hartid, mcause, mtval);
30 	sbi_printf("%s: hart%d: mepc=0x%" PRILX " mstatus=0x%" PRILX "\n",
31 		   __func__, hartid, regs->mepc, regs->mstatus);
32 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
33 		   hartid, "ra", regs->ra, "sp", regs->sp);
34 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
35 		   hartid, "gp", regs->gp, "tp", regs->tp);
36 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
37 		   hartid, "s0", regs->s0, "s1", regs->s1);
38 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
39 		   hartid, "a0", regs->a0, "a1", regs->a1);
40 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
41 		   hartid, "a2", regs->a2, "a3", regs->a3);
42 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
43 		   hartid, "a4", regs->a4, "a5", regs->a5);
44 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
45 		   hartid, "a6", regs->a6, "a7", regs->a7);
46 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
47 		   hartid, "s2", regs->s2, "s3", regs->s3);
48 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
49 		   hartid, "s4", regs->s4, "s5", regs->s5);
50 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
51 		   hartid, "s6", regs->s6, "s7", regs->s7);
52 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
53 		   hartid, "s8", regs->s8, "s9", regs->s9);
54 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
55 		   hartid, "s10", regs->s10, "s11", regs->s11);
56 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
57 		   hartid, "t0", regs->t0, "t1", regs->t1);
58 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
59 		   hartid, "t2", regs->t2, "t3", regs->t3);
60 	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
61 		   hartid, "t4", regs->t4, "t5", regs->t5);
62 	sbi_printf("%s: hart%d: %s=0x%" PRILX "\n", __func__, hartid, "t6",
63 		   regs->t6);
64 
65 	sbi_hart_hang();
66 }
67 
68 /**
69  * Redirect trap to lower privledge mode (S-mode or U-mode)
70  *
71  * @param regs pointer to register state
72  * @param scratch pointer to sbi_scratch of current HART
73  * @param epc error PC for lower privledge mode
74  * @param cause exception cause for lower privledge mode
75  * @param tval trap value for lower privledge mode
76  *
77  * @return 0 on success and negative error code on failure
78  */
sbi_trap_redirect(struct sbi_trap_regs * regs,struct sbi_scratch * scratch,ulong epc,ulong cause,ulong tval)79 int sbi_trap_redirect(struct sbi_trap_regs *regs, struct sbi_scratch *scratch,
80 		      ulong epc, ulong cause, ulong tval)
81 {
82 	ulong hstatus, vsstatus, prev_mode;
83 #if __riscv_xlen == 32
84 	bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
85 	bool prev_stage2 = (regs->mstatusH & MSTATUSH_MTL) ? TRUE : FALSE;
86 #else
87 	bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
88 	bool prev_stage2 = (regs->mstatus & MSTATUS_MTL) ? TRUE : FALSE;
89 #endif
90 	/* By default, we redirect to HS-mode */
91 	bool next_virt = FALSE;
92 
93 	/* Sanity check on previous mode */
94 	prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
95 	if (prev_mode != PRV_S && prev_mode != PRV_U)
96 		return SBI_ENOTSUPP;
97 
98 	/* For certain exceptions from VS/VU-mode we redirect to VS-mode */
99 	if (misa_extension('H') && prev_virt && !prev_stage2) {
100 		switch (cause) {
101 		case CAUSE_FETCH_PAGE_FAULT:
102 		case CAUSE_LOAD_PAGE_FAULT:
103 		case CAUSE_STORE_PAGE_FAULT:
104 			next_virt = TRUE;
105 			break;
106 		default:
107 			break;
108 		};
109 	}
110 
111 	/* Update MSTATUS MPV and MTL bits */
112 #if __riscv_xlen == 32
113 	regs->mstatusH &= ~MSTATUSH_MPV;
114 	regs->mstatusH |= (next_virt) ? MSTATUSH_MPV : 0UL;
115 	regs->mstatusH &= ~MSTATUSH_MTL;
116 #else
117 	regs->mstatus &= ~MSTATUS_MPV;
118 	regs->mstatus |= (next_virt) ? MSTATUS_MPV : 0UL;
119 	regs->mstatus &= ~MSTATUS_MTL;
120 #endif
121 
122 	/* Update HSTATUS for VS/VU-mode to HS-mode transition */
123 	if (misa_extension('H') && prev_virt && !next_virt) {
124 		/* Update HSTATUS SP2P, SP2V, SPV, and STL bits */
125 		hstatus = csr_read(CSR_HSTATUS);
126 		hstatus &= ~HSTATUS_SP2P;
127 		hstatus |= (regs->mstatus & MSTATUS_SPP) ? HSTATUS_SP2P : 0;
128 		hstatus &= ~HSTATUS_SP2V;
129 		hstatus |= (hstatus & HSTATUS_SPV) ? HSTATUS_SP2V : 0;
130 		hstatus &= ~HSTATUS_SPV;
131 		hstatus |= (prev_virt) ? HSTATUS_SPV : 0;
132 		hstatus &= ~HSTATUS_STL;
133 		hstatus |= (prev_stage2) ? HSTATUS_STL : 0;
134 		csr_write(CSR_HSTATUS, hstatus);
135 	}
136 
137 	/* Update exception related CSRs */
138 	if (next_virt) {
139 		/* Update VS-mode exception info */
140 		csr_write(CSR_VSTVAL, tval);
141 		csr_write(CSR_VSEPC, epc);
142 		csr_write(CSR_VSCAUSE, cause);
143 
144 		/* Set MEPC to VS-mode exception vector base */
145 		regs->mepc = csr_read(CSR_VSTVEC);
146 
147 		/* Set MPP to VS-mode */
148 		regs->mstatus &= ~MSTATUS_MPP;
149 		regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
150 
151 		/* Get VS-mode SSTATUS CSR */
152 		vsstatus = csr_read(CSR_VSSTATUS);
153 
154 		/* Set SPP for VS-mode */
155 		vsstatus &= ~SSTATUS_SPP;
156 		if (prev_mode == PRV_S)
157 			vsstatus |= (1UL << SSTATUS_SPP_SHIFT);
158 
159 		/* Set SPIE for VS-mode */
160 		vsstatus &= ~SSTATUS_SPIE;
161 		if (vsstatus & SSTATUS_SIE)
162 			vsstatus |= (1UL << SSTATUS_SPIE_SHIFT);
163 
164 		/* Clear SIE for VS-mode */
165 		vsstatus &= ~SSTATUS_SIE;
166 
167 		/* Update VS-mode SSTATUS CSR */
168 		csr_write(CSR_VSSTATUS, vsstatus);
169 	} else {
170 		/* Update S-mode exception info */
171 		csr_write(CSR_STVAL, tval);
172 		csr_write(CSR_SEPC, epc);
173 		csr_write(CSR_SCAUSE, cause);
174 
175 		/* Set MEPC to S-mode exception vector base */
176 		regs->mepc = csr_read(CSR_STVEC);
177 
178 		/* Set MPP to S-mode */
179 		regs->mstatus &= ~MSTATUS_MPP;
180 		regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
181 
182 		/* Set SPP for S-mode*/
183 		regs->mstatus &= ~MSTATUS_SPP;
184 		if (prev_mode == PRV_S)
185 			regs->mstatus |= (1UL << MSTATUS_SPP_SHIFT);
186 
187 		/* Set SPIE for S-mode */
188 		regs->mstatus &= ~MSTATUS_SPIE;
189 		if (regs->mstatus & MSTATUS_SIE)
190 			regs->mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
191 
192 		/* Clear SIE for S-mode */
193 		regs->mstatus &= ~MSTATUS_SIE;
194 	}
195 
196 	return 0;
197 }
198 
199 /**
200  * Handle trap/interrupt
201  *
202  * This function is called by firmware linked to OpenSBI
203  * library for handling trap/interrupt. It expects the
204  * following:
205  * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART
206  * 2. The 'mcause' CSR is having exception/interrupt cause
207  * 3. The 'mtval' CSR is having additional trap information
208  * 4. Stack pointer (SP) is setup for current HART
209  * 5. Interrupts are disabled in MSTATUS CSR
210  *
211  * @param regs pointer to register state
212  * @param scratch pointer to sbi_scratch of current HART
213  */
sbi_trap_handler(struct sbi_trap_regs * regs,struct sbi_scratch * scratch)214 void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch)
215 {
216 	int rc = SBI_ENOTSUPP;
217 	const char *msg = "trap handler failed";
218 	u32 hartid = sbi_current_hartid();
219 	ulong mcause = csr_read(CSR_MCAUSE);
220 	ulong mtval = csr_read(CSR_MTVAL);
221 	struct unpriv_trap *uptrap;
222 
223 	if (mcause & (1UL << (__riscv_xlen - 1))) {
224 		mcause &= ~(1UL << (__riscv_xlen - 1));
225 		switch (mcause) {
226 		case IRQ_M_TIMER:
227 			sbi_timer_process(scratch);
228 			break;
229 		case IRQ_M_SOFT:
230 			sbi_ipi_process(scratch);
231 			break;
232 		default:
233 			msg = "unhandled external interrupt";
234 			goto trap_error;
235 		};
236 		return;
237 	}
238 
239 	switch (mcause) {
240 	case CAUSE_ILLEGAL_INSTRUCTION:
241 		rc  = sbi_illegal_insn_handler(hartid, mcause, regs, scratch);
242 		msg = "illegal instruction handler failed";
243 		break;
244 	case CAUSE_MISALIGNED_LOAD:
245 		rc = sbi_misaligned_load_handler(hartid, mcause, regs, scratch);
246 		msg = "misaligned load handler failed";
247 		break;
248 	case CAUSE_MISALIGNED_STORE:
249 		rc  = sbi_misaligned_store_handler(hartid, mcause, regs,
250 						   scratch);
251 		msg = "misaligned store handler failed";
252 		break;
253 	case CAUSE_SUPERVISOR_ECALL:
254 	case CAUSE_HYPERVISOR_ECALL:
255 		rc  = sbi_ecall_handler(hartid, mcause, regs, scratch);
256 		msg = "ecall handler failed";
257 		break;
258 	case CAUSE_LOAD_ACCESS:
259 	case CAUSE_STORE_ACCESS:
260 	case CAUSE_LOAD_PAGE_FAULT:
261 	case CAUSE_STORE_PAGE_FAULT:
262 		uptrap = sbi_hart_get_trap_info(scratch);
263 		if ((regs->mstatus & MSTATUS_MPRV) && uptrap) {
264 			rc = 0;
265 			regs->mepc += uptrap->ilen;
266 			uptrap->cause = mcause;
267 			uptrap->tval = mtval;
268 		} else {
269 			rc = sbi_trap_redirect(regs, scratch, regs->mepc,
270 					       mcause, mtval);
271 		}
272 		msg = "page/access fault handler failed";
273 		break;
274 	default:
275 		/* If the trap came from S or U mode, redirect it there */
276 		rc = sbi_trap_redirect(regs, scratch, regs->mepc,
277 				       mcause, mtval);
278 		break;
279 	};
280 
281 trap_error:
282 	if (rc) {
283 		sbi_trap_error(msg, rc, hartid, mcause, csr_read(CSR_MTVAL),
284 			       regs);
285 	}
286 }
287