#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/signalvar.h>
#include <sys/siginfo.h>
#include <machine/riscvreg.h>
#include <machine/syscall.h>
#include <machine/db_machdep.h>
void do_trap_supervisor(struct trapframe *);
void do_trap_user(struct trapframe *);
static void udata_abort(struct trapframe *);
static void kdata_abort(struct trapframe *);
static void
dump_regs(struct trapframe *frame)
{
int n;
int i;
n = (sizeof(frame->tf_t) / sizeof(frame->tf_t[0]));
for (i = 0; i < n; i++)
printf("t[%d] == 0x%016lx\n", i, frame->tf_t[i]);
n = (sizeof(frame->tf_s) / sizeof(frame->tf_s[0]));
for (i = 0; i < n; i++)
printf("s[%d] == 0x%016lx\n", i, frame->tf_s[i]);
n = (sizeof(frame->tf_a) / sizeof(frame->tf_a[0]));
for (i = 0; i < n; i++)
printf("a[%d] == 0x%016lx\n", i, frame->tf_a[i]);
printf("sepc == 0x%016lx\n", frame->tf_sepc);
printf("sstatus == 0x%016lx\n", frame->tf_sstatus);
printf("stval == 0x%016lx\n", frame->tf_stval);
printf("scause == 0x%016lx\n", frame->tf_scause);
}
void
do_trap_supervisor(struct trapframe *frame)
{
uint64_t exception;
KASSERTMSG((csr_read(sstatus) & (SSTATUS_SPP | SSTATUS_SIE)) ==
SSTATUS_SPP, "Came from S mode with interrupts enabled");
KASSERTMSG((csr_read(sstatus) & (SSTATUS_SUM)) == 0,
"Came from S mode with SUM enabled");
if (frame->tf_scause & EXCP_INTR) {
riscv_cpu_intr(frame);
return;
}
intr_enable();
atomic_inc_int(&uvmexp.traps);
exception = (frame->tf_scause & EXCP_MASK);
switch (exception) {
case EXCP_FAULT_LOAD:
case EXCP_FAULT_STORE:
case EXCP_FAULT_FETCH:
case EXCP_STORE_PAGE_FAULT:
case EXCP_LOAD_PAGE_FAULT:
kdata_abort(frame);
break;
case EXCP_BREAKPOINT:
#ifdef DDB
db_trapper(frame->tf_sepc,0, frame, exception);
#else
dump_regs(frame);
panic("No debugger in kernel.");
#endif
break;
case EXCP_ILLEGAL_INSTRUCTION:
dump_regs(frame);
panic("Illegal instruction at 0x%016lx", frame->tf_sepc);
break;
default:
dump_regs(frame);
panic("Unknown kernel exception %llx trap value pc 0x%lx stval %lx",
exception, frame->tf_sepc, frame->tf_stval);
}
}
void
do_trap_user(struct trapframe *frame)
{
uint64_t exception;
union sigval sv;
struct proc *p = curcpu()->ci_curproc;
p->p_addr->u_pcb.pcb_tf = frame;
KASSERTMSG((csr_read(sstatus) & (SSTATUS_SPP | SSTATUS_SIE)) == 0,
"Came from U mode with interrupts enabled");
KASSERTMSG((csr_read(sstatus) & (SSTATUS_SUM)) == 0,
"Came from U mode with SUM enabled");
if (frame->tf_scause & EXCP_INTR) {
riscv_cpu_intr(frame);
return;
}
intr_enable();
atomic_inc_int(&uvmexp.traps);
refreshcreds(p);
exception = (frame->tf_scause & EXCP_MASK);
switch (exception) {
case EXCP_FAULT_LOAD:
case EXCP_FAULT_STORE:
case EXCP_FAULT_FETCH:
case EXCP_STORE_PAGE_FAULT:
case EXCP_LOAD_PAGE_FAULT:
case EXCP_INST_PAGE_FAULT:
udata_abort(frame);
break;
case EXCP_USER_ECALL:
frame->tf_sepc += 4;
svc_handler(frame);
break;
case EXCP_ILLEGAL_INSTRUCTION:
if ((frame->tf_sstatus & SSTATUS_FS_MASK) == SSTATUS_FS_OFF) {
fpu_load(p);
break;
}
sv.sival_ptr = (void *)frame->tf_stval;
trapsignal(p, SIGILL, 0, ILL_ILLTRP, sv);
break;
case EXCP_BREAKPOINT:
sv.sival_ptr = (void *)frame->tf_stval;
trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv);
break;
default:
dump_regs(frame);
panic("Unknown userland exception %llx, trap value %lx",
exception, frame->tf_stval);
}
userret(p);
}
static inline vm_prot_t
accesstype(struct trapframe *frame)
{
vm_prot_t access_type;
if (frame->tf_scause == EXCP_FAULT_STORE ||
frame->tf_scause == EXCP_STORE_PAGE_FAULT) {
access_type = PROT_WRITE;
} else if (frame->tf_scause == EXCP_FAULT_FETCH ||
frame->tf_scause == EXCP_INST_PAGE_FAULT) {
access_type = PROT_EXEC;
} else {
access_type = PROT_READ;
}
return access_type;
}
static void
udata_abort(struct trapframe *frame)
{
struct vm_map *map;
uint64_t stval = frame->tf_stval;
union sigval sv;
vm_prot_t access_type = accesstype(frame);
vaddr_t va;
struct proc *p;
int error, sig, code;
p = curcpu()->ci_curproc;
va = trunc_page(stval);
map = &p->p_vmspace->vm_map;
if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p),
"[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n",
uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial))
return;
if (pmap_fault_fixup(map->pmap, va, access_type))
return;
error = uvm_fault(map, va, 0, access_type);
if (error == 0) {
uvm_grow(p, va);
return;
}
if (error == ENOMEM) {
sig = SIGKILL;
code = 0;
} else if (error == EIO) {
sig = SIGBUS;
code = BUS_OBJERR;
} else if (error == EACCES) {
sig = SIGSEGV;
code = SEGV_ACCERR;
} else {
sig = SIGSEGV;
code = SEGV_MAPERR;
}
sv.sival_ptr = (void *)stval;
trapsignal(p, sig, 0, code, sv);
}
static void
kdata_abort(struct trapframe *frame)
{
struct vm_map *map;
uint64_t stval = frame->tf_stval;
struct pcb *pcb;
vm_prot_t access_type = accesstype(frame);
vaddr_t va;
struct proc *p;
int error = 0;
pcb = curcpu()->ci_curpcb;
p = curcpu()->ci_curproc;
va = trunc_page(stval);
if (stval >= VM_MAXUSER_ADDRESS)
map = kernel_map;
else {
map = &p->p_vmspace->vm_map;
}
if (!pmap_fault_fixup(map->pmap, va, access_type)) {
error = uvm_fault(map, va, 0, access_type);
if (error == 0 && map != kernel_map)
uvm_grow(p, va);
}
if (error != 0) {
if (curcpu()->ci_idepth == 0 &&
pcb->pcb_onfault != 0) {
frame->tf_a[0] = error;
frame->tf_sepc = (register_t)pcb->pcb_onfault;
return;
}
dump_regs(frame);
panic("Fatal page fault at %#lx: %#08lx", frame->tf_sepc,
(vaddr_t)stval);
}
}