用户模式下的中断处理 先来回顾一下中断发生时系统的处理过程。当中断发生时,系统跳转到vector_irq处执行,它获得返回地址,在sp指针(中断模式下的栈,临时性的)所指的地方保存r0、lr和spsr,之后进入SVC模式,并根据中断产生时CPU的模式,以模式的低4位值为索引,来取相应的处理程序的地址,从而进入中断的处理过程。r0中保存中断时中断模式的SP的值。还是在这里补充一点ARM的CPU模式的东西好。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 0x 中断发生时,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指向的地址处的值传给r1到r13,即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,lr,ARM体系结构中用户模式和系统模式共用同一个 // SP和LR 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_info中flags域的值 tst r1, #_TIF_WORK_MASK @ 判断task是否被阻塞 bne work_pending
@ 根据需要进行进程的切换。 no_work_pending: /*
perform architecture specific actions before user return */ // 宏,空的在arch/arm/mach-s // 定义 arch_ret_to_user r1, lr restore_user_regs
fast = 0, offset = 0 ENDPROC(ret_to_user) 宏restore_user_regs在arch/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又可以从被中断的地方继续执行了,而且这个 // 时候所有的寄存器值(r0到r12),包括状态寄存器值(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函数里的中断流程在系统模式下的中断处理里已经说的很多了,这里不再赘述。 |
|