1/* $OpenBSD: trap.c,v 1.19 2023/11/28 09:10:18 jsg Exp $ */ 2 3/* 4 * Copyright (c) 2020 Shivam Waghela <shivamwaghela@gmail.com> 5 * Copyright (c) 2020 Brian Bamsch <bbamsch@google.com> 6 * Copyright (c) 2020 Mengshi Li <mengshi.li.mars@gmail.com> 7 * Copyright (c) 2015 Dale Rahn <drahn@dalerahn.com> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22#include <sys/param.h> 23#include <sys/systm.h> 24#include <sys/proc.h> 25#include <sys/user.h> 26#include <sys/signalvar.h> 27#include <sys/siginfo.h> 28 29#include <machine/riscvreg.h> 30#include <machine/syscall.h> 31#include <machine/db_machdep.h> 32 33/* Called from exception.S */ 34void do_trap_supervisor(struct trapframe *); 35void do_trap_user(struct trapframe *); 36 37static void udata_abort(struct trapframe *); 38static void kdata_abort(struct trapframe *); 39 40static void 41dump_regs(struct trapframe *frame) 42{ 43 int n; 44 int i; 45 46 n = (sizeof(frame->tf_t) / sizeof(frame->tf_t[0])); 47 for (i = 0; i < n; i++) 48 printf("t[%d] == 0x%016lx\n", i, frame->tf_t[i]); 49 50 n = (sizeof(frame->tf_s) / sizeof(frame->tf_s[0])); 51 for (i = 0; i < n; i++) 52 printf("s[%d] == 0x%016lx\n", i, frame->tf_s[i]); 53 54 n = (sizeof(frame->tf_a) / sizeof(frame->tf_a[0])); 55 for (i = 0; i < n; i++) 56 printf("a[%d] == 0x%016lx\n", i, frame->tf_a[i]); 57 58 printf("sepc == 0x%016lx\n", frame->tf_sepc); 59 printf("sstatus == 0x%016lx\n", frame->tf_sstatus); 60 printf("stval == 0x%016lx\n", frame->tf_stval); 61 printf("scause == 0x%016lx\n", frame->tf_scause); 62} 63 64void 65do_trap_supervisor(struct trapframe *frame) 66{ 67 uint64_t exception; 68 69 /* Ensure we came from supervisor mode, interrupts disabled */ 70 KASSERTMSG((csr_read(sstatus) & (SSTATUS_SPP | SSTATUS_SIE)) == 71 SSTATUS_SPP, "Came from S mode with interrupts enabled"); 72 73 KASSERTMSG((csr_read(sstatus) & (SSTATUS_SUM)) == 0, 74 "Came from S mode with SUM enabled"); 75 76 if (frame->tf_scause & EXCP_INTR) { 77 /* Interrupt */ 78 riscv_cpu_intr(frame); 79 return; 80 } 81 82 intr_enable(); 83 84 exception = (frame->tf_scause & EXCP_MASK); 85 switch (exception) { 86 case EXCP_FAULT_LOAD: 87 case EXCP_FAULT_STORE: 88 case EXCP_FAULT_FETCH: 89 case EXCP_STORE_PAGE_FAULT: 90 case EXCP_LOAD_PAGE_FAULT: 91 kdata_abort(frame); 92 break; 93 case EXCP_BREAKPOINT: 94#ifdef DDB 95 db_trapper(frame->tf_sepc,0/*XXX*/, frame, exception); 96#else 97 dump_regs(frame); 98 panic("No debugger in kernel."); 99#endif 100 break; 101 case EXCP_ILLEGAL_INSTRUCTION: 102 dump_regs(frame); 103 panic("Illegal instruction at 0x%016lx", frame->tf_sepc); 104 break; 105 default: 106 dump_regs(frame); 107 panic("Unknown kernel exception %llx trap value pc 0x%lx stval %lx", 108 exception, frame->tf_sepc, frame->tf_stval); 109 } 110} 111 112void 113do_trap_user(struct trapframe *frame) 114{ 115 uint64_t exception; 116 union sigval sv; 117 struct proc *p = curcpu()->ci_curproc; 118 119 p->p_addr->u_pcb.pcb_tf = frame; 120 121 /* Ensure we came from usermode, interrupts disabled */ 122 KASSERTMSG((csr_read(sstatus) & (SSTATUS_SPP | SSTATUS_SIE)) == 0, 123 "Came from U mode with interrupts enabled"); 124 125 KASSERTMSG((csr_read(sstatus) & (SSTATUS_SUM)) == 0, 126 "Came from U mode with SUM enabled"); 127 128 if (frame->tf_scause & EXCP_INTR) { 129 /* Interrupt */ 130 riscv_cpu_intr(frame); 131 return; 132 } 133 134 intr_enable(); 135 refreshcreds(p); 136 137 exception = (frame->tf_scause & EXCP_MASK); 138 switch (exception) { 139 case EXCP_FAULT_LOAD: 140 case EXCP_FAULT_STORE: 141 case EXCP_FAULT_FETCH: 142 case EXCP_STORE_PAGE_FAULT: 143 case EXCP_LOAD_PAGE_FAULT: 144 case EXCP_INST_PAGE_FAULT: 145 udata_abort(frame); 146 break; 147 case EXCP_USER_ECALL: 148 frame->tf_sepc += 4; /* Next instruction */ 149 svc_handler(frame); 150 break; 151 case EXCP_ILLEGAL_INSTRUCTION: 152 if ((frame->tf_sstatus & SSTATUS_FS_MASK) == SSTATUS_FS_OFF) { 153 fpu_load(p); 154 break; 155 } 156 sv.sival_ptr = (void *)frame->tf_stval; 157 trapsignal(p, SIGILL, 0, ILL_ILLTRP, sv); 158 break; 159 case EXCP_BREAKPOINT: 160 sv.sival_ptr = (void *)frame->tf_stval; 161 trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv); 162 break; 163 default: 164 dump_regs(frame); 165 panic("Unknown userland exception %llx, trap value %lx", 166 exception, frame->tf_stval); 167 } 168 169 userret(p); 170} 171 172static inline vm_prot_t 173accesstype(struct trapframe *frame) 174{ 175 vm_prot_t access_type; 176 177 if ((frame->tf_scause == EXCP_FAULT_STORE) || 178 (frame->tf_scause == EXCP_STORE_PAGE_FAULT)) { 179 access_type = PROT_WRITE; 180 } else if (frame->tf_scause == EXCP_INST_PAGE_FAULT) { 181 access_type = PROT_EXEC; 182 } else { 183 access_type = PROT_READ; 184 } 185 return access_type; 186} 187 188static void 189udata_abort(struct trapframe *frame) 190{ 191 struct vm_map *map; 192 uint64_t stval = frame->tf_stval; 193 union sigval sv; 194 vm_prot_t access_type = accesstype(frame); 195 vaddr_t va; 196 struct proc *p; 197 int error, sig, code; 198 199 p = curcpu()->ci_curproc; 200 201 va = trunc_page(stval); 202 203 map = &p->p_vmspace->vm_map; 204 205 if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p), 206 "[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n", 207 uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial)) 208 return; 209 210 /* Handle referenced/modified emulation */ 211 if (pmap_fault_fixup(map->pmap, va, access_type)) 212 return; 213 214 error = uvm_fault(map, va, 0, access_type); 215 216 if (error == 0) { 217 uvm_grow(p, va); 218 return; 219 } 220 221 if (error == ENOMEM) { 222 sig = SIGKILL; 223 code = 0; 224 } else if (error == EIO) { 225 sig = SIGBUS; 226 code = BUS_OBJERR; 227 } else if (error == EACCES) { 228 sig = SIGSEGV; 229 code = SEGV_ACCERR; 230 } else { 231 sig = SIGSEGV; 232 code = SEGV_MAPERR; 233 } 234 sv.sival_ptr = (void *)stval; 235 trapsignal(p, sig, 0, code, sv); 236} 237 238static void 239kdata_abort(struct trapframe *frame) 240{ 241 struct vm_map *map; 242 uint64_t stval = frame->tf_stval; 243 struct pcb *pcb; 244 vm_prot_t access_type = accesstype(frame); 245 vaddr_t va; 246 struct proc *p; 247 int error = 0; 248 249 pcb = curcpu()->ci_curpcb; 250 p = curcpu()->ci_curproc; 251 252 va = trunc_page(stval); 253 254 if (stval >= VM_MAXUSER_ADDRESS) 255 map = kernel_map; 256 else { 257 map = &p->p_vmspace->vm_map; 258 } 259 260 /* Handle referenced/modified emulation */ 261 if (!pmap_fault_fixup(map->pmap, va, access_type)) { 262 error = uvm_fault(map, va, 0, access_type); 263 264 if (error == 0 && map != kernel_map) 265 uvm_grow(p, va); 266 } 267 268 if (error != 0) { 269 if (curcpu()->ci_idepth == 0 && 270 pcb->pcb_onfault != 0) { 271 frame->tf_a[0] = error; 272 frame->tf_sepc = (register_t)pcb->pcb_onfault; 273 return; 274 } 275 dump_regs(frame); 276 panic("Fatal page fault at %#lx: %#08lx", frame->tf_sepc, 277 (vaddr_t)stval); 278 } 279} 280