找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4461|回复: 0
收起左侧

ARM SVC模式源码

[复制链接]
ID:487325 发表于 2019-3-9 12:35 | 显示全部楼层 |阅读模式
采用svc模式可以将SDK(API)和Application分离, 也可以省略很多重复的代码,比如在bootload中用到了usb, 然后在application中想复用的话,可以用svc
原理: 类似软件中断,中添加了一个软件中断源,通过中断源表明调用的函数接口

源程序如下:
  1. #ifndef NRF_SVC__
  2. #define NRF_SVC__

  3. #ifdef SVCALL_AS_NORMAL_FUNCTION
  4. #define SVCALL(number, return_type, signature) return_type signature
  5. #else

  6. #ifndef SVCALL
  7. #if defined (__CC_ARM)
  8. #define SVCALL(number, return_type, signature) return_type __svc(number) signature
  9. #elif defined (__GNUC__)
  10. #define SVCALL(number, return_type, signature) \
  11.   _Pragma("GCC diagnostic ignored \"-Wunused-function\"") \
  12.   _Pragma("GCC diagnostic push") \
  13.   _Pragma("GCC diagnostic ignored \"-Wreturn-type\"") \
  14.   __attribute__((naked)) static return_type signature \
  15.   { \
  16.     __asm( \
  17.         "svc %0\n" \
  18.         "bx r14" : : "I" (number) : "r0" \
  19.     ); \
  20.   }    \
  21.   _Pragma("GCC diagnostic pop")
  22. #elif defined (__ICCARM__)
  23. #define PRAGMA(x) _Pragma(#x)
  24. #define SVCALL(number, return_type, signature) \
  25. PRAGMA(swi_number = number) \
  26. __swi return_type signature;
  27. #else
  28. #define SVCALL(number, return_type, signature) return_type signature  
  29. #endif
  30. #endif  // SVCALL

  31. #endif  // SVCALL_AS_NORMAL_FUNCTION
  32. #endif  // NRF_SVC__

  33. void C_SVC_Handler(uint8_t svc_num, uint32_t * p_svc_args)
  34. {
  35.     switch (svc_num)
  36.     {
  37.         case NRF_SEC_SVC_HASH:
  38.             p_svc_args[0] = nrf_sec_hash((nrf_sec_data_t *)     p_svc_args[0],
  39.                                          (uint8_t *)            p_svc_args[1],
  40.                                          (nrf_sec_hash_func_t)  p_svc_args[2]);
  41.             break;

  42.         case NRF_SEC_SVC_VERIFY:
  43.             p_svc_args[0] = nrf_sec_verify((nrf_sec_data_t *)           p_svc_args[0],
  44.                                            (nrf_sec_ecc_point_t *)      p_svc_args[1],
  45.                                            (nrf_sec_ecc_signature_t *)  p_svc_args[2],
  46.                                            (nrf_sec_algo_t)             p_svc_args[3]);
  47.             break;
  48.         
  49.         default:
  50.             p_svc_args[0] = NRF_ERROR_SVC_HANDLER_MISSING;
  51.             break;
  52.     }
  53. }

  54. #if defined ( __CC_ARM )
  55. __asm void SVC_Handler(void)
  56. {
  57. EXC_RETURN_CMD_PSP  EQU 0xFFFFFFFD  ; EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.

  58.     IMPORT C_SVC_Handler
  59.     LDR   R0, =EXC_RETURN_CMD_PSP   ; Load the EXC_RETURN into R0 to be able to compare against LR to determine stack pointer used.
  60.     CMP   R0, LR                    ; Compare the link register with R0. If equal then PSP was used, otherwise MSP was used before SVC.
  61.     BNE   UseMSP                    ; Branch to code fetching SVC arguments using MSP.
  62.     MRS   R1, PSP                   ; Move PSP into R1.
  63.     B     Call_C_SVC_Handler        ; Branch to Call_C_SVC_Handler below.
  64. UseMSP
  65.     MRS   R1, MSP                   ; MSP was used, therefore Move MSP into R1.
  66. Call_C_SVC_Handler
  67.     LDR   R0, [R1, #24]             ; The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
  68.                                     ; R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
  69.     SUBS  R0, #2                    ; The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
  70.     LDRB  R0, [R0]                  ; SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
  71.     LDR   R2, =C_SVC_Handler        ; Load address of C implementation of SVC handler.
  72.     BX    R2                        ; Branch to C implementation of SVC handler. R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
  73.     ALIGN
  74. }
  75. #elif defined ( __GNUC__ )
  76. void __attribute__ (( naked )) SVC_Handler(void)
  77. {
  78.     const uint32_t exc_return = 0xFFFFFFFD;      // EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.
  79.    
  80.     __asm volatile(
  81.         "cmp   lr, %0\t\n"                       // Compare the link register with argument 0 (%0), which is exc_return. If equal then PSP was used, otherwise MSP was used before SVC.
  82.         "bne   UseMSP\t\n"                       // Branch to code fetching SVC arguments using MSP.
  83.         "mrs   r1, psp\t\n"                      // Move PSP into R1.
  84.         "b     Call_C_SVC_Handler\t\n"           // Branch to Call_C_SVC_Handler below.
  85.         "UseMSP:  \t\n"                          //
  86.         "mrs   r1, msp\t\n"                      // MSP was used, therefore Move MSP into R1.
  87.         "Call_C_SVC_Handler:  \t\n"              //
  88.         "ldr   r0, [r1, #24]\t\n"                // The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
  89.                                                  // R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
  90.         "sub   r0, r0, #2\t\n"                   // The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
  91.         "ldrb  r0, [r0]\t\n"                     // SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
  92.         "bx    %1\t\n"                           // Branch to C implementation of SVC handler, argument 1 (%1). R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
  93.         ".align\t\n"
  94.         :: "r" (exc_return), "r" (C_SVC_Handler) // Argument list for the gcc assembly. exc_return is %0, C_SVC_Handler is %1.
  95.         : "r0", "r1"                             // List of register maintained manually.
  96.     );
  97. }
  98. #else
  99. #error Compiler not supported.
  100. #endif


  101. __asm void SVCHandler(void)
  102. {
  103.     IMPORT SVCHandler_main
  104.     TST lr, #4
  105.     ITE EQ
  106.     MRSEQ R0, MSP
  107.     MRSNE R0, PSP
  108.     B SVCHandler_main
  109. }
  110. void SVCHandler_main(unsigned int * svc_args)
  111. {
  112.     unsigned int svc_number;
  113.     /*
  114.     * Stack contains:
  115.     * R0, R1, R2, R3, R12, R14, the return address and xPSR
  116.     * First argument (R0) is svc_args[0]
  117.     */
  118.     svc_number = ((char *)svc_args[6])[-2];
  119.     switch(svc_number)
  120.     {
  121.         case SVC_00:
  122.             /* Handle SVC 00 */
  123.             break;
  124.         case SVC_01:
  125.             /* Handle SVC 01 */
  126.             break;
  127.         default:
  128.             /* Unknown SVC */
  129.             break;
  130.     }
  131. }

  132. #define SVC_00 0x00
  133. #define SVC_01 0x01
  134. void __svc(SVC_00) svc_zero(const char *string);
  135. void __svc(SVC_01) svc_one(const char *string);
  136. int call_system_func(void)
  137. {
  138.     svc_zero("String to pass to SVC handler zero");
  139.     svc_one("String to pass to a different OS function");
  140. }

  141. 在 ARM 状态时为 0。

  142. ARM 和 Thumb 指令集均有 SVC 指令。 在 Thumb 状态下调用 SVC 时,必须考虑以下情况:

  143. 指令的地址在 lr–2,而不在 lr–4。

  144. 该指令本身为 16 位,因而需要半字加载,请参阅Figure 6.3。

  145. 在 ARM 状态下,SVC 编号以 8 位存储,而不是 24 位。
  146. Example 6.8. SVC 处理程序
  147.     PRESERVE8
  148.     AREA SVC_Area, CODE, READONLY
  149.     EXPORT SVC_Handler    IMPORT C_SVC_Handler
  150. T_bit   EQU    0x20                    ; Thumb bit (5) of CPSR/SPSR.
  151. SVC_Handler
  152.     STMFD   sp!, {r0-r3, r12, lr}  ; Store registers
  153.     MOV     r1, sp                 ; Set pointer to parameters
  154.     MRS     r0, spsr               ; Get spsr
  155.     STMFD   sp!, {r0, r3}          ; Store spsr onto stack and another
  156.                                    ; register to maintain 8-byte-aligned stack
  157.     TST     r0, #T_bit             ; Occurred in Thumb state?
  158.     LDRNEH  r0, [lr,#-2]           ; Yes: Load halfword and...
  159.     BICNE   r0, r0, #0xFF00        ; ...extract comment field
  160.     LDREQ   r0, [lr,#-4]           ; No: Load word and...
  161.     BICEQ   r0, r0, #0xFF000000    ; ...extract comment field

  162.     ; r0 now contains SVC number
  163.     ; r1 now contains pointer to stacked registers

  164.     BL      C_SVC_Handler          ; Call main part of handler
  165.     LDMFD   sp!, {r0, r3}          ; Get spsr from stack
  166.     MSR     SPSR_cxsf, r0               ; Restore spsr
  167.     LDMFD   sp!, {r0-r3, r12, pc}^ ; Restore registers and return
  168.     END
复制代码

所有资料51hei提供下载:
ARM SVC.zip (3.58 KB, 下载次数: 9)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表