Skip to main content
Visitor II
September 4, 2025
Solved

STM32H5: Getting the provisioned state from within the firmware

  • September 4, 2025
  • 2 replies
  • 410 views

Hello,

 

we are using a STM32H5 with TZ disabled. In firmware, we are doing the following:

- set the product state to provisioning (0x17)

- provision the debug authentication

This already works fine. Afterwards, we can do a discovery an see the validity of the DA ( we read 0xeaeaeaea). Regression afterwards works fine.

However, we would like to check this validity from within the firmware.

1) where does this 0xeaeaeaea comes from? Can we also access it?

2) reading back the storage area of the DA (0x0ffd0100) is not reliable. Sometimes, we can read the data we just wrote, sometime we just read 0xff... This depends on the code we execute after writing. It is not a timing issue, should we maybe set a certain flag or something like that?

 

The code we used is based on the github project https://github.com/stm32-hotspot/STM32H5_DA_EmbeddedProvisioning

 

Best regards

Peter

    This topic has been closed for replies.
    Best answer by KimmoJ

    You need to invalidate the (Icache) after programming OBK.

     

    Your function isProvisioned() fetches a cache line that you hit when reading the next time. 

    2 replies

    ST Employee
    September 11, 2025

    Hello @peter7 ,

    The 0xeaeaeaea code comes from STDA component that you activate when requesting a discovery.

    This status is basically the result of the hash check done on DA OBK.

    Regarding the reliability of writing the OBK from firmware, I have never seen any issue.

    Especially, this is used by secure boot OEMiROT to write firmware hash and version.

    What you see may be related to reading timing, I need to reproduce to understand what happens.

    By the way, did you have any time a DA configuration written by firmware that couldn't be used to perform the regression ?

    Best regards

    Jocelyn

    peter7Author
    Visitor II
    September 15, 2025

    Hello Jocelyn,

     

    thanks for the reply. I'm doing the following in firmware:

    • Read the DA-storage area -> 0xFF...
    • Write DA-data -> ok
    • Read the DA-storage area -> 0xFF...
    • Reset
    • Read the DA-storage area -> provisioned data

    Please see the attached code. During my tests, provisioning always works.

    When I execute certain code between the writing and the reading, I can directly read back the provisioned data. This behaviour is reproducible, some intermediate code works, some does not, but I could not narrow it down to a certain instruction. I have the impression that a function call is needed, so maybe it has something to do with the accessing other memory banks.

     

    We would like to close the device on the first start of the firmware. This requires provisioning, setting to provisioned, checking the provisioned state first. Obviously, we don't want to close a device, before we are sure the the provisioning was successful. 

    The planned production process does not permit to do these steps with the flasher.

     

    I'm hoping that you can help me figure out what to do to allow reading back without executing a reset.

     

    /**********************************************************************************************************************/
    /** Main function and initialization
     **********************************************************************************************************************/
    #include "stm32h5xx_hal.h"
    #include "main.h"
    
    extern "C" int main(void);
    
    /**********************************************************************************************************************/
    static void SystemClock_Config(void)
    {
     RCC_OscInitTypeDef RCC_OscInitStruct {};
     RCC_ClkInitTypeDef RCC_ClkInitStruct {};
    
     /** Configure the main internal regulator output voltage */
     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
    
     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    
     /** Initializes the RCC Oscillators according to the specified parameters in the RCC_OscInitTypeDef structure. */
     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
     RCC_OscInitStruct.HSIState = RCC_HSI_ON;
     RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
     RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
     if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
     Error_Handler();
     }
    
     /** Initializes the CPU, AHB and APB buses clocks */
     RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
     |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
     |RCC_CLOCKTYPE_PCLK3;
     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
     RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
     RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
     RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
     RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
    
     if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) {
     Error_Handler();
     }
    }
    
    /**********************************************************************************************************************/
    static void MX_ICACHE_Init(void) {
     if (HAL_ICACHE_ConfigAssociativityMode(ICACHE_1WAY) != HAL_OK) {
     Error_Handler();
     }
     if (HAL_ICACHE_Enable() != HAL_OK) {
     Error_Handler();
     }
    }
    
    /**********************************************************************************************************************/
    static void MX_GPIO_Init(void) {
    
     GPIO_InitTypeDef GPIO_InitStruct {};
    
     /* GPIO Ports Clock Enable */
     __HAL_RCC_GPIOE_CLK_ENABLE();
     __HAL_RCC_GPIOC_CLK_ENABLE();
     __HAL_RCC_GPIOF_CLK_ENABLE();
     __HAL_RCC_GPIOH_CLK_ENABLE();
     __HAL_RCC_GPIOA_CLK_ENABLE();
     __HAL_RCC_GPIOB_CLK_ENABLE();
     __HAL_RCC_GPIOD_CLK_ENABLE();
     __HAL_RCC_GPIOG_CLK_ENABLE();
    
     __HAL_RCC_USART3_CLK_ENABLE();
    
     /*Configure GPIO pins : PD8 PD9 */
     GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
     GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
     HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
     // Initialize UART
     UART_HandleTypeDef uartHandle;
    
     uartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    
     uartHandle.Instance = USART3;
     uartHandle.Init.BaudRate = 1000000;
     uartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
     uartHandle.Init.Mode = UART_MODE_TX;
     uartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
     uartHandle.Init.Parity = UART_PARITY_NONE;
     uartHandle.Init.StopBits = UART_STOPBITS_1;
     uartHandle.Init.WordLength = UART_WORDLENGTH_8B;
     uartHandle.Init.ClockPrescaler = UART_PRESCALER_DIV1;
     uartHandle.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED;
    
     HAL_UART_Init(&uartHandle);
    }
    
    /**********************************************************************************************************************/
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
     if (htim->Instance == TIM3) {
     HAL_IncTick();
     }
    }
    
    /**********************************************************************************************************************/
    void Error_Handler(void) {
     __disable_irq();
     while (1){}
    }
    
    /**********************************************************************************************************************/
    static void PrintLn(uint8_t count, uint8_t * data) {
     for(uint8_t pos = 0; pos < count; pos++) {
     while((USART3->ISR & USART_ISR_TXE) == 0);
     USART3->TDR = data[pos];
     }
     while((USART3->ISR & USART_ISR_TXE) == 0);
     USART3->TDR = '\n';
    }
    
    /**********************************************************************************************************************/
    static bool IsProvisioned() {
     if ((*(uint32_t *)(0x0FFD0100)) != 0xFFFFFFFF) {
     PrintLn(11,(uint8_t *)"provisioned");
     return true;
     }
     else {
     PrintLn(15,(uint8_t *)"not provisioned");
     return false;
     }
    }
    
    /**********************************************************************************************************************/
    const unsigned char DA_Config[] = {
    
     // addr
     0x00, 0x01, 0xfd, 0x0f,
     // size
     0x60, 0x00, 0x00, 0x00,
     // enc
     0x00, 0x00, 0x00, 0x00,
     // integrity
     0x42, 0x09, 0x02, 0x43,
     0x9c, 0x31, 0x95, 0x3b, 0x6a, 0x8b, 0xff, 0xdd, 0xab, 0x06, 0x19, 0x95, 0x81, 0x29, 0xe2, 0xb5,
     0x2c, 0x51, 0xb0, 0x0f, 0x13, 0x91, 0x27, 0x4c, 0xe7, 0xdd, 0xb3, 0x53,
    
     // sha256
     0x18, 0x4a, 0xa4, 0x6d,
     0x81, 0x34, 0x11, 0x72, 0x7d, 0xa0, 0xdc, 0x9e, 0x64, 0x18, 0x6b, 0xb9, 0x90, 0x72, 0x89, 0xb5,
     0xaa, 0xb4, 0xb3, 0x20, 0xd2, 0x6f, 0xff, 0x5e, 0xa4, 0x5d, 0x8e, 0x3d,
     // perm
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     // reserved
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    };
    
    /**********************************************************************************************************************/
    static void OBKProvisioning_ProvisionDA(void) {
     __HAL_RCC_SBS_CLK_ENABLE();
    
     /* Unlock Flash area */
     (void) HAL_FLASH_Unlock();
     (void) HAL_FLASHEx_OBK_Unlock();
    
     /* Force use of EPOCH_S value for DHUK */
     WRITE_REG(SBS_S->EPOCHSELCR, (1U << SBS_EPOCHSELCR_EPOCH_SEL_Pos));
    
     /* Erase OBKeys */
     FLASH_EraseInitTypeDef FLASH_EraseInitStruct = {0U};
     uint32_t sector_error = 0U;
     FLASH_EraseInitStruct.TypeErase = FLASH_TYPEERASE_OBK_ALT;
     if (HAL_FLASHEx_Erase(&FLASH_EraseInitStruct, &sector_error) != HAL_OK) {
     PrintLn(11, (uint8_t *)"Error Erase");
     }
    
     /* Program OBKeys */
     for (uint32_t i = 0U; i < 0x60; i += 16) {
     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD_OBK_ALT, (0x0FFD0100 + i), ((uint32_t)&(DA_Config[12 + i]))) != HAL_OK) {
     PrintLn(11, (uint8_t *)"Error Flash");
     }
     }
    
     /* Swap all OBKeys */
     if (HAL_FLASHEx_OBK_Swap(0x1FFU) != HAL_OK) {
     PrintLn(10, (uint8_t *)"Error Swap");
     }
    
     /* Lock the User Flash area */
     (void) HAL_FLASH_Lock();
     (void) HAL_FLASHEx_OBK_Lock();
    }
    
    /** Main function *****************************************************************************************************/
    int main(void)
    {
     // Reset of all peripherals, Initializes the Flash interface and the Systick.
     HAL_Init();
    
     /* Configure the system clock */
     SystemClock_Config();
    
     // Initialize all configured peripherals
     MX_GPIO_Init();
     MX_ICACHE_Init();
    
     if(IsProvisioned() == false) {
     // Write provisioning data
     PrintLn(13,(uint8_t *)"do provision");
     }
     OBKProvisioning_ProvisionDA();
    
     IsProvisioned();
    
     while (true){}
    }

     

    Best regards,

    Peter

    KimmoJAnswer
    Visitor II
    October 10, 2025

    You need to invalidate the (Icache) after programming OBK.

     

    Your function isProvisioned() fetches a cache line that you hit when reading the next time.