【问题标题】:Writing own SVC calls ARM assembly编写自己的 SVC 调用 ARM 程序集
【发布时间】:2013-04-22 20:47:02
【问题描述】:

我想知道在基于 ARM 的微控制器上编写 SVC 调用的正确方法。

到目前为止,我的理解是 ARM 有一个异常向量表,这意味着任何程序中的第一条指令都必须分支到适当的处理程序:

RESET          ;Handles reset
UNDEFINED      ;Undefined instructions
SVC             BL            SVC_Entry 
PRE_ABORT      ;Prefetch abort
DAT_ABORT      ;Data abort

然后,每次运行 SVC 指令时,模式切换到监督模式,SVC 提供的编号存储在 R0 中,程序将跳转到适当的处理程序:

;== Handling SVC calls ========================================================

Max_SVC     EQU 1

SVC_Entry   CMP R0, #Max_SVC            ;Check upper limit
            BHI SVC_end                 ;Does nothing if unknown    
            ADD R0, PC, R0, LSL #2      ;Calculate table address
            LDR PC, [R0, #0]        

Jump_table  DEFW    SVC_0                   ;Halt
            DEFW    SVC_1                   ;Print string

;== SVC calls ================================================================

SVC_1       B   SVC_end

SVC_end     MOVS    PC, LR          ;Exiting

所以,如果我们有这些说明:

ADR R1, string       ;R1 points to the string
SVC 1                ;SVC_1 handles the printing

程序必须切换到主管模式,将数字“1”存储在 R0 中,然后按照跳转表分支到 SVC_1,运行代码并切换回用户模式。

这是正确的吗?我做得对吗?

到目前为止我遇到的问题是我的编译器对这一行说“操作员预期”:

SVC             BL            SVC_Entry 

在互联网上很难找到有关此主题的信息,我只想知道如何在 ARM 微控制器上正确使用 SVC 调用。

非常感谢。

编辑: 底层处理器是时钟频率约为 240 MHz 的 ARM9。这住在 一个 AT91 微控制器。它所在的实验室板已经过修改,以满足我大学的需要。

代码通过串行端口使用定制程序加载到板上。该程序还允许调试。

【问题讨论】:

  • 您的目标是什么微控制器或系列? arm7tdmi?皮质-m?
  • 你需要这样的东西,SVC: B SVC_Entry。请注意,: 冒号使其成为标签。将其命名为 svc_vec 可能更好,所以是 svc_vec: b SVC_Entry。另一个问题,如果你使用BL,你会杀死user返回地址。您可以使用R0 或您希望建立的任何约定;检查指令通常很慢。不要忘记设置 supervisor 堆栈和模式。您可能希望在更改之前保存一些 user 寄存器;除非你在调用约定中定义。
  • @dwelch 传统上,SVC 索书号被编码在操作码中(这就是 Acorn 在 RISCOS 中的做法)。但是,ARM Linux 和 iOS 都将它传递到一个寄存器中——从性能角度来看,这在所有具有哈佛架构的 ARM 设备上可能是有意义的,因为它避免了一条只有 4 个字节可能被读取的行污染 D-Cache ,以及相应的缓存行填充。
  • @artlessnoise,我已经尝试按照你的建议命名它,编译器说:找不到助记符。
  • 'Mnemonic not found' 意味着汇编器仍然认为你的标签是一条指令。您应该使用您现在拥有的确切代码更新您的问题。 大部分你勾勒出来的似乎是正确的。您实际上不需要向量表中的标签,因为 dwelch 显示 cortex-m 上的 Thumb。 真实代码越多越好。

标签: assembly arm hardware microcontroller


【解决方案1】:

如前所述,不要使用BL跳转到SVC入口,使用B。首先有一个例程来确定SVC号。 (我的称为 SVC_dispatcher)。你在哪所大学?我将尝试彻底解释这一点,不假设您知道或不知道多少。我已经输入了正确的术语,因此如果我的 cmets 不清楚或您想要更多深度,您可以在 Google 上搜索更多信息。我不确定用冒号标记的方法,我习惯了旧的指令集。

祝你好运

SVC_dispatcher
            PUSH    {LR}              ;  always save your LR
            LDR     R14, [LR, #-4]    ; its been stacked, so we can it (LR is R14)
   ; the link register is the line after the SVC instruction
   ; above, we load the instruction that is one before the 
   ; link register (#-4 preindexed load) means instruction (SVC 1) into R14.                      

   ; Use bit clear to remove the mnemonic from the  32 bit instruction, 
  ; leaving the data (1)     
             BIC     R14, R14, #&FF000000  

SVC_entry
            CMP     R14, #Max_SVC
            BHI     SVC_unknown
            ADR     R1, Jump_Table        ; use this format, never add to the PC
            LDR     PC, [R1, R14, LSL #2] 
       ; Beware: PC can be changed by IRQs **AT ANY TIME**

Jump_Table                                 ; you know the drill here
            DEFW    SVC_0            
            DEFW    SVC_1

SVC_0       B       SVC_end

SVC_1       BL      printString     ; consider stacking registers that printString 
            B       SVC_end         ; will corrupt

SVC_end     POP     {LR}            ; restore link register 
            MOV     PC, LR          

【讨论】:

    【解决方案2】:

    只是补充上面 Yoker 的回答以进行澄清:

    正如 Yoker 的答案示例所示,传统上 SVC 指令中的数字不存储在 R0 中(如您在问题中所述),您应该从代码中检索它,直接从 svc 指令中读取立即值.

    为此,您需要在返回地址之前获取指向该指令的指针,例如,如果您有以下指令:

    0x800010: ADR R1, string       ;R1 points to the string
    0x800014: SVC 1                ;SVC_1 handles the printing
    0x800018: ADD R1, R2           ;The next instruction after the SVC where LR will point to
    

    返回地址将是LR=0x800018,然后我们可以检索SVC指令LR-4=0x800014的地址,读取该地址内容(即SVC 1指令)并只获取它的第一个字节(我们忽略 svc 操作码,只获取立即值)。

    因此,在 Yoker 的示例中,我们可以看到以下说明正是这样做的:

    LDR     R14, [LR, #-4]
    BIC     R14, R14, #&FF000000
    

    这是另一个在皮质 M0 中使用 C 代码的示例(拇指指令):

    void SVC_Handler(void)
    {
        // Get stack pointer, assuming we the thread that generated
        // the svc call was using the psp stack instead of msp
        unsigned int *stack;
        asm volatile ("MRS %0, psp\n\t"  : "=rm" (stack) );
    
        // Stack frame contains:
        // r0, r1, r2, r3, r12, r14, the return address and xPSR
        // - Stacked R0 = stack[0]
        // - Stacked R1 = stack[1]
        // - Stacked R2 = stack[2]
        // - Stacked R3 = stack[3]
        // - Stacked R12 = stack[4]
        // - Stacked LR = stack[5]
        // - Stacked PC = stack[6]
        // - Stacked xPSR= stack[7]
    
        // Thumb instructions have 2 bytes instead of 4, then get
        // the first byte of the instruction right before the
        // instruction pointed by the stacked PC
        unsigned int svc_number = ((char *)svc_args[6])[-2];
        switch(svc_number) {
            case 0:
                ...
                break;
            case 1:
                ...
                break;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-11
      • 2011-03-09
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      相关资源
      最近更新 更多