Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/powerpc Fix a problem where the kernel could random...



details:   https://anonhg.NetBSD.org/src/rev/983a9d281a04
branches:  trunk
changeset: 780646:983a9d281a04
user:      matt <matt%NetBSD.org@localhost>
date:      Wed Aug 01 16:19:42 2012 +0000

description:
Fix a problem where the kernel could randomly reset due to a watchdog event.
When an exception happens, the srr0 (exception PC) was being saved in the
normal location of the current callframe.  This was fine except when the
routine was in its prologue after it had saved LR but had not yet updated the
stack pointer or when the routine was in its epilogue after it has restored
the stack pointer but not yet loaded the LR.  In either case this would cause
the LR to be corrupted (either running the routine forever or by branching
to itself forever).  Now we save and restore the contents of that memory
location so the corruption can't happen.

diffstat:

 sys/arch/powerpc/booke/genassym.cf |   3 ++-
 sys/arch/powerpc/booke/trap_subr.S |  26 ++++++++++++++++++--------
 sys/arch/powerpc/include/frame.h   |   3 ++-
 3 files changed, 22 insertions(+), 10 deletions(-)

diffs (113 lines):

diff -r 3ab9c7e3eb4a -r 983a9d281a04 sys/arch/powerpc/booke/genassym.cf
--- a/sys/arch/powerpc/booke/genassym.cf        Wed Aug 01 15:24:22 2012 +0000
+++ b/sys/arch/powerpc/booke/genassym.cf        Wed Aug 01 16:19:42 2012 +0000
@@ -1,4 +1,4 @@
-#      $NetBSD: genassym.cf,v 1.8 2011/06/29 06:06:04 matt Exp $
+#      $NetBSD: genassym.cf,v 1.9 2012/08/01 16:19:43 matt Exp $
 
 #-
 # Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
@@ -61,6 +61,7 @@
 define FRAME_MCAR      offsetof(struct ktrapframe, ktf_tf.tf_mcar)
 define FRAME_SPRG1     offsetof(struct ktrapframe, ktf_tf.tf_sprg1)
 define FRAME_SPEFSCR   offsetof(struct ktrapframe, ktf_tf.tf_spefscr)
+define FRAME_CFRAME_LR offsetof(struct ktrapframe, ktf_cframe_lr)
 
 define CI_SAVELIFO     offsetof(struct cpu_info, ci_savearea[0])
 define CI_PMAP_SEGTAB  offsetof(struct cpu_info, ci_pmap_segtabs[0])
diff -r 3ab9c7e3eb4a -r 983a9d281a04 sys/arch/powerpc/booke/trap_subr.S
--- a/sys/arch/powerpc/booke/trap_subr.S        Wed Aug 01 15:24:22 2012 +0000
+++ b/sys/arch/powerpc/booke/trap_subr.S        Wed Aug 01 16:19:42 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: trap_subr.S,v 1.7 2012/07/09 17:36:55 matt Exp $       */
+/*     $NetBSD: trap_subr.S,v 1.8 2012/08/01 16:19:43 matt Exp $       */
 /*-
  * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -34,7 +34,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-RCSID("$NetBSD: trap_subr.S,v 1.7 2012/07/09 17:36:55 matt Exp $")
+RCSID("$NetBSD: trap_subr.S,v 1.8 2012/08/01 16:19:43 matt Exp $")
 
        .globl  _C_LABEL(sctrapexit), _C_LABEL(trapexit), _C_LABEL(intrcall)
 
@@ -137,14 +137,24 @@
 /*
  * DDB expects to fetch the LR from the previous frame.  But it also
  * expects to be pointing at the instruction after the branch link.  Since
- * we didn't branch, we need to advance it by to fake out DDB.
+ * we didn't branch, we need to advance it by to fake out DDB.  But there's
+ * problem.  If the routine is in either its first or last two instructions
+ * (before or after its adjusted its stack pointer), we could possibly
+ * overwrite stored return address.  So that stored return address needs to
+ * saved and restored.
  */
-#ifdef DDB
+#if defined(DDB)
 #define FRAME_SAVE_SRR0_FOR_DDB                                                \
-       addi    %r30, %r30, 4;          /* ddb thinks its the next insn */ \
-       stw     %r30, FRAMELEN+SZREG(%r1); /* appease ddb stacktrace */
+       lwz     %r29, FRAMELEN+CFRAME_LR(%r1);  /* fetch old return address */\
+       stw     %r29, FRAME_CFRAME_LR(%r1);     /* save it */            \
+       addi    %r30, %r30, 4;                  /* point to s the next insn */ \
+       stw     %r30, FRAMELEN+CFRAME_LR(%r1)   /* appease ddb stacktrace */
+#define FRAME_RESTORE_RETURN_ADDRESS                                   \
+       lwz     %r3, FRAME_CFRAME_LR(%r1);      /* fetch old return address */ \
+       stw     %r3, FRAMELEN+CFRAME_LR(%r1)    /* restore it */
 #else
 #define FRAME_SAVE_SRR0_FOR_DDB
+#define FRAME_RESTORE_RETURN_ADDRESS
 #endif
 
 #ifdef PPC_HAVE_SPE
@@ -175,7 +185,6 @@
        addi    %r1, %r2, USPACE-CALLFRAMELEN;                          \
                                        /* start stack at top of it */  \
 1:                                                                     \
-       stw     %r30, CFRAME_LR(%r1);   /* save in previous callframe */ \
        stwu    %r31, -FRAMELEN(%r1);   /* get space for trapframe */   \
        stw     %r0, FRAME_R0(%r1);     /* save r0 */                   \
        stw     %r31, FRAME_R1(%r1);    /* save (saved) r1 */           \
@@ -203,6 +212,7 @@
        addi    tf, %r1, FRAME_TF       /* get address of trap frame */
 
 #define FRAME_EXC_EXIT(rfi, srr)                                       \
+       FRAME_RESTORE_RETURN_ADDRESS;   /* restore return address */    \
        lmw     %r26, FRAME_LR(%r1);    /* get LR CR XER CTR SRR0/1 */  \
        oris    %r31,%r31,PSL_CE@h;                                     \
        mtspr   SPR_##srr##1, %r31;     /* restore SRR1 */              \
@@ -262,7 +272,6 @@
        mtcr    %r31;                   /* user mode exception? */      \
        mr      %r31, %r1;              /* save SP (SRR1 is safe in CR) */ \
        get_intr_sp;                    /* get kernel stack pointer */  \
-       stw     %r30, CFRAME_LR(%r1);   /* save in .. */                \
        stwu    %r31, -FRAMELEN(%r1);   /* get space for trapframe */   \
        stw     %r0, FRAME_R0(%r1);     /* save r0 */                   \
        stw     %r31, FRAME_R1(%r1);    /* save (saved) r1 */           \
@@ -294,6 +303,7 @@
        addi    %r3, %r1, FRAME_TF      /* only argument is trapframe */
 
 #define FRAME_INTR_XEXIT(rfi, srr)                                     \
+       FRAME_RESTORE_RETURN_ADDRESS;   /* restore return address */    \
        lwz     %r8, FRAME_LR(%r1);     /* get LR */                    \
        lwz     %r9, FRAME_CR(%r1);     /* get CR */                    \
        lwz     %r10, FRAME_XER(%r1);   /* get XER */                   \
diff -r 3ab9c7e3eb4a -r 983a9d281a04 sys/arch/powerpc/include/frame.h
--- a/sys/arch/powerpc/include/frame.h  Wed Aug 01 15:24:22 2012 +0000
+++ b/sys/arch/powerpc/include/frame.h  Wed Aug 01 16:19:42 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: frame.h,v 1.24 2012/02/19 21:06:23 rmind Exp $ */
+/*     $NetBSD: frame.h,v 1.25 2012/08/01 16:19:42 matt Exp $  */
 
 /*
  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
@@ -113,6 +113,7 @@
        register_t ktf_sp;
        register_t ktf_lr;
        struct trapframe ktf_tf;
+       register_t ktf_cframe_lr;       /* for DDB */
 };
 
 #if defined(_KERNEL) || defined(_LKM)



Home | Main Index | Thread Index | Old Index