High Security for FreeRTOS Applications
by Ralph Moore
April 2025
SecureSMX was recently released, to bring high security to IoT and embedded devices based upon Cortex-M MCUs with Memory Protection Units (MPUs). It is characterized by:
- Fully isolated partitions.
- Mission-critical code runs in highly-protected, privileged mode, with little modification.
- Vulnerable code runs isolated in unprivileged mode partitions, with some additional code.
- A compromised partition can be shut down and restarted while the system runs normally.
- Partition-only updates can be installed while the system runs normally.
- Numerous other system protections are available.
- Applicable to existing as well as to new applications.
- Support contracts are available.
SecureSMX makes full use of advanced SMX features that are missing in FreeRTOS and thus it cannot be ported to FreeRTOS. So we have taken the alternate approach of enabling FreeRTOS applications to be ported to SMX, via FRPort, where they can make use of the above SecureSMX security features.
FRPort
FRPort consists of porting functions such as the following, which map FreeRTOS services onto SMX services:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
if (smx_TaskCreate((FUN_PTR)pxTaskCode, uxPriority, usStackDepth*4, 0,
pcName, NULL, (TCB**)pxCreatedTask))
{
if (smx_TaskStart((TCB*)*pxCreatedTask, (u32)pvParameters))
return pdPASS;
}
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
This would be used as follows:
TaskHandle_t t2b;
if (xTaskCreateStatic(tt00_t2b, "t2b", 100, (void*)0x66, TP1, &t2b))
FreeRTOS, like most RTOSs, creates and starts a task in a single RTOS service. SMX creates a task in one RTOS service and starts it in another RTOS service, as shown above. The advantage of this is that it is possible to insert other RTOS services in between, such as:
ut2a = smx_TaskCreate(tm04_ut2a, TP2, 0, SMX_FL_UMODE, "ut2a");
mp_MPACreate(ut2a, (MPA*)&mpa_tmplt_ut2a, 0xF);
smx_TaskSet(ut2a, SMX_ST_UMODE, 1);
smx_TaskStart(ut2a);
In the above, a Memory Protection Array (MPA) is being created for ut2a and ut2a is being put into unprivileged mode (umode) before being started. A task's MPA is loaded into the MPU when the task is dispatched by the task scheduler.
In most cases, the FRPort function is quite simple:
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
return (BaseType_t)smx_MutexRel((MUCB_PTR)xMutex);
}
Interrupts
Handling interrupts is an important function in real-time systems. FreeRTOS, like many RTOSs, provides modified versions of some of its services so they can be called from Interrupt Service Routines (ISRs). The names of these services end in "fromISR". They do almost everything the normal services do, except task switching, which is done by the scheduler when all ISRs have finished running.
Apart from a few simple pipe calls, smx allows ISRs to only invoke Link Service Routines (LSRs). So, a FreeRTOS ISR such as:
SemaphoreHandle_t semA;
void exampleISR(void)
{
...
xSemaphoreGiveFromISR( semA, &xHigherPriorityTaskWoken );
...
}
must be modified to an ISR and LSR:
void exampleISR(void)
{
...
smx_LSR_INVOKE(semLSR, (u32)semA);
...
}
void semLSR(u32 par)
{
xSemaphoreGiveFromISR( (SemaphoreHandle_t)semA,
}
and the FRPort function is:
BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
BaseType_t * const pxHigherPriorityTaskWoken )
{
return((BaseType_t)smx_SemSignal((SCB_PTR)xQueue));
}
(Note: FreeRTOS maps xSemaphoreGiveFromISR() to xQueueGiveFromISR().)
ISRs should be as short as possible, with most processing deferred elsewhere. In the case of FreeRTOS, and most RTOSs, it is deferred to tasks. Doing so is often necessary to avoid missed interrupts during periods of high interrupt activity. Unfortunately, task switching takes time, and tasks are subject to preemptions and priority inversions. The result may unacceptable jitter in system outputs or other system operations. If so, the solution is usually to put all or most of the code back into the ISRs.
LSRs provide a better solution for deferred interrupt processing because they have very low overhead, run after all ISRs are done and before any tasks run, and they can call any no-wait smx services. So, in the above example, in addition to moving xSemaphoreGiveFromISR() into semLSR(), it is feasible to also move most of the other code from the ISR into it. The LSR can perform any urgent processing, then signal a task to perform lower-priority processing.
Safe Interrupt Processing
Large ISRs are unfortunate from a security standpoint because they provide large targets for hackers and adding fromISR RTOS services makes the targets even larger. This is especially serious because ISRs run in handler mode (hmode) and, if a hacker breaks into an ISR, “the system is his oyster” – i.e. he can do anything he wants!
The smx LSRs, called trusted LSRs (tLSRs), run in hmode and thus offer no better security than FreeRTOS ISRs. However, SecureSMX introduces a new type of LSR, called a safe LSR (sLSR), which runs in a umode partition and thus is "sandboxed". sLSRs have higher overhead than tLSRs, but still less than tasks and are not subject to task preemptions and priority inversions. Therefore jitter is still low. When security is beefed up, tLSRs can be converted to sLSRs that typically run in the same partitions as the tasks they serve. Thus the same partition limitations apply to them.
Control Blocks
FRPort converts FreeRTOS services into smx services, as shown above, but smx functions do not generate FreeRTOS control blocks -- they generate smx control blocks. Hence the FreeRTOS handle points to an smx control block, not to a FreeRTOS control block. In order to see the smx control block, do the following:
TimerHandle_t t1;
t1 = xTimerCreate("t1", 10, pdFALSE, (void*)1, (TimerCallbackFunction_t)ti00_LSR1);
TMRCB_PTR t1x = (TMRCB_PTR)t1;
Then t1x can be used to look at the smx control block. The control blocks differ, but the field names are similar, and it fairly easy to find desired information. During debugging, it is also possible to step into smx functions in order to see related smx control blocks.
It may seem a little awkward, at first, to use FreeRTOS functions with smx control blocks, but one gets used to it quickly and it gives a good introduction to smx.
Conclusion
This has been a very brief overview of FRPort. It may seem strange to run one RTOS's API upon another RTOS's engine, but it is possible. The payoff is to bring high-security and more powerful RTOS features to FreeRTOS applications, which otherwise would not be possible.
Ralph Moore is a graduate of Caltech. He and a partner started Micro Digital Inc. in 1975 as one of the first microprocessor design services. Now Ralph is primarily the Micro Digital RTOS innovator. His current focus is to improve the security of IoT and embedded devices through firmware partitioning. He believes that it is the most practical approach for achieving acceptable security for devices connected to networks.
Copyright © 2025 Micro Digital, Inc. All rights reserved.
smx is a registered trademark of Micro Digital Inc. smx product names are trademarks of Micro Digital, Inc.
|