struct unwind_ctrl_block {
        unsigned long vrs[16];          /* virtual register set */
        const unsigned long *insn;      /* pointer to the current instructions word */
+       unsigned long sp_high;          /* highest value of sp allowed */
+       /*
+        * 1 : check for stack overflow for each register pop.
+        * 0 : save overhead if there is plenty of stack remaining.
+        */
+       int check_each_pop;
        int entries;                    /* number of entries left to interpret */
        int byte;                       /* current byte number in the instructions word */
 };
        return ret;
 }
 
+/* Before poping a register check whether it is feasible or not */
+static int unwind_pop_register(struct unwind_ctrl_block *ctrl,
+                               unsigned long **vsp, unsigned int reg)
+{
+       if (unlikely(ctrl->check_each_pop))
+               if (*vsp >= (unsigned long *)ctrl->sp_high)
+                       return -URC_FAILURE;
+
+       ctrl->vrs[reg] = *(*vsp)++;
+       return URC_OK;
+}
+
+/* Helper functions to execute the instructions */
+static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl,
+                                               unsigned long mask)
+{
+       unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
+       int load_sp, reg = 4;
+
+       load_sp = mask & (1 << (13 - 4));
+       while (mask) {
+               if (mask & 1)
+                       if (unwind_pop_register(ctrl, &vsp, reg))
+                               return -URC_FAILURE;
+               mask >>= 1;
+               reg++;
+       }
+       if (!load_sp)
+               ctrl->vrs[SP] = (unsigned long)vsp;
+
+       return URC_OK;
+}
+
+static int unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block *ctrl,
+                                       unsigned long insn)
+{
+       unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
+       int reg;
+
+       /* pop R4-R[4+bbb] */
+       for (reg = 4; reg <= 4 + (insn & 7); reg++)
+               if (unwind_pop_register(ctrl, &vsp, reg))
+                               return -URC_FAILURE;
+
+       if (insn & 0x80)
+               if (unwind_pop_register(ctrl, &vsp, 14))
+                               return -URC_FAILURE;
+
+       ctrl->vrs[SP] = (unsigned long)vsp;
+
+       return URC_OK;
+}
+
+static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl,
+                                               unsigned long mask)
+{
+       unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
+       int reg = 0;
+
+       /* pop R0-R3 according to mask */
+       while (mask) {
+               if (mask & 1)
+                       if (unwind_pop_register(ctrl, &vsp, reg))
+                               return -URC_FAILURE;
+               mask >>= 1;
+               reg++;
+       }
+       ctrl->vrs[SP] = (unsigned long)vsp;
+
+       return URC_OK;
+}
+
 /*
  * Execute the current unwind instruction.
  */
 static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
 {
        unsigned long insn = unwind_get_byte(ctrl);
+       int ret = URC_OK;
 
        pr_debug("%s: insn = %08lx\n", __func__, insn);
 
                ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
        else if ((insn & 0xf0) == 0x80) {
                unsigned long mask;
-               unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
-               int load_sp, reg = 4;
 
                insn = (insn << 8) | unwind_get_byte(ctrl);
                mask = insn & 0x0fff;
                        return -URC_FAILURE;
                }
 
-               /* pop R4-R15 according to mask */
-               load_sp = mask & (1 << (13 - 4));
-               while (mask) {
-                       if (mask & 1)
-                               ctrl->vrs[reg] = *vsp++;
-                       mask >>= 1;
-                       reg++;
-               }
-               if (!load_sp)
-                       ctrl->vrs[SP] = (unsigned long)vsp;
+               ret = unwind_exec_pop_subset_r4_to_r13(ctrl, mask);
+               if (ret)
+                       goto error;
        } else if ((insn & 0xf0) == 0x90 &&
                   (insn & 0x0d) != 0x0d)
                ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
        else if ((insn & 0xf0) == 0xa0) {
-               unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
-               int reg;
-
-               /* pop R4-R[4+bbb] */
-               for (reg = 4; reg <= 4 + (insn & 7); reg++)
-                       ctrl->vrs[reg] = *vsp++;
-               if (insn & 0x80)
-                       ctrl->vrs[14] = *vsp++;
-               ctrl->vrs[SP] = (unsigned long)vsp;
+               ret = unwind_exec_pop_r4_to_rN(ctrl, insn);
+               if (ret)
+                       goto error;
        } else if (insn == 0xb0) {
                if (ctrl->vrs[PC] == 0)
                        ctrl->vrs[PC] = ctrl->vrs[LR];
                ctrl->entries = 0;
        } else if (insn == 0xb1) {
                unsigned long mask = unwind_get_byte(ctrl);
-               unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
-               int reg = 0;
 
                if (mask == 0 || mask & 0xf0) {
                        pr_warning("unwind: Spare encoding %04lx\n",
                        return -URC_FAILURE;
                }
 
-               /* pop R0-R3 according to mask */
-               while (mask) {
-                       if (mask & 1)
-                               ctrl->vrs[reg] = *vsp++;
-                       mask >>= 1;
-                       reg++;
-               }
-               ctrl->vrs[SP] = (unsigned long)vsp;
+               ret = unwind_exec_pop_subset_r0_to_r3(ctrl, mask);
+               if (ret)
+                       goto error;
        } else if (insn == 0xb2) {
                unsigned long uleb128 = unwind_get_byte(ctrl);
 
        pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__,
                 ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]);
 
-       return URC_OK;
+error:
+       return ret;
 }
 
 /*
  */
 int unwind_frame(struct stackframe *frame)
 {
-       unsigned long high, low;
+       unsigned long low;
        const struct unwind_idx *idx;
        struct unwind_ctrl_block ctrl;
 
-       /* only go to a higher address on the stack */
+       /* store the highest address on the stack to avoid crossing it*/
        low = frame->sp;
-       high = ALIGN(low, THREAD_SIZE);
+       ctrl.sp_high = ALIGN(low, THREAD_SIZE);
 
        pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__,
                 frame->pc, frame->lr, frame->sp);
                return -URC_FAILURE;
        }
 
+       ctrl.check_each_pop = 0;
+
        while (ctrl.entries > 0) {
-               int urc = unwind_exec_insn(&ctrl);
+               int urc;
+               if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs))
+                       ctrl.check_each_pop = 1;
+               urc = unwind_exec_insn(&ctrl);
                if (urc < 0)
                        return urc;
-               if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high)
+               if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= ctrl.sp_high)
                        return -URC_FAILURE;
        }