#include <sys/param.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <sys/systm.h>
#include <sys/syscall_mi.h>
#include <uvm/uvm_extern.h>
#include <machine/asm_macro.h>
#include <machine/cmmu.h>
#include <machine/cpu.h>
#ifdef M88100
#include <machine/m88100.h>
#include <machine/m8820x.h>
#endif
#ifdef M88110
#include <machine/m88110.h>
#endif
#include <machine/fpu.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/trap.h>
#include <machine/db_machdep.h>
#define SSBREAKPOINT (0xF000D1F8U)
#define USERMODE(PSR) (((PSR) & PSR_MODE) == 0)
#define SYSTEMMODE(PSR) (((PSR) & PSR_MODE) != 0)
void printtrap(int, struct trapframe *);
__dead void panictrap(int, struct trapframe *);
__dead void error_fatal(struct trapframe *);
int double_reg_fixup(struct trapframe *, int);
int ss_put_value(struct proc *, vaddr_t, u_int);
extern void regdump(struct trapframe *f);
const char *trap_type[] = {
"Reset",
"Interrupt Exception",
"Instruction Access",
"Data Access Exception",
"Misaligned Access",
"Unimplemented Opcode",
"Privilege Violation",
"Bounds Check Violation",
"Illegal Integer Divide",
"Integer Overflow",
"Error Exception",
"Non-Maskable Exception",
};
const int trap_types = sizeof trap_type / sizeof trap_type[0];
#ifdef M88100
const char *pbus_exception_type[] = {
"Success (No Fault)",
"unknown 1",
"unknown 2",
"Bus Error",
"Segment Fault",
"Page Fault",
"Supervisor Violation",
"Write Violation",
};
#endif
void
printtrap(int type, struct trapframe *frame)
{
#ifdef M88100
if (CPU_IS88100) {
if (type == 2) {
printf("\nInstr access fault (%s) v = %lx, frame %p\n",
pbus_exception_type[
CMMU_PFSR_FAULT(frame->tf_ipfsr)],
frame->tf_sxip & XIP_ADDR, frame);
} else if (type == 3) {
printf("\nData access fault (%s) v = %lx, frame %p\n",
pbus_exception_type[
CMMU_PFSR_FAULT(frame->tf_dpfsr)],
frame->tf_sxip & XIP_ADDR, frame);
} else
printf("\nTrap type %d, v = %lx, frame %p\n",
type, frame->tf_sxip & XIP_ADDR, frame);
}
#endif
#ifdef M88110
if (CPU_IS88110) {
printf("\nTrap type %d, v = %lx, frame %p\n",
type, frame->tf_exip, frame);
}
#endif
#ifdef DDB
regdump(frame);
#endif
}
__dead void
panictrap(int type, struct trapframe *frame)
{
static int panicing = 0;
if (panicing++ == 0)
printtrap(type, frame);
if ((u_int)type < trap_types)
panic("%s", trap_type[type]);
else
panic("trap %d", type);
}
void
interrupt(struct trapframe *frame)
{
struct cpu_info *ci = curcpu();
ci->ci_idepth++;
md_interrupt_func(frame);
ci->ci_idepth--;
}
#ifdef M88110
int
nmi(struct trapframe *frame)
{
return md_nmi_func(frame);
}
void
nmi_wrapup(struct trapframe *frame)
{
md_nmi_wrapup_func(frame);
}
#endif
void
ast(struct trapframe *frame)
{
struct cpu_info *ci = curcpu();
struct proc *p = ci->ci_curproc;
p->p_md.md_astpending = 0;
atomic_inc_int(&uvmexp.softs);
mi_ast(p, ci->ci_want_resched);
userret(p);
}
#ifdef M88100
void
m88100_trap(u_int type, struct trapframe *frame)
{
struct proc *p;
struct vm_map *map;
vaddr_t va, pcb_onfault;
vm_prot_t access_type;
int fault_type, pbus_type;
u_long fault_code;
vaddr_t fault_addr;
struct vmspace *vm;
union sigval sv;
int result;
#ifdef DDB
int s;
u_int psr;
#endif
int sig = 0;
atomic_inc_int(&uvmexp.traps);
if ((p = curproc) == NULL)
p = &proc0;
if (USERMODE(frame->tf_epsr)) {
type |= T_USER;
p->p_md.md_tf = frame;
refreshcreds(p);
}
fault_type = SI_NOINFO;
fault_code = 0;
fault_addr = frame->tf_sxip & XIP_ADDR;
switch (type) {
default:
case T_ILLFLT:
lose:
panictrap(frame->tf_vector, frame);
break;
#if defined(DDB)
case T_KDB_BREAK:
s = splhigh();
set_psr((psr = get_psr()) & ~PSR_IND);
ddb_break_trap(T_KDB_BREAK, (db_regs_t*)frame);
set_psr(psr);
splx(s);
return;
case T_KDB_ENTRY:
s = splhigh();
set_psr((psr = get_psr()) & ~PSR_IND);
ddb_entry_trap(T_KDB_ENTRY, (db_regs_t*)frame);
set_psr(psr);
splx(s);
return;
#endif
case T_MISALGNFLT:
printf("kernel misaligned access exception @0x%08lx\n",
frame->tf_sxip);
goto lose;
case T_INSTFLT:
#ifdef TRAPDEBUG
pbus_type = CMMU_PFSR_FAULT(frame->tf_ipfsr);
printf("Kernel Instruction fault #%d (%s) v = 0x%x, frame 0x%x cpu %p\n",
pbus_type, pbus_exception_type[pbus_type],
fault_addr, frame, frame->tf_cpu);
#endif
goto lose;
case T_DATAFLT:
if ((frame->tf_dmt0 & DMT_DAS) == 0)
goto user_fault;
fault_addr = frame->tf_dma0;
if (frame->tf_dmt0 & (DMT_WRITE|DMT_LOCKBAR)) {
access_type = PROT_READ | PROT_WRITE;
fault_code = PROT_WRITE;
} else {
access_type = PROT_READ;
fault_code = PROT_READ;
}
va = trunc_page((vaddr_t)fault_addr);
vm = p->p_vmspace;
map = kernel_map;
pbus_type = CMMU_PFSR_FAULT(frame->tf_dpfsr);
#ifdef TRAPDEBUG
printf("Kernel Data access fault #%d (%s) v = 0x%x, frame 0x%x cpu %p\n",
pbus_type, pbus_exception_type[pbus_type],
fault_addr, frame, frame->tf_cpu);
#endif
pcb_onfault = p->p_addr->u_pcb.pcb_onfault;
switch (pbus_type) {
case CMMU_PFSR_SUCCESS:
p->p_addr->u_pcb.pcb_onfault = 0;
KERNEL_LOCK();
data_access_emulation((u_int *)frame);
KERNEL_UNLOCK();
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
frame->tf_dmt0 = 0;
frame->tf_dpfsr = 0;
return;
case CMMU_PFSR_SFAULT:
case CMMU_PFSR_PFAULT:
p->p_addr->u_pcb.pcb_onfault = 0;
KERNEL_LOCK();
result = uvm_fault(map, va, 0, access_type);
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
if (result == 0) {
p->p_addr->u_pcb.pcb_onfault = 0;
data_access_emulation((u_int *)frame);
KERNEL_UNLOCK();
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
frame->tf_dmt0 = 0;
frame->tf_dpfsr = 0;
return;
} else if (pcb_onfault != 0) {
KERNEL_UNLOCK();
frame->tf_snip = pcb_onfault | NIP_V;
frame->tf_sfip = (pcb_onfault + 4) | FIP_V;
frame->tf_dmt0 = 0;
frame->tf_dpfsr = 0;
return;
}
KERNEL_UNLOCK();
break;
}
#ifdef TRAPDEBUG
printf("PBUS Fault %d (%s) va = 0x%x\n", pbus_type,
pbus_exception_type[pbus_type], va);
#endif
goto lose;
case T_INSTFLT+T_USER:
case T_DATAFLT+T_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 userexit;
user_fault:
if (type == T_INSTFLT + T_USER) {
pbus_type = CMMU_PFSR_FAULT(frame->tf_ipfsr);
#ifdef TRAPDEBUG
printf("User Instruction fault #%d (%s) v = 0x%x, frame 0x%x cpu %p\n",
pbus_type, pbus_exception_type[pbus_type],
fault_addr, frame, frame->tf_cpu);
#endif
access_type = PROT_EXEC;
fault_code = PROT_EXEC;
} else {
fault_addr = frame->tf_dma0;
pbus_type = CMMU_PFSR_FAULT(frame->tf_dpfsr);
#ifdef TRAPDEBUG
printf("User Data access fault #%d (%s) v = 0x%x, frame 0x%x cpu %p\n",
pbus_type, pbus_exception_type[pbus_type],
fault_addr, frame, frame->tf_cpu);
#endif
if (frame->tf_dmt0 & (DMT_WRITE | DMT_LOCKBAR)) {
access_type = PROT_READ | PROT_WRITE;
fault_code = PROT_WRITE;
} else {
access_type = PROT_READ;
fault_code = PROT_READ;
}
}
va = trunc_page((vaddr_t)fault_addr);
vm = p->p_vmspace;
map = &vm->vm_map;
if ((pcb_onfault = p->p_addr->u_pcb.pcb_onfault) != 0)
p->p_addr->u_pcb.pcb_onfault = 0;
switch (pbus_type) {
case CMMU_PFSR_SUCCESS:
result = 0;
break;
case CMMU_PFSR_BERROR:
result = EACCES;
break;
default:
KERNEL_LOCK();
result = uvm_fault(map, va, 0, access_type);
KERNEL_UNLOCK();
if (result == EACCES)
result = EFAULT;
break;
}
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
if (result == 0) {
uvm_grow(p, va);
if (type == T_INSTFLT + T_USER) {
m88100_rewind_insn(&(frame->tf_regs));
frame->tf_sfip &= ~FIP_E;
frame->tf_snip &= ~NIP_E;
frame->tf_ipfsr = 0;
} else {
p->p_addr->u_pcb.pcb_onfault = 0;
KERNEL_LOCK();
data_access_emulation((u_int *)frame);
KERNEL_UNLOCK();
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
frame->tf_dmt0 = 0;
frame->tf_dpfsr = 0;
}
} else {
if (pcb_onfault != 0) {
frame->tf_snip = pcb_onfault | NIP_V;
frame->tf_sfip = (pcb_onfault + 4) | FIP_V;
frame->tf_dmt0 = 0;
frame->tf_dpfsr = 0;
} else {
sig = result == EACCES ? SIGBUS : SIGSEGV;
fault_type = result == EACCES ?
BUS_ADRERR : SEGV_MAPERR;
}
}
break;
case T_MISALGNFLT+T_USER:
sig = double_reg_fixup(frame, T_MISALGNFLT);
fault_type = BUS_ADRALN;
break;
case T_PRIVINFLT+T_USER:
case T_ILLFLT+T_USER:
#ifndef DDB
case T_KDB_BREAK:
case T_KDB_ENTRY:
#endif
case T_KDB_BREAK+T_USER:
case T_KDB_ENTRY+T_USER:
case T_KDB_TRACE:
case T_KDB_TRACE+T_USER:
sig = SIGILL;
break;
case T_BNDFLT+T_USER:
sig = SIGFPE;
break;
case T_ZERODIV+T_USER:
sig = SIGFPE;
fault_type = FPE_INTDIV;
break;
case T_OVFFLT+T_USER:
sig = SIGFPE;
fault_type = FPE_INTOVF;
break;
case T_FPEPFLT+T_USER:
m88100_fpu_precise_exception(frame);
goto userexit;
case T_FPEIFLT:
case T_FPEIFLT+T_USER:
m88100_fpu_imprecise_exception(frame);
goto userexit;
case T_SIGSYS+T_USER:
sig = SIGSYS;
break;
case T_STEPBPT+T_USER:
#ifdef PTRACE
{
u_int instr;
vaddr_t pc = PC_REGS(&frame->tf_regs);
copyinsn(p, (u_int32_t *)pc, (u_int32_t *)&instr);
if ((p->p_md.md_bp0va != pc &&
p->p_md.md_bp1va != pc) ||
instr != SSBREAKPOINT) {
sig = SIGTRAP;
fault_type = TRAP_TRACE;
break;
}
KERNEL_LOCK();
if (p->p_md.md_bp0va == pc) {
ss_put_value(p, pc, p->p_md.md_bp0save);
p->p_md.md_bp0va = 0;
}
if (p->p_md.md_bp1va == pc) {
ss_put_value(p, pc, p->p_md.md_bp1save);
p->p_md.md_bp1va = 0;
}
KERNEL_UNLOCK();
frame->tf_sxip = pc | NIP_V;
sig = SIGTRAP;
fault_type = TRAP_BRKPT;
}
#else
sig = SIGTRAP;
fault_type = TRAP_TRACE;
#endif
break;
case T_USERBPT+T_USER:
sig = SIGTRAP;
fault_type = TRAP_BRKPT;
break;
}
if (type < T_USER)
return;
if (sig) {
sv.sival_ptr = (void *)fault_addr;
trapsignal(p, sig, fault_code, fault_type, sv);
frame->tf_dmt0 = 0;
frame->tf_ipfsr = frame->tf_dpfsr = 0;
}
userexit:
userret(p);
}
#endif
#ifdef M88110
void
m88110_trap(u_int type, struct trapframe *frame)
{
struct proc *p;
struct vm_map *map;
vaddr_t va, pcb_onfault;
vm_prot_t access_type;
int fault_type;
u_long fault_code;
vaddr_t fault_addr;
struct vmspace *vm;
union sigval sv;
int result;
#ifdef DDB
int s;
u_int psr;
#endif
int sig = 0;
atomic_inc_int(&uvmexp.traps);
if ((p = curproc) == NULL)
p = &proc0;
fault_type = SI_NOINFO;
fault_code = 0;
fault_addr = frame->tf_exip & XIP_ADDR;
if ((frame->tf_exip & PAGE_MASK) == 0x00000001 && type == T_INSTFLT) {
u_int instr;
frame->tf_exip = (frame->tf_exip & ~1) - 4;
if (!USERMODE(frame->tf_epsr)) {
instr = *(u_int *)fault_addr;
if (instr == 0xf400cc01)
panic("mc88110 errata #16, exip 0x%lx enip 0x%lx",
(frame->tf_exip + 4) | 1, frame->tf_enip);
} else {
if (copyinsn(p, (u_int32_t *)frame->tf_exip,
(u_int32_t *)&instr) == 0 &&
instr == 0xf400cc01) {
printf("mc88110 errata #16, exip 0x%lx enip 0x%lx",
(frame->tf_exip + 4) | 1, frame->tf_enip);
sig = SIGILL;
}
}
}
if (USERMODE(frame->tf_epsr)) {
type |= T_USER;
p->p_md.md_tf = frame;
refreshcreds(p);
}
if (sig != 0)
goto deliver;
switch (type) {
default:
lose:
panictrap(frame->tf_vector, frame);
break;
#ifdef DEBUG
case T_110_DRM+T_USER:
case T_110_DRM:
printf("DMMU read miss: Hardware Table Searches should be enabled!\n");
goto lose;
case T_110_DWM+T_USER:
case T_110_DWM:
printf("DMMU write miss: Hardware Table Searches should be enabled!\n");
goto lose;
case T_110_IAM+T_USER:
case T_110_IAM:
printf("IMMU miss: Hardware Table Searches should be enabled!\n");
goto lose;
#endif
#ifdef DDB
case T_KDB_TRACE:
s = splhigh();
set_psr((psr = get_psr()) & ~PSR_IND);
ddb_break_trap(T_KDB_TRACE, (db_regs_t*)frame);
set_psr(psr);
splx(s);
return;
case T_KDB_BREAK:
s = splhigh();
set_psr((psr = get_psr()) & ~PSR_IND);
ddb_break_trap(T_KDB_BREAK, (db_regs_t*)frame);
set_psr(psr);
splx(s);
return;
case T_KDB_ENTRY:
s = splhigh();
set_psr((psr = get_psr()) & ~PSR_IND);
ddb_entry_trap(T_KDB_ENTRY, (db_regs_t*)frame);
set_psr(psr);
m88110_skip_insn(frame);
splx(s);
return;
#endif
case T_ILLFLT:
{
extern void *kernel_text, *etext;
if (fault_addr >= (vaddr_t)&kernel_text &&
fault_addr < (vaddr_t)&etext) {
cmmu_icache_inv(curcpu()->ci_cpuid,
trunc_page(fault_addr), PAGE_SIZE);
cmmu_cache_wbinv(curcpu()->ci_cpuid,
trunc_page(fault_addr), PAGE_SIZE);
return;
}
}
goto lose;
case T_MISALGNFLT:
printf("kernel misaligned access exception @%p\n",
(void *)frame->tf_exip);
goto lose;
case T_INSTFLT:
#ifdef TRAPDEBUG
printf("Kernel Instruction fault exip %x isr %x ilar %x\n",
frame->tf_exip, frame->tf_isr, frame->tf_ilar);
#endif
goto lose;
case T_DATAFLT:
if ((frame->tf_dsr & CMMU_DSR_SU) == 0)
goto m88110_user_fault;
#ifdef TRAPDEBUG
printf("Kernel Data access fault exip %x dsr %x dlar %x\n",
frame->tf_exip, frame->tf_dsr, frame->tf_dlar);
#endif
fault_addr = frame->tf_dlar;
if (frame->tf_dsr & CMMU_DSR_RW) {
access_type = PROT_READ;
fault_code = PROT_READ;
} else {
access_type = PROT_READ | PROT_WRITE;
fault_code = PROT_WRITE;
}
va = trunc_page((vaddr_t)fault_addr);
vm = p->p_vmspace;
map = kernel_map;
if (frame->tf_dsr & (CMMU_DSR_SI | CMMU_DSR_PI)) {
if ((pcb_onfault = p->p_addr->u_pcb.pcb_onfault) != 0)
p->p_addr->u_pcb.pcb_onfault = 0;
KERNEL_LOCK();
result = uvm_fault(map, va, 0, access_type);
KERNEL_UNLOCK();
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
if (result != 0 && pcb_onfault != 0) {
frame->tf_exip = pcb_onfault;
result = 0;
}
if (result == 0)
return;
}
goto lose;
case T_INSTFLT+T_USER:
case T_DATAFLT+T_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 userexit;
m88110_user_fault:
if (type == T_INSTFLT+T_USER) {
access_type = PROT_EXEC;
fault_code = PROT_EXEC;
#ifdef TRAPDEBUG
printf("User Instruction fault exip %lx isr %lx ilar %lx\n",
frame->tf_exip, frame->tf_isr, frame->tf_ilar);
#endif
} else {
fault_addr = frame->tf_dlar;
if ((frame->tf_dsr & CMMU_DSR_RW) != 0 &&
((frame->tf_dsr & (CMMU_DSR_PI|CMMU_DSR_SI)) != 0 ||
(frame->tf_dsr & CMMU_DSR_WE) == 0)) {
access_type = PROT_READ;
fault_code = PROT_READ;
} else {
access_type = PROT_READ | PROT_WRITE;
fault_code = PROT_WRITE;
}
#ifdef TRAPDEBUG
printf("User Data access fault exip %lx dsr %lx dlar %lx\n",
frame->tf_exip, frame->tf_dsr, frame->tf_dlar);
#endif
}
va = trunc_page((vaddr_t)fault_addr);
vm = p->p_vmspace;
map = &vm->vm_map;
if ((pcb_onfault = p->p_addr->u_pcb.pcb_onfault) != 0)
p->p_addr->u_pcb.pcb_onfault = 0;
if (type == T_INSTFLT+T_USER) {
if (frame->tf_isr &
(CMMU_ISR_BE | CMMU_ISR_SP | CMMU_ISR_TBE)) {
result = EACCES;
} else
if (frame->tf_isr & (CMMU_ISR_SI | CMMU_ISR_PI)) {
KERNEL_LOCK();
result = uvm_fault(map, va, 0, access_type);
KERNEL_UNLOCK();
if (result == EACCES)
result = EFAULT;
} else {
#ifdef TRAPDEBUG
printf("Unexpected Instruction fault isr %lx\n",
frame->tf_isr);
#endif
goto lose;
}
} else {
if (frame->tf_dsr & CMMU_DSR_BE) {
result = EACCES;
} else
if (frame->tf_dsr & (CMMU_DSR_SI | CMMU_DSR_PI)) {
KERNEL_LOCK();
result = uvm_fault(map, va, 0, access_type);
KERNEL_UNLOCK();
if (result == EACCES)
result = EFAULT;
} else
if (frame->tf_dsr & (CMMU_DSR_CP | CMMU_DSR_WA)) {
result = EACCES;
} else
if (frame->tf_dsr & CMMU_DSR_WE) {
if (pmap_set_modify(map->pmap, va)) {
#ifdef TRAPDEBUG
printf("Corrected userland write fault, pmap %p va %p\n",
map->pmap, (void *)va);
#endif
result = 0;
} else {
#ifdef TRAPDEBUG
printf("Uncorrected userland write fault, pmap %p va %p\n",
map->pmap, (void *)va);
#endif
result = uvm_fault(map, va, 0, access_type);
if (result == EACCES)
result = EFAULT;
}
} else {
#ifdef TRAPDEBUG
printf("Unexpected Data access fault dsr %lx\n",
frame->tf_dsr);
#endif
goto lose;
}
}
p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
if (result == 0)
uvm_grow(p, va);
if (result != 0 && pcb_onfault != 0) {
frame->tf_exip = pcb_onfault;
result = 0;
}
if (result != 0) {
sig = result == EACCES ? SIGBUS : SIGSEGV;
fault_type = result == EACCES ?
BUS_ADRERR : SEGV_MAPERR;
}
break;
case T_MISALGNFLT+T_USER:
sig = double_reg_fixup(frame, T_MISALGNFLT);
fault_type = BUS_ADRALN;
if (sig == 0) {
m88110_skip_insn(frame);
goto userexit;
}
break;
case T_ILLFLT+T_USER:
sig = double_reg_fixup(frame, T_ILLFLT);
fault_type = ILL_PRVREG;
if (sig == 0) {
m88110_skip_insn(frame);
goto userexit;
}
break;
case T_PRIVINFLT+T_USER:
fault_type = ILL_PRVREG;
#ifndef DDB
case T_KDB_BREAK:
case T_KDB_ENTRY:
case T_KDB_TRACE:
#endif
case T_KDB_BREAK+T_USER:
case T_KDB_ENTRY+T_USER:
case T_KDB_TRACE+T_USER:
sig = SIGILL;
break;
case T_BNDFLT+T_USER:
sig = SIGFPE;
m88110_skip_insn(frame);
break;
case T_ZERODIV+T_USER:
sig = SIGFPE;
fault_type = FPE_INTDIV;
m88110_skip_insn(frame);
break;
case T_OVFFLT+T_USER:
sig = SIGFPE;
fault_type = FPE_INTOVF;
m88110_skip_insn(frame);
break;
case T_FPEPFLT+T_USER:
m88110_fpu_exception(frame);
goto userexit;
case T_SIGSYS+T_USER:
sig = SIGSYS;
break;
case T_STEPBPT+T_USER:
#ifdef PTRACE
{
u_int instr;
vaddr_t pc = PC_REGS(&frame->tf_regs);
copyinsn(p, (u_int32_t *)pc, (u_int32_t *)&instr);
if ((p->p_md.md_bp0va != pc &&
p->p_md.md_bp1va != pc) ||
instr != SSBREAKPOINT) {
sig = SIGTRAP;
fault_type = TRAP_TRACE;
break;
}
KERNEL_LOCK();
if (p->p_md.md_bp0va == pc) {
ss_put_value(p, pc, p->p_md.md_bp0save);
p->p_md.md_bp0va = 0;
}
if (p->p_md.md_bp1va == pc) {
ss_put_value(p, pc, p->p_md.md_bp1save);
p->p_md.md_bp1va = 0;
}
KERNEL_UNLOCK();
sig = SIGTRAP;
fault_type = TRAP_BRKPT;
}
#else
sig = SIGTRAP;
fault_type = TRAP_TRACE;
#endif
break;
case T_USERBPT+T_USER:
sig = SIGTRAP;
fault_type = TRAP_BRKPT;
break;
}
if (type < T_USER)
return;
if (sig) {
deliver:
sv.sival_ptr = (void *)fault_addr;
trapsignal(p, sig, fault_code, fault_type, sv);
}
userexit:
userret(p);
}
#endif
__dead void
error_fatal(struct trapframe *frame)
{
if (frame->tf_vector == 0)
printf("\nCPU %d Reset Exception\n", cpu_number());
else
printf("\nCPU %d Error Exception\n", cpu_number());
#ifdef DDB
regdump((struct trapframe*)frame);
#endif
panic("unrecoverable exception %ld", frame->tf_vector);
}
#ifdef M88100
void
m88100_syscall(register_t code, struct trapframe *tf)
{
const struct sysent *callp = sysent;
struct proc *p = curproc;
int error;
register_t *args;
register_t rval[2] __aligned(8);
atomic_inc_int(&uvmexp.syscalls);
p->p_md.md_tf = tf;
if (code > 0 && code < SYS_MAXSYSCALL)
callp += code;
args = &tf->tf_r[2];
rval[0] = 0;
rval[1] = tf->tf_r[3];
error = mi_syscall(p, code, callp, args, rval);
switch (error) {
case 0:
tf->tf_r[2] = rval[0];
tf->tf_r[3] = rval[1];
tf->tf_epsr &= ~PSR_C;
tf->tf_snip = tf->tf_sfip & ~NIP_E;
tf->tf_sfip = tf->tf_snip + 4;
break;
case ERESTART:
m88100_rewind_insn(&(tf->tf_regs));
tf->tf_sfip &= ~FIP_E;
tf->tf_snip &= ~NIP_E;
break;
case EJUSTRETURN:
break;
default:
tf->tf_r[2] = error;
tf->tf_epsr |= PSR_C;
tf->tf_snip = tf->tf_snip & ~NIP_E;
tf->tf_sfip = tf->tf_sfip & ~FIP_E;
break;
}
mi_syscall_return(p, code, error, rval);
}
#endif
#ifdef M88110
void
m88110_syscall(register_t code, struct trapframe *tf)
{
const struct sysent *callp = sysent;
struct proc *p = curproc;
int error;
register_t rval[2] __aligned(8);
register_t *args;
atomic_inc_int(&uvmexp.syscalls);
p->p_md.md_tf = tf;
if (code > 0 && code < SYS_MAXSYSCALL)
callp += code;
args = &tf->tf_r[2];
rval[0] = 0;
rval[1] = tf->tf_r[3];
error = mi_syscall(p, code, callp, args, rval);
switch (error) {
case 0:
tf->tf_r[2] = rval[0];
tf->tf_r[3] = rval[1];
tf->tf_epsr &= ~PSR_C;
m88110_skip_insn(tf);
m88110_skip_insn(tf);
break;
case ERESTART:
break;
case EJUSTRETURN:
m88110_skip_insn(tf);
break;
default:
tf->tf_r[2] = error;
tf->tf_epsr |= PSR_C;
m88110_skip_insn(tf);
break;
}
mi_syscall_return(p, code, error, rval);
}
#endif
void
child_return(void *arg)
{
struct proc *p = arg;
struct trapframe *tf;
tf = (struct trapframe *)USER_REGS(p);
tf->tf_r[2] = 0;
tf->tf_epsr &= ~PSR_C;
#ifdef M88100
if (CPU_IS88100) {
tf->tf_snip = (tf->tf_sfip & XIP_ADDR) | XIP_V;
tf->tf_sfip = tf->tf_snip + 4;
}
#endif
#ifdef M88110
if (CPU_IS88110) {
m88110_skip_insn(tf);
m88110_skip_insn(tf);
}
#endif
KERNEL_UNLOCK();
mi_child_return(p);
}
#ifdef PTRACE
#include <sys/ptrace.h>
vaddr_t ss_branch_taken(u_int, vaddr_t, struct reg *);
int ss_get_value(struct proc *, vaddr_t, u_int *);
int ss_inst_branch_or_call(u_int);
int ss_put_breakpoint(struct proc *, vaddr_t, vaddr_t *, u_int *);
#define SYSCALL_INSTR 0xf000d080
int
ss_get_value(struct proc *p, vaddr_t addr, u_int *value)
{
struct uio uio;
struct iovec iov;
iov.iov_base = (caddr_t)value;
iov.iov_len = sizeof(u_int);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)addr;
uio.uio_resid = sizeof(u_int);
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = curproc;
return (process_domem(curproc, p->p_p, &uio, PT_READ_I));
}
int
ss_put_value(struct proc *p, vaddr_t addr, u_int value)
{
struct uio uio;
struct iovec iov;
iov.iov_base = (caddr_t)&value;
iov.iov_len = sizeof(u_int);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)addr;
uio.uio_resid = sizeof(u_int);
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_WRITE;
uio.uio_procp = curproc;
return (process_domem(curproc, p->p_p, &uio, PT_WRITE_I));
}
vaddr_t
ss_branch_taken(u_int inst, vaddr_t pc, struct reg *regs)
{
u_int regno;
switch (inst >> (32 - 5)) {
case 0x18:
case 0x19:
inst = (inst & 0x03ffffff) << 2;
if (inst & 0x08000000)
inst |= 0xf0000000;
return (pc + inst);
case 0x1a:
case 0x1b:
case 0x1d:
inst = (inst & 0x0000ffff) << 2;
if (inst & 0x00020000)
inst |= 0xfffc0000;
return (pc + inst);
case 0x1e:
regno = inst & 0x1f;
return (regno == 0 ? 0 : regs->r[regno]);
default:
return (pc + 8);
}
}
int
ss_inst_branch_or_call(u_int ins)
{
switch (ins >> (32 - 5)) {
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1d:
return (TRUE);
case 0x1e:
if ((ins & 0xfffff3e0) == 0xf400c000)
return (TRUE);
}
return (FALSE);
}
int
ss_put_breakpoint(struct proc *p, vaddr_t va, vaddr_t *bpva, u_int *bpsave)
{
int rc;
if (*bpva != 0) {
ss_put_value(p, *bpva, *bpsave);
*bpva = 0;
}
if ((rc = ss_get_value(p, va, bpsave)) != 0)
return (rc);
*bpva = va;
return (ss_put_value(p, va, SSBREAKPOINT));
}
int
process_sstep(struct proc *p, int sstep)
{
struct reg *sstf = USER_REGS(p);
vaddr_t pc, brpc;
u_int32_t instr;
int rc;
if (sstep == 0) {
if (p->p_md.md_bp0va != 0) {
ss_put_value(p, p->p_md.md_bp0va, p->p_md.md_bp0save);
p->p_md.md_bp0va = 0;
}
if (p->p_md.md_bp1va != 0) {
ss_put_value(p, p->p_md.md_bp1va, p->p_md.md_bp1save);
p->p_md.md_bp1va = 0;
}
return (0);
}
pc = PC_REGS(sstf);
if ((rc = ss_get_value(p, pc, &instr)) != 0)
return (rc);
if (ss_inst_branch_or_call(instr) || instr == SYSCALL_INSTR) {
brpc = ss_branch_taken(instr, pc, sstf);
if (brpc != pc && brpc != 0) {
if ((rc = ss_put_breakpoint(p, brpc,
&p->p_md.md_bp1va, &p->p_md.md_bp1save)) != 0)
return (rc);
}
}
if ((rc = ss_put_breakpoint(p, pc + 4,
&p->p_md.md_bp0va, &p->p_md.md_bp0save)) != 0)
return (rc);
return (0);
}
#endif
#ifdef DIAGNOSTIC
void
splassert_check(int wantipl, const char *func)
{
int oldipl;
oldipl = getipl();
if (oldipl < wantipl) {
splassert_fail(wantipl, oldipl, func);
(void)splraise(wantipl);
}
}
#endif
int
double_reg_fixup(struct trapframe *frame, int fault)
{
uint32_t pc, instr, value;
int regno, store;
vaddr_t addr;
pc = PC_REGS(&frame->tf_regs);
if (copyinsn(NULL, (u_int32_t *)pc, (u_int32_t *)&instr) != 0)
return SIGSEGV;
switch (instr & 0xfc00ffe0) {
case 0xf4001000:
addr = frame->tf_r[(instr >> 16) & 0x1f]
+ frame->tf_r[(instr & 0x1f)];
store = 0;
break;
case 0xf4001200:
addr = frame->tf_r[(instr >> 16) & 0x1f]
+ 8 * frame->tf_r[(instr & 0x1f)];
store = 0;
break;
case 0xf4002000:
addr = frame->tf_r[(instr >> 16) & 0x1f]
+ frame->tf_r[(instr & 0x1f)];
store = 1;
break;
case 0xf4002200:
addr = frame->tf_r[(instr >> 16) & 0x1f]
+ 8 * frame->tf_r[(instr & 0x1f)];
store = 1;
break;
default:
switch (instr >> 26) {
case 0x10000000 >> 26:
addr = (instr & 0x0000ffff) +
frame->tf_r[(instr >> 16) & 0x1f];
store = 0;
break;
case 0x20000000 >> 26:
addr = (instr & 0x0000ffff) +
frame->tf_r[(instr >> 16) & 0x1f];
store = 1;
break;
default:
return SIGBUS;
}
break;
}
regno = (instr >> 21) & 0x1f;
switch (fault) {
case T_MISALGNFLT:
if ((addr & 0x07) != 4)
return SIGBUS;
break;
case T_ILLFLT:
if ((regno & 0x01) == 0)
return SIGILL;
if ((addr & 0x03) != 0)
return SIGBUS;
break;
}
if (store) {
if (regno == 0)
value = 0;
else
value = frame->tf_r[regno];
if (copyout(&value, (void *)addr, sizeof(u_int32_t)) != 0)
return SIGSEGV;
if (regno == 31)
value = 0;
else
value = frame->tf_r[regno + 1];
if (copyout(&value, (void *)(addr + 4), sizeof(u_int32_t)) != 0)
return SIGSEGV;
} else {
if (copyin32((const uint32_t *)addr, &value) != 0)
return SIGSEGV;
if (regno != 0)
frame->tf_r[regno] = value;
if (copyin32((const uint32_t *)(addr + 4), &value) != 0)
return SIGSEGV;
if (regno != 31)
frame->tf_r[regno + 1] = value;
}
return 0;
}
void
cache_flush(struct trapframe *tf)
{
struct proc *p = curproc;
struct pmap *pmap;
paddr_t pa;
vaddr_t va;
vsize_t len, count;
p->p_md.md_tf = tf;
pmap = vm_map_pmap(&p->p_vmspace->vm_map);
va = tf->tf_r[2];
len = tf->tf_r[3];
if (va < VM_MIN_ADDRESS || va >= VM_MAXUSER_ADDRESS ||
va + len <= va || va + len >= VM_MAXUSER_ADDRESS)
len = 0;
while (len != 0) {
count = min(len, PAGE_SIZE - (va & PAGE_MASK));
if (pmap_extract(pmap, va, &pa) != FALSE)
dma_cachectl(pa, count, DMA_CACHE_SYNC);
va += count;
len -= count;
}
#ifdef M88100
if (CPU_IS88100) {
tf->tf_sfip &= ~FIP_E;
tf->tf_snip &= ~NIP_E;
}
#endif
#ifdef M88110
if (CPU_IS88110) {
m88110_skip_insn(tf);
}
#endif
userret(p);
}