entry-armv.s中
当ARM处理器发生异常(中断是一种异常)时,会跳转到异常向量表(起始地址为0xFFFF_0000或0x0000_0000)。 如3.2节中所述,在中断机制的初始化过程中,把在arch/arm/kernel/entry-armv.S中的异常向量表及其处理程序的stub重定位到了0xFFFF_0000处,这样才使得ARM处理器能够正常处理异常,而且事实上执行的就是entry-armv.S中的异常处理程序
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
异常向量表:
@下面这些才是最初的入口点,__vector_start和__vector_end之间的代码会被移动到CONFIG_VECTORS_BASE开始的地方,例如0xc000000
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0 @Reset
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset @swi instructionSWI指令用于产生软件中断,从而实现从用户模式到管理模式的变换
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset @irq中断入口
b vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
///
__stubs_start和__stubs_end之间,实际上就是异常处理程序的入口
.globl __stubs_start
__stubs_start:
/*
- Interrupt dispatcher
/
vector_stub irq, IRQ_MODE, 4 @此处便是vector_irq的声明调用
.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
//
/ - Vector stubs.
- This code is copied to 0xffff0200 so we can use branches in the
- vectors, rather than ldr’s. Note that this code must not
- exceed 0x300 bytes.
- Common stub entry macro:
- Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- SP points to a minimal amount of processor-private memory, the address
- of which is copied into r0 for the mode specific abort handler.
*/
.macro vector_stub, name, mode, correction=0 @看看 vector_stub irq, IRQ_MODE, 4 是如何变成vector_irq的。
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction @修正返回地址,如果必要的话
.endif
@
@ Save r0, lr_ (parent PC) and spsr_
@ (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
ENDPROC(vector_\name)
.endm
//如果中断以前是svc模式,就会跳到__irq_svc
http://blog.chinaunix.net/uid-12567959-id-160972.html
.align 5
__irq_svc:
svc_entry
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
get_thread_info tsk
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
str r8, [tsk, #TI_PREEMPT] @ restore preempt count
ldr r0, [tsk, #TI_FLAGS] @ get flags
teq r8, #0 @ if preempt count != 0
movne r0, #0 @ force flags to 0
tst r0, #_TIF_NEED_RESCHED
blne svc_preempt
#endif
ldr r0, [sp, #S_PSR] @ irqs are already disabled
msr spsr_cxsf, r0
#ifdef CONFIG_TRACE_IRQFLAGS
tst r0, #PSR_I_BIT
bleq trace_hardirqs_on
#endif
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
ENDPROC(__irq_svc)
///
@usr模式中断入口
.align 5
__irq_usr:
usr_entry
kuser_cmpxchg_check
#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
ENDPROC(__irq_usr)
回看__irq_usr和__irq_svc标号处的代码,在完成了irq_handler中断处理函数后,要完成从中断异常处理程序返回到中断点的工作。如果中断产生于用户空间,则调用ret_to_user来恢复中断现场并返回用户空间继续运行:
//
entry_common.s中
ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq @ disable interrupts
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne work_pending
no_work_pending:
/* perform architecture specific actions before user return */
arch_ret_to_user r1, lr
@ slow_restore_user_regs
ldr r1, [sp, #S_PSR] @ get calling cpsr
ldr lr, [sp, #S_PC]! @ get pc
msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
movs pc, lr @ return & move spsr_svc into cpsr
ENDPROC(ret_to_user)
/
.macro irq_handler
get_irqnr_preamble r5, lr
1: get_irqnr_and_base r0, r6, r5, lr
宏查询ISPR(IRQ待定中断服务寄存器,当有需要处理的中断时,
这个寄存器的相应位会置位,任意时刻,最多一个位会置位)计算出的中断号放在irqnr指定的寄存器中
movne r1, sp @如果中断号不等于0,将r1=sp,即pt_regs结构体首地址
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b @返回到1处,asm_do_IRQ返回后将再次查询发生的中断???
@如果r0(中断号)不等于0, lr(返回地址)等于标号1处,即get_irqnr_and_base r0, r6, r5, lr的那行,即循环处理所有的中断
bne asm_do_IRQ @kernel的中断处理函数
//中断底层汇编可知asm_do_IRQ函数式中断的C语言总入口函数
///
traps.c中的early_trap_init()函数中
unsigned long vectors = CONFIG_VECTORS_BASE;
/*
- Copy the vectors, stubs and kuser helpers (in entry-armv.S)
- into the vector page, mapped at 0xffff0000, and ensure these
- are visible to the instruction stream.
*/
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start
//entry_armv.s中有.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
实际copy动作一目了然,就是两个memcpy(第三个实际上是拷贝一些别的东西,原理是一样的,这里不提了). copy的目的地是vectors,这个值是CONFIG_VECTORS_BASE,在2.6.32.7内核中CONFIG_VECTORS_BASE是在各个平台的配置文件中设定的,如: arch/arm/configs/S3C2410_defconfig中
CONFIG_VECTORS_BASE=0xffff0000