[PARISC] Fix unwinder on 64-bit kernels
[linux-2.6/lfs.git] / arch / parisc / kernel / unwind.c
blobcad9d78312e00f6bd121146feceeb238a4fd9e86
1 /*
2 * Kernel unwinding support
4 * (c) 2002-2004 Randolph Chung <tausq@debian.org>
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 */
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>
17 #include <asm/uaccess.h>
18 #include <asm/assembly.h>
20 #include <asm/unwind.h>
22 /* #define DEBUG 1 */
23 #ifdef DEBUG
24 #define dbg(x...) printk(x)
25 #else
26 #define dbg(x...)
27 #endif
29 #define KERNEL_START (KERNEL_BINARY_TEXT_START - 0x1000)
31 extern struct unwind_table_entry __start___unwind[];
32 extern struct unwind_table_entry __stop___unwind[];
34 static spinlock_t unwind_lock;
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)
40 static struct unwind_table kernel_unwind_table __read_mostly;
41 static LIST_HEAD(unwind_tables);
43 static inline const struct unwind_table_entry *
44 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
46 const struct unwind_table_entry *e = NULL;
47 unsigned long lo, hi, mid;
49 lo = 0;
50 hi = table->length - 1;
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;
63 return NULL;
66 static const struct unwind_table_entry *
67 find_unwind_entry(unsigned long addr)
69 struct unwind_table *table;
70 const struct unwind_table_entry *e = NULL;
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;
84 return e;
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)
92 struct unwind_table_entry *start = table_start;
93 struct unwind_table_entry *end =
94 (struct unwind_table_entry *)table_end - 1;
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);
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);
111 start->region_start += base_addr;
112 start->region_end += base_addr;
116 static void
117 unwind_table_sort(struct unwind_table_entry *start,
118 struct unwind_table_entry *finish)
120 struct unwind_table_entry el, *p, *q;
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;
136 struct unwind_table *
137 unwind_table_add(const char *name, unsigned long base_addr,
138 unsigned long gp,
139 void *start, void *end)
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;
146 unwind_table_sort(s, e);
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);
156 return table;
159 void unwind_table_remove(struct unwind_table *table)
161 unsigned long flags;
163 spin_lock_irqsave(&unwind_lock, flags);
164 list_del(&table->list);
165 spin_unlock_irqrestore(&unwind_lock, flags);
167 kfree(table);
170 /* Called from setup_arch to import the kernel unwind info */
171 static int unwind_init(void)
173 long start, stop;
174 register unsigned long gp __asm__ ("r27");
176 start = (long)&__start___unwind[0];
177 stop = (long)&__stop___unwind[0];
179 spin_lock_init(&unwind_lock);
181 printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
182 start, stop,
183 (stop - start) / sizeof(struct unwind_table_entry));
185 unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
186 gp,
187 &__start___unwind[0], &__stop___unwind[0]);
188 #if 0
190 int i;
191 for (i = 0; i < 10; i++)
193 printk("region 0x%x-0x%x\n",
194 __start___unwind[i].region_start,
195 __start___unwind[i].region_end);
198 #endif
199 return 0;
202 static void unwind_frame_regs(struct unwind_frame_info *info)
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;
210 e = find_unwind_entry(info->ip);
211 if (e == NULL) {
212 unsigned long sp;
213 extern char _stext[], _etext[];
215 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
217 #ifdef CONFIG_KALLSYMS
218 /* Handle some frequent special cases.... */
220 char symname[KSYM_NAME_LEN+1];
221 char *modname;
223 kallsyms_lookup(info->ip, NULL, NULL, &modname,
224 symname);
226 dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
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;
242 #endif
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;
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);
264 info->rp = 0;
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);
275 looking_for_rp = e->Save_RP;
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) {
283 insn = *(unsigned int *)npc;
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);
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;
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);
329 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
330 struct pt_regs *regs)
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];
339 dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n",
340 t ? (int)t->pid : -1, info->sp, info->ip);
343 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
345 struct pt_regs *r = &t->thread.regs;
346 struct pt_regs *r2;
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);
358 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
360 unwind_frame_init(info, current, regs);
363 int unwind_once(struct unwind_frame_info *next_frame)
365 unwind_frame_regs(next_frame);
367 if (next_frame->prev_sp == 0 ||
368 next_frame->prev_ip == 0)
369 return -1;
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;
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);
380 return 0;
383 int unwind_to_user(struct unwind_frame_info *info)
385 int ret;
387 do {
388 ret = unwind_once(info);
389 } while (!ret && !(info->ip & 3));
391 return ret;
394 module_init(unwind_init);