#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <sys/syscall_mi.h>
#include <uvm/uvm_extern.h>
#include <machine/fpu.h>
#include <machine/pte.h>
#include <machine/trap.h>
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
#endif
void decr_intr(struct trapframe *);
void exi_intr(struct trapframe *);
void hvi_intr(struct trapframe *);
void syscall(struct trapframe *);
#ifdef TRAP_DEBUG
void dumpframe(struct trapframe *);
#endif
void
trap(struct trapframe *frame)
{
struct cpu_info *ci = curcpu();
struct proc *p = curproc;
int type = frame->exc;
union sigval sv;
struct vm_map *map;
struct vm_map_entry *entry;
pmap_t pm;
vaddr_t va;
int access_type;
int error, sig, code;
mtmsr(mfmsr() & ~(PSL_FPU|PSL_VEC|PSL_VSX));
switch (type) {
case EXC_DECR:
atomic_inc_int(&uvmexp.intrs);
ci->ci_idepth++;
decr_intr(frame);
ci->ci_idepth--;
return;
case EXC_EXI:
atomic_inc_int(&uvmexp.intrs);
ci->ci_idepth++;
exi_intr(frame);
ci->ci_idepth--;
return;
case EXC_HVI:
atomic_inc_int(&uvmexp.intrs);
ci->ci_idepth++;
hvi_intr(frame);
ci->ci_idepth--;
return;
case EXC_SC:
atomic_inc_int(&uvmexp.syscalls);
break;
default:
atomic_inc_int(&uvmexp.traps);
break;
}
if (frame->srr1 & PSL_EE)
intr_enable();
if (frame->srr1 & PSL_PR) {
type |= EXC_USER;
p->p_md.md_regs = frame;
refreshcreds(p);
}
switch (type) {
#ifdef DDB
case EXC_PGM:
if (frame->srr1 & EXC_PGM_TRAP) {
if (frame->srr0 == (register_t)db_enter)
frame->srr0 = frame->lr;
db_ktrap(T_BREAKPOINT, frame);
return;
}
goto fatal;
case EXC_TRC:
db_ktrap(T_BREAKPOINT, frame);
return;
#endif
case EXC_DSI:
map = kernel_map;
va = frame->dar;
if (curpcb->pcb_onfault &&
(va >> ADDR_ESID_SHIFT) == USER_ESID) {
map = &p->p_vmspace->vm_map;
va = curpcb->pcb_userva | (va & SEGMENT_MASK);
}
if (frame->dsisr & DSISR_STORE)
access_type = PROT_WRITE;
else
access_type = PROT_READ;
error = uvm_fault(map, trunc_page(va), 0, access_type);
if (error == 0)
return;
if (curpcb->pcb_onfault) {
frame->srr0 = curpcb->pcb_onfault;
return;
}
printf("dar 0x%lx dsisr 0x%lx\n", frame->dar, frame->dsisr);
goto fatal;
case EXC_DSE:
va = frame->dar;
if (curpcb->pcb_onfault &&
(va >> ADDR_ESID_SHIFT) == USER_ESID) {
map = &p->p_vmspace->vm_map;
va = curpcb->pcb_userva | (va & SEGMENT_MASK);
if (pmap_set_user_slb(map->pmap, va, NULL, NULL) == 0)
return;
}
if (curpcb->pcb_onfault) {
frame->srr0 = curpcb->pcb_onfault;
return;
}
printf("dar 0x%lx dsisr 0x%lx\n", frame->dar, frame->dsisr);
goto fatal;
case EXC_ALI:
{
uint32_t insn = *(uint32_t *)frame->srr0;
if ((insn & 0xfc000002) == 0xf8000000) {
uint32_t rs = (insn >> 21) & 0x1f;
uint32_t ra = (insn >> 16) & 0x1f;
uint64_t ds = insn & 0xfffc;
uint64_t ea;
if ((insn & 0x00000001) == 0 && ra == 0)
panic("invalid stdu instruction form");
if (ds & 0x8000)
ds |= ~0x7fff;
if (ra == 0)
ea = ds;
else
ea = frame->fixreg[ra] + ds;
*(volatile uint32_t *)ea = frame->fixreg[rs] >> 32;
*(volatile uint32_t *)(ea + 4) = frame->fixreg[rs];
if (insn & 0x00000001)
frame->fixreg[ra] = ea;
frame->srr0 += 4;
return;
}
printf("dar 0x%lx dsisr 0x%lx\n", frame->dar, frame->dsisr);
goto fatal;
}
case EXC_DSE|EXC_USER:
pm = p->p_vmspace->vm_map.pmap;
error = pmap_slbd_fault(pm, frame->dar);
if (error == 0)
break;
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))
goto out;
map = &p->p_vmspace->vm_map;
vm_map_lock_read(map);
if (uvm_map_lookup_entry(map, frame->dar, &entry))
error = pmap_slbd_enter(pm, frame->dar);
else
error = EFAULT;
vm_map_unlock_read(map);
if (error) {
sv.sival_ptr = (void *)frame->dar;
trapsignal(p, SIGSEGV, 0, SEGV_MAPERR, sv);
}
break;
case EXC_DSI|EXC_USER:
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))
goto out;
map = &p->p_vmspace->vm_map;
va = frame->dar;
if (frame->dsisr & DSISR_STORE)
access_type = PROT_WRITE;
else
access_type = PROT_READ;
error = uvm_fault(map, trunc_page(va), 0, access_type);
if (error == 0)
uvm_grow(p, va);
if (error) {
#ifdef TRAP_DEBUG
printf("type %x dar 0x%lx dsisr 0x%lx %s\r\n",
type, frame->dar, frame->dsisr, p->p_p->ps_comm);
dumpframe(frame);
#endif
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 *)va;
trapsignal(p, sig, 0, code, sv);
}
break;
case EXC_ISE|EXC_USER:
pm = p->p_vmspace->vm_map.pmap;
error = pmap_slbd_fault(pm, frame->srr0);
if (error == 0)
break;
case EXC_ISI|EXC_USER:
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))
goto out;
map = &p->p_vmspace->vm_map;
va = frame->srr0;
access_type = PROT_EXEC;
error = uvm_fault(map, trunc_page(va), 0, access_type);
if (error == 0)
uvm_grow(p, va);
if (error) {
#ifdef TRAP_DEBUG
printf("type %x srr0 0x%lx %s\r\n",
type, frame->srr0, p->p_p->ps_comm);
dumpframe(frame);
#endif
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 *)va;
trapsignal(p, sig, 0, code, sv);
}
break;
case EXC_SC|EXC_USER:
syscall(frame);
return;
case EXC_AST|EXC_USER:
p->p_md.md_astpending = 0;
atomic_inc_int(&uvmexp.softs);
mi_ast(p, curcpu()->ci_want_resched);
break;
case EXC_ALI|EXC_USER:
sv.sival_ptr = (void *)frame->dar;
trapsignal(p, SIGBUS, 0, BUS_ADRALN, sv);
break;
case EXC_PGM|EXC_USER:
sv.sival_ptr = (void *)frame->srr0;
if (frame->srr1 & EXC_PGM_FPENABLED)
trapsignal(p, SIGFPE, 0, fpu_sigcode(p), sv);
else if (frame->srr1 & EXC_PGM_TRAP)
trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv);
else
trapsignal(p, SIGILL, 0, ILL_PRVOPC, sv);
break;
case EXC_FPU|EXC_USER:
if ((frame->srr1 & (PSL_FP|PSL_VEC|PSL_VSX)) == 0)
restore_vsx(p);
curpcb->pcb_flags |= PCB_FPU;
frame->srr1 |= PSL_FPU;
break;
case EXC_TRC|EXC_USER:
sv.sival_ptr = (void *)frame->srr0;
trapsignal(p, SIGTRAP, 0, TRAP_TRACE, sv);
break;
case EXC_HEA|EXC_USER:
sv.sival_ptr = (void *)frame->srr0;
trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
break;
case EXC_VEC|EXC_USER:
if ((frame->srr1 & (PSL_FP|PSL_VEC|PSL_VSX)) == 0)
restore_vsx(p);
curpcb->pcb_flags |= PCB_VEC;
frame->srr1 |= PSL_VEC;
break;
case EXC_VSX|EXC_USER:
if ((frame->srr1 & (PSL_FP|PSL_VEC|PSL_VSX)) == 0)
restore_vsx(p);
curpcb->pcb_flags |= PCB_VSX;
frame->srr1 |= PSL_VSX;
break;
case EXC_FAC|EXC_USER:
sv.sival_ptr = (void *)frame->srr0;
trapsignal(p, SIGILL, 0, ILL_PRVOPC, sv);
break;
default:
fatal:
#ifdef DDB
db_printf("trap type %x srr1 %lx at %lx lr %lx\n",
type, frame->srr1, frame->srr0, frame->lr);
db_ktrap(0, frame);
#endif
panic("trap type %x srr1 %lx at %lx lr %lx",
type, frame->srr1, frame->srr0, frame->lr);
}
out:
userret(p);
}
#ifdef TRAP_DEBUG
#include <machine/opal.h>
void
dumpframe(struct trapframe *frame)
{
int i;
for (i = 0; i < 32; i++)
opal_printf("r%d 0x%lx\r\n", i, frame->fixreg[i]);
opal_printf("ctr 0x%lx\r\n", frame->ctr);
opal_printf("xer 0x%lx\r\n", frame->xer);
opal_printf("cr 0x%lx\r\n", frame->cr);
opal_printf("lr 0x%lx\r\n", frame->lr);
opal_printf("srr0 0x%lx\r\n", frame->srr0);
opal_printf("srr1 0x%lx\r\n", frame->srr1);
}
#endif