F0 Bootloader jump to App -> HardFault_Handler - what can cause this?
I have looked at a bunch of examples throughout here and elsewhere about custom bootloaders.
I noticed the Cortex M0 does not have the VTOR register and found that what people do is, at start, copy the vector table contents from the app flash beginning to the beginning of SRAM and then remap SRAM to address 0.
I did that. But as soon as I, debugging within the bootloader project, step over the call on the application function pointer, the target is in the hardfault handler.
The application address that the pointer variable holds looks correct, except it has exactly 1 byte more. I have read that odd addresses mean it is a Thumb2 instruction and the LSB goes elsewhere and is not really part of the address - so that would be expected.
The CRC of the app code in the app flash region also matches.
I have also exported that memory contents from CubeIDE (Eclipse) Memory Browser and compared it to the compiled binary in a hex editor - they match.
My linker scripts look like this:
Ther is one .ld file that get included in both the bootloader's and the app's main .ld file that CubeIDE initially provided, where I threw out the original MEMORY region and replaced it with include... the file below.
As you can see I have an extra 128 byte region there, for a header, which is generated by a custom program that parses the app binary to generate CRC and some meta info.
This app header is concatenated with the app binary to form one block that can be flashed at 0x8008000. The actual app thus starts 0x80 bytes later.
In the C/C++ programs, I use a header that declares these linker script variables as extern unsigned, and provide matching convenience macros that take their adresses, to get the actual values.
The values I see then in the debugger where they are used, seem to be all correct.
The SP register also does get set to end of RAM when stepping over the __set_MSP(..) line.
The jump does not happen from an ISR, it is in a global function, albeit one in a .cpp file, which is called by the main.c generated by CubeIDE.
Now I would not be surprise if the default Reset_Handler in the stm32*.S file did something suboptimal w.r.t. my goal here - but I can't see that it even gets there, it goes directly from stepping over appJumpAddr(); to the hardfault handler. (see code further below)
Edit: the main() befor calling my function, does this, as generated by CubeIDE:
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SYSCFG);
So that thing is on.
Any ideas what to check, to see what's wrong?
MEMORY
{
/* vector table fits in 48 32bitpointers i.e. 192 bytes (0xC0)*/
APPVTABLERAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192
RAM (xrw) : ORIGIN = 0x200000C0, LENGTH = 32K-192
/* bootloader binary */
BOOTFLASH (rx) : ORIGIN = 0x8000000, LENGTH = 32K
/* Header binary, created by external custom program to calculate CRC etc */
HEADERFLASH (rx) : ORIGIN = 0x8008000, LENGTH = 128
/* Start of actual application binary */
APPFLASH (rx) : ORIGIN = 0x8008080, LENGTH = 256K-32K-128
}
__mm_headerflash_start__ = ORIGIN(HEADERFLASH);
__mm_headerflash_size__ = LENGTH(HEADERFLASH);
__mm_bootflash_start__ = ORIGIN(BOOTFLASH);
__mm_bootflash_size__ = LENGTH(BOOTFLASH);
__mm_appflash_start__ = ORIGIN(APPFLASH);
__mm_appflash_size__ = LENGTH(APPFLASH);
__mm_app_vtab_ram_start__ = ORIGIN(APPVTABLERAM);
__mm_app_vtab_ram_size__ = LENGTH(APPVTABLERAM);void BootLoaderMain_Run()
{
const unsigned Nptrs = MM_app_vtab_ram_size / sizeof(uint32_t*);
volatile uint32_t* movedIrqVectorTab = (volatile uint32_t*) MM_app_vtab_ram_start;
const volatile uint32_t* appIrqVectorTab = (const volatile uint32_t*) MM_appflash_start;
for (unsigned i=0; i<Nptrs; ++i) // copy the vector table into RAM at the prepared location
{
movedIrqVectorTab[i] = appIrqVectorTab[i];
}
if (check_app_crc())
{
NVIC_DisableIRQ(I2C1_IRQn); // the only peripheral IRQ that's on in the bootloader, so far
//__disable_irq(); // Also tried more brutal, no visible difference
__DSB();
__ISB();
//__attribute__((noreturn))
void (*appJumpAddr)() = (void (*)()) (*((uint32_t *)(MM_appflash_start + 4)));
__set_MSP( *((uint32_t *)MM_appflash_start) );
LL_SYSCFG_SetRemapMemory( LL_SYSCFG_REMAP_SRAM );
__DSB();
__ISB();
appJumpAddr(); // <<< BOOM! Hard fault
}
else
{
while (1)
{ // blink LED forever
}
}
}