Search code examples
assemblyarminterruptinline-assemblycortex-m

Cortex M4 SVC code appears to always pass in 255 for the SVC number


I've tried following the documentation to make an SVC instruction work. From the Arm documentation here my SVC_Handler function is as they specify:

void SVC_Handler(void)
{
  __asm(
    ".global SVC_Handler_Main\n"
    "TST lr, #4\n"
    "ITE EQ\n"
    "MRSEQ r0, MSP\n"
    "MRSNE r0, PSP\n"
    "B SVC_Handler_Main\n"
  ) ;
}

My SVC_Handler_Main's first few lines is again just like they specify:

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

  /*
  * Stack contains:
  * r0, r1, r2, r3, r12, r14, the return address and xPSR
  * First argument (r0) is svc_args[0]
  */
  svc_number = ( ( char * )svc_args[ 6 ] )[ -2 ] ;
  switch( svc_number )
  {...

When I run the following code:

__asm("SVC #17"); //17 is an example number

No matter what, the variable svc_number is always 255. The SVC actually triggers just fine and I can do whatever I want inside of it...as long as the svc_number of 255 is used. Any other number just gets set to 255. Did I miss something?

This is an STM32F401RE chip on a Nucleo board.


Solution

  • I have searched far and wide and have figured out the issue. I'm not entirely sure why, but the code provided by ARM didn't work as written, despite using the STM32CubeIDE. I had to re-write the SVC_Handler function so that both volatile and naked attributes were used. It looks like this now and works just fine:

    __attribute__ ((naked)) void SVC_Handler(void) {
      __asm volatile(".global SVC_Handler_Main");
      __asm volatile("TST LR, 4"); // check LR to know which stack is used
      __asm volatile("ITE EQ"); // 2 next instructions are conditional
      __asm volatile("MRSEQ R0, MSP"); // save MSP if bit 2 is 0
      __asm volatile("MRSNE R0, PSP"); // save PSP if bit 2 is 1
      __asm volatile("B SVC_Handler_Main"); // pass R0 as the argument
    }