1
2
3
4
5
6
7
8
9
10
11
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/sched.h>
15#include <linux/slab.h>
16#include <linux/sort.h>
17
18#include <linux/uaccess.h>
19#include <asm/assembly.h>
20#include <asm/asm-offsets.h>
21#include <asm/ptrace.h>
22
23#include <asm/unwind.h>
24
25
26#ifdef DEBUG
27#define dbg(x...) pr_debug(x)
28#else
29#define dbg(x...)
30#endif
31
32#define KERNEL_START (KERNEL_BINARY_TEXT_START)
33
34extern struct unwind_table_entry __start___unwind[];
35extern struct unwind_table_entry __stop___unwind[];
36
37static DEFINE_SPINLOCK(unwind_lock);
38
39
40
41
42
43static struct unwind_table kernel_unwind_table __ro_after_init;
44static LIST_HEAD(unwind_tables);
45
46static inline const struct unwind_table_entry *
47find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
48{
49 const struct unwind_table_entry *e = NULL;
50 unsigned long lo, hi, mid;
51
52 lo = 0;
53 hi = table->length - 1;
54
55 while (lo <= hi) {
56 mid = (hi - lo) / 2 + lo;
57 e = &table->table[mid];
58 if (addr < e->region_start)
59 hi = mid - 1;
60 else if (addr > e->region_end)
61 lo = mid + 1;
62 else
63 return e;
64 }
65
66 return NULL;
67}
68
69static const struct unwind_table_entry *
70find_unwind_entry(unsigned long addr)
71{
72 struct unwind_table *table;
73 const struct unwind_table_entry *e = NULL;
74
75 if (addr >= kernel_unwind_table.start &&
76 addr <= kernel_unwind_table.end)
77 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
78 else {
79 unsigned long flags;
80
81 spin_lock_irqsave(&unwind_lock, flags);
82 list_for_each_entry(table, &unwind_tables, list) {
83 if (addr >= table->start &&
84 addr <= table->end)
85 e = find_unwind_entry_in_table(table, addr);
86 if (e) {
87
88 list_move(&table->list, &unwind_tables);
89 break;
90 }
91 }
92 spin_unlock_irqrestore(&unwind_lock, flags);
93 }
94
95 return e;
96}
97
98static void
99unwind_table_init(struct unwind_table *table, const char *name,
100 unsigned long base_addr, unsigned long gp,
101 void *table_start, void *table_end)
102{
103 struct unwind_table_entry *start = table_start;
104 struct unwind_table_entry *end =
105 (struct unwind_table_entry *)table_end - 1;
106
107 table->name = name;
108 table->base_addr = base_addr;
109 table->gp = gp;
110 table->start = base_addr + start->region_start;
111 table->end = base_addr + end->region_end;
112 table->table = (struct unwind_table_entry *)table_start;
113 table->length = end - start + 1;
114 INIT_LIST_HEAD(&table->list);
115
116 for (; start <= end; start++) {
117 if (start < end &&
118 start->region_end > (start+1)->region_start) {
119 pr_warn("Out of order unwind entry! %px and %px\n",
120 start, start+1);
121 }
122
123 start->region_start += base_addr;
124 start->region_end += base_addr;
125 }
126}
127
128static int cmp_unwind_table_entry(const void *a, const void *b)
129{
130 return ((const struct unwind_table_entry *)a)->region_start
131 - ((const struct unwind_table_entry *)b)->region_start;
132}
133
134static void
135unwind_table_sort(struct unwind_table_entry *start,
136 struct unwind_table_entry *finish)
137{
138 sort(start, finish - start, sizeof(struct unwind_table_entry),
139 cmp_unwind_table_entry, NULL);
140}
141
142struct unwind_table *
143unwind_table_add(const char *name, unsigned long base_addr,
144 unsigned long gp,
145 void *start, void *end)
146{
147 struct unwind_table *table;
148 unsigned long flags;
149 struct unwind_table_entry *s = (struct unwind_table_entry *)start;
150 struct unwind_table_entry *e = (struct unwind_table_entry *)end;
151
152 unwind_table_sort(s, e);
153
154 table = kmalloc(sizeof(struct unwind_table), GFP_USER);
155 if (table == NULL)
156 return NULL;
157 unwind_table_init(table, name, base_addr, gp, start, end);
158 spin_lock_irqsave(&unwind_lock, flags);
159 list_add_tail(&table->list, &unwind_tables);
160 spin_unlock_irqrestore(&unwind_lock, flags);
161
162 return table;
163}
164
165void unwind_table_remove(struct unwind_table *table)
166{
167 unsigned long flags;
168
169 spin_lock_irqsave(&unwind_lock, flags);
170 list_del(&table->list);
171 spin_unlock_irqrestore(&unwind_lock, flags);
172
173 kfree(table);
174}
175
176
177int __init unwind_init(void)
178{
179 long start, stop;
180 register unsigned long gp __asm__ ("r27");
181
182 start = (long)&__start___unwind[0];
183 stop = (long)&__stop___unwind[0];
184
185 dbg("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
186 start, stop,
187 (stop - start) / sizeof(struct unwind_table_entry));
188
189 unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
190 gp,
191 &__start___unwind[0], &__stop___unwind[0]);
192#if 0
193 {
194 int i;
195 for (i = 0; i < 10; i++)
196 {
197 printk("region 0x%x-0x%x\n",
198 __start___unwind[i].region_start,
199 __start___unwind[i].region_end);
200 }
201 }
202#endif
203 return 0;
204}
205
206static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
207{
208
209
210
211
212
213
214
215 extern void * const handle_interruption;
216 extern void * const ret_from_kernel_thread;
217 extern void * const syscall_exit;
218 extern void * const intr_return;
219 extern void * const _switch_to_ret;
220#ifdef CONFIG_IRQSTACKS
221 extern void * const _call_on_stack;
222#endif
223
224 if (pc == (unsigned long) &handle_interruption) {
225 struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
226 dbg("Unwinding through handle_interruption()\n");
227 info->prev_sp = regs->gr[30];
228 info->prev_ip = regs->iaoq[0];
229 return 1;
230 }
231
232 if (pc == (unsigned long) &ret_from_kernel_thread ||
233 pc == (unsigned long) &syscall_exit) {
234 info->prev_sp = info->prev_ip = 0;
235 return 1;
236 }
237
238 if (pc == (unsigned long) &intr_return) {
239 struct pt_regs *regs;
240
241 dbg("Found intr_return()\n");
242 regs = (struct pt_regs *)(info->sp - PT_SZ_ALGN);
243 info->prev_sp = regs->gr[30];
244 info->prev_ip = regs->iaoq[0];
245 info->rp = regs->gr[2];
246 return 1;
247 }
248
249 if (pc == (unsigned long) &_switch_to_ret) {
250 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
251 info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
252 return 1;
253 }
254
255#ifdef CONFIG_IRQSTACKS
256 if (pc == (unsigned long) &_call_on_stack) {
257 info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ);
258 info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET);
259 return 1;
260 }
261#endif
262
263 return 0;
264}
265
266static void unwind_frame_regs(struct unwind_frame_info *info)
267{
268 const struct unwind_table_entry *e;
269 unsigned long npc;
270 unsigned int insn;
271 long frame_size = 0;
272 int looking_for_rp, rpoffset = 0;
273
274 e = find_unwind_entry(info->ip);
275 if (e == NULL) {
276 unsigned long sp;
277
278 dbg("Cannot find unwind entry for %pS; forced unwinding\n",
279 (void *) info->ip);
280
281
282
283
284
285
286
287
288 sp = info->sp & ~63;
289 do {
290 unsigned long tmp;
291
292 info->prev_sp = sp - 64;
293 info->prev_ip = 0;
294
295
296
297
298 if (info->prev_sp >= (unsigned long) task_thread_info(info->t) &&
299 info->prev_sp < ((unsigned long) task_thread_info(info->t)
300 + THREAD_SZ_ALGN)) {
301 info->prev_sp = 0;
302 break;
303 }
304
305 if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET)))
306 break;
307 info->prev_ip = tmp;
308 sp = info->prev_sp;
309 } while (!kernel_text_address(info->prev_ip));
310
311 info->rp = 0;
312
313 dbg("analyzing func @ %lx with no unwind info, setting "
314 "prev_sp=%lx prev_ip=%lx\n", info->ip,
315 info->prev_sp, info->prev_ip);
316 } else {
317 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
318 "Save_RP = %d, Millicode = %d size = %u\n",
319 e->region_start, e->region_end, e->Save_SP, e->Save_RP,
320 e->Millicode, e->Total_frame_size);
321
322 looking_for_rp = e->Save_RP;
323
324 for (npc = e->region_start;
325 (frame_size < (e->Total_frame_size << 3) ||
326 looking_for_rp) &&
327 npc < info->ip;
328 npc += 4) {
329
330 insn = *(unsigned int *)npc;
331
332 if ((insn & 0xffffc001) == 0x37de0000 ||
333 (insn & 0xffe00001) == 0x6fc00000) {
334
335 frame_size += (insn & 0x3fff) >> 1;
336 dbg("analyzing func @ %lx, insn=%08x @ "
337 "%lx, frame_size = %ld\n", info->ip,
338 insn, npc, frame_size);
339 } else if ((insn & 0xffe00009) == 0x73c00008) {
340
341 frame_size += ((insn >> 4) & 0x3ff) << 3;
342 dbg("analyzing func @ %lx, insn=%08x @ "
343 "%lx, frame_size = %ld\n", info->ip,
344 insn, npc, frame_size);
345 } else if (insn == 0x6bc23fd9) {
346
347 rpoffset = 20;
348 looking_for_rp = 0;
349 dbg("analyzing func @ %lx, insn=stw rp,"
350 "-20(sp) @ %lx\n", info->ip, npc);
351 } else if (insn == 0x0fc212c1) {
352
353 rpoffset = 16;
354 looking_for_rp = 0;
355 dbg("analyzing func @ %lx, insn=std rp,"
356 "-16(sp) @ %lx\n", info->ip, npc);
357 }
358 }
359
360 if (frame_size > e->Total_frame_size << 3)
361 frame_size = e->Total_frame_size << 3;
362
363 if (!unwind_special(info, e->region_start, frame_size)) {
364 info->prev_sp = info->sp - frame_size;
365 if (e->Millicode)
366 info->rp = info->r31;
367 else if (rpoffset)
368 info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
369 info->prev_ip = info->rp;
370 info->rp = 0;
371 }
372
373 dbg("analyzing func @ %lx, setting prev_sp=%lx "
374 "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp,
375 info->prev_ip, npc);
376 }
377}
378
379void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
380 struct pt_regs *regs)
381{
382 memset(info, 0, sizeof(struct unwind_frame_info));
383 info->t = t;
384 info->sp = regs->gr[30];
385 info->ip = regs->iaoq[0];
386 info->rp = regs->gr[2];
387 info->r31 = regs->gr[31];
388
389 dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n",
390 t ? (int)t->pid : -1, info->sp, info->ip);
391}
392
393void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
394{
395 struct pt_regs *r = &t->thread.regs;
396 struct pt_regs *r2;
397
398 r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
399 if (!r2)
400 return;
401 *r2 = *r;
402 r2->gr[30] = r->ksp;
403 r2->iaoq[0] = r->kpc;
404 unwind_frame_init(info, t, r2);
405 kfree(r2);
406}
407
408#define get_parisc_stackpointer() ({ \
409 unsigned long sp; \
410 __asm__("copy %%r30, %0" : "=r"(sp)); \
411 (sp); \
412})
413
414void unwind_frame_init_task(struct unwind_frame_info *info,
415 struct task_struct *task, struct pt_regs *regs)
416{
417 task = task ? task : current;
418
419 if (task == current) {
420 struct pt_regs r;
421
422 if (!regs) {
423 memset(&r, 0, sizeof(r));
424 r.iaoq[0] = _THIS_IP_;
425 r.gr[2] = _RET_IP_;
426 r.gr[30] = get_parisc_stackpointer();
427 regs = &r;
428 }
429 unwind_frame_init(info, task, regs);
430 } else {
431 unwind_frame_init_from_blocked_task(info, task);
432 }
433}
434
435int unwind_once(struct unwind_frame_info *next_frame)
436{
437 unwind_frame_regs(next_frame);
438
439 if (next_frame->prev_sp == 0 ||
440 next_frame->prev_ip == 0)
441 return -1;
442
443 next_frame->sp = next_frame->prev_sp;
444 next_frame->ip = next_frame->prev_ip;
445 next_frame->prev_sp = 0;
446 next_frame->prev_ip = 0;
447
448 dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n",
449 next_frame->t ? (int)next_frame->t->pid : -1,
450 next_frame->sp, next_frame->ip);
451
452 return 0;
453}
454
455int unwind_to_user(struct unwind_frame_info *info)
456{
457 int ret;
458
459 do {
460 ret = unwind_once(info);
461 } while (!ret && !(info->ip & 3));
462
463 return ret;
464}
465
466unsigned long return_address(unsigned int level)
467{
468 struct unwind_frame_info info;
469
470
471 unwind_frame_init_task(&info, current, NULL);
472
473
474 level += 2;
475 do {
476 if (unwind_once(&info) < 0 || info.ip == 0)
477 return 0;
478 if (!kernel_text_address(info.ip))
479 return 0;
480 } while (info.ip && level--);
481
482 return info.ip;
483}
484