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