#include <debug.h>
#include <mach_kdb.h>
#include <mach_kdp.h>
#include <db_machine_commands.h>
#include <cpus.h>
#include <kern/thread.h>
#include <machine/pmap.h>
#include <machine/mach_param.h>
#include <device/device_types.h>
#include <mach/vm_param.h>
#include <mach/clock_types.h>
#include <mach/machine.h>
#include <mach/kmod.h>
#include <ppc/boot.h>
#include <kern/misc_protos.h>
#include <kern/startup.h>
#include <ppc/misc_protos.h>
#include <ppc/proc_reg.h>
#include <ppc/thread.h>
#include <ppc/asm.h>
#include <ppc/mem.h>
#include <ppc/Firmware.h>
#include <ppc/low_trace.h>
#include <ppc/mappings.h>
#include <ppc/FirmwareCalls.h>
#include <ppc/setjmp.h>
#include <ppc/exception.h>
#include <kern/clock.h>
#include <kern/debug.h>
#include <machine/trap.h>
#include <kern/spl.h>
#include <pexpert/pexpert.h>
#include <ppc/mp.h>
#include <IOKit/IOPlatformExpert.h>
#include <mach/vm_prot.h>
#include <vm/pmap.h>
#include <mach/time_value.h>
#include <machine/machparam.h>
#if MACH_KDB
#include <ddb/db_aout.h>
#include <ddb/db_output.h>
#include <ddb/db_command.h>
#include <machine/db_machdep.h>
extern struct db_command ppc_db_commands[];
#endif
char kernel_args_buf[256] = "/mach_kernel";
char boot_args_buf[256] = "/mach_servers/bootstrap";
char env_buf[256];
#define TRAP_DEBUGGER __asm__ volatile("tw 4,r3,r3");
#define TRAP_DEBUGGER_INST 0x7c831808
#define TRAP_DIRECT __asm__ volatile("tw 4,r4,r4");
#define TRAP_DIRECT_INST 0x7c842008
#define TRAP_INST_SIZE 4
#define BREAK_TO_KDP0 0x7fe00008
#define BREAK_TO_KDP1 0x7c800008
#define BREAK_TO_KDB0 0x7c810808
hw_lock_data_t debugger_lock;
hw_lock_data_t pbtlock;
int debugger_cpu = -1;
int debugger_debug = 0;
int debugger_is_slave[NCPUS];
int debugger_active[NCPUS];
int debugger_pending[NCPUS];
int debugger_holdoff[NCPUS];
int db_run_mode;
unsigned int debugger_sync = 0;
extern unsigned int NMIss;
extern volatile int panicwait;
volatile unsigned int pbtcnt = 0;
volatile unsigned int pbtcpu = -1;
unsigned int lastTrace;
volatile unsigned int cpus_holding_bkpts;
void unlock_debugger(void);
void lock_debugger(void);
void dump_backtrace(unsigned int stackptr, unsigned int fence);
void dump_savearea(savearea *sv, unsigned int fence);
#if !MACH_KDB
boolean_t db_breakpoints_inserted = TRUE;
jmp_buf_t *db_recover = 0;
#endif
#if MACH_KDB
#include <ddb/db_run.h>
int kdb_flag=0;
extern boolean_t db_breakpoints_inserted;
extern jmp_buf_t *db_recover;
#define KDB_READY 0x1
#endif
#if MACH_KDP
extern int kdp_flag;
#define KDP_READY 0x1
#endif
boolean_t db_im_stepping = 0xFFFFFFFF;
char *failNames[] = {
"Debugging trap",
"Corrupt stack",
"Corrupt mapping tables",
"Corrupt context",
"No saveareas",
"Savearea corruption",
"Invalid live context",
"Unknown failure code"
};
char *invxcption = "Unknown code";
extern const char version[];
extern char *trap_type[];
extern vm_offset_t mem_actual;
#if !MACH_KDB
void kdb_trap(int type, struct savearea *regs);
void kdb_trap(int type, struct savearea *regs) {
return;
}
#endif
#if !MACH_KDP
void kdp_trap(int type, struct savearea *regs);
void kdp_trap(int type, struct savearea *regs) {
return;
}
#endif
void
machine_startup(boot_args *args)
{
int boot_arg;
if (PE_parse_boot_arg("cpus", &wncpu)) {
if (!((wncpu > 0) && (wncpu < NCPUS)))
wncpu = NCPUS;
} else
wncpu = NCPUS;
if( PE_get_hotkey( kPEControlKey ))
halt_in_debugger = halt_in_debugger ? 0 : 1;
if (PE_parse_boot_arg("debug", &boot_arg)) {
if (boot_arg & DB_HALT) halt_in_debugger=1;
if (boot_arg & DB_PRT) disableDebugOuput=FALSE;
if (boot_arg & DB_SLOG) systemLogDiags=TRUE;
if (boot_arg & DB_NMI) panicDebugging=TRUE;
if (boot_arg & DB_LOG_PI_SCRN) logPanicDataToScreen=TRUE;
}
hw_lock_init(&debugger_lock);
hw_lock_init(&pbtlock);
#if MACH_KDB
#if DB_MACHINE_COMMANDS
db_machine_commands_install(ppc_db_commands);
#endif
ddb_init();
if (boot_arg & DB_KDB)
current_debugger = KDB_CUR_DB;
if (halt_in_debugger && (current_debugger == KDB_CUR_DB)) {
Debugger("inline call to debugger(machine_startup)");
halt_in_debugger = 0;
active_debugger =1;
}
#endif
if (PE_parse_boot_arg("preempt", &boot_arg)) {
extern int default_preemption_rate;
default_preemption_rate = boot_arg;
}
if (PE_parse_boot_arg("unsafe", &boot_arg)) {
extern int max_unsafe_quanta;
max_unsafe_quanta = boot_arg;
}
if (PE_parse_boot_arg("poll", &boot_arg)) {
extern int max_poll_quanta;
max_poll_quanta = boot_arg;
}
if (PE_parse_boot_arg("yield", &boot_arg)) {
extern int sched_poll_yield_shift;
sched_poll_yield_shift = boot_arg;
}
machine_conf();
ml_thrm_init();
setup_main();
}
char *
machine_boot_info(
char *buf,
vm_size_t size)
{
return(PE_boot_args());
}
void
machine_conf(void)
{
machine_info.max_cpus = NCPUS;
machine_info.avail_cpus = 1;
machine_info.memory_size = mem_size;
}
void
machine_init(void)
{
clock_config();
}
void slave_machine_init(void)
{
(void) ml_set_interrupts_enabled(FALSE);
clock_init();
cpu_machine_init();
}
void
halt_all_cpus(boolean_t reboot)
{
if(reboot)
{
printf("MACH Reboot\n");
PEHaltRestart(kPERestartCPU);
}
else
{
printf("CPU halted\n");
PEHaltRestart(kPEHaltCPU);
}
while(1);
}
void
halt_cpu(void)
{
halt_all_cpus(FALSE);
}
#if MACH_ASSERT
void machine_callstack(
natural_t *buf,
vm_size_t callstack_max)
{
}
#endif
void
print_backtrace(struct savearea *ssp)
{
unsigned int stackptr, *raddr, *rstack, trans, fence;
int i, frames_cnt, skip_top_frames, frames_max;
unsigned int store[8];
vm_offset_t backtrace_entries[32];
thread_act_t *act;
savearea *sv, *svssp;
int cpu;
cpu = cpu_number();
if(pbtcpu != cpu) {
hw_atomic_add(&pbtcnt, 1);
while(!hw_lock_try(&pbtlock));
pbtcpu = cpu;
}
svssp = (savearea *)ssp;
sv = 0;
if(current_thread()) sv = (savearea *)current_act()->mact.pcb;
fence = 0xFFFFFFFF;
if(sv) fence = sv->save_r1;
if(!svssp) {
kdb_printf("Latest stack backtrace for cpu %d:\n", cpu_number());
__asm__ volatile("mr %0,r1" : "=r" (stackptr));
dump_backtrace(stackptr, fence);
if(!sv) {
kdb_printf("\nKernel version:\n%s\n",version);
hw_lock_unlock(&pbtlock);
return;
}
}
else {
fence = 0xFFFFFFFF;
if(svssp->save_hdr.save_prev) {
if((svssp->save_hdr.save_prev <= VM_MAX_KERNEL_ADDRESS) && ((unsigned int)LRA(PPC_SID_KERNEL, (void *)svssp->save_hdr.save_prev))) {
fence = svssp->save_hdr.save_prev->save_r1;
}
}
kdb_printf("Latest crash info for cpu %d:\n", cpu_number());
kdb_printf(" Exception state (sv=0x%08X)\n", sv);
dump_savearea(svssp, fence);
}
if(!sv) {
kdb_printf("\nKernel version:\n%s\n",version);
hw_lock_unlock(&pbtlock);
return;
}
kdb_printf("Proceeding back via exception chain:\n");
while(sv) {
if(!((sv <= VM_MAX_KERNEL_ADDRESS) && (unsigned int)LRA(PPC_SID_KERNEL, (void *)sv))) {
kdb_printf(" Exception state (sv=0x%08X) Not mapped or invalid. stopping...\n", sv);
break;
}
kdb_printf(" Exception state (sv=0x%08X)\n", sv);
if(sv == svssp) {
kdb_printf(" previously dumped as \"Latest\" state. skipping...\n");
}
else {
fence = 0xFFFFFFFF;
if(sv->save_hdr.save_prev) {
if((sv->save_hdr.save_prev <= VM_MAX_KERNEL_ADDRESS) && ((unsigned int)LRA(PPC_SID_KERNEL, (void *)sv->save_hdr.save_prev))) {
fence = sv->save_hdr.save_prev->save_r1;
}
}
dump_savearea(sv, fence);
}
sv = sv->save_hdr.save_prev;
}
kdb_printf("\nKernel version:\n%s\n",version);
pbtcpu = -1;
hw_lock_unlock(&pbtlock);
hw_atomic_sub(&pbtcnt, 1);
while(pbtcnt);
return;
}
void dump_savearea(savearea *sv, unsigned int fence) {
char *xcode;
if(sv->save_exception > T_MAX) xcode = invxcption;
else xcode = trap_type[sv->save_exception / 4];
kdb_printf(" PC=0x%08X; MSR=0x%08X; DAR=0x%08X; DSISR=0x%08X; LR=0x%08X; R1=0x%08X; XCP=0x%08X (%s)\n",
sv->save_srr0, sv->save_srr1, sv->save_dar, sv->save_dsisr,
sv->save_lr, sv->save_r1, sv->save_exception, xcode);
if(!(sv->save_srr1 & MASK(MSR_PR))) {
dump_backtrace(sv->save_r1, fence);
}
return;
}
#define DUMPFRAMES 32
#define LRindex 2
void dump_backtrace(unsigned int stackptr, unsigned int fence) {
unsigned int bframes[DUMPFRAMES];
unsigned int sframe[8], raddr, dumbo;
int i;
kdb_printf(" Backtrace:\n");
for(i = 0; i < DUMPFRAMES; i++) {
if(!stackptr || (stackptr == fence)) break;
if(stackptr & 0x0000000f) {
kdb_printf("\n backtrace terminated - unaligned frame address: 0x%08X\n", stackptr);
break;
}
raddr = (unsigned int)LRA(PPC_SID_KERNEL, (void *)stackptr);
if(!raddr || (stackptr > VM_MAX_KERNEL_ADDRESS)) {
kdb_printf("\n backtrace terminated - frame not mapped or invalid: 0x%08X\n", stackptr);
break;
}
if(raddr >= mem_actual) {
kdb_printf("\n backtrace terminated - frame outside of RAM: v=0x%08X, p=%08X\n", stackptr, raddr);
break;
}
ReadReal(raddr, &sframe[0]);
bframes[i] = sframe[LRindex];
if(!i) kdb_printf(" ");
else if(!(i & 7)) kdb_printf("\n ");
kdb_printf("0x%08X ", bframes[i]);
stackptr = sframe[0];
}
kdb_printf("\n");
if(i >= DUMPFRAMES) kdb_printf(" backtrace continues...\n");
if(i) kmod_dump((vm_offset_t *)&bframes[0], i);
}
void
Debugger(const char *message) {
int i;
unsigned int store[8];
unsigned long pi_size = 0;
spl_t spl;
spl = splhigh();
if ((panicstr != (char *)0) &&
(((nestedpanic != 0) && (current_debugger == 1)) || (active_debugger == 0))) {
print_backtrace(NULL);
if (nestedpanic != 0) {
splx(spl);
return;
}
}
if (debug_mode && debugger_active[cpu_number()]) {
splx(spl);
return;
}
if ( panicstr != (char *)0 )
{
disable_preemption();
if( debug_buf_size > 0)
pi_size = PESavePanicInfo( debug_buf, debug_buf_ptr - debug_buf);
if( !panicDebugging && (pi_size != 0) ) {
int my_cpu, debugger_cpu;
int tcpu;
my_cpu = cpu_number();
debugger_cpu = my_cpu;
hw_atomic_add(&debug_mode, 1);
debugger_active[my_cpu]++;
lock_debugger();
for(tcpu = 0; tcpu < NCPUS; tcpu++) {
if(tcpu == my_cpu) continue;
hw_atomic_add(&debugger_sync, 1);
(void)cpu_signal(tcpu, SIGPdebug, 0 ,0);
}
(void)hw_cpu_sync(&debugger_sync, LockTimeOut);
debugger_sync = 0;
}
draw_panic_dialog();
if( !panicDebugging && (pi_size != 0))
PEHaltRestart( kPEHangCPU );
enable_preemption();
}
if ((current_debugger != NO_CUR_DB)) {
printf("Debugger(%s)\n", message);
TRAP_DEBUGGER;
splx(spl);
return;
}
printf("\nNo debugger configured - dumping debug information\n");
printf("MSR=%08X\n",mfmsr());
print_backtrace(NULL);
splx(spl);
return;
}
void SysChoked(int type, savearea *sv) {
unsigned int failcode;
mp_disable_preemption();
disableDebugOuput = FALSE;
debug_mode = TRUE;
failcode = sv->save_r3;
if(failcode > failUnknown) failcode = failUnknown;
kprintf("System Failure: cpu=%d; code=%08X (%s)\n", cpu_number(), sv->save_r3, failNames[failcode]);
kdb_printf("System Failure: cpu=%d; code=%08X (%s)\n", cpu_number(), sv->save_r3, failNames[failcode]);
print_backtrace(sv);
Call_DebuggerC(type, sv);
if ((current_debugger != NO_CUR_DB)) Call_DebuggerC(type, sv);
}
int Call_DebuggerC(
int type,
struct savearea *saved_state)
{
int directcall, wait;
vm_offset_t instr_ptr;
unsigned int instr;
int my_cpu, tcpu;
my_cpu = cpu_number();
#if MACH_KDB
if((debugger_cpu == my_cpu) &&
debugger_active[my_cpu] &&
db_recover &&
(current_debugger == KDB_CUR_DB)) {
kdb_trap(type, saved_state);
}
#endif
hw_atomic_add(&debug_mode, 1);
debugger_active[my_cpu]++;
lock_debugger();
if(db_im_stepping == my_cpu) {
enable_preemption_no_check();
db_im_stepping = 0xFFFFFFFF;
}
if (debugger_debug) {
#if 0
kprintf("Call_DebuggerC(%d): %08X %08X, debact = %d\n", my_cpu, type, saved_state, debug_mode);
#endif
printf("Call_Debugger: enter - cpu %d, is_slave %d, debugger_cpu %d, pc %08X\n",
my_cpu, debugger_is_slave[my_cpu], debugger_cpu, saved_state->save_srr0);
}
if (instr_ptr = (vm_offset_t)LRA(PPC_SID_KERNEL, (void *)(saved_state->save_srr0))) {
instr = ml_phys_read(instr_ptr);
}
else instr = 0;
#if 0
if (debugger_debug) kprintf("Call_DebuggerC(%d): instr_ptr = %08X, instr = %08X\n", my_cpu, instr_ptr, instr);
#endif
if (db_breakpoints_inserted) cpus_holding_bkpts++;
if (debugger_cpu == -1 && !debugger_is_slave[my_cpu]) {
#if 0
if (debugger_debug) kprintf("Call_DebuggerC(%d): lasttrace = %08X\n", my_cpu, lastTrace);
#endif
debugger_cpu = my_cpu;
lastTrace = LLTraceSet(0);
for(tcpu = 0; tcpu < NCPUS; tcpu++) {
if(tcpu == my_cpu) continue;
hw_atomic_add(&debugger_sync, 1);
(void)cpu_signal(tcpu, SIGPdebug, 0 ,0);
}
(void)hw_cpu_sync(&debugger_sync, LockTimeOut);
debugger_sync = 0;
}
else if (debugger_cpu != my_cpu) goto debugger_exit;
if (instr == TRAP_DIRECT_INST) {
disableDebugOuput = FALSE;
print_backtrace(saved_state);
}
switch_debugger = 0;
directcall = 1;
if (saved_state->save_srr1 & MASK(SRR1_PRG_TRAP)) {
directcall = 0;
switch (instr) {
#if MACH_KDP
case BREAK_TO_KDP0:
case BREAK_TO_KDP1:
current_debugger = KDP_CUR_DB;
kdp_trap(type, saved_state);
break;
#endif
#if MACH_KDB
case BREAK_TO_KDB0:
current_debugger = KDB_CUR_DB;
kdb_trap(type, saved_state);
break;
#endif
case TRAP_DEBUGGER_INST:
case TRAP_DIRECT_INST:
if (current_debugger == KDP_CUR_DB)
kdp_trap(type, saved_state);
else if (current_debugger == KDB_CUR_DB)
kdb_trap(type, saved_state);
else goto debugger_error;
break;
default:
goto debugger_error;
}
}
while(1) {
if(!directcall) {
if(!switch_debugger) break;
#if 0
if (debugger_debug) kprintf("Call_DebuggerC(%d): switching debuggers\n", my_cpu);
#endif
#if MACH_KDB
if(current_debugger == KDP_CUR_DB) current_debugger = KDB_CUR_DB;
#if MACH_KDP
else
#endif
#endif
#if MACH_KDP
if(current_debugger == KDB_CUR_DB) current_debugger = KDP_CUR_DB;
#endif
}
switch_debugger = 0;
directcall = 0;
switch (current_debugger) {
case KDP_CUR_DB:
kdp_trap(type, saved_state);
break;
case KDB_CUR_DB:
kdb_trap(type, saved_state);
break;
default:
goto debugger_error;
break;
}
}
debugger_exit:
#if 0
if (debugger_debug) kprintf("Call_DebuggerC(%d): exit - inst = %08X, cpu=%d(%d), run=%d\n", my_cpu,
instr, my_cpu, debugger_cpu, db_run_mode);
#endif
if ((instr == TRAP_DEBUGGER_INST) ||
(instr == TRAP_DIRECT_INST)) saved_state->save_srr0 += TRAP_INST_SIZE;
if(debugger_cpu == my_cpu) LLTraceSet(lastTrace);
wait = FALSE;
if (db_run_mode == STEP_CONTINUE) {
wait = TRUE;
debugger_cpu = -1;
debugger_pending[0] = 0;
debugger_pending[1] = 0;
NMIss = 0;
}
if(db_run_mode == STEP_ONCE) {
disable_preemption();
db_im_stepping = my_cpu;
}
if (db_breakpoints_inserted) cpus_holding_bkpts--;
if (debugger_is_slave[my_cpu]) debugger_is_slave[my_cpu]--;
if (debugger_debug)
printf("Call_Debugger: exit - cpu %d, debugger_cpu %d, run_mode %d holds %d\n",
my_cpu, debugger_cpu, db_run_mode,
cpus_holding_bkpts);
unlock_debugger();
debugger_active[my_cpu]--;
if (wait) while(cpus_holding_bkpts);
hw_atomic_sub(&debug_mode, 1);
return(1);
debugger_error:
if(db_run_mode != STEP_ONCE) enable_preemption_no_check();
hw_atomic_sub(&debug_mode, 1);
return(0);
}
void lock_debugger(void) {
int my_cpu;
register int i;
my_cpu = cpu_number();
while(1) {
if (debugger_cpu != -1 && debugger_cpu != my_cpu) continue;
if (hw_lock_try(&debugger_lock)) {
if (debugger_cpu == -1 || debugger_cpu == my_cpu) break;
hw_lock_unlock(&debugger_lock);
}
}
}
void unlock_debugger(void) {
hw_lock_unlock(&debugger_lock);
}