Skip to main content
NickO
Associate III
December 2, 2025
Solved

Calling non-secure callable from FreeRTOS Thread causing HardFault on STM32H573

  • December 2, 2025
  • 1 reply
  • 549 views

I've been struggling with this problem for several hours.

  • Using bare metal, I can invoke a non-secure callable from the non-secure application.
  • When I added FreeRTOS + CMSIS RTOS v2 wrapper, and try to do the same from a thread context, I get a hard fault when I try to invoke the non-secure callable (see below)

I would assume this is related to the thread stack. 

I am using the following:

  • Cube MX 6.16
  • Cube IDE Version: 2.0.0 Build: 26820_20251114_1348 (UTC)

In the secure project is secure_nsc.c, I define my API:

 CMSE_NS_ENTRY void ToggleBackLight(void)
 {
 	HAL_GPIO_TogglePin(BACKLIGHT_GPIO_Port, BACKLIGHT_Pin);
 }

 In the non-secure project, I invoke this from a thread as follows:

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
typedef void (*SecureFunc_t)(void) __attribute__((cmse_nonsecure_call));
void doBlinkyThread(void *argument)
{
 /* USER CODE BEGIN defaultTask */
 SecureFunc_t secure_func = (SecureFunc_t)ToggleBackLight; // from veneer table
 /* Infinite loop */
 for(;;)
 {
	 secure_func();
	 osDelay(500);
 }
 /* USER CODE END defaultTask */
}
/* USER CODE END Application */

 Notes:

I've used Cube MX to generate the thread-initialisation code, as shown below:

/* Definitions for blinkyThread */
osThreadId_t blinkyThreadHandle;
uint32_t MyBufferTask02[ 256 ];
osStaticThreadDef_t MycontrolBlocTask02;
const osThreadAttr_t blinkyThread_attributes = {
 .name = "blinkyThread",
 .stack_mem = &MyBufferTask02[0],
 .stack_size = sizeof(MyBufferTask02),
 .cb_mem = &MycontrolBlocTask02,
 .cb_size = sizeof(MycontrolBlocTask02),
 .priority = (osPriority_t) osPriorityRealtime7,
};

...

 /* creation of blinkyThread */
 blinkyThreadHandle = osThreadNew(doBlinkyThread, NULL, &blinkyThread_attributes);

I note that there is nothing in the attribute structure to make this TrustZone (see below). I tried setting .tz_module=1 (as suggested in another forum post), but this did not solve it.

typedef struct {
 const char *name; ///< name of the thread
 uint32_t attr_bits; ///< attribute bits
 void *cb_mem; ///< memory for control block
 uint32_t cb_size; ///< size of provided memory for control block
 void *stack_mem; ///< memory for stack
 uint32_t stack_size; ///< size of stack
 osPriority_t priority; ///< initial thread priority (default: osPriorityNormal)
 TZ_ModuleId_t tz_module; ///< TrustZone module identifier
 uint32_t reserved; ///< reserved (must be 0)
} osThreadAttr_t;

I also noted a warning:

warning: 'cmse_nonsecure_call' attribute ignored without '-mcmse' option [-Wattributes]

So, in addition to the above, I added this parameter to the compiler (on both sides), and the warning went away, but I still get the hard fault.

 

When I find myself having to mess with MX generated code and settings, it suggests I've missed something in Cube MX?

 

Best answer by NickO

Solved (I think)!

Seems adding .tz_module = 1 has no effect, and indeed, Cube MX would overwrite it anyway. However, if I call portALLOCATE_SECURE_CONTEXT at the start of my thread, it now allows me to call the non-secure callable function directly, and without a hard fault.

 

void doBlinkyThread(void *argument)
{
 /* USER CODE BEGIN defaultTask */

 /* Infinite loop */
 portALLOCATE_SECURE_CONTEXT(configMINIMAL_SECURE_STACK_SIZE);	//This is needed to use secure functions from a secure thread
 for(;;)
 {
	 ToggleBackLight();
	 osDelay(500);
 }
 /* USER CODE END defaultTask */
}

 

1 reply

NickO
NickOAuthorBest answer
Associate III
December 3, 2025

Solved (I think)!

Seems adding .tz_module = 1 has no effect, and indeed, Cube MX would overwrite it anyway. However, if I call portALLOCATE_SECURE_CONTEXT at the start of my thread, it now allows me to call the non-secure callable function directly, and without a hard fault.

 

void doBlinkyThread(void *argument)
{
 /* USER CODE BEGIN defaultTask */

 /* Infinite loop */
 portALLOCATE_SECURE_CONTEXT(configMINIMAL_SECURE_STACK_SIZE);	//This is needed to use secure functions from a secure thread
 for(;;)
 {
	 ToggleBackLight();
	 osDelay(500);
 }
 /* USER CODE END defaultTask */
}