Keil Logo

ARM: How to Write an SVC Function


Information in this knowledgebase article applies to:

  • Keil MDK
  • Arm Compiler 5 and Arm Compiler 6

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 Keil RTX 4 or 5.

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 SVC 0. But also have in mind, that other software components in the project may use this already. Select another parameter value then.

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

void __svc( 0 ) EnablePrivilegedMode( void ) ;

With Arm Compiler 6: 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 Keil RTX, then the RTX already implements its own SVC handler, that also needs to remain in the image. See the RTX 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 Arm Compiler 5:

__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 Arm Compiler 6:

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 Keil RTX4 User Guide.
  • Refer to SVC Functions in the Keil RTX5 User Guide.

SEE ALSO

Last Reviewed: Thursday, November 12, 2020


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.