Understanding Access Levels – ARM Cortex-M
This blog post focuses on explaining the privileged and unprivileged access levels available in ARM Cortex-M core. With an assembly code, it demonstrates the use of these access levels and explains how to switch between them.
Introduction
ARM Cortex-M core provides a number of features to support RTOS based systems. One of such features is separation of code execution into Privileged and Unprivileged Access Levels. In this post, let’s go little deeper into ARM Cortex-M access levels.
What Are Access Levels?
ARM Cortex-M provides two different code execution levels called access levels – Privileged and Unprivileged. The unprivileged access level restricts access to some system registers and protected memory regions. The processor either ignores or generates a fault condition for any attempt to access these restricted resources from unprivileged code. The privileged access level can access all resources and memory regions in the processor.
Operating Modes
ARM Cortex-M also provides two different modes of operation - Thread mode and Handler mode. When the processor is executing an exception handler, it is in Handler mode. In all other cases it is in Thread mode. Handler mode always uses privileged access level. Thus the handler code can manipulate system resources and protected memory regions. Application code uses the Thread mode. It can use either privileged or unprivileged access level. Thread mode with privileged access level is the default when processor starts up. Most applications do not need strong separation between user tasks and system tasks.
Access levels are important to sandbox application tasks and system tasks. Classic example for such systems is an RTOS based system. Systems without RTOS can also benefit by restricting access to critical code sections.
Consider an embedded system which stores some credentials in protected part of memory. The system designer may put these credentials in protected part of memory. Thus code running in unprivileged level won’t be able to gain access to the credentials.
As another example, a system may be using SysTick timer to carry out some periodic task. To change the periodicity of the timer at run time, you need to manipulate SysTick control registers. An error in application code may manipulate these control registers.
A typical RTOS uses SysTick timer for context switching. This may affect real time performance of the system or lead to a system crash. For more details about SysTick timer in ARM Cortex-M, refer to this post.
Another real world example is the FreeRTOS-MPU port of FreeRTOS. It uses MPU and privileged access levels to protect certain regions of memory. Tho operation of MPU is closely related to access levels, I will not discuss it here. It deserves another blog post.
Controlling Access Level
Let’s explore the resources and instructions involved in using the access levels. The CONTROL
register in ARM Cortex-M core controls privilege level for code execution.
As shown in image below, when bit 0 is at logic 1, it indicates unprivileged level. When this bit is logic 0, it indicates privileged level.
At power on, this register is 0 which means the processor starts in privileged level and thread mode. Setting the nPRIV bit to 1 changes the access level to unprivileged. Once the processor is in unprivileged mode, clearing this bit has no effect in thread mode. Only the handler mode can change the access level from unprivileged to privileged.
How to Use Access Levels?
Let’s understand all the stuff about access levels by trying out some code execution. As usual we will use the STM32F4 Discovery board and the demo code is provided on GitHub. Here is a brief explanation of important parts of the code.
Towards top of the file main.s, the statement GBLL USE_SVC
declares an assembly variable. GBLL
is an assembly directive which declares a Global Logical variable, which means that the variable can be set to either True or False. Next, the SETL {FALSE}
statement sets this variable _USE_SVC_
to False. We will see later in this post how this variable is used in the code.
|
|
As mentioned earlier, the processor starts in privileged access level. This makes it easier for simple applications as you don’t need to switch to privileged level. But, switching from unprivileged to privileged level is not straightforward. We will shortly explore this.
Switching to Unprivileged Access Level
|
|
In our code, the first two instructions MOV R0, #01
and MSR CONTROL, R0
set bit 0 of CONTROL
register. This switches the processor access level to unprivileged. The instruction MSR
is an instruction to write to the special registers. The image below shows access level switching from Privileged to Unprivileged.
Switching Back to Privileged Access Level via Control register
It is not possible to switch back to privileged level except from an exception handler. The processor ignores the code below but it does not lead to any fault condition though.
|
|
Correct way to switch back to privileged access level is via an exception handler. We will see it later in this post.
Access to Special Registers Ignored
Let’s try to access some special registers in unprivileged level.
|
|
In unprivileged access level, the processor ignores attempt to write to special registers such as BASEPRI, PRIMSK
and FAULTMASK
. In our code shown above, we try to write 0x01 to these registers using MSR
instruction. But the registers remain at their original value 0x00 as you can see in the image below. Also CPS
instruction to enable / disable faults and interrupts do not have any effect on these registers in unprivileged access level. I will avoid details of CPS
instruction for brevity.
Access to Special Registers Leading to Fault Condition
Let’s try to manipulate the SysTick timer registers.
|
|
In the code section above, the instruction STR R2, [R1]
attempts to write to SysTick reload register. This results in a fault condition because of the access from unprivileged level. The fault is actually a bus fault but it leads to hard fault handler as the bus fault is disabled by default.
The Hard fault handler switches on LED connected to PD13 on STM32F4 to show system fault. It then stays in an infinite loop. Only a system reset can take it out of fault condition. There are other ways to exit the fault handler and resume executing normal code though.
If we try to access SysTick control register, it will lead to a fault as well. In fact as the processor is in fault already it does not reach the code to change SysTick Control register.
|
|
If you would like to know details about SysTick operation, please refer this post
Switching Back to Privileged Access Level via Exception Handler
Recall that during an exception handler, processor is always in Privileged access level.
One way to access special registers is via the exception handlers. This will keep the application code always in unprivileged level. When needed, the application can access services of the OS to manipulate special registers or protected memory. This is the scheme used with SVC exception mechanism as demonstrated in this post.
The other way is to switch the access level to privileged from an exception handler itself. This involves writing to the CONTROL register from within an exception handler. This will switch the processor to privileged level for the rest of the application code. The code presented in this blog post uses this method. To see this in action, you will need to recompile the code with the USE_SVC variable set to True.
|
|
After you rebuild the code, download it to the board and enter debug mode.
Call for Service
When this USE_SVC is True, the code executes SVC instruction with number 0x05 as shown below. If you are not familiar with SVC, this post will help you.
|
|
Executing an SVC instruction triggers the exception handler.
|
|
Since the SVC number in our case is 0x05, it goes on to call function Service_Call_5.
|
|
The service function switches code execution level to privileged by clearing nPRIV bit of CONTROL register. Even after the processor exits exception handler, the access level stays as Privileged. You can step through the code and see for yourself that special registers get written to and read from. Likewise, SysTick registers manipulation does not lead to any fault condition.
Finally the SysTick timer handler code should start blinking LED on the STM32F4 Discovery board.
Is that all?
Restriction in unprivileged access level is not limited to the registers discussed here. The NVIC and System Control Block also has limited access in unprivileged access level. Besides, when you protect certain parts of memory by using the MPU, only privileged code can access the protected memory region. Moreover, the LDR
and STR
instructions can execute at unprivileged level if added with a suffix T (For e.g. LDRT, STRT etc.). You can refer to the Cortex-M4 Devices Generic Users Guide for more details.
Access Levels in Action
Here is a demo of the code execution in both unprivileged and privileged access levels.
Conclusion
Privileged and Unprivileged software execution levels is an important feature in ARM Cortex-M. The separation of access levels along with SVC mechanism makes ARM Cortex-M a powerful candidate for RTOS and other complex systems.
I hope you got a good idea of how the access levels work in the ARM Cortex-M processor. I encourage you to explore it further and try your hands on assembly programming as well. Have fun!