Linux异常处理体系结构
arm11处理器裸机的异常与中断处理参考:
另外参考一篇:Linux中断体系结构
在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。V和中断向量表的对应关系如下: [V=0]0x00000000~0x0000001C /[V=1]0xffff0000~0xffff001C 。Linux内核使用0xffff0000。
异常向量的代码很简单,只是一些跳转指令。发生异常时,cpu自动执行这些指令,跳转到更复杂得代码。
地址__vectors_start~__vectors_end间的代码就是异常向量,在arch/arm/kernel/entry-armv.S中定义,这些异常向量会被复制到0xffff0000处。
.globl __vectors_start __vectors_start: swi SYS_ERROR0 //复位时,cpu将执行这条指令 b vector_und + stubs_offset //未定义异常时,cpu执行这条指令 ldr pc, .LCvswi + stubs_offset //swi异常 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 //fiq异常 .globl __vectors_end __vectors_end:
“更复杂得代码”在地址__stubs_start~__stubs_end之间,在arch/arm/kernel/entry-armv.S中定义,这些异常向量会被复制到0xffff0200处。
1 .globl __stubs_start 2 __stubs_start: 3 /* 4 * Interrupt dispatcher 5 */ 6 vector_stub irq, IRQ_MODE, 4 7 8 .long __irq_usr @ 0 (USR_26 / USR_32) 9 .long __irq_invalid @ 1 (FIQ_26 / FIQ_32) 10 .long __irq_invalid @ 2 (IRQ_26 / IRQ_32) 11 .long __irq_svc @ 3 (SVC_26 / SVC_32) 12 .long __irq_invalid @ 4 13 .long __irq_invalid @ 5 14 .long __irq_invalid @ 6 15 .long __irq_invalid @ 7 16 .long __irq_invalid @ 8 17 .long __irq_invalid @ 9 18 .long __irq_invalid @ a 19 .long __irq_invalid @ b 20 .long __irq_invalid @ c 21 .long __irq_invalid @ d 22 .long __irq_invalid @ e 23 .long __irq_invalid @ f 24 25 /* 26 * Data abort dispatcher 27 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC 28 */ 29 vector_stub dabt, ABT_MODE, 8 30 31 .long __dabt_usr @ 0 (USR_26 / USR_32) 32 .long __dabt_invalid @ 1 (FIQ_26 / FIQ_32) 33 .long __dabt_invalid @ 2 (IRQ_26 / IRQ_32) 34 .long __dabt_svc @ 3 (SVC_26 / SVC_32) 35 .long __dabt_invalid @ 4 36 .long __dabt_invalid @ 5 37 .long __dabt_invalid @ 6 38 .long __dabt_invalid @ 7 39 .long __dabt_invalid @ 8 40 .long __dabt_invalid @ 9 41 .long __dabt_invalid @ a 42 .long __dabt_invalid @ b 43 .long __dabt_invalid @ c 44 .long __dabt_invalid @ d 45 .long __dabt_invalid @ e 46 .long __dabt_invalid @ f 47 48 /* 49 * Prefetch abort dispatcher 50 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC 51 */ 52 vector_stub pabt, ABT_MODE, 4 53 54 .long __pabt_usr @ 0 (USR_26 / USR_32) 55 .long __pabt_invalid @ 1 (FIQ_26 / FIQ_32) 56 .long __pabt_invalid @ 2 (IRQ_26 / IRQ_32) 57 .long __pabt_svc @ 3 (SVC_26 / SVC_32) 58 .long __pabt_invalid @ 4 59 .long __pabt_invalid @ 5 60 .long __pabt_invalid @ 6 61 .long __pabt_invalid @ 7 62 .long __pabt_invalid @ 8 63 .long __pabt_invalid @ 9 64 .long __pabt_invalid @ a 65 .long __pabt_invalid @ b 66 .long __pabt_invalid @ c 67 .long __pabt_invalid @ d 68 .long __pabt_invalid @ e 69 .long __pabt_invalid @ f 70 71 /* 72 * Undef instr entry dispatcher 73 * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC 74 */ 75 vector_stub und, UND_MODE 76 77 .long __und_usr @ 0 (USR_26 / USR_32) 78 .long __und_invalid @ 1 (FIQ_26 / FIQ_32) 79 .long __und_invalid @ 2 (IRQ_26 / IRQ_32) 80 .long __und_svc @ 3 (SVC_26 / SVC_32) 81 .long __und_invalid @ 4 82 .long __und_invalid @ 5 83 .long __und_invalid @ 6 84 .long __und_invalid @ 7 85 .long __und_invalid @ 8 86 .long __und_invalid @ 9 87 .long __und_invalid @ a 88 .long __und_invalid @ b 89 .long __und_invalid @ c 90 .long __und_invalid @ d 91 .long __und_invalid @ e 92 .long __und_invalid @ f 93 94 .align 5 95 96 /*============================================================================= 97 * Undefined FIQs 98 *----------------------------------------------------------------------------- 99 * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC 100 * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg. 101 * Basically to switch modes, we *HAVE* to clobber one register... brain 102 * damage alert! I don't think that we can execute any code in here in any 103 * other mode than FIQ... Ok you can switch to another mode, but you can't 104 * get out of that mode without clobbering one register. 105 */ 106 vector_fiq: 107 disable_fiq 108 subs pc, lr, #4 109 110 /*============================================================================= 111 * Address exception handler 112 *----------------------------------------------------------------------------- 113 * These aren't too critical. 114 * (they're not supposed to happen, and won't happen in 32-bit data mode). 115 */ 116 117 vector_addrexcptn: 118 b vector_addrexcptn 119 120 /* 121 * We group all the following data together to optimise 122 * for CPUs with separate I & D caches. 123 */ 124 .align 5 125 126 .LCvswi: 127 .word vector_swi 128 129 .globl __stubs_end 130 __stubs_end:
关于stubs_offset的值及解析,参考Linux异常体系之stubs_offset
关于__stubs_start~__stubs_end之间的代码解析,参考vector_stub宏解析 。vector_irq、 vector_dabt、vector_pabt、vector_und、vector_fiq都在它们中间。
各种异常的C处理函数分为5类,分布在不同的函数中。
1.中断处理C总入口函数asm_do_IRQ,在linux/arch/arm/kernel/irq.c中。它调用其它文件注册的中断处理函数。
init_IRQ函数被用来初始化中断的处理框架,设置各种中断的默认处理函数。当中断发生时,中断总入口函数asm_do_IRQ就可以调用这些函数坐进一步的处理。
b vector_irq + stubs_offset -->vector_stub irq, IRQ_MODE, 4 -->__irq_usr/__irq_svc -->void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
2.未定义指令处理C总入口函数do_undefinstr,在linux/arch/arm/kernel/traps.c中。
b vector_und + stubs_offset -->vector_stub und, UND_MODE -->__und_usr/__und_svc -->b do_undefinstr
3.与内存访问相关的异常处理C总入口函数do_DataAbort,do_PrefechAbort,在linux/arch/arm/kernel/fault.c中。
4.swi异常处理函数,在在linux/arch/arm/kernel/calls.S中。swi异常的处理函数指针被组织成一个表格;swi指令机器码的为[23:0]用来作为索引,通过不同的“swi index”指令可以调用不同的swi异常处理函数,也称为系统调用,比如sys_open、sys_read、sys_write等。
5.没有使用的异常,linux中没有使用FIQ异常。