The SVC (Supervisor Call) instruction is an important part of the ARM instruction set architecture. It allows applications running in user mode to request privileged operations that require kernel access. The SVC instruction is used to perform system calls and other critical low-level functions needed by applications.
What is the SVC Instruction?
The SVC instruction causes an exception to occur, triggering a switch from user mode to privileged kernel mode in the processor. This grants access to protected system resources and features that are not available to unprivileged code. When the SVC executes, the processor:
- Saves the current program state (registers, program counter, etc.)
- Changes to a privileged execution mode
- Branches to a predefined kernel exception vector to handle the SVC
After handling the SVC, the kernel code returns back to where the SVC originated. The user state is restored and execution continues in user mode. This mechanism allows the operating system kernel to provide controlled access to system resources needed by applications.
SVC Instruction Syntax
Here is the assembly syntax for the SVC instruction on ARM processors: SVC #imm
Where #imm is an immediate value from 0 to 255 specifying the SVC number to trigger. This numeric code identifies the requested service and any parameters to pass to the kernel handler.
SVC Handler Overview
When an SVC exception occurs, the processor branches to the kernel’s pre-defined exception vector. This invokes the SVC handler function which does the following main steps:
- Save remaining user mode registers to the stack
- Identify the requested service based on the SVC number
- Retrieve any parameters from the user mode stack
- Perform the privileged operation
- Set return value(s) for the user application
- Restore user mode registers and return to the SVC caller
The kernel has full access to system resources while in this handler. After finishing the requested task, it returns to user space as if the SVC call were a normal function.
Example SVC Usage
Here is an example of how an application can use the SVC instruction to perform a system call on ARM: /* User mode code */ … int value; __asm volatile(“svc #0x1”); // SVC call número 1 …
When this SVC executes, the kernel handler function gets invoked. The handler checks the SVC number, sees this is service #1, and handles it accordingly: /* Kernel privileged mode */ void handle_svc(uint32_t svc_number) { switch(svc_number) { case 1: // Code for handling SVC 1 value = do_something_privileged(); break; default: break; } // Return to user mode }
This is a simple example of how the SVC provides access to privileged kernel operations. The kernel handler for each SVC number implements the required low-level service. Typical services include system calls like process creation/destruction, file manipulation, network operations, driver access, and more.
Commonly Used SVC Numbers
While the SVC number is arbitrary in principle, there are commonly used values for standard system calls on ARM systems:
- 0x00 – Unix-style system call convention
- 0x01 – Semihosting calls like debug print
- 0x02 – Reserved for ARM Linux
- 0x03 – ARM state management functions
Values from 0x04 to 0xFF are available for custom SVC implementations. For example, a real-time OS could use 0x4 for task management calls, 0x5 for timer services, etc.
SVC Implementation
There are a few ways the kernel can actually implement the SVC handler:
- Branch Table: A simple branch table indexed by the SVC number
- Individual Handlers: Dedicated handler function for each SVC number
- Central Dispatcher: Single dispatcher checks SVC number and calls handler
The branch table approach provides fastest dispatching by jumping directly to the correct handler based on the SVC number. However, it consumes more memory for the table. The other methods have slightly more overhead to identify the handler, but reduced memory usage.
Passing Parameters via SVC
The SVC instruction itself has no parameter passing built-in. Parameters can be passed in the following ways:
- Registers – Handler examines preset registers
- Stack – Values pushed onto stack before SVC
- Shared Memory – Global param region accessed by handler
The registers and stack approaches are most common. For example, R0-R3 could be designated to hold the first parameters for each SVC call. The handler would then access those registers for the values. For more than 4 parameters, the user code can push additional values to the stack prior to triggering the SVC.
SVC Usage in ARM Cortex-M
The SVC system is used similarly on Cortex-M microcontrollers. These devices also have privileged handler modes to implement SVCs securely. Usage is largely the same, with a few differences in the handlers:
- Handler resides in a separate privileged exception table
- Stacked registers are different on Cortex-M
- Programming is done in C instead of pure assembly
So while the SVC mechanisms are essentially equivalent, the handler implementation varies across ARM device classes. Refer to programming guides for details on integrating SVC into your particular system.
Typical Uses of the SVC Instruction
Here are some common examples of how the SVC instruction is utilized in ARM-based software:
- System Calls – Access kernel functions like process control, file I/O, network sockets, driver access, etc.
- Memory Allocation – Kernel manages memory segmentation, SVC used to request dynamic memory
- Power Management – Switch power states, enable/disable hardware resources
- Task Communication – Kernel events, semaphores, queues, mutexes, etc.
- Timekeeping – Access system timers, clocks, schedule events
SVCs provide user mode access to these and many other low-level functions needed by complex applications. Without it, user code would be too limited and unsafe running in unprivileged mode.
Summary
The SVC instruction is a crucial part of ARM’s execution environment. It enables applications to safely access privileged hardware resources and kernel services they otherwise would not have rights to use. SVCs make complex user programs for embedded devices possible while still protecting the core system components from damage or interference. Overall, they provide an essential bridge between unprivileged user mode and privileged kernel mode in ARM systems.