ARM中断向量表的简单分析

2019-04-26来源: eefocus关键字:ARM  中断向量表  简单分析

一般编写arm的裸机程序的时候,创建中断向量表就把它放在0x00000000~0x0000001c中,一般都放在这个位置上。但是中断向量表也可以放在0xffff0000~0xffff001c中,知道这是怎么设置的么?开始看到的时候真的有点奇怪,因为在学习arm的时候,根本没去看arm中的协处理器CP15中的c1控制寄存器中的v位来控制,我们一般都使用默认的值0,则必须将中断向量表放在0x00000000~0x0000001c中。 


在看Linux内核对arm中的中断的初始化的时候,就一直对0xffff0000的地址有点怀疑,果然在网上发现这个地址不是随便写的,当我看到arm的协处理器进行控制,中断向量表的地址的时候,真的是哭笑不得啊!! 


有人肯定会问?v位是什么时候设置的呢?其实仔细的朋友就知道在head.S中,在创建完页表的时候,如add pc,r10,#PROCINFO_INITFUNC 


别急,r10保存在前面设置的procinfo的地址,但是很多人就觉得PROCINFO_INITFUNC的宏定义就不知道在哪找了,在include/asm/asm-offset.h中有定义。 


这些搞懂了,首先必须将中断向量表拷贝到0xffff0000的地址上去,把中断处理函数也拷贝到0xffff0200的地址上去,那么在中断向量表进行跳转的时候,如b vector_irq+stubs_offset,但是stubs_offset的偏移怎么设置呢?如果用b vector_irq的话,它就会跳转到原先的中断处理函数中去,因为它也拷贝到了0xffff0200的地址上去,所以将__vector_start-_stubs_start+0x200的话就转移到拷贝后的地址上去执行了。 


很多人应该会有点疑问吧,vector_irq好像找不到,别急,细心点,就在宏定义.macro vector_stubs,name,mode,correction中对各种处理函数有定义,所以很快就将中断向量表创建好了。


Linux Version : 2.6.29

1. start_kernel-->setup_arch-->early_trap_init

   1:  

    memcpy((void

 *)vectors, __vectors_start, __vectors_end - __vectors_start);

   2:  

    memcpy((void

 *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

   3:  

    memcpy((void

 *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);



对于第一行: 

__vectors_start 和 __vectors_end 定义在 arch/arm/kernel/entry-armv.S , 它们之间保存了中断向量表。


   1:  

    .globl    __vectors_start

   2:  

__vectors_start:

   3:  

    swi    SYS_ERROR0   

   4:  

    b    vector_und + stubs_offset

   5:  

    ldr    pc, .LCvswi + stubs_offset

   6:  

    b    vector_pabt + stubs_offset

   7:  

    b    vector_dabt + stubs_offset

   8:  

    b    vector_addrexcptn + stubs_offset

   9:  

    b    vector_irq + stubs_offset

  10:  

    b    vector_fiq + stubs_offset

  11:  


  12:  

    .globl    __vectors_end

  13:  

__vectors_end:


vectors 的地址为CONFIG_VECTORS_BASE , 在.config中定义为0xffff0000 

所以 第1行就是把中断向量表拷贝到0xffff0000 

对于第二行: 

vector_stub是一个带参数的宏,第一个是name,第二个是arm excepiton mode,第三个是为了得到返回地址,lr需要减去的偏移


  1:  

    .macro    vector_stub, name, mode, correction=0

   2:  

    .align    5

   3:  


   4:  

vector_/name:

   5:  

    .if

 /correction

   6:  

    sub    lr, lr, #/correction          @得到正确的返回地址

   7:  

    .endif

   8:  


   9:  

    @

  10:  

    @ Save r0, lr_

  11:  

    @ (parent CPSR)

  12:  

    @

  13:  

    stmia    sp, {r0, lr}        @ save r0, lr

  14:  

    mrs    lr, spsr

  15:  

    str    lr, [sp, #8]        @ save spsr

  16:  


  17:  

    @

  18:  

    @ Prepare for

 SVC32 mode.  IRQs remain disabled.

  19:  

    @ 

  20:  

    mrs    r0, cpsr

  21:  

    eor    r0, r0, #(/mode ^ SVC_MODE) @把cpsr内容与(mode^SVC_mode)异或,即r0里为SVC_MODE      

  22:  

    msr    spsr_cxsf, r0  @把r0的值写入整个spsr寄存器(cxsf表示要往哪个字节写入)

  23:  


  24:  

    @

  25:  

    @ the branch table must immediately follow this

 code

  26:  

    @

  27:  

    and    lr, lr, #0x0f  @lr为spsr_

  28:  

    mov    r0, sp         @ 

  29:  

    ldr    lr, [pc, lr, lsl #2] @lr=pc+mode*4,其中pc为紧接着30的指令,即vector_stub后的第一条指令

  30:  

    movs    pc, lr            @ movs会把spsr的值赋给cpsr,所以branch to handler in

 SVC mode

  31:  

ENDPROC(vector_/name)

  32:  

    .endm


再来看下vector 跳转表


   1:  

    .long

    __irq_usr            @  0  (USR_26 / USR_32)

   2:  

    .long

    __irq_invalid            @  1  (FIQ_26 / FIQ_32)

   3:  

    .long

    __irq_invalid            @  2  (IRQ_26 / IRQ_32)

   4:  

    .long

    __irq_svc            @  3  (SVC_26 / SVC_32)

   5:  

    .long

    __irq_invalid            @  4

   6:  

    .long

    __irq_invalid            @  5

   7:  

    .long

    __irq_invalid            @  6

   8:  

    .long

    __irq_invalid            @  7

   9:  

    .long

    __irq_invalid            @  8

  10:  

    .long

    __irq_invalid            @  9

  11:  

    .long

    __irq_invalid            @  a

  12:  

    .long

    __irq_invalid            @  b

  13:  

    .long

    __irq_invalid            @  c

  14:  

    .long

    __irq_invalid            @  d

  15:  

    .long

    __irq_invalid            @  e

  16:  

    .long

    __irq_invalid            @  f


这里只有usr 和svc 有入口,而其他都是invalid ,是因为linux只会从usr(application) 和svc(kernel)两种mode跳转到exception来 

__stubs_start 和 __stubs_end 之间的代码简化后为:


   1:  

__stubs_start:

   2:  

   vector_irq:    @vector_stub    irq, IRQ_MODE, 4

   3:  

   vector_dabt:   @vector_stub    dabt, ABT_MODE, 8

   4:  

   vector_pabt:   @vector_stub    pabt, ABT_MODE, 4

   5:  

   vector_und:    @vector_stub    und, UND_MODE

   6:  

   vector_fiq:

   7:  

   vector_addrexcptn:

   8:  

   .LCvswi:

   9:  

__stubs_end:


由此可以知道 __stubs_start 和 __stubs_end 之间定义了各种异常的入口 

我们再来看为什么异常入口是“b vector_und + stubs_offset”, 同时为什么stubs_offset 的定义如下 

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start 

arm 的跳转指令b 是跳转到相对于PC的一个偏移地址( offset ),汇编器在编译时会对label 减去PC 得到offset,同时vector 拷贝后是如下排列的


__vectors_start




B vector_




__vectors_end



+0x200

__stubs_start




vector_




__stubs_end


因此,”b vector_” 的label –PC = offset, 而offset 为 b 指令与vector的offset,即


                         vector_

                       = vector_

1

2

所以异常入口为“b vector_und + stubs_offset”, 同时stubs_offset= __vectors_start + 0x200 – __stubs_start 

我们可以通过objdump反汇编来验证:


00000060

    .globl    __stubs_start 

__stubs_start: 

/* 

* Interrupt

[1] [2] [3] [4] [5] [6]

关键字:ARM  中断向量表  简单分析

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic459913.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:2440裸板程序之时钟初始化
下一篇:2440裸板程序之inline关键字

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

嵌入式ARM处理器的7种集成开发环境和6种JTAG调试器

7种集成开发环境1.ADS(ARM Developer Suite)ADS由以下几部分组成:(1)命令行开发工具:armcc、armcpp、tcc、tcpp、armasm、armlink、armsd。(2)ARM时库(3)GUI开发环境:Code Warrior或AXD(4)实用程序(5)支持软件组成ADS是ARM公司为了取代其以前的开发工具ARM SDT而推出的新代集成开发工具,它提供了一套完整、高效、快捷、节约的ARM软件开发解决方案。ADS最大的特点在于采用了Code Warrior集成开发环境(IDE), 使其在软件界面、项目管理、代码接口等方面有了大幅度的提升。ADS可以运行在Windows 95/98NT/XP操作系统
发表于 2019-09-21
嵌入式ARM处理器的7种集成开发环境和6种JTAG调试器

嵌入式ARM启动代码的工作

(1)定义代码的初始入口点:初始入口点是指代码运行时的起始点,它在每个映像文件中是唯一的,也是每个可执行的映像文件             所必须含有的,而且必须位于映像文件的可执行域内。(2)设置中断向量表,链接到包括复位、未定义指令,软件中断、取指中断、取数中断、IRQ和FIQ等异常或中断的处理程序。(3)初始化存储系统。(4)初始化ARM各个模式下的堆栈:根据应用程序使用资源的情况,设置每种或者某些ARM处理器模式下的堆栈区域。(5)始化关键的I/O设备:用于防止在使能中断时产生不必要的开销。(6)初始化中断时需要使用的一些变量。(7)中断使能
发表于 2019-09-21

ARM模拟器-skyeye(天目)的安装和使用!

SkyEye是一个可以运行嵌入式操作系统的硬件仿真工具,这样就可以在没有硬件条件下来进行嵌入式系统的开发。以下操作均在Fedora Core 1.0里通过。文档摘要:1、什么是SkyEye?2、SkyEye可以做什么事情?3、安装SkyEye4、安装arm-elf交叉编译器5、测试你的arm-elf-gcc编译器6、执行你的hello程序7、一个应用程序的开发实例8、编译并运行uClinux-dist-20030909.tar.gz9、加入网络功能10、安装完成SkyEye后,下一步将做什么?1、什么是SkyEye?SkyEye是开源软件的一个项目,SkyEye的目标是在Linux和Windows操作系统里提供一个完全的仿真环境
发表于 2019-09-21

ARM汇编:汇编中proc、endp、ret、near、far指令用法

子程序名 PROC NEAR ( 或 FAR )……ret子程序名 ENDP(1)NEAR属性(段内近调用): 调用程序和子程序在同一代码段中,只能被相同代码段的其他程序调用;FAR属性(段间远调用): 调用程序和子程序不在同一代码段中,可以被相同或不同代码段的程序调用.(2)proc是定义子程序的伪指令,位置在子程序的开始处,它和endp分别表示子程序定义的开始和结束两者必须成对出现。(3)ret指令的内部操作是:栈顶字单元出栈,其值赋给IP寄存器。即实现了一个程序的转移,将栈顶字单元保存的偏移地址作为下一条指令的偏移地址。
发表于 2019-09-21

ARM汇编:左移shl、右移shr指令用法!

shl和shr是逻辑移位指令。1.shl是逻辑左移指令,它的功能为:(1)将一个寄存器或内存单元中的数据向左移位;(2)将最后移出的一位写入CF中;(3)最低位用0补充。指令:mov al,01001000b shl al,1 ;将al中数据左移一位 执行后(al)=10010000b,CF=0。 注意:如果移动位数大于1时,必须将移动位数放在cl中。比如,指令:mov al,01010001b mov cl,3 shl al,cl执行后(al)=10001000b,因为最后移出的一位是0,所以CF=0。 2.shr是逻辑右移指令,它和shl所进行的操作刚好相反。
发表于 2019-09-21

cortex-M3与ARM7的比较

发表于 2019-09-21
cortex-M3与ARM7的比较

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved