免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1997 | 回复: 0
打印 上一主题 下一主题

arm swi 软中断测试 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-20 09:44 |只看该作者 |倒序浏览

swi 是 arm 的软件中断指令,大概是 software interrupt 的意思 执行完swi指令后,cpu会做几件事情:

  1. 将swi的下一条指令地址保存到 r14_svc 中
  2. 将当前 cpsr 保存到 spsr_svc 中
  3. 将cpu模式改为特权模式svc_mode, 即更改 cpsr 的低五位, cspr[4:0]=0b10011
  4. 切换到ARM状态, cspr[5]=0
  5. 禁止IRQ, cspr[7]=1
  6. 将 pc 置为 0x00000008, 即跳到中断向量的地方开始执行

其于CPU的这种行为,我们需要考虑几件事情:


  • cpu最后跳到0x00000008地址执行,而且该地址处只有4个字节可以使用。 
    这4个字节正好可以放置一条指令。那么显然这个指令要将pc跳到另一个地方。


如何实现呢?

首先考虑当前的环境和需求。

在我的这块开发板上,SRAM的地址空间为 0x30000000 ~ 0x34000000 所以最开始要将测试程序放到 0x30000000 开始的地方。当程序从 0x30000000 地址跑 起来之后,我们再去设置中断向量表,即在在代码中去设置 0x00000000 ~ 0x0000001c 这块地址空间的指令。

于是想到可以如下来实现:

  1. .global _start
  2. _start:
  3.     mov r8, #0
  4.     adr r9, vector_init_block
  5.     ldmia {r0-r7}
  6.     stmia {r0-r7}

  7. vector_init_block:
  8.     b    reset_addr
  9.     b    undefined_addr
  10.     b    swi_addr
  11.     b    prefetch_addr
  12.     b    abort_addr
  13.     b    notused_addr
  14.     b    irq_addr
  15.     b    fiq_addr

程序从 _start 开始执行,第一件事情即将 vector_init_block 开始的8条指令复制到 0x00000000 开始的地方,每条指令占4字节,共 8*4=32字节。

假想现在程序从 0x30000000 开始执行,之后调用 swi 发生了软件中断,pc开始跳到 0x00000008 开始执行,这个位置的指令是 b swi_addr , 然后再跳到swi_addr去继续 执行。

这里有一个问题,跳转指令 b 只能在 32M 空间范围内跳转。 而 swi_addr 肯定是在 0x30000000 之后的,所以这里会跳转失败。

要想在32位(4G)地址空间实现跳转,可以使用 ldr 指令。 于是可以如下实现

  1. .global _start
  2. _start:
  3.     mov r8, #0
  4.     adr r9, vector_init_block
  5.     ldmia {r0-r7}
  6.     stmia {r0-r7}
  7.     ldmia {r0-r7}
  8.     stmia {r0-r7}

  9. vector_init_block:
  10.     ldr pc, reset_addr
  11.     ldr pc, undefined_addr
  12.     ldr pc, swi_addr
  13.     ldr pc, prefetch_addr
  14.     ldr pc, abort_addr
  15.     ldr pc, notused_addr
  16.     ldr pc, irq_addr
  17.     ldr pc, fiq_addr

  18. reset_addr:     .word reset_handler
  19. undefined_addr: .word undefined_handler
  20. swi_addr:     .word swi_handler
  21. prefetch_addr:     .word prefetch_handler
  22. abort_addr:     .word abort_handler
  23. notused_addr:    .word 0
  24. irq_addr:     .word irq_handler
  25. fiq_addr:     .word fiq_handler

ldr 指令是将某个地址里的值读到寄存器中,而紧接着我们将几个中断处理函数 的地址放到内存中供 ldr 读取。

reset_addr .word reset_handler 就是很典型的指针用法, 将处理函数 reset_handler 的地址放到内存中存起来。

与上面不同的时,这里 _start 中不仅复制了指令,而且复制了后面的‘指针’



  • 上面构造好中断向量表之后,swi 就可以正确跳到 swi_handler 地址处开始执行了。 
    那么在 swi_handler 处理函数中,需要做些什么事情呢?


swi处理通常分为两级:

  1. 汇编实现,保存现场,计算swi中的24位立即数
  2. 可以用c实现,具体实现swi各个功能

细分如下:

  1. 保存环境,将要用到的寄存器和返回地址保存到栈中,供退出时恢复
  2. 计算中断号,swi指令中包括一个24位的立即数,用于指定特定功能
  3. 进入swi处理程序。这部分可以用c实现
  4. 中断返回,恢复寄存器
  1. swi_handler:
  2.     stmfd     {r0-r12, lr}

  3.     ldr    r0, [lr, #-4]
  4.     bic    r0, r0, #0xff000000
  5.     bl    c_swi_handler

  6.     ldmfd     {r0-r12, pc}^

看到 swi 是怎么返回的了么! ldmfd sp!, {r0-r12, pc}^ ^ 表示恢复 spsr_svc -> cpsr



  • c语言部分。 首先要注意的是上面 bl c_swi_handler 之前计算好了 r0 的值,跳到 
    c_swi_handler时,r0将作为第一个实参传给c_swi_handler, 所以在c_swi_handler中 
    可以得到中断号

    1. void c_swi_handler(unsigned int nr)
    2. {
    3.     /* print swi exception number */
    4.     puts("c_swi_handler\t");
    5.     put_dex(nr);
    6.     
    7.     return;
    8. }


  • 如何触发 swi 中断呢? 这好像不是个问题,直接使用 swi 不就可以了么。 
    那在c语言里呢? 嵌入汇编!

  1. static inline void swi(void)
  2. {
  3.     __asm__ __volatile__ (
  4.         "swi 0x1\n\t"
  5.     );
  6. }



swi 带的参数 0x1 即为中断号

附件是本文例子的完整代码,运行于2410上,其中通过串口打印来方便看到测试结果

讲了这么多,swi倒底可以用来做什么呢? 通过上面的测试,对swi运行流程有了一个大概的了解。之后第一映像就是swi可以用来实 现系统调用。 如同在x86下linux中使用的 int 0x80

 2410-exception-swi.rar   

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP