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