Linux2.6.37内核异常跳转流程

1、  trap_init函数(在arch/arm/kernel/traps.c中)将异常向量表复制到0xffff0000处。

ARM架构的CPU异常向量基地址可以是0x00000000,也可以是0xffff0000Linux内核使用后者。所以需要将异常向量表拷贝至0xffff0000处。

void  __init trap_init(void)

{

unsigned long vectors = CONFIG_VECTORS_BASE;

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

       …

       }

       vectors等于CONFIG_VECTORS_BASECONFIG_VECTORS_BASE在内核.config配置文件中可以查到CONFIG_VECTORS_BASE=0xffff0000

       __vectors_startlinux/arch/arm/kernel/entry-armv.S文件中,其为异常向量表,内容如下:

.globl      __vectors_start

__vectors_start:

      ARM(     swi  SYS_ERROR0       )

      THUMB( svc  #0           )

      THUMB( nop                )

       W(b)       vector_und + stubs_offset

       W(ldr)     pc, .LCvswi + stubs_offset

       W(b)       vector_pabt + stubs_offset

       W(b)       vector_dabt + stubs_offset

       W(b)       vector_addrexcptn + stubs_offset

       W(b)       vector_irq + stubs_offset

       W(b)       vector_fiq + stubs_offset

2、  当异常发生时,跳转到相应的函数入口。以普通中断为例,当发生普通中断是PC指针跳转到vector_irq + stubs_offset处。vector_irq为一个宏定义(在linux/arch/arm/kernel/entry-armv.S

vector_stub     irq, IRQ_MODE, 4               展开如下:

 

.macro     vector_stub, name, mode, correction=0

       .align       5

 

vector_ irq:

              sub  lr, lr, #4

 

              @

              @ Save r0, lr_<exception> (parent PC) and spsr_<exception>

              @ (parent CPSR)

              @

 

              stmia       sp, {r0, lr}            @ save r0, lr

              mrs  lr, spsr

              str   lr, [sp, #8]             @ save spsr

 

              @

              @ Prepare for SVC32 mode.  IRQs remain disabled.

              @

 

              mrs  r0, cpsr

              eor   r0, r0, #( IRQ_MODE ^ SVC_MODE | PSR_ISETSTATE)

              msr  spsr_cxsf, r0

 

              @

              @ the branch table must immediately follow this code

              @

 

              and  lr, lr, #0x0f

             THUMB( adr   r0, 1f                    )

THUMB( ldr   lr, [r0, lr, lsl #2]      )

              mov r0, sp

             ARM(     ldr   lr, [pc, lr, lsl #2]     )

              movs      pc, lr                     @ branch to handler in SVC mode

ENDPROC(vector_ irq)

 

完成以上操作后,跳转到下面的跳转表中,

.long       __irq_usr               @  0  (USR_26 / USR_32)

       .long       __irq_invalid                 @  1  (FIQ_26 / FIQ_32)

       .long       __irq_invalid                 @  2  (IRQ_26 / IRQ_32)

       .long       __irq_svc                     @  3  (SVC_26 / SVC_32)

       .long       __irq_invalid                 @  4

       .long       __irq_invalid                 @  5

       .long       __irq_invalid                 @  6

       .long       __irq_invalid                 @  7

       .long       __irq_invalid                 @  8

       .long       __irq_invalid                 @  9

       .long       __irq_invalid                 @  a

       .long       __irq_invalid                 @  b

       .long       __irq_invalid                 @  c

       .long       __irq_invalid                 @  d

       .long       __irq_invalid                 @  e

       .long       __irq_invalid                 @  f

例如:在usr模式下发生irq中断,则跳转到__irq_usr处。

.long       __irq_usr               @  0  (USR_26 / USR_32)

 

3__irq_usr(在linux/arch/arm/kernel/entry-armv.S)的定义如下:

       __irq_usr:

              usr_entry

              kuser_cmpxchg_check

 

              get_thread_info tsk

#ifdef CONFIG_PREEMPT

                     ldr   r8, [tsk, #TI_PREEMPT]             @ get preempt count

                     add  r7, r8, #1               @ increment it

                     str   r7, [tsk, #TI_PREEMPT]

#endif

              irq_handler

#ifdef CONFIG_PREEMPT

                     ldr   r0, [tsk, #TI_PREEMPT]

                     str   r8, [tsk, #TI_PREEMPT]

                     teq   r0, r7

ARM(     strne       r0, [r0, -r0]    )

                    THUMB( movne     r0, #0             )

                    THUMB( strne       r0, [r0]    )

#endif

 

              mov why, #0

              b     ret_to_user

             UNWIND(.fnend           )

ENDPROC(__irq_usr)

 

1)其中usr_entry为宏定义,保存相关寄存器,内容如下:

.macro    usr_entry

UNWIND(.fnstart  )

UNWIND(.cantunwind  )      @ don't unwind the user space

sub  sp, sp, #S_FRAME_SIZE

ARM(     stmib      sp, {r1 - r12} )

THUMB( stmia       sp, {r0 - r12} )

 

ldmia       r0, {r1 - r3}

       add  r0, sp, #S_PC        @ here for interlock avoidance

       mov r4, #-1                  @  ""  ""     ""        ""

 

       str   r1, [sp]          @ save the "real" r0 copied

                                   @ from the exception stack

       @

       @ We are now ready to fill in the remaining blanks on the stack:

       @

       @  r2 - lr_<exception>, already fixed up for correct return/restart

       @  r3 - spsr_<exception>

       @  r4 - orig_r0 (see pt_regs definition in ptrace.h)

       @

       @ Also, separately save sp_usr and lr_usr

       @

       stmia       r0, {r2 - r4}

      ARM(     stmdb     r0, {sp, lr}^                  )

      THUMB( store_user_sp_lr r0, r1, S_SP - S_PC   )

 

       @

       @ Enable the alignment trap while in kernel mode

       @

       alignment_trap r0

 

       @

       @ Clear FP to mark the first stack frame

       @

       zero_fp

       .endm

2irq_handler也是一个宏定义,内容如下:

/*

 * Interrupt handling.  Preserves r7, r8, r9

 */

       .macro    irq_handler

       get_irqnr_preamble r5, lr

1:     get_irqnr_and_base r0, r6, r5, lr

       movne     r1, sp

       @

       @ routine called with r0 = irq number, r1 = struct pt_regs *

       @

       adrne      lr, BSYM(1b)

       bne  asm_do_IRQ

 

#ifdef CONFIG_SMP

       /*

        * this macro assumes that irqstat (r6) and base (r5) are

        * preserved from get_irqnr_and_base above

        */

 

       ALT_SMP(test_for_ipi r0, r6, r5, lr)

       ALT_UP_B(9997f)

       movne     r0, sp

       adrne      lr, BSYM(1b)

       bne  do_IPI

 

#ifdef CONFIG_LOCAL_TIMERS

       test_for_ltirq r0, r6, r5, lr

       movne     r0, sp

       adrne      lr, BSYM(1b)

       bne  do_local_timer

#endif

9997:

#endif

       .endm

最终跳转到asm_do_IRQ中断处理函数。此函数在irq.c文件中,内容如下:

 

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

       struct pt_regs *old_regs = set_irq_regs(regs);

 

       irq_enter();

 

       /*

        * Some hardware gives randomly wrong interrupts.  Rather

        * than crashing, do something sensible.

        */

       if (unlikely(irq >= NR_IRQS)) {

              if (printk_ratelimit())

                     printk(KERN_WARNING "Bad IRQ%u\n", irq);

              ack_bad_irq(irq);

       } else {

              generic_handle_irq(irq);

       }

 

       /* AT91 specific workaround */

       irq_finish(irq);

 

       irq_exit();

       set_irq_regs(old_regs);

}

至此中断完成跳转到处理函数的流程。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值