您的位置:首页 > 运维架构 > Linux

Linux内核中断机制(二):异常向量表建立

2016-12-27 22:01 176 查看
异常向量表建立

①arm硬件方面

a,ARM中通过CP15协处理器C1寄存器的bit[13]控制。

要么

V=0 0x00000000~0x0000001C

V=1 0xFFFF0000~0xFFFF001C

 

b,arch/arm/mm/proc-arm920.S里面有介绍

 

②Linux内核

init/main.c -> start_kernel()->trap_init()

//注意这里我的内核版本似乎使用了early_trap_init()函数代替了

void __init early_trap_init(void)

{

#endif

unsigned long vectors = CONFIG_VECTORS_BASE;

extern char __stubs_start[], __stubs_end[];

extern char __vectors_start[], __vectors_end[];

extern char __kuser_helper_start[], __kuser_helper_end[];

int kuser_sz = __kuser_helper_end - __kuser_helper_start;

 

/*

 * 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);

memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

 

/*

 * Copy signal return handlers into the vector page, and

 * set sigreturn to be a pointer to these.

 */

memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,

       sizeof(sigreturn_codes));

 

flush_icache_range(vectors, vectors + PAGE_SIZE);

modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

}

A.CONFIG_VECTORS_BASE最初是在各个平台的配置文件中设定的,

如:arch/arm/configs/s3c2410_defconfig
中CONFIG_VECTORS_BASE=0xffff0000

 

 

B.__vectors_end
至 __vectors_start 之间为异常向量表。

位于 arch/arm/kernel/entry-armv.S

.globl__vectors_start

__vectors_start:

swi SYS_ERROR0

b vector_und + stubs_offset    //复位异常

ldr pc, .LCvswi + stubs_offset    //未定义指令异常

b vector_pabt + stubs_offset   //软件中断异常

b vector_dabt + stubs_offset  //数据中断异常

b vector_addrexcptn + stubs_offset

b vector_irq + stubs_offset   //普通中断

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

 

C.__stubs_end 至
__stubs_start 之间是异常处理的位置。也位于文件

arch/arm/kernel/entry-armv.S 中。vector_und、vector_pabt、vector_irq、

vector_fiq 都在它们中间。

.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

 

....................

.....................

/*=============================================================================

 * 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:

disable_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:

 

.equstubs_offset, __vectors_start + 0x200 - __stubs_start

.globl__vectors_start

 

D.如何定义subs_offset的值呢?

stubs_offset 值如下:

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start

stubs_offset 是如何确定的呢?(引用网络上的一段比较详细的解释)

当汇编器看到 B指令后会把要跳转的标签转化为相对于当前PC的偏移量 (±32M)

写入指令码。从上面的代码可以看到中断向量表和 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
是由汇 编器完成的,而后

面的 vectors_start+200-stubs_start
就应该是 stubs_offset,实际上在

entry-armv.S 中也是这样定义的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息