采用svc模式可以將SDK(API)和Application分離, 也可以省略很多重復的代碼,比如在bootload中用到了usb, 然后在application中想復用的話,可以用svc
原理: 類似軟件中斷,中添加了一個軟件中斷源,通過中斷源表明調用的函數接口
源程序如下:
- #ifndef NRF_SVC__
- #define NRF_SVC__
- #ifdef SVCALL_AS_NORMAL_FUNCTION
- #define SVCALL(number, return_type, signature) return_type signature
- #else
- #ifndef SVCALL
- #if defined (__CC_ARM)
- #define SVCALL(number, return_type, signature) return_type __svc(number) signature
- #elif defined (__GNUC__)
- #define SVCALL(number, return_type, signature) \
- _Pragma("GCC diagnostic ignored \"-Wunused-function\"") \
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wreturn-type\"") \
- __attribute__((naked)) static return_type signature \
- { \
- __asm( \
- "svc %0\n" \
- "bx r14" : : "I" (number) : "r0" \
- ); \
- } \
- _Pragma("GCC diagnostic pop")
- #elif defined (__ICCARM__)
- #define PRAGMA(x) _Pragma(#x)
- #define SVCALL(number, return_type, signature) \
- PRAGMA(swi_number = number) \
- __swi return_type signature;
- #else
- #define SVCALL(number, return_type, signature) return_type signature
- #endif
- #endif // SVCALL
- #endif // SVCALL_AS_NORMAL_FUNCTION
- #endif // NRF_SVC__
- void C_SVC_Handler(uint8_t svc_num, uint32_t * p_svc_args)
- {
- switch (svc_num)
- {
- case NRF_SEC_SVC_HASH:
- p_svc_args[0] = nrf_sec_hash((nrf_sec_data_t *) p_svc_args[0],
- (uint8_t *) p_svc_args[1],
- (nrf_sec_hash_func_t) p_svc_args[2]);
- break;
- case NRF_SEC_SVC_VERIFY:
- p_svc_args[0] = nrf_sec_verify((nrf_sec_data_t *) p_svc_args[0],
- (nrf_sec_ecc_point_t *) p_svc_args[1],
- (nrf_sec_ecc_signature_t *) p_svc_args[2],
- (nrf_sec_algo_t) p_svc_args[3]);
- break;
-
- default:
- p_svc_args[0] = NRF_ERROR_SVC_HANDLER_MISSING;
- break;
- }
- }
- #if defined ( __CC_ARM )
- __asm void SVC_Handler(void)
- {
- 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.
- IMPORT C_SVC_Handler
- LDR R0, =EXC_RETURN_CMD_PSP ; Load the EXC_RETURN into R0 to be able to compare against LR to determine stack pointer used.
- CMP R0, LR ; Compare the link register with R0. If equal then PSP was used, otherwise MSP was used before SVC.
- BNE UseMSP ; Branch to code fetching SVC arguments using MSP.
- MRS R1, PSP ; Move PSP into R1.
- B Call_C_SVC_Handler ; Branch to Call_C_SVC_Handler below.
- UseMSP
- MRS R1, MSP ; MSP was used, therefore Move MSP into R1.
- Call_C_SVC_Handler
- 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.
- ; R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
- 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.
- LDRB R0, [R0] ; SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
- LDR R2, =C_SVC_Handler ; Load address of C implementation of SVC handler.
- 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.
- ALIGN
- }
- #elif defined ( __GNUC__ )
- void __attribute__ (( naked )) SVC_Handler(void)
- {
- 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.
-
- __asm volatile(
- "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.
- "bne UseMSP\t\n" // Branch to code fetching SVC arguments using MSP.
- "mrs r1, psp\t\n" // Move PSP into R1.
- "b Call_C_SVC_Handler\t\n" // Branch to Call_C_SVC_Handler below.
- "UseMSP: \t\n" //
- "mrs r1, msp\t\n" // MSP was used, therefore Move MSP into R1.
- "Call_C_SVC_Handler: \t\n" //
- "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.
- // R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
- "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.
- "ldrb r0, [r0]\t\n" // SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
- "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.
- ".align\t\n"
- :: "r" (exc_return), "r" (C_SVC_Handler) // Argument list for the gcc assembly. exc_return is %0, C_SVC_Handler is %1.
- : "r0", "r1" // List of register maintained manually.
- );
- }
- #else
- #error Compiler not supported.
- #endif
- __asm void SVCHandler(void)
- {
- IMPORT SVCHandler_main
- TST lr, #4
- ITE EQ
- MRSEQ R0, MSP
- MRSNE R0, PSP
- B SVCHandler_main
- }
- void SVCHandler_main(unsigned int * svc_args)
- {
- unsigned int svc_number;
- /*
- * Stack contains:
- * R0, R1, R2, R3, R12, R14, the return address and xPSR
- * First argument (R0) is svc_args[0]
- */
- svc_number = ((char *)svc_args[6])[-2];
- switch(svc_number)
- {
- case SVC_00:
- /* Handle SVC 00 */
- break;
- case SVC_01:
- /* Handle SVC 01 */
- break;
- default:
- /* Unknown SVC */
- break;
- }
- }
- #define SVC_00 0x00
- #define SVC_01 0x01
- void __svc(SVC_00) svc_zero(const char *string);
- void __svc(SVC_01) svc_one(const char *string);
- int call_system_func(void)
- {
- svc_zero("String to pass to SVC handler zero");
- svc_one("String to pass to a different OS function");
- }
- 在 ARM 狀態時為 0。
- ARM 和 Thumb 指令集均有 SVC 指令。 在 Thumb 狀態下調用 SVC 時,必須考慮以下情況:
- 指令的地址在 lr–2,而不在 lr–4。
- 該指令本身為 16 位,因而需要半字加載,請參閱Figure 6.3。
- 在 ARM 狀態下,SVC 編號以 8 位存儲,而不是 24 位。
- Example 6.8. SVC 處理程序
- PRESERVE8
- AREA SVC_Area, CODE, READONLY
- EXPORT SVC_Handler IMPORT C_SVC_Handler
- T_bit EQU 0x20 ; Thumb bit (5) of CPSR/SPSR.
- SVC_Handler
- STMFD sp!, {r0-r3, r12, lr} ; Store registers
- MOV r1, sp ; Set pointer to parameters
- MRS r0, spsr ; Get spsr
- STMFD sp!, {r0, r3} ; Store spsr onto stack and another
- ; register to maintain 8-byte-aligned stack
- TST r0, #T_bit ; Occurred in Thumb state?
- LDRNEH r0, [lr,#-2] ; Yes: Load halfword and...
- BICNE r0, r0, #0xFF00 ; ...extract comment field
- LDREQ r0, [lr,#-4] ; No: Load word and...
- BICEQ r0, r0, #0xFF000000 ; ...extract comment field
- ; r0 now contains SVC number
- ; r1 now contains pointer to stacked registers
- BL C_SVC_Handler ; Call main part of handler
- LDMFD sp!, {r0, r3} ; Get spsr from stack
- MSR SPSR_cxsf, r0 ; Restore spsr
- LDMFD sp!, {r0-r3, r12, pc}^ ; Restore registers and return
- END
復制代碼
所有資料51hei提供下載:
ARM SVC.zip
(3.58 KB, 下載次數: 9)
2019-3-9 12:35 上傳
點擊文件名下載附件
svc的匯編代碼和c調用的方式 下載積分: 黑幣 -5
|