embOS MPU add-on
Example application
Suitable for any safety-critical application, the embOS MPU add-on is available for any MCU containing a hardware MPU or MMU.
Example application
The MPU add-on for embOS-Classic and embOS-Ultra offers simple and straightforward runtime configuration that is easy to integrate into both new and existing products. It provides an unlimited number of privileged and unprivileged tasks 100 % sandboxed.
The following example details how easy it is to use this feature:
#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 example application simulates a stack overflow and shows how the issue is handled by the MPU add-on. Easy to use, embOS-MPU needs just three additional API calls:
- 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 the MPU add-on detects an issue.
- OS_MPU_Enable() actually enables the memory protection unit.
The example application creates two tasks:
- The HPTaskruns completely in a privileged state.
- TheLPTask also starts in a 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);
}The MPU add-on 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]);
}