分享

ARM Linux对中断的处理

 写意人生 2014-06-20

   用户模式下的中断处理

先来回顾一下中断发生时系统的处理过程。当中断发生时,系统跳转到vector_irq处执行,它获得返回地址,在sp指针(中断模式下的栈,临时性的)所指的地方保存r0lrspsr,之后进入SVC模式,并根据中断产生时CPU的模式,以模式的低4位值为索引,来取相应的处理程序的地址,从而进入中断的处理过程。r0中保存中断时中断模式的SP的值。还是在这里补充一点ARMCPU模式的东西好。ARM处理器的最低5位用来指示处理当前所在的模式。各模式对应的模式值如下(在arch/arm/include/asm/ptrace.h中):

#define USR_MODE   0x00000010

#define FIQ_MODE   0x00000011

#define IRQ_MODE   0x00000012

#define SVC_MODE   0x00000013

#define ABT_MODE   0x00000017

#define UND_MODE   0x0000001b

#define SYSTEM_MODE   0x0000001f

中断发生时,CPU处于用户模式下,则当然会调用__irq_usr例程,

   .align 5

__irq_usr:

   usr_entry  // 宏,保存各寄存器,便于返回的时候恢复

  kuser_cmpxchg_check

 

  get_thread_info tsk // 获取保存当前task信息的地址

#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

#ifdef CONFIG_TRACE_IRQFLAGS

  bl trace_hardirqs_on

#endif

 

  mov   why, #0

  b  ret_to_user // 返回

 UNWIND(.fnend      )

ENDPROC(__irq_usr)

先来看usr_entry,在arch/arm/kernel/entry-armv.S中:

   .macro usr_entry

 UNWIND(.fnstart   )

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

   sub   sp, sp, #S_FRAME_SIZE

// stmib,每次传送前地址加四

 ARM( stmib sp, {r1 - r12}  )

 THUMB(  stmia sp, {r0 - r12}  )

// r0指向的地址处的值传给r1r13,即r1=r0,r2=lr,r3=spsr

   ldmia r0, {r1 - r3}

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

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

// r1存放的是实际r0的值,这里就是存储实际的r0

   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}

// 存储用户模式下的sp,lrARM体系结构中用户模式和系统模式共用同一个

// SPLR

 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

 

   asm_trace_hardirqs_off

   .endm

这个宏和svc_entry一样,主要也是保存各个寄存器值到栈上相应的位置。__irq_usr中间的代码和__irq_svc一样,这里就不再赘述了。__irq_usr处理完中断后,调用ret_to_user来返回到用户模式,这个例程在arch/arm/kernel/entry-common.S下定义:

ENTRY(ret_to_user)

ret_slow_syscall:

   disable_irq           @ disable interrupts

   ldr   r1, [tsk, #TI_FLAGS]  @ 获取thread_infoflags域的值

   tst   r1, #_TIF_WORK_MASK  @ 判断task是否被阻塞

   bne   work_pending          @ 根据需要进行进程的切换。

no_work_pending:

   /* perform architecture specific actions before user return */

// 宏,空的在arch/arm/mach-s3c2410/include/mach/entry-macro.S

// 定义

arch_ret_to_user r1, lr 

 

   restore_user_regs fast = 0, offset = 0

ENDPROC(ret_to_user)

 

restore_user_regsarch/arm/kernel/entry-header.S中定义:

   .macro restore_user_regs, fast = 0, offset = 0

// 获取被中断代码处的状态(cpsr)

   ldr   r1, [sp, #\offset + S_PSR]  @ get calling cpsr

   ldr   lr, [sp, #\offset + S_PC]!  @ get pc

    // spsr里保存好被中断代码处的状态(cpsp)

   msr   spsr_cxsf, r1         @ save in spsr_svc

#if defined(CONFIG_CPU_32v6K)

   clrex              @ clear the exclusive monitor

#elif defined (CONFIG_CPU_V6)

   strex r1, r2, [sp]       @ clear the exclusive monitor

#endif

   .if   \fast

   ldmdb sp, {r1 - lr}^        @ get calling r1 - lr

   .else

   ldmdb sp, {r0 - lr}^        @ get calling r0 - lr

   .endif

@栈地址恢复,避免多个中断后溢出

   add   sp, sp, #S_FRAME_SIZE - S_PC

// 返回被中断代码处继续执行,并把spsr赋给cpsr,即恢复被中断处

// 的现场状态。这样CPU又可以从被中断的地方继续执行了,而且这个

// 时候所有的寄存器值(r0r12),包括状态寄存器值(cpsr)都是源

// 码被中断时的值。

   movs  pc, lr         @ return & move spsr_svc into cpsr

   .endm

恢复寄存器,完成中断处理,回到用户模式。

 

顺便看下work_pending,在arch/arm/kernel/entry-common.S中:

work_pending:

    tst r1, #_TIF_NEED_RESCHED  @ 判断是否需要调度进程

    bne work_resched            @ 进程调度

    tst r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME

    beq no_work_pending    @  无需调度,返回

    mov r0, sp            @ 'regs'

    mov r2, why              @ 'syscall'

    bl  do_notify_resume

    b   ret_slow_syscall     @ Check work again

 

work_resched:

    bl  schedule

由该汇编代码可知,如果在用户模式下产生中断的话,在返回的时候,会根据需要进行进程调度,而从代码可知,如果中断发生在管理等内核模式下的话是不会进行进程调度的。

Ok, 中断的流程大体就是这样的,c函数里的中断流程在系统模式下的中断处理里已经说的很多了,这里不再赘述。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多