[PARISC] Fix unwinder on 64-bit kernels
[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
17 #include <asm/uaccess.h>
18 #include <asm/assembly.h>
19
20 #include <asm/unwind.h>
21
22 /* #define DEBUG 1 */
23 #ifdef DEBUG
24 #define dbg(x...) printk(x)
25 #else
26 #define dbg(x...)
27 #endif
28
29 #define KERNEL_START (KERNEL_BINARY_TEXT_START - 0x1000)
30
31 extern struct unwind_table_entry __start___unwind[];
32 extern struct unwind_table_entry __stop___unwind[];
33
34 static spinlock_t unwind_lock;
35 /*
36  * the kernel unwind block is not dynamically allocated so that
37  * we can call unwind_init as early in the bootup process as 
38  * possible (before the slab allocator is initialized)
39  */
40 static struct unwind_table kernel_unwind_table __read_mostly;
41 static LIST_HEAD(unwind_tables);
42
43 static inline const struct unwind_table_entry *
44 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
45 {
46         const struct unwind_table_entry *e = NULL;
47         unsigned long lo, hi, mid;
48
49         lo = 0; 
50         hi = table->length - 1; 
51         
52         while (lo <= hi) {
53                 mid = (hi - lo) / 2 + lo;
54                 e = &table->table[mid];
55                 if (addr < e->region_start)
56                         hi = mid - 1;
57                 else if (addr > e->region_end)
58                         lo = mid + 1;
59                 else
60                         return e;
61         }
62
63         return NULL;
64 }
65
66 static const struct unwind_table_entry *
67 find_unwind_entry(unsigned long addr)
68 {
69         struct unwind_table *table;
70         const struct unwind_table_entry *e = NULL;
71
72         if (addr >= kernel_unwind_table.start && 
73             addr <= kernel_unwind_table.end)
74                 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
75         else 
76                 list_for_each_entry(table, &unwind_tables, list) {
77                         if (addr >= table->start && 
78                             addr <= table->end)
79                                 e = find_unwind_entry_in_table(table, addr);
80                         if (e)
81                                 break;
82                 }
83
84         return e;
85 }
86
87 static void
88 unwind_table_init(struct unwind_table *table, const char *name,
89                   unsigned long base_addr, unsigned long gp,
90                   void *table_start, void *table_end)
91 {
92         struct unwind_table_entry *start = table_start;
93         struct unwind_table_entry *end = 
94                 (struct unwind_table_entry *)table_end - 1;
95
96         table->name = name;
97         table->base_addr = base_addr;
98         table->gp = gp;
99         table->start = base_addr + start->region_start;
100         table->end = base_addr + end->region_end;
101         table->table = (struct unwind_table_entry *)table_start;
102         table->length = end - start + 1;
103         INIT_LIST_HEAD(&table->list);
104
105         for (; start <= end; start++) {
106                 if (start < end && 
107                     start->region_end > (start+1)->region_start) {
108                         printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1);
109                 }
110
111                 start->region_start += base_addr;
112                 start->region_end += base_addr;
113         }
114 }
115
116 static void
117 unwind_table_sort(struct unwind_table_entry *start,
118                   struct unwind_table_entry *finish)
119 {
120         struct unwind_table_entry el, *p, *q;
121
122         for (p = start + 1; p < finish; ++p) {
123                 if (p[0].region_start < p[-1].region_start) {
124                         el = *p;
125                         q = p;
126                         do {
127                                 q[0] = q[-1];
128                                 --q;
129                         } while (q > start && 
130                                  el.region_start < q[-1].region_start);
131                         *q = el;
132                 }
133         }
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 static 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 static void unwind_frame_regs(struct unwind_frame_info *info)
203 {
204         const struct unwind_table_entry *e;
205         unsigned long npc;
206         unsigned int insn;
207         long frame_size = 0;
208         int looking_for_rp, rpoffset = 0;
209
210         e = find_unwind_entry(info->ip);
211         if (e == NULL) {
212                 unsigned long sp;
213                 extern char _stext[], _etext[];
214
215                 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
216
217 #ifdef CONFIG_KALLSYMS
218                 /* Handle some frequent special cases.... */
219                 {
220                         char symname[KSYM_NAME_LEN+1];
221                         char *modname;
222
223                         kallsyms_lookup(info->ip, NULL, NULL, &modname,
224                                 symname);
225
226                         dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
227
228                         if (strcmp(symname, "_switch_to_ret") == 0) {
229                                 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
230                                 info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
231                                 dbg("_switch_to_ret @ %lx - setting "
232                                     "prev_sp=%lx prev_ip=%lx\n", 
233                                     info->ip, info->prev_sp, 
234                                     info->prev_ip);
235                                 return;
236                         } else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
237                                    strcmp(symname, "syscall_exit") == 0) {
238                                 info->prev_ip = info->prev_sp = 0;
239                                 return;
240                         }
241                 }
242 #endif
243
244                 /* Since we are doing the unwinding blind, we don't know if
245                    we are adjusting the stack correctly or extracting the rp
246                    correctly. The rp is checked to see if it belongs to the
247                    kernel text section, if not we assume we don't have a 
248                    correct stack frame and we continue to unwind the stack.
249                    This is not quite correct, and will fail for loadable
250                    modules. */
251                 sp = info->sp & ~63;
252                 do {
253                         unsigned long tmp;
254
255                         info->prev_sp = sp - 64;
256                         info->prev_ip = 0;
257                         if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) 
258                                 break;
259                         info->prev_ip = tmp;
260                         sp = info->prev_sp;
261                 } while (info->prev_ip < (unsigned long)_stext ||
262                          info->prev_ip > (unsigned long)_etext);
263
264                 info->rp = 0;
265
266                 dbg("analyzing func @ %lx with no unwind info, setting "
267                     "prev_sp=%lx prev_ip=%lx\n", info->ip, 
268                     info->prev_sp, info->prev_ip);
269         } else {
270                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
271                     "Save_RP = %d, Millicode = %d size = %u\n", 
272                     e->region_start, e->region_end, e->Save_SP, e->Save_RP, 
273                     e->Millicode, e->Total_frame_size);
274
275                 looking_for_rp = e->Save_RP;
276
277                 for (npc = e->region_start; 
278                      (frame_size < (e->Total_frame_size << 3) || 
279                       looking_for_rp) && 
280                      npc < info->ip; 
281                      npc += 4) {
282
283                         insn = *(unsigned int *)npc;
284
285                         if ((insn & 0xffffc000) == 0x37de0000 ||
286                             (insn & 0xffe00000) == 0x6fc00000) {
287                                 /* ldo X(sp), sp, or stwm X,D(sp) */
288                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
289                                         ((insn & 0x3fff) >> 1);
290                                 dbg("analyzing func @ %lx, insn=%08x @ "
291                                     "%lx, frame_size = %ld\n", info->ip,
292                                     insn, npc, frame_size);
293                         } else if ((insn & 0xffe00008) == 0x73c00008) {
294                                 /* std,ma X,D(sp) */
295                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
296                                         (((insn >> 4) & 0x3ff) << 3);
297                                 dbg("analyzing func @ %lx, insn=%08x @ "
298                                     "%lx, frame_size = %ld\n", info->ip,
299                                     insn, npc, frame_size);
300                         } else if (insn == 0x6bc23fd9) { 
301                                 /* stw rp,-20(sp) */
302                                 rpoffset = 20;
303                                 looking_for_rp = 0;
304                                 dbg("analyzing func @ %lx, insn=stw rp,"
305                                     "-20(sp) @ %lx\n", info->ip, npc);
306                         } else if (insn == 0x0fc212c1) {
307                                 /* std rp,-16(sr0,sp) */
308                                 rpoffset = 16;
309                                 looking_for_rp = 0;
310                                 dbg("analyzing func @ %lx, insn=std rp,"
311                                     "-16(sp) @ %lx\n", info->ip, npc);
312                         }
313                 }
314
315                 info->prev_sp = info->sp - frame_size;
316                 if (e->Millicode)
317                         info->rp = info->r31;
318                 else if (rpoffset)
319                         info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
320                 info->prev_ip = info->rp;
321                 info->rp = 0;
322
323                 dbg("analyzing func @ %lx, setting prev_sp=%lx "
324                     "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, 
325                     info->prev_ip, npc);
326         }
327 }
328
329 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
330                        struct pt_regs *regs)
331 {
332         memset(info, 0, sizeof(struct unwind_frame_info));
333         info->t = t;
334         info->sp = regs->gr[30];
335         info->ip = regs->iaoq[0];
336         info->rp = regs->gr[2];
337         info->r31 = regs->gr[31];
338
339         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", 
340             t ? (int)t->pid : -1, info->sp, info->ip);
341 }
342
343 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
344 {
345         struct pt_regs *r = &t->thread.regs;
346         struct pt_regs *r2;
347
348         r2 = kmalloc(sizeof(struct pt_regs), GFP_KERNEL);
349         if (!r2)
350                 return;
351         *r2 = *r;
352         r2->gr[30] = r->ksp;
353         r2->iaoq[0] = r->kpc;
354         unwind_frame_init(info, t, r2);
355         kfree(r2);
356 }
357
358 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
359 {
360         unwind_frame_init(info, current, regs);
361 }
362
363 int unwind_once(struct unwind_frame_info *next_frame)
364 {
365         unwind_frame_regs(next_frame);
366
367         if (next_frame->prev_sp == 0 ||
368             next_frame->prev_ip == 0)
369                 return -1;
370
371         next_frame->sp = next_frame->prev_sp;
372         next_frame->ip = next_frame->prev_ip;
373         next_frame->prev_sp = 0;
374         next_frame->prev_ip = 0;
375
376         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", 
377             next_frame->t ? (int)next_frame->t->pid : -1, 
378             next_frame->sp, next_frame->ip);
379
380         return 0;
381 }
382
383 int unwind_to_user(struct unwind_frame_info *info)
384 {
385         int ret;
386         
387         do {
388                 ret = unwind_once(info);
389         } while (!ret && !(info->ip & 3));
390
391         return ret;
392 }
393
394 module_init(unwind_init);