This commit was manufactured by cvs2svn to create branch 'vserver'.
[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 /*
12  * J. David Anglin writes:
13  *
14  * "You have to adjust the current sp to that at the begining of the function.
15  * There can be up to two stack additions to allocate the frame in the
16  * prologue.  Similar things happen in the epilogue.  In the presence of
17  * interrupts, you have to be concerned about where you are in the function
18  * and what stack adjustments have taken place."
19  *
20  * For now these cases are not handled, but they should be!
21  */
22
23 #include <linux/config.h>
24 #include <linux/kernel.h>
25 #include <linux/init.h>
26 #include <linux/slab.h>
27
28 #include <asm/uaccess.h>
29
30 #include <asm/unwind.h>
31
32 /* #define DEBUG 1 */
33 #ifdef DEBUG
34 #define dbg(x...) printk(x)
35 #else
36 #define dbg(x...)
37 #endif
38
39 extern const struct unwind_table_entry __start___unwind[];
40 extern const struct unwind_table_entry __stop___unwind[];
41
42 static spinlock_t unwind_lock;
43 /*
44  * the kernel unwind block is not dynamically allocated so that
45  * we can call unwind_init as early in the bootup process as 
46  * possible (before the slab allocator is initialized)
47  */
48 static struct unwind_table kernel_unwind_table;
49 static struct unwind_table *unwind_tables, *unwind_tables_end;
50
51
52 static inline const struct unwind_table_entry *
53 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
54 {
55         const struct unwind_table_entry *e = 0;
56         unsigned long lo, hi, mid;
57
58         addr -= table->base_addr;
59
60         for (lo = 0, hi = table->length; lo < hi; )
61         {
62                 mid = (lo + hi) / 2;
63                 e = &table->table[mid];
64                 if (addr < e->region_start)
65                         hi = mid;
66                 else if (addr > e->region_end)
67                         lo = mid + 1;
68                 else
69                         break;
70         }
71
72         return e;
73 }
74
75 static inline const struct unwind_table_entry *
76 find_unwind_entry(unsigned long addr)
77 {
78         struct unwind_table *table = unwind_tables;
79         const struct unwind_table_entry *e = NULL;
80
81         if (addr >= kernel_unwind_table.start && 
82             addr <= kernel_unwind_table.end)
83                 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
84         else
85                 for (; table; table = table->next)
86                 {
87                         if (addr >= table->start && 
88                             addr <= table->end)
89                                 e = find_unwind_entry_in_table(table, addr);
90                         if (e)
91                                 break;
92                 }
93
94         return e;
95 }
96
97 static void
98 unwind_table_init(struct unwind_table *table, const char *name,
99                   unsigned long base_addr, unsigned long gp,
100                   const void *table_start, const void *table_end)
101 {
102         const struct unwind_table_entry *start = table_start;
103         const struct unwind_table_entry *end = table_end - 1;
104
105         table->name = name;
106         table->base_addr = base_addr;
107         table->gp = gp;
108         table->start = base_addr + start->region_start;
109         table->end = base_addr + end->region_end;
110         table->table = (struct unwind_table_entry *)table_start;
111         table->length = end - start;
112         table->next = NULL;
113 }
114
115 void *
116 unwind_table_add(const char *name, unsigned long base_addr, 
117                  unsigned long gp,
118                  const void *start, const 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 0;
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) {
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                 /* Since we are doing the unwinding blind, we don't know if
188                    we are adjusting the stack correctly or extracting the rp
189                    correctly. The rp is checked to see if it belongs to the
190                    kernel text section, if not we assume we don't have a 
191                    correct stack frame and we continue to unwind the stack.
192                    This is not quite correct, and will fail for loadable
193                    modules. */
194                 sp = info->sp & ~63;
195                 do {
196                         info->prev_sp = sp - 64;
197
198                         /* FIXME: what happens if we unwind too far so that 
199                            sp no longer falls in a mapped kernel page? */
200 #ifndef __LP64__
201                         info->prev_ip = *(unsigned long *)(info->prev_sp - 20);
202 #else
203                         info->prev_ip = *(unsigned long *)(info->prev_sp - 16);
204 #endif
205
206                         sp = info->prev_sp;
207                 } while (info->prev_ip < (unsigned long)_stext ||
208                          info->prev_ip > (unsigned long)_etext);
209         } else {
210
211                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, Save_RP = %d size = %u\n",
212                                 e->region_start, e->region_end, e->Save_SP, e->Save_RP, e->Total_frame_size);
213
214                 looking_for_rp = e->Save_RP;
215
216                 for (npc = e->region_start; 
217                      (frame_size < (e->Total_frame_size << 3) || looking_for_rp) && 
218                      npc < info->ip; 
219                      npc += 4) {
220
221                         insn = *(unsigned int *)npc;
222
223                         if ((insn & 0xffffc000) == 0x37de0000 ||
224                             (insn & 0xffe00000) == 0x6fc00000) {
225                                 /* ldo X(sp), sp, or stwm X,D(sp) */
226                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
227                                         ((insn & 0x3fff) >> 1);
228                         } else if ((insn & 0xffe00008) == 0x7ec00008) {
229                                 /* std,ma X,D(sp) */
230                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
231                                         (((insn >> 4) & 0x3ff) << 3);
232                         } else if (insn == 0x6bc23fd9) { 
233                                 /* stw rp,-20(sp) */
234                                 rpoffset = 20;
235                                 looking_for_rp = 0;
236                         } else if (insn == 0x0fc212c1) {
237                                 /* std rp,-16(sr0,sp) */
238                                 rpoffset = 16;
239                                 looking_for_rp = 0;
240                         }
241                 }
242
243                 info->prev_sp = info->sp - frame_size;
244                 if (rpoffset)
245                         info->prev_ip = *(unsigned long *)(info->prev_sp - rpoffset);
246         }
247 }
248
249 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
250                        struct pt_regs *regs)
251 {
252         memset(info, 0, sizeof(struct unwind_frame_info));
253         info->t = t;
254         info->sp = regs->ksp;
255         info->ip = regs->kpc;
256
257         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", (int)t->pid, info->sp, info->ip);
258 }
259
260 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
261 {
262         struct pt_regs *regs = &t->thread.regs;
263         unwind_frame_init(info, t, regs);
264 }
265
266 int unwind_once(struct unwind_frame_info *next_frame)
267 {
268         unwind_frame_regs(next_frame);
269
270         if (next_frame->prev_sp == 0 ||
271             next_frame->prev_ip == 0)
272                 return -1;
273
274         next_frame->sp = next_frame->prev_sp;
275         next_frame->ip = next_frame->prev_ip;
276         next_frame->prev_sp = 0;
277         next_frame->prev_ip = 0;
278
279         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", (int)next_frame->t->pid, next_frame->sp, next_frame->ip);
280
281         return 0;
282 }
283
284 int unwind_to_user(struct unwind_frame_info *info)
285 {
286         int ret;
287         
288         do {
289                 ret = unwind_once(info);
290         } while (!ret && !(info->ip & 3));
291
292         return ret;
293 }
294
295 module_init(unwind_init);