#include <mach_kdb.h>
#include <mach_kdp.h>
#include <debug.h>
#include <cpus.h>
#include <kern/thread.h>
#include <kern/exception.h>
#include <kern/syscall_sw.h>
#include <kern/cpu_data.h>
#include <kern/debug.h>
#include <mach/thread_status.h>
#include <vm/vm_fault.h>
#include <vm/vm_kern.h>
#include <ppc/misc_protos.h>
#include <ppc/trap.h>
#include <ppc/exception.h>
#include <ppc/proc_reg.h>
#include <ppc/pmap.h>
#include <ppc/mem.h>
#include <ppc/mappings.h>
#include <ppc/Firmware.h>
#include <ppc/low_trace.h>
#include <ppc/Diagnostics.h>
#include <ppc/hw_perfmon.h>
#include <sys/kdebug.h>
perfTrap perfTrapHook = 0;
#if MACH_KDB
#include <ddb/db_watch.h>
#include <ddb/db_run.h>
#include <ddb/db_break.h>
#include <ddb/db_trap.h>
boolean_t let_ddb_vm_fault = FALSE;
boolean_t debug_all_traps_with_kdb = FALSE;
extern struct db_watchpoint *db_watchpoint_list;
extern boolean_t db_watchpoints_inserted;
extern boolean_t db_breakpoints_inserted;
#endif
extern int debugger_active[NCPUS];
extern task_t bsd_init_task;
extern char init_task_failure_data[];
extern int not_in_kdp;
#define PROT_EXEC (VM_PROT_EXECUTE)
#define PROT_RO (VM_PROT_READ)
#define PROT_RW (VM_PROT_READ|VM_PROT_WRITE)
#define UPDATE_PPC_EXCEPTION_STATE { \
thread_act_t thr_act = current_act(); \
thr_act->mact.pcb->save_dar = (uint64_t)dar; \
thr_act->mact.pcb->save_dsisr = dsisr; \
thr_act->mact.pcb->save_exception = trapno / T_VECTOR_SIZE; \
}
static void unresolved_kernel_trap(int trapno,
struct savearea *ssp,
unsigned int dsisr,
addr64_t dar,
char *message);
static void handleMck(struct savearea *ssp);
struct savearea *trap(int trapno,
struct savearea *ssp,
unsigned int dsisr,
addr64_t dar)
{
int exception;
int code;
int subcode;
vm_map_t map;
unsigned int sp;
unsigned int space, space2;
unsigned int offset;
thread_act_t thr_act;
boolean_t intr;
#ifdef MACH_BSD
time_value_t tv;
#endif
if(perfTrapHook) {
if(perfTrapHook(trapno, ssp, dsisr, (unsigned int)dar) == KERN_SUCCESS) return ssp;
}
#if 0
{
extern void fctx_text(void);
fctx_test();
}
#endif
thr_act = current_act();
exception = 0;
intr = (ssp->save_srr1 & MASK(MSR_EE)) != 0;
if (!USER_MODE(ssp->save_srr1)) {
switch (trapno) {
case T_PREEMPT:
ast_taken(AST_PREEMPTION, FALSE);
break;
case T_PERF_MON:
perfmon_handle_pmi(ssp);
break;
case T_RESET:
if (!Call_Debugger(trapno, ssp))
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
break;
case T_DECREMENTER:
case T_IN_VAIN:
case T_SYSTEM_MANAGEMENT:
case T_ALTIVEC_ASSIST:
case T_INTERRUPT:
case T_FP_UNAVAILABLE:
case T_IO_ERROR:
case T_RESERVED:
default:
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
break;
case T_MACHINE_CHECK:
handleMck(ssp);
break;
case T_ALIGNMENT:
KERNEL_DEBUG_CONSTANT(
MACHDBG_CODE(DBG_MACH_EXCP_ALNG, 0) | DBG_FUNC_NONE,
(int)ssp->save_srr0 - 4, (int)dar, (int)dsisr, (int)ssp->save_lr, 0);
break;
case T_EMULATE:
KERNEL_DEBUG_CONSTANT(
MACHDBG_CODE(DBG_MACH_EXCP_EMUL, 0) | DBG_FUNC_NONE,
(int)ssp->save_srr0 - 4, (int)((savearea_comm *)ssp)->save_misc2, (int)dsisr, (int)ssp->save_lr, 0);
break;
case T_TRACE:
case T_RUNMODE_TRACE:
case T_INSTRUCTION_BKPT:
if (!Call_Debugger(trapno, ssp))
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
break;
case T_PROGRAM:
if (ssp->save_srr1 & MASK(SRR1_PRG_TRAP)) {
if (!Call_Debugger(trapno, ssp))
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
} else {
unresolved_kernel_trap(trapno, ssp,
dsisr, dar, NULL);
}
break;
case T_DATA_ACCESS:
#if MACH_KDB
mp_disable_preemption();
if (debug_mode
&& debugger_active[cpu_number()]
&& !let_ddb_vm_fault) {
kdb_trap(trapno, ssp);
}
mp_enable_preemption();
#endif
if (debug_mode
&& debugger_active[cpu_number()]
&& !not_in_kdp) {
kdp_dump_trap(trapno, ssp);
}
if(ssp->save_dsisr & dsiInvMode) {
panic("trap: disallowed access to cache inhibited memory - %016llX\n", dar);
}
if(intr) ml_set_interrupts_enabled(TRUE);
if(((dar >> 28) < 0xE) | ((dar >> 28) > 0xF)) {
offset = (unsigned int)dar;
map = kernel_map;
if((0 == (offset & -PAGE_SIZE)) &&
((thr_act->mact.specFlags) & ignoreZeroFault)) {
ssp->save_srr0 += 4;
break;
}
code = vm_fault(map, trunc_page_32(offset),
dsisr & MASK(DSISR_WRITE) ? PROT_RW : PROT_RO,
FALSE, THREAD_UNINT, NULL, 0);
if (code != KERN_SUCCESS) {
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
} else {
ssp->save_hdr.save_flags |= SAVredrive;
ssp->save_dsisr = (ssp->save_dsisr &
~((MASK(DSISR_NOEX) | MASK(DSISR_PROT)))) | MASK(DSISR_HASH);
}
break;
}
map = thr_act->map;
offset = (unsigned int)(thr_act->mact.cioRelo + dar);
code = vm_fault(map, trunc_page_32(offset),
dsisr & MASK(DSISR_WRITE) ? PROT_RW : PROT_RO,
FALSE, THREAD_UNINT, NULL, 0);
if (code != KERN_SUCCESS) {
if (thr_act->thread->recover) {
act_lock_thread(thr_act);
ssp->save_srr0 = thr_act->thread->recover;
thr_act->thread->recover =
(vm_offset_t)NULL;
act_unlock_thread(thr_act);
} else {
unresolved_kernel_trap(trapno, ssp, dsisr, dar, "copyin/out has no recovery point");
}
}
else {
ssp->save_hdr.save_flags |= SAVredrive;
ssp->save_dsisr = (ssp->save_dsisr &
~((MASK(DSISR_NOEX) | MASK(DSISR_PROT)))) | MASK(DSISR_HASH);
}
break;
case T_INSTRUCTION_ACCESS:
#if MACH_KDB
if (debug_mode
&& debugger_active[cpu_number()]
&& !let_ddb_vm_fault) {
kdb_trap(trapno, ssp);
}
#endif
if(intr) ml_set_interrupts_enabled(TRUE);
map = kernel_map;
code = vm_fault(map, trunc_page_64(ssp->save_srr0),
PROT_EXEC, FALSE, THREAD_UNINT, NULL, 0);
if (code != KERN_SUCCESS) {
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
} else {
ssp->save_hdr.save_flags |= SAVredrive;
ssp->save_srr1 = (ssp->save_srr1 &
~((unsigned long long)(MASK(DSISR_NOEX) | MASK(DSISR_PROT)))) | MASK(DSISR_HASH);
}
break;
case T_SYSTEM_CALL:
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
break;
case T_AST:
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
break;
}
} else {
ml_set_interrupts_enabled(TRUE);
#ifdef MACH_BSD
{
void get_procrustime(time_value_t *);
get_procrustime(&tv);
}
#endif
switch (trapno) {
case T_PREEMPT:
unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
break;
case T_PERF_MON:
perfmon_handle_pmi(ssp);
break;
case T_DECREMENTER:
case T_IN_VAIN:
case T_INTERRUPT:
case T_FP_UNAVAILABLE:
case T_SYSTEM_MANAGEMENT:
case T_RESERVED:
case T_IO_ERROR:
default:
ml_set_interrupts_enabled(FALSE);
panic("Unexpected user state trap(cpu %d): 0x%08X DSISR=0x%08X DAR=0x%016llX PC=0x%016llX, MSR=0x%016llX\n",
cpu_number(), trapno, dsisr, dar, ssp->save_srr0, ssp->save_srr1);
break;
case T_MACHINE_CHECK:
handleMck(ssp);
break;
case T_RESET:
ml_set_interrupts_enabled(FALSE);
if (!Call_Debugger(trapno, ssp))
panic("Unexpected Reset exception: srr0 = %016llx, srr1 = %016llx\n",
ssp->save_srr0, ssp->save_srr1);
break;
case T_ALIGNMENT:
KERNEL_DEBUG_CONSTANT(
MACHDBG_CODE(DBG_MACH_EXCP_ALNG, 0) | DBG_FUNC_NONE,
(int)ssp->save_srr0 - 4, (int)dar, (int)dsisr, (int)ssp->save_lr, 0);
break;
case T_EMULATE:
KERNEL_DEBUG_CONSTANT(
MACHDBG_CODE(DBG_MACH_EXCP_EMUL, 0) | DBG_FUNC_NONE,
(int)ssp->save_srr0 - 4, (int)((savearea_comm *)ssp)->save_misc2, (int)dsisr, (int)ssp->save_lr, 0);
break;
case T_TRACE:
if (be_tracing()) {
add_pcbuffer();
return ssp;
}
case T_INSTRUCTION_BKPT:
exception = EXC_BREAKPOINT;
code = EXC_PPC_TRACE;
subcode = (unsigned int)ssp->save_srr0;
break;
case T_PROGRAM:
if (ssp->save_srr1 & MASK(SRR1_PRG_FE)) {
fpu_save(thr_act->mact.curctx);
UPDATE_PPC_EXCEPTION_STATE;
exception = EXC_ARITHMETIC;
code = EXC_ARITHMETIC;
mp_disable_preemption();
subcode = ssp->save_fpscr;
mp_enable_preemption();
}
else if (ssp->save_srr1 & MASK(SRR1_PRG_ILL_INS)) {
UPDATE_PPC_EXCEPTION_STATE
exception = EXC_BAD_INSTRUCTION;
code = EXC_PPC_UNIPL_INST;
subcode = (unsigned int)ssp->save_srr0;
} else if ((unsigned int)ssp->save_srr1 & MASK(SRR1_PRG_PRV_INS)) {
UPDATE_PPC_EXCEPTION_STATE;
exception = EXC_BAD_INSTRUCTION;
code = EXC_PPC_PRIVINST;
subcode = (unsigned int)ssp->save_srr0;
} else if (ssp->save_srr1 & MASK(SRR1_PRG_TRAP)) {
unsigned int inst;
char *iaddr;
iaddr = CAST_DOWN(char *, ssp->save_srr0);
if (copyin(iaddr, (char *) &inst, 4 )) panic("copyin failed\n");
if(dgWork.dgFlags & enaDiagTrap) {
if((inst & 0xFFFFFFF0) == 0x0FFFFFF0) {
if(diagTrap(ssp, inst & 0xF)) {
ssp->save_srr0 += 4ULL;
exception = 0;
break;
}
}
}
UPDATE_PPC_EXCEPTION_STATE;
if (inst == 0x7FE00008) {
exception = EXC_BREAKPOINT;
code = EXC_PPC_BREAKPOINT;
} else {
exception = EXC_SOFTWARE;
code = EXC_PPC_TRAP;
}
subcode = (unsigned int)ssp->save_srr0;
}
break;
case T_ALTIVEC_ASSIST:
UPDATE_PPC_EXCEPTION_STATE;
exception = EXC_ARITHMETIC;
code = EXC_PPC_ALTIVECASSIST;
subcode = (unsigned int)ssp->save_srr0;
break;
case T_DATA_ACCESS:
map = thr_act->map;
if(ssp->save_dsisr & dsiInvMode) {
UPDATE_PPC_EXCEPTION_STATE;
exception = EXC_BAD_ACCESS;
subcode = (unsigned int)dar;
break;
}
code = vm_fault(map, trunc_page_64(dar),
dsisr & MASK(DSISR_WRITE) ? PROT_RW : PROT_RO,
FALSE, THREAD_ABORTSAFE, NULL, 0);
if ((code != KERN_SUCCESS) && (code != KERN_ABORTED)) {
UPDATE_PPC_EXCEPTION_STATE;
exception = EXC_BAD_ACCESS;
subcode = (unsigned int)dar;
} else {
ssp->save_hdr.save_flags |= SAVredrive;
ssp->save_dsisr = (ssp->save_dsisr &
~((MASK(DSISR_NOEX) | MASK(DSISR_PROT)))) | MASK(DSISR_HASH);
}
break;
case T_INSTRUCTION_ACCESS:
map = thr_act->map;
code = vm_fault(map, trunc_page_64(ssp->save_srr0),
PROT_EXEC, FALSE, THREAD_ABORTSAFE, NULL, 0);
if ((code != KERN_SUCCESS) && (code != KERN_ABORTED)) {
UPDATE_PPC_EXCEPTION_STATE;
exception = EXC_BAD_ACCESS;
subcode = (unsigned int)ssp->save_srr0;
} else {
ssp->save_hdr.save_flags |= SAVredrive;
ssp->save_srr1 = (ssp->save_srr1 &
~((unsigned long long)(MASK(DSISR_NOEX) | MASK(DSISR_PROT)))) | MASK(DSISR_HASH);
}
break;
case T_AST:
ml_set_interrupts_enabled(FALSE);
ast_taken(AST_ALL, intr);
break;
}
#ifdef MACH_BSD
{
void bsd_uprofil(time_value_t *, unsigned int);
bsd_uprofil(&tv, ssp->save_srr0);
}
#endif
}
if (exception) {
#if 0
if(bsd_init_task == current_task()) {
char *buf;
int i;
buf = init_task_failure_data;
buf += sprintf(buf, "Exception Code = 0x%x, Subcode = 0x%x\n", code, subcode);
buf += sprintf(buf, "DSISR = 0x%08x, DAR = 0x%016llx\n"
, dsisr, dar);
for (i=0; i<32; i++) {
if ((i % 8) == 0) {
buf += sprintf(buf, "\n%4d :",i);
}
buf += sprintf(buf, " %08x",*(&ssp->save_r0+i));
}
buf += sprintf(buf, "\n\n");
buf += sprintf(buf, "cr = 0x%08X\t\t",ssp->save_cr);
buf += sprintf(buf, "xer = 0x%08X\n",ssp->save_xer);
buf += sprintf(buf, "lr = 0x%016llX\t\t",ssp->save_lr);
buf += sprintf(buf, "ctr = 0x%016llX\n",ssp->save_ctr);
buf += sprintf(buf, "srr0(iar) = 0x%016llX\t\t",ssp->save_srr0);
buf += sprintf(buf, "srr1(msr) = 0x%016llX\n",ssp->save_srr1,
"\x10\x11""EE\x12PR\x13""FP\x14ME\x15""FE0\x16SE\x18"
"FE1\x19""AL\x1a""EP\x1bIT\x1c""DT");
buf += sprintf(buf, "\n\n");
buf += sprintf(buf, "Application level back trace:\n");
if (ssp->save_srr1 & MASK(MSR_PR)) {
char *addr = (char*)ssp->save_r1;
unsigned int stack_buf[3];
for (i = 0; i < 8; i++) {
if (addr == (char*)NULL)
break;
if (!copyin(addr,(char*)stack_buf,
3 * sizeof(int))) {
buf += sprintf(buf, "0x%08X : 0x%08X\n"
,addr,stack_buf[2]);
addr = (char*)stack_buf[0];
} else {
break;
}
}
}
buf[0] = '\0';
}
#endif
doexception(exception, code, subcode);
}
ml_set_interrupts_enabled(FALSE);
if (USER_MODE(ssp->save_srr1))
while (ast_needed(cpu_number())) {
ast_taken(AST_ALL, intr);
ml_set_interrupts_enabled(FALSE);
}
return ssp;
}
extern int syscall_trace(int, struct savearea *);
extern int pmdebug;
int syscall_trace(int retval, struct savearea *ssp)
{
int i, argc;
int kdarg[3];
kdarg[0]=0;
kdarg[1]=0;
kdarg[2]=0;
argc = mach_trap_table[-((unsigned int)ssp->save_r0)].mach_trap_arg_count;
if (argc > 3)
argc = 3;
for (i=0; i < argc; i++)
kdarg[i] = (int)*(&ssp->save_r3 + i);
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_SC, (-(ssp->save_r0))) | DBG_FUNC_START,
kdarg[0], kdarg[1], kdarg[2], 0, 0);
return retval;
}
extern int syscall_trace_end(int, struct savearea *);
int syscall_trace_end(int retval, struct savearea *ssp)
{
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_SC,(-((unsigned int)ssp->save_r0))) | DBG_FUNC_END,
retval, 0, 0, 0, 0);
return retval;
}
int syscall_error(
int exception,
int code,
int subcode,
struct savearea *ssp)
{
register thread_t thread;
thread = current_thread();
if (thread == 0)
panic("syscall error in boot phase");
if (!USER_MODE(ssp->save_srr1))
panic("system call called from kernel");
doexception(exception, code, subcode);
return 0;
}
void
doexception(
int exc,
int code,
int sub)
{
exception_data_type_t codes[EXCEPTION_CODE_MAX];
codes[0] = code;
codes[1] = sub;
exception(exc, codes, 2);
}
char *trap_type[] = {
"Unknown",
"0x100 - System reset",
"0x200 - Machine check",
"0x300 - Data access",
"0x400 - Inst access",
"0x500 - Ext int",
"0x600 - Alignment",
"0x700 - Program",
"0x800 - Floating point",
"0x900 - Decrementer",
"0xA00 - n/a",
"0xB00 - n/a",
"0xC00 - System call",
"0xD00 - Trace",
"0xE00 - FP assist",
"0xF00 - Perf mon",
"0xF20 - VMX",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"0x1300 - Inst bkpnt",
"0x1400 - Sys mgmt",
"0x1600 - Altivec Assist",
"0x1700 - Thermal",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"INVALID EXCEPTION",
"Emulate",
"0x2000 - Run Mode/Trace",
"Signal Processor",
"Preemption",
"Context Switch",
"Shutdown",
"System Failure"
};
int TRAP_TYPES = sizeof (trap_type) / sizeof (trap_type[0]);
void unresolved_kernel_trap(int trapno,
struct savearea *ssp,
unsigned int dsisr,
addr64_t dar,
char *message)
{
char *trap_name;
extern void print_backtrace(struct savearea *);
extern unsigned int debug_mode, disableDebugOuput;
ml_set_interrupts_enabled(FALSE);
lastTrace = LLTraceSet(0);
if( logPanicDataToScreen )
disableDebugOuput = FALSE;
debug_mode++;
if ((unsigned)trapno <= T_MAX)
trap_name = trap_type[trapno / T_VECTOR_SIZE];
else
trap_name = "???? unrecognized exception";
if (message == NULL)
message = trap_name;
kdb_printf("\n\nUnresolved kernel trap(cpu %d): %s DAR=0x%016llX PC=0x%016llX\n",
cpu_number(), trap_name, dar, ssp->save_srr0);
print_backtrace(ssp);
draw_panic_dialog();
if( panicDebugging )
(void *)Call_Debugger(trapno, ssp);
panic(message);
}
char *corr[2] = {"uncorrected", "corrected "};
void handleMck(struct savearea *ssp) {
int cpu;
cpu = cpu_number();
printf("Machine check (%d) - %s - pc = %016llX, msr = %016llX, dsisr = %08X, dar = %016llX\n",
cpu, corr[ssp->save_hdr.save_misc3], ssp->save_srr0, ssp->save_srr1, ssp->save_dsisr, ssp->save_dar);
printf("Machine check (%d) - AsyncSrc = %016llX, CoreFIR = %016llx\n", cpu, ssp->save_xdat0, ssp->save_xdat1);
printf("Machine check (%d) - L2FIR = %016llX, BusFir = %016llx\n", cpu, ssp->save_xdat2, ssp->save_xdat3);
if(ssp->save_hdr.save_misc3) return;
panic("Uncorrectable machine check: pc = %016llX, msr = %016llX, dsisr = %08X, dar = %016llX\n"
" AsyncSrc = %016llX, CoreFIR = %016llx\n"
" L2FIR = %016llX, BusFir = %016llx\n",
ssp->save_srr0, ssp->save_srr1, ssp->save_dsisr, ssp->save_dar,
ssp->save_xdat0, ssp->save_xdat1, ssp->save_xdat2, ssp->save_xdat3);
return;
}
void
thread_syscall_return(
kern_return_t ret)
{
register thread_act_t thr_act = current_act();
register struct savearea *regs = USER_REGS(thr_act);
if (kdebug_enable && ((unsigned int)regs->save_r0 & 0x80000000)) {
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_SC,(-(regs->save_r0))) | DBG_FUNC_END,
ret, 0, 0, 0, 0);
}
regs->save_r3 = ret;
thread_exception_return();
}
#if MACH_KDB
void
thread_kdb_return(void)
{
register thread_act_t thr_act = current_act();
register thread_t cur_thr = current_thread();
register struct savearea *regs = USER_REGS(thr_act);
Call_Debugger(thr_act->mact.pcb->save_exception, regs);
#if MACH_LDEBUG
assert(cur_thr->mutex_count == 0);
#endif
check_simple_locks();
thread_exception_return();
}
#endif