692 lines
41 KiB
ReStructuredText
692 lines
41 KiB
ReStructuredText
.. _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** |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| **OS Features** | | |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Programmable fault | | | | | | | | | |
|
|
| IRQ priorities | | Y | N | Y | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Single-thread kernel support | | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Thread local storage support | | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Interrupt handling | | |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | Regular interrupts | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | Dynamic interrupts | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | Direct interrupts | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | Zero Latency interrupts | N | N | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| CPU idling | | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Native system timer (SysTick) | | N [#f1]_ | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Memory protection | | |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | User mode | N | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | HW stack protection (MPU) | N | N | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | HW-assisted stack limit checking | N | N | N | N | N |Y [#f2]_ | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| HW-assisted null-pointer | | | | | | | | | |
|
|
| dereference detection | | N | N | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| HW-assisted atomic operations | | N | N | Y | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
|Support for non-cacheable regions| | N | N | Y | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Execute SRAM functions | | N | N | Y | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Floating Point Services | | N | N | N | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| DSP ISA | | N | N | N | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Trusted-Execution | |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | Native TrustZone-M support | N | N | N | N | N | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| | TF-M integration | N | N | N | N | N | N | Y | N |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| Code relocation | | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| SW-based vector table relaying | | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
| HW-assisted timing functions | | N | N | Y | Y | Y | N | Y | Y |
|
|
+---------------------------------+-----------------------------------+-----------------+---------+--------+-----------+--------+---------+------------+------------+
|
|
|
|
Notes
|
|
=====
|
|
|
|
.. [#f1] SysTick is optional in Cortex-M1
|
|
.. [#f2] Stack limit checking only in Secure builds in Cortex-M23
|
|
|
|
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 be 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/aarch32/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/arch/arm/aarch32/exc.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/aarch32/cpu_idle.S`.
|
|
|
|
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.
|
|
|
|
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 compatible
|
|
:dtcompatible:`zephyr,memory-region` and at least the property
|
|
``zephyr,memory-region-mpu`` defining the MPU permissions for the memory region.
|
|
See the next section for more details.
|
|
|
|
The above MPU regions are defined in :file:`soc/arm/common/cortex_m/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
|
|
---------------------------------------
|
|
|
|
The user can define memory regions to be allocated and created in the linker
|
|
script using nodes with the :dtcompatible:`zephyr,memory-region` devicetree
|
|
compatible. When the property ``zephyr,memory-region-mpu`` is present in such
|
|
a node, a new MPU region will be allocated and programmed during system
|
|
boot.
|
|
|
|
The property ``zephyr,memory-region-mpu`` is a string carrying the attributes
|
|
for the MPU region. It is converted to a C token for use defining the attributes
|
|
of the 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-region-mpu = "RAM_NOCACHE";
|
|
};
|
|
|
|
This will automatically create a new MPU entry in
|
|
:zephyr_file:`soc/arm/common/cortex_m/arm_mpu_regions.c` with the correct name, base,
|
|
size and attributes gathered directly from the devicetree. See
|
|
:zephyr_file:`include/zephyr/linker/devicetree_regions.h` 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
|
|
|
|
* 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 no-cache region to allow for a none-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/arch/arm/aarch32/cortex-m/scripts/linked.ld`,
|
|
although it is possible for platforms to use a custom linker
|
|
script as well.
|
|
|
|
|
|
CMSIS
|
|
*****
|
|
|
|
Cortex-M CMSIS headers are hosted in a standalone module repository:
|
|
`zephyrproject-rtos/cmsis <https://github.com/zephyrproject-rtos/cmsis>`_.
|
|
|
|
: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** | **mps3_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`.
|