历史上的今天
返回首页

历史上的今天

今天是:2025年04月26日(星期六)

2019年04月26日 | ARM中断向量表的简单分析

2019-04-26 来源:eefocus

一般编写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 dispatcher 

*/ 

    vector_stub    irq, IRQ_MODE, 4 

  60 :    e24ee004     sub    lr, lr, #4    ; 0x4 

  64:    e88d4001     stm    sp, {r0, lr}


1d4:    e1a00000     .word    0xe1a00000 

1d8:    e1a00000     .word    0xe1a00000 

1dc:    e1a00000     .word    0xe1a00000

000001e0

/* 

* Undef instr entry dispatcher 

* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC 

*/


__vectors_start: 

    swi    SYS_ERROR0 

284:    ef9f0000     svc    0x009f0000 

    b    vector_und + stubs_offset 

 288:    ea0000dd     b    604

    ldr    pc, .LCvswi + stubs_offset 

28c:    e59ff410     ldr    pc, [pc, #1040]    ; 6a4

    b    vector_pabt + stubs_offset 

290:    ea0000bb     b    584

    b    vector_dabt + stubs_offset 

294:    ea00009a     b    504

    b    vector_addrexcptn + stubs_offset 

298:    ea0000fa     b    688

    b    vector_irq + stubs_offset 

29c:    ea000078     b    484

    b    vector_fiq + stubs_offset 

2a0:    ea0000f7     b    684

推荐阅读

史海拾趣

Altus Technology Inc公司的发展小趣事

Altus深知人才是企业发展的根本。因此,公司一直注重人才培养和引进工作。通过建立完善的培训体系和激励机制,Altus吸引了大量优秀人才加入公司,并为他们提供了广阔的发展空间和职业晋升机会。这些人才在公司的各个岗位上发挥着重要作用,推动了公司的技术创新和市场拓展。同时,Altus还注重员工的福利待遇和文化建设,营造了积极向上、团结和谐的工作氛围。

GAIA Converter Inc公司的发展小趣事

锋鸣电子(北京分公司)在成立初期便意识到供应链管理的重要性。公司通过与上游原材料供应商建立长期稳定的合作关系,确保原材料的稳定供应和成本控制。同时,公司还加强了对下游客户的沟通与服务,根据客户需求定制化生产,提升客户满意度。通过高效的供应链整合,锋鸣电子在激烈的市场竞争中保持了良好的竞争力,实现了业务的稳步增长。

Denyo Europa Gmbh公司的发展小趣事

随着技术的成熟,Denyo Europa Gmbh公司开始将目光投向国际市场。公司利用自身的技术优势,结合对目标市场的深入调研,制定了一系列市场拓展策略。通过与当地合作伙伴的紧密合作,公司成功将产品打入多个国家和地区,实现了业务的全球化布局。这一过程中,公司不仅积累了宝贵的国际市场经验,也为公司的持续发展奠定了坚实基础。

Ericsson公司的发展小趣事

在19世纪70年代,Ericsson公司主要以修理电报机和其他电器仪表为主要业务。然而,随着电话技术的引入和普及,Ericsson敏锐地捕捉到了这一新技术带来的商机。公司创始人Lars Magnus Ericsson通过购买、拆解和维修电话机,迅速掌握了电话机制造技术。1878年,Ericsson公司成功推出了自己的电话机,并凭借其经济耐用的特点迅速赢得了市场认可,为公司的发展奠定了坚实的基础。

福建国光公司的发展小趣事

Ericsson公司在早期就非常注重市场拓展和全球布局。通过与各国电信运营商建立合作关系,Ericsson成功将其产品和服务推广到了全球各地。特别是在中国市场,Ericsson早在19世纪90年代就与中国签订了供货合同,成为中国通信网络建设的重要参与者。如今,Ericsson已经在中国扎根130余年,成为了唯一一家从1G到5G全程参与中国通信网络建设的企业。

ZTE高新兴(Gosuncn)公司的发展小趣事

Ericsson非常重视人才培养和团队建设。公司设立了专门的培训机构为员工提供全面的培训和发展机会帮助他们不断更新知识和技能以适应快速变化的市场环境。此外Ericsson还注重营造积极向上的企业文化鼓励员工创新创造和发挥潜力为公司的发展贡献自己的力量。这种注重人才培养和团队建设的策略也为Ericsson的长期发展提供了有力保障。

问答坊 | AI 解惑

windows ce 刚入门,请教。。。。

   我现在学习windows ce6.0 用的C#平台,希望大家能够介绍一下是否有关于windows ce6.0应用程序开发,希望是用C#进行开发的参考书籍,或者是介绍在ce6.0中对API的一些介绍,最好也是用C#进行举例说明的。谢谢大家了!…

查看全部问答>

EVC 的wince5.0模拟器的问题

   我2台机子EVC都装了EVC的WINCE5.0模拟器SDK ,一台机子上EVC呈现编译能在WINCE5模拟器跑起来,而将此程序复制到另外一台机子的WINCE5模拟器却跑不起来,提示Cannot open(or one of its components).Make sure the path and filen~~不 ...…

查看全部问答>

帮忙看一句代码是什么意思...10分谢谢

typedef struct { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; unsigned char bit3 : 1; unsigned char bit4 : 1; unsigned char bit5 : 1; unsigned char bit6 : 1; unsigned char bit7 : 1; } tpBIT; # ...…

查看全部问答>

C#移动项目PPC 2003,连接ACCESS在MSDN里有吗??

C#移动项目PPC 2003连接ACCESS在MSDN里有吗? 我找了半天没找到呀!谁能帮助帮助我!!!…

查看全部问答>

请问版主,通过STLINKIII能不能直接烧写HEX或BIN二进制文件

                                 有没有什么软件能通过STLINKIII直接烧写HEX或BIN二进制文件…

查看全部问答>

请教版主.怎样才算不是广告.我说在卖STM32F系列的就是广告吗

请给我一个回复.有点纳闷.我发个针对中小型客户服务的贴就叫广告贴.那怎样不算广告. 这里只能允许发什么贴.…

查看全部问答>

【求助】也是中断式按键

不知道为什么,我的程序也是在没有按键的时候就直接进入中断程序。下面是我的一个很简单的程序,大家帮忙找找原因,多谢! #include <msp430x44x.h> void main(void) { int i; WDTCTL=WDTPW+WDTHOLD; //停止看门狗 L ...…

查看全部问答>

关于源文件的问题

在一个工程里面,别写了多个.c程序,之后建立一个main.c,在main中怎么其它.c文件中的函数,是不是直接调用,跟写在一个文件里一样的?…

查看全部问答>

XS128的nrf24l01的收发程序

前一阵子在写XS128的nrf24l01的收发程序 还是挺难写的 网上的代码都没有XS128的  现在都弄通了  所以把自己的成果传上来 希望大家共同学习 [ 本帖最后由 iwbago 于 2011-8-19 09:29 编辑 ]…

查看全部问答>