X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fpowerpc%2Fxmon%2Fxmon.c;fp=arch%2Fpowerpc%2Fxmon%2Fxmon.c;h=77540a2f77040734396570889e6127230d965404;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=7d02fa2a899029d3df4023ef804f4b14588b539c;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 7d02fa2a8..77540a2f7 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2,13 +2,14 @@ * Routines providing a simple monitor for use on the PowerMac. * * Copyright (C) 1996-2005 Paul Mackerras. + * Copyright (C) 2001 PPC64 Team, IBM Corp + * Copyrignt (C) 2006 Michael Ellerman, IBM Corp * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -20,15 +21,14 @@ #include #include #include +#include +#include #include #include #include #include #include -#ifdef CONFIG_PMAC_BACKLIGHT -#include -#endif #include #include #include @@ -36,14 +36,19 @@ #include #include #include -#include +#include +#include +#include +#include #ifdef CONFIG_PPC64 #include #include +#include #endif #include "nonstdio.h" +#include "dis-asm.h" #define scanhex xmon_scanhex #define skipbl xmon_skipbl @@ -107,7 +112,6 @@ static int bsesc(void); static void dump(void); static void prdump(unsigned long, long); static int ppc_inst_dump(unsigned long, long, int); -void print_address(unsigned long); static void backtrace(struct pt_regs *); static void excprint(struct pt_regs *); static void prregs(struct pt_regs *); @@ -141,11 +145,15 @@ static void bootcmds(void); static void proccall(void); void dump_segments(void); static void symbol_lookup(void); +static void xmon_show_stack(unsigned long sp, unsigned long lr, + unsigned long pc); static void xmon_print_symbol(unsigned long address, const char *mid, const char *after); static const char *getvecname(unsigned long vec); -extern int print_insn_powerpc(unsigned long, unsigned long, int); +static int do_spu_cmd(void); + +int xmon_no_auto_backtrace; extern void xmon_enter(void); extern void xmon_leave(void); @@ -191,6 +199,7 @@ Commands:\n\ di dump instructions\n\ df dump float values\n\ dd dump double values\n\ + dr dump stream of raw bytes\n\ e print exception information\n\ f flush cache\n\ la lookup symbol+offset of specified address\n\ @@ -204,8 +213,15 @@ Commands:\n\ mi show information about memory allocation\n\ p call a procedure\n\ r print registers\n\ - s single step\n\ - S print special registers\n\ + s single step\n" +#ifdef CONFIG_SPU_BASE +" ss stop execution on all spus\n\ + sr restore execution on stopped spus\n\ + sf # dump spu fields for spu # (in hex)\n\ + sd # dump spu local store for spu # (in hex)\ + sdi # disassemble spu local store for spu # (in hex)\n" +#endif +" S print special registers\n\ t print backtrace\n\ x exit monitor and recover\n\ X exit monitor and dont recover\n" @@ -502,7 +518,7 @@ static int xmon_core(struct pt_regs *regs, int fromipi) mtmsr(msr); /* restore interrupt enable */ - return cmd != 'X'; + return cmd != 'X' && cmd != EOF; } int xmon(struct pt_regs *excp) @@ -513,17 +529,17 @@ int xmon(struct pt_regs *excp) xmon_save_regs(®s); excp = ®s; } + return xmon_core(excp, 0); } EXPORT_SYMBOL(xmon); -irqreturn_t -xmon_irq(int irq, void *d, struct pt_regs *regs) +irqreturn_t xmon_irq(int irq, void *d) { unsigned long flags; local_irq_save(flags); printf("Keyboard interrupt\n"); - xmon(regs); + xmon(get_irq_regs()); local_irq_restore(flags); return IRQ_HANDLED; } @@ -739,6 +755,12 @@ cmds(struct pt_regs *excp) last_cmd = NULL; xmon_regs = excp; + + if (!xmon_no_auto_backtrace) { + xmon_no_auto_backtrace = 1; + xmon_show_stack(excp->gpr[1], excp->link, excp->nip); + } + for(;;) { #ifdef CONFIG_SMP printf("%x:", smp_processor_id()); @@ -799,6 +821,8 @@ cmds(struct pt_regs *excp) cacheflush(); break; case 's': + if (do_spu_cmd() == 0) + break; if (do_step(excp)) return cmd; break; @@ -1322,7 +1346,7 @@ static void backtrace(struct pt_regs *excp) static void print_bug_trap(struct pt_regs *regs) { - struct bug_entry *bug; + const struct bug_entry *bug; unsigned long addr; if (regs->msr & MSR_PR) @@ -1333,11 +1357,11 @@ static void print_bug_trap(struct pt_regs *regs) bug = find_bug(regs->nip); if (bug == NULL) return; - if (bug->line & BUG_WARNING_TRAP) + if (is_warning_bug(bug)) return; - printf("kernel BUG in %s at %s:%d!\n", - bug->function, bug->file, (unsigned int)bug->line); + printf("kernel BUG at %s:%u!\n", + bug->file, bug->line); } void excprint(struct pt_regs *fp) @@ -1545,11 +1569,6 @@ void super_regs(void) { int cmd; unsigned long val; -#ifdef CONFIG_PPC_ISERIES - struct paca_struct *ptrPaca = NULL; - struct lppaca *ptrLpPaca = NULL; - struct ItLpRegSave *ptrLpRegSave = NULL; -#endif cmd = skipbl(); if (cmd == '\n') { @@ -1566,26 +1585,32 @@ void super_regs(void) printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3)); printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR)); #ifdef CONFIG_PPC_ISERIES - // Dump out relevant Paca data areas. - printf("Paca: \n"); - ptrPaca = get_paca(); - - printf(" Local Processor Control Area (LpPaca): \n"); - ptrLpPaca = ptrPaca->lppaca_ptr; - printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", - ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1); - printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", - ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4); - printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5); - - printf(" Local Processor Register Save Area (LpRegSave): \n"); - ptrLpRegSave = ptrPaca->reg_save_ptr; - printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n", - ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0); - printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n", - ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3); - printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n", - ptrLpRegSave->xMSR, ptrLpRegSave->xNIA); + if (firmware_has_feature(FW_FEATURE_ISERIES)) { + struct paca_struct *ptrPaca; + struct lppaca *ptrLpPaca; + struct ItLpRegSave *ptrLpRegSave; + + /* Dump out relevant Paca data areas. */ + printf("Paca: \n"); + ptrPaca = get_paca(); + + printf(" Local Processor Control Area (LpPaca): \n"); + ptrLpPaca = ptrPaca->lppaca_ptr; + printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", + ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1); + printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", + ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4); + printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5); + + printf(" Local Processor Register Save Area (LpRegSave): \n"); + ptrLpRegSave = ptrPaca->reg_save_ptr; + printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n", + ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0); + printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n", + ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3); + printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n", + ptrLpRegSave->xMSR, ptrLpRegSave->xNIA); + } #endif return; @@ -1938,6 +1963,28 @@ bsesc(void) return c; } +static void xmon_rawdump (unsigned long adrs, long ndump) +{ + long n, m, r, nr; + unsigned char temp[16]; + + for (n = ndump; n > 0;) { + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for (m = 0; m < r; ++m) { + if (m < nr) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + n -= r; + if (nr < r) + break; + } + printf("\n"); +} + #define isxdigit(c) (('0' <= (c) && (c) <= '9') \ || ('a' <= (c) && (c) <= 'f') \ || ('A' <= (c) && (c) <= 'F')) @@ -1960,6 +2007,13 @@ dump(void) nidump = MAX_DUMP; adrs += ppc_inst_dump(adrs, nidump, 1); last_cmd = "di\n"; + } else if (c == 'r') { + scanhex(&ndump); + if (ndump == 0) + ndump = 64; + xmon_rawdump(adrs, ndump); + adrs += ndump; + last_cmd = "dr\n"; } else { scanhex(&ndump); if (ndump == 0) @@ -2014,8 +2068,11 @@ prdump(unsigned long adrs, long ndump) } } +typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr); + int -ppc_inst_dump(unsigned long adr, long count, int praddr) +generic_inst_dump(unsigned long adr, long count, int praddr, + instruction_dump_func dump_func) { int nr, dotted; unsigned long first_adr; @@ -2045,12 +2102,18 @@ ppc_inst_dump(unsigned long adr, long count, int praddr) if (praddr) printf(REG" %.8x", adr, inst); printf("\t"); - print_insn_powerpc(inst, adr, 0); /* always returns 4 */ + dump_func(inst, adr); printf("\n"); } return adr - first_adr; } +int +ppc_inst_dump(unsigned long adr, long count, int praddr) +{ + return generic_inst_dump(adr, count, praddr, print_insn_powerpc); +} + void print_address(unsigned long addr) { @@ -2518,6 +2581,10 @@ void dump_segments(void) void xmon_init(int enable) { +#ifdef CONFIG_PPC_ISERIES + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return; +#endif if (enable) { __debugger = xmon; __debugger_ipi = xmon_ipi; @@ -2539,12 +2606,11 @@ void xmon_init(int enable) } #ifdef CONFIG_MAGIC_SYSRQ -static void sysrq_handle_xmon(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) +static void sysrq_handle_xmon(int key, struct tty_struct *tty) { /* ensure xmon is enabled */ xmon_init(1); - debugger(pt_regs); + debugger(get_irq_regs()); } static struct sysrq_key_op sysrq_xmon_op = @@ -2556,8 +2622,303 @@ static struct sysrq_key_op sysrq_xmon_op = static int __init setup_xmon_sysrq(void) { +#ifdef CONFIG_PPC_ISERIES + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return 0; +#endif register_sysrq_key('x', &sysrq_xmon_op); return 0; } __initcall(setup_xmon_sysrq); #endif /* CONFIG_MAGIC_SYSRQ */ + +int __initdata xmon_early, xmon_off; + +static int __init early_parse_xmon(char *p) +{ + if (!p || strncmp(p, "early", 5) == 0) { + /* just "xmon" is equivalent to "xmon=early" */ + xmon_init(1); + xmon_early = 1; + } else if (strncmp(p, "on", 2) == 0) + xmon_init(1); + else if (strncmp(p, "off", 3) == 0) + xmon_off = 1; + else if (strncmp(p, "nobt", 4) == 0) + xmon_no_auto_backtrace = 1; + else + return 1; + + return 0; +} +early_param("xmon", early_parse_xmon); + +void __init xmon_setup(void) +{ +#ifdef CONFIG_XMON_DEFAULT + if (!xmon_off) + xmon_init(1); +#endif + if (xmon_early) + debugger(NULL); +} + +#ifdef CONFIG_SPU_BASE + +struct spu_info { + struct spu *spu; + u64 saved_mfc_sr1_RW; + u32 saved_spu_runcntl_RW; + unsigned long dump_addr; + u8 stopped_ok; +}; + +#define XMON_NUM_SPUS 16 /* Enough for current hardware */ + +static struct spu_info spu_info[XMON_NUM_SPUS]; + +void xmon_register_spus(struct list_head *list) +{ + struct spu *spu; + + list_for_each_entry(spu, list, full_list) { + if (spu->number >= XMON_NUM_SPUS) { + WARN_ON(1); + continue; + } + + spu_info[spu->number].spu = spu; + spu_info[spu->number].stopped_ok = 0; + spu_info[spu->number].dump_addr = (unsigned long) + spu_info[spu->number].spu->local_store; + } +} + +static void stop_spus(void) +{ + struct spu *spu; + int i; + u64 tmp; + + for (i = 0; i < XMON_NUM_SPUS; i++) { + if (!spu_info[i].spu) + continue; + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + spu = spu_info[i].spu; + + spu_info[i].saved_spu_runcntl_RW = + in_be32(&spu->problem->spu_runcntl_RW); + + tmp = spu_mfc_sr1_get(spu); + spu_info[i].saved_mfc_sr1_RW = tmp; + + tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK; + spu_mfc_sr1_set(spu, tmp); + + sync(); + __delay(200); + + spu_info[i].stopped_ok = 1; + + printf("Stopped spu %.2d (was %s)\n", i, + spu_info[i].saved_spu_runcntl_RW ? + "running" : "stopped"); + } else { + catch_memory_errors = 0; + printf("*** Error stopping spu %.2d\n", i); + } + catch_memory_errors = 0; + } +} + +static void restart_spus(void) +{ + struct spu *spu; + int i; + + for (i = 0; i < XMON_NUM_SPUS; i++) { + if (!spu_info[i].spu) + continue; + + if (!spu_info[i].stopped_ok) { + printf("*** Error, spu %d was not successfully stopped" + ", not restarting\n", i); + continue; + } + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + spu = spu_info[i].spu; + spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW); + out_be32(&spu->problem->spu_runcntl_RW, + spu_info[i].saved_spu_runcntl_RW); + + sync(); + __delay(200); + + printf("Restarted spu %.2d\n", i); + } else { + catch_memory_errors = 0; + printf("*** Error restarting spu %.2d\n", i); + } + catch_memory_errors = 0; + } +} + +#define DUMP_WIDTH 23 +#define DUMP_VALUE(format, field, value) \ +do { \ + if (setjmp(bus_error_jmp) == 0) { \ + catch_memory_errors = 1; \ + sync(); \ + printf(" %-*s = "format"\n", DUMP_WIDTH, \ + #field, value); \ + sync(); \ + __delay(200); \ + } else { \ + catch_memory_errors = 0; \ + printf(" %-*s = *** Error reading field.\n", \ + DUMP_WIDTH, #field); \ + } \ + catch_memory_errors = 0; \ +} while (0) + +#define DUMP_FIELD(obj, format, field) \ + DUMP_VALUE(format, field, obj->field) + +static void dump_spu_fields(struct spu *spu) +{ + printf("Dumping spu fields at address %p:\n", spu); + + DUMP_FIELD(spu, "0x%x", number); + DUMP_FIELD(spu, "%s", name); + DUMP_FIELD(spu, "0x%lx", local_store_phys); + DUMP_FIELD(spu, "0x%p", local_store); + DUMP_FIELD(spu, "0x%lx", ls_size); + DUMP_FIELD(spu, "0x%x", node); + DUMP_FIELD(spu, "0x%lx", flags); + DUMP_FIELD(spu, "0x%lx", dar); + DUMP_FIELD(spu, "0x%lx", dsisr); + DUMP_FIELD(spu, "%d", class_0_pending); + DUMP_FIELD(spu, "0x%lx", irqs[0]); + DUMP_FIELD(spu, "0x%lx", irqs[1]); + DUMP_FIELD(spu, "0x%lx", irqs[2]); + DUMP_FIELD(spu, "0x%x", slb_replace); + DUMP_FIELD(spu, "%d", pid); + DUMP_FIELD(spu, "%d", prio); + DUMP_FIELD(spu, "0x%p", mm); + DUMP_FIELD(spu, "0x%p", ctx); + DUMP_FIELD(spu, "0x%p", rq); + DUMP_FIELD(spu, "0x%p", timestamp); + DUMP_FIELD(spu, "0x%lx", problem_phys); + DUMP_FIELD(spu, "0x%p", problem); + DUMP_VALUE("0x%x", problem->spu_runcntl_RW, + in_be32(&spu->problem->spu_runcntl_RW)); + DUMP_VALUE("0x%x", problem->spu_status_R, + in_be32(&spu->problem->spu_status_R)); + DUMP_VALUE("0x%x", problem->spu_npc_RW, + in_be32(&spu->problem->spu_npc_RW)); + DUMP_FIELD(spu, "0x%p", priv2); + DUMP_FIELD(spu, "0x%p", pdata); +} + +int +spu_inst_dump(unsigned long adr, long count, int praddr) +{ + return generic_inst_dump(adr, count, praddr, print_insn_spu); +} + +static void dump_spu_ls(unsigned long num, int subcmd) +{ + unsigned long offset, addr, ls_addr; + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + ls_addr = (unsigned long)spu_info[num].spu->local_store; + sync(); + __delay(200); + } else { + catch_memory_errors = 0; + printf("*** Error: accessing spu info for spu %d\n", num); + return; + } + catch_memory_errors = 0; + + if (scanhex(&offset)) + addr = ls_addr + offset; + else + addr = spu_info[num].dump_addr; + + if (addr >= ls_addr + LS_SIZE) { + printf("*** Error: address outside of local store\n"); + return; + } + + switch (subcmd) { + case 'i': + addr += spu_inst_dump(addr, 16, 1); + last_cmd = "sdi\n"; + break; + default: + prdump(addr, 64); + addr += 64; + last_cmd = "sd\n"; + break; + } + + spu_info[num].dump_addr = addr; +} + +static int do_spu_cmd(void) +{ + static unsigned long num = 0; + int cmd, subcmd = 0; + + cmd = inchar(); + switch (cmd) { + case 's': + stop_spus(); + break; + case 'r': + restart_spus(); + break; + case 'd': + subcmd = inchar(); + if (isxdigit(subcmd) || subcmd == '\n') + termch = subcmd; + case 'f': + scanhex(&num); + if (num >= XMON_NUM_SPUS || !spu_info[num].spu) { + printf("*** Error: invalid spu number\n"); + return 0; + } + + switch (cmd) { + case 'f': + dump_spu_fields(spu_info[num].spu); + break; + default: + dump_spu_ls(num, subcmd); + break; + } + + break; + default: + return -1; + } + + return 0; +} +#else /* ! CONFIG_SPU_BASE */ +static int do_spu_cmd(void) +{ + return -1; +} +#endif