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 中也是这样定义的。
①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 中也是这样定义的。
相关文章推荐
- ARM-linux异常向量表的建立
- Linux中断实现方法(一):中断注册方法及异常向量表的建立
- 异常向量表
- [dotNET]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法
- [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法
- 6-4 建立学生信息链表(20 分)——尾插建链表_题程序访问是越界——真是醉了,你自己跑下是会抛出异常的
- 接入安卓SDK java.io.IOException: 您的主机中的软件中止了一个已建立的连接异常
- ARM的异常及向量表
- bootload开发之异常向量表
- Kmeans 聚类之建立文档向量模型(VSM)
- ARM的异常处理过程分析(异常向量/工作模式)
- 异常:已引发: "已成功与服务器建立连接,但是在登录过程中发生错误。 (provider: SSL Provider, error: 31 - 加密(ssl/tls)握手失败) 已成功与服务器建立连接
- Unity建立全局异常处理机制
- 【Linux操作系统分析】中断和异常(1)——中断描述符表IDT,I/O中断处理,中断向量
- Spark Mllib里如何建立密集向量和稀疏向量(图文详解)
- [dotNET]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法
- opencl:一个关于向量赋值的异常
- mysql 建立索引的时候 字段太长导致的异常
- 中国电信欢go建立话费话费异常预警(国际)
- ARM 异常向量表及寄存器 (转)