linux中断系统那些事之----中断初始化过程
来源:互联网 发布:简小数据统计图 编辑:程序博客网 时间:2024/05/16 17:08
linux中断向量的初始化
中断异常向量表的地址有两种:
异常向量表的加载地址: 就是在向量表加载到内存,但在运行之前的地址
异常向量表的运行地址: 实际运行时的中断向量表地址
中断异常向量表的基地址的确定:
在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0x00000000,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。V和中断向量表的地址的对应关系如下:
V=0 ~ 0x00000000~0x0000001C
V=1 ~ 0xffff0000~0xffff001C
linux内核目前都是将0xffff0000设置为中断向量表的开始地址
中断异常向量表的内容(arch/arm/kernel/entry-armv.S)
/* * We group all the following data together to optimise * for CPUs with separate I & D caches. */.align5.LCvswi:.wordvector_swi.globl__stubs_end__stubs_end:.equstubs_offset, __vectors_start + 0x200 - __stubs_start/**interruption ventor entry table*rubbitxiao*/.globl__vectors_start__vectors_start: ARM(swiSYS_ERROR0) THUMB(svc#0) THUMB(nop)W(b)vector_und + stubs_offsetW(ldr)pc, .LCvswi + stubs_offsetW(b)vector_pabt + stubs_offsetW(b)vector_dabt + stubs_offsetW(b)vector_addrexcptn + stubs_offsetW(b)vector_irq + stubs_offsetW(b)vector_fiq + stubs_offset.globl__vectors_end__vectors_end:
以上向量表的反汇编代码如下:000002c4 <__stubs_end>: 2c4: ef9f0000 svc 0x009f0000 2c8: ea0000dd b 644 <__switch_to+0x19c> 2cc: e59ff410 ldr pc, [pc, #1040] ; 6e4 <__switch_to+0x23c> 2d0: ea0000bb b 5c4 <__switch_to+0x11c> 2d4: ea00009a b 544 <__switch_to+0x9c> 2d8: ea0000fa b 6c8 <__switch_to+0x220> 2dc: ea000078 b 4c4 <__switch_to+0x1c> 2e0: ea0000f7 b 6c4 <__switch_to+0x21c>
由以上可知中断向量表是一个跳转表,对应8个中断向量,每个异常向量都有固定的pc地址相对应,并且都对应个特定的处理器模式,而每种处理器模式又有各自特有的备份寄存器。他们的对应关系如下:偏移量
中断类型
处理器的模式
0x00
复位
特权模式 1
0x04
未定义的指令
未定义指令终止模式 6
0x08
软件中断
特权模式 6
0x0C
指令预取终止
终止模式 5
0x10
数据访问终止
终止模式 2
0x14
保留
未使用 未使用
0x18
外部中断请求
IRQ模式 4
0x1C
快速中断请求
FIQ模式 3以上异常向量表的基地址为0xffff0000,再加上各自的偏移量,就是各异常向量的虚拟地址,当异常产生时,硬件处理器会自动将各入口地址复制到pc寄存器中。
每种处理器模式下各自的寄存器分布情况如下:
在这里需要重点说明的是:
r0-r12,r15(pc),cpsr是各种处理器模式(除fiq模式)都公共的通用寄存器,而r13(sp),r14(lr)每个模式都有自己的备份寄存器,譬如当处理器从user模式切换到svc模式时,则是使用的管理模式自己私有的r13_svn和r14_svn寄存器,而不再使用用户模式的r13_user和r14_user寄存器,同理除用户模式外其他特权模式都有自己的cpsr寄存器。
在用户模式下是不能够操作特权模式下的私有寄存器的,而特权模式则可以操作用户模式下的r13_user和r14_user寄存器。
另外各种处理器模式是怎么切换的呢?
除用户模式外的其他6种模式称为特权模式,这些模式中,程序可以访问所有系统资源,也可以任意进行处理器模式的切换。处理器模式可以通过软件控制进行切换(直接设置CPSR寄存器的后五位就可以在6种特权模式之间互相切换),也可以通过外部中断或异常处理过程进行切换(例如,在USR模式下,发生中断后切换到IRQ模式)。
那么在汇编代码中,怎么指定是使用用户模式的寄存器还是特权模式自己的寄存器呢?
是通过^后缀来实现的,该后缀的含义如下:'^'是一个后缀标志,不能在User模式和Sys系统模式下使用该标志.该标志有两个存在目的:
1.对于LDM操作,同时恢复的寄存器中含有pc(r15)寄存器,那么指令执行的同时cpu自动将spsr拷贝到cpsr中
如:在IRQ中断返回代码中[如下为ads环境下的代码gliethttp]ldmfd {r4} //读取sp中保存的的spsr值到r4中msr spsr_cxsf,r4 //对spsr的所有控制为进行写操作,将r4的值全部注入spsrldmfd {r0-r12,lr,pc}^//当指令执行完毕,pc跳转之前,将spsr的值自动拷贝到cpsr中
2.数据的送入、送出发生在User用户模式下的寄存器,而非当前模式寄存器,如:
ldmdb sp,{r0 - lr}^;表示sp栈中的数据回复到User分组寄存器r0-lr中,而不是恢复到当前模式寄存器r0-lr当然对于User,System,IRQ,SVC,Abort,Undefined这6种模式来说r0-r12是共用的,只是r13和r14为分别独有,对于FIQ模式,仅仅r0-r7是和前6中模式的r0-r7共用,r8-r14都是FIQ模式下专有
中断异常向量表的建立
该建立过程,就是将中断向量表从加载地址拷贝到运行地址(即0xffff0000)的过程,代码的执行过程如下:
start_kernel(init/main.c)
setup_arch(arch/arm/kernel/setup.c)
paging_init
devicemaps_init(arch/arm/mm/mmu.c)
static void __init devicemaps_init(struct machine_desc *mdesc){struct map_desc map;unsigned long addr;void *vectors;/* * Allocate the vector page early. */vectors = early_alloc(PAGE_SIZE);//分配一个空闲的物理页early_trap_init(vectors); //将中断向量拷贝到这个物理页for (addr = VMALLOC_START; addr; addr += PMD_SIZE)pmd_clear(pmd_off_k(addr));
... ... ... .../* * Create a mapping for the machine vectors at the high-vectors * location (0xffff0000). If we aren't using high-vectors, also * create a mapping at the low-vectors virtual address. */map.pfn = __phys_to_pfn(virt_to_phys(vectors));//将刚分配的并且已经拷贝了中断向量的物理页映射到0xffff0000地址,即为中断向量表的基地址
map.virtual = 0xffff0000;map.length = PAGE_SIZE;map.type = MT_HIGH_VECTORS;create_mapping(&map, false);//建立映射if (!vectors_high()) {//如果不是使用的高端向量,则将物理页映射到0x00的位置。map.virtual = 0;map.type = MT_LOW_VECTORS;create_mapping(&map, false);} ... ...}
void __init early_trap_init(void *vectors_base){unsigned long vectors = (unsigned long)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;vectors_page = 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);//拷贝中断向量表到0xffff0000开始的位置,总计大小为4*8=32bytememcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);//拷贝异常处理程序到0xffff0000+0x200的位置memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);/* * Do processor specific fixups for the kuser helpers */kuser_get_tls_init(vectors);/* * Copy signal return handlers into the vector page, and * set sigreturn to be a pointer to these. */memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE), sigreturn_codes, sizeof(sigreturn_codes));memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE), syscall_restart_code, sizeof(syscall_restart_code));flush_icache_range(vectors, vectors + PAGE_SIZE);modify_domain(DOMAIN_USER, DOMAIN_CLIENT);}
关于中断向量表中的相对偏移量的计算
即每条中断向量中,为什么都要加一个stubs_offset的偏移量呢?
具体见如下的向量表内容:
.equstubs_offset, __vectors_start + 0x200 - __stubs_start/**interruption ventor entry table*rubbitxiao*/.globl__vectors_start__vectors_start: ARM(swiSYS_ERROR0) THUMB(svc#0) THUMB(nop)W(b)vector_und + stubs_offset//为什么每条跳转指令都需要添加stubs_offset常量呢? W(ldr)pc, .LCvswi + stubs_offsetW(b)vector_pabt + stubs_offsetW(b)vector_dabt + stubs_offsetW(b)vector_addrexcptn + stubs_offsetW(b)vector_irq + stubs_offsetW(b)vector_fiq + stubs_offset.globl__vectors_end__vectors_end:
首先异常向量中的跳转指令使用的是b而不是ldr指令的原因是因为b指令相较ldr指令更高效,但跳转的范围是相对当前pc值的,并且大小范围不超过正负32KB。关于arm寻址方式中的相对寻址的定义如下:相对寻址是一种特殊的基址寻址,特殊性是它把程序计数器PC中的当前值作为基地址,语句中的地址标号作为偏移量,将两者相加之后得到操作数的地址。
所以各异常处理程序跟异常向量表都是一起拷贝的,因为他们不能离的太远。
内核代码编译生成后,需要将异常向量表拷贝到指定位置(0x00000000 or 0xffff0000),这就需要将内核中的异常向量表设计成与位置无关的异常向量表的拷贝过程用图表示比较清晰,如下图所示:
向量表搬移及offset偏移量计算示图
图一说明:上面两条有方向的横线,横线方向代表地址生长方向,下面那个是Code/Load视图,是搬移前的代码在生成的二进制内核中的组织情况,上面的Exec view是代码在内存中开始执行后的分配情况。
另外还需要叙说的是:offset = vector_dabt + stub_offset - t2,而t2即使当前pc的值,vector_dabt-t2的值根据相对寻址的概念,就是汇编代码指令中的
vector_dabt标号的值
编译器在汇编这条:W(b)vector_dabt + stubs_offset
指令时,会将vector_dabt标号赋值为:vector_dabt的绝对地址减去当前pc值。
- linux中断系统那些事之----中断初始化过程
- linux中断系统那些事之----中断处理过程
- linux-arm中断系统之中断过程
- linux中断那些事
- Linux中断子系统-中断初始化
- ARM Linux中断机制之中断的初始化
- ARM Linux中断机制之中断的初始化
- linux中断系列之中断子系统初始化(三)
- Linux内核之中断系统
- 中断系统的初始化
- linux中断导读之--初始化<1>
- linux中断导读之--初始化<2>
- Linux中断机制之二:初始化
- Linux中断机制之二:初始化
- Linux kernel 软中断机制之初始化
- arm linux 中断初始化
- linux中断之中断注册
- linux中断之中断注册
- android sensor framework
- 全排列算法原理和实现
- 一个很经典的面试记录
- HttpClient_4 用法 由HttpClient_3 升级到 HttpClient_4 必看
- hdu 2122
- linux中断系统那些事之----中断初始化过程
- 1D1D动规优化初步
- __autoreleasing 修饰符
- qt 国际化
- 会计总论考题
- UVA 11549 Calculator Conundrum
- C#开发之Socket网络编程
- engine 删除FeatureClass 方法
- All about control file in Oracle Database