当前位置:   article > 正文

Cortex-M核 SVC系统服务的使用(ARM AC6环境下)_svc服务

svc服务

SVC系统服务的使用(ARM AC6环境下)

1、熟悉内嵌汇编

__attribute__((always_inline)) void svc_service_add(uint32_t x, uint32_t y, uint32_t* res)
{
    register unsigned r0 __asm("r0") = x;
    register unsigned r1 __asm("r1") = y;
    register unsigned r2 __asm("r2") = (uint32_t)res;
 
    __asm volatile("SVC #4" :: "r" (r0), "r" (r1), "r" (r2));
    __asm volatile("STR R0, [R2]");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上述是一段调用svc 4服务的接口,期望功能是 res = x + y.

针对 __asm volatile("SVC #4" :: "r" (r0), "r" (r1), "r" (r2));解释。

  • "SVC #4"是触发SVC中断,设置服务号为4
  • 第一个 :后面一般跟随可以从汇编指令中反赋值到C代码变量中
  • 第二个 :后面一般跟随仅从C代码输入到汇编指令的的变量
  • "r" (r0)中的 "r"目测没什么特殊含义,更换成其他字符也是可以的

上述代码在运行第一段内嵌汇编时,会将SVC机器码(包含服务号)、r0、r1、r2压入栈中,同时触发SVC中断,进入内核服务中

举个例子:

void testASM(void)
{
    register uint32_t val = 12, res;

    __ASM volatile (
		".syntax unified\n"
        "mov %[val],%[res]" 

		: [res] "=&l" (res)
		: [val] "l"   (val)
    );

    printf("res = %d\n", res);
}
// 结果 res = 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2、编写SVC中断服务函数(使用汇编实现)

        .syntax  unified									;使用GNU语法
        .section ".rodata"									;定义只读数据段
															;这里没有定义只读数据,如有需要,可在此处定义
        .thumb												;使用thumb指令集
        .section ".text"									;定义代码段
        .align   2											;定义字节对齐为 2^2 = 4
        .eabi_attribute Tag_ABI_align_preserved, 1			;不知道,这样抄过来的

        .thumb_func											;不知道,抄过来的,目测定义函数需要
        .type    SVC_Handler, %function						;不知道,抄过来的,目测定义为 (一个类型名为SVC_Handler, 描述为函数)
        .global  SVC_Handler								;不知道,抄过来的,目测允许此函数对外声明
        .fnstart											;不知道,抄过来的,目测为函数的开始标志,和 .fnend 成对使用
        .cantunwind											;不知道,抄过来的,目测定义函数需要
SVC_Handler:												;标号,在此处范围内获取lr寄存器的值,判断SVC服务的触发在主堆栈还是线程堆栈下
        mov      r0, lr
        lsrs     r0, r0, #3
	    bcc      SVC_MSP									;合成指令,为B + CC,跳转和识别C符号的混合使用,意为当C标号为0时跳转
        mrs      r0, psp

SVC_Number:
        push     {r0,lr}        // Save EXC_RETURN
		bl       SVC_Handler_C								;此时r0里的内容时sp指针,调用C函数,同时r0会成为第一个入口参数参与解析
        pop      {r0,r1}        // Restore EXC_RETURN		;不是很理解,压栈时时r0,lr,出栈时时r0,r1
        mov      lr,r1 

        bx       lr

SVC_MSP:
        mrs      r0, msp
        b        SVC_Number
   

        .fnend												;不知道,抄过来的,目测为函数的结束标志,和 .fnstart 成对使用
        .size    SVC_Handler, .-SVC_Handler					;不知道,抄过来的,目测定义函数需要


        .end												;汇编文件的截至
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

3、解析SVC服务

void SVC_Handler_C(uint32_t* svc_args)
{
    uint32_t svc_number;
    
    svc_number = ((uint8_t*)svc_args[6])[-2];
    
    switch(svc_number)
    {
        case 4:
            printf("SVC 4\r\n");
            break;
        case 5:
            printf("SVC 5\r\n");
            break;
        default:
            break;
    }    
    
    return;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

svc_args是传入的sp堆栈地址,由于压栈顺序依次是从左到右(R0, R1, R2, R3, R12, LR, PC, xPSR),因此,svc_args[6]= PC,即保存机器指令码在flash上的地址,又由于在取指令后PC自动偏移,因此此处的PC指向即将运行的指令,而我们需要的指令在 当前PC - 4的地址上。由于SVC机器码为 0xDF00 | 服务号(服务号是8bit的数据),因此 ((uint8_t*)svc_args[6])[-2]为当前实际触发的服务号。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/1013256
推荐阅读
相关标签
  

闽ICP备14008679号