cancel
Showing results for 
Search instead for 
Did you mean: 

Writing on a magic RAM address to trigger a bootloader?

Antonio Teixeira
Associate II

Hello!

I've been working with embedded software for a while now, but managed to avoid the nitty-gritty of bootloaders most of the time. Now on my current project I have some catching up to do.

We are working on top of the reference firmware for the ST25RU3993-EVAL. The bootloader here isn't executed every boot, instead it is summoned by an UART command, which fits what our application should do too. While studying the code I had some questions regarding how the bootloader is triggered. Here is the relevant section of code:

/*        Whole SRAM: 0x20000000 - 0x20017FFF
Bootloader-MagicWord: 0x20017FF0 - 0x20017FFF (16 bytes)
      Remaining SRAM: 0x20000000 - 0x20017FEF 
*/
#define SYS_MEM_ADDR                    0x1FFF0000
#define BOOTLOADER_MAGIC_INFO_ADDR      0x20017FF0
 
const char* BtlMagicInfo = ".EnterBooloader.";
 
static void bootloaderInit(void)
{
    __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
    __set_MSP(*(__IO uint32_t*) SYS_MEM_ADDR);
 
    void (*Bootloader)(void) = (void(*)(void)) *((uint32_t *) (SYS_MEM_ADDR + 4));
    Bootloader();
 
    while(1);
}
 
void bootloaderCheck(void)
{
    if(0 == memcmp((const void*)BOOTLOADER_MAGIC_INFO_ADDR, BtlMagicInfo, strlen(BtlMagicInfo)))
    {
        *(uint32_t *)BOOTLOADER_MAGIC_INFO_ADDR = 0;
        bootloaderInit();
    }
}
 
void bootloaderEnterAndReset(void)
{
    memcpy((void*)BOOTLOADER_MAGIC_INFO_ADDR, BtlMagicInfo, strlen(BtlMagicInfo));
    HAL_NVIC_SystemReset();
 
    while(1);
}

Okay, so first of all lets see if I understood how this is supposed to work: When bootloaderEnterAndReset() gets called a magic word is written at a previously known RAM address and the MCU is reset. Since it is a soft reset, the magic word doesn't get erased and is checked in bootloaderCheck() (called first thing in main). If the magic word is there, bootloaderInit() performs the actual jump.

Almost all this seems clear and makes sense, I'm just having a hard time understanding how the RAM address works. It points to the last 16 bytes of RAM (MCU is a STM32L476, by the way), but there is no variable there, its just a raw address.

  • Isn't this supposed to be a stack area then?
  • From what I understand, the stack grows top-down, so aren't you overwriting the stack when copying the magic word to this address?

I thought that maybe these last 16 bytes were reserved in the linker script somehow, but it doesn't seem to be the case.

Am I on the right track here? Can someone shed some light on what trick am I missing?

Thanks a lot for taking the time to help and sorry for any English mistakes.

Edit: Had typed the wrong MCU model.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

> I suppose that since the MCU is immediately reset afterwards and the bootloaderCheck is literally the first thing in main it behaves consistently. Is this reliable/good practice in the long run though?

I'm not an expert on this, but once you're in C code, I don't think this will work in general. If you did the check in the startup script, then there is no problem.

I would move the end of the stack down 16 bytes to avoid the overlap.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

5 REPLIES 5

Yes the stack should probably be moved on GNU/GCC implementations, via the Linker Script.

Personally I don't use ridiculously long tags, and do the check in the Reset_Handler away from code that is using the stack. ​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
TDK
Guru

> Isn't this supposed to be a stack area then?

Datasheet says SRAM1 is 128kB, so the end would be at 0x20020000. Does your linker script say it's less?

https://www.st.com/resource/en/datasheet/stm32l452re.pdf

> From what I understand, the stack grows top-down, so aren't you overwriting the stack when copying the magic word to this address?

If it's the end of the stack then yes, otherwise no.

If you feel a post has answered your question, please click "Accept as Solution".

Yes, this is what I thought. Rechecking the linker it really doesn't seem to offset this region in any way.

Dealing with this in the Reset_Handler is definitely a possibility which seems more reliable, maybe we'll go this way.

But the general idea remains the same? Write at a known address, reset, and check this address during the Reset_Handler? What is bothering me is if this stack overwrite is safe in this specific situation or if I really should enforce a RAM offset just to be really safe.

Thanks for the help!

> Datasheet says SRAM1 is 128kB, so the end would be at 0x20020000. Does your linker script say it's less?

Oops, that was my bad. I'm working at another project using a 452 and mixed things up. The kit we're using related to this problem has a STM32L476, with 96kB of SRAM1. Apologies for the confusion

> If it's the end of the stack then yes, otherwise no.

Yeah, pretty sure it is the end of the stack. I suppose that since the MCU is immediately reset afterwards and the bootloaderCheck is literally the first thing in main it behaves consistently. Is this reliable/good practice in the long run though?

Thank you!

TDK
Guru

> I suppose that since the MCU is immediately reset afterwards and the bootloaderCheck is literally the first thing in main it behaves consistently. Is this reliable/good practice in the long run though?

I'm not an expert on this, but once you're in C code, I don't think this will work in general. If you did the check in the startup script, then there is no problem.

I would move the end of the stack down 16 bytes to avoid the overlap.

If you feel a post has answered your question, please click "Accept as Solution".