Merge git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-2.6 into test
[pandora-kernel.git] / arch / parisc / kernel / unwind.c
1 /*
2  * Kernel unwinding support
3  *
4  * (c) 2002-2004 Randolph Chung <tausq@debian.org>
5  *
6  * Derived partially from the IA64 implementation. The PA-RISC
7  * Runtime Architecture Document is also a useful reference to
8  * understand what is happening here
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/sched.h>
14 #include <linux/slab.h>
15 #include <linux/kallsyms.h>
16 #include <linux/sort.h>
17
18 #include <asm/uaccess.h>
19 #include <asm/assembly.h>
20 #include <asm/asm-offsets.h>
21 #include <asm/ptrace.h>
22
23 #include <asm/unwind.h>
24
25 /* #define DEBUG 1 */
26 #ifdef DEBUG
27 #define dbg(x...) printk(x)
28 #else
29 #define dbg(x...)
30 #endif
31
32 #define KERNEL_START (KERNEL_BINARY_TEXT_START)
33
34 extern struct unwind_table_entry __start___unwind[];
35 extern struct unwind_table_entry __stop___unwind[];
36
37 static spinlock_t unwind_lock;
38 /*
39  * the kernel unwind block is not dynamically allocated so that
40  * we can call unwind_init as early in the bootup process as 
41  * possible (before the slab allocator is initialized)
42  */
43 static struct unwind_table kernel_unwind_table __read_mostly;
44 static LIST_HEAD(unwind_tables);
45
46 static inline const struct unwind_table_entry *
47 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
48 {
49         const struct unwind_table_entry *e = NULL;
50         unsigned long lo, hi, mid;
51
52         lo = 0; 
53         hi = table->length - 1; 
54         
55         while (lo <= hi) {
56                 mid = (hi - lo) / 2 + lo;
57                 e = &table->table[mid];
58                 if (addr < e->region_start)
59                         hi = mid - 1;
60                 else if (addr > e->region_end)
61                         lo = mid + 1;
62                 else
63                         return e;
64         }
65
66         return NULL;
67 }
68
69 static const struct unwind_table_entry *
70 find_unwind_entry(unsigned long addr)
71 {
72         struct unwind_table *table;
73         const struct unwind_table_entry *e = NULL;
74
75         if (addr >= kernel_unwind_table.start && 
76             addr <= kernel_unwind_table.end)
77                 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
78         else 
79                 list_for_each_entry(table, &unwind_tables, list) {
80                         if (addr >= table->start && 
81                             addr <= table->end)
82                                 e = find_unwind_entry_in_table(table, addr);
83                         if (e) {
84                                 /* Move-to-front to exploit common traces */
85                                 list_move(&table->list, &unwind_tables);
86                                 break;
87                         }
88                 }
89
90         return e;
91 }
92
93 static void
94 unwind_table_init(struct unwind_table *table, const char *name,
95                   unsigned long base_addr, unsigned long gp,
96                   void *table_start, void *table_end)
97 {
98         struct unwind_table_entry *start = table_start;
99         struct unwind_table_entry *end = 
100                 (struct unwind_table_entry *)table_end - 1;
101
102         table->name = name;
103         table->base_addr = base_addr;
104         table->gp = gp;
105         table->start = base_addr + start->region_start;
106         table->end = base_addr + end->region_end;
107         table->table = (struct unwind_table_entry *)table_start;
108         table->length = end - start + 1;
109         INIT_LIST_HEAD(&table->list);
110
111         for (; start <= end; start++) {
112                 if (start < end && 
113                     start->region_end > (start+1)->region_start) {
114                         printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1);
115                 }
116
117                 start->region_start += base_addr;
118                 start->region_end += base_addr;
119         }
120 }
121
122 static int cmp_unwind_table_entry(const void *a, const void *b)
123 {
124         return ((const struct unwind_table_entry *)a)->region_start
125              - ((const struct unwind_table_entry *)b)->region_start;
126 }
127
128 static void
129 unwind_table_sort(struct unwind_table_entry *start,
130                   struct unwind_table_entry *finish)
131 {
132         sort(start, finish - start, sizeof(struct unwind_table_entry),
133              cmp_unwind_table_entry, NULL);
134 }
135
136 struct unwind_table *
137 unwind_table_add(const char *name, unsigned long base_addr, 
138                  unsigned long gp,
139                  void *start, void *end)
140 {
141         struct unwind_table *table;
142         unsigned long flags;
143         struct unwind_table_entry *s = (struct unwind_table_entry *)start;
144         struct unwind_table_entry *e = (struct unwind_table_entry *)end;
145
146         unwind_table_sort(s, e);
147
148         table = kmalloc(sizeof(struct unwind_table), GFP_USER);
149         if (table == NULL)
150                 return NULL;
151         unwind_table_init(table, name, base_addr, gp, start, end);
152         spin_lock_irqsave(&unwind_lock, flags);
153         list_add_tail(&table->list, &unwind_tables);
154         spin_unlock_irqrestore(&unwind_lock, flags);
155
156         return table;
157 }
158
159 void unwind_table_remove(struct unwind_table *table)
160 {
161         unsigned long flags;
162
163         spin_lock_irqsave(&unwind_lock, flags);
164         list_del(&table->list);
165         spin_unlock_irqrestore(&unwind_lock, flags);
166
167         kfree(table);
168 }
169
170 /* Called from setup_arch to import the kernel unwind info */
171 int unwind_init(void)
172 {
173         long start, stop;
174         register unsigned long gp __asm__ ("r27");
175
176         start = (long)&__start___unwind[0];
177         stop = (long)&__stop___unwind[0];
178
179         spin_lock_init(&unwind_lock);
180
181         printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", 
182             start, stop,
183             (stop - start) / sizeof(struct unwind_table_entry));
184
185         unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
186                           gp, 
187                           &__start___unwind[0], &__stop___unwind[0]);
188 #if 0
189         {
190                 int i;
191                 for (i = 0; i < 10; i++)
192                 {
193                         printk("region 0x%x-0x%x\n", 
194                                 __start___unwind[i].region_start, 
195                                 __start___unwind[i].region_end);
196                 }
197         }
198 #endif
199         return 0;
200 }
201
202 #ifdef CONFIG_64BIT
203 #define get_func_addr(fptr) fptr[2]
204 #else
205 #define get_func_addr(fptr) fptr[0]
206 #endif
207
208 static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
209 {
210         extern void handle_interruption(int, struct pt_regs *);
211         static unsigned long *hi = (unsigned long *)&handle_interruption;
212
213         if (pc == get_func_addr(hi)) {
214                 struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
215                 dbg("Unwinding through handle_interruption()\n");
216                 info->prev_sp = regs->gr[30];
217                 info->prev_ip = regs->iaoq[0];
218
219                 return 1;
220         }
221
222         return 0;
223 }
224
225 static void unwind_frame_regs(struct unwind_frame_info *info)
226 {
227         const struct unwind_table_entry *e;
228         unsigned long npc;
229         unsigned int insn;
230         long frame_size = 0;
231         int looking_for_rp, rpoffset = 0;
232
233         e = find_unwind_entry(info->ip);
234         if (e == NULL) {
235                 unsigned long sp;
236                 extern char _stext[], _etext[];
237
238                 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
239
240 #ifdef CONFIG_KALLSYMS
241                 /* Handle some frequent special cases.... */
242                 {
243                         char symname[KSYM_NAME_LEN];
244                         char *modname;
245
246                         kallsyms_lookup(info->ip, NULL, NULL, &modname,
247                                 symname);
248
249                         dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
250
251                         if (strcmp(symname, "_switch_to_ret") == 0) {
252                                 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
253                                 info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
254                                 dbg("_switch_to_ret @ %lx - setting "
255                                     "prev_sp=%lx prev_ip=%lx\n", 
256                                     info->ip, info->prev_sp, 
257                                     info->prev_ip);
258                                 return;
259                         } else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
260                                    strcmp(symname, "syscall_exit") == 0) {
261                                 info->prev_ip = info->prev_sp = 0;
262                                 return;
263                         }
264                 }
265 #endif
266
267                 /* Since we are doing the unwinding blind, we don't know if
268                    we are adjusting the stack correctly or extracting the rp
269                    correctly. The rp is checked to see if it belongs to the
270                    kernel text section, if not we assume we don't have a 
271                    correct stack frame and we continue to unwind the stack.
272                    This is not quite correct, and will fail for loadable
273                    modules. */
274                 sp = info->sp & ~63;
275                 do {
276                         unsigned long tmp;
277
278                         info->prev_sp = sp - 64;
279                         info->prev_ip = 0;
280                         if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) 
281                                 break;
282                         info->prev_ip = tmp;
283                         sp = info->prev_sp;
284                 } while (info->prev_ip < (unsigned long)_stext ||
285                          info->prev_ip > (unsigned long)_etext);
286
287                 info->rp = 0;
288
289                 dbg("analyzing func @ %lx with no unwind info, setting "
290                     "prev_sp=%lx prev_ip=%lx\n", info->ip, 
291                     info->prev_sp, info->prev_ip);
292         } else {
293                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
294                     "Save_RP = %d, Millicode = %d size = %u\n", 
295                     e->region_start, e->region_end, e->Save_SP, e->Save_RP, 
296                     e->Millicode, e->Total_frame_size);
297
298                 looking_for_rp = e->Save_RP;
299
300                 for (npc = e->region_start; 
301                      (frame_size < (e->Total_frame_size << 3) || 
302                       looking_for_rp) && 
303                      npc < info->ip; 
304                      npc += 4) {
305
306                         insn = *(unsigned int *)npc;
307
308                         if ((insn & 0xffffc000) == 0x37de0000 ||
309                             (insn & 0xffe00000) == 0x6fc00000) {
310                                 /* ldo X(sp), sp, or stwm X,D(sp) */
311                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
312                                         ((insn & 0x3fff) >> 1);
313                                 dbg("analyzing func @ %lx, insn=%08x @ "
314                                     "%lx, frame_size = %ld\n", info->ip,
315                                     insn, npc, frame_size);
316                         } else if ((insn & 0xffe00008) == 0x73c00008) {
317                                 /* std,ma X,D(sp) */
318                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
319                                         (((insn >> 4) & 0x3ff) << 3);
320                                 dbg("analyzing func @ %lx, insn=%08x @ "
321                                     "%lx, frame_size = %ld\n", info->ip,
322                                     insn, npc, frame_size);
323                         } else if (insn == 0x6bc23fd9) { 
324                                 /* stw rp,-20(sp) */
325                                 rpoffset = 20;
326                                 looking_for_rp = 0;
327                                 dbg("analyzing func @ %lx, insn=stw rp,"
328                                     "-20(sp) @ %lx\n", info->ip, npc);
329                         } else if (insn == 0x0fc212c1) {
330                                 /* std rp,-16(sr0,sp) */
331                                 rpoffset = 16;
332                                 looking_for_rp = 0;
333                                 dbg("analyzing func @ %lx, insn=std rp,"
334                                     "-16(sp) @ %lx\n", info->ip, npc);
335                         }
336                 }
337
338                 if (!unwind_special(info, e->region_start, frame_size)) {
339                         info->prev_sp = info->sp - frame_size;
340                         if (e->Millicode)
341                                 info->rp = info->r31;
342                         else if (rpoffset)
343                                 info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
344                         info->prev_ip = info->rp;
345                         info->rp = 0;
346                 }
347
348                 dbg("analyzing func @ %lx, setting prev_sp=%lx "
349                     "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, 
350                     info->prev_ip, npc);
351         }
352 }
353
354 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
355                        struct pt_regs *regs)
356 {
357         memset(info, 0, sizeof(struct unwind_frame_info));
358         info->t = t;
359         info->sp = regs->gr[30];
360         info->ip = regs->iaoq[0];
361         info->rp = regs->gr[2];
362         info->r31 = regs->gr[31];
363
364         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", 
365             t ? (int)t->pid : -1, info->sp, info->ip);
366 }
367
368 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
369 {
370         struct pt_regs *r = &t->thread.regs;
371         struct pt_regs *r2;
372
373         r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
374         if (!r2)
375                 return;
376         *r2 = *r;
377         r2->gr[30] = r->ksp;
378         r2->iaoq[0] = r->kpc;
379         unwind_frame_init(info, t, r2);
380         kfree(r2);
381 }
382
383 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
384 {
385         unwind_frame_init(info, current, regs);
386 }
387
388 int unwind_once(struct unwind_frame_info *next_frame)
389 {
390         unwind_frame_regs(next_frame);
391
392         if (next_frame->prev_sp == 0 ||
393             next_frame->prev_ip == 0)
394                 return -1;
395
396         next_frame->sp = next_frame->prev_sp;
397         next_frame->ip = next_frame->prev_ip;
398         next_frame->prev_sp = 0;
399         next_frame->prev_ip = 0;
400
401         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", 
402             next_frame->t ? (int)next_frame->t->pid : -1, 
403             next_frame->sp, next_frame->ip);
404
405         return 0;
406 }
407
408 int unwind_to_user(struct unwind_frame_info *info)
409 {
410         int ret;
411         
412         do {
413                 ret = unwind_once(info);
414         } while (!ret && !(info->ip & 3));
415
416         return ret;
417 }
418
419 unsigned long return_address(unsigned int level)
420 {
421         struct unwind_frame_info info;
422         struct pt_regs r;
423         unsigned long sp;
424
425         /* initialize unwind info */
426         asm volatile ("copy %%r30, %0" : "=r"(sp));
427         memset(&r, 0, sizeof(struct pt_regs));
428         r.iaoq[0] = (unsigned long) current_text_addr();
429         r.gr[2] = (unsigned long) __builtin_return_address(0);
430         r.gr[30] = sp;
431         unwind_frame_init(&info, current, &r);
432
433         /* unwind stack */
434         ++level;
435         do {
436                 if (unwind_once(&info) < 0 || info.ip == 0)
437                         return 0;
438                 if (!__kernel_text_address(info.ip)) {
439                         return 0;
440                 }
441         } while (info.ip && level--);
442
443         return info.ip;
444 }