You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
736 lines
46 KiB
736 lines
46 KiB
.. _arm_cortex_m_developer_guide: |
|
|
|
Arm Cortex-M Developer Guide |
|
############################ |
|
|
|
Overview |
|
******** |
|
|
|
This page contains detailed information about the status of the Arm Cortex-M |
|
architecture porting in the Zephyr RTOS and describes key aspects when |
|
developing Zephyr applications for Arm Cortex-M-based platforms. |
|
|
|
Key supported features |
|
********************** |
|
|
|
The table below summarizes the status of key OS features in the different |
|
Arm Cortex-M implementation variants. |
|
|
|
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | | **Processor families** | | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Architecture variant | | Arm v6-M | Arm v7-M | Arm v8-M | Arm v8.1-M | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | | **M0/M1** | **M0+** | **M3** | **M4** | **M7** | **M23** | **M33** | **M55** | **M85** | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| **OS Features** | | | | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Programmable fault | | | | | | | | | | | |
|
| IRQ priorities | | Y | N | Y | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Single-thread kernel support | | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Thread local storage support | | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| **Interrupt handling** | | | | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | Regular interrupts | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | Dynamic interrupts | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | Direct interrupts | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | Zero Latency interrupts | N | N | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| CPU idling | | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Native system timer (SysTick) | | N [#f1]_ | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| **Memory protection** | | | | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | User mode | N | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | HW stack protection (MPU) | N | N | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | HW-assisted stack limit checking | N | N | N | N | N |Y [#f2]_ | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | Privileged Execute Never [#f3]_ | N | N | N | N | N | N | N | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| HW-assisted Control | | | | | | | | | | | |
|
| Flow integrity | PACBTI | N | N | N | N | N | N | N | N | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| HW-assisted null-pointer | | | | | | | | | | | |
|
| dereference detection | | N | N | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| HW-assisted atomic operations | | N | N | Y | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
|Support for non-cacheable regions| | N | N | Y | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Execute SRAM functions | | N | N | Y | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Floating Point Services | | N | N | N | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| DSP ISA | | N | N | N | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| **Trusted-Execution** | | | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | Native TrustZone-M support | N | N | N | N | N | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| | TF-M integration | N | N | N | N | N | N | Y | N | N | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| Code relocation | | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| SW-based vector table relaying | | Y | Y | Y | Y | Y | Y | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
| HW-assisted timing functions | | N | N | Y | Y | Y | N | Y | Y | Y | |
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+------------+ |
|
|
|
Notes |
|
===== |
|
|
|
.. [#f1] SysTick is optional in Cortex-M1 |
|
.. [#f2] Stack limit checking only in Secure builds in Cortex-M23 |
|
.. [#f3] https://developer.arm.com/documentation/107655/100/RTOS-and-Secure-software-design-considerations/Secure-software-development-design-considerations/Security-and-privilege-combination/Using-PXN-bit?lang=en |
|
|
|
OS features |
|
*********** |
|
|
|
Threads |
|
======= |
|
|
|
Thread stack alignment |
|
---------------------- |
|
|
|
Each Zephyr thread is defined with its own stack memory. By default, Cortex-M enforces a double word thread stack alignment, see |
|
:kconfig:option:`CONFIG_STACK_ALIGN_DOUBLE_WORD`. If MPU-based HW-assisted stack overflow detection (:kconfig:option:`CONFIG_MPU_STACK_GUARD`) |
|
is enabled, thread stacks need to be aligned with a larger value, reflected by :kconfig:option:`CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE`. |
|
In Arm v6-M and Arm v7-M architecture variants, thread stacks are additionally required to align with a value equal to their size, |
|
in applications that need to support user mode (:kconfig:option:`CONFIG_USERSPACE`). The thread stack sizes in that case need to be a power |
|
of two. This is all reflected by :kconfig:option:`CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT`, that is enforced in Arm v6-M and Arm v7-M |
|
builds with user mode support. |
|
|
|
Stack pointers |
|
-------------- |
|
|
|
While executing in thread mode the processor is using the Process Stack Pointer (PSP). The processor uses the Main Stack Pointer (MSP) |
|
while executing in handler mode, that is, while servicing exceptions and HW interrupts. Using PSP in thread mode *facilitates thread |
|
stack pointer manipulation* during thread context switching, without affecting the current execution context flow in |
|
handler mode. |
|
|
|
In Arm Cortex-M builds a single interrupt stack memory is shared among exceptions and interrupts. The size of the interrupt stack needs |
|
to be selected taking into consideration nested interrupts, each pushing an additional stack frame. Developers can modify the interrupt |
|
stack size using :kconfig:option:`CONFIG_ISR_STACK_SIZE`. |
|
|
|
The interrupt stack is also used during early boot so the kernel can initialize the main thread's stack before switching to the main thread. |
|
|
|
Thread context switching |
|
======================== |
|
|
|
In Arm Cortex-M builds, the PendSV exception is used in order to trigger a context switch to a different thread. |
|
PendSV exception is always present in Cortex-M implementations. PendSV is configured with the lowest possible |
|
interrupt priority level, in all Cortex-M variants. The main reasons for that design are |
|
|
|
* to utilize the tail chaining feature of Cortex-M processors, and thus limit the number of context switch |
|
operations that occur. |
|
* to not impact the interrupt latency observed by HW interrupts. |
|
|
|
As a result, context switch in Cortex-M is non-atomic, i.e. it may be *preempted* by HW interrupts, |
|
however, a context-switch operation must be completed before a new thread context-switch may start. |
|
|
|
Typically a thread context-switch will perform the following operations |
|
|
|
* When switching-out the current thread, the processor stores |
|
|
|
* the callee-saved registers (R4 - R11) in the thread's container for callee-saved registers, |
|
which is located in kernel memory |
|
* the thread's current operation *mode* |
|
|
|
* user or privileged execution mode |
|
* presence of an active floating point context |
|
* the EXC_RETURN value of the current handler context (PendSV) |
|
|
|
* the floating point callee-saved registers (S16 - S31) in the thread's container for FP |
|
callee-saved registers, if the current thread has an active FP context |
|
* the PSP of the current thread which points to the beginning of the current thread's exception |
|
stack frame. The latter contains the caller-saved context and the return address of the switched-out |
|
thread. |
|
|
|
* When switching-in a new thread the processor |
|
|
|
* restores the new thread's callee-saved registers from the thread's |
|
container for callee-saved registers |
|
* restores the new thread's operation *mode* |
|
* restores the FP callee-saved registers if the switched-in thread had |
|
an active FP context before being switched-out |
|
* re-programs the dynamic MPU regions to allow a user thread access its stack and application |
|
memories, and/or programs a stack-overflow MPU guard at the bottom of the thread's |
|
privileged stack |
|
* restores the PSP for the incoming thread and re-programs the stack pointer limit |
|
register (if applicable, see :kconfig:option:`CONFIG_BUILTIN_STACK_GUARD`) |
|
* optionally does a stack limit checking for the switched-in thread, if |
|
sentinel-based stack limit checking is enabled (see :kconfig:option:`CONFIG_STACK_SENTINEL`). |
|
|
|
PendSV exception return sequence restores the new thread's caller-saved registers and the |
|
return address, as part of unstacking the exception stack frame. |
|
|
|
The implementation of the context-switch mechanism is present in |
|
:file:`arch/arm/core/cortex_m/swap_helper.S`. |
|
|
|
Stack limit checking (Arm v8-M) |
|
------------------------------- |
|
|
|
Armv8-M and Armv8.1-M variants support stack limit checking using the MSPLIM and PSPLIM |
|
core registers. The feature is enabled when :kconfig:option:`CONFIG_BUILTIN_STACK_GUARD` is set. |
|
When stack limit checking is enabled, both the thread's privileged or user stack, as well |
|
as the interrupt stack are guarded by PSPLIM and MSPLIM registers, respectively. MSPLIM is |
|
configured *once* during kernel boot, while PSLIM is re-programmed during every thread |
|
context-switch or during system calls, when the thread switches from using its default |
|
stack to using its privileged stack, and vice versa. PSPLIM re-programming |
|
|
|
* has a relatively low runtime overhead (programming is done with MSR instructions) |
|
* does not impact interrupt latency |
|
* does not require any memory areas to be reserved for stack guards |
|
* does not make use of MPU regions |
|
|
|
It is, therefore, considered as a lightweight but very efficient stack overflow |
|
detection mechanism in Cortex-M applications. |
|
|
|
Stack overflows trigger the dedicated UsageFault exception provided by Arm v8-M. |
|
|
|
Interrupt handling features |
|
=========================== |
|
|
|
This section describes certain aspects around exception and interrupt |
|
handling in Arm Cortex-M. |
|
|
|
Interrupt priority levels |
|
------------------------- |
|
|
|
The number of available (configurable) interrupt priority levels is |
|
determined by the number of implemented interrupt priority bits in |
|
NVIC; this needs to be described for each Cortex-M platform using |
|
DeviceTree: |
|
|
|
.. code-block:: devicetree |
|
|
|
&nvic { |
|
arm,num-irq-priority-bits = <#priority-bits>; |
|
}; |
|
|
|
|
|
Reserved priority levels |
|
------------------------ |
|
|
|
A number of interrupt priority levels are reserved for the OS. |
|
|
|
By design, system fault exceptions have the highest priority level. In |
|
*Baseline* Cortex-M, this is actually enforced by hardware, as HardFault |
|
is the only available processor fault exception, and its priority is |
|
higher than any configurable exception priority. |
|
|
|
In *Mainline* Cortex-M, the available fault exceptions (e.g. MemManageFault, |
|
UsageFault, etc.) are assigned the highest *configurable* priority level. |
|
(:kconfig:option:`CONFIG_CPU_CORTEX_M_HAS_PROGRAMMABLE_FAULT_PRIOS` signifies explicitly |
|
that the Cortex-M implementation supports configurable fault priorities.) |
|
|
|
This priority level is never shared with HW interrupts (an exception to |
|
this rule is described below). As a result, processor faults occurring in regular |
|
ISRs will be handled by the corresponding fault handler and will not escalate to |
|
a HardFault, *similar to processor faults occurring in thread mode*. |
|
|
|
SVC exception is normally configured with the highest configurable priority level |
|
(an exception to this rule will be described below). |
|
SVCs are used by the Zephyr kernel to dispatch system calls, trigger runtime |
|
system errors (e.g. Kernel oops or panic), or implement IRQ offloading. |
|
|
|
In Baseline Cortex-M the priority level of SVC may be shared with other exceptions |
|
or HW interrupts that are also given the highest configurable priority level (As a |
|
result of this, kernel runtime errors during interrupt handling will escalate to |
|
HardFault. Additional logic in the fault handling routines ensures that such |
|
runtime errors are detected successfully). |
|
|
|
In Mainline Cortex-M, however, the SVC priority level is *reserved*, thus normally it |
|
is only shared with the fault exceptions of configurable priority. This simplifies the |
|
fault handling routines in Mainline Cortex-M architecture, since runtime kernel errors |
|
are serviced by the SVC handler (i.e no HardFault escalation, even if the kernel errors |
|
occur in ISR context). |
|
|
|
HW interrupts in Mainline Cortex-M builds are allocated a priority level lower than the SVC. |
|
|
|
One exception to the above rules is when Zephyr applications support Zero Latency Interrupts |
|
(ZLIs). Such interrupts are designed to have a priority level higher than any HW or system |
|
interrupt. If the ZLI feature is enabled in Mainline Cortex-M builds (see |
|
:kconfig:option:`CONFIG_ZERO_LATENCY_IRQS`), then |
|
|
|
* ZLIs are assigned the highest configurable priority level |
|
* SVCs are assigned the second highest configurable priority level |
|
* Regular HW interrupts are assigned priority levels lower than SVC. |
|
|
|
The priority level configuration in Cortex-M is implemented in |
|
:file:`include/zephyr/arch/arm/cortex_m/exception.h`. |
|
|
|
Locking and unlocking IRQs |
|
-------------------------- |
|
|
|
In Baseline Cortex-M locking interrupts is implemented using the PRIMASK register. |
|
|
|
.. code-block:: c |
|
|
|
arch_irq_lock() |
|
|
|
will set the PRIMASK register to 1, eventually, masking all IRQs with configurable |
|
priority. While this fulfils the OS requirement of locking interrupts, the consequence |
|
is that kernel runtime errors (triggering SVCs) will escalate to HardFault. |
|
|
|
In Mainline Cortex-M locking interrupts is implemented using the BASEPRI register (Mainline |
|
Cortex-M builds select :kconfig:option:`CONFIG_CPU_CORTEX_M_HAS_BASEPRI` to signify that BASEPRI register is |
|
implemented.). By modifying BASEPRI (or BASEPRI_MAX) arch_irq_lock() masks all system and HW |
|
interrupts with the exception of |
|
|
|
* SVCs |
|
* processor faults |
|
* ZLIs |
|
|
|
This allows zero latency interrupts to be triggered inside OS critical sections. |
|
Additionally, this allows system (processor and kernel) faults to be handled by Zephyr |
|
in *exactly the same way*, regardless of whether IRQs have been locked or not when the |
|
error occurs. It also allows for system calls to be dispatched while IRQs are locked. |
|
|
|
.. note:: |
|
|
|
Mainline Cortex-M fault handling is designed and configured in a way that all processor |
|
and kernel faults are handled by the corresponding exception handlers and never result |
|
in HardFault escalation. In other words, a HardFault may only occur in Zephyr applications |
|
that have modified the default fault handling configurations. The main reason for this |
|
design was to reserve the HardFault exception for handling exceptional error conditions |
|
in safety critical applications. |
|
|
|
Dynamic direct interrupts |
|
------------------------- |
|
|
|
Cortex-M builds support the installation of direct interrupt service routines during |
|
runtime. Direct interrupts are designed for performance-critical interrupt |
|
handling and do not go through all of the common Zephyr interrupt handling |
|
code. |
|
|
|
Direct dynamic interrupts are enabled via switching on |
|
:kconfig:option:`CONFIG_DYNAMIC_DIRECT_INTERRUPTS`. |
|
|
|
Note that enabling direct dynamic interrupts requires enabling support for |
|
dynamic interrupts in the kernel, as well (see :kconfig:option:`CONFIG_DYNAMIC_INTERRUPTS`). |
|
|
|
Zero Latency interrupts |
|
----------------------- |
|
|
|
As described above, in Mainline Cortex-M applications, the Zephyr kernel reserves |
|
the highest configurable interrupt priority level for its own use (SVC). SVCs will |
|
not be masked by interrupt locking. Zero-latency interrupt can be used to set up |
|
an interrupt at the highest interrupt priority which will not be blocked by interrupt |
|
locking. To use the ZLI feature :kconfig:option:`CONFIG_ZERO_LATENCY_IRQS` needs to be enabled. |
|
|
|
Zero latency IRQs have minimal interrupt latency, as they will always preempt regular HW |
|
or system interrupts. |
|
|
|
Note, however, that since ZLI ISRs will run at a priority level higher than the kernel |
|
exceptions they **cannot use** any kernel functionality. Additionally, since the ZLI |
|
interrupt priority level is equal to processor fault priority level, faults occurring |
|
in ZLI ISRs will escalate to HardFault and will not be handled in the same way as regular |
|
processor faults. Developers need to be aware of this limitation. |
|
|
|
CPU Idling |
|
========== |
|
|
|
The Cortex-M architecture port implements both k_cpu_idle() |
|
and k_cpu_atomic_idle(). The implementation is present in |
|
:file:`arch/arm/core/cortex_m/cpu_idle.c`. |
|
|
|
In both implementations, the processor |
|
will attempt to put the core to low power mode. |
|
In k_cpu_idle() the processor ends up executing WFI (Wait For Interrupt) |
|
instruction, while in k_cpu_atomic_idle() the processor will |
|
execute a WFE (Wait For Event) instruction. |
|
|
|
When using the CPU idling API in Cortex-M it is important to note the |
|
following: |
|
|
|
* Both k_cpu_idle() and k_cpu_atomic_idle() are *assumed* to be invoked |
|
with interrupts locked. This is taken care of by the kernel if the APIs |
|
are called by the idle thread. |
|
* After waking up from low power mode, both functions will *restore* |
|
interrupts unconditionally, that is, regardless of the interrupt lock |
|
status before the CPU idle API was called. |
|
|
|
The Zephyr CPU Idling mechanism is detailed in :ref:`cpu_idle`. |
|
|
|
Memory protection features |
|
========================== |
|
|
|
This section describes certain aspects around memory protection features |
|
in Arm Cortex-M applications. |
|
|
|
User mode system calls |
|
---------------------- |
|
|
|
User mode is supported in Cortex-M platforms that implement the standard (Arm) MPU |
|
or a similar core peripheral logic for memory access policy configuration and |
|
control, such as the NXP MPU for Kinetis platforms. (Currently, |
|
:kconfig:option:`CONFIG_ARCH_HAS_USERSPACE` is selected if :kconfig:option:`CONFIG_ARM_MPU` is enabled |
|
by the user in the board default Kconfig settings). |
|
|
|
A thread performs a system call by triggering a (synchronous) SVC exception, where |
|
|
|
* up to 5 arguments are placed on registers R1 - R5 |
|
* system call ID is placed on register R6. |
|
|
|
The SVC Handler will branch to the system call preparation logic, which will perform |
|
the following operations |
|
|
|
* switch the thread's PSP to point to the beginning of the thread's privileged |
|
stack area, optionally reprogramming the PSPLIM if stack limit checking is enabled |
|
* modify CONTROL register to switch to privileged mode |
|
* modify the return address in the SVC exception stack frame, so that after exception |
|
return the system call dispatcher is executed (in thread privileged mode) |
|
|
|
Once the system call execution is completed the system call dispatcher will restore the |
|
user's original PSP and PSPLIM and switch the CONTROL register back to unprivileged mode |
|
before returning back to the caller of the system call. |
|
|
|
System calls execute in thread mode and can be preempted by interrupts at any time. A |
|
thread may also be context-switched-out while doing a system call; the system call will |
|
resume as soon as the thread is switched-in again. |
|
|
|
The system call dispatcher executes at SVC priority, therefore it cannot be preempted |
|
by HW interrupts (with the exception of ZLIs), which may observe some additional interrupt |
|
latency if they occur during a system call preparation. |
|
|
|
MPU-assisted stack overflow detection |
|
------------------------------------- |
|
|
|
Cortex-M platforms with MPU may enable :kconfig:option:`CONFIG_MPU_STACK_GUARD` to enable the MPU-based |
|
stack overflow detection mechanism. The following points need to be considered when enabling the |
|
MPU stack guards |
|
|
|
* stack overflows are triggering processor faults as soon as they occur |
|
* the mechanism is essential for detecting stack overflows in supervisor threads, or |
|
user threads in privileged mode; stack overflows in threads in user mode will always be |
|
detected regardless of :kconfig:option:`CONFIG_MPU_STACK_GUARD` being set. |
|
* stack overflows are always detected, however, the mechanism does not guarantee that |
|
no memory corruption occurs when supervisor threads overflow their stack memory |
|
* :kconfig:option:`CONFIG_MPU_STACK_GUARD` will normally reserve one MPU region for programming |
|
the stack guard (in certain Arm v8-M configurations with :kconfig:option:`CONFIG_MPU_GAP_FILLING` |
|
enabled 2 MPU regions are required to implement the guard feature) |
|
* MPU guards are re-programmed at every context-switch, adding a small overhead to the |
|
thread swap routine. Compared, however, to the :kconfig:option:`CONFIG_BUILTIN_STACK_GUARD` feature, |
|
no re-programming occurs during system calls. |
|
* When :kconfig:option:`CONFIG_HW_STACK_PROTECTION` is enabled on Arm v8-M platforms the native |
|
stack limit checking mechanism is used by default instead of the MPU-based stack overflow |
|
detection mechanism; users may override this setting by manually enabling :kconfig:option:`CONFIG_MPU_STACK_GUARD` |
|
in these scenarios. |
|
|
|
Pointer Authentication and Branch Target Identification (PACBTI) |
|
================================================================ |
|
|
|
The Armv8.1-M Pointer Authentication and Branch Target Identification (PACBTI) extension is an |
|
optional extension for the Armv8.1-M architecture profile and consists of the implementation of the |
|
following control-flow integrity approaches: |
|
* Return address signing and authentication (PAC-RET) as a mitigation for Return Oriented Programming (ROP) style attack. |
|
* BTI instruction placement (BTI) as a mitigation for Jump Oriented Programming (JOP) style attacks. |
|
|
|
When hardware support is present (e.g., Cortex-M85) and compiler support is available, PACBTI can be |
|
enabled at build time in Zephyr by selecting one of the below configs: |
|
|
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_STANDARD` |
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_PACRET` |
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_PACRET_LEAF` |
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_BTI` |
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_PACRET_BTI` |
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_PACRET_LEAF_BTI` |
|
- :kconfig:option:`CONFIG_ARMV8_1_M_PACBTI_NONE` |
|
|
|
The config options ensures that compiler flags enabling PACBTI instructions are added to the build, |
|
specifically: |
|
|
|
- ``-mbranch-protection=`` for GCC toolchains. |
|
|
|
**Limitations:** |
|
|
|
- Only builds targeting Armv8.1-M Mainline processors with PACBTI hardware support (e.g., |
|
Cortex-M85) are able to fully use this feature. |
|
- Zephyr’s integrated SDK currently includes GCC 12.2 which does not support PACBTI so external GCC |
|
toolchains (14.2 or later) must be used for PACBTI support. |
|
Refer [this](https://docs.zephyrproject.org/latest/develop/toolchains/index.html) on how to set up |
|
toolchains. |
|
|
|
For more information about PACBTI, refer to the official [Arm documentation](https://developer.arm.com/documentation/109576/latest/). |
|
|
|
.. _arm_cortex_m_mpu_considerations: |
|
|
|
Memory map and MPU considerations |
|
================================= |
|
|
|
Fixed MPU regions |
|
----------------- |
|
|
|
By default, when :kconfig:option:`CONFIG_ARM_MPU` is enabled a set of *fixed* MPU regions |
|
are programmed during system boot. |
|
|
|
* One MPU region programs the entire flash area as read-execute. |
|
User can override this setting by enabling :kconfig:option:`CONFIG_MPU_ALLOW_FLASH_WRITE`, |
|
which programs the flash with RWX permissions. If :kconfig:option:`CONFIG_USERSPACE` is |
|
enabled unprivileged access on the entire flash area is allowed. |
|
* One MPU region programs the entire SRAM area with privileged-only |
|
RW permissions. That is, an MPU region is utilized to disallow execute permissions on |
|
SRAM. (An exception to this setting is when :kconfig:option:`CONFIG_MPU_GAP_FILLING` is disabled (Arm v8-M only); |
|
in that case no SRAM MPU programming is done so the access is determined by the default |
|
Arm memory map policies, allowing for privileged-only RWX permissions on SRAM). |
|
* All the memory regions defined in the devicetree with the property |
|
``zephyr,memory-attr`` defining the MPU permissions for the memory region. |
|
See the next section for more details. |
|
|
|
The above MPU regions are defined in :file:`arch/arm/core/mpu/arm_mpu_regions.c`. |
|
Alternative MPU configurations are allowed by enabling :kconfig:option:`CONFIG_CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS`. |
|
When enabled, this option signifies that the Cortex-M SoC will define and |
|
configure its own fixed MPU regions in the SoC definition. |
|
|
|
Fixed MPU regions defined in devicetree |
|
--------------------------------------- |
|
|
|
When the property ``zephyr,memory-attr`` is present in a memory node, a new MPU |
|
region will be allocated and programmed during system boot. When used with the |
|
:dtcompatible:`zephyr,memory-region` devicetree compatible, it will result in a |
|
linker section being generated associated to that MPU region. |
|
|
|
For example, to define a new non-cacheable memory region in devicetree: |
|
|
|
.. code-block:: devicetree |
|
|
|
sram_no_cache: memory@20300000 { |
|
compatible = "zephyr,memory-region", "mmio-sram"; |
|
reg = <0x20300000 0x100000>; |
|
zephyr,memory-region = "SRAM_NO_CACHE"; |
|
zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE) )>; |
|
}; |
|
|
|
This will automatically create a new MPU entry in with the correct name, base, |
|
size and attributes gathered directly from the devicetree. See :ref:`cache_guide` |
|
and :ref:`mem_mgmt_api` for more details. |
|
|
|
Static MPU regions |
|
------------------ |
|
|
|
Additional *static* MPU regions may be programmed once during system boot. These regions |
|
are required to enable certain features. See :ref:`cache_guide` for more details. |
|
|
|
* a RX region to allow execution from SRAM, when :kconfig:option:`CONFIG_ARCH_HAS_RAMFUNC_SUPPORT` is |
|
enabled and users have defined functions to execute from SRAM. |
|
* a RX region for relocating text sections to SRAM, when :kconfig:option:`CONFIG_CODE_DATA_RELOCATION_SRAM` is enabled |
|
* a ``nocache`` region to allow for a non-cacheable SRAM area, when :kconfig:option:`CONFIG_NOCACHE_MEMORY` is enabled |
|
* a possibly unprivileged RW region for GCOV code coverage accounting area, when :kconfig:option:`CONFIG_COVERAGE_GCOV` is enabled |
|
* a no-access region to implement null pointer dereference detection, when :kconfig:option:`CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU` is enabled |
|
|
|
The boundaries of these static MPU regions are derived from symbols exposed by the linker, in |
|
:file:`include/linker/linker-defs.h`. |
|
|
|
Dynamic MPU regions |
|
------------------- |
|
|
|
Certain thread-specific MPU regions may be re-programmed dynamically, at each thread context switch: |
|
|
|
* an unprivileged RW region for the current thread's stack area (for user threads) |
|
* a read-only region for the MPU stack guard |
|
* unprivileged RW regions for the partitions of the current thread's application memory |
|
domain. |
|
|
|
|
|
Considerations |
|
-------------- |
|
|
|
The number of available MPU regions for a Cortex-M platform is a limited resource. |
|
Most platforms have 8 MPU regions, while some Cortex-M33 or Cortex-M7 platforms may |
|
have up to 16 MPU regions. Therefore there is a relatively strict limitation on how |
|
many fixed, static and dynamic MPU regions may be programmed simultaneously. For platforms |
|
with 8 available MPU regions it might not be possible to enable all the aforementioned |
|
features that require MPU region programming. In most practical applications, however, |
|
only a certain set of features is required and 8 MPU regions are, in many cases, sufficient. |
|
|
|
In Arm v8-M processors the MPU architecture does not allow programmed MPU regions to |
|
overlap. :kconfig:option:`CONFIG_MPU_GAP_FILLING` controls whether the fixed MPU region |
|
covering the entire SRAM is programmed. When it does, a full SRAM area partitioning |
|
is required, in order to program the static and the dynamic MPU regions. This increases |
|
the total number of required MPU regions. When :kconfig:option:`CONFIG_MPU_GAP_FILLING` is not |
|
enabled the fixed MPU region covering the entire SRAM is not programmed, thus, the static |
|
and dynamic regions are simply programmed on top of the always-existing background region |
|
(full-SRAM partitioning is not required). |
|
Note, however, that the background SRAM region allows execution from SRAM, so when |
|
:kconfig:option:`CONFIG_MPU_GAP_FILLING` is not set Zephyr is not protected against attacks |
|
that attempt to execute malicious code from SRAM. |
|
|
|
|
|
Floating point Services |
|
======================= |
|
|
|
Both unshared and shared FP registers mode are supported in Cortex-M (see |
|
:ref:`float_v2` for more details). |
|
|
|
When FPU support is enabled in the build |
|
(:kconfig:option:`CONFIG_FPU` is enabled), the |
|
sharing FP registers mode (:kconfig:option:`CONFIG_FPU_SHARING`) |
|
is enabled by default. This is done as some compiler configurations |
|
may activate a floating point context by generating FP instructions |
|
for any thread, regardless of whether floating point calculations are |
|
performed, and that context must be preserved when switching such |
|
threads in and out. |
|
|
|
The developers can still disable the FP sharing mode in their |
|
application projects, and switch to Unshared FP registers mode, |
|
if it is guaranteed that the image code does not generate FP |
|
instructions outside the single thread context that is allowed |
|
(and supposed) to do so. |
|
|
|
Under FPU sharing mode, the callee-saved FPU registers are saved |
|
and restored in context-switch, if the corresponding threads have |
|
an active FP context. This adds some runtime overhead on the swap |
|
routine. In addition to the runtime overhead, the sharing FPU mode |
|
|
|
* requires additional memory for each thread to save the callee-saved |
|
FP registers |
|
* requires additional stack memory for each thread, to stack the caller-saved |
|
FP registers, upon exception entry, if an FP context is active. Note, however, |
|
that since lazy stacking is enabled, there is no runtime overhead of FP context |
|
stacking in regular interrupts (FP state preservation is only activated in the |
|
swap routine in PendSV interrupt). |
|
|
|
|
|
Misc |
|
**** |
|
|
|
Chain-loadable images |
|
===================== |
|
|
|
Cortex-M applications may either be standalone images or chain-loadable, for instance, |
|
by a bootloader. Application images chain-loadable by bootloaders (or other applications) |
|
normally occupy a specific area in the flash denoted as their *code partition*. |
|
:kconfig:option:`CONFIG_USE_DT_CODE_PARTITION` will ensure that a Zephyr chain-loadable image |
|
will be linked into its code partition, specified in DeviceTree. |
|
|
|
HW initialization at boot |
|
------------------------- |
|
|
|
In order to boot properly, chain-loaded applications may require that the core Arm |
|
hardware registers and peripherals are initialized in their reset values. Enabling |
|
:kconfig:option:`CONFIG_INIT_ARCH_HW_AT_BOOT` Zephyr to force the initialization of the |
|
internal Cortex-M architectural state during boot to the reset values as specified |
|
by the corresponding Arm architecture manual. |
|
|
|
Software vector relaying |
|
------------------------ |
|
|
|
In Cortex-M platforms that implement the VTOR register (see :kconfig:option:`CONFIG_CPU_CORTEX_M_HAS_VTOR`), |
|
chain-loadable images relocate the Cortex-M vector table by updating the VTOR register with the offset |
|
of the image vector table. |
|
|
|
Baseline Cortex-M platforms without VTOR register might not be able to relocate their |
|
vector table which remains at a fixed location. Therefore, a chain-loadable image will |
|
require an alternative way to route HW interrupts and system exceptions to its own vector |
|
table; this is achieved with software vector relaying. |
|
|
|
When a bootloader image enables :kconfig:option:`CONFIG_SW_VECTOR_RELAY` |
|
it is able to relay exceptions and interrupts based on a vector table |
|
pointer that is set by the chain-loadable application. The latter sets |
|
the :kconfig:option:`CONFIG_SW_VECTOR_RELAY_CLIENT` option to instruct the boot |
|
sequence to set the vector table pointer in SRAM so that the bootloader can |
|
forward the exceptions and interrupts to the chain-loadable image's software |
|
vector table. |
|
|
|
While this feature is intended for processors without VTOR register, it |
|
may also be used in Mainline Cortex-M platforms. |
|
|
|
Code relocation |
|
=============== |
|
|
|
Cortex-M support the code relocation feature. When |
|
:kconfig:option:`CONFIG_CODE_DATA_RELOCATION_SRAM` is selected, |
|
Zephyr will relocate .text, data and .bss sections |
|
from the specified files and place it in SRAM. It is |
|
possible to relocate only parts of the code sections |
|
into SRAM, without relocating the whole image text |
|
and data sections. More details on the code relocation |
|
feature can be found in :ref:`code_data_relocation`. |
|
|
|
|
|
Linking Cortex-M applications |
|
***************************** |
|
|
|
Most Cortex-M platforms make use of the default Cortex-M |
|
GCC linker script in :file:`include/zephyr/arch/arm/cortex_m/scripts/linker.ld`, |
|
although it is possible for platforms to use a custom linker |
|
script as well. |
|
|
|
|
|
CMSIS |
|
***** |
|
|
|
Cortex-M CMSIS headers are provided through standalone module repositories: |
|
|
|
- **CMSIS 5**: `zephyrproject-rtos/cmsis <https://github.com/zephyrproject-rtos/cmsis>`_ |
|
- **CMSIS 6**: `zephyrproject-rtos/CMSIS_6 <https://github.com/zephyrproject-rtos/CMSIS_6>`_ |
|
|
|
Zephyr has begun transitioning to **CMSIS 6** as the default source for Cortex-M core headers. |
|
However, at present, Zephyr includes headers from **both** the CMSIS 6 and legacy CMSIS 5 modules. |
|
|
|
The legacy CMSIS 5 headers remain available primarily for compatibility with vendor HALs, while all |
|
new architecture-level development should use **CMSIS 6** headers whenever possible. |
|
|
|
:kconfig:option:`CONFIG_CPU_CORTEX_M` selects :kconfig:option:`CONFIG_HAS_CMSIS_CORE` to signify that |
|
CMSIS headers are available for all supported Cortex-M variants. |
|
|
|
Testing |
|
******* |
|
|
|
A list of unit tests for the Cortex-M porting and miscellaneous features |
|
is present in :file:`tests/arch/arm/`. The tests suites are continuously |
|
extended and new test suites are added, in an effort to increase the coverage |
|
of the Cortex-M architecture support in Zephyr. |
|
|
|
QEMU |
|
**** |
|
|
|
We use QEMU to verify the implemented features of the Cortex-M architecture port in Zephyr. |
|
Adequate coverage is achieved by defining and utilizing a list of QEMU targets, |
|
each with a specific architecture variant and Arm peripheral support list. |
|
|
|
The table below lists the QEMU platform targets defined in Zephyr |
|
along with the corresponding Cortex-M implementation variant and the peripherals |
|
these targets emulate. |
|
|
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| | **QEMU target** | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| Architecture variant | Arm v6-M | Arm v7-M | Arm v8-M | Arm v8.1-M | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| | **qemu_cortex_m0** | **qemu_cortex_m3** | **mps2/an385** | **mps2/an521/cpu0** | **mps3/corstone300/an547** | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| **Emulated features** | | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| NVIC | Y | Y | Y | Y | Y | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| BASEPRI | N | Y | Y | Y | Y | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| SysTick | N | Y | Y | Y | Y | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| MPU | N | N | Y | Y | Y | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| FPU | N | N | N | Y | N | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| SPLIM | N | N | N | Y | Y | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
| TrustZone-M | N | N | N | Y | N | |
|
+---------------------------------+--------------------+--------------------+----------------+----------------------+----------------------------+ |
|
|
|
Maintainers & Collaborators |
|
*************************** |
|
|
|
The status of the Arm Cortex-M architecture port in Zephyr is: *maintained*. |
|
The updated list of maintainers and collaborators for Cortex-M can be found |
|
in :file:`MAINTAINERS.yml`.
|
|
|