Jump to bootloader.

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderators: RoccoMarco, barthess

rew
Posts: 380
Joined: Sat Jul 19, 2014 12:59 pm
Has thanked: 2 times
Been thanked: 13 times

Jump to bootloader.

Postby rew » Thu Oct 22, 2020 5:10 pm

I've wanted a "jump to bootloader" functionality for a while.

Doing that from the application by resetting everything and then jumping to the location specified in "system memory" doesn't work. I am apparently not resetting enough, so the bootloader doesn't work.

Setting a variable in RAM with a "special value", triggering a reset and then checking for that value as the first thing in main, doesn't work. Still too much is initialized to frustrate the bootloader.

The only thing I've been able to get tot work is to add some code to "Reset_Handler" in crt0_v6m.S .

However, this is a "system file", it belongs to chibios, and I don't like having to modify files "owned" by chibios.

Now I was hoping to create a define for an "early asm hook" that I could define to do my magic....

Two problems: What headers are included in the crt0_v6m.S that are "owned" by me, the user? I don't see any includes in there. Is the preprocessor only used to expand things defined in the file itself? I don't see any #include statements.

Second, I tried defining a multi line asm statement. However, apparently the preprocessor always generates one line, and that is not working for me. Update: I think I've got this one sorted: semicolon allows multiple statements on one line.

Would a patch that includes SOME user file and then calls some "EARLY_INIT_ASM_HOOK()" from Reset_Handler be accepted?

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: Jump to bootloader.

Postby Giovanni » Thu Oct 22, 2020 5:32 pm

Hi,

You could add your early code to board.h, there are hooks in there.

Giovanni

rew
Posts: 380
Joined: Sat Jul 19, 2014 12:59 pm
Has thanked: 2 times
Been thanked: 13 times

Re: Jump to bootloader.

Postby rew » Fri Oct 23, 2020 11:57 am

You said "board.h" and the only I could find was "boardInit" which is called late in halInit and I tried adding my code in main before alInit, and that was too late.

You mean __early_init in board.c .

I added my code there and... that's too late: It doesn't work.

I moved the call to __early_init up, Above: VTOR_INIT... too late: doesn't work.
above CONTROL? Too late.

Only when I move the bl __early_init above cspid, just after Reset_Handler: then did it work. Soo.... It seems the cpsid is the culprit.

Well... not so fast. So I moved cspid i to the place where __early_init used to be called, in the hopes of just having to move the cpsid, or possibly reverting that in my jump-to-bootloader code.

Then it continues to work if I put the code after the "stack init".

But when I put it after the msr CONTROL: it stops working: my jump-to-bootloader doesn't work, it just boots the application.

The bootloader is REALLY picky: It needs almost everything in "reset condition", Not a single byte is wasted "interrupts should be off, but lets disable them anyway just in case.".

Trying to understand the code I'm working with....
https://www.keil.com/support/man/docs/a ... 871865.htm
documents the isb instruction as being available on armV7 and I have an ARMV6. Moreover it is documented as being available in ARM mode and not in thumb. My CPU only has Thumb.
https://github.com/ChibiOS/ChibiOS/blob ... v6m.S#L167

OK..... debugging some more.... I need to undo the msr CONTROL and the cpsid i instructions:

Code: Select all

   __asm ("\tmov r0, #0\n\t"
      "msr     CONTROL, r0\n\t"
      "cpsie i\n");

before jumping to the bootloader. I've reverted crt0_v6m.S to stock version.

Polux
Posts: 27
Joined: Thu Apr 28, 2016 11:52 am
Been thanked: 7 times

Re: Jump to bootloader.

Postby Polux » Fri Oct 23, 2020 1:00 pm

Hi,

In your last post, you wrote 3 times cpsid, but your code is "cpsie i\n"

Probably a typo error. And other than that, I am of absolute no help on this :mrgreen:

Angelo

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: Jump to bootloader.

Postby Giovanni » Fri Oct 23, 2020 2:08 pm

Then you need to put an asm module after vector.S and before crt0.S, note that it is possible to do this already, there is a weak label to override.

Giovanni

rew
Posts: 380
Joined: Sat Jul 19, 2014 12:59 pm
Has thanked: 2 times
Been thanked: 13 times

Re: Jump to bootloader.

Postby rew » Mon Oct 26, 2020 12:57 pm

Polux wrote:in your last post, you wrote 3 times cpsid, but your code is "cpsie
yeah. The startup code does cpsid where the d stands for Disable. Then to revert that I need to Enable the same stuff, so the instrution becomes cpsie.

electronic_eel
Posts: 77
Joined: Sat Mar 19, 2016 8:07 pm
Been thanked: 17 times

Re: Jump to bootloader.

Postby electronic_eel » Thu Oct 29, 2020 10:29 pm

I want to do exactly the same thing in my next project.

Which exact STM32 model were you using? I guess the bootloader code is quite different between the series, as they have a wide variety of different features.

Were you able to implement Giovannis suggestion, a module after vector.S and before crt0.S, overriding the weak label? This looks like the most elegant solution to me, as you wouldn't have to undo any initializations.

rew
Posts: 380
Joined: Sat Jul 19, 2014 12:59 pm
Has thanked: 2 times
Been thanked: 13 times

Re: Jump to bootloader.

Postby rew » Sun Nov 01, 2020 7:09 pm

I was using an STM32F072RC.

My recommendation is to do the same as I did. Just put the jump-to-bootloader in the __early_init function (depending on a magic value in some variable).

Next find the crt0.S file that your CPU uses. Make a guess, put in some garbage, if things still compile, you guessed wrong. (I tried looking up which file I modified, and... found said garbage there.... not the right one! ) for me it was: os/common/startup/ARMCMx/compilers/GCC/crt0_v6m.S

Now find the call to __early_init and move it up to be close to the Reset_Handler: Hopefully, it then works. Now move it back to where it was. If it still works, great. Done.

If not you can move it up and down between where it was to find the instruction that makes it not work and you'll have to undo that too.

I would hazard a guess that my code above will already work even for armV7. The armV7 code only initializes a possible FPU as a difference with the V6 code. (before the __early_init, anything after that is irrelevant).

electronic_eel wrote:Were you able to implement Giovannis suggestion, a module after vector.S and before crt0.S, overriding the weak label? This looks like the most elegant solution to me, as you wouldn't have to undo any initializations.
No, I did not go that way: doing it from __early_init is "nicer" as that function is defined in the "board.c" file that your project probably already defines. So even if I have to "bother" undoing a few things that were initialized before the __early_init call, it is nicer that way from a "software engineering" point-of-view.

IMHO, STM32cube and chibios don't get this right. In STM32cube, you hit a button and it generates your main for you. Then that becomes your file and you modify it. In chibios you have to copy a demo project and start modifying it. This leads to all sorts of problems. For example, when I needed USB and ETH in one project, I copied an USB demo project and worked from there integrating the ETH stuff. Turns out that somewhere where i didn't expect it there was another difference between the two projects that resulted in a "difficult to debug" problem.

Arduino does get it right: Your basic "blinky" project you can type from scratch in under two minutes.

Oh... One last thing... My personal preference would be to simply de-initialize the whole CPU and jump to bootloader from the middle of my app when the user has requested the jump-to-bootloader. Maybe the two things that needed to be done are close enough that it becomes possible to get that route working. My old code did a lot of "reset part of the CPU" by poking at the RCC.

electronic_eel
Posts: 77
Joined: Sat Mar 19, 2016 8:07 pm
Been thanked: 17 times

Re: Jump to bootloader.

Postby electronic_eel » Tue Mar 09, 2021 8:24 pm

I now implemented it using the weak symbol before crt0.S, as Giovanni suggested. I think this is the more clean method as I don't have to change anything in generated board-code and also don't have to reset anything like cpsid, but just keep the original settings from the reset of the MCU.

This is the code for handling the jump to bootloader and triggering it from the shell:

Code: Select all

#include "ch.h"
#include "hal.h"

#include "shell.h"
#include "chprintf.h"

// MCU specific address of the bootloader (STM32F072.B)
#define SYSTEM_MEMORY_ADDRESS   0x1FFFC800

// magic value that signifies that the bootloader should be run
#define BOOTLOADER_MAGIC 0xDEADBEEF

// fixed address at the end of the heap, used to keep the bootloader-trigger over the soft-reset
#define BOOTLOADER_FLAG *((uint32_t *)0x20003FF0)

// this is designed to be called from the shell
void cmd_bootloader(BaseSequentialStream *chp, __attribute__((unused)) int argc, __attribute__((unused)) char *argv[])
{
    chprintf(chp, "Entering Bootloader\r\n");

    // some time to transmit the string before reset
    chThdSleepMilliseconds(100);
   
    BOOTLOADER_FLAG = BOOTLOADER_MAGIC;

    // Soft-Reset the MCU
    NVIC_SystemReset();
   
    return;
}

// defined in os/common/startup/ARMCMx/compilers/GCC/crt0_v6m.S
__attribute__((naked)) void _crt0_entry(void);

// overrides the weak symbol in vector.S
__attribute__((naked)) __attribute__((__used__)) void Reset_Handler(void)
{
    // check reset reason in RCC_CSR register: must be soft reset for jump to bootloader
    // also bootloader magic code must be set
    // this combination ensures that no stray jumps to bootloader can happen due to random values in ram on boot
    if ( (RCC->CSR & RCC_CSR_SFTRSTF) && BOOTLOADER_FLAG == BOOTLOADER_MAGIC )
    {
        // reset the trigger to prevent unwanted jump to bootloader later on
        BOOTLOADER_FLAG =  0x0;
   
        register void (*BootloaderStartAddr)(void);
        BootloaderStartAddr = (void (*)(void)) (*((uint32_t *) ((SYSTEM_MEMORY_ADDRESS + 4))));

        // Initialize the stack pointer for the bootloader
        __set_MSP(*(__IO uint32_t*) SYSTEM_MEMORY_ADDRESS);

        BootloaderStartAddr();
    }

    // reset the trigger to prevent unwanted jump to bootloader later on
    BOOTLOADER_FLAG =  0x0;

    // jump to _crt0_entry, as the overridden weak Reset_Handler normally would do
    _crt0_entry();
}


To make this work properly, you have to clear the RCC_CSR register like this in main:

Code: Select all

uint32_t rcc_csr_copy;

int main(void) {
  halInit();
  chSysInit();
 
  // clear reset flags so that we don't read old values on the next reset, keep a copy
  rcc_csr_copy = RCC->CSR;
  RCC->CSR |= RCC_CSR_RMVF;
[...]

rew
Posts: 380
Joined: Sat Jul 19, 2014 12:59 pm
Has thanked: 2 times
Been thanked: 13 times

Re: Jump to bootloader.

Postby rew » Wed Mar 31, 2021 8:49 am

Hi,
I used the flag-at-a-constant address method at first too. However, after a while I changed to simply using a variable. Now normally a global variable starts out as "zero", but that doesn't happen by accident: There is code to clear all global variables just AFTER the "early_board_init()" call that I used. So you can just use

Code: Select all

int bootloader_flags;
... naked ... reset_handler ...
if (bootloader_flags == MAGIC_VALUE) { ...


Return to “STM32 Support”

Who is online

Users browsing this forum: No registered users and 20 guests