diff options
Diffstat (limited to 'arch/parisc/kernel/unwind.c')
| -rw-r--r-- | arch/parisc/kernel/unwind.c | 50 |
1 files changed, 36 insertions, 14 deletions
diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c index a36799e85693..d58eac1a8288 100644 --- a/arch/parisc/kernel/unwind.c +++ b/arch/parisc/kernel/unwind.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
| 14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
| 15 | #include <linux/kallsyms.h> | 15 | #include <linux/kallsyms.h> |
| 16 | #include <linux/sort.h> | ||
| 16 | 17 | ||
| 17 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
| 18 | #include <asm/assembly.h> | 19 | #include <asm/assembly.h> |
| @@ -115,24 +116,18 @@ unwind_table_init(struct unwind_table *table, const char *name, | |||
| 115 | } | 116 | } |
| 116 | } | 117 | } |
| 117 | 118 | ||
| 119 | static int cmp_unwind_table_entry(const void *a, const void *b) | ||
| 120 | { | ||
| 121 | return ((const struct unwind_table_entry *)a)->region_start | ||
| 122 | - ((const struct unwind_table_entry *)b)->region_start; | ||
| 123 | } | ||
| 124 | |||
| 118 | static void | 125 | static void |
| 119 | unwind_table_sort(struct unwind_table_entry *start, | 126 | unwind_table_sort(struct unwind_table_entry *start, |
| 120 | struct unwind_table_entry *finish) | 127 | struct unwind_table_entry *finish) |
| 121 | { | 128 | { |
| 122 | struct unwind_table_entry el, *p, *q; | 129 | sort(start, finish - start, sizeof(struct unwind_table_entry), |
| 123 | 130 | cmp_unwind_table_entry, NULL); | |
| 124 | for (p = start + 1; p < finish; ++p) { | ||
| 125 | if (p[0].region_start < p[-1].region_start) { | ||
| 126 | el = *p; | ||
| 127 | q = p; | ||
| 128 | do { | ||
| 129 | q[0] = q[-1]; | ||
| 130 | --q; | ||
| 131 | } while (q > start && | ||
| 132 | el.region_start < q[-1].region_start); | ||
| 133 | *q = el; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | 131 | } |
| 137 | 132 | ||
| 138 | struct unwind_table * | 133 | struct unwind_table * |
| @@ -417,3 +412,30 @@ int unwind_to_user(struct unwind_frame_info *info) | |||
| 417 | 412 | ||
| 418 | return ret; | 413 | return ret; |
| 419 | } | 414 | } |
| 415 | |||
| 416 | unsigned long return_address(unsigned int level) | ||
| 417 | { | ||
| 418 | struct unwind_frame_info info; | ||
| 419 | struct pt_regs r; | ||
| 420 | unsigned long sp; | ||
| 421 | |||
| 422 | /* initialize unwind info */ | ||
| 423 | asm volatile ("copy %%r30, %0" : "=r"(sp)); | ||
| 424 | memset(&r, 0, sizeof(struct pt_regs)); | ||
| 425 | r.iaoq[0] = (unsigned long) current_text_addr(); | ||
| 426 | r.gr[2] = (unsigned long) __builtin_return_address(0); | ||
| 427 | r.gr[30] = sp; | ||
| 428 | unwind_frame_init(&info, current, &r); | ||
| 429 | |||
| 430 | /* unwind stack */ | ||
| 431 | ++level; | ||
| 432 | do { | ||
| 433 | if (unwind_once(&info) < 0 || info.ip == 0) | ||
| 434 | return 0; | ||
| 435 | if (!__kernel_text_address(info.ip)) { | ||
| 436 | return 0; | ||
| 437 | } | ||
| 438 | } while (info.ip && level--); | ||
| 439 | |||
| 440 | return info.ip; | ||
| 441 | } | ||
