Commit 261f72ea authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] parisc: unwind fixes

- fix unwind table search so it works reliably
- add special case handling for ret_from_kernel_thread and _switch_to_ret
- tidy up the code a bit
- Fix sp falling in an unmapped kernel page
- Sparse annotations
Committed-by: default avatarRandolph Chung <tausq@parisc-linux.org>
parent 87d2857b
...@@ -12,8 +12,10 @@ ...@@ -12,8 +12,10 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kallsyms.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/assembly.h>
#include <asm/unwind.h> #include <asm/unwind.h>
...@@ -40,25 +42,27 @@ static struct unwind_table *unwind_tables, *unwind_tables_end; ...@@ -40,25 +42,27 @@ static struct unwind_table *unwind_tables, *unwind_tables_end;
static inline const struct unwind_table_entry * static inline const struct unwind_table_entry *
find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr) find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
{ {
const struct unwind_table_entry *e = 0; const struct unwind_table_entry *e = NULL;
unsigned long lo, hi, mid; unsigned long lo, hi, mid;
for (lo = 0, hi = table->length; lo < hi; ) lo = 0;
{ hi = table->length - 1;
mid = (lo + hi) / 2;
while (lo <= hi) {
mid = (hi - lo) / 2 + lo;
e = &table->table[mid]; e = &table->table[mid];
if (addr < e->region_start) if (addr < e->region_start)
hi = mid; hi = mid - 1;
else if (addr > e->region_end) else if (addr > e->region_end)
lo = mid + 1; lo = mid + 1;
else else
break; return e;
} }
return e; return NULL;
} }
static inline const struct unwind_table_entry * static const struct unwind_table_entry *
find_unwind_entry(unsigned long addr) find_unwind_entry(unsigned long addr)
{ {
struct unwind_table *table = unwind_tables; struct unwind_table *table = unwind_tables;
...@@ -68,8 +72,7 @@ find_unwind_entry(unsigned long addr) ...@@ -68,8 +72,7 @@ find_unwind_entry(unsigned long addr)
addr <= kernel_unwind_table.end) addr <= kernel_unwind_table.end)
e = find_unwind_entry_in_table(&kernel_unwind_table, addr); e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
else else
for (; table; table = table->next) for (; table; table = table->next) {
{
if (addr >= table->start && if (addr >= table->start &&
addr <= table->end) addr <= table->end)
e = find_unwind_entry_in_table(table, addr); e = find_unwind_entry_in_table(table, addr);
...@@ -99,6 +102,11 @@ unwind_table_init(struct unwind_table *table, const char *name, ...@@ -99,6 +102,11 @@ unwind_table_init(struct unwind_table *table, const char *name,
table->next = NULL; table->next = NULL;
for (; start <= end; start++) { for (; start <= end; start++) {
if (start < end &&
start->region_end > (start+1)->region_start) {
printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1);
}
start->region_start += base_addr; start->region_start += base_addr;
start->region_end += base_addr; start->region_end += base_addr;
} }
...@@ -114,7 +122,7 @@ unwind_table_add(const char *name, unsigned long base_addr, ...@@ -114,7 +122,7 @@ unwind_table_add(const char *name, unsigned long base_addr,
table = kmalloc(sizeof(struct unwind_table), GFP_USER); table = kmalloc(sizeof(struct unwind_table), GFP_USER);
if (table == NULL) if (table == NULL)
return 0; return NULL;
unwind_table_init(table, name, base_addr, gp, start, end); unwind_table_init(table, name, base_addr, gp, start, end);
spin_lock_irqsave(&unwind_lock, flags); spin_lock_irqsave(&unwind_lock, flags);
if (unwind_tables) if (unwind_tables)
...@@ -170,12 +178,40 @@ static void unwind_frame_regs(struct unwind_frame_info *info) ...@@ -170,12 +178,40 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
int looking_for_rp, rpoffset = 0; int looking_for_rp, rpoffset = 0;
e = find_unwind_entry(info->ip); e = find_unwind_entry(info->ip);
if (!e) { if (e == NULL) {
unsigned long sp; unsigned long sp;
extern char _stext[], _etext[]; extern char _stext[], _etext[];
dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip); dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
#ifdef CONFIG_KALLSYMS
/* Handle some frequent special cases.... */
{
char symname[KSYM_NAME_LEN+1];
char *modname;
unsigned long symsize, offset;
kallsyms_lookup(info->ip, &symsize, &offset,
&modname, symname);
dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
if (strcmp(symname, "_switch_to_ret") == 0) {
info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
dbg("_switch_to_ret @ %lx - setting "
"prev_sp=%lx prev_ip=%lx\n",
info->ip, info->prev_sp,
info->prev_ip);
return;
} else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
strcmp(symname, "syscall_exit") == 0) {
info->prev_ip = info->prev_sp = 0;
return;
}
}
#endif
/* Since we are doing the unwinding blind, we don't know if /* Since we are doing the unwinding blind, we don't know if
we are adjusting the stack correctly or extracting the rp we are adjusting the stack correctly or extracting the rp
correctly. The rp is checked to see if it belongs to the correctly. The rp is checked to see if it belongs to the
...@@ -185,30 +221,33 @@ static void unwind_frame_regs(struct unwind_frame_info *info) ...@@ -185,30 +221,33 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
modules. */ modules. */
sp = info->sp & ~63; sp = info->sp & ~63;
do { do {
info->prev_sp = sp - 64; unsigned long tmp;
/* FIXME: what happens if we unwind too far so that
sp no longer falls in a mapped kernel page? */
#ifndef __LP64__
info->prev_ip = *(unsigned long *)(info->prev_sp - 20);
#else
info->prev_ip = *(unsigned long *)(info->prev_sp - 16);
#endif
info->prev_sp = sp - 64;
info->prev_ip = 0;
if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET)))
break;
info->prev_ip = tmp;
sp = info->prev_sp; sp = info->prev_sp;
} while (info->prev_ip < (unsigned long)_stext || } while (info->prev_ip < (unsigned long)_stext ||
info->prev_ip > (unsigned long)_etext); info->prev_ip > (unsigned long)_etext);
dbg("analyzing func @ %lx with no unwind info, setting prev_sp=%lx prev_ip=%lx\n", info->ip, info->prev_sp, info->prev_ip); info->rp = 0;
} else {
dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, Save_RP = %d size = %u\n", dbg("analyzing func @ %lx with no unwind info, setting "
e->region_start, e->region_end, e->Save_SP, e->Save_RP, e->Total_frame_size); "prev_sp=%lx prev_ip=%lx\n", info->ip,
info->prev_sp, info->prev_ip);
} else {
dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
"Save_RP = %d size = %u\n", e->region_start,
e->region_end, e->Save_SP, e->Save_RP,
e->Total_frame_size);
looking_for_rp = e->Save_RP; looking_for_rp = e->Save_RP;
for (npc = e->region_start; for (npc = e->region_start;
(frame_size < (e->Total_frame_size << 3) || looking_for_rp) && (frame_size < (e->Total_frame_size << 3) ||
looking_for_rp) &&
npc < info->ip; npc < info->ip;
npc += 4) { npc += 4) {
...@@ -219,22 +258,28 @@ static void unwind_frame_regs(struct unwind_frame_info *info) ...@@ -219,22 +258,28 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
/* ldo X(sp), sp, or stwm X,D(sp) */ /* ldo X(sp), sp, or stwm X,D(sp) */
frame_size += (insn & 0x1 ? -1 << 13 : 0) | frame_size += (insn & 0x1 ? -1 << 13 : 0) |
((insn & 0x3fff) >> 1); ((insn & 0x3fff) >> 1);
dbg("analyzing func @ %lx, insn=%08x @ %lx, frame_size = %ld\n", info->ip, insn, npc, frame_size); dbg("analyzing func @ %lx, insn=%08x @ "
} else if ((insn & 0xffe00008) == 0x7ec00008) { "%lx, frame_size = %ld\n", info->ip,
insn, npc, frame_size);
} else if ((insn & 0xffe00008) == 0x73c00008) {
/* std,ma X,D(sp) */ /* std,ma X,D(sp) */
frame_size += (insn & 0x1 ? -1 << 13 : 0) | frame_size += (insn & 0x1 ? -1 << 13 : 0) |
(((insn >> 4) & 0x3ff) << 3); (((insn >> 4) & 0x3ff) << 3);
dbg("analyzing func @ %lx, insn=%08x @ %lx, frame_size = %ld\n", info->ip, insn, npc, frame_size); dbg("analyzing func @ %lx, insn=%08x @ "
"%lx, frame_size = %ld\n", info->ip,
insn, npc, frame_size);
} else if (insn == 0x6bc23fd9) { } else if (insn == 0x6bc23fd9) {
/* stw rp,-20(sp) */ /* stw rp,-20(sp) */
rpoffset = 20; rpoffset = 20;
looking_for_rp = 0; looking_for_rp = 0;
dbg("analyzing func @ %lx, insn=stw rp,-20(sp) @ %lx\n", info->ip, npc); dbg("analyzing func @ %lx, insn=stw rp,"
"-20(sp) @ %lx\n", info->ip, npc);
} else if (insn == 0x0fc212c1) { } else if (insn == 0x0fc212c1) {
/* std rp,-16(sr0,sp) */ /* std rp,-16(sr0,sp) */
rpoffset = 16; rpoffset = 16;
looking_for_rp = 0; looking_for_rp = 0;
dbg("analyzing func @ %lx, insn=std rp,-16(sp) @ %lx\n", info->ip, npc); dbg("analyzing func @ %lx, insn=std rp,"
"-16(sp) @ %lx\n", info->ip, npc);
} }
} }
...@@ -244,7 +289,9 @@ static void unwind_frame_regs(struct unwind_frame_info *info) ...@@ -244,7 +289,9 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
info->prev_ip = info->rp; info->prev_ip = info->rp;
info->rp = 0; info->rp = 0;
dbg("analyzing func @ %lx, setting prev_sp=%lx prev_ip=%lx\n", info->ip, info->prev_sp, info->prev_ip); dbg("analyzing func @ %lx, setting prev_sp=%lx "
"prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp,
info->prev_ip, npc);
} }
} }
...@@ -257,7 +304,8 @@ void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, ...@@ -257,7 +304,8 @@ void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
info->ip = ip; info->ip = ip;
info->rp = rp; info->rp = rp;
dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", t ? (int)t->pid : 0, info->sp, info->ip); dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n",
t ? (int)t->pid : -1, info->sp, info->ip);
} }
void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t) void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
...@@ -285,7 +333,9 @@ int unwind_once(struct unwind_frame_info *next_frame) ...@@ -285,7 +333,9 @@ int unwind_once(struct unwind_frame_info *next_frame)
next_frame->prev_sp = 0; next_frame->prev_sp = 0;
next_frame->prev_ip = 0; next_frame->prev_ip = 0;
dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", (int)next_frame->t->pid, next_frame->sp, next_frame->ip); dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n",
next_frame->t ? (int)next_frame->t->pid : -1,
next_frame->sp, next_frame->ip);
return 0; return 0;
} }
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define STREGM std,ma #define STREGM std,ma
#define RP_OFFSET 16 #define RP_OFFSET 16
#define FRAME_SIZE 128 #define FRAME_SIZE 128
#define CALLEE_SAVE_FRAME_SIZE 144
#else #else
#define LDREG ldw #define LDREG ldw
#define STREG stw #define STREG stw
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
#define STREGM stwm #define STREGM stwm
#define RP_OFFSET 20 #define RP_OFFSET 20
#define FRAME_SIZE 64 #define FRAME_SIZE 64
#define CALLEE_SAVE_FRAME_SIZE 128
#endif #endif
#ifdef CONFIG_PA20 #ifdef CONFIG_PA20
...@@ -292,7 +294,7 @@ ...@@ -292,7 +294,7 @@
#ifdef __LP64__ #ifdef __LP64__
.macro callee_save .macro callee_save
std,ma %r3, 144(%r30) std,ma %r3, CALLEE_SAVE_FRAME_SIZE(%r30)
mfctl %cr27, %r3 mfctl %cr27, %r3
std %r4, -136(%r30) std %r4, -136(%r30)
std %r5, -128(%r30) std %r5, -128(%r30)
...@@ -330,13 +332,13 @@ ...@@ -330,13 +332,13 @@
ldd -128(%r30), %r5 ldd -128(%r30), %r5
ldd -136(%r30), %r4 ldd -136(%r30), %r4
mtctl %r3, %cr27 mtctl %r3, %cr27
ldd,mb -144(%r30), %r3 ldd,mb -CALLEE_SAVE_FRAME_SIZE(%r30), %r3
.endm .endm
#else /* ! __LP64__ */ #else /* ! __LP64__ */
.macro callee_save .macro callee_save
stw,ma %r3, 128(%r30) stw,ma %r3, CALLEE_SAVE_FRAME_SIZE(%r30)
mfctl %cr27, %r3 mfctl %cr27, %r3
stw %r4, -124(%r30) stw %r4, -124(%r30)
stw %r5, -120(%r30) stw %r5, -120(%r30)
...@@ -374,7 +376,7 @@ ...@@ -374,7 +376,7 @@
ldw -120(%r30), %r5 ldw -120(%r30), %r5
ldw -124(%r30), %r4 ldw -124(%r30), %r4
mtctl %r3, %cr27 mtctl %r3, %cr27
ldw,mb -128(%r30), %r3 ldw,mb -CALLEE_SAVE_FRAME_SIZE(%r30), %r3
.endm .endm
#endif /* ! __LP64__ */ #endif /* ! __LP64__ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment