ARM linux的中断处理过程
这其实就是一个lookup table,根据CPSR.M[3:0]的值进行跳转(参考上一节的代码:and lr, lr, #0x0f)。因此,该lookup table共设定了16个入口,当然只有两项有效,分别对应user mode和svc mode的跳转地址。其他入口的__irq_invalid也是非常关键的,这保证了在其模式下发生了中断,系统可以捕获到这样的错误,为debug提供有用的信息。
.align 5
__irq_usr:
usr_entry---------请参考本章第一节(1)保存用户现场的描述
kuser_cmpxchg_check---和本文描述的内容无关,这些不就介绍了
irq_handler----------核心处理内容,请参考本章第二节的描述
get_thread_info tsk------tsk是r9,指向当前的thread info数据结构
mov why, #0--------why是r8
b ret_to_user_from_irq----中断返回,下一章会详细描述
(1)保存发生中断时候的现场。所谓保存现场其实就是把发生中断那一刻的硬件上下文(各个寄存器)保存在了SVC mode的stack上。
.macro usr_entry
sub sp, sp, #S_FRAME_SIZE--------------A
stmib sp, {r1 - r12} -------------------Bldmia r0, {r3 - r5}--------------------C
add r0, sp, #S_PC-------------------D
mov r6, #-1----orig_r0的值str r3, [sp] ----保存中断那一刻的r0
stmia r0, {r4 - r6}--------------------E
stmdb r0, {sp, lr}^-------------------F
.endm
A:代码执行到这里的时候,ARM处理已经切换到了SVC mode。一旦进入SVC mode,ARM处理器看到的寄存器已经发生变化,这里的sp已经变成了sp_svc了。因此,后续的压栈操作都是压入了发生中断那一刻的进程的(或者内核线程)内核栈(svc mode栈)。具体保存多少个寄存器值?S_FRAME_SIZE已经给出了答案,这个值是18个寄存器。r0~r15再加上CPSR也只有17个而已。先保留这个疑问,我们稍后回答。
B:压栈首先压入了r1~r12,这里为何不处理r0?因为r0在irq mode切到svc mode的时候被污染了,不过,原始的r0被保存的irq mode的stack上了。r13(sp)和r14(lr)需要保存吗,当然需要,稍后再保存。执行到这里,内核栈的布局如下图所示:
stmib中的ib表示increment before,因此,在压入R1的时候,stack pointer会先增加4,重要是预留r0的位置。stmib sp, {r1 - r12}指令中的sp没有“!”的修饰符,表示压栈完成后并不会真正更新stack pointer,因此sp保持原来的值。
C:注意,这里r0指向了irq stack,因此,r3是中断时候的r0值,r4是中断现场的PC值,r5是中断现场的CPSR值。
D:把r0赋值为S_PC的值。根据struct pt_regs的定义(这个数据结构反应了内核栈上的保存的寄存器的排列信息),从低地址到高地址依次为:
ARM_r0
ARM_r1
ARM_r2
ARM_r3
ARM_r4
ARM_r5
ARM_r6
ARM_r7
ARM_r8
ARM_r9
ARM_r10
ARM_fp
ARM_ip
ARM_sp
ARM_lr
ARM_pc<---------add r0, sp, #S_PC指令使得r0指向了这个位置
ARM_cpsr
ARM_ORIG_r0
为什么要给r0赋值?因此kernel不想修改sp的值,保持sp指向栈顶。
E:在内核栈上保存剩余的寄存器的值,根据代码,依次是r0,PC,CPSR和orig r0。执行到这里,内核栈的布局如下图所示:
R0,PC和CPSR来自IRQ mode的stack。实际上这段操作就是从irq stack就中断现场搬移到内核栈上。
F:内核栈上还有两个寄存器没有保持,分别是发生中断时候sp和lr这两个寄存器。这时候,r0指向了保存PC寄存器那个地址(add r0, sp, #S_PC),stmdb r0, {sp, lr}^中的“db”是decrement before,因此,将sp和lr压入stack中的剩余的两个位置。需要注意的是,我们保存的是发生中断那一刻(对于本节,这是当时user mode的sp和lr),指令中的“^”符号表示访问user mode的寄存器。
(2)核心处理
irq_handler的处理有两种配置。一种是配置了CONFIG_MULTI_IRQ_HANDLER。这种情况下,linux kernel允许run time设定irq handler。如果我们需要一个linux kernel image支持多个平台,这是就需要配置这个选项。另外一种是传统的linux的做法,irq_handler实际上就是arch_irq_handler_default,具体代码如下:
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr r1, =handle_arch_irq
mov r0, sp--------设定传递给machine定义的handle_arch_irq的参数
adr lr, BSYM(9997f)----设定返回地址
ldr pc, [r1]
#else
arch_irq_handler_default
#endif
9997:
.endm
对于情况一,machine相关代码需要设定handle_arch_irq函
ARMlinux中断处 相关文章:
- arm linux 下中断流程简要分析中断处理流程(11-09)
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)