VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[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
16 #include <asm/uaccess.h>
17
18 #include <asm/unwind.h>
19
20 /* #define DEBUG 1 */
21 #ifdef DEBUG
22 #define dbg(x...) printk(x)
23 #else
24 #define dbg(x...)
25 #endif
26
27 extern struct unwind_table_entry __start___unwind[];
28 extern struct unwind_table_entry __stop___unwind[];
29
30 static spinlock_t unwind_lock;
31 /*
32  * the kernel unwind block is not dynamically allocated so that
33  * we can call unwind_init as early in the bootup process as 
34  * possible (before the slab allocator is initialized)
35  */
36 static struct unwind_table kernel_unwind_table;
37 static struct unwind_table *unwind_tables, *unwind_tables_end;
38
39
40 static inline const struct unwind_table_entry *
41 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
42 {
43         const struct unwind_table_entry *e = 0;
44         unsigned long lo, hi, mid;
45
46         for (lo = 0, hi = table->length; lo < hi; )
47         {
48                 mid = (lo + hi) / 2;
49                 e = &table->table[mid];
50                 if (addr < e->region_start)
51                         hi = mid;
52                 else if (addr > e->region_end)
53                         lo = mid + 1;
54                 else
55                         break;
56         }
57
58         return e;
59 }
60
61 static inline const struct unwind_table_entry *
62 find_unwind_entry(unsigned long addr)
63 {
64         struct unwind_table *table = unwind_tables;
65         const struct unwind_table_entry *e = NULL;
66
67         if (addr >= kernel_unwind_table.start && 
68             addr <= kernel_unwind_table.end)
69                 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
70         else
71                 for (; table; table = table->next)
72                 {
73                         if (addr >= table->start && 
74                             addr <= table->end)
75                                 e = find_unwind_entry_in_table(table, addr);
76                         if (e)
77                                 break;
78                 }
79
80         return e;
81 }
82
83 static void
84 unwind_table_init(struct unwind_table *table, const char *name,
85                   unsigned long base_addr, unsigned long gp,
86                   void *table_start, void *table_end)
87 {
88         struct unwind_table_entry *start = table_start;
89         struct unwind_table_entry *end = 
90                 (struct unwind_table_entry *)table_end - 1;
91
92         table->name = name;
93         table->base_addr = base_addr;
94         table->gp = gp;
95         table->start = base_addr + start->region_start;
96         table->end = base_addr + end->region_end;
97         table->table = (struct unwind_table_entry *)table_start;
98         table->length = end - start + 1;
99         table->next = NULL;
100
101         for (; start <= end; start++) {
102                 start->region_start += base_addr;
103                 start->region_end += base_addr;
104         }
105 }
106
107 void *
108 unwind_table_add(const char *name, unsigned long base_addr, 
109                  unsigned long gp,
110                  void *start, void *end)
111 {
112         struct unwind_table *table;
113         unsigned long flags;
114
115         table = kmalloc(sizeof(struct unwind_table), GFP_USER);
116         if (table == NULL)
117                 return 0;
118         unwind_table_init(table, name, base_addr, gp, start, end);
119         spin_lock_irqsave(&unwind_lock, flags);
120         if (unwind_tables)
121         {
122                 unwind_tables_end->next = table;
123                 unwind_tables_end = table;
124         }
125         else
126         {
127                 unwind_tables = unwind_tables_end = table;
128         }
129         spin_unlock_irqrestore(&unwind_lock, flags);
130
131         return table;
132 }
133
134 /* Called from setup_arch to import the kernel unwind info */
135 static int unwind_init(void)
136 {
137         long start, stop;
138         register unsigned long gp __asm__ ("r27");
139
140         start = (long)&__start___unwind[0];
141         stop = (long)&__stop___unwind[0];
142
143         printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", 
144             start, stop,
145             (stop - start) / sizeof(struct unwind_table_entry));
146
147         unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
148                           gp, 
149                           &__start___unwind[0], &__stop___unwind[0]);
150 #if 0
151         {
152                 int i;
153                 for (i = 0; i < 10; i++)
154                 {
155                         printk("region 0x%x-0x%x\n", 
156                                 __start___unwind[i].region_start, 
157                                 __start___unwind[i].region_end);
158                 }
159         }
160 #endif
161         return 0;
162 }
163
164 static void unwind_frame_regs(struct unwind_frame_info *info)
165 {
166         const struct unwind_table_entry *e;
167         unsigned long npc;
168         unsigned int insn;
169         long frame_size = 0;
170         int looking_for_rp, rpoffset = 0;
171
172         e = find_unwind_entry(info->ip);
173         if (!e) {
174                 unsigned long sp;
175                 extern char _stext[], _etext[];
176
177                 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
178
179                 /* Since we are doing the unwinding blind, we don't know if
180                    we are adjusting the stack correctly or extracting the rp
181                    correctly. The rp is checked to see if it belongs to the
182                    kernel text section, if not we assume we don't have a 
183                    correct stack frame and we continue to unwind the stack.
184                    This is not quite correct, and will fail for loadable
185                    modules. */
186                 sp = info->sp & ~63;
187                 do {
188                         info->prev_sp = sp - 64;
189
190                         /* FIXME: what happens if we unwind too far so that 
191                            sp no longer falls in a mapped kernel page? */
192 #ifndef __LP64__
193                         info->prev_ip = *(unsigned long *)(info->prev_sp - 20);
194 #else
195                         info->prev_ip = *(unsigned long *)(info->prev_sp - 16);
196 #endif
197
198                         sp = info->prev_sp;
199                 } while (info->prev_ip < (unsigned long)_stext ||
200                          info->prev_ip > (unsigned long)_etext);
201
202                 dbg("analyzing func @ %lx with no unwind info, setting prev_sp=%lx prev_ip=%lx\n", info->ip, info->prev_sp, info->prev_ip);
203         } else {
204
205                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, Save_RP = %d size = %u\n",
206                                 e->region_start, e->region_end, e->Save_SP, e->Save_RP, e->Total_frame_size);
207
208                 looking_for_rp = e->Save_RP;
209
210                 for (npc = e->region_start; 
211                      (frame_size < (e->Total_frame_size << 3) || looking_for_rp) && 
212                      npc < info->ip; 
213                      npc += 4) {
214
215                         insn = *(unsigned int *)npc;
216
217                         if ((insn & 0xffffc000) == 0x37de0000 ||
218                             (insn & 0xffe00000) == 0x6fc00000) {
219                                 /* ldo X(sp), sp, or stwm X,D(sp) */
220                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
221                                         ((insn & 0x3fff) >> 1);
222                                 dbg("analyzing func @ %lx, insn=%08x @ %lx, frame_size = %ld\n", info->ip, insn, npc, frame_size);
223                         } else if ((insn & 0xffe00008) == 0x7ec00008) {
224                                 /* std,ma X,D(sp) */
225                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
226                                         (((insn >> 4) & 0x3ff) << 3);
227                                 dbg("analyzing func @ %lx, insn=%08x @ %lx, frame_size = %ld\n", info->ip, insn, npc, frame_size);
228                         } else if (insn == 0x6bc23fd9) { 
229                                 /* stw rp,-20(sp) */
230                                 rpoffset = 20;
231                                 looking_for_rp = 0;
232                                 dbg("analyzing func @ %lx, insn=stw rp,-20(sp) @ %lx\n", info->ip, npc);
233                         } else if (insn == 0x0fc212c1) {
234                                 /* std rp,-16(sr0,sp) */
235                                 rpoffset = 16;
236                                 looking_for_rp = 0;
237                                 dbg("analyzing func @ %lx, insn=std rp,-16(sp) @ %lx\n", info->ip, npc);
238                         }
239                 }
240
241                 info->prev_sp = info->sp - frame_size;
242                 if (rpoffset)
243                         info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
244                 info->prev_ip = info->rp;
245                 info->rp = 0;
246
247                 dbg("analyzing func @ %lx, setting prev_sp=%lx prev_ip=%lx\n", info->ip, info->prev_sp, info->prev_ip);
248         }
249 }
250
251 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
252                        unsigned long sp, unsigned long ip, unsigned long rp)
253 {
254         memset(info, 0, sizeof(struct unwind_frame_info));
255         info->t = t;
256         info->sp = sp;
257         info->ip = ip;
258         info->rp = rp;
259
260         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", t ? (int)t->pid : 0, info->sp, info->ip);
261 }
262
263 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
264 {
265         struct pt_regs *regs = &t->thread.regs;
266         unwind_frame_init(info, t, regs->ksp, regs->kpc, 0);
267 }
268
269 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
270 {
271         unwind_frame_init(info, current, regs->gr[30], regs->iaoq[0],
272                           regs->gr[2]);
273 }
274
275 int unwind_once(struct unwind_frame_info *next_frame)
276 {
277         unwind_frame_regs(next_frame);
278
279         if (next_frame->prev_sp == 0 ||
280             next_frame->prev_ip == 0)
281                 return -1;
282
283         next_frame->sp = next_frame->prev_sp;
284         next_frame->ip = next_frame->prev_ip;
285         next_frame->prev_sp = 0;
286         next_frame->prev_ip = 0;
287
288         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", (int)next_frame->t->pid, next_frame->sp, next_frame->ip);
289
290         return 0;
291 }
292
293 int unwind_to_user(struct unwind_frame_info *info)
294 {
295         int ret;
296         
297         do {
298                 ret = unwind_once(info);
299         } while (!ret && !(info->ip & 3));
300
301         return ret;
302 }
303
304 module_init(unwind_init);