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