Memory Protection Unit (MPU) Support
On this page:
Introduction
FreeRTOS provides official Memory Protection Unit (MPU) support on ARMv7-M (Cortex-M3, Cortex-M4 and Cortex-M7 microcontrollers) and ARMv8-M (Cortex-M23 and Cortex-M33 microcontroller) cores:
-
There are two FreeRTOS ports for ARMv7-M cores, one that includes MPU support and one that doesn't.
-
There is only one FreeRTOS port for ARMv8-M cores as
MPU support is a compile time option.
Third parties maintain MPU ports to other microcontroller cores.
MPU costs and benefits
FreeRTOS MPU ports enable microcontroller applications to be more robust and more secure by first enabling tasks to run in either privileged or unprivileged mode, and second restricting access to resources such as RAM, executable code, peripherals, and memory beyond the limit of a task's stack. Huge benefit is gained from, for example, preventing code from ever executing from RAM as doing so will protect against many attack vectors such as buffer overflow exploits or the execution of malicious code loaded into RAM.
Use of an MPU necessarily makes application design more complex because first the MPU's memory region restrictions must be determined and described to the RTOS, and second the MPU restricts what application tasks can and cannot do.
MPU strategies
Creating an application that constrains every task to its own memory region may be the most secure, but it is also the most complex to design and implement. Often it is best to use an MPU to create a pseudo process and thread model - where groups of threads are allowed to share a memory space. For example, create one memory space accessible to trusted first party code, and another that is only accessible to untrusted third party code.
Additional examples
The LPC17xx edition of the FreeRTOS eBook contains a chapter on using FreeRTOS-MPU, although the information it contains is a little out of date.
The Using FreeRTOS on ARMv8-M Microcontrollers
blog mentions how to use MPU on ARMv8-M microcontrollers.
The demo project in the FreeRTOS/Demo/CORTEX_MPU_Simulator_Keil_GCC directory, which uses Keil uVision
to build and simulate a GCC project, provides a worked example that does not require any particular hardware.
Additional FreeRTOS-MPU examples projects include the Nuvoton NuMaker-PFM-M2351 demo
and the NXP LPCXpresso55S69 demo demo, among others.
[The FreeRTOS-MPU demo projects located in the FreeRTOS/Demo/CORTEX_MPU_LPC1768_GCC_RedSuite and
FreeRTOS/Demo/CORTEX_MPU_LM3Sxxxx_Rowley directories were retired prior to the
release of FreeRTOS V9.0.0]
Upgrade Information
FreeRTOS MPU ports have been updated in response to end-user feedback. This section describes the changes necessary to upgrade to FreeRTOS V10.5.0 or newer, and prior to that, to upgrade to FreeRTOS V10.4.6, V10.4.0, and V10.3.0 or newer.
Changes in FreeRTOS version 10.5.0:
- The FreeRTOS ARMv7-M (ARM Cortex-M3/4/7) and ARMv8-M (ARM Cortex-M23/33/55) ports with memory protection unit (MPU) support can
no longer create privileged tasks from unprivileged tasks using xTaskCreate or xTaskCreateStatic APIs. Also, unprivileged tasks
can no longer call the following APIs:
- xTimerCreate
- xTimerCreateStatic
- xTimerPendFunctionCall
The application writer needs to perform these operations either before starting the scheduler or from a privileged task.
Changes in FreeRTOS version 10.4.6:
-
FreeRTOS-MPU ports for ARMv7-M MCUs (ARM Cortex-M3/4/7) now include a new configuration option
configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS
. Setting the constant to 0 in FreeRTOSConfig.h
prevents unprivileged application tasks from using the taskENTER_CRITICAL()
macro to
create a critical section. Set the constant to 1, or leave it undefined, to maintain compatibility
with previous FreeRTOS kernel versions, which allow both privileged and unprivileged tasks to create
critical sections. Note: it is recommended to define the constant to 0 for maximum security; because
of this, a compiler warning is output if the constant is left undefined.
Changes in FreeRTOS version 10.4.0:
Changes in FreeRTOS version 10.3.0:
-
It is now possible to prevent any privilege escalations originating from
outside of the kernel code (other than escalations performed by the hardware
itself when an interrupt is entered). To do so set
configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY to 1 in FreeRTOSConfig.h,
and define the linker variables __syscalls_flash_start__ and
__syscalls_flash_end__ to be the start and end addresses of the system calls memory
respectively.
FreeRTOS-MPU Specifics
FreeRTOS-MPU features
-
Compatible with the standard ARM Cortex-M3 and Cortex-M4F ports.
-
Tasks can be created to run in either Privileged mode or Unprivileged mode. Unprivileged
tasks can only access their own stack and up to three user definable memory regions (three per task).
User definable memory regions are assigned to tasks when the task is created, and can
be reconfigured at run time if required.
-
User definable memory regions can be parameterised individually.
For example, some regions may be set to read only, while others may be set to not executable
(execute never, or simply XN, in ARM terminology), etc.
-
No data memory is shared between Unprivileged tasks, but Unprivileged tasks can pass messages to
each other using the standard queue and semaphore mechanisms. Shared memory regions can be
explicitly created by using a user definable memory region but this is discouraged.
-
A Privileged mode task can set itself into Unprivileged mode, but once in Unprivileged mode it
cannot set itself back to Privileged mode.
-
The FreeRTOS API is located in a region of Flash that can only be accessed while the microcontroller
is in Privileged mode (calling an API function causes a temporary switch to Privilege mode).
-
The data maintained by the RTOS kernel (all non stack data that is private to the FreeRTOS source files)
is located in a region of RAM that can only be accessed while the microcontroller is in Privileged
mode.
-
System peripherals can only be accessed while the microcontroller is in Privileged mode. Standard
peripherals (UARTs, etc.) are accessible by any code but can be explicitly protected using a user
definable memory region.
Creating Tasks
FreeRTOS-MPU port can have two type of tasks:
-
Privileged Tasks: A privileged task has access to the entire memory map.
Privileged tasks can be created using either the xTaskCreate()
or xTaskCreateRestricted() API function.
-
Unprivileged Tasks: An unprivileged task only has access to its stack. In
addition, it can be granted access up to three user definable memory regions (three per
task). Unprivilged tasks can only be created using the
xTaskCreateRestricted() API.
Note that xTaskCreate() API must not be used to create an
unprivileged task.
If a task wants to use the MPU then the following additional information has to be provided:
- The address of the task stack.
- The start, size and access parameters for up to three user definable memory regions.
The total number of parameters required to create a task is therefore quite large. To make creating
MPU aware tasks easier FreeRTOS-MPU uses an API function called
xTaskCreateRestricted().
This allows all but one of the parameters to be defined in a const struct, with the struct being passed
(by reference) into xTaskCreateRestricted() as a single parameter.
A Privileged mode task can call
portSWITCH_TO_USER_MODE() to set
itself into Unprivileged mode. A task that is running in Unprivileged mode cannot set itself into
Privileged mode.
The memory regions allocated to a task can be changed using
vTaskAllocateMPURegions(). See the
xTaskCreateRestricted() and vTaskAllocateMPURegions() API documentation for more information.
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.