When using a custom bootloader, application hard faults
Hi all,
I put together a custom bootloader for my application (to allow the program to update itself). The bootloader itself is incredibly simple. It just reads from a known location in flash to determine which address to load from and then attempts to boot to that address. The main() function for that bootloader is included below (the rest of the code is auto generated minus some defines):
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
/* USER CODE BEGIN 2 */
uint32_t load_addr;
void (*appl_ptr)(void);
// If magic number address contains our magic number (for saying it's ok to use the
// program space) then use it, if not then it's corrupted and we need ot boot from
// gold working space
if ((*(__IO uint32_t*)SEMAPHORE_ADDR) == 1) {
load_addr = PROG1_ADDR;
} else if ((*(__IO uint32_t*)SEMAPHORE_ADDR) == 2) {
load_addr = PROG2_ADDR;
} else {
load_addr = GOLD_ADDR;
}
// disable irq and set vector table location
__disable_irq();
SCB->VTOR = load_addr;
// Clear interrupts?
for (uint8_t i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
// Force hardcoded address (+4 for first command) to application pointer
appl_ptr = (void (*)())(load_addr + 4);
// Other stuff a bootloader example did
HAL_RCC_DeInit();
HAL_DeInit();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
__HAL_RCC_SYSCFG_CLK_ENABLE();
// Set stack pointer to the value at the beginning of that space
__set_MSP(*(__IO uint32_t*) load_addr);
// Jump!
appl_ptr();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// SHOULD NEVER GET HERE
}
/* USER CODE END 3 */
}
Right now that semaphore location is set to 0 so it attempts to boot from the GOLD_ADDR location (0x080C0000).
If I program the application itself (after changing the STM32H735VGHX_FLASH.ld file to have flash start at 0x080C0000) and changing the VECT_TAB_OFFSET in system_stm32h7xx.c to 0x000C0000U it works perfectly fine. I can verify that we're in 0x080C0000+ space with the PC when debugging directly and can verify that the VECT_TAB_OFFSET is being set correctly because when I set it back to 0, interrupts no longer function correctly (and at 0xC0000 they do work correctly).
If I attempt to boot with that bootloader code above though, I end up hitting the application's hard fault handler (shown below):

I can verify in the .map file that the applications hardfault handler is at 0x080c0a54. Looking at the stack trace it seems to imply the hardfault triggers upon the calling the instruction at 0x80C0004. This is confusing to me though because it seems like the hardfault happens sometime after jumping but before reaching my applications main function. I think that because after line 62 in the above bootloader main function is called, the stack pointer is set to 0x24050000 but when the hardfault occurs, its equal to 0x2404ffe0 (the same value it seems to be when first reaching the main function). If I put an infinite loop at the start of my applications main function though, I still end up in the hardfault when using the bootloader instead of stuck in that loop.
On the other hand though, looking at 0x80C0004, it contains 0x80C0D3D which seems like the address to the reset handler (0x80C0D3C) but not word aligned. Is it possible that this is where the issue is coming from? If so, why does that value get generated when compiling and programming the application project and why does it work when directly running but not when using the bootloader? Looking at the bootloader's equiavlent address (0x8000004) it's 0x8000691 it's also the reset handler + 1.
Is there something else I'm missing?
