2

I am trying to create simple SVC Handler in GNU GCC (Not ARM GCC). If SVC service is called, it enters SVC_Handler() correctly, but when I try to find the SVC number used to call SVC Handler (svc_number), I get value 248 instead of 1.

I'm using CubeMX IDE and Nucleo-F401RE board.

Code:

void SVC_Handler(void)
{
     asm volatile (
            "TST lr, #4\t\n"
            "ITE EQ\t\n"
            "MRSEQ r0, msp\t\n"
            "MRSNE r0, psp\t\n"
            "B %[SVC_Handler_Main]\t\n"
            :
            : [SVC_Handler_Main] "i" (SVC_Handler_Main)
            : "r0"
    );

}

void SVC_Handler_Main(unsigned int* svc_args) {
     uint8_t svc_number;

     svc_number = ((char* )svc_args[6])[-2];
     switch(svc_number) { // <- that's where I get 248 instead of 1
     case SVC_ADD:
         SVC_Add_Handler(svc_args[0], svc_args[1]);
         break;
     default:
         break;
     }
}

int __attribute__ ((noinline)) SVC_Service_Add(int x, int y) {
    svc(SVC_ADD);
}

#define SVC_ADD 1

#define svc(code) asm volatile ("SVC %[immediate]"::[immediate] "I" (code))

I used watch expression with breakpoint after svc_number and it's value is 248, instead of 1 that it should be.

Service Calls (SVC) are used mainly in RTOS design for software to enter privileged mode. ARM GCC has some nice support for SV calls, whereas in GNU GCC you have to do it all yourself. The theory goes like this:
When a SV call (in my case SVC_Service_Add() ) is made, it invokes the SVC_Handler(). SVC_Handler() checks which stack is used (main msp or process psp), that information is found by reading bit 2 of Link Register (LR). Depending on that, either msp or psp is saved in r0. After that, compiler puts r0,r1,r2,r3,r12,r14, return address and xPSR in svc_args, so those parameters can be used in SVC_Handler_Main. svc_args[0]=r0, svc_args[1]=r1,...,svc_args[6]=SP (the one we are interested in, the one where SVC number is kept).

Since Cortex M4 stack is full descending, we need to do [-2] to get the byte of svc_args[6] we are interested in. Since the call to the SVC_Handler was done by the SVC_ADD macro (0x01), ((char *) svc_args[6])[-2] should equal to SVC_ADD, so a proper function can be called from SVC_Handler_Main(). I'm not getting 1, I'm getting 248 for some reason.

Question: why is svc_number equal to 248 whereas I was expecting 1

12
  • providing negative integers as arguments of arrays is not recommended. As a matter of fact, you cast an unsigned int into a pointer on char...the issue is to be found on that line I guess. Dump the svc_args array. Commented Feb 3, 2020 at 19:41
  • define what you mean by service number how are you passing the service number? Commented Feb 3, 2020 at 19:46
  • @Heyji using cast on char* with negative int as index is the method used for SVC by many books/online sources. Commented Feb 3, 2020 at 20:22
  • 3
    right passed in the instruction or a register or what? thats the question, in order to extract it how was it encoded, if you dont know that then that is your first problem, then once you know you can figure out why you dont see it Commented Feb 3, 2020 at 20:29
  • 1
    It looks like you want SVC_Handler to be __attribute__((naked)) since you use a b instruction inside inline asm to leave the function. If you let GCC emit a function prologue, that could change things (especially in a debug build). And if it inlines into its caller an optimized build, that's eve worse. (Although that's not a concern if it's only called via hardware dispatch.) Commented Feb 4, 2020 at 3:33

1 Answer 1

3
#define svc(code) asm volatile ("SVC %[immediate]"::[immediate] "I" (code))

This creates the assembler of the form svc #1. Note that the '1' is encoded in the instruction op-code. In order to find the '1', you have to look at the lr and load the op-code at that address and then parse (by masking) the value.

For instance,

ldr r1,[lr]            ; load svc opcode to r1 (might need an offset)
and r1, r1, #SVC_MASK  ; may need shifts as well.

This is not efficient as you are treating a code pipe as data. The normal way to do this is to define an service register, like r7 and then set r7 to the '#1' before the svc #0 instruction. So, the macro is something like,

#define svc(code) asm volatile ("mov r7, %[immediate]\n" \
                                " SVC #0"::[immediate] "I" (code) \
                                 : "r7" /*clobbers r7*)

You can use just r0, but if your call hierarchy gets more complex, many functions may put args in r0,r1,r2,r3 and then you will need to shuffle them. That is why r7 is typically used.


why is svc_number equal to 248 whereas I was expecting 1

It looks like you thought it was put on the stack and this is NOT the case. The value 248 is just something random on the stack.

Sign up to request clarification or add additional context in comments.

2 Comments

Although I have seen a lot of examples with SV Calls for GNU GCC using the syntax I provided (one of which is in The Definitive guide to Cortex M4), the solution with using r7 as a holder for svc code worked for me. Thanks!
I see the recommendations here, I think that the offset '6' might be different for your system depending on what happened in the exception table; Unless SVC_Handler is the primary entry. Basically your offset '6' is trying to get the lr and the look at an instruction back 2 bytes which should be the SVC number (I guess the low byte is the mask operation which assume endianess and is fine for a Cortex-M).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.