Proper way to jump into the application memory location in bootloader - Page 1
Forum section: electronics / general

author: mathanjb56
author_rank: Newbie
timestamp: September 01, 2023, 06:32:23 am
content: Hi guys,I have been working on implementation of bootloader in SAMV71(Arm cortex M7). I am using below code to jump into the application code:uint32_t mainAppAddr = (uint32_t)APP_START_ADDRESS;uint32_t mainAppStack = (uint32_t)*((uint32_t*)mainAppAddr);mainApplication = (pMainApp)*(uint32_t*)(mainAppAddr + 4);if ((*(uint32_t *)APP_START_ADDRESS == 0xFFFFFFFF)){return;}__disable_irq();__set_MSP(mainAppStack);SCB->VTOR = mainAppAddr;__enable_irq();mainApplication();In above code we are using function pointer to jump the application code location. Usually execution of function takes stack memory, so my question is calling mainApplication() will use stack memory, I mean whole application code use stack memory? could anyone help me with that?Thanks in advance.!Regards,Mathan

author: abyrvalg
author_rank: Frequent Contributor
timestamp: September 01, 2023, 09:41:15 am
content: There is no point disabling/enabling IRQs around those two lines. Instead of that you need to shut down all interruptsourcesif you’ve configured any of them earlier. Otherwise an interrupt can reach the app before app’s initialization (or worse, an interrupt not used by app can land into some endless loop Default_Handler stub).The call to mainApplication() itself would not consume any stack (ARM doesn’t push the return address), but there is another thing to worry already discussed several times here: possible local vars corruption after __set_MSP(). In your case there are two vars being used after that call, normally they are stored in registers and survive the MSP change, but the compiler has right to store them on the stack instead. In this case changing the MSP would “teleport them away”. To prevent that you should set the MSP and jump to mainApplication in a single asm block.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: September 01, 2023, 11:25:42 am
content: The proper way to do it is to trigger a full reset. Without a full reset, all registers used in the bootloader will have unspecified content and this interferes with the normal program.

author: peter-h
author_rank: Super Contributor
timestamp: September 01, 2023, 12:30:52 pm
content: Here is code I use. Some of it won't apply to you. In this product a customer can write "overlays" which execute at base+32k.Code:// Interrupts are still disabled at this point.// Configure the Vector Table location to its new place at the start of the customer image.SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET_CUST;// set new VT location// Fill user stack and load SP to top of *real* RAM per CPU type.// Note the 437 stack is filled with 'S' while the 417 stack is filled with 's'// Setting SP throws away the current stack frame so local variables cannot be used anymore!// The 417 stack fills are shorter because b_memset needs to be able to return; at// that position on the stack frame about 700 bytes room is needed.extern char _top;static char* stack_base;// base of stack (from linkfile, for 417)stack_base = &_top;extern char _Stack_Size;// size of stack (from linkfile)static char* stack_size;stack_size = &_Stack_Size;#ifdef EXTRA_64Kif (B_g_dev_id==437){B_memset((char*)stack_base+65536,'S',(uint32_t)stack_size);asm volatile ("ldr sp, = _estack+65536 \n");}else{B_memset((char*)stack_base,'s',(uint32_t)stack_size-1024);asm volatile ("ldr sp, = _estack \n");}#elseB_memset((char*)stack_base,'s',((uint32_t)stack_size)-1024);asm volatile ("ldr sp, = _estack \n");#endif// Enter the normal main() (via main_stub) in the customer application program.// Note that we can get here with the serial FLASH totally zeroed, if above all-zero code was used// We do "jmp main()" even though the C code further below also works, in case main() is not// in the symbol table.extern char _customer_code_base;// This is base+32kuint32_t jmpaddr = (uint32_t)&_customer_code_base;// Weird stuff to get a usable valuejmpaddr |= 1;// Bit 0 = 1 for thumb codeasm("bx %0"::"r" (jmpaddr));// jmp (bx) etcIf you use a loader which runs out of RAM (so it can program CPU FLASH with no restrictions) end it with a reboot so you start with a freshly reset CPUCode:// Reboot the unit. This function is copied in a number of places.// Probably a good idea to wait say 100ms for any SPI ops to finish.static inline void reboot(void){// Ensure all outstanding memory accesses including buffered write are completed before reset__ASM volatile ("dsb 0xF":::"memory");// Keep priority group unchangedSCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |SCB_AIRCR_SYSRESETREQ_Msk );__ASM volatile ("dsb 0xF":::"memory");// wait until resetfor(;;){__NOP();}}

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: September 01, 2023, 12:45:22 pm
content: As mentioned, doing a reset is important if possible.e.g. your application code may be relying on some state that is defined on reset but may be altered by the bootloader ( e.g. UART mode). Not ideal as the app code should initialise everything, but it happens, and sometimes in less-than-obvious ways - one occasion it caught me was where the baudrate could not be altered when the UART was enabled, so the app code ended up with the wrong baudrate as it hadn't explicitly disabled the UART first.The way I usually do things on PICs, but probably applicable to many others is :On reset ( in the bootloader), before doing anything else, check if reset cause was a watchdog timeout, if so, jump to application code. (maybe ater doing a checksum on the app code & staying in BL if bad).Otherwise stay in the bootloader as long as needed, and allow the watchdog to time out to cause a reset when we want to start the application code.If the application code needs to re-enter the bootloader, it sets the "not a watchdog reset" status bit and does a software reset.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: September 01, 2023, 09:52:28 pm
content: Since VTOR is reset during full reset, doing it doesn’t remove the necessity to set the MSP and jump to the app somehow (that’s what OP’s code is about).Regarding the mandatory full reset - while it is great in “generic” bootloaders intended for public use, there can be gazillion of reasons against it (like necessity to hold some power enable pin in a defined state) or even reasons to inherit some known initialized peripheral state (i.e. there are systems booting an app via USB under BootROM control, then the app seamlessly continues to answer over USB, thus inheriting the address and all EP toggle states). It is totally ok to define a custom “contract” between the bootloader and the app as long as you understand it and keep following it in both parts.