Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing own SVC calls ARM assembly

I would like to know the proper way of writing SVC calls on an ARM-based microcontroller.

My understanding so far is that ARM has an exception vector table, meaning that the first instructions in any program have to be branches to the appropriate handlers:

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

Then, every time an SVC instruction is ran, the mode is switched to supervisor, the number provided with the SVC is stored in R0 and the program would branch to an appropriate handler:

;== 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

So, if we have these instructions:

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

The program would have to switch to supervisor mode, have the number "1" stored in R0, and following the jump table branch to SVC_1, run the code and switch back to user mode.

Is this correct? Am I doing it right?

The problems I have so far is that my compiler says "operator expected" for this line:

SVC             BL            SVC_Entry 

Information on this topic is hard to find on the internet and I just want to know how to properly use SVC calls on an ARM microcontroller.

Thank you very much.

EDIT: The underlying processor is an ARM9 clocking at around 240 MHz. This lives in an AT91 microcontroller. The lab board on which it resides has been modified to fit my University's needs.

The code is loaded on the board using a custom-made program through a serial port. The program also allows debugging.

like image 491
Percentage Avatar asked Apr 22 '13 20:04

Percentage


2 Answers

As said, don't use BL to jump to the SVC entry, use B. Have a routine first to decide determine the SVC number. (Mine is called SVC_dispatcher). What university are you at? I'll try and explain this thoroughly, making no assumptions on how much you do or don't know. I've put in the correct terms so you can google it for more info if my comments are unclear or you want more depth. I'm unsure of the labelling methods with the colon, I'm used to an older instruction set.

Good luck

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          
like image 83
Yoker Avatar answered Oct 02 '22 23:10

Yoker


Just complementing Yoker's answer above for clarifications:

As the example of Yoker's answer shows, traditionally the number from the SVC instruction is NOT stored in the R0 (as you stated in your question), you should retrieve it from the code, reading the immediate value directly from the svc instruction.

To do that, you need to get the pointer to the instruction just before the return address, for example, if you have these instructions:

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

The return address will be LR=0x800018, then we can retrieve the address of the SVC instruction LR-4=0x800014, read that address content (which is the SVC 1 instruction) and get only the first byte of it (we ignore the svc opcode and only get the immediate value).

So, in the Yoker's example, we can see the following instructions doing exactly that:

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

Here is another example using C code in a cortex M0 for instance (thumb instruction):

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;
    }
like image 40
Lilás Avatar answered Oct 02 '22 22:10

Lilás