Skip navigation

embOS-MPU—Comprehensive memory protection

  • Suitable for any safety-critical application
  • Available for any MCU containing a hardware MPU or MMU
  • Unlimited number of privileged und unprivileged tasks possible
  • Unprivileged tasks 100% Sandboxed
  • Simple and straightforward runtime configuration
  • Easy to integrate in both new and existing products

The following example shows the ease of use of embOS-MPU.

Sample Application

#include "RTOS.h"
#include "BSP.h"
#include <stdio.h>

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
//
// Linker symbols to get section addresses.
// embOS-MPU needs to know where RAM, ROM and the OS section is located.
//
extern unsigned int __FLASH_segment_start__;
extern unsigned int __FLASH_segment_size__;
extern unsigned int __RAM_segment_start__;
extern unsigned int __RAM_segment_size__;
extern unsigned int __os_start__;
extern unsigned int __os_size__;
//
// Control structures for tasks and event object
//
static OS_EVENT HW_Event;
static OS_TASK TCBHP;
static OS_TASK TCBLP;
//
// Stack for privileged task.
// This stack has not to be aligned because it does not affect MPU settings
//
static OS_STACKPTR int StackHP[128];
//
// Stack for unprivileged task. This will be used for MPU settings and with Cortex-M it requires alignment.
// In most cases, the same power of 2 will work for both size and alignment
//
static OS_STACKPTR int StackLP[128] __attribute__ ((aligned (512)));
//
// List of allowed OS objects for the LPTask
//
static const OS_MPU_OBJ _aObjList[] = { {(OS_U32)&HW_Event, OS_MPU_OBJTYPE_EVENT},
                                        {(OS_U32)NULL,      OS_MPU_OBJTYPE_INVALID}};  // Last entry

/*********************************************************************
*
*       Local functions
*
**********************************************************************
*/

/*********************************************************************
*
*       _HPTask()
*
*  Function description
*    High priority task.
*    Periodically resumes the low priority task via an event object
*/
static void _HPTask(void) {
  while (1) {
    BSP_ToggleLED(1);
    OS_EVENT_Set(&HW_Event);  // Signal Event to wake LP task
    OS_Delay(200);
  }
}

/*********************************************************************
*
*       _Recursive()
*
*  Function description
*    This function produces a stack overflow.
*    embOS detects the illegal memory access and automatically suspends
*    the task. Additionally the error callback function is called.
*/
static void _Recursive(unsigned int i) {
  volatile int k;
  k = i + 1;
  _Recursive(k);
}

/*********************************************************************
*
*       _Unpriv()
*
*  Function description
*    Routine for demonstrating an illegal memory access after
*    toggling an LED for 4 seconds.
*/
static void _Unpriv(void) {
  unsigned int i;

  for (i = 0u; i < 20u; i++) {
    OS_EVENT_Wait(&HW_Event);
    BSP_SetLED(0);
    OS_Delay(20);
    BSP_ClrLED(0);
  }
  //
  // Produce illegal memory access
  //
  _Recursive(1u);
}

/*********************************************************************
*
*       _LPTask()
*
*  Function description
*    Low priority task.
*/
static void _LPTask(void) {
  OS_MPU_SetAllowedObjects(&TCBLP, _aObjList);
  OS_MPU_SwitchToUnprivState();
  _Unpriv();
}

/*********************************************************************
*
*       _ErrorCallback()
*
*  Function description
*    User callback function which is called when an unprivileged task
*    does something disallowed, e.g. tries to write to memory which
*    does not belong to the task
*/
static void _ErrorCallback(OS_TASK* pTask, OS_MPU_ERRORCODE ErrorCode) {
  static const char* _sErrTxt[] = {  "OS_MPU_ERROR_INVALID_REGION", "OS_MPU_ERROR_INVALID_OBJECT",
                                     "OS_MPU_ERROR_INVALID_API", "OS_MPU_ERROR_HARDFAULT",
                                     "OS_MPU_ERROR_MEMFAULT", "OS_MPU_ERROR_BUSFAULT",
                                     "OS_MPU_ERROR_USAGEFAULT", "OS_MPU_ERROR_SVC"};
  printf("Task with ID 0x%x has been stopped due to error %s\n", (OS_U32)pTask, _sErrTxt[ErrorCode]);
}

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       main()
*/
int main(void) {
  OS_InitKern();                /* Initialize OS                 */
  OS_InitHW();                  /* Initialize Hardware for OS    */
  BSP_Init();                   /* Initialize LED ports          */
  //
  // Setup memory information, must be done before first task is created
  //
  OS_MPU_ConfigMem((OS_U32)&__FLASH_segment_start__, (OS_U32)&__FLASH_segment_size__,
                   (OS_U32)&__RAM_segment_start__,   (OS_U32)&__RAM_segment_size__,
                   (OS_U32)&__os_start__,            (OS_U32)&__os_size__);
  //
  // Setup optionally error callback function
  //
  OS_MPU_SetErrorCallback(&_ErrorCallback);
  //
  // Enable embOS-MPU support
  //
  OS_MPU_Enable();
  OS_CREATETASK(&TCBHP, "HP Task", _HPTask, 100, StackHP);
  OS_CREATETASK(&TCBLP, "LP Task", _LPTask,  50, StackLP);
  OS_EVENT_Create(&HW_Event);
  OS_Start();                   /* Start multitasking            */
  return 0;
}

Detailed Description

The sample application simulates a stack overflow and shows how the issue is handled by embOS-MPU. embOS-MPU is easy to use. Just three additional API calls are necessary:
OS_MPU_ConfigMem() tells the OS where RAM, ROM and the OS is located in the memory.
OS_MPU_SetErrorCallback() sets an optional callback function which is called whenever embOS-MPU detects an issue.  
OS_MPU_Enable() actually enables the memory protection unit. 

The sample application creates two tasks:
The HPTask runs completely in privileged state.
The LPTask also starts in privileged state but switches to unprivileged state with OS_MPU_SwitchToUnprivState().

The HPTask toggles a LED and sends events to the LPTask.

/*********************************************************************
*
*       main()
*/
int main(void) {
  OS_InitKern();                /* Initialize OS                 */
  OS_InitHW();                  /* Initialize Hardware for OS    */
  BSP_Init();                   /* Initialize LED ports          */
  //
  // Setup memory information, must be done before first task is created
  //
  OS_MPU_ConfigMem((OS_U32)&__FLASH_segment_start__, (OS_U32)&__FLASH_segment_size__,
                   (OS_U32)&__RAM_segment_start__,   (OS_U32)&__RAM_segment_size__,
                   (OS_U32)&__os_start__,            (OS_U32)&__os_size__);
  //
  // Setup optionally error callback function
  //
  OS_MPU_SetErrorCallback(&_ErrorCallback);
  //
  // Enable embOS-MPU support
  //
  OS_MPU_Enable();
  OS_CREATETASK(&TCBHP, "HP Task", _HPTask, 100, StackHP);
  OS_CREATETASK(&TCBLP, "LP Task", _LPTask, 50, StackLP);
  OS_EVENT_Create(&HW_Event);
  OS_Start();                   /* Start multitasking           */
  return 0;
}

The HPTask toggles a LED and sends events to the LPTask.

/*********************************************************************
*
*       _HPTask()
*
*  Function description
*    High priority task.
*    Periodically resumes the low priority task via an event object
*/
static void _HPTask(void) {
  while (1) {
    BSP_ToggleLED(1);
    OS_EVENT_Set(&HW_Event); // Signal Event to wake LP task
    OS_Delay(200);
  }
}

The LPTask adds permission to the event object before it switches to the unprivileged state with OS_MPU_SwitchToUnprivState().

/*********************************************************************
*
*       _LPTask()
*
*  Function description
*    Low priority task.
*/
static void _LPTask(void) {
  OS_MPU_SetAllowedObjects(&TCBLP, _aObjList);
  OS_MPU_SwitchToUnprivState();
  _Unpriv();
}

The _Unpriv() function runs in unprivileged state and toggles another LED for four seconds before it calls the function _Recursive().

/*********************************************************************
*
*       _Unpriv()
*
*  Function description
*    Routine for demonstrating an illegal memory access after
*    toggling an LED for 4 seconds.
*/
static void _Unpriv(void) {
  unsigned int i;

  for (i = 0u; i < 20u; i++) {
    OS_EVENT_Wait(&HW_Event);
    BSP_SetLED(0);
    OS_Delay(20);
    BSP_ClrLED(0);
  }
  //
  // Produce illegal memory access
  //
  _Recursive(1u);
}

The function _Recursive()  generates the actual stack overflow.

/*********************************************************************
*
*       _Recursive()
*
*  Function description
*    This function produces a stack overflow.
*    embOS detects the illegal memory access and automatically suspends
*    the task. Additionally the error callback function is called.
*/
static void _Recursive(unsigned int i) {
  volatile int k;
  k = i + 1;
  _Recursive(k);
}

embOS-MPU will terminate the LPTask as soon as the task tries to write to memory outside its task stack. Additionally the error callback function is called. The error callback function prints a message with the task id and the error cause. The HPTask and the OS are not affected and still run.

/*********************************************************************
*
*       _ErrorCallback()
*
*  Function description
*    User callback function which is called when an unprivileged task
*    does something disallowed, e.g. tries to write to memory which
*    does not belong to the task
*/
static void _ErrorCallback(OS_TASK* pTask, OS_MPU_ERRORCODE ErrorCode) {
  static const char* _sErrTxt[] = {  "OS_MPU_ERROR_INVALID_REGION", "OS_MPU_ERROR_INVALID_OBJECT",
                                     "OS_MPU_ERROR_INVALID_API", "OS_MPU_ERROR_HARDFAULT",
                                     "OS_MPU_ERROR_MEMFAULT", "OS_MPU_ERROR_BUSFAULT",
                                     "OS_MPU_ERROR_USAGEFAULT", "OS_MPU_ERROR_SVC"};
  printf("Task with ID 0x%x has been stopped due to error %s\n", (OS_U32)pTask, _sErrTxt[ErrorCode]);
}