Skip to content

Commit

Permalink
Detect more startup config errors on Cortex M (FreeRTOS#832)
Browse files Browse the repository at this point in the history
Verify that the application has correctly installed PendSV
and SVCall handlers. The application can choose to
disable these checks by setting configCHECK_HANDLER_INSTALLATION
to 0 in their FreeRTOSConfig.h.
  • Loading branch information
jefftenney authored Dec 11, 2023
1 parent 553caa1 commit 30e6b8a
Show file tree
Hide file tree
Showing 35 changed files with 1,765 additions and 350 deletions.
11 changes: 11 additions & 0 deletions include/FreeRTOS.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,17 @@
#define configPRECONDITION_DEFINED 1
#endif

#ifndef configCHECK_HANDLER_INSTALLATION
#define configCHECK_HANDLER_INSTALLATION 1
#else

/* The application has explicitly defined configCHECK_HANDLER_INSTALLATION
* to 1. The checks requires configASSERT() to be defined. */
#if ( ( configCHECK_HANDLER_INSTALLATION == 1 ) && ( configASSERT_DEFINED == 0 ) )
#error You must define configASSERT() when configCHECK_HANDLER_INSTALLATION is 1.
#endif
#endif

#ifndef portMEMORY_BARRIER
#define portMEMORY_BARRIER()
#endif
Expand Down
71 changes: 56 additions & 15 deletions portable/ARMv8M/non_secure/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@
#endif
/*-----------------------------------------------------------*/

/**
* @brief Prototype of all Interrupt Service Routines (ISRs).
*/
typedef void ( * portISR_t )( void );
/*-----------------------------------------------------------*/

/**
* @brief Constants required to manipulate the NVIC.
*/
Expand All @@ -101,10 +107,18 @@
/**
* @brief Constants required to manipulate the SCB.
*/
#define portSCB_SYS_HANDLER_CTRL_STATE_REG ( *( volatile uint32_t * ) 0xe000ed24 )
#define portSCB_VTOR_REG ( *( ( portISR_t ** ) 0xe000ed08 ) )
#define portSCB_SYS_HANDLER_CTRL_STATE_REG ( *( ( volatile uint32_t * ) 0xe000ed24 ) )
#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL )
/*-----------------------------------------------------------*/

/**
* @brief Constants used to check the installation of the FreeRTOS interrupt handlers.
*/
#define portVECTOR_INDEX_SVC ( 11 )
#define portVECTOR_INDEX_PENDSV ( 14 )
/*-----------------------------------------------------------*/

/**
* @brief Constants required to check the validity of an interrupt priority.
*/
Expand Down Expand Up @@ -1598,22 +1612,52 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO

BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */
{
/* An application can install FreeRTOS interrupt handlers in one of the
* folllowing ways:
* 1. Direct Routing - Install the functions SVC_Handler and PendSV_Handler
* for SVCall and PendSV interrupts respectively.
* 2. Indirect Routing - Install separate handlers for SVCall and PendSV
* interrupts and route program control from those handlers to
* SVC_Handler and PendSV_Handler functions.
*
* Applications that use Indirect Routing must set
* configCHECK_HANDLER_INSTALLATION to 0 in their FreeRTOSConfig.h. Direct
* routing, which is validated here when configCHECK_HANDLER_INSTALLATION
* is 1, should be preferred when possible. */
#if ( configCHECK_HANDLER_INSTALLATION == 1 )
{
const portISR_t * const pxVectorTable = portSCB_VTOR_REG;

/* Validate that the application has correctly installed the FreeRTOS
* handlers for SVCall and PendSV interrupts. We do not check the
* installation of the SysTick handler because the application may
* choose to drive the RTOS tick using a timer other than the SysTick
* timer by overriding the weak function vPortSetupTimerInterrupt().
*
* Assertion failures here indicate incorrect installation of the
* FreeRTOS handlers. For help installing the FreeRTOS handlers, see
* https://www.FreeRTOS.org/FAQHelp.html.
*
* Systems with a configurable address for the interrupt vector table
* can also encounter assertion failures or even system faults here if
* VTOR is not set correctly to point to the application's vector table. */
configASSERT( pxVectorTable[ portVECTOR_INDEX_SVC ] == SVC_Handler );
configASSERT( pxVectorTable[ portVECTOR_INDEX_PENDSV ] == PendSV_Handler );
}
#endif /* configCHECK_HANDLER_INSTALLATION */

#if ( ( configASSERT_DEFINED == 1 ) && ( portHAS_ARMV8M_MAIN_EXTENSION == 1 ) )
{
volatile uint32_t ulOriginalPriority;
volatile uint32_t ulImplementedPrioBits = 0;
volatile uint8_t ucMaxPriorityValue;

/* Determine the maximum priority from which ISR safe FreeRTOS API
* functions can be called. ISR safe functions are those that end in
* "FromISR". FreeRTOS maintains separate thread and ISR API functions to
* functions can be called. ISR safe functions are those that end in
* "FromISR". FreeRTOS maintains separate thread and ISR API functions to
* ensure interrupt entry is as fast and simple as possible.
*
* Save the interrupt priority value that is about to be clobbered. */
ulOriginalPriority = portNVIC_SHPR2_REG;

/* Determine the number of priority bits available. First write to all
* possible bits. */
* First, determine the number of priority bits available. Write to all
* possible bits in the priority setting for SVCall. */
portNVIC_SHPR2_REG = 0xFF000000;

/* Read the value back to see how many bits stuck. */
Expand All @@ -1636,7 +1680,6 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */

/* Calculate the maximum acceptable priority group value for the number
* of bits read back. */

while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
{
ulImplementedPrioBits++;
Expand Down Expand Up @@ -1674,16 +1717,14 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */
* register. */
ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;

/* Restore the clobbered interrupt priority register to its original
* value. */
portNVIC_SHPR2_REG = ulOriginalPriority;
}
#endif /* #if ( ( configASSERT_DEFINED == 1 ) && ( portHAS_ARMV8M_MAIN_EXTENSION == 1 ) ) */

/* Make PendSV, CallSV and SysTick the same priority as the kernel. */
/* Make PendSV and SysTick the lowest priority interrupts, and make SVCall
* the highest priority. */
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
portNVIC_SHPR2_REG = 0;

#if ( configENABLE_MPU == 1 )
{
Expand Down
58 changes: 50 additions & 8 deletions portable/GCC/ARM_CM0/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
#include "FreeRTOS.h"
#include "task.h"

/* Prototype of all Interrupt Service Routines (ISRs). */
typedef void ( * portISR_t )( void );

/* Constants required to manipulate the NVIC. */
#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
Expand All @@ -51,6 +54,10 @@
#define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL )
#define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL )

/* Constants used to check the installation of the FreeRTOS interrupt handlers. */
#define portSCB_VTOR_REG ( *( ( portISR_t ** ) 0xe000ed08 ) )
#define portVECTOR_INDEX_PENDSV ( 14 )

/* Constants required to set up the initial stack. */
#define portINITIAL_XPSR ( 0x01000000 )

Expand Down Expand Up @@ -200,20 +207,20 @@ void vPortSVCHandler( void )

void vPortStartFirstTask( void )
{
/* The MSP stack is not reset as, unlike on M3/4 parts, there is no vector
* table offset register that can be used to locate the initial stack value.
* Not all M0 parts have the application vector table at address 0. */
/* Don't reset the MSP stack as is done on CM3/4 devices. The vector table
* in some CM0 devices cannot be modified and thus may not hold the
* application's initial MSP value. */
__asm volatile (
" .syntax unified \n"
" ldr r2, pxCurrentTCBConst2 \n" /* Obtain location of pxCurrentTCB. */
" ldr r3, [r2] \n"
" ldr r0, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" adds r0, #32 \n" /* Discard everything up to r0. */
" msr psp, r0 \n" /* This is now the new top of stack to use in the task. */
" adds r0, #32 \n" /* Discard everything up to r0. */
" msr psp, r0 \n" /* This is now the new top of stack to use in the task. */
" movs r0, #2 \n" /* Switch to the psp stack. */
" msr CONTROL, r0 \n"
" msr CONTROL, r0 \n"
" isb \n"
" pop {r0-r5} \n" /* Pop the registers that are saved automatically. */
" pop {r0-r5} \n" /* Pop the registers that are saved automatically. */
" mov lr, r5 \n" /* lr is now in r5. */
" pop {r3} \n" /* Return address is now in r3. */
" pop {r2} \n" /* Pop and discard XPSR. */
Expand All @@ -231,7 +238,42 @@ void vPortStartFirstTask( void )
*/
BaseType_t xPortStartScheduler( void )
{
/* Make PendSV, CallSV and SysTick the same priority as the kernel. */
/* An application can install FreeRTOS interrupt handlers in one of the
* folllowing ways:
* 1. Direct Routing - Install the function xPortPendSVHandler for PendSV
* interrupt.
* 2. Indirect Routing - Install separate handler for PendSV interrupt and
* route program control from that handler to xPortPendSVHandler function.
*
* Applications that use Indirect Routing must set
* configCHECK_HANDLER_INSTALLATION to 0 in their FreeRTOSConfig.h. Direct
* routing, which is validated here when configCHECK_HANDLER_INSTALLATION
* is 1, should be preferred when possible. */
#if ( configCHECK_HANDLER_INSTALLATION == 1 )
{
/* Point pxVectorTable to the interrupt vector table. Systems without
* a VTOR register provide the value zero in the VTOR register and
* the vector table itself is located at the address 0x00000000. */
const portISR_t * const pxVectorTable = portSCB_VTOR_REG;

/* Validate that the application has correctly installed the FreeRTOS
* handler for PendSV interrupt. We do not check the installation of the
* SysTick handler because the application may choose to drive the RTOS
* tick using a timer other than the SysTick timer by overriding the
* weak function vPortSetupTimerInterrupt().
*
* Assertion failures here indicate incorrect installation of the
* FreeRTOS handler. For help installing the FreeRTOS handler, see
* https://www.FreeRTOS.org/FAQHelp.html.
*
* Systems with a configurable address for the interrupt vector table
* can also encounter assertion failures or even system faults here if
* VTOR is not set correctly to point to the application's vector table. */
configASSERT( pxVectorTable[ portVECTOR_INDEX_PENDSV ] == xPortPendSVHandler );
}
#endif /* configCHECK_HANDLER_INSTALLATION */

/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;

Expand Down
71 changes: 56 additions & 15 deletions portable/GCC/ARM_CM23/non_secure/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@
#endif
/*-----------------------------------------------------------*/

/**
* @brief Prototype of all Interrupt Service Routines (ISRs).
*/
typedef void ( * portISR_t )( void );
/*-----------------------------------------------------------*/

/**
* @brief Constants required to manipulate the NVIC.
*/
Expand All @@ -101,10 +107,18 @@
/**
* @brief Constants required to manipulate the SCB.
*/
#define portSCB_SYS_HANDLER_CTRL_STATE_REG ( *( volatile uint32_t * ) 0xe000ed24 )
#define portSCB_VTOR_REG ( *( ( portISR_t ** ) 0xe000ed08 ) )
#define portSCB_SYS_HANDLER_CTRL_STATE_REG ( *( ( volatile uint32_t * ) 0xe000ed24 ) )
#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL )
/*-----------------------------------------------------------*/

/**
* @brief Constants used to check the installation of the FreeRTOS interrupt handlers.
*/
#define portVECTOR_INDEX_SVC ( 11 )
#define portVECTOR_INDEX_PENDSV ( 14 )
/*-----------------------------------------------------------*/

/**
* @brief Constants required to check the validity of an interrupt priority.
*/
Expand Down Expand Up @@ -1598,22 +1612,52 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO

BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */
{
/* An application can install FreeRTOS interrupt handlers in one of the
* folllowing ways:
* 1. Direct Routing - Install the functions SVC_Handler and PendSV_Handler
* for SVCall and PendSV interrupts respectively.
* 2. Indirect Routing - Install separate handlers for SVCall and PendSV
* interrupts and route program control from those handlers to
* SVC_Handler and PendSV_Handler functions.
*
* Applications that use Indirect Routing must set
* configCHECK_HANDLER_INSTALLATION to 0 in their FreeRTOSConfig.h. Direct
* routing, which is validated here when configCHECK_HANDLER_INSTALLATION
* is 1, should be preferred when possible. */
#if ( configCHECK_HANDLER_INSTALLATION == 1 )
{
const portISR_t * const pxVectorTable = portSCB_VTOR_REG;

/* Validate that the application has correctly installed the FreeRTOS
* handlers for SVCall and PendSV interrupts. We do not check the
* installation of the SysTick handler because the application may
* choose to drive the RTOS tick using a timer other than the SysTick
* timer by overriding the weak function vPortSetupTimerInterrupt().
*
* Assertion failures here indicate incorrect installation of the
* FreeRTOS handlers. For help installing the FreeRTOS handlers, see
* https://www.FreeRTOS.org/FAQHelp.html.
*
* Systems with a configurable address for the interrupt vector table
* can also encounter assertion failures or even system faults here if
* VTOR is not set correctly to point to the application's vector table. */
configASSERT( pxVectorTable[ portVECTOR_INDEX_SVC ] == SVC_Handler );
configASSERT( pxVectorTable[ portVECTOR_INDEX_PENDSV ] == PendSV_Handler );
}
#endif /* configCHECK_HANDLER_INSTALLATION */

#if ( ( configASSERT_DEFINED == 1 ) && ( portHAS_ARMV8M_MAIN_EXTENSION == 1 ) )
{
volatile uint32_t ulOriginalPriority;
volatile uint32_t ulImplementedPrioBits = 0;
volatile uint8_t ucMaxPriorityValue;

/* Determine the maximum priority from which ISR safe FreeRTOS API
* functions can be called. ISR safe functions are those that end in
* "FromISR". FreeRTOS maintains separate thread and ISR API functions to
* functions can be called. ISR safe functions are those that end in
* "FromISR". FreeRTOS maintains separate thread and ISR API functions to
* ensure interrupt entry is as fast and simple as possible.
*
* Save the interrupt priority value that is about to be clobbered. */
ulOriginalPriority = portNVIC_SHPR2_REG;

/* Determine the number of priority bits available. First write to all
* possible bits. */
* First, determine the number of priority bits available. Write to all
* possible bits in the priority setting for SVCall. */
portNVIC_SHPR2_REG = 0xFF000000;

/* Read the value back to see how many bits stuck. */
Expand All @@ -1636,7 +1680,6 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */

/* Calculate the maximum acceptable priority group value for the number
* of bits read back. */

while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
{
ulImplementedPrioBits++;
Expand Down Expand Up @@ -1674,16 +1717,14 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */
* register. */
ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;

/* Restore the clobbered interrupt priority register to its original
* value. */
portNVIC_SHPR2_REG = ulOriginalPriority;
}
#endif /* #if ( ( configASSERT_DEFINED == 1 ) && ( portHAS_ARMV8M_MAIN_EXTENSION == 1 ) ) */

/* Make PendSV, CallSV and SysTick the same priority as the kernel. */
/* Make PendSV and SysTick the lowest priority interrupts, and make SVCall
* the highest priority. */
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
portNVIC_SHPR2_REG = 0;

#if ( configENABLE_MPU == 1 )
{
Expand Down
Loading

0 comments on commit 30e6b8a

Please sign in to comment.