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 by 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. 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, 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.6.2 or newer,
and prior to that, to upgrade to FreeRTOS V10.6.1, V10.6.0, V10.5.0, V10.4.6, V10.4.0, and V10.3.0 or newer.
Changes in FreeRTOS version 10.6.2:
FreeRTOS V10.6.2 makes the following improvements to the new MPU wrapper (mpu_wrappers_v2.c) introduced in version 10.6.0:
- Introduce an Access Control List (ACL) feature to allow the application writer to control an unprivileged task’s access to kernel objects.
- Update the system call entry mechanism to only require one Supervisor Call (SVC) instruction.
- Wrap parameters for system calls with more than four parameters in a struct to avoid special handling during system call entry.
- Fix 2 possible integer overflows.
- Convert some asserts to run time checks.
Changes in FreeRTOS version 10.6.1:
FreeRTOS V10.6.1 introduces runtime parameter checks to functions in the mpu_wrappers_v2.c file. The same checks are already performed
in API implementations using asserts.
We thank the following people for their inputs in these changes:
- Lan Luo, Zixia Liu of School of Computer Science and Technology, Anhui University of Technology, China.
- Xinwen Fu of Department of Computer Science, University of Massachusetts Lowell, USA.
- Xinhui Shao, Yumeng Wei, Huaiyu Yan, Zhen Ling of School of Computer Science and Engineering, Southeast University, China.
Changes in FreeRTOS version 10.6.0:
FreeRTOS V10.6.0 introduces a new MPU wrapper that places additional restrictions on unprivileged tasks. The following is the list of changes
introduced with the new MPU wrapper:
- Opaque and indirectly verifiable integers for kernel object handles: All the kernel object handles (for example, queue handles) are now
opaque integers. Previously object handles were raw pointers.
- The task context is saved in the Task Control Block (TCB): When a task is swapped out by the scheduler, the task's context is now saved in its TCB.
Previously the task’s context was saved on its stack.
- System calls execute on a separate, privileged only stack: FreeRTOS system calls, which execute with elevated privilege, now use a separate,
privileged only stack. Previously system calls used the calling task's stack.
- Memory bounds checks: FreeRTOS system calls which accept a pointer and de-reference it, now verify that the calling task has required permissions
to access the memory location referenced by the pointer.
- System calls restrictions: The following system calls are no longer available to unprivileged tasks:
- vQueueDelete
- xQueueCreateMutex
- xQueueCreateMutexStatic
- xQueueCreateCountingSemaphore
- xQueueCreateCountingSemaphoreStatic
- xQueueGenericCreate
- xQueueGenericCreateStatic
- xQueueCreateSet
- xQueueRemoveFromSet
- xQueueGenericReset
- xTaskCreate
- xTaskCreateStatic
- vTaskDelete
- vTaskPrioritySet
- vTaskSuspendAll
- xTaskResumeAll
- xTaskGetHandle
- xTaskCallApplicationTaskHook
- vTaskList
- vTaskGetRunTimeStats
- xTaskCatchUpTicks
- xEventGroupCreate
- xEventGroupCreateStatic
- vEventGroupDelete
- xStreamBufferGenericCreate
- xStreamBufferGenericCreateStatic
- vStreamBufferDelete
- xStreamBufferReset
Also, an unprivileged task can no longer use vTaskSuspend to suspend any task other than itself.
We thank the following people for their inputs in these enhancements:
- David Reiss of Meta Platforms, Inc.
- Lan Luo, Xinhui Shao, Yumeng Wei, Zixia Liu, Huaiyu Yan, and Zhen Ling of the School of Computer Science and Engineering, Southeast
University, China.
- Xinwen Fu of the Department of Computer Science, University of Massachusetts Lowell, USA.
- Yueqi Chen, Zicheng Wang, Minghao Lin, and Jiahe Wang of the University of Colorado Boulder, USA.
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.