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