MIPS-Linux Kernel分析 3

MIPS-Linux Kernel分析(3)
 
作者:陈怀临翻译   来源:嵌入式技术网   

  好了,说了一通系统调用,无非是想让大家明白内核中寄存器的保存恢复过程,以及
   为了少做些无用功所做的努力.下面看为什么要save_static_function:为了避免
   s0寄存器的破坏.
     如果我们使用
        sys_rt_sigsuspend()
        {   ..
           save_static;
           ...
        }
    会有什么问题呢,请看,
 
    Nasty degree - 3 days of tracking.
 
    The symptom was pthread cannot be created.  In the end the caller will 
    get a BUS error.
 
    What exactly happened has to do with how registers are saved.  Below
    attached is the beginning part of sys_sigsuspend() function.  It is easy
    to see that s0 is saved into stack frame AFTER its modified.  Next time
    when process returns to userland, the s0 reg will be wrong!
 
    So the bug is either
 
    1) that we need to save s0 register in SAVE_SOME and not save it in
    save_static; or that
 
    2) we fix compiler so that it does not use s0 register in that case (it
            does the same thing for sys_rt_sigsuspend)
 
    I am sure Ralf will have something to say about it.  :-)  In any case, I
    attached a patch for 1) fix.
 
sys_sigsuspend(struct pt_regs regs)
{
        8008e280:   27bdffc0        addiu   $sp,$sp,-64
        8008e284:   afb00030        sw      $s0,48($sp)
                   sigset_t *uset, saveset, newset;
 
                    save_static(®s);
        8008e288:   27b00040        addiu   $s0,$sp,64   /* save_static
                                                            s0已经破坏*/
        8008e28c:   afbf003c        sw      $ra,60($sp)
        8008e290:   afb20038        sw      $s2,56($sp)
        8008e294:   afb10034        sw      $s1,52($sp)
        8008e298:   afa40040        sw      $a0,64($sp)
        8008e29c:   afa50044        sw      $a1,68($sp)
        8008e2a0:   afa60048        sw      $a2,72($sp)
        8008e2a4:   afa7004c        sw      $a3,76($sp)
        8008e2a8:   ae100058        sw      $s0,88($s0)
        8008e2ac:   ae11005c        sw      $s1,92($s0)
 
#ifdef CONFIG_SMP
#  define GET_SAVED_SP                                   \
                mfc0    k0, CP0_CONTEXT;                 \
                lui     k1, %hi(kernelsp);               \
                srl     k0, k0, 23;                      \
               sll     k0, k0, 2;                       \
                addu    k1, k0;                          \
                lw      k1, %lo(kernelsp)(k1);        
 
#else
#  define GET_SAVED_SP                                   \
/*实际上就是k1 = kernelsp, kernelsp保存当前进程的内核栈指针 */
               lui     k1, %hi(kernelsp);               \
               lw      k1, %lo(kernelsp)(k1);           
#endif
 
/*判断当前运行态,设置栈顶sp
  保存寄存器--参数a0-a3:4-7,返回值v0-v1:2-3,25,28,31以及一些控制寄存器,
  */
#define SAVE_SOME                                        \
               .set    push;                            \
               .set    reorder;                         \
               mfc0    k0, CP0_STATUS;                  \
               sll     k0, 3;     /* extract cu0 bit */ \
               .set    noreorder;                       \
               bltz    k0, 8f;                          \
                move   k1, sp;                          \
               .set    reorder;                         \
               /* Called from user mode, new stack. */  \
                GET_SAVED_SP                             \
8:                                                       \
               move    k0, sp;                          \
               subu    sp, k1, PT_SIZE;                 \
               sw      k0, PT_R29(sp);                  \
                sw     $3, PT_R3(sp);                   \
               sw      $0, PT_R0(sp);                  \
               mfc0    v1, CP0_STATUS;                  \
               sw      $2, PT_R2(sp);                   \
               sw      v1, PT_STATUS(sp);               \
               sw      $4, PT_R4(sp);                   \
               mfc0    v1, CP0_CAUSE;                   \
               sw      $5, PT_R5(sp);                   \
               sw      v1, PT_CAUSE(sp);                \
               sw      $6, PT_R6(sp);                   \
               mfc0    v1, CP0_EPC;                     \
               sw      $7, PT_R7(sp);                   \
               sw      v1, PT_EPC(sp);                  \
               sw      $25, PT_R25(sp);                 \
               sw      $28, PT_R28(sp);                 \
               sw      $31, PT_R31(sp);                 \
               ori     $28, sp, 0x1fff;                 \
               xori    $28, 0x1fff;                     \
               .set    pop
 
#define SAVE_ALL                                         \
               SAVE_SOME;                               \
               SAVE_AT;                                 \
               SAVE_TEMP;                               \
               SAVE_STATIC
 
#define RESTORE_AT                                       \
               .set    push;                            \
               .set    noat;                            \
               lw      $1,  PT_R1(sp);                  \
               .set    pop;
 
#define RESTORE_TEMP                                     \
               lw      $24, PT_LO(sp);                  \
               lw      $8, PT_R8(sp);                   \
               lw      $9, PT_R9(sp);                   \
               mtlo    $24;                             \
               lw      $24, PT_HI(sp);                  \
               lw      $10,PT_R10(sp);                  \
               lw      $11, PT_R11(sp);                 \
               mthi    $24;                             \
               lw      $12, PT_R12(sp);                 \
               lw      $13, PT_R13(sp);                 \
               lw      $14, PT_R14(sp);                 \
               lw      $15, PT_R15(sp);                 \
               lw      $24, PT_R24(sp)
 
#define RESTORE_STATIC                                   \
               lw      $16, PT_R16(sp);                 \
               lw      $17, PT_R17(sp);                 \
               lw      $18, PT_R18(sp);                 \
               lw      $19, PT_R19(sp);                 \
               lw      $20, PT_R20(sp);                 \
               lw      $21, PT_R21(sp);                 \
               lw      $22, PT_R22(sp);                 \
               lw      $23, PT_R23(sp);                 \
               lw      $30, PT_R30(sp)
 
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
 
#define RESTORE_SOME                                     \
               .set    push;                            \
               .set    reorder;                         \
               mfc0    t0, CP0_STATUS;                  \
               .set    pop;                             \
               ori     t0, 0x1f;                        \
               xori    t0, 0x1f;                        \
               mtc0    t0, CP0_STATUS;                  \
               li      v1, 0xff00;                      \
               and     t0, v1;                         \
               lw      v0, PT_STATUS(sp);               \
               nor     v1, $0, v1;                     \
               and     v0, v1;                         \
               or      v0, t0;                         \
               mtc0    v0, CP0_STATUS;                  \
               lw      $31, PT_R31(sp);                 \
               lw      $28, PT_R28(sp);                 \
               lw      $25, PT_R25(sp);                 \
               lw      $7,  PT_R7(sp);                  \
               lw      $6,  PT_R6(sp);                  \
               lw      $5,  PT_R5(sp);                  \
               lw      $4,  PT_R4(sp);                  \
               lw      $3,  PT_R3(sp);                  \
               lw      $2,  PT_R2(sp)
 
#define RESTORE_SP_AND_RET                               \
               .set    push;                           \
               .set    noreorder;                      \
               lw      k0, PT_EPC(sp);                  \
               lw      sp,  PT_R29(sp);                 \
               jr      k0;                              \
                rfe;                                  \
                ^^^^^
/* 异常返回时,把控制转移到用户代码和把模式从内核态改为用户态要同时完成
   如果前者先完成,用户态指令有机会以内核态运行导致安全漏洞;
   反之则会由于用户态下不能修改状态而导致异常
   r3000以前使用rfe(restore from exception)指令,这个指令把status寄存器
   状态位修改回异常发生前的状态(利用硬件的一个小堆栈),但不做跳转.我们使用一个
   技巧来完成要求:在一个跳转指令的delay slot中放rte.因为delay slot的指令
   是一定会做的,跳转完成时,status也恢复了.
   MIPS III(r4000)以上的指令集则增加了eret指令来完成整个工作: 它清除
   status寄存器的EXL位并跳转到epc指定的位置.
*/
 
               .set    pop
 
#else
 
#define RESTORE_SOME                                     \
               .set    push;                            \
               .set    reorder;                         \
               mfc0    t0, CP0_STATUS;                  \
               .set    pop;                             \
               ori     t0, 0x1f;                        \
               xori    t0, 0x1f;                        \
               mtc0    t0, CP0_STATUS;                  \
               li      v1, 0xff00;                      \
               and     t0, v1;                         \
               lw      v0, PT_STATUS(sp);               \
               nor     v1, $0, v1;                     \
               and     v0, v1;                         \
               or      v0, t0;                         \
               mtc0    v0, CP0_STATUS;                  \
               lw      v1, PT_EPC(sp);                  \
               mtc0    v1, CP0_EPC;                     \
               lw      $31, PT_R31(sp);                 \
               lw      $28, PT_R28(sp);                 \
               lw      $25, PT_R25(sp);                 \
               lw      $7,  PT_R7(sp);                  \
               lw      $6,  PT_R6(sp);                  \
               lw      $5,  PT_R5(sp);                  \
               lw      $4,  PT_R4(sp);                  \
               lw      $3,  PT_R3(sp);                  \
               lw      $2,  PT_R2(sp)
 
#define RESTORE_SP_AND_RET                               \
               lw      sp,  PT_R29(sp);                 \
               .set    mips3;                          \
               eret;                                  \
               .set    mips0
 
#endif
 
#define RESTORE_SP                                       \
               lw      sp,  PT_R29(sp);                 \
 
#define RESTORE_ALL                                      \
               RESTORE_SOME;                            \
               RESTORE_AT;                              \
               RESTORE_TEMP;                            \
               RESTORE_STATIC;                          \
               RESTORE_SP
 
#define RESTORE_ALL_AND_RET                              \
               RESTORE_SOME;                            \
               RESTORE_AT;                              \
               RESTORE_TEMP;                            \
               RESTORE_STATIC;                          \
               RESTORE_SP_AND_RET
 
 
/*
 * Move to kernel mode and disable interrupts.
 * Set cp0 enable bit as sign that we''re running on the kernel stack
 */
#define CLI                                             \
               mfc0    t0,CP0_STATUS;                  \
               li      t1,ST0_CU0|0x1f;                \
               or      t0,t1;                          \
               xori    t0,0x1f;                        \
               mtc0    t0,CP0_STATUS
 
/*
 * Move to kernel mode and enable interrupts.
 * Set cp0 enable bit as sign that we''re running on the kernel stack
 */
#define STI                                             \
               mfc0    t0,CP0_STATUS;                  \
               li      t1,ST0_CU0|0x1f;                \
               or      t0,t1;                          \
               xori    t0,0x1e;                        \
               mtc0    t0,CP0_STATUS
 
/*
 * Just move to kernel mode and leave interrupts as they are.
 * Set cp0 enable bit as sign that we''re running on the kernel stack
 */
#define KMODE                                           \
               mfc0    t0,CP0_STATUS;                  \
               li      t1,ST0_CU0|0x1e;                \
               or      t0,t1;                          \
               xori    t0,0x1e;                        \
               mtc0    t0,CP0_STATUS
 
#endif /* __ASM_STACKFRAME_H */

投 票

觉得本文不错,投一票   

评 论


验证码: 看不清?换一张