uboot/arch/powerpc/lib/kgdb.c
<<
>>
Prefs
   1#include <common.h>
   2#include <command.h>
   3#include <kgdb.h>
   4#include <asm/signal.h>
   5#include <asm/processor.h>
   6
   7#define PC_REGNUM 64
   8#define SP_REGNUM 1
   9
  10void breakinst(void);
  11
  12int
  13kgdb_setjmp(long *buf)
  14{
  15        unsigned long temp;
  16
  17        asm volatile("mflr %0; stw %0,0(%1);"
  18             "stw %%r1,4(%1); stw %%r2,8(%1);"
  19             "mfcr %0; stw %0,12(%1);"
  20             "stmw %%r13,16(%1)"
  21             : "=&r"(temp) : "r" (buf));
  22        /* XXX should save fp regs as well */
  23        return 0;
  24}
  25
  26void
  27kgdb_longjmp(long *buf, int val)
  28{
  29        unsigned long temp;
  30
  31        if (val == 0)
  32                val = 1;
  33
  34        asm volatile("lmw %%r13,16(%1);"
  35             "lwz %0,12(%1); mtcrf 0x38,%0;"
  36             "lwz %0,0(%1); lwz %%r1,4(%1); lwz %%r2,8(%1);"
  37             "mtlr %0; mr %%r3,%2"
  38             : "=&r"(temp) : "r" (buf), "r" (val));
  39}
  40
  41static inline unsigned long
  42get_msr(void)
  43{
  44        unsigned long msr;
  45        asm volatile("mfmsr %0" : "=r" (msr):);
  46        return msr;
  47}
  48
  49static inline void
  50set_msr(unsigned long msr)
  51{
  52        asm volatile("mtmsr %0" : : "r" (msr));
  53}
  54
  55/* Convert the SPARC hardware trap type code to a unix signal number. */
  56/*
  57 * This table contains the mapping between PowerPC hardware trap types, and
  58 * signals, which are primarily what GDB understands.
  59 */
  60static struct hard_trap_info
  61{
  62        unsigned int tt;                /* Trap type code for powerpc */
  63        unsigned char signo;            /* Signal that we map this trap into */
  64} hard_trap_info[] = {
  65        { 0x200, SIGSEGV },                     /* machine check */
  66        { 0x300, SIGSEGV },                     /* address error (store) */
  67        { 0x400, SIGBUS },                      /* instruction bus error */
  68        { 0x500, SIGINT },                      /* interrupt */
  69        { 0x600, SIGBUS },                      /* alingment */
  70        { 0x700, SIGTRAP },                     /* breakpoint trap */
  71        { 0x800, SIGFPE },                      /* fpu unavail */
  72        { 0x900, SIGALRM },                     /* decrementer */
  73        { 0xa00, SIGILL },                      /* reserved */
  74        { 0xb00, SIGILL },                      /* reserved */
  75        { 0xc00, SIGCHLD },                     /* syscall */
  76        { 0xd00, SIGTRAP },                     /* single-step/watch */
  77        { 0xe00, SIGFPE },                      /* fp assist */
  78        { 0, 0}                         /* Must be last */
  79};
  80
  81static int
  82computeSignal(unsigned int tt)
  83{
  84        struct hard_trap_info *ht;
  85
  86        for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
  87                if (ht->tt == tt)
  88                        return ht->signo;
  89
  90        return SIGHUP;         /* default for things we don't know about */
  91}
  92
  93void
  94kgdb_enter(struct pt_regs *regs, kgdb_data *kdp)
  95{
  96        unsigned long msr;
  97
  98        kdp->private[0] = msr = get_msr();
  99        set_msr(msr & ~MSR_EE); /* disable interrupts */
 100
 101        if (regs->nip == (unsigned long)breakinst) {
 102                /* Skip over breakpoint trap insn */
 103                regs->nip += 4;
 104        }
 105        regs->msr &= ~MSR_SE;
 106
 107        /* reply to host that an exception has occurred */
 108        kdp->sigval = computeSignal(regs->trap);
 109
 110        kdp->nregs = 2;
 111
 112        kdp->regs[0].num = PC_REGNUM;
 113        kdp->regs[0].val = regs->nip;
 114
 115        kdp->regs[1].num = SP_REGNUM;
 116        kdp->regs[1].val = regs->gpr[SP_REGNUM];
 117}
 118
 119void
 120kgdb_exit(struct pt_regs *regs, kgdb_data *kdp)
 121{
 122        unsigned long msr = kdp->private[0];
 123
 124        if (kdp->extype & KGDBEXIT_WITHADDR)
 125                regs->nip = kdp->exaddr;
 126
 127        switch (kdp->extype & KGDBEXIT_TYPEMASK) {
 128
 129        case KGDBEXIT_KILL:
 130        case KGDBEXIT_CONTINUE:
 131                set_msr(msr);
 132                break;
 133
 134        case KGDBEXIT_SINGLE:
 135                regs->msr |= MSR_SE;
 136#if 0
 137                set_msr(msr | MSR_SE);
 138#endif
 139                break;
 140        }
 141}
 142
 143int
 144kgdb_trap(struct pt_regs *regs)
 145{
 146        return (regs->trap);
 147}
 148
 149/* return the value of the CPU registers.
 150 * some of them are non-PowerPC names :(
 151 * they are stored in gdb like:
 152 * struct {
 153 *     u32 gpr[32];
 154 *     f64 fpr[32];
 155 *     u32 pc, ps, cnd, lr; (ps=msr)
 156 *     u32 cnt, xer, mq;
 157 * }
 158 */
 159
 160#define SPACE_REQUIRED  ((32*4)+(32*8)+(6*4))
 161
 162#ifdef CONFIG_8260
 163/* store floating double indexed */
 164#define STFDI(n,p)      __asm__ __volatile__ ("stfd " #n ",%0" : "=o"(p[2*n]))
 165/* store floating double multiple */
 166#define STFDM(p)        { STFDI( 0,p); STFDI( 1,p); STFDI( 2,p); STFDI( 3,p); \
 167                          STFDI( 4,p); STFDI( 5,p); STFDI( 6,p); STFDI( 7,p); \
 168                          STFDI( 8,p); STFDI( 9,p); STFDI(10,p); STFDI(11,p); \
 169                          STFDI(12,p); STFDI(13,p); STFDI(14,p); STFDI(15,p); \
 170                          STFDI(16,p); STFDI(17,p); STFDI(18,p); STFDI(19,p); \
 171                          STFDI(20,p); STFDI(21,p); STFDI(22,p); STFDI(23,p); \
 172                          STFDI(24,p); STFDI(25,p); STFDI(26,p); STFDI(27,p); \
 173                          STFDI(28,p); STFDI(29,p); STFDI(30,p); STFDI(31,p); }
 174#endif
 175
 176int
 177kgdb_getregs(struct pt_regs *regs, char *buf, int max)
 178{
 179        int i;
 180        unsigned long *ptr = (unsigned long *)buf;
 181
 182        if (max < SPACE_REQUIRED)
 183                kgdb_error(KGDBERR_NOSPACE);
 184
 185        if ((unsigned long)ptr & 3)
 186                kgdb_error(KGDBERR_ALIGNFAULT);
 187
 188        /* General Purpose Regs */
 189        for (i = 0; i < 32; i++)
 190                *ptr++ = regs->gpr[i];
 191
 192        /* Floating Point Regs */
 193#ifdef CONFIG_8260
 194        STFDM(ptr);
 195        ptr += 32*2;
 196#else
 197        for (i = 0; i < 32; i++) {
 198                *ptr++ = 0;
 199                *ptr++ = 0;
 200        }
 201#endif
 202
 203        /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
 204        *ptr++ = regs->nip;
 205        *ptr++ = regs->msr;
 206        *ptr++ = regs->ccr;
 207        *ptr++ = regs->link;
 208        *ptr++ = regs->ctr;
 209        *ptr++ = regs->xer;
 210
 211        return (SPACE_REQUIRED);
 212}
 213
 214/* set the value of the CPU registers */
 215
 216#ifdef CONFIG_8260
 217/* load floating double */
 218#define LFD(n,v)        __asm__ __volatile__ ("lfd " #n ",%0" :: "o"(v))
 219/* load floating double indexed */
 220#define LFDI(n,p)       __asm__ __volatile__ ("lfd " #n ",%0" :: "o"((p)[2*n]))
 221/* load floating double multiple */
 222#define LFDM(p)         { LFDI( 0,p); LFDI( 1,p); LFDI( 2,p); LFDI( 3,p); \
 223                          LFDI( 4,p); LFDI( 5,p); LFDI( 6,p); LFDI( 7,p); \
 224                          LFDI( 8,p); LFDI( 9,p); LFDI(10,p); LFDI(11,p); \
 225                          LFDI(12,p); LFDI(13,p); LFDI(14,p); LFDI(15,p); \
 226                          LFDI(16,p); LFDI(17,p); LFDI(18,p); LFDI(19,p); \
 227                          LFDI(20,p); LFDI(21,p); LFDI(22,p); LFDI(23,p); \
 228                          LFDI(24,p); LFDI(25,p); LFDI(26,p); LFDI(27,p); \
 229                          LFDI(28,p); LFDI(29,p); LFDI(30,p); LFDI(31,p); }
 230#endif
 231
 232void
 233kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length)
 234{
 235        unsigned long *ptr = (unsigned long *)buf;
 236
 237        if (regno < 0 || regno >= 70)
 238                kgdb_error(KGDBERR_BADPARAMS);
 239        else if (regno >= 32 && regno < 64) {
 240                if (length < 8)
 241                        kgdb_error(KGDBERR_NOSPACE);
 242        }
 243        else {
 244                if (length < 4)
 245                        kgdb_error(KGDBERR_NOSPACE);
 246        }
 247
 248        if ((unsigned long)ptr & 3)
 249                kgdb_error(KGDBERR_ALIGNFAULT);
 250
 251        if (regno >= 0 && regno < 32)
 252                regs->gpr[regno] = *ptr;
 253        else switch (regno) {
 254
 255#ifdef CONFIG_8260
 256#define caseF(n) \
 257        case (n) + 32:  LFD(n, *ptr);           break;
 258
 259caseF( 0) caseF( 1) caseF( 2) caseF( 3) caseF( 4) caseF( 5) caseF( 6) caseF( 7)
 260caseF( 8) caseF( 9) caseF(10) caseF(11) caseF(12) caseF(13) caseF(14) caseF(15)
 261caseF(16) caseF(17) caseF(18) caseF(19) caseF(20) caseF(21) caseF(22) caseF(23)
 262caseF(24) caseF(25) caseF(26) caseF(27) caseF(28) caseF(29) caseF(30) caseF(31)
 263
 264#undef caseF
 265#endif
 266
 267        case 64:        regs->nip = *ptr;       break;
 268        case 65:        regs->msr = *ptr;       break;
 269        case 66:        regs->ccr = *ptr;       break;
 270        case 67:        regs->link = *ptr;      break;
 271        case 68:        regs->ctr = *ptr;       break;
 272        case 69:        regs->ctr = *ptr;       break;
 273
 274        default:
 275                kgdb_error(KGDBERR_BADPARAMS);
 276        }
 277}
 278
 279void
 280kgdb_putregs(struct pt_regs *regs, char *buf, int length)
 281{
 282        int i;
 283        unsigned long *ptr = (unsigned long *)buf;
 284
 285        if (length < SPACE_REQUIRED)
 286                kgdb_error(KGDBERR_NOSPACE);
 287
 288        if ((unsigned long)ptr & 3)
 289                kgdb_error(KGDBERR_ALIGNFAULT);
 290
 291        /*
 292         * If the stack pointer has moved, you should pray.
 293         * (cause only god can help you).
 294         */
 295
 296        /* General Purpose Regs */
 297        for (i = 0; i < 32; i++)
 298                regs->gpr[i] = *ptr++;
 299
 300        /* Floating Point Regs */
 301#ifdef CONFIG_8260
 302        LFDM(ptr);
 303#endif
 304        ptr += 32*2;
 305
 306        /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
 307        regs->nip = *ptr++;
 308        regs->msr = *ptr++;
 309        regs->ccr = *ptr++;
 310        regs->link = *ptr++;
 311        regs->ctr = *ptr++;
 312        regs->xer = *ptr++;
 313}
 314
 315/* This function will generate a breakpoint exception.  It is used at the
 316   beginning of a program to sync up with a debugger and can be used
 317   otherwise as a quick means to stop program execution and "break" into
 318   the debugger. */
 319
 320void
 321kgdb_breakpoint(int argc, char * const argv[])
 322{
 323        asm("   .globl breakinst\n\
 324             breakinst: .long 0x7d821008\n\
 325            ");
 326}
 327