summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/unwind.c26
1 files changed, 16 insertions, 10 deletions
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c
index e619ec7856b7..a37ea6c772cd 100644
--- a/arch/arm/kernel/unwind.c
+++ b/arch/arm/kernel/unwind.c
@@ -33,6 +33,8 @@
#include <asm/traps.h>
#include <asm/unwind.h>
+#include "reboot.h"
+
/* Dummy functions to avoid linker complaints */
void __aeabi_unwind_cpp_pr0(void)
{
@@ -52,7 +54,6 @@ EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2);
struct unwind_ctrl_block {
unsigned long vrs[16]; /* virtual register set */
const unsigned long *insn; /* pointer to the current instructions word */
- unsigned long sp_low; /* lowest value of sp allowed */
unsigned long sp_high; /* highest value of sp allowed */
unsigned long *lr_addr; /* address of LR value on the stack */
/*
@@ -262,9 +263,6 @@ static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl,
}
if (!load_sp) {
ctrl->vrs[SP] = (unsigned long)vsp;
- } else {
- ctrl->sp_low = ctrl->vrs[SP];
- ctrl->sp_high = ALIGN(ctrl->sp_low, THREAD_SIZE);
}
return URC_OK;
@@ -323,7 +321,6 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4;
else if ((insn & 0xc0) == 0x40) {
ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
- ctrl->sp_low = ctrl->vrs[SP];
} else if ((insn & 0xf0) == 0x80) {
unsigned long mask;
@@ -341,8 +338,6 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
} else if ((insn & 0xf0) == 0x90 &&
(insn & 0x0d) != 0x0d) {
ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
- ctrl->sp_low = ctrl->vrs[SP];
- ctrl->sp_high = ALIGN(ctrl->sp_low, THREAD_SIZE);
} else if ((insn & 0xf0) == 0xa0) {
ret = unwind_exec_pop_r4_to_rN(ctrl, insn);
if (ret)
@@ -388,10 +383,11 @@ int unwind_frame(struct stackframe *frame)
{
const struct unwind_idx *idx;
struct unwind_ctrl_block ctrl;
+ unsigned long sp_low;
/* store the highest address on the stack to avoid crossing it*/
- ctrl.sp_low = frame->sp;
- ctrl.sp_high = ALIGN(ctrl.sp_low - THREAD_SIZE, THREAD_ALIGN)
+ sp_low = frame->sp;
+ ctrl.sp_high = ALIGN(sp_low - THREAD_SIZE, THREAD_ALIGN)
+ THREAD_SIZE;
pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__,
@@ -452,6 +448,16 @@ int unwind_frame(struct stackframe *frame)
ctrl.check_each_pop = 0;
+ if (prel31_to_addr(&idx->addr_offset) == (u32)&call_with_stack) {
+ /*
+ * call_with_stack() is the only place where we permit SP to
+ * jump from one stack to another, and since we know it is
+ * guaranteed to happen, set up the SP bounds accordingly.
+ */
+ sp_low = frame->fp;
+ ctrl.sp_high = ALIGN(frame->fp, THREAD_SIZE);
+ }
+
while (ctrl.entries > 0) {
int urc;
if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs))
@@ -459,7 +465,7 @@ int unwind_frame(struct stackframe *frame)
urc = unwind_exec_insn(&ctrl);
if (urc < 0)
return urc;
- if (ctrl.vrs[SP] < ctrl.sp_low || ctrl.vrs[SP] > ctrl.sp_high)
+ if (ctrl.vrs[SP] < sp_low || ctrl.vrs[SP] > ctrl.sp_high)
return -URC_FAILURE;
}