ARM linux的中断处理过程
拷贝vector table,拷贝stub function
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start);
kuser_init(vectors_base); ----copy kuser helper function
flush_icache_range(vectors, vectors + PAGE_SIZE * 2);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
一旦涉及代码的拷贝,我们就需要关心其编译连接时地址(link-time address)和运行时地址(run-time address)。在kernel完成链接后,__vectors_start有了其link-time address,如果link-time address和run-time address一致,那么这段代码运行时毫无压力。但是,目前对于vector table而言,其被copy到其他的地址上(对于High vector,这是地址就是0xffff00000),也就是说,link-time address和run-time address不一样了,如果仍然想要这些代码可以正确运行,那么需要这些代码是位置无关的代码。对于vector table而言,必须要位置无关。B这个branch instruction本身就是位置无关的,它可以跳转到一个当前位置的offset。不过并非所有的vector都是使用了branch instruction,对于软中断,其vector地址上指令是“W(ldr) pc, __vectors_start + 0x1000 ”,这条指令被编译器编译成ldr pc, [pc, #4080],这种情况下,该指令也是位置无关的,但是有个限制,offset必须在4K的范围内,这也是为何存在stub section的原因了。
4、中断控制器的初始化
具体可以参考GIC代码分析。
三、ARM HW对中断事件的处理
当一切准备好之后,一旦打开处理器的全局中断就可以处理来自外设的各种中断事件了。
当外设(SOC内部或者外部都可以)检测到了中断事件,就会通过interrupt requestion line上的电平或者边沿(上升沿或者下降沿或者both)通知到该外设连接到的那个中断控制器,而中断控制器就会在多个处理器中选择一个,并把该中断通过IRQ(或者FIQ,本文不讨论FIQ的情况)分发给process。ARM处理器感知到了中断事件后,会进行下面一系列的动作:
1、修改CPSR(Current Program Status Register)寄存器中的M[4:0]。M[4:0]表示了ARM处理器当前处于的模式( processor modes)。ARM定义的mode包括:
处理器模式 | 缩写 | 对应的M[4:0]编码 | Privilege level |
User | usr | 10000 | PL0 |
FIQ | fiq | 10001 | PL1 |
IRQ | irq | 10010 | PL1 |
Supervisor | svc | 10011 | PL1 |
Monitor | mon | 10110 | PL1 |
Abort | abt | 10111 | PL1 |
Hyp | hyp | 11010 | PL2 |
Undefined | und | 11011 | PL1 |
System | sys | 11111 | PL1 |
一旦设定了CPSR.M,ARM处理器就会将processor mode切换到IRQ mode。
2、保存发生中断那一点的CPSR值(step 1之前的状态)和PC值
ARM处理器支持9种processor mode,每种mode看到的ARM core register(R0~R15,共计16个)都是不同的。每种mode都是从一个包括所有的Banked ARM core register中选取。全部Banked ARM core register包括:
Usr | System | Hyp | Supervisor | abort | undefined | Monitor | IRQ | FIQ |
R0_usr | ||||||||
R1_usr | ||||||||
R2_usr | ||||||||
R3_usr | ||||||||
R4_usr | ||||||||
R5_usr | ||||||||
R6_usr | ||||||||
R7_usr | ||||||||
R8_usr | R8_fiq | |||||||
R9_usr | R9_fiq | |||||||
R10_usr | R10_fiq | |||||||
R11_usr | R11_fiq | |||||||
R12_usr | R12_fiq | |||||||
SP_usr | SP_hyp | SP_svc | SP_abt | SP_und | SP_mon | SP_irq | SP_fiq | |
LR_usr | LR_svc | LR_abt | LR_und | LR_mon | LR_irq | LR_fiq | ||
PC | ||||||||
CPSR | ||||||||
SPSR_hyp | SPSR_svc | SPSR_abt | SPSR_und | SPSR_mon | SPSR_irq | SPSR_fiq | ||
ELR_hyp |
在IRQ mode下,CPU看到的R0~R12寄存器、PC以及CPSR是和usr mode(userspace)或者svc mode(kernel space)是一样的。不同的是IRQ mode下,有自己的R13(SP,stack pointer)、R14(LR,link register)和SPSR(Saved Program Status Register)。
CPSR是共用的,虽然中断可能发生在usr mode(用户空间),也可能是svc mode(内核空间),不过这些信息都是体现在CPSR寄存器中。硬件会将发生中断那一刻的CPSR保存在SPSR寄存器中(由于不同的mode下有不同的SPSR寄存器,因此更准确的说应该是SPSR-irq,也就是IRQ mode中的SPSR寄存器)。
PC也是共用的,由于后续PC会被修改为irq exception vector,因此有必要保存PC值。当然,与其说保存PC值,不如说是保存返回执行的地址。对于IRQ而言,我们期望返回地址是发生中断那一点执行指令的下一条指令。具体的返回地址保存在lr寄存器中(注意:这个lr寄存器是IRQ mode的lr寄存器,可以表示为lr_irq):
(1)对于thumb state,lr_irq = PC
(2)对于ARM state,lr_irq = PC - 4
为何要减去4?我的理解是这样的(不一定对)。由于ARM采用流水线结构,当CPU正在执行某一条指令的时候,其实取指的动作早
ARMlinux中断处 相关文章:
- arm linux 下中断流程简要分析中断处理流程(11-09)
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)