基于linux2.6.30.4, arm-s3c2440板
参考了韦东山老师的<第4.1节 字符设备驱动程序之中断方式的按键驱动_Linux异常处理结构>
中断向量表
- 向量加载处理调用栈
decompressor code ->
stext ->
__switch_data ->
__mmap_switched ->
start_kernel ->
setup_arch ->
early_trap_init ->
- early_tray_init中的处理节选
early_trap_init
unsigned long vectors = CONFIG_VECTORS_BASE; //zImage制作时.config文件中定义CONFIG_VECTORS_BASE=0xffff0000,这个地址就是mmu启用时中断向量表的虚拟首地址
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); //拷贝中断向量表
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); //拷贝各种中断服务程序
- 加载后的的情况
+-----------------------------+- <- __stubs_start`, +0x1000(4096) | | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | <- __stubs_end` | ... | | func vector_dabt | stubs(exception services) | func vector_irq | +-----------------------------+- <- __stubs_start`, +0x200(512) | | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | <- __vectors_end` | ... | | b vector_und` | vector table | swi SYS_ERROR0 | +-----------------------------+ <- __vectors_start`, 0xffff0000(va)
- 来看一下__vectors_start
//entry-armv.S
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0 @on reset
b vector_und + stubs_offset @on undine
ldr pc, .LCvswi + stubs_offset @on swi
b vector_pabt + stubs_offset @on prefetch abort
b vector_dabt + stubs_offset @on data abort
b vector_addrexcptn + stubs_offset @reserved
b vector_irq + stubs_offset @on irq
b vector_fiq + stubs_offset @on fiq
.globl __vectors_end
__vectors_end:
中断服务程序
- 再来看__stubs_start~__stubs_end内容
包含了如下几个函数,这正好是异常发生时,branch到的目标
vector_irq
vector_dabt
vector_pabt
vector_und
vector_fiq
vector_addrexcptn
LCvswi(应该说是一个函数的地址基地)
- 异常服务程序的实现
这里选取vector_und来分析
.align 5 @//对齐到2^5,即32b 4字
vector_und: @//为了说明方便,这里局部位置假设了进入本异常前的mode为user mode
@// Save r0, lr_<exception> (parent PC) and spsr_<exception>
@// (parent CPSR)
stmia sp, {r0, lr} @// save r0, lr
@//stmia before after
@// | | | | high addr
@// | ??? | | ??? |
@// | ??? | | lr | next addr in usr mode
@// und_sp--->| ??? | und_sp--->| r0 | user mode 's r0
@// | ??? | | ??? |
mrs lr, spsr @//将user mode下的cpsr值赋于lr
str lr, [sp, #8] @// save spsr 将lr保存到*(sp+8)
@//stmia before after
@// | | | | high addr
@// | ??? | | spsr | the cpsr in user mode
@// | lr | | lr | next addr in usr mode
@// und_sp--->| r0 | und_sp--->| r0 | user mode 's r0
@// | ??? | | ??? |
@// Prepare for SVC32 mode. IRQs remain disabled.
mrs r0, cpsr @//将undefine mode下的cpsr值保存到r0
eor r0, r0, #(\mode ^ SVC_MODE) @//r0 = r0 ^ (0b11011 ^ 10011),其实就是将低5位清0然后设置为SVC_MODE 0b10011
@//这里包含了一个基本的算法知识,就是异或运算的结合律,即a ⊕ b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c
@//r0 ^ 当前mode的cpsr.mode,也就是自身异或自身,当然就是清零了
@//然后0异或上x,那么就相当于或运算(异或运算又称为不带进位的半加运算 0+0=0 0+1=1+0=1 1+1=0),也就是设置为SVC_MODE了
msr spsr_cxsf, r0 @//将仅修改了mode为SVC_MODE的cpsr写回,以强制进入SVC mode
@// the branch table must immediately follow this code
and lr, lr, #0x0f @//由手册可知mode是用一个5bit来表示的,而所有mode的bit4(第5位)又都是1,所以低4位就可以唯一标识一个mode
@//所以这里的与运算之后的lr就是进入本异常前的mode的标识
mov r0, sp @//set r0=sp,对于即将的函数调用来说,也就是参数1为und_sp
ldr lr, [pc, lr, lsl #2] @//lr = *(pc + lr*4),这当中的玄机看[Table 2-1. PSR Mode Bit Values]的低4位就明白了吧
@//由于三级流水线机制,执行到本指令时,pc应该是指向了__und_usr数据的地址
@//由Table 2-1可以知道,未使用的4,5,6等的坑是预留的,并且不可能有routine进来
movs pc, lr @// branch to handler in SVC mode
@//所进入本异常前的mode,进入子服务程序
ENDPROC(vector_und)
.long __und_usr @// 0 (USR_26 / USR_32)
.long __und_invalid @// 1 (FIQ_26 / FIQ_32)
.long __und_invalid @// 2 (IRQ_26 / IRQ_32)
.long __und_svc @// 3 (SVC_26 / SVC_32)
.long __und_invalid @// 4
.long __und_invalid @// 5
.long __und_invalid @// 6
.long __und_invalid @// 7
.long __und_invalid @// 8
.long __und_invalid @// 9
.long __und_invalid @// a
.long __und_invalid @// b
.long __und_invalid @// c
.long __und_invalid @// d
.long __und_invalid @// e
.long __und_invalid @// f
常用irq情况的进一步处理
由上面的分析可知,应该是进入子函数__irq_usr,做进一上的处理.
.align 5 @align to 32bit
__irq_usr:
usr_entry
kuser_cmpxchg_check
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
add r7, r8, #1 @ increment it
str r7, [tsk, #TI_PREEMPT]
irq_handler @主处理
mov why, #0
b ret_to_user
UNWIND(.fnend )
ENDPROC(__irq_usr)
.macro irq_handler @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, 1b
bne asm_do_IRQ @调用c子函数,有啥要处理的,在这里面去搞吧
.endm
(完)