Keil Logo

ARM: How to write an SVC function


Information in this knowledgebase article applies to:

  • MDK
  • armcc and armclang

QUESTION

The Cortex-M cores support the SuperVisor Call (SVC) instruction, with that the user can trigger an exception. This can become handy, if e. g. the core is in unprivileged mode and the program needs to access special registers, that only can be accessed in privileged mode. Exceptions run in privileged mode.
Can the toolchain be used to generate such an SVC?

ANSWER

Yes, but how it works depends on the used compiler version and if there is some other software component in use, that already implements an SVC handler, like the CMSIS RTOS.

Calling a SVC

For that the program needs to insert the SVC instruction into the code. This instruction has a parameter (for Thumb code 0-255) to select different SVC functions. The example generates the SVC 0. But also have in mind, that other software components in the project may use this already. Select an other parameter value then.

With ARMCC: This compiler knows the __svc keyword, which makes it simple to define a SVC function:

void __svc( 0 ) EnablePrivilegedMode( void ) ;

With ARMCLANG: With this compiler inline assembler needs to be used to generate something equivalent:

#define EnablePrivilegedMode() __asm("SVC #0")

Calling this function in the program will generate the required instruction at that location of the function call.

Handling a SVC

When using a CMSIS RTOS, then the RTOS already implements its own SVC handler, that also needs to remain in the image. See the RTOS documentation on how to add additional user SVC calls.
Else, the application needs to implement the SVC handler, which requires a specific syntax for a toolchain, as assembler code is used. The following example works with cores like the Cortex-M3 that support conditional execution of instructions.

With ARMCC:

__asm void SVC_Handler(void)
{
  IMPORT SVC_Handler_Main
  TST lr, #4
  ITE EQ
  MRSEQ r0, MSP
  MRSNE r0, PSP
  B SVC_Handler_Main
}

With ARMCLANG:

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"
  ) ;
}

Both functions end in the SVC_Handler_Main(), which is a normal C function. This function looks at the instruction before the stacked return address to determine the SVC number. The SVC 0 in this case disables the privileged mode.

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 )
  {
    case 0:  /* EnablePrivilegedMode */
      __set_CONTROL( __get_CONTROL( ) & ~CONTROL_nPRIV_Msk ) ;
      break;
    default:    /* unknown SVC */
      break;
  }
}

MORE INFORMATION

  • Refer to SVC in the Assembler User Guide.
  • Refer to __svc in the Compiler User Guide.
  • Refer to SVC Functions in the CMSIS RTOS User Guide.

SEE ALSO

Last Reviewed: Thursday, September 6, 2018


Did this article provide the answer you needed?
 
Yes
No
Not Sure
 
  Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings

Privacy Policy Update

Arm’s Privacy Policy has been updated. By continuing to use our site, you consent to Arm’s Privacy Policy. Please review our Privacy Policy to learn more about our collection, use and transfers
of your data.