5

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 マイクロコントローラーに含まれています。それが常駐するラボボードは、私の大学のニーズに合わせて変更されています。

コードは、シリアル ポートを介してカスタムメイドのプログラムを使用してボードにロードされます。このプログラムはデバッグも可能です。

4

2 に答える 2

7

前述のように、BL を使用して SVC エントリにジャンプしないでください。B を使用してください。最初にルーチンで SVC 番号を決定します。(私のものは SVC_dispatcher と呼ばれます)。どこの大学ですか?私はこれを徹底的に説明しようと試みます。正しい用語を入力したので、私のコメントが不明確な場合や詳細が必要な場合は、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          
于 2014-04-30T21:30:31.430 に答える
4

明確化のために上記のヨーカーの答えを補足するだけです:

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 は無視します)。オペコードを取得し、即値のみを取得します)。

したがって、ヨーカーの例では、次の命令がまさにそれを行っていることがわかります。

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

たとえば、Cortex M0 で C コードを使用した別の例 (thumb 命令) を次に示します。

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;
    }
于 2016-03-13T17:51:34.443 に答える