diff options
author | Kenji Aoyama <aoyama@cvs.openbsd.org> | 2004-04-21 15:24:16 +0000 |
---|---|---|
committer | Kenji Aoyama <aoyama@cvs.openbsd.org> | 2004-04-21 15:24:16 +0000 |
commit | fa26623fb9b8bea0664d8b457f0cb9117f8b650b (patch) | |
tree | 3abec74dfa27f8152e5f2d45ed4e404eac62be3c /sys/arch/luna88k/ddb | |
parent | ea460b0cb47a034a8e87cb24dd8a38b9f83756e6 (diff) |
Initial commit for OpenBSD/luna88k, based on OpenBSD/mvme88k, NetBSD/luna68k and CMU Mach.
Diffstat (limited to 'sys/arch/luna88k/ddb')
-rw-r--r-- | sys/arch/luna88k/ddb/db_disasm.c | 689 | ||||
-rw-r--r-- | sys/arch/luna88k/ddb/db_interface.c | 827 | ||||
-rw-r--r-- | sys/arch/luna88k/ddb/db_sstep.c | 337 | ||||
-rw-r--r-- | sys/arch/luna88k/ddb/db_trace.c | 1143 |
4 files changed, 2996 insertions, 0 deletions
diff --git a/sys/arch/luna88k/ddb/db_disasm.c b/sys/arch/luna88k/ddb/db_disasm.c new file mode 100644 index 00000000000..feef3d4afe7 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_disasm.c @@ -0,0 +1,689 @@ +/* $OpenBSD: db_disasm.c,v 1.1 2004/04/21 15:23:49 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * m88k disassembler for use in ddb + */ + +#include <sys/param.h> /* cputyp and friends */ +#include <sys/types.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_sym.h> /* DB_STGY_PROC, db_printsym() */ +#include <ddb/db_access.h> /* db_get_value() */ +#include <ddb/db_output.h> /* db_printf() */ +#include <ddb/db_interface.h> + +static const char *instwidth[4] = { + ".d", " ", ".h", ".b" +}; + +static const char *condname[6] = { + "gt0 ", "eq0 ", "ge0 ", "lt0 ", "ne0 ", "le0 " +}; + +static const char *m88100_ctrlreg[64] = { + "cr0(PID) ", + "cr1(PSR) ", + "cr2(EPSR) ", + "cr3(SSBR) ", + "cr4(SXIP) ", + "cr5(SNIP) ", + "cr6(SFIP) ", + "cr7(VBR) ", + "cr8(DMT0) ", + "cr9(DMD0) ", + "cr10(DMA0) ", + "cr11(DMT1) ", + "cr12(DMD1) ", + "cr13(DMA1) ", + "cr14(DMT2) ", + "cr15(DMD2) ", + "cr16(DMA2) ", + "cr17(SR0) ", + "cr18(SR1) ", + "cr19(SR2) ", + "cr20(SR3) ", + "fcr0(FPECR)", + "fcr1(FPHS1)", + "fcr2(FPLS1)", + "fcr3(FPHS2)", + "fcr4(FPLS2)", + "fcr5(FPPT) ", + "fcr6(FPRH) ", + "fcr7(FPRL) ", + "fcr8(FPIT) ", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "fcr62(FPSR)", + "fcr63(FPCR)" +}; + +static const char *m88110_ctrlreg[64] = { + "cr0(PID) ", + "cr1(PSR) ", + "cr2(EPSR) ", + NULL, + "cr4(EXIP) ", + "cr5(ENIP) ", + NULL, + "cr7(VBR) ", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "cr14(RES1) ", + "cr15(RES2) ", + "cr16(SR0) ", + "cr17(SR1) ", + "cr18(SR2) ", + "cr19(SR3) ", + "cr20(SR4) ", + "fcr0(FPECR)", + NULL, + NULL, + NULL, + "cr25(ICMD) ", + "cr26(ICTL) ", + "cr27(ISAR) ", + "cr28(ISAP) ", + "cr29(IUAP) ", + "cr30(IIR) ", + "cr31(IBP) ", + "cr32(IPPU) ", + "cr33(IPPL) ", + "cr34(ISR) ", + "cr35(ILAR) ", + "cr36(IPAR) ", + NULL, + NULL, + NULL, + "cr40(DCMD) ", + "cr41(DCTL) ", + "cr42(DSAR) ", + "cr43(DSAP) ", + "cr44(DUAP) ", + "cr45(DIR) ", + "cr46(DBP) ", + "cr47(DPPU) ", + "cr48(DPPL) ", + "cr49(DSR) ", + "cr50(DLAR) ", + "cr51(DPAR) ", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, + "fcr62(FPSR)", + "fcr63(FPCR)" +}; + +#define printval(x) \ + do { \ + if ((x) < 0) \ + db_printf("-0x%X", -(x)); \ + else \ + db_printf("0x%X", (x)); \ + } while (0) + +/* prototypes */ +void oimmed(int, const char *, long); +void ctrlregs(int, const char *, long); +void printsod(int); +void sindou(int, const char *, long); +void jump(int, const char *, long); +void instset(int, const char *, long); +void symofset(int, int, int); +void obranch(int, const char *, long); +void brcond(int, const char *, long); +void otrap(int, const char *, long); +void obit(int, const char *, long); +void bitman(int, const char *, long); +void immem(int, const char *, long); +void nimmem(int, const char *, long); +void lognim(int, const char *, long); +void onimmed(int, const char *, long); + +/* Handlers immediate integer arithmetic instructions */ +void +oimmed(int inst, const char *opcode, long iadr) +{ + int Linst = inst & 0177777; + int Hinst = inst >> 16; + int H6inst = Hinst >> 10; + int rs1 = Hinst & 037; + int rd = ( Hinst >> 5 ) & 037; + + if (( H6inst > 017 ) && ( H6inst < 030 ) && ( H6inst & 01) == 1 ) + db_printf("\t%s.u",opcode); + else { + db_printf("\t%s",opcode); + db_printf(" "); + } + db_printf("\t\tr%-3d,r%-3d,", rd, rs1); + printval(Linst); +} + + +/* Handles instructions dealing with control registers */ +void +ctrlregs(int inst, const char *opcode, long iadr) +{ + int L6inst = (inst >> 11) & 037; + int creg = (inst >> 5) & 077; + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + + db_printf("\t%s",opcode); + + if ( L6inst == 010 || L6inst == 011 ) + db_printf("\t\tr%-3d,%s", rd, + cputyp == CPU_88100 ? m88100_ctrlreg[creg] : m88110_ctrlreg[creg]); + else if ( L6inst == 020 || L6inst == 021 ) + db_printf("\t\tr%-3d,%s", rs1, + cputyp == CPU_88100 ? m88100_ctrlreg[creg] : m88110_ctrlreg[creg]); + else + db_printf("\t\tr%-3d,r%-3d,%s", rd, rs1, + cputyp == CPU_88100 ? m88100_ctrlreg[creg] : m88110_ctrlreg[creg]); +} + + +void +printsod(int t) +{ + if ( t == 0 ) + db_printf("s"); + else + db_printf("d"); +} + +/* Handles floating point instructions */ +void +sindou(int inst, const char *opcode, long iadr) +{ + int rs2 = inst & 037; + int td = ( inst >> 5 ) & 03; + int t2 = ( inst >> 7 ) & 03; + int t1 = ( inst >> 9 ) & 03; + int rs1 = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int checkbits = ( inst >> 11 ) & 037; + + db_printf("\t%s.",opcode); + printsod(td); + if (( checkbits > 010 && checkbits < 014 ) || ( checkbits == 04 )) { + printsod(t2); + db_printf(" "); + if ( checkbits == 012 || checkbits == 013 ) + db_printf("\t\tr%-3d,r%-3d", rd, rs2); + else + db_printf("\t\tr%-3d,r%-3d", rd, rs2); + } else { + printsod(t1);printsod(t2); + db_printf("\t\tr%-3d,r%-3d,r%-3d", rd, rs1, rs2); + } +} + + +void +jump(int inst, const char *opcode, long iadr) +{ + int rs2 = inst & 037; + int Nbit = ( inst >> 10 ) & 01; + + db_printf("\t%s",opcode); + if ( Nbit == 1 ) + db_printf(".n"); + else + db_printf(" "); + db_printf("\t\tr%-3d",rs2); +} + + +/* Handles ff1, ff0, tbnd and rte instructions */ +void +instset(int inst, const char *opcode, long iadr) +{ + int rs2 = inst & 037; + int rs1 = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int checkbits = ( inst >> 10 ) & 077; + int H6inst = ( inst >> 26 ) & 077; + + db_printf("\t%s",opcode); + if ( H6inst == 076 ) { + db_printf("\t\tr%-3d,",rs1); + printval(inst & 0177777); + } else if (( checkbits == 072 ) || ( checkbits == 073 )) + db_printf("\t\tr%-3d,r%-3d", rd, rs2); + else if ( checkbits == 076 ) + db_printf("\t\tr%-3d,r%-3d",rs1,rs2); +} + +void +symofset(int disp, int bit, int iadr) +{ + long addr; + + if ( disp & (1 << (bit-1)) ) { + /* negative value */ + addr = iadr + ((disp << 2) | (~0 << bit)); + } else { + addr = iadr + (disp << 2); + } + db_printsym(addr,DB_STGY_PROC, db_printf); + return; +} + +void +obranch(int inst, const char *opcode, long iadr) +{ + int cond = ( inst >> 26 ) & 01; + int disp = inst &0377777777; + + if ( cond == 0 ) { + db_printf("\t%s\t\t",opcode); + symofset(disp, 26, iadr); + } else { + db_printf("\t%s.n\t\t",opcode); + symofset(disp, 26, iadr); + } +} + + +/* Handles branch on conditions instructions */ +void +brcond(int inst, const char *opcode, long iadr) +{ + int cond = ( inst >> 26 ) & 1; + int match = ( inst >> 21 ) & 037; + int rs = ( inst >> 16 ) & 037; + int disp = ( inst & 0177777 ); + + if ( cond == 0 ) + db_printf("\t%s\t\t", opcode); + else + db_printf("\t%s.n\t\t", opcode); + if ( ( ( inst >> 27 ) & 03 ) == 1 ) + switch (match) { + case 1 : db_printf("%s,", condname[0]); break; + case 2 : db_printf("%s,", condname[1]); break; + case 3 : db_printf("%s,", condname[2]); break; + case 12: db_printf("%s,", condname[3]); break; + case 13: db_printf("%s,", condname[4]); break; + case 14: db_printf("%s,", condname[5]); break; + default: printval(match); + db_printf(","); + } else { + printval(match); + db_printf(","); + } + + db_printf("r%-3d,", rs); + symofset(disp, 16, iadr); +} + + +void +otrap(int inst, const char *opcode, long iadr) +{ + int vecno = inst & 0777; + int match = ( inst >> 21 ) & 037; + int rs = ( inst >> 16 ) & 037; + + db_printf("\t%s\t",opcode); + if ( ( ( inst >> 12 ) & 017 ) == 0xe ) + switch (match) { + case 1 : db_printf("%s,", condname[0]);break; + case 2 : db_printf("%s,", condname[1]);break; + case 3 : db_printf("%s,", condname[2]);break; + case 12: db_printf("%s,", condname[3]);break; + case 13: db_printf("%s,", condname[4]);break; + case 14: db_printf("%s,", condname[5]);break; + default: printval(match); + db_printf(","); + } else { + printval(match); + db_printf(","); + } + db_printf("\tr%-3d,", rs); + printval(vecno); +} + + +/* Handles 10 bit immediate bit field operations */ +void +obit(int inst, const char *opcode, long iadr) +{ + int rs = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int width = ( inst >> 5 ) & 037; + int offset = ( inst & 037 ); + + db_printf("\t%s\t\tr%-3d,r%-3d,", opcode, rd, rs); + if ( ( ( inst >> 10 ) & 077 ) == 052 ) { + db_printf("<"); + printval(offset); + db_printf(">"); + } else { + printval(width); + db_printf("<"); + printval(offset); + db_printf(">"); + } +} + + +/* Handles triadic mode bit field instructions */ +void +bitman(int inst, const char *opcode, long iadr) +{ + + int rs1 = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int rs2 = inst & 037; + + db_printf("\t%s\t\tr%-3d,r%-3d,r%-3d", opcode, rd, rs1, rs2); +} + + +/* Handles immediate load/store/exchange instructions */ +void +immem(int inst, const char *opcode, long iadr) +{ + int immed = inst & 0xFFFF; + int rd = (inst >> 21) & 037; + int rs = (inst >> 16) & 037; + int st_lda = (inst >> 28) & 03; + int aryno = (inst >> 26) & 03; + char c = ' '; + + if (!st_lda) { + if ((aryno == 0) || (aryno == 01)) + opcode = "xmem"; + else + opcode = "ld"; + if (aryno == 0) + aryno = 03; + if (!(aryno == 01)) + c = 'u'; + } else + if (st_lda == 01) + opcode = "ld"; + + db_printf("\t%s%s%c\t\tr%-3d,r%-3d,", opcode, instwidth[aryno], + c, rd, rs); + printval(immed); +} + + +/* Handles triadic mode load/store/exchange instructions */ +void +nimmem(int inst, const char *opcode, long iadr) +{ + int scaled = (inst >> 9) & 01; + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + int rs2 = inst & 037; + int st_lda = (inst >> 12) & 03; + int aryno = (inst >> 10) & 03; + int user_bit = 0; + int signed_fg = 1; + char *user = " "; + char c = ' '; + + if (!st_lda) { + if ((aryno == 0) || (aryno == 01)) + opcode = "xmem"; + else + opcode = "ld"; + if (aryno == 0) + aryno = 03; + if (!(aryno == 01)) { + c = 'u'; + signed_fg = 0; + } + } else + if (st_lda == 01) + opcode = "ld"; + + if (!(st_lda == 03)) { + user_bit = (inst >> 8) & 01; + if (user_bit) + user = ".usr"; + } + + if (user_bit && signed_fg && (aryno == 01)) { + if (st_lda) + db_printf("\t%s%s\tr%-3d,r%-3d", opcode, + user, rd, rs1); + else + db_printf("\t%s%s\tr%-3d,r%-3d", opcode, + user, rd, rs1); + } else + if (user_bit && signed_fg) + db_printf("\t%s%s%s\tr%-3d,r%-3d", opcode, + instwidth[aryno], user, rd, rs1); + else + db_printf("\t%s%s%c%s\tr%-3d,r%-3d", opcode, + instwidth[aryno], c, user, rd, rs1); + + if (scaled) + db_printf("[r%-3d]", rs2); + else + db_printf(",r%-3d", rs2); +} + + +/* Handles triadic mode logical instructions */ +void +lognim(int inst, const char *opcode, long iadr) +{ + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + int rs2 = inst & 037; + int complemt = (inst >> 10) & 01; + char *c = " "; + + if (complemt) + c = ".c"; + + db_printf("\t%s%s\t\tr%-3d,r%-3d,r%-3d", opcode, c, rd, rs1, rs2); +} + + +/* Handles triadic mode arithmetic instructions */ +void +onimmed(int inst, const char *opcode, long iadr) +{ + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + int rs2 = inst & 037; + int carry = (inst >> 8) & 03; + int nochar = (inst >> 10) & 07; + int nodecode = (inst >> 11) & 01; + char *tab, *c ; + + if (nochar > 02) + tab = "\t\t"; + else + tab = "\t"; + + if (!nodecode) { + if (carry == 01) + c = ".co "; + else + if (carry == 02) + c = ".ci "; + else + if (carry == 03) + c = ".cio"; + else + c = " "; + } else + c = " "; + + db_printf("\t%s%s%sr%-3d,r%-3d,r%-3d", opcode, c, + tab, rd, rs1, rs2); +} + +static const struct opdesc { + unsigned mask, match; + void (*opfun)(int, const char *, long); + const char *farg; +} opdecode[] = { + + /* ORDER IS IMPORTANT BELOW */ + + { 0xF0000000U, 0x00000000U, immem, NULL}, + { 0xF0000000U, 0x10000000U, immem, NULL}, + { 0xF0000000U, 0x20000000U, immem, "st"}, + { 0xF0000000U, 0x30000000U, immem, "lda"}, + + { 0xF8000000U, 0x40000000U, oimmed, "and"}, + { 0xF8000000U, 0x48000000U, oimmed, "mask"}, + { 0xF8000000U, 0x50000000U, oimmed, "xor"}, + { 0xF8000000U, 0x58000000U, oimmed, "or"}, + { 0xFC000000U, 0x60000000U, oimmed, "addu"}, + { 0xFC000000U, 0x64000000U, oimmed, "subu"}, + { 0xFC000000U, 0x68000000U, oimmed, "divu"}, + { 0xFC000000U, 0x6C000000U, oimmed, "mul"}, + { 0xFC000000U, 0x70000000U, oimmed, "add"}, + { 0xFC000000U, 0x74000000U, oimmed, "sub"}, + { 0xFC000000U, 0x78000000U, oimmed, "div"}, + { 0xFC000000U, 0x7C000000U, oimmed, "cmp"}, + + { 0xFC00F800U, 0x80004000U, ctrlregs, "ldcr"}, + { 0xFC00F800U, 0x80004800U, ctrlregs, "fldcr"}, + { 0xFC00F800U, 0x80008000U, ctrlregs, "stcr"}, + { 0xFC00F800U, 0x80008800U, ctrlregs, "fstcr"}, + { 0xFC00F800U, 0x8000C000U, ctrlregs, "xcr"}, + { 0xFC00F800U, 0x8000C800U, ctrlregs, "fxcr"}, + + { 0xFC00F800U, 0x84000000U, sindou, "fmul"}, + { 0xFC1FFF80U, 0x84002000U, sindou, "flt"}, + { 0xFC00F800U, 0x84002800U, sindou, "fadd"}, + { 0xFC00F800U, 0x84003000U, sindou, "fsub"}, + { 0xFC00F860U, 0x84003800U, sindou, "fcmp"}, + { 0xFC1FFE60U, 0x84004800U, sindou, "int"}, + { 0xFC1FFE60U, 0x84005000U, sindou, "nint"}, + { 0xFC1FFE60U, 0x84005800U, sindou, "trnc"}, + { 0xFC00F800U, 0x84007000U, sindou, "fdiv"}, + + { 0xF8000000U, 0xC0000000U, obranch, "br"}, + { 0xF8000000U, 0xC8000000U, obranch, "bsr"}, + + { 0xF8000000U, 0xD0000000U, brcond, "bb0"}, + { 0xF8000000U, 0xD8000000U, brcond, "bb1"}, + { 0xF8000000U, 0xE8000000U, brcond, "bcnd"}, + + { 0xFC00FC00U, 0xF0008000U, obit, "clr"}, + { 0xFC00FC00U, 0xF0008800U, obit, "set"}, + { 0xFC00FC00U, 0xF0009000U, obit, "ext"}, + { 0xFC00FC00U, 0xF0009800U, obit, "extu"}, + { 0xFC00FC00U, 0xF000A000U, obit, "mak"}, + { 0xFC00FC00U, 0xF000A800U, obit, "rot"}, + + { 0xFC00FE00U, 0xF000D000U, otrap, "tb0"}, + { 0xFC00FE00U, 0xF000D800U, otrap, "tb1"}, + { 0xFC00FE00U, 0xF000E800U, otrap, "tcnd"}, + + { 0xFC00F2E0U, 0xF4000000U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4000200U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4001000U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4001200U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4002000U, nimmem, "st"}, + { 0xFC00F2E0U, 0xF4002200U, nimmem, "st"}, + { 0xFC00F2E0U, 0xF4003000U, nimmem, "lda"}, + { 0xFC00F2E0U, 0xF4003200U, nimmem, "lda"}, + + { 0xFC00FBE0U, 0xF4004000U, lognim, "and"}, + { 0xFC00FBE0U, 0xF4005000U, lognim, "xor"}, + { 0xFC00FBE0U, 0xF4005800U, lognim, "or"}, + + { 0xFC00FCE0U, 0xF4006000U, onimmed, "addu"}, + { 0xFC00FCE0U, 0xF4006400U, onimmed, "subu"}, + { 0xFC00FCE0U, 0xF4006800U, onimmed, "divu"}, + { 0xFC00FCE0U, 0xF4006C00U, onimmed, "mul"}, + { 0xFC00FCE0U, 0xF4007000U, onimmed, "add"}, + { 0xFC00FCE0U, 0xF4007400U, onimmed, "sub"}, + { 0xFC00FCE0U, 0xF4007800U, onimmed, "div"}, + { 0xFC00FCE0U, 0xF4007C00U, onimmed, "cmp"}, + + { 0xFC00FFE0U, 0xF4008000U, bitman, "clr"}, + { 0xFC00FFE0U, 0xF4008800U, bitman, "set"}, + { 0xFC00FFE0U, 0xF4009000U, bitman, "ext"}, + { 0xFC00FFE0U, 0xF4009800U, bitman, "extu"}, + { 0xFC00FFE0U, 0xF400A000U, bitman, "mak"}, + { 0xFC00FFE0U, 0xF400A800U, bitman, "rot"}, + + { 0xFC00FBE0U, 0xF400C000U, jump, "jmp"}, + { 0xFC00FBE0U, 0xF400C800U, jump, "jsr"}, + + { 0xFC00FFE0U, 0xF400E800U, instset, "ff1"}, + { 0xFC00FFE0U, 0xF400EC00U, instset, "ff0"}, + { 0xFC00FFE0U, 0xF400F800U, instset, "tbnd"}, + { 0xFC00FFE0U, 0xF400FC00U, instset, "rte"}, + { 0xFC000000U, 0xF8000000U, instset, "tbnd"}, + { 0, 0, NULL, NULL} +}; + +static const char *badop = "\t???"; + +int +m88k_print_instruction(unsigned iadr, long inst) +{ + const struct opdesc *p; + + /* this messes up "orb" instructions ever so slightly, */ + /* but keeps us in sync between routines... */ + if (inst == 0) { + db_printf ("\t.word 0"); + } else { + for (p = opdecode; p->mask; p++) + if ((inst & p->mask) == p->match) { + (*p->opfun) (inst, p->farg, iadr); + break; + } + if (!p->mask) + db_printf (badop); + } + + return iadr+4; +} + +db_addr_t +db_disasm(db_addr_t loc, boolean_t altfmt) +{ + m88k_print_instruction(loc, db_get_value(loc, 4, FALSE)); + db_printf ("\n"); + return loc+4; +} diff --git a/sys/arch/luna88k/ddb/db_interface.c b/sys/arch/luna88k/ddb/db_interface.c new file mode 100644 index 00000000000..f08df609f78 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_interface.c @@ -0,0 +1,827 @@ +/* $OpenBSD: db_interface.c,v 1.1 2004/04/21 15:23:50 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * m88k interface to ddb debugger + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/reboot.h> + +#include <uvm/uvm_extern.h> + +#include <machine/asm_macro.h> /* flush_pipeline() */ +#include <machine/cmmu.h> /* CMMU defs */ +#include <machine/trap.h> /* current_thread() */ +#include <machine/db_machdep.h> /* local ddb stuff */ +#include <machine/locore.h> +#include <machine/cpu_number.h> +#ifdef M88100 +#include <machine/m88100.h> +#include <machine/m8820x.h> +#endif + +#include <ddb/db_access.h> +#include <ddb/db_command.h> +#include <ddb/db_extern.h> +#include <ddb/db_interface.h> +#include <ddb/db_output.h> +#include <ddb/db_sym.h> + +extern label_t *db_recover; +extern unsigned db_trace_get_val(vaddr_t, unsigned *); +extern int frame_is_sane(db_regs_t *, int); +extern void cnpollc(int); +void kdbprinttrap(int, int); + +void m88k_db_trap(int, struct trapframe *); +int ddb_nmi_trap(int, db_regs_t *); +void ddb_error_trap(char *, db_regs_t *); +void db_putc(int); +int db_getc(void); +int m88k_dmx_print(unsigned, unsigned, unsigned, unsigned); +void m88k_db_pause(unsigned); +void m88k_db_print_frame(db_expr_t, int, db_expr_t, char *); +void m88k_db_registers(db_expr_t, int, db_expr_t, char *); +void m88k_db_where(db_expr_t, int, db_expr_t, char *); +void m88k_db_frame_search(db_expr_t, int, db_expr_t, char *); +void m88k_db_iflush(db_expr_t, int, db_expr_t, char *); +void m88k_db_dflush(db_expr_t, int, db_expr_t, char *); +void m88k_db_peek(db_expr_t, int, db_expr_t, char *); +void m88k_db_noise(db_expr_t, int, db_expr_t, char *); +void m88k_db_translate(db_expr_t, int, db_expr_t, char *); +void m88k_db_cmmucfg(db_expr_t, int, db_expr_t, char *); +void m88k_db_prom_cmd(db_expr_t, int, db_expr_t, char *); + +int db_active; +int db_noisy; + +db_regs_t ddb_regs; + +/* + * + * If you really feel like understanding the following procedure and + * macros, see pages 6-22 to 6-30 (Section 6.7.3) of + * + * MC88100 RISC Microprocessor User's Manual Second Edition + * (Motorola Order: MC88100UM/AD REV 1) + * + * and ERRATA-5 (6-23, 6-24, 6-24) of + * + * Errata to MC88100 User's Manual Second Edition MC88100UM/AD Rev 1 + * (Oct 2, 1990) + * (Motorola Order: MC88100UMAD/AD) + * + */ + +#ifdef M88100 +/* macros for decoding dmt registers */ + +/* + * return 1 if the printing of the next stage should be suppressed + */ +int +m88k_dmx_print(t, d, a, no) + unsigned t, d, a, no; +{ + static const unsigned addr_mod[16] = { + 0, 3, 2, 2, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const char *mode[16] = { + "?", ".b", ".b", ".h", ".b", "?", "?", "?", + ".b", "?", "?" , "?" , ".h" , "?", "?", "" + }; + static const unsigned mask[16] = { + 0, 0xff, 0xff00, 0xffff, + 0xff0000, 0, 0, 0, + 0xff000000, 0, 0, 0, + 0xffff0000, 0, 0, 0xffffffff + }; + static const unsigned shift[16] = { + 0, 0, 8, 0, 16, 0, 0, 0, + 24, 0, 0, 0, 16, 0, 0, 0 + }; + int reg = DMT_DREGBITS(t); + + if (ISSET(t, DMT_LOCKBAR)) { + db_printf("xmem%s%s r%d(0x%x) <-> mem(0x%x),", + DMT_ENBITS(t) == 0x0f ? "" : ".bu", ISSET(t, DMT_DAS) ? "" : ".usr", reg, + (((t)>>2 & 0xf) == 0xf) ? d : (d & 0xff), a); + return 1; + } else { + if (DMT_ENBITS(t) == 0xf) { + /* full or double word */ + if (ISSET(t, DMT_WRITE)) { + if (ISSET(t, DMT_DOUB1) && no == 2) + db_printf("st.d%s -> mem(0x%x) (** restart sxip **)", + ISSET(t, DMT_DAS) ? "" : ".usr", a); + else + db_printf("st%s (0x%x) -> mem(0x%x)", + ISSET(t, DMT_DAS) ? "" : ".usr", d, a); + } else { + /* load */ + if (ISSET(t, DMT_DOUB1) && no == 2) + db_printf("ld.d%s r%d <- mem(0x%x), r%d <- mem(0x%x)", + ISSET(t, DMT_DAS) ? "" : ".usr", reg, a, reg+1, a+4); + else + db_printf("ld%s r%d <- mem(0x%x)", + ISSET(t, DMT_DAS) ? "" : ".usr", reg, a); + } + } else { + /* fractional word - check if load or store */ + a += addr_mod[DMT_ENBITS(t)]; + if (ISSET(t, DMT_WRITE)) + db_printf("st%s%s (0x%x) -> mem(0x%x)", + mode[DMT_ENBITS(t)], ISSET(t, DMT_DAS) ? "" : ".usr", + (d & mask[DMT_ENBITS(t)]) >> shift[DMT_ENBITS(t)], a); + else + db_printf("ld%s%s%s r%d <- mem(0x%x)", + mode[DMT_ENBITS(t)], + ISSET(t, DMT_SIGNED) ? "" : "u", + ISSET(t, DMT_DAS) ? "" : ".usr", reg, a); + } + } + return 0; +} +#endif /* M88100 */ + +void +m88k_db_print_frame(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + struct trapframe *s = (struct trapframe *)addr; + char *name; + db_expr_t offset; +#ifdef M88100 + int suppress1 = 0, suppress2 = 0; +#endif + int c, force = 0, help = 0; + + if (!have_addr) { + db_printf("requires address of frame\n"); + help = 1; + } + + while (modif && *modif) { + switch (c = *modif++, c) { + case 'f': + force = 1; + break; + case 'h': + help = 1; + break; + default: + db_printf("unknown modifier [%c]\n", c); + help = 1; + break; + } + } + + if (help) { + db_printf("usage: mach frame/[f] ADDRESS\n"); + db_printf(" /f force printing of insane frames.\n"); + return; + } + + if (badwordaddr((vaddr_t)s) || + badwordaddr((vaddr_t)(&((db_regs_t*)s)->fpit))) { + db_printf("frame at %8p is unreadable\n", s); + return; + } + + if (frame_is_sane((db_regs_t *)s, 0) == 0) { /* see db_trace.c */ + if (force == 0) + return; + } + +#define R(i) s->tf_r[i] +#define IPMASK(x) ((x) & ~(3)) + db_printf("R00-05: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(0), R(1), R(2), R(3), R(4), R(5)); + db_printf("R06-11: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(6), R(7), R(8), R(9), R(10), R(11)); + db_printf("R12-17: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(12), R(13), R(14), R(15), R(16), R(17)); + db_printf("R18-23: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(18), R(19), R(20), R(21), R(22), R(23)); + db_printf("R24-29: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(24), R(25), R(26), R(27), R(28), R(29)); + db_printf("R30-31: 0x%08x 0x%08x\n", R(30), R(31)); + + db_printf("%cxip: 0x%08x ", + cputyp == CPU_88110 ? 'e' : 's', s->tf_sxip & ~3); + db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_sxip), + &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n"); + + if (s->tf_snip != s->tf_sxip + 4) { + db_printf("%cnip: 0x%08x ", + cputyp == CPU_88110 ? 'e' : 's', s->tf_snip); + db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_snip), + &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n"); + } + + if (cputyp != CPU_88110) { + if (s->tf_sfip != s->tf_snip + 4) { + db_printf("sfip: 0x%08x ", s->tf_sfip); + db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_sfip), + &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n"); + } + } else { + db_printf("fpsr: 0x%08x fpcr: 0x%08x fpecr: 0x%08x\n", + s->tf_fpsr, s->tf_fpcr, s->tf_fpecr); + db_printf("dsap 0x%08x duap 0x%08x dsr 0x%08x dlar 0x%08x dpar 0x%08x\n", + s->tf_dsap, s->tf_duap, s->tf_dsr, s->tf_dlar, s->tf_dpar); + db_printf("isap 0x%08x iuap 0x%08x isr 0x%08x ilar 0x%08x ipar 0x%08x\n", + s->tf_isap, s->tf_iuap, s->tf_isr, s->tf_ilar, s->tf_ipar); + } + + db_printf("epsr: 0x%08x current process: %p\n", + s->tf_epsr, curproc); + db_printf("vector: 0x%02x interrupt mask: 0x%08x\n", + s->tf_vector, s->tf_mask); + + /* + * If the vector indicates trap, instead of an exception or + * interrupt, skip the check of dmt and fp regs. + * + * Interrupt and exceptions are vectored at 0-10 and 114-127. + */ + + if (!(s->tf_vector <= 10 || (114 <= s->tf_vector && s->tf_vector <= 127))) { + db_printf("\n"); + return; + } + +#ifdef M88100 + if (cputyp != CPU_88110) { + if (s->tf_vector == /*data*/3 || s->tf_dmt0 & DMT_VALID) { + db_printf("dmt,d,a0: 0x%08x 0x%08x 0x%08x ", + s->tf_dmt0, s->tf_dmd0, s->tf_dma0); + db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma0, &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n "); + + suppress1 = m88k_dmx_print(s->tf_dmt0, s->tf_dmd0, s->tf_dma0, 0); + db_printf("\n"); + + if ((s->tf_dmt1 & DMT_VALID) && (!suppress1)) { + db_printf("dmt,d,a1: 0x%08x 0x%08x 0x%08x ", + s->tf_dmt1, s->tf_dmd1, s->tf_dma1); + db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma1, + &name, &offset); + if (name != NULL && + (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n "); + suppress2 = m88k_dmx_print(s->tf_dmt1, s->tf_dmd1, s->tf_dma1, 1); + db_printf("\n"); + + if ((s->tf_dmt2 & DMT_VALID) && (!suppress2)) { + db_printf("dmt,d,a2: 0x%08x 0x%08x 0x%08x ", + s->tf_dmt2, s->tf_dmd2, s->tf_dma2); + db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma2, + &name, &offset); + if (name != 0 && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n "); + m88k_dmx_print(s->tf_dmt2, s->tf_dmd2, s->tf_dma2, 2); + db_printf("\n"); + } + } + + db_printf("fault code %d\n", + CMMU_PFSR_FAULT(s->tf_dpfsr)); + } + } +#endif /* M88100 */ + + if (s->tf_fpecr & 255) { /* floating point error occurred */ + db_printf("fpecr: 0x%08x fpsr: 0x%08x fpcr: 0x%08x\n", + s->tf_fpecr, s->tf_fpsr, s->tf_fpcr); +#ifdef M88100 + if (cputyp != CPU_88110) { + db_printf("fcr1-4: 0x%08x 0x%08x 0x%08x 0x%08x\n", + s->tf_fphs1, s->tf_fpls1, s->tf_fphs2, s->tf_fpls2); + db_printf("fcr5-8: 0x%08x 0x%08x 0x%08x 0x%08x\n", + s->tf_fppt, s->tf_fprh, s->tf_fprl, s->tf_fpit); + } +#endif + } + db_printf("\n"); +} + +void +m88k_db_registers(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + m88k_db_print_frame((db_expr_t)DDB_REGS, TRUE, 0, modif); +} + +/* + * pause for 2*ticks many cycles + */ +void +m88k_db_pause(ticks) + unsigned volatile ticks; +{ + while (ticks) + ticks -= 1; +} + +/* + * m88k_db_trap - field a TRACE or BPT trap + * Note that only the tf_regs part of the frame is valid - some ddb routines + * invoke this function with a promoted struct reg! + */ +void +m88k_db_trap(type, frame) + int type; + struct trapframe *frame; +{ + + if (get_psr() & (1 << PSR_INTERRUPT_DISABLE_BIT)) + db_printf("WARNING: entered debugger with interrupts disabled\n"); + + switch(type) { + case T_KDB_BREAK: + case T_KDB_TRACE: + case T_KDB_ENTRY: + break; + case -1: + break; + default: + kdbprinttrap(type, 0); + if (db_recover != 0) { + db_error("Caught exception in ddb.\n"); + /*NOTREACHED*/ + } + } + + ddb_regs = frame->tf_regs; + + db_active++; + cnpollc(TRUE); + db_trap(type, 0); + cnpollc(FALSE); + db_active--; + + frame->tf_regs = ddb_regs; +} + +extern const char *trap_type[]; +extern const int trap_types; + +/* + * Print trap reason. + */ +void +kdbprinttrap(type, code) + int type, code; +{ + printf("kernel: "); + if (type >= trap_types || type < 0) + printf("type %d", type); + else + printf("%s", trap_type[type]); + printf(" trap\n"); +} + +void +Debugger() +{ + asm (ENTRY_ASM); /* entry trap */ + /* ends up at ddb_entry_trap below */ +} + +/* fielded a non maskable interrupt */ +int +ddb_nmi_trap(level, eframe) + int level; + db_regs_t *eframe; +{ + if (db_noisy) + db_printf("kernel: nmi interrupt\n"); + m88k_db_trap(T_KDB_ENTRY, (struct trapframe *)eframe); + + return 0; +} + +/* + * When the below routine is entered interrupts should be on + * but spl should be high + * + * The following routine is for breakpoint and watchpoint entry. + */ + +/* breakpoint/watchpoint entry */ +int +ddb_break_trap(type, eframe) + int type; + db_regs_t *eframe; +{ + m88k_db_trap(type, (struct trapframe *)eframe); + + if (type == T_KDB_BREAK) { + /* + * back up an instruction and retry the instruction + * at the breakpoint address. mc88110's exip reg + * already has the address of the exception instruction. + */ + if (cputyp != CPU_88110) { + eframe->sfip = eframe->snip; + eframe->snip = eframe->sxip; + } + } + + return 0; +} + +/* enter at splhigh */ +int +ddb_entry_trap(level, eframe) + int level; + db_regs_t *eframe; +{ + m88k_db_trap(T_KDB_ENTRY, (struct trapframe *)eframe); + + return 0; +} + +/* + * When the below routine is entered interrupts should be on + * but spl should be high + */ +/* error trap - unreturnable */ +void +ddb_error_trap(error, regs) + char *error; + db_regs_t *regs; +{ + db_printf("KERNEL: terminal error [%s]\n", error); + db_printf("KERNEL: Exiting debugger will cause abort to rom\n"); + db_printf("at 0x%x ", regs->sxip & XIP_ADDR); + db_printf("dmt0 0x%x dma0 0x%x", regs->dmt0, regs->dma0); + m88k_db_pause(1000000); + m88k_db_trap(T_KDB_BREAK, (struct trapframe *)regs); +} + +/* + * Read bytes from kernel address space for debugger. + */ +void +db_read_bytes(db_addr_t addr, size_t size, char *data) +{ + char *src; + + src = (char *)addr; + + while(size-- > 0) { + *data++ = *src++; + } +} + +/* + * Write bytes to kernel address space for debugger. + * This should make a text page writable to be able + * to plant a break point (right now text is mapped with + * write access in pmap_bootstrap()). XXX nivas + */ +void +db_write_bytes(db_addr_t addr, size_t size, char *data) +{ + char *dst; + paddr_t physaddr; + psize_t psize = size; + + dst = (char *)addr; + + while (size-- > 0) { + *dst++ = *data++; + } + /* XXX test return value */ + pmap_extract(pmap_kernel(), (vaddr_t)addr, &physaddr); + cmmu_flush_cache(cpu_number(), physaddr, psize); +} + +#define __ROM_FUNC_TABLE ((int **)0x00001100) + +/* to print a character to the console */ +void +db_putc(c) + int c; +{ + (*(void (*)(int))__ROM_FUNC_TABLE[4])(c & 0xff); +} + +/* to peek at the console; returns -1 if no character is there */ +int +db_getc() +{ + int c; + do { + c = (*(int (*)(void))__ROM_FUNC_TABLE[3])(); + } while (c == -1); + return c; +} + +/* display where all the cpus are stopped at */ +void +m88k_db_where(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + char *name; + db_expr_t offset; + db_addr_t l; + + l = PC_REGS(DDB_REGS); /* clear low bits */ + + db_find_xtrn_sym_and_offset(l, &name, &offset); + if (name && (unsigned)offset <= db_maxoff) + db_printf("stopped at 0x%lx (%s+0x%x)\n", l, name, offset); + else + db_printf("stopped at 0x%lx\n", l); +} + +/* + * Walk back a stack, looking for exception frames. + * These frames are recognized by the routine frame_is_sane. Frames + * only start with zero, so we only call frame_is_sane if the + * current address contains zero. + * + * If addr is given, it is assumed to an address on the stack to be + * searched. Otherwise, r31 of the current cpu is used. + */ +void +m88k_db_frame_search(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + if (have_addr) + addr &= ~3; /* round to word */ + else + addr = (DDB_REGS->r[31]); + + /* walk back up stack until 8k boundry, looking for 0 */ + while (addr & ((8 * 1024) - 1)) { + if (frame_is_sane((db_regs_t *)addr, 1) != 0) + db_printf("frame found at 0x%x\n", addr); + addr += 4; + } + + db_printf("(Walked back until 0x%x)\n",addr); +} + +/* flush icache */ +void +m88k_db_iflush(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + addr = 0; +#ifdef may_be_removed + cmmu_remote_set(addr, CMMU_SCR, 0, CMMU_FLUSH_CACHE_CBI_ALL); +#endif +} + +/* flush dcache */ + +void +m88k_db_dflush(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + addr = 0; +#ifdef may_be_removed + cmmu_remote_set(addr, CMMU_SCR, 1, CMMU_FLUSH_CACHE_CBI_ALL); +#endif +} + +/* probe my cache */ +void +m88k_db_peek(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ +#ifdef may_be_removed + int pa12; + int valmask; + + pa12 = addr & ~((1<<12) -1); + + /* probe dcache */ + cmmu_remote_set(0, CMMU_SAR, 1, addr); + + valmask = cmmu_remote_get(0, CMMU_CSSP, 1); + db_printf("dcache valmask 0x%x\n", (unsigned)valmask); + db_printf("dcache tag ports 0x%x 0x%x 0x%x 0x%x\n", + (unsigned)cmmu_remote_get(0, CMMU_CTP0, 1), + (unsigned)cmmu_remote_get(0, CMMU_CTP1, 1), + (unsigned)cmmu_remote_get(0, CMMU_CTP2, 1), + (unsigned)cmmu_remote_get(0, CMMU_CTP3, 1)); + + /* probe icache */ + cmmu_remote_set(0, CMMU_SAR, 0, addr); + + valmask = cmmu_remote_get(0, CMMU_CSSP, 0); + db_printf("icache valmask 0x%x\n", (unsigned)valmask); + db_printf("icache tag ports 0x%x 0x%x 0x%x 0x%x\n", + (unsigned)cmmu_remote_get(0, CMMU_CTP0, 0), + (unsigned)cmmu_remote_get(0, CMMU_CTP1, 0), + (unsigned)cmmu_remote_get(0, CMMU_CTP2, 0), + (unsigned)cmmu_remote_get(0, CMMU_CTP3, 0)); +#endif +} + + +/* + * control how much info the debugger prints about itself + */ +void +m88k_db_noise(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + if (!have_addr) { + /* if off make noisy; if noisy or very noisy turn off */ + if (db_noisy) { + db_printf("changing debugger status from %s to quiet\n", + db_noisy == 1 ? "noisy" : + db_noisy == 2 ? "very noisy" : "violent"); + db_noisy = 0; + } else { + db_printf("changing debugger status from quiet to noisy\n"); + db_noisy = 1; + } + } else if (addr < 0 || addr > 3) + db_printf("invalid noise level to m88k_db_noisy; should be 0, 1, 2, or 3\n"); + else { + db_noisy = addr; + db_printf("debugger noise level set to %s\n", + db_noisy == 0 ? "quiet" : + (db_noisy == 1 ? "noisy" : + db_noisy==2 ? "very noisy" : "violent")); + } +} + +/* + * See how a virtual address translates. + * Must have an address. + */ +void +m88k_db_translate(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + char c; + int verbose_flag = 0; + int supervisor_flag = 1; + int wanthelp = 0; + + if (!have_addr) + wanthelp = 1; + else { + while (c = *modif++, c != 0) { + switch (c) { + default: + db_printf("bad modifier [%c]\n", c); + wanthelp = 1; + break; + case 'h': + wanthelp = 1; + break; + case 'v': + verbose_flag++; + break; + case 's': + supervisor_flag = 1; + break; + case 'u': + supervisor_flag = 0; + break; + } + } + } + + if (wanthelp) { + db_printf("usage: translate[/vvsu] address\n"); + db_printf("flags: v - be verbose (vv - be very verbose)\n"); + db_printf(" s - use cmmu's supervisor area pointer (default)\n"); + db_printf(" u - use cmmu's user area pointer\n"); + return; + } + cmmu_show_translation(addr, supervisor_flag, verbose_flag, -1); +} + +void +m88k_db_cmmucfg(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + if (modif != NULL && *modif != 0) { + db_printf("usage: mach cmmucfg\n"); + return; + } + + cmmu_dump_config(); +} + +void +m88k_db_prom_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + db_printf("m88k_db_prom_cmd() is not implemented\n"); +} + +/************************/ +/* COMMAND TABLE / INIT */ +/************************/ + +struct db_command m88k_cache_cmds[] = { + { "iflush", m88k_db_iflush, 0, NULL }, + { "dflush", m88k_db_dflush, 0, NULL }, + { "peek", m88k_db_peek, 0, NULL }, + { NULL, NULL, 0, NULL } +}; + +struct db_command db_machine_cmds[] = { + { "cache", NULL, 0, m88k_cache_cmds }, + { "frame", m88k_db_print_frame, 0, NULL }, + { "regs", m88k_db_registers, 0, NULL }, + { "noise", m88k_db_noise, 0, NULL }, + { "searchframe",m88k_db_frame_search, 0, NULL }, + { "translate", m88k_db_translate, 0, NULL }, + { "cmmucfg", m88k_db_cmmucfg, 0, NULL }, + { "where", m88k_db_where, 0, NULL }, + { "prom", m88k_db_prom_cmd, 0, NULL }, + { NULL, NULL, 0, NULL } +}; + +void +db_machine_init() +{ + db_machine_commands_install(db_machine_cmds); +} diff --git a/sys/arch/luna88k/ddb/db_sstep.c b/sys/arch/luna88k/ddb/db_sstep.c new file mode 100644 index 00000000000..7c9a2da3254 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_sstep.c @@ -0,0 +1,337 @@ +/* $OpenBSD: db_sstep.c,v 1.1 2004/04/21 15:23:51 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_access.h> /* db_get_value() */ +#include <ddb/db_break.h> /* db_breakpoint_t */ + +/* + * Support routines for software single step. + * + * Author: Daniel Stodolsky (danner@cs.cmu.edu) + * + */ + +boolean_t inst_delayed(unsigned int ins); + +#ifdef INTERNAL_SSTEP +db_breakpoint_t db_not_taken_bkpt = 0; +db_breakpoint_t db_taken_bkpt = 0; +#endif + +/* + * Returns TRUE is the instruction a branch or jump instruction + * (br, bb0, bb1, bcnd, jmp) but not a function call (bsr or jsr) + */ +boolean_t +inst_branch(ins) + unsigned int ins; +{ + /* check high five bits */ + switch (ins >> (32 - 5)) { + case 0x18: /* br */ + case 0x1a: /* bb0 */ + case 0x1b: /* bb1 */ + case 0x1d: /* bcnd */ + return TRUE; + break; + case 0x1e: /* could be jmp */ + if ((ins & 0xfffffbe0U) == 0xf400c000U) + return TRUE; + } + + return FALSE; +} + +/* + * inst_load(ins) + * Returns the number of words the instruction loads. byte, + * half and word count as 1; double word as 2 + */ +unsigned +inst_load(ins) + unsigned int ins; +{ + /* look at the top six bits, for starters */ + switch (ins >> (32 - 6)) { + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + + case 0x2: /* unsigned half-word load imm */ + case 0x3: /* unsigned byte load imm */ + case 0x5: /* signed word load imm */ + case 0x6: /* signed half-word load imm */ + case 0x7: /* signed byte load imm */ + return 1; + + case 0x4: /* signed double word load imm */ + return 2; + + case 0x3d: /* load/store/xmem scaled/unscaled instruction */ + if ((ins & 0xf400c0e0U) == 0xf4000000U) /* is load/xmem */ + switch ((ins & 0x0000fce0) >> 5) { /* look at bits 15-5, but mask bits 8-9 */ + case 0x0: /* xmem byte */ + case 0x1: /* xmem word */ + case 0x2: /* unsigned half word */ + case 0x3: /* unsigned byte load */ + case 0x5: /* signed word load */ + case 0x6: /* signed half-word load */ + case 0x7: /* signed byte load */ + return 1; + + case 0x4: /* signed double word load */ + return 2; + } /* end switch load/xmem */ + break; + } /* end switch 32-6 */ + + return 0; +} + +/* + * inst_store + * Like inst_load, except for store instructions. + */ +unsigned +inst_store(ins) + unsigned int ins; +{ + /* decode top 6 bits again */ + switch (ins >> (32 - 6)) { + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + case 0x9: /* store word imm */ + case 0xa: /* store half-word imm */ + case 0xb: /* store byte imm */ + return 1; + + case 0x8: /* store double word */ + return 2; + case 0x3d: /* load/store/xmem scaled/unscaled instruction */ + /* check bits 15,14,12,7,6,5 are all 0 */ + if ((ins & 0x0000d0e0U) == 0) + switch ((ins & 0x00003c00U) >> 10) { /* decode bits 10-13 */ + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + case 0x9: /* store word */ + case 0xa: /* store half-word */ + case 0xb: /* store byte */ + return 1; + + case 0x8: /* store double word */ + return 2; + } /* end switch store/xmem */ + break; + } /* end switch 32-6 */ + + return 0; +} + +/* + * inst_delayed + * Returns TRUE if this instruction is followed by a delay slot. + * Could be br.n, bsr.n bb0.n, bb1.n, bcnd.n or jmp.n or jsr.n + */ +boolean_t +inst_delayed(ins) + unsigned int ins; +{ + /* check the br, bsr, bb0, bb1, bcnd cases */ + switch ((ins & 0xfc000000U) >> (32 - 6)) { + case 0x31: /* br */ + case 0x33: /* bsr */ + case 0x35: /* bb0 */ + case 0x37: /* bb1 */ + case 0x3b: /* bcnd */ + return TRUE; + } + + /* check the jmp, jsr cases */ + /* mask out bits 0-4, bit 11 */ + return ((ins & 0xfffff7e0U) == 0xf400c400U) ? TRUE : FALSE; +} + +/* + * next_instr_address(pc,delay_slot,task) has the following semantics. + * Let inst be the instruction at pc. + * If delay_slot = 1, next_instr_address should return + * the address of the instruction in the delay slot; if this instruction + * does not have a delay slot, it should return pc. + * If delay_slot = 0, next_instr_address should return the + * address of next sequential instruction, or pc if the instruction is + * followed by a delay slot. + * + * 91-11-28 jfriedl: I think the above is wrong. I think it should be: + * if delay_slot true, return address of the delay slot if there is one, + * return pc otherwise. + * if delay_slot false, return (pc + 4) regardless. + * + */ +db_addr_t +next_instr_address(pc, delay_slot) + db_addr_t pc; + unsigned delay_slot; +{ + if (delay_slot == 0) + return pc + 4; + else { + if (inst_delayed(db_get_value(pc, sizeof(int), FALSE))) + return pc + 4; + else + return pc; + } +} + + +/* + * branch_taken(instruction, program counter, func, func_data) + * + * instruction will be a control flow instruction location at address pc. + * Branch taken is supposed to return the address to which the instruction + * would jump if the branch is taken. Func can be used to get the current + * register values when invoked with a register number and func_data as + * arguments. + * + * If the instruction is not a control flow instruction, panic. + */ +db_addr_t +branch_taken(inst, pc, func, func_data) + u_int inst; + db_addr_t pc; + db_expr_t (*func)(db_regs_t *, int); + db_regs_t *func_data; +{ + /* check if br/bsr */ + if ((inst & 0xf0000000U) == 0xc0000000U) { + /* signed 26 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x03ffffffU) << 2; + /* check if sign extension is needed */ + if (inst & 0x08000000U) + inst |= 0xf0000000U; + return pc + inst; + } + + /* check if bb0/bb1/bcnd case */ + switch ((inst & 0xf8000000U)) { + case 0xd0000000U: /* bb0 */ + case 0xd8000000U: /* bb1 */ + case 0xe8000000U: /* bcnd */ + /* signed 16 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x0000ffffU) << 2; + /* check if sign extension is needed */ + if (inst & 0x00020000U) + inst |= 0xfffc0000U; + return pc + inst; + } + + /* check jmp/jsr case */ + /* check bits 5-31, skipping 10 & 11 */ + if ((inst & 0xfffff3e0U) == 0xf400c000U) { + return (*func)(func_data, (inst & 0x0000001fU)); /* the register value */ + } + + + panic("branch_taken"); + return 0; /* keeps compiler happy */ +} + +/* + * getreg_val - handed a register number and an exception frame. + * Returns the value of the register in the specified + * frame. Only makes sense for general registers. + */ + +db_expr_t +getreg_val(frame, regno) + db_regs_t *frame; + int regno; +{ + if (regno == 0) + return 0; + else if (regno < 31) + return frame->r[regno]; + else + panic("bad register number (%d) to getreg_val.", regno); +} + +#ifdef INTERNAL_SSTEP +void +db_set_single_step(regs) + db_regs_t *regs; +{ + if (cputyp == CPU_88110) { + ((regs)->epsr |= (PSR_TRACE | PSR_SER)); + } else { + db_addr_t pc = PC_REGS(regs); +#ifndef SOFTWARE_SSTEP_EMUL + db_addr_t brpc; + u_int inst; + + /* + * User was stopped at pc, e.g. the instruction + * at pc was not executed. + */ + inst = db_get_value(pc, sizeof(int), FALSE); + if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { + brpc = branch_taken(inst, pc, getreg_val, regs); + if (brpc != pc) { /* self-branches are hopeless */ + db_taken_bkpt = db_set_temp_breakpoint(brpc); + } +#if 0 + /* XXX this seems like a true bug, no? */ + pc = next_instr_address(pc, 1); +#endif + } +#endif /*SOFTWARE_SSTEP_EMUL*/ + pc = next_instr_address(pc, 0); + db_not_taken_bkpt = db_set_temp_breakpoint(pc); + } +} + +void +db_clear_single_step(regs) + db_regs_t *regs; +{ + if (cputyp == CPU_88110) { + ((regs)->epsr &= ~(PSR_TRACE | PSR_SER)); + } else { + if (db_taken_bkpt != 0) { + db_delete_temp_breakpoint(db_taken_bkpt); + db_taken_bkpt = 0; + } + if (db_not_taken_bkpt != 0) { + db_delete_temp_breakpoint(db_not_taken_bkpt); + db_not_taken_bkpt = 0; + } + } +} +#endif diff --git a/sys/arch/luna88k/ddb/db_trace.c b/sys/arch/luna88k/ddb/db_trace.c new file mode 100644 index 00000000000..3c24e6c4105 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_trace.c @@ -0,0 +1,1143 @@ +/* $OpenBSD: db_trace.c,v 1.1 2004/04/21 15:23:52 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/db_machdep.h> +#include <machine/locore.h> + +#include <ddb/db_variables.h> /* db_variable, DB_VAR_GET, etc. */ +#include <ddb/db_output.h> /* db_printf */ +#include <ddb/db_sym.h> /* DB_STGY_PROC, etc. */ +#include <ddb/db_command.h> /* db_recover */ +#include <ddb/db_access.h> +#include <ddb/db_interface.h> + +union instruction { + unsigned rawbits; + + struct { + unsigned int : 5; + unsigned int n: 1; + signed int d26:26; + } br; + + struct { + unsigned int : 4; + unsigned int isbb1: 1; /* isbb1==0 means bb0, isbb1==1 means bb1 */ + unsigned int n : 1; + unsigned int b5 : 5; + unsigned int s1 : 5; + signed int d16 :16; + } bb; /* bcnd too, except "isbb1" makes no sense for bcnd */ + + struct { + unsigned int : 6; + unsigned int b5 : 5; + unsigned int s1 : 5; + unsigned int : 7; + unsigned int vec9 : 9; + } tb; /* tcnd too */ + + struct { + unsigned int :21; + unsigned int n : 1; + unsigned int : 5; + unsigned int s2 : 5; + } jump; /* jmp, jsr */ + + struct { + unsigned int : 6; + unsigned int d : 5; + unsigned int s1 : 5; + unsigned int i16 :16; + } diatic; /* general reg/reg/i16 instructions */ + + struct { + unsigned int : 6; + unsigned int d : 5; + unsigned int s1 : 5; + unsigned int :11; + unsigned int s2 : 5; + } triatic; /* general reg/reg/reg instructions */ + +}; + +static inline unsigned br_dest(unsigned addr, union instruction inst) +{ + return addr + inst.br.d26 * 4; +} + + +#define TRACE_DEBUG /* undefine to disable debugging */ + +int frame_is_sane(db_regs_t *regs, int); +char *m88k_exception_name(unsigned vector); +unsigned db_trace_get_val(vaddr_t addr, unsigned *ptr); + +/* + * Some macros to tell if the given text is the instruction. + */ +#define JMPN_R1(I) ( (I) == 0xf400c401U) /* jmp.n r1 */ +#define JMP_R1(I) ( (I) == 0xf400c001U) /* jmp r1 */ + +/* gets the IMM16 value from an instruction */ +#define IMM16VAL(I) (((union instruction)(I)).diatic.i16) + +/* subu r31, r31, IMM */ +#define SUBU_R31_R31_IMM(I) (((I) & 0xffff0000U) == 0x67ff0000U) + +/* st r1, r31, IMM */ +#define ST_R1_R31_IMM(I) (((I) & 0xffff0000U) == 0x243f0000U) + +static int trace_flags; +#define TRACE_DEBUG_FLAG 0x01 +#define TRACE_SHOWCALLPRESERVED_FLAG 0x02 +#define TRACE_SHOWADDRESS_FLAG 0x04 +#define TRACE_SHOWFRAME_FLAG 0x08 +#define TRACE_USER_FLAG 0x10 + +#ifdef TRACE_DEBUG + #define DEBUGGING_ON (trace_flags & TRACE_DEBUG_FLAG) +#endif + +#ifndef TRACE_DEBUG + #define SHOW_INSTRUCTION(Addr, Inst, Note) { /*nothing*/ } +#else + #define SHOW_INSTRUCTION(Addr, Inst, Note) if (DEBUGGING_ON) { \ + db_printf("%s0x%x: (0x%08x) ", Note, (unsigned)(Addr), (Inst)); \ + m88k_print_instruction((unsigned)(Addr), (Inst)); \ + db_printf("\n"); \ + } +#endif + +extern label_t *db_recover; + +/* + * m88k trace/register state interface for ddb. + */ + +/* lifted from mips */ +static int +db_setf_regs(struct db_variable *vp, + db_expr_t *valuep, + int op) /* read/write */ +{ + int *regp = (int *) ((char *) DDB_REGS + (int) (vp->valuep)); + + if (op == DB_VAR_GET) + *valuep = *regp; + else if (op == DB_VAR_SET) + *regp = *valuep; + + return (0); /* silence warning */ +} + +#define N(s, x) {s, (long *)&(((db_regs_t *) 0)->x), db_setf_regs} + +struct db_variable db_regs[] = { + N("r1", r[1]), N("r2", r[2]), N("r3", r[3]), N("r4", r[4]), + N("r5", r[5]), N("r6", r[6]), N("r7", r[7]), N("r8", r[8]), + N("r9", r[9]), N("r10", r[10]), N("r11", r[11]), N("r12", r[12]), + N("r13", r[13]), N("r14", r[14]), N("r15", r[15]), N("r16", r[16]), + N("r17", r[17]), N("r18", r[18]), N("r19", r[19]), N("r20", r[20]), + N("r21", r[21]), N("r22", r[22]), N("r23", r[23]), N("r24", r[24]), + N("r25", r[25]), N("r26", r[26]), N("r27", r[27]), N("r28", r[28]), + N("r29", r[29]), N("r30", r[30]), N("r31", r[31]), N("epsr", epsr), + N("sxip", sxip), N("snip", snip), N("sfip", sfip), N("ssbr", ssbr), + N("dmt0", dmt0), N("dmd0", dmd0), N("dma0", dma0), N("dmt1", dmt1), + N("dmd1", dmd1), N("dma1", dma1), N("dmt2", dmt2), N("dmd2", dmd2), + N("dma2", dma2), N("fpecr", fpecr),N("fphs1", fphs1),N("fpls1", fpls1), + N("fphs2", fphs2), N("fpls2", fpls2),N("fppt", fppt), N("fprh", fprh), + N("fprl", fprl), N("fpit", fpit), N("fpsr", fpsr), N("fpcr", fpcr), +}; +#undef N + +struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); + + +#define TRASHES 0x001 /* clobbers instruction field D */ +#define STORE 0x002 /* does a store to S1+IMM16 */ +#define LOAD 0x004 /* does a load from S1+IMM16 */ +#define DOUBLE 0x008 /* double-register */ +#define FLOW_CTRL 0x010 /* flow-control instruction */ +#define DELAYED 0x020 /* delayed flow control */ +#define JSR 0x040 /* flow-control is a jsr[.n] */ +#define BSR 0x080 /* flow-control is a bsr[.n] */ + +/* + * Given a word of instruction text, return some flags about that + * instruction (flags defined above). + */ +static unsigned +m88k_instruction_info(unsigned instruction) +{ + static const struct { + unsigned mask, value, flags; + } *ptr, control[] = { + /* runs in the same order as 2nd Ed 88100 manual Table 3-14 */ + { 0xf0000000U, 0x00000000U, /* xmem */ TRASHES | STORE | LOAD}, + { 0xec000000U, 0x00000000U, /* ld.d */ TRASHES | LOAD | DOUBLE}, + { 0xe0000000U, 0x00000000U, /* load */ TRASHES | LOAD}, + { 0xfc000000U, 0x20000000U, /* st.d */ STORE | DOUBLE}, + { 0xf0000000U, 0x20000000U, /* store */ STORE}, + { 0xc0000000U, 0x40000000U, /* arith */ TRASHES}, + { 0xfc004000U, 0x80004000U, /* ld cr */ TRASHES}, + { 0xfc004000U, 0x80000000U, /* st cr */ 0}, + { 0xfc008060U, 0x84000000U, /* f */ TRASHES}, + { 0xfc008060U, 0x84000020U, /* f.d */ TRASHES | DOUBLE}, + { 0xfc000000U, 0xcc000000U, /* bsr.n */ FLOW_CTRL | DELAYED | BSR}, + { 0xfc000000U, 0xc8000000U, /* bsr */ FLOW_CTRL | BSR}, + { 0xe4000000U, 0xc4000000U, /* br/bb.n */ FLOW_CTRL | DELAYED}, + { 0xe4000000U, 0xc0000000U, /* br/bb */ FLOW_CTRL}, + { 0xfc000000U, 0xec000000U, /* bcnd.n */ FLOW_CTRL | DELAYED}, + { 0xfc000000U, 0xe8000000U, /* bcnd */ FLOW_CTRL}, + { 0xfc00c000U, 0xf0008000U, /* bits */ TRASHES}, + { 0xfc00c000U, 0xf000c000U, /* trap */ 0}, + { 0xfc00f0e0U, 0xf4002000U, /* st */ 0}, + { 0xfc00cce0U, 0xf4000000U, /* ld.d */ TRASHES | DOUBLE}, + { 0xfc00c0e0U, 0xf4000000U, /* ld */ TRASHES}, + { 0xfc00c0e0U, 0xf4004000U, /* arith */ TRASHES}, + { 0xfc00c3e0U, 0xf4008000U, /* bits */ TRASHES}, + { 0xfc00ffe0U, 0xf400cc00U, /* jsr.n */ FLOW_CTRL | DELAYED | JSR}, + { 0xfc00ffe0U, 0xf400c800U, /* jsr */ FLOW_CTRL | JSR}, + { 0xfc00ffe0U, 0xf400c400U, /* jmp.n */ FLOW_CTRL | DELAYED}, + { 0xfc00ffe0U, 0xf400c000U, /* jmp */ FLOW_CTRL}, + { 0xfc00fbe0U, 0xf400e800U, /* ff */ TRASHES}, + { 0xfc00ffe0U, 0xf400f800U, /* tbnd */ 0}, + { 0xfc00ffe0U, 0xf400fc00U, /* rte */ FLOW_CTRL}, + { 0xfc000000U, 0xf8000000U, /* tbnd */ 0}, + }; +#define ctrl_count (sizeof(control)/sizeof(control[0])) + for (ptr = &control[0]; ptr < &control[ctrl_count]; ptr++) + if ((instruction & ptr->mask) == ptr->value) + return ptr->flags; + SHOW_INSTRUCTION(0, instruction, "bad m88k_instruction_info"); + return 0; +} + +static int +hex_value_needs_0x(unsigned value) +{ + int i; + unsigned last = 0; + unsigned char c; + unsigned have_a_hex_digit = 0; + + if (value <= 9) + return 0; + + for (i = 0; i < 8; i++) { + c = value & 0xf; + value >>= 4; + if (c) + last = c; + if (c > 9) + have_a_hex_digit = 1; + } + if (have_a_hex_digit == 0) + return 1; + if (last > 9) + return 1; + return 0; +} + +/* + * returns + * 1 if regs seems to be a reasonable kernel exception frame. + * 2 if regs seems to be a reasonable user exception frame + * (in the current task). + * 0 if this looks like neither. + */ +int +frame_is_sane(db_regs_t *regs, int quiet) +{ + /* no good if we can't read the whole frame */ + if (badwordaddr((vaddr_t)regs) || badwordaddr((vaddr_t)®s->fpit)) { + if (quiet == 0) + db_printf("[WARNING: frame at %p : unreadable]\n", regs); + return 0; + } + + /* r0 must be 0 (obviously) */ + if (regs->r[0] != 0) { + if (quiet == 0) + db_printf("[WARNING: frame at %p : r[0] != 0]\n", regs); + return 0; + } + + /* stack sanity ... r31 must be nonzero, and must be word aligned */ + if (regs->r[31] == 0 || (regs->r[31] & 3) != 0) { + if (quiet == 0) + db_printf("[WARNING: frame at %p : r[31] == 0 or not word aligned]\n", regs); + return 0; + } + + if (cputyp != CPU_88110) { + /* sxip is reasonable */ +#if 0 + if ((regs->sxip & 1) == 1) + return 0; +#endif + /* snip is reasonable */ + if ((regs->snip & 3) != 2) + return 0; + /* sfip is reasonable */ + if ((regs->sfip & 3) != 2) + return 0; + } + + /* epsr sanity */ + if ((regs->epsr & PSR_MODE)) { /* kernel mode */ + if (regs->epsr & PSR_BO) + return 0; + return 1; + } + if (!(regs->epsr & PSR_MODE)) { /* user mode */ + if (regs->epsr & PSR_BO) + return 0; + return 2; + } + if (quiet == 0) + db_printf("[WARNING: not an exception frame?]\n"); + return 0; +} + +char * +m88k_exception_name(unsigned vector) +{ + switch (vector) { + default: + case 0: return "Reset"; + case 1: return "Interrupt"; + case 2: return "Instruction Access Exception"; + case 3: return "Data Access Exception"; + case 4: return "Misaligned Access Exception"; + case 5: return "Unimplemented Opcode Exception"; + case 6: return "Privilege Violation"; + case 7: return "Bounds Check"; + case 8: return "Integer Divide Exception"; + case 9: return "Integer Overflow Exception"; + case 10: return "Error Exception"; + case 11: return "Non Maskable Interrupt"; + case 114: return "FPU precise"; + case 115: return "FPU imprecise"; + case DDB_ENTRY_BKPT_NO: + return "ddb break"; + case DDB_ENTRY_TRACE_NO: + return "ddb trace"; + case DDB_ENTRY_TRAP_NO: + return "ddb trap"; + case 451: return "Syscall"; + } +} + +/* + * Read a word at address addr. + * Return 1 if was able to read, 0 otherwise. + */ +unsigned +db_trace_get_val(vaddr_t addr, unsigned *ptr) +{ + label_t db_jmpbuf; + label_t *prev = db_recover; + + if (setjmp((db_recover = &db_jmpbuf)) != 0) { + db_recover = prev; + return 0; + } else { + db_read_bytes(addr, 4, (char *)ptr); + db_recover = prev; + return 1; + } +} + +#define FIRST_CALLPRESERVED_REG 14 +#define LAST_CALLPRESERVED_REG 29 +#define FIRST_ARG_REG 2 +#define LAST_ARG_REG 9 +#define RETURN_VAL_REG 1 + +static unsigned global_saved_list = 0x0; /* one bit per register */ +static unsigned local_saved_list = 0x0; /* one bit per register */ +static unsigned trashed_list = 0x0; /* one bit per register */ +static unsigned saved_reg[32]; /* one value per register */ + +#define reg_bit(reg) (1<<((reg)%32)) + +static void +save_reg(int reg, unsigned value) +{ +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) db_printf("save_reg(%d, %x)\n", reg, value); +#endif + if (trashed_list & reg_bit(reg)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) db_printf("<trashed>\n"); +#endif + return; /* don't save trashed registers */ + } + saved_reg[(reg%32)] = value; + global_saved_list |= reg_bit(reg); + local_saved_list |= reg_bit(reg); +} + +#define mark_reg_trashed(reg) (trashed_list |= reg_bit(reg)) + +#define have_global_reg(reg) (global_saved_list & (1<<(reg))) +#define have_local_reg(reg) (local_saved_list & (1<<(reg))) + +#define clear_local_saved_regs() { local_saved_list = trashed_list = 0; } +#define clear_global_saved_regs() { local_saved_list = global_saved_list = 0; } + +#define saved_reg_value(reg) (saved_reg[(reg)]) + +/* + * Show any arguments that we might have been able to determine. + */ +static void +print_args(void) +{ + int reg, last_arg; + + /* find the highest argument register saved */ + for (last_arg = LAST_ARG_REG; last_arg >= FIRST_ARG_REG; last_arg--) + if (have_local_reg(last_arg)) + break; + if (last_arg < FIRST_ARG_REG) + return; /* none were saved */ + + db_printf("("); + + /* print each one, up to the highest */ + for (reg = FIRST_ARG_REG; /*nothing */; reg++) { + if (!have_local_reg(reg)) + db_printf("?"); + else { + unsigned value = saved_reg_value(reg); + db_printf("%s%x", hex_value_needs_0x(value) ? + "0x" : "", value); + } + if (reg == last_arg) + break; + else + db_printf(", "); + } + db_printf(")"); +} + + +#define JUMP_SOURCE_IS_BAD 0 +#define JUMP_SOURCE_IS_OK 1 +#define JUMP_SOURCE_IS_UNLIKELY 2 + +/* + * Give an address to where we return, and an address to where we'd jumped, + * Decided if it all makes sense. + * + * Gcc sometimes optimized something like + * if (condition) + * func1(); + * else + * OtherStuff... + * to + * bcnd !condition mark + * bsr.n func1 + * or r1, r0, mark2 + * mark: + * OtherStuff... + * mark2: + * + * So RETURN_TO will be MARK2, even though we really did branch via + * 'bsr.n func1', so this makes it difficult to be certaian about being + * wrong. + */ +static int +is_jump_source_ok(unsigned return_to, unsigned jump_to) +{ + unsigned flags; + union instruction instruction; + + /* + * Delayed branches are most common... look two instructions before + * where we were going to return to to see if it's a delayed branch. + */ + if (!db_trace_get_val(return_to - 8, &instruction.rawbits)) + return JUMP_SOURCE_IS_BAD; + flags = m88k_instruction_info(instruction.rawbits); + + if ((flags & FLOW_CTRL) && (flags & DELAYED) && (flags & (JSR|BSR))) { + if (flags & JSR) + return JUMP_SOURCE_IS_OK; /* have to assume it's correct */ + /* calculate the offset */ + if (br_dest(return_to - 8, instruction) == jump_to) + return JUMP_SOURCE_IS_OK; /* exactamundo! */ + else + return JUMP_SOURCE_IS_UNLIKELY; /* seems wrong */ + } + + /* + * Try again, looking for a non-delayed jump one back. + */ + if (!db_trace_get_val(return_to - 4, &instruction.rawbits)) + return JUMP_SOURCE_IS_BAD; + flags = m88k_instruction_info(instruction.rawbits); + + if ((flags & FLOW_CTRL) && !(flags & DELAYED) && (flags & (JSR|BSR))) { + if (flags & JSR) + return JUMP_SOURCE_IS_OK; /* have to assume it's correct */ + /* calculate the offset */ + if (br_dest(return_to - 4, instruction) == jump_to) + return JUMP_SOURCE_IS_OK; /* exactamundo! */ + else + return JUMP_SOURCE_IS_UNLIKELY; /* seems wrong */ + } + + return JUMP_SOURCE_IS_UNLIKELY; +} + +static char *note = 0; +static int next_address_likely_wrong = 0; + +/* How much slop we expect in the stack trace */ +#define FRAME_PLAY 8 + +/* + * Stack decode - + * unsigned addr; program counter + * unsigned *stack; IN/OUT stack pointer + * + * given an address within a function and a stack pointer, + * try to find the function from which this one was called + * and the stack pointer for that function. + * + * The return value is zero (if we get confused) or + * we determine that the return address has not yet + * been saved (early in the function prologue). Otherwise + * the return value is the address from which this function + * was called. + * + * Note that even is zero is returned (the second case) the + * stack pointer can be adjusted. + * + */ +static int +stack_decode(db_addr_t addr, unsigned *stack, int (*pr)(const char *, ...)) +{ + db_sym_t proc; + db_expr_t offset_from_proc; + unsigned instructions_to_search; + db_addr_t check_addr; + db_addr_t function_addr; /* start of function */ + unsigned r31 = *stack; /* the r31 of the function */ + unsigned inst; /* text of an instruction */ + unsigned ret_addr; /* address to which we return */ + unsigned tried_to_save_r1 = 0; + +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("\n>>>stack_decode(addr=%x, stack=%x)\n", + addr, *stack); +#endif + + /* get what we hope will be the db_sym_t for the function name */ + proc = db_search_symbol(addr, DB_STGY_PROC, &offset_from_proc); + if (offset_from_proc == addr) /* i.e. no symbol found */ + proc = DB_SYM_NULL; + + /* + * Somehow, find the start of this function. + * If we found a symbol above, it'll have the address. + * Otherwise, we've got to search for it.... + */ + if (proc != DB_SYM_NULL) { + char *names; + db_symbol_values(proc, &names, &function_addr); + if (names == 0) + return 0; +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("name %s address 0x%x\n", + names, function_addr); +#endif + } else { + int instructions_to_check = 400; + /* + * hmm - unable to find symbol. Search back + * looking for a function prolog. + */ + for (check_addr = addr; instructions_to_check-- > 0; check_addr -= 4) { + if (!db_trace_get_val(check_addr, &inst)) + break; + + if (SUBU_R31_R31_IMM(inst)) { +#if 0 + /* + * If the next instruction is "st r1, r31, ####" + * then we can feel safe we have the start of + * a function. + */ + if (!db_trace_get_val(check_addr + 4, &inst)) + continue; + if (ST_R1_R31_IMM(instr)) + break; /* success */ +#else + /* + * Latest GCC optimizer is just too good... the store + * of r1 might come much later... so we'll have to + * settle for just the "subr r31, r31, ###" to mark + * the start.... + */ + break; +#endif + } + /* + * if we come across a [jmp r1] or [jmp.n r1] assume we have hit + * the previous functions epilogue and stop our search. + * Since we know we would have hit the "subr r31, r31" if it was + * right in front of us, we know this doesn't have one so + * we just return failure.... + */ + if (JMP_R1(inst) || JMPN_R1(inst)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("ran into a [jmp r1] at %x (addr=%x)\n", + check_addr, addr); +#endif + return 0; + } + } + if (instructions_to_check < 0) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("couldn't find func start (addr=%x)\n", addr); +#endif + return 0; /* bummer, couldn't find it */ + } + function_addr = check_addr; + } + + /* + * We now know the start of the function (function_addr). + * If we're stopped right there, or if it's not a + * subu r31, r31, #### + * then we're done. + */ + if (addr == function_addr) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("at start of func\n"); +#endif + return 0; + } + if (!db_trace_get_val(function_addr, &inst)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("couldn't read %x at line %d\n", + function_addr, __LINE__); +#endif + return 0; + } + SHOW_INSTRUCTION(function_addr, inst, "start of function: "); + if (!SUBU_R31_R31_IMM(inst)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("<not subu,r31,r31,imm>\n"); +#endif + return 0; + } + + /* add the size of this frame to the stack (for the next frame) */ + *stack += IMM16VAL(inst); + + /* + * Search from the beginning of the function (funstart) to where we are + * in the function (addr) looking to see what kind of registers have + * been saved on the stack. + * + * We'll stop looking before we get to ADDR if we hit a branch. + */ + clear_local_saved_regs(); + check_addr = function_addr + 4; /* we know the first inst isn't a store */ + + for (instructions_to_search = (addr - check_addr)/sizeof(long); + instructions_to_search-- > 0; + check_addr += 4) { + union instruction instruction; + unsigned flags; + + /* read the instruction */ + if (!db_trace_get_val(check_addr, &instruction.rawbits)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("couldn't read %x at line %d\n", + check_addr, __LINE__); +#endif + break; + } + + SHOW_INSTRUCTION(check_addr, instruction.rawbits, "prolog: "); + + /* find out the particulars about this instruction */ + flags = m88k_instruction_info(instruction.rawbits); + + /* if a store to something off the stack pointer, note the value */ + if ((flags & STORE) && instruction.diatic.s1 == /*stack pointer*/31) { + unsigned value; + if (!have_local_reg(instruction.diatic.d)) { + if (instruction.diatic.d == 1) + tried_to_save_r1 = r31 + instruction.diatic.i16 ; + if (db_trace_get_val(r31 + instruction.diatic.i16, &value)) + save_reg(instruction.diatic.d, value); + } + if ((flags & DOUBLE) && !have_local_reg(instruction.diatic.d + 1)) { + if (instruction.diatic.d == 0) + tried_to_save_r1 = r31+instruction.diatic.i16 +4; + if (db_trace_get_val(r31+instruction.diatic.i16 +4, &value)) + save_reg(instruction.diatic.d + 1, value); + } + } + + /* if an inst that kills D (and maybe D+1), note that */ + if (flags & TRASHES) { + mark_reg_trashed(instruction.diatic.d); + if (flags & DOUBLE) + mark_reg_trashed(instruction.diatic.d + 1); + } + + /* if a flow control instruction, stop now (or next if delayed) */ + if ((flags & FLOW_CTRL) && instructions_to_search != 0) + instructions_to_search = (flags & DELAYED) ? 1 : 0; + } + + /* + * If we didn't save r1 at some point, we're hosed. + */ + if (!have_local_reg(1)) { + if (tried_to_save_r1) { + (*pr)(" <return value of next fcn unreadable in %08x>\n", + tried_to_save_r1); + } +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("didn't save r1\n"); +#endif + return 0; + } + + ret_addr = saved_reg_value(1); + +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("Return value is = %x, function_addr is %x.\n", + ret_addr, function_addr); +#endif + + /* + * In support of this, continuation.s puts the low bit on the + * return address for continuations (the return address will never + * be used, so it's ok to do anything you want to it). + */ + if (ret_addr & 1) { + note = "<<can not trace past a continuation>>"; + ret_addr = 0; + } else if (ret_addr != 0x00) { + switch (is_jump_source_ok(ret_addr, function_addr)) { + case JUMP_SOURCE_IS_OK: + break; /* excellent */ + + case JUMP_SOURCE_IS_BAD: +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("jump is bad\n"); +#endif + return 0; /* bummer */ + + case JUMP_SOURCE_IS_UNLIKELY: + next_address_likely_wrong = 1; + break; + } + } + + return ret_addr; +} + +static void +db_stack_trace_cmd2(db_regs_t *regs, int (*pr)(const char *, ...)) +{ + unsigned stack; + unsigned depth=1; + unsigned where; + unsigned ft; + unsigned pair[2]; + int i; + + /* + * Frame_is_sane returns: + * 1 if regs seems to be a reasonable kernel exception frame. + * 2 if regs seems to be a reasonable user exception frame + * (in the current task). + * 0 if this looks like neither. + */ + if ((ft = frame_is_sane(regs, 1)) == 0) { + (*pr)("Register frame 0x%x is suspicious; skipping trace\n", regs); + return; + } + + /* if user space and no user space trace specified, puke */ + if (ft == 2 && !(trace_flags & TRACE_USER_FLAG)) + return; + + /* fetch address */ + /* use sxip if valid, otherwise try snip or sfip */ + if (cputyp == CPU_88110) { + where = regs->exip & ~3; + } else { + where = ((regs->sxip & 2) ? regs->sxip : + ((regs->snip & 2) ? regs->snip : + regs->sfip) ) & ~3; + } + stack = regs->r[31]; + (*pr)("stack base = 0x%x\n", stack); + (*pr)("(0) "); /*depth of trace */ + if (trace_flags & TRACE_SHOWADDRESS_FLAG) + (*pr)("%08x ", where); + db_printsym(where, DB_STGY_PROC, pr); + clear_global_saved_regs(); + + /* see if this routine had a stack frame */ + if ((where=stack_decode(where, &stack, pr))==0) { + where = regs->r[1]; + (*pr)("(stackless)"); + } else { + print_args(); + if (trace_flags & TRACE_SHOWFRAME_FLAG) + (*pr)(" [frame 0x%x]", stack); + } + (*pr)("\n"); + if (note) { + (*pr)(" %s\n", note); + note = 0; + } + + do { + /* + * If requested, show preserved registers at the time + * the next-shown call was made. Only registers known to have + * changed from the last exception frame are shown, as others + * can be gotten at by looking at the exception frame. + */ + if (trace_flags & TRACE_SHOWCALLPRESERVED_FLAG) { + int r, title_printed = 0; + + for (r = FIRST_CALLPRESERVED_REG; r<=LAST_CALLPRESERVED_REG; r++) { + if (have_global_reg(r)) { + unsigned value = saved_reg_value(r); + if (title_printed == 0) { + title_printed = 1; + (*pr)("[in next func:"); + } + if (value == 0) + (*pr)(" r%d", r); + else if (value <= 9) + (*pr)(" r%d=%x", r, value); + else + (*pr)(" r%d=x%x", r, value); + } + } + if (title_printed) + (*pr)("]\n"); + } + + (*pr)("(%d)%c", depth++, next_address_likely_wrong ? '?':' '); + next_address_likely_wrong = 0; + + if (trace_flags & TRACE_SHOWADDRESS_FLAG) + (*pr)("%08x ", where); + db_printsym(where, DB_STGY_PROC, pr); + where = stack_decode(where, &stack, pr); + print_args(); + if (trace_flags & TRACE_SHOWFRAME_FLAG) + (*pr)(" [frame 0x%x]", stack); + (*pr)("\n"); + if (note) { + (*pr)(" %s\n", note); + note = 0; + } + } while (where); + + /* try to trace back over trap/exception */ + + stack &= ~7; /* double word aligned */ + /* take last top of stack, and try to find an exception frame near it */ + + i = FRAME_PLAY; + +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("(searching for exception frame at 0x%x)\n", stack); +#endif + + while (i) { + /* + * On the stack, a pointer to the exception frame is written + * in two adjacent words. In the case of a fault from the kernel, + * this should point to the frame right above them: + * + * Exception Frame Top + * .. + * Exception Frame Bottom <-- frame addr + * frame addr + * frame addr <-- stack pointer + * + * In the case of a fault from user mode, the top of stack + * will just have the address of the frame + * replicated twice. + * + * frame addr <-- top of stack + * frame addr + * + * Here we are just looking for kernel exception frames. + */ + + if (badwordaddr((vaddr_t)stack) || + badwordaddr((vaddr_t)(stack+4))) + break; + + db_read_bytes((vaddr_t)stack, 2*sizeof(int), (char *)pair); + + /* the pairs should match and equal stack+8 */ + if (pair[0] == pair[1]) { + if (pair[0] != stack+8) { + /* + if (!badwordaddr((vaddr_t)pair[0]) && (pair[0]!=0)) + (*pr)("stack_trace:found pair 0x%x but != to stack+8\n", + pair[0]); + */ + } + + else if (frame_is_sane((db_regs_t*)pair[0], 1) != 0) { + struct trapframe *frame = + (struct trapframe *)pair[0]; + + (*pr)("-------------- %s [EF: 0x%x] -------------\n", + m88k_exception_name(frame->tf_vector), + frame); + db_stack_trace_cmd2(&frame->tf_regs, pr); + return; + } +#ifdef TRACE_DEBUG + else if (DEBUGGING_ON) + (*pr)("pair matched, but frame at 0x%x looks insane\n", + stack+8); +#endif + } + stack += 8; + i--; + } + + /* + * If we go here, crawling back on the stack failed to find us + * a previous exception frame. Look for a user frame pointer + * pointed to by a word 8 bytes off of the top of the stack + * if the "u" option was specified. + */ + if (trace_flags & TRACE_USER_FLAG) { + struct trapframe *user; + + /* Make sure we are back on the right page */ + stack -= 4*FRAME_PLAY; + stack = stack & ~(KERNEL_STACK_SIZE-1); /* point to the bottom */ + stack += KERNEL_STACK_SIZE - 8; + + if (badwordaddr((vaddr_t)stack) || + badwordaddr((vaddr_t)stack)) + return; + + db_read_bytes((vaddr_t)stack, 2*sizeof(int), (char *)pair); + if (pair[0] != pair[1]) + return; + + /* have a hit */ + user = *((struct trapframe **)stack); + + if (frame_is_sane(&user->tf_regs, 1) == 2) { + (*pr)("---------------- %s [EF : 0x%x] -------------\n", + m88k_exception_name(user->tf_vector), user); + db_stack_trace_cmd2(&user->tf_regs, pr); + } + } +} + +/* + * stack trace - needs a pointer to a m88k saved state. + * + * If argument f is given, the stack pointer of each call frame is + * printed. + */ +void +db_stack_trace_print(db_expr_t addr, + int have_addr, + db_expr_t count, + char *modif, + int (*pr)(const char *, ...)) +{ + enum { + Default, Stack, Frame + } style = Default; + db_regs_t frame; + db_regs_t *regs; + union { + db_regs_t *frame; + db_expr_t num; + } arg; + + arg.num = addr; + + trace_flags = 0; /* flags will be set via modifers */ + + while (modif && *modif) { + switch (*modif++) { + case 'd': +#ifdef TRACE_DEBUG + trace_flags |= TRACE_DEBUG_FLAG; +#else + db_printtf("<debug trace not compiled in, ignoring>\n"); +#endif + break; + + case 's': style = Stack ; break; + case 'f': style = Frame ; break; + case 'p': trace_flags |= TRACE_SHOWCALLPRESERVED_FLAG; break; + case 'a': trace_flags |= TRACE_SHOWADDRESS_FLAG; break; + case 'F': trace_flags |= TRACE_SHOWFRAME_FLAG; break; + case 'u': trace_flags |= TRACE_USER_FLAG; break; + default: + (*pr)("unknown trace modifier [%c]\n", modif[-1]); + /*FALLTHROUGH*/ + case 'h': + (*pr)("usage: trace/[MODIFIER] [ARG]\n"); + (*pr)(" u = include user trace\n"); + (*pr)(" F = print stack frames\n"); + (*pr)(" a = show return addresses\n"); + (*pr)(" p = show call-preserved registers\n"); + (*pr)(" s = ARG is a stack pointer\n"); + (*pr)(" f = ARG is a frame pointer\n"); +#ifdef TRACE_DEBUG + (*pr)(" d = trace-debugging output\n"); +#endif + return; + } + } + + if (!have_addr && style != Default) { + (*pr)("expecting argument with /s or /f\n"); + return; + } + if (have_addr && style == Default) + style = Frame; + + switch (style) { + case Default: + regs = DDB_REGS; + break; + case Frame: + regs = arg.frame; + break; + case Stack: + { + unsigned val1, val2, sxip; + unsigned ptr; + bzero((void *)&frame, sizeof(frame)); +#define REASONABLE_FRAME_DISTANCE 2048 + + /* + * We've got to find the top of a stack frame so we can get both + * a PC and and real SP. + */ + for (ptr = arg.num;/**/; ptr += 4) { + /* Read a word from the named stack */ + if (db_trace_get_val(ptr, &val1) == 0) { + (*pr)("can't read from %x, aborting.\n", ptr); + return; + } + + /* + * See if it's a frame pointer.... if so it will be larger than + * the address it was taken from (i.e. point back up the stack) + * and we'll be able to read where it points. + */ + if (val1 <= ptr || + (val1 & 3) || + val1 > (ptr + REASONABLE_FRAME_DISTANCE)) + continue; + + /* peek at the next word to see if it could be a return address */ + if (db_trace_get_val(ptr, &sxip) == 0) { + (*pr)("can't read from %x, aborting.\n", ptr); + return; + } + if (sxip == 0 || !db_trace_get_val(sxip, &val2)) + continue; + + if (db_trace_get_val(val1, &val2) == 0) { + (*pr)("can't read from %x, aborting.\n", val1); + continue; + } + + /* + * The value we've just read will be either another frame pointer, + * or the start of another exception frame. + */ + if ( +#ifdef JEFF_DEBUG + val2 == 0 +#else + val2 == 0x12345678 +#endif + && db_trace_get_val(val1-4, &val2) && val2 == val1 + && db_trace_get_val(val1-8, &val2) && val2 == val1) { + /* we've found a frame, so the stack must have been good */ + (*pr)("%x looks like a frame, accepting %x\n",val1,ptr); + break; + } + + if (val2 > val1 && (val2 & 3) == 0) { + /* well, looks close enough to be another frame pointer */ + (*pr)("*%x = %x looks like a stack frame pointer, accepting %x\n", val1, val2, ptr); + break; + } + } + frame.r[31] = ptr; + frame.epsr = 0x800003f0U; + if (cputyp != CPU_88110) { + frame.sxip = sxip | 2; + frame.snip = frame.sxip + 4; + frame.sfip = frame.snip + 4; + } + (*pr)("[r31=%x, %sxip=%x]\n", frame.r[31], + cputyp == CPU_88110 ? "e" : "s", frame.sxip); + regs = &frame; + } + } + db_stack_trace_cmd2(regs, pr); +} |