//前文提到发生了中断之后,会散转到下面的跳转语句
W(b) vector_irq + stubs_offset
//那么vector_irq+stubs_offset指向的地址有是什么呢?
//首先看下vector_irq
//@arch/arm/kernel/entry-armv.S
THUMB( .thumb )
/*
* 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
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
@
@ 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 | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
ENDPROC(vector_\name)
.align 2
@ handler addresses follow this label
1:
.endm
从entry-armv.S中的中断向量表定义可以看到,发生中断时系统会跳到 vector_irq + stubs_offset处运行,这个位置实际上就是中断入口函数。
vector_irq已经是中断的入口函数了,为什么又要加上 stubs_offset?是因为b指令实际上是相对当前PC的跳转,也就是说当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量写入指令码。从上面的代码可以看到中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。
我们把搬移前的中断向量表中的irq入口地址记irq_PC,它在中断向量表的偏移量就是irq_PC-vectors_start, vector_irq在stubs中的偏移量是vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start在0xffff0000处,而stubs_start在0xffff0200处,所以搬移后的vector_irq相对于中断向量中的中断入口地址的偏移量就是:200+vector_irq在stubs中的偏移量再减去中断入口在向量表中的偏移量,即200+ vector_irq-stubs_start-irq_PC +vectors_start = (vector_irq-irq_PC) + vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的vector_irq,减去irq_PC是由汇编器完成的
为什么说:"减去irq_PC是由汇编器完成的"?
举个例子:
b vector_irq实际上实现的就是vector_irq-irq_PC这么大偏移量的相对跳转。
而要在这个位置上实现相对偏移量为"((vector_irq-irq_PC)+ (vectors_start + 200 - stubs_start))"这么大的相对跳转,b指令后的地址标识符也应加上一个"(vectors_start + 200 - stubs_start)",也就是stubs_offset,因此在entry-armv.S中将stubs_offset定义为"__vectors_start + 0x200 - __stubs_start":
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
虽然在搬移前,这个跳转指令无法正确跳转,但是在搬移后,这个指令刚好令PC跳转到vector_irq_after_moved,进而执行vector_irq对应的代码
搬移到0xffff0000之前
irq_PC stubs_start vector_irq
| | |
----------|----+------------|~~~~|-----------------+--------------------------|
|
vector_start
irq_PC - vector_start
___
/ \
----------|----+------------|~~~~|-----------------+--------------------------|
\________________________________________/
vector_irq - irq_PC
搬移到0xffff0000之后:
vector_irq_after_moved
0xffff0000 |
----------|----+------------|~~|-----------------+--------------------------|
\___________________/\________________/
200 |
V
vector_irq - stubs_start
格式难以调整,回家画图上传!!!
//下面看看stubs_start后面的对各个异常是如何处理的:
.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
.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
/*
* Data abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub dabt, ABT_MODE, 8
.long __dabt_usr @ 0 (USR_26 / USR_32)
.long __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __dabt_svc @ 3 (SVC_26 / SVC_32)
.long __dabt_invalid @ 4
.long __dabt_invalid @ 5
.long __dabt_invalid @ 6
.long __dabt_invalid @ 7
.long __dabt_invalid @ 8
.long __dabt_invalid @ 9
.long __dabt_invalid @ a
.long __dabt_invalid @ b
.long __dabt_invalid @ c
.long __dabt_invalid @ d
.long __dabt_invalid @ e
.long __dabt_invalid @ f
/*
* Prefetch abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub pabt, ABT_MODE, 4
.long __pabt_usr @ 0 (USR_26 / USR_32)
.long __pabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __pabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __pabt_svc @ 3 (SVC_26 / SVC_32)
.long __pabt_invalid @ 4
.long __pabt_invalid @ 5
.long __pabt_invalid @ 6
.long __pabt_invalid @ 7
.long __pabt_invalid @ 8
.long __pabt_invalid @ 9
.long __pabt_invalid @ a
.long __pabt_invalid @ b
.long __pabt_invalid @ c
.long __pabt_invalid @ d
.long __pabt_invalid @ e
.long __pabt_invalid @ f
/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
vector_stub und, UND_MODE
.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
.align 5
/*=============================================================================
* Undefined FIQs
*-----------------------------------------------------------------------------
* Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
* MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
* Basically to switch modes, we *HAVE* to clobber one register... brain
* damage alert! I don't think that we can execute any code in here in any
* other mode than FIQ... Ok you can switch to another mode, but you can't
* get out of that mode without clobbering one register.
*/
vector_fiq:
subs pc, lr, #4
/*=============================================================================
* Address exception handler
*-----------------------------------------------------------------------------
* These aren't too critical.
* (they're not supposed to happen, and won't happen in 32-bit data mode).
*/
vector_addrexcptn:
b vector_addrexcptn
/*
* We group all the following data together to optimise
* for CPUs with separate I & D caches.
*/
.align 5
.LCvswi:
.word vector_swi
.globl __stubs_end
__stubs_end:
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
.globl __vectors_start
__vectors_start:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
.data
.globl cr_alignment
.globl cr_no_alignment
cr_alignment:
.space 4
cr_no_alignment:
.space 4
#ifdef CONFIG_MULTI_IRQ_HANDLER
.globl handle_arch_irq
handle_arch_irq:
.space 4
//前面已经给出
//.macro vector_stub, name, mode, correction=0
// .align 5
//
//vector_\name:
// ......
//ENDPROC(vector_\name)
//
// .align 2
// @ handler addresses follow this label
//1:
// .endm
//因此第一个branch就是vector_irq
// vector_stub irq, IRQ_MODE, 4
//
// .long __irq_usr @ 0 (USR_26 / USR_32)
// ...
// .long __irq_svc @ 3 (SVC_26 / SVC_32)
// ...
//就是
//vector_\name:
// .if \correction
// sub lr, lr, #\correction
// .endif
//
// @
// @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
// @ (parent CPSR)
// @保存前一个状态的lr,spsr,r0因为马要将返回模式改为SVC,防止中断嵌套
//
// 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 | PSR_ISETSTATE)
// msr spsr_cxsf, r0
// @
// @ the branch table must immediately follow this code
// @
// and lr, lr, #0x0f
// THUMB( adr r0, 1f )
// THUMB( ldr lr, [r0, lr, lsl #2] )
// mov r0, sp
// ARM( ldr lr, [pc, lr, lsl #2] )
// movs pc, lr @ branch to handler in SVC mode
// ENDPROC(vector_\name)
// .align 2
// @ handler addresses follow this label
//1:
// .long __irq_usr @ 0 (USR_26 / USR_32)
// ...
// .long __irq_svc @ 3 (SVC_26 / SVC_32)
// ...
//vector_irq把发生中断时的r0,PC-4以及CPSR压栈(注意,压入的的irq模式的堆栈),把中断栈的栈指针赋给 r0,
//最后根据原来发生中断时CPU所处的工作模式(CPSR的低4位)找到相应的入口函数,在进入svc模式后进一步处理中断。
__irq_usr:
usr_entry
kuser_cmpxchg_check
irq_handler
get_thread_info tsk
mov why, #0
b ret_to_user_from_irq
UNWIND(.fnend )
ENDPROC(__irq_usr)
__irq_svc:
svc_entry
irq_handler
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get 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
#ifdef CONFIG_TRACE_IRQFLAGS
@ The parent context IRQs must have been enabled to get here in
@ the first place, so there's no point checking the PSR I bit.
bl trace_hardirqs_on
#endif
svc_exit r5 @ return from exception
UNWIND(.fnend )
ENDPROC(__irq_svc)
//__irq_usr/__irq_svc主要通过调用宏usr_entry进一步保存现场,然后调用irq_handler进行中断处理
/*
* Interrupt handling.
*/
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr r1, =handle_arch_irq
mov r0, sp
adr lr, BSYM(9997f)
ldr pc, [r1] //调用handle_arch_irq
#else
arch_irq_handler_default
#endif
9997:
.endm
//如果CONFIG_MULTI_IRQ_HANDLER没有配置则调用arch_irq_handler_default
//arch/arm/include/asm/entry-macro-multi.S
/*
* Interrupt handling. Preserves r7, r8, r9
*/
.macro arch_irq_handler_default
get_irqnr_preamble r6, lr
//它首先通过宏 get_irqnr_and_base获得中断号,存在r0,然后把上面建立的pt_regs结构的指针,也就是sp值赋给r1,
//把调用宏 get_irqnr_and_base的位置作为返回地址
1: get_irqnr_and_base r0, r2, r6, lr
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, BSYM(1b)
bne asm_do_IRQ //然后调用c语言定义的asm_do_IRQ()进一步处理中断
#ifdef CONFIG_SMP
/*
* XXX
*
* this macro assumes that irqstat (r2) and base (r6) are
* preserved from get_irqnr_and_base above
*/
ALT_SMP(test_for_ipi r0, r2, r6, lr)
ALT_UP_B(9997f)
movne r1, sp
adrne lr, BSYM(1b)
bne do_IPI
#endif
9997:
.endm
.macro arch_irq_handler, symbol_name
.align 5
.global \symbol_name
\symbol_name:
mov r8, lr
arch_irq_handler_default
mov pc, r8
.endm
//asm_do_IRQ()@arch/arm/kernel/irq.c定义如下:
/*
* asm_do_IRQ is the interface to be used from assembly code.
*/
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
handle_IRQ(irq, regs);
}
//如果CONFIG_MULTI_IRQ_HANDLER有配置,则调用handle_arch_irq
//handle_arch_irq是entry-armv.S里面定义的全局变量:
#ifdef CONFIG_MULTI_IRQ_HANDLER
.globl handle_arch_irq
handle_arch_irq:
.space 4
#endif
//@arch/arm/include/asm/mach/irq.h:
extern void (*handle_arch_irq)(struct pt_regs *);
//@arch/arm/kernel/setup.c:
void __init setup_arch(char **cmdline_p)
+-- struct machine_desc *mdesc;
+-- setup_processor();
+-- if (!mdesc)
mdesc = setup_machine_tags(machine_arch_type);
+-- ...
+-- paging_init(mdesc);
+-- devicemaps_init(mdesc);
+-- vectors = early_alloc(PAGE_SIZE);
+-- early_trap_init(vectors);
+-- ...
+-- handle_arch_irq = mdesc->handle_irq; //因此handle_arch_irq实际上就是machine_desc结构里面的handle_irq函数指针
//而machine_desc结构通过board-xxx.c中的MACHINE_START() ~ MACHINE_END 宏之间的代码进行初始化
MACHINE_START(MSM8X60_RUMI3, "QCT MSM8X60 RUMI3")
.fixup = msm8x60_fixup,
.reserve = msm8x60_reserve,
.map_io = msm8x60_map_io,
.init_irq = msm8x60_init_irq,
.handle_irq = gic_handle_irq,
.init_machine = msm8x60_init,
.timer = &msm_timer,
MACHINE_END
//@arch/arm/common/gic.c
asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
handle_IRQ(irqnr, regs); //handle_IRQ()...
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs); //IPI:inter processor interrupt
#endif
continue;
}
break;
} while (1);
}
//暂且抛开负责处理器间中断处理的handle_IPI()函数不管,两个调用链最终都归结到
//void handle_IRQ(unsigned int irq, struct pt_regs *regs)
//这个函数