一 Linux 异常处理体系结构/框架
通用异常处理机制
二 linux内核对于异常的设置(P397)
对于下面分析的小结:
(1)异常向量起始地址__vector_start
的代码拷贝到vectors
地址去
(2)一旦出现中断就会跳转到vector_irq
开始的代码;该代码用宏的形式实现的
(3)宏:计算返回地址->保存现场->调用中断处理的c函数->恢复现场
- trap_init函数分析
代码如下:
其中:其中:
vectors=0xffff0000
__vectors_start=异常代码存储地址
,这段代码都是跳转指令,
例如
LINE1064:出现未定义的指令
LINE1069:中断跳转指令
- 追踪
vector_und
变量
继续追踪上图vector_und
变量,我们搜遍代码也找不到…实际上该变量是一个宏定义。(详细说明参见:Linux异常体系之vector_stub宏解析)
下图代码是对于宏vector_stub
的引用:
下图是代码中对于宏vector_stub
的定义:
- 追踪
vector_irq
变量
下面代码是对于宏vector_stub
的引用
按照宏定义展开可以得到如下实际代码:
/*
* Interrupt dispatcher
*/
/*
vector_stub irq, IRQ_MODE, 4
*/
/*
. macro vector_stub, name, mode, correction=0
参数赋值:
name=irq
mode=IRQ_MODE
correction=4
*/
.align 5
vector_irq:
@计算返回地址,与单片机程序一致
sub lr, lr, #4
@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@转换到管理模式
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@向下面的列表跳转
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
movs pc, lr @ branch to handler in SVC mode
.long __irq_usr @ 0 (USR_26 / USR_32)-用户模式下发生中断的时候跳到这里来
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)-管理模式下发生中断的时候跳到这里来
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
- 追踪
__irq_usr
对应的代码
__irq_usr:
usr_entry
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
get_thread_info tsk
#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
strne r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_on
#endif
mov why, #0
b ret_to_user
.ltorg
.align 5
其中usr_entry
也是宏,定义如下:
//功能:保存现场
.macro usr_entry
sub sp, sp, #S_FRAME_SIZE
stmib sp, {r1 - r12}
ldmia r0, {r1 - r3}
add r0, sp, #S_PC @ here for interlock avoidance
mov r4, #-1 @ "" "" "" ""
str r1, [sp] @ save the "real" r0 copied
@ from the exception stack
#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
#ifndef CONFIG_MMU
#warning "NPTL on non MMU needs fixing"
#else
@ make sure our user space atomic helper is aborted
cmp r2, #TASK_SIZE
bichs r3, r3, #PSR_Z_BIT
#endif
#endif
@
@ 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}
stmdb r0, {sp, lr}^
@
@ Enable the alignment trap while in kernel mode
@
alignment_trap r0
@
@ Clear FP to mark the first stack frame
@
zero_fp
.endm
其中,irq_handler
也是宏,定义如下
/*
* Interrupt handling. Preserves r7, r8, r9
*/
.macro 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 *
@最终调用了函数asm_do_IRQ
adrne lr, 1b
bne asm_do_IRQ
...
.endm
那么,追踪函数asm_do_IRQ
即中断处理函数