当前位置:   article > 正文

ARM 软中断指令SWI_arm swi指令

arm swi指令

前面我们学习ARM工作模式中,处理器模式切换可以通过软件控制进行切换,即修改CPSR模式位,但这是在特权模式下,当我们处于用户模式下,是没有权限实现模式转换的。若想实现模式切换,只能由另一种方法来实现,即通过外部中断或是异常处理过程进行切换。于是ARM指令集中提供了两条产生异常的指令,通过这两条指令可以用软件的方法实现异常,其中一个就是中断指令SWI

一、软件中断

软中断是利用硬件中断的概念,用软件方式进行模拟,实现从用户模式切换到特权模式并执行特权程序的机制。

硬件中断是由电平的物理特性决定,在电平变化时引发中断操作,而软中断是通过一条具体指令SWI,引发中断操作,也就是说用户程序里可以通过写入SWI指令来切换到特权模式,当CPU执行到SWI指令时会从用户模式切换到管理模式下,执行软件中断处理。由于SWI指令由操作系统提供的API封装起来,并且软件中断处理程序也是操作系统编写者提前写好的,因此用户程序调用API时就是将操作权限交给了操作系统,所以用户程序还是不能随意访问硬件。

软件中断指令(Software Interrupt, SWI)用于产生软中断,实现从用户模式变换到管理模式,CPSR保存到管理模式的SPSR中,执行转移到SWI向量。在其他模式下也可以使用SWI指令,处理器同样切换到管理模式。

1、SWI指令格式如下:

SWI{cond} immed_24

其中:

immed_24 24位立即数,值为从0――16777215之间的整数。

SWI指令后面的24立即数是干什么用的呢?用户程序通过SWI指令切换到特权模式,进入软中断处理程序,但是软中断处理程序不知道用户程序到底想要做什么?SWI指令后面的24位用来做用户程序和软中断处理程序之间的接头暗号。通过该软中断立即数来区分用户不同操作,执行不同内核函数。如果用户程序调用系统调用时传递参数,根据ATPCSC语言与汇编混合编程规则将参数放入R0~R4即可。

2、指令举例

使用SWI指令时,通常使用以下两种方法进行参数传递,SWI异常处理程序可以提供相关的服务,这两种方法均是用户软件协定。SWI异常中断处理程序要通过读取引起软件中断的SWI指令,以取得24为立即数。

1)、指令中24位的立即数指定了用户请求的服务类型,中断服务的参数通过通用寄存器传递。

如下面这个程序产生一个中断号位12 的软中断:

MOV R0,#34 ;设置功能号为34

SWI 12 ;产生软中断,中断号为12

2)、指令中的24位立即数被忽略,用户请求的服务类型有寄存器R0的值决定,参数通过其他的通用寄存器传递。

如下面的例子通过R0传递中断号,R1传递中断的子功能号:

MOV R0, #12 ;设置12号软中断

MOV R1, #34 ;设置功能号为34

SWI 0

下面的例子通过系统调用函数int led_on(int led_no)实现点亮第led_no 个LED灯,由于C语言里没有SWI 指令对应的语句,因此这儿要用到C语言与汇编混合编程,led_on函数里将参数led_no的值传递给R0,通过软中断SWI指令切换到软中断管理模式,同时R0 软中断方式点亮LED灯,用户通过SWI #1指令可以点灯,具体点亮哪个灯,通过R0保存参数传递,如果亮灯成功返回对应LED号。

  1. #define __led_on_swi_no 1 // 软中断号1,调用管理模式下的do_led_on函数
  2. int led_on(int led_no){
  3. int ret; // 返回值
  4. __asm{ // 由于C程序中没有SWI对应表达式,所以使用混合编程
  5. mov r0, led_no // 根据ATPCS规则,r0存放第一个参数
  6. swi __led_on_swi_no // 产生SWI软中断,中断号为__led_on_swi_no
  7. mov ret, r0 // 软中断处理结束,取得中断处理返回值,传递给ret变量
  8. }
  9. return ret; // 将ret返回给调用led_on的语句
  10. }

二、软中断处理

CPU执行到swi xxx执行后,产生软件中断,由异常处理部分知识可知,软中断产生后CPU将强制将PC的值置为异常向量表地址0x08,在异常向量表0x08处安放跳转指令b HandleSWI,这样CPU就跳往我们自己定义的HandleSWI处执行。

1--保护现场

软中断处理中通过STMFD SP!, {R0-R12, LR} 要保存程序执行现场,将R0~R12通用寄存器数据保存在管理模式下SP栈内,LR由硬件自动保存软中断指令下一条指令的地址(后面利用LR的地址取得SWI指令编码),该寄存器值也保存在SP栈内,将来处理完毕之后返回;

2--获取SWI指令编码

由SWI指令编码知识可知,SWI指令低24位保存有软中断号,通过LDR R4, [LR, #-4]指令,取得SWI指令编码(LR为硬件自动保存SWI xxx指令的下一条指令地址,LR – 4就是SWI指令地址),将其保存在R4寄存器中。通过BIC R4, R4, #0xFF000000 指令将SWI指令高8位清除掉,只保留低24位立即数,取得SWI指令编码;

3--根据SWI指令做出相应操作

根据24位立即数中的软中断号判断用户程序的请求操作。如果24位立即数为1,表示led_on系统调用产生的软中断,则在管理模式下调用对应的亮灯操作do_led_on。如果24位立即数为2,表示led_off系统调用产生的软中断,则调用灭灯操作do_led_on,根据ATPCS调用规则,R0~R3做为参数传递寄存器,在软中断处理中没有使用这4个寄存器,而是使用R4作为操作寄存器的

4--返回并恢复现场

执行完系统调用操作之后,返回到swi_return(在调用对应系统操作时,通过LDREQ LR, =swi_return设置了返回地址),执行返回处理,通过LDMIA SP!, {R0-R12, PC}^ 指令将用户寄存器数据恢复到R0~R12,将进入软中断处理时保存的返回地址LR的值恢复给PC,实现程序返回,同时还恢复了状态寄存器。切换回用户模式下程序中继续执行。

  1. ; 异常向量表开始
  2. ; 0x00: 复位Reset异常
  3. b Reset
  4. ; 0x04: 未定义异常(未处理)
  5. HandleUndef
  6. b HandleUndef
  7. ; 0x08: 软件中断异常,跳往软件中断处理函数HandleSWI
  8. b HandleSWI
  9. … …
  10. ; 省略其它异常向量和对应处理
  11. … …
  12. ;***********************************************************************
  13. ; 软中断处理
  14. ;***********************************************************************
  15. IMPORT do_led_on
  16. IMPORT do_led_off
  17. HandleSWI
  18. STMFD SP!, {R0-R12, LR} ; 保存程序执行现场
  19. LDR R4, [LR, #-4] ; LR - 4 为指令" swi xxx" 的地址,低24位是软件中断号
  20. BIC R4, R4, #0xFF000000 ; 取得ARM指令24位立即数
  21. CMP R4, #1 ; 判断24位立即数,如果为1,调用do_led_on系统调用
  22. LDREQ LR, =swi_return ; 软中断处理返回地址
  23. LDREQ PC, = do_led_on ; 软中断号1对应系统调用处理
  24. CMP R4, #2 ; 判断24位立即数,如果为2,调用do_led_off系统调用
  25. LDREQ LR, =swi_return ; 软中断处理返回地址
  26. LDREQ PC, = do_led_off ; 软中断号2对应系统调用处理
  27. MOVNE R0, #-1 ; 没有该软中断号对应函数,出错返回-1
  28. swi_return
  29. LDMIA SP!, {R0-R12, PC}^ ; 中断返回, ^表示将spsr的值复制到cpsr

其实讲到这,会产生一个疑问,什么时候需要我们从用户模式切换到管理模式?我们应该记得系统调用,就是用户态向内核态的切换。

三、系统调用

操作系统的主要功能是为应用程序的运行创建良好的环境,保障每个程序都可以最大化利用硬件资源,防止非法程序破坏其它应用程序执行环境,为了达到这个目的,操作系统会将硬件的操作权限交给内核来管理,用户程序不能随意使用硬件,使用硬件(对硬件寄存器进行读写)时要先向操作系统发出请求,操作系统内核帮助用户程序实现其操作,也就是说用户程序不会直接操作硬件,而是提供给用户程序一些具备预定功能的内核函数,通过一组称为系统调用的(system call)的接口呈现给用户,系统调用把应用程序的请求传给内核,调用相应的内核函数完成所需的处理,将处理结果返回给应用程序。

这好比我们去银行取款,用户自己的银行帐户不可能随意操作,必须要有一个安全的操作流程和规范,银行里的布局通常被分成两部分,中间用透明玻璃分隔开,只留一个小窗口,面向用户的是用户服务区,工作人员所在区域为内部业务操作区,取款时,将银行卡或存折通过小窗口交给业务员,并且告诉他要取多少钱,具体取钱的操作你是不会直接接触的,业务员会将银行帐户里减掉取款金额,将现金给你。上述操作流程可以很好保护银行系统,银行系统的操作全部由业务员来实现,用户只能向业务员提出自己的服务请求。银行里的小窗口就类似与操作系统的系统调用接口,是将用户请求传递给内核的接口。

操作系统里将用户程序运行在用户模式下,并且为其分配可以使用内存空间,其它内存空间不能访问,内核态运行在特权模式下,对系统所有硬件进行统一管理和控制。从前面所学知识可以了解到,用户模式下没有权限进行模式切换,这也就意味着用户程序不可能直接通过切换模式去访问硬件寄存器,如果用户程序试图访问没有权限的硬件,会产生异常。这样用户程序被限制起来,如果用户程序想要使用硬件时怎么办呢?用户程序使用硬件时,必须调用操作系统提供的API接口才可以,而操作系统API接口通过软件中断方式切换到管理模式下,实现从用户模式下进入特权模式。

At91rm9200处理器对应的linux2.4.19内核系统调用对应的软中断定义如下:

  1. #if defined(__thumb__) //thumb模式
  2. #define __syscall(name) \
  3. "push {r7}\n\t" \
  4. "mov r7, #" __sys1(__NR_##name) "\n\t" \
  5. "swi 0\n\t" \
  6. "pop {r7}"
  7. #else //arm模式
  8. #define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t"
  9. #endif
  10. #define __sys2(x) #x
  11. #define __sys1(x) __sys2(x)
  12. #define __NR_SYSCALL_BASE 0x900000 //此为OS_NUMBER << 20运算值
  13. #define __NR_open (__NR_SYSCALL_BASE+ 5) //0x900005

举一个例子来说:open系统调用,库函数最终会调用__syscall(open),宏展开之后为swi #__NR_open,即,swi #0x900005触发中断,中断号0x900005存放在[lr,#-4]地址中,处理器跳转到arch/arm/kernel/entry-common.S中vector_swi读取[lr,#-4]地址中的中断号,之后查询arch/arm/kernel/entry-common.S中的sys_call_table系统调用表,该表内容在arch/arm/kernel/calls.S中定义,__NR_open在表中对应的顺序号为

__syscall_start:

  1. .long SYMBOL_NAME(sys_open) //5
  2. ...
  3. 将sys_call_table[5]中内容传给pc,系统进入sys_open函数,处理实质的open动作
  4. 注:用到的一些函数数据所在文件,如下所示
  5. arch/arm/kernel/calls.S声明了系统调用函数
  6. include/asm-arm/unistd.h定义了系统调用的调用号规则
  7. vector_swi定义在arch/arm/kernel/entry-common.S
  8. vector_IRQ定义在arch/arm/kernel/entry-armv.S
  9. vector_FIQ定义在arch/arm/kernel/entry-armv.S
  10. arch/arm/kernel/entry-common.S中对sys_call_table进行了定义:
  11. .type sys_call_table, #object
  12. ENTRY(sys_call_table)
  13. #include "calls.S" //将calls.S中的内容顺序链接到这里

源程序:

  1. ENTRY(vector_swi)
  2. save_user_regs
  3. zero_fp
  4. get_scno //将[lr,#-4]中的中断号转储到scno(r7)
  5. arm710_bug_check scno, ip
  6. #ifdef CONFIG_ALIGNMENT_TRAP
  7. ldr ip, __cr_alignment
  8. ldr ip, [ip]
  9. mcr p15, 0, ip, c1, c0 @ update control register
  10. #endif
  11. enable_irq ip
  12. str r4, [sp, #-S_OFF]! @ push fifth arg
  13. get_current_task tsk
  14. ldr ip, [tsk, #TSK_PTRACE] @ check for syscall tracing
  15. bic scno, scno, #0xff000000 @ mask off SWI op-code
  16. //#define OS_NUMBER 9[entry-header.S]
  17. //所以对于上面示例中open系统调用号scno=0x900005
  18. //eor scno,scno,#0x900000
  19. //之后scno=0x05
  20. eor scno, scno, #OS_NUMBER << 20 @ check OS number
  21. //sys_call_table项为calls.S的内容
  22. adr tbl, sys_call_table @ load syscall table pointer
  23. tst ip, #PT_TRACESYS @ are we tracing syscalls?
  24. bne __sys_trace
  25. adrsvc al, lr, ret_fast_syscall @ return address
  26. cmp scno, #NR_syscalls @ check upper syscall limit
  27. //执行sys_open函数
  28. ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
  29. add r1, sp, #S_OFF
  30. 2: mov why, #0 @ no longer a real syscall
  31. cmp scno, #ARMSWI_OFFSET
  32. eor r0, scno, #OS_NUMBER << 20 @ put OS number back
  33. bcs SYMBOL_NAME(arm_syscall)
  34. b SYMBOL_NAME(sys_ni_syscall) @ not private func
  35. /*
  36. * This is the really slow path. We're going to be doing
  37. * context switches, and waiting for our parent to respond.
  38. */
  39. __sys_trace:
  40. add r1, sp, #S_OFF
  41. mov r0, #0 @ trace entry [IP = 0]
  42. bl SYMBOL_NAME(syscall_trace)
  43. /*
  44. //2007-07-01 gliethttp [entry-header.S]
  45. //Like adr, but force SVC mode (if required)
  46. .macro adrsvc, cond, reg, label
  47. adr\cond \reg, \label
  48. .endm
  49. //对应反汇编:
  50. //add lr, pc, #16 ; lr = __sys_trace_return
  51. */
  52. adrsvc al, lr, __sys_trace_return @ return address
  53. add r1, sp, #S_R0 + S_OFF @ pointer to regs
  54. cmp scno, #NR_syscalls @ check upper syscall limit
  55. ldmccia r1, {r0 - r3} @ have to reload r0 - r3
  56. ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
  57. b 2b
  58. __sys_trace_return:
  59. str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
  60. mov r1, sp
  61. mov r0, #1 @ trace exit [IP = 1]
  62. bl SYMBOL_NAME(syscall_trace)
  63. b ret_disable_irq
  64. .align 5
  65. #ifdef CONFIG_ALIGNMENT_TRAP
  66. .type __cr_alignment, #object
  67. __cr_alignment:
  68. .word SYMBOL_NAME(cr_alignment)
  69. #endif
  70. .type sys_call_table, #object
  71. ENTRY(sys_call_table)
  72. #include "calls.S"
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/440864
推荐阅读
相关标签
  

闽ICP备14008679号