Quality RTOS & Embedded Software


Memory Protection Unit (MPU) Support

On this page:


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 the end-user feedback. This section describes the changes necessary to upgrade to FreeRTOS V10.4.0 or newer, and prior to that, to upgrade to FreeRTOS V10.3.0 or newer.

Changes in FreeRTOS version 10.4.0:

  • FreeRTOS V10.4.0 introduced a new variable __privileged_functions_start__ to indicate the starting location of the privileged code. It needs to be exported from linker script in the same way as the pre-existing linker variables such as __privileged_functions_end__.

    If you get a linker error in an earlier created project for unresolved symbol __privileged_functions_start__, you need to export a variable __privileged_functions_start__ with the value equal to __FLASH_segment_start__.

  • Heap memory is now placed in the privileged section, and as a result, unprivileged tasks cannot call pvPortMalloc() or vPortFree().

  • xTaskCreate() can no longer be used to create an unprivileged task. Use xTaskCreateRestricted() instead.

  • FreeRTOS-MPU ports for ARM Cortex-M4 microcontrollers now support microcontrollers with 16 MPU regions. To use 16 regions set configTOTAL_MPU_REGIONS to 16 in FreeRTOSConfig.h.

  • Application writers can now override the default values for the for TEX, Shareable (S), Cacheable (C) and Bufferable (B) bits for MPU regions covering Flash and RAM by defining configTEX_S_C_B_FLASH and configTEX_S_C_B_SRAM respectively in FreeRTOSConfig.h.

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:
  1. 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.

  2. 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.