Skip to main content
Visitor II
June 4, 2021
Solved

Custom "thick" bootloader to boot identical application binary from either address A or B?

  • June 4, 2021
  • 13 replies
  • 4139 views

Hello. I have run into a problem in a MCU project I have. I'll explain the situation.

We have a STM32F072RB -based product where we are implementing firmware update functionality. There would be 3 binaries in the MCU:

  1. Custom "thick" bootloader selecting application binary 1 or application binary 2 and jumping to run it
  2. Application binary 1 beginning at address A
  3. Application binary 2 beginning at address B

Our initial plan was to build different application binaries with explicitly different locations, but my customer is kind of wishing to use only one application binary. Therefore binary 1 would have exactly the same contents as binary 2.

Some experts say the way my customer wants the functionality is almost impossible to make. What do you say?

If anyone has any pointers what to google, what documents to read, etc, I'm all ears.

(My current, possibly stupid, idea is to make the single application binary to contain some kind of functionality to determine where it is run from and if it is run from binary 2 beginning from address B, then maybe perform some kind of elegant function/resource remapping so that when the image 2 runs, it does not use image 1 addresses for stuff, but locations in image 2 address space. Is this totally dumb?)

    This topic has been closed for replies.

    13 replies

    Graduate II
    June 4, 2021

    >>Some experts say the way my customer wants the functionality is almost impossible to make. What do you say?

    Address/position Independent code, relocatable vector table, sure.

    Thick loader defines the ground rules

    Super User
    June 4, 2021

    I cannot understand how compiling in position Independent mode alone can be enough.

    The code must be written in a specific way, to avoid for example this:

    void foo() {.... }
     
    const void (*funcptr)() = foo;

    as the const data is placed in the flash. Even if not, the image needs to contain relocations.

    -- pa

    Graduate II
    June 5, 2021

    Can't the address of a symbol be computed in a PC relative manner?

    The real singular problem is absolute addresses within the executable region that might move. The linker should be able to identify these, and the libraries would need to be built with the same options/expectations.

    Some testing and validation would be in order, one could start by having the linker build the images for two different base locations, and then comparing and identifying areas where the build changed.

    The loader in this case would need to set aside some RAM to relocate the vector table into,and create an environment where either image code run successfully.

    Code257Author
    Visitor II
    June 4, 2021

    Thanks, I'll get googling with this.

    Graduate II
    June 5, 2021

    Here is a pretty dumb idea - pack both binaries in a single file. There you go. It's a "single binary" now.

    Seriously... Compile and run firmware only at address A and use the address B as a backup storage. Before update a bootloader can copy the old firmware to it and in case of necessity copy it back to the address A.

    More seriously... The "two applications" approach seems easy only for customer, marketing people and other non-engineers. And the gain is also pretty minor. Compared to other more typical approaches, you only gain one feature - in case of a failed update, a user doesn't have to repeat it until it is successful for the device to be fully usable again. But for most use cases there is no such requirement and the only real requirement is for the device to be able to recover. That means the bootloader must be smart enough to always be able to update the main firmware regardless of the main firmware's current state. With such an approach you also don't need to waste a half of the FLASH memory.

    Code257Author
    Visitor II
    June 5, 2021

    Yeah I think least hassle for us with most of the customer constraints honored would be if we had permission to have "regular", different binary for bank 1 and bank 2. And off course the bootloader. And yeah I have thought about bundling both application binaries together. I don't understand what's customer's endgame with single binary, but I promised some research.

    Code257Author
    Visitor II
    June 9, 2021

    Ok, got finally the idea. The external system hosting the application binary (and doing the firmware upload to the product module) is also some kind of embedded system with limited memory. They want therefore to limit the storage footprint and one thing to practically halve it is the PIC .

    Code257Author
    Visitor II
    June 7, 2021

    Ok I started to experiment a bit. I had NUCLEO-F070RB evaluation board and I followed this https://archive.is/GZubE : What I did:

    1. Created 2 example projects, 1 x Bootloader and 1 x Application (application blinks LEDs)
    2. For linker scripts, defined sections RAM, BOOTLOADER and FIRMWARE, removed FLASH sections, made ROM alias of BOOTLOADER or FIRMWARE and chanced FLASH references to ROM. I also added firmware start address symbol of firmware at 0x08004000
    3. For Bootloader main.c I added the extern to the firmware start address symbol, then added the jump command (needed to change CPU_NVIC_DisableIRQ => HAL_NVIC_DisableIRQ )
    4. For Application main.c I added the extern to the firmware start address symbol and in main() I made it call the added Remap_Table() function the first thing. I also added simple LED state changer for loop
    5. I opened STM32CubeProgrammer, did mass erase, then flashed the Bootloader to 0x08000000 and Application to 0x08004000.

    To my utmost surprise the thing worked on the first try.

    Of course this does not solve the original problem, but now I can experimentally poke the Application with compile and link options and try to arbitrarily locate it, put stuff to r9, etc. I need of course update the position to Bootloader, but only there if everything goes right. What an exciting endeavor so far!

    Code257Author
    Visitor II
    June 7, 2021

    Ok, this is too complicated to gather info about. I'm giving up regarding the project (STM32F072RB).

    But I'm still interested to get it working as a hobby (NUCLEO-F070RB).

    Remember, there are 2 IDE projects, Bootloader and Application. Keeping this in mind, how should I change linker scripts? Should I add got and or plt sections? Where? Bootloader? Application? Both?

     (I tried to follow this partially: https://github.com/rgujju/STM32-projects/tree/master/got_plt but I have a bit different scenario I think and I'm failing)

    Super User
    June 7, 2021

    In this case you only want position independent code, executing in place.. Every image will use the same stack, data & bss addresses.

    So tinkering with GOT looks like overkill.

    --pa

    Code257Author
    Visitor II
    June 8, 2021

    So if I just jump from Bootloader to arbitrarily-positioned Application, GOT is not needed? Just PIC/pic is needed? And just for Application? And I just keep the Bootloader notified of the Application location?

    I will test this. I was experimenting with all of these options:

    # Generate position independent code.

    -fPIC

    # Access bss via the GOT.

    -mno-pic-data-is-text-relative

    # GOT is not PC-relative; store GOT location in a register.

    -msingle-pic-base

    # Store GOT location in r9.

    -mpic-register=r9

    But maybe I just use -fpic / -fPIC and see how what happens.

    Super User
    June 14, 2021

    > Customer has another valid point. The HW module is a critical component of a bigger system and does not tolerate more downtime than maybe just a few seconds.

    > Firmware upgrade is prepared "on the side" when the main functionality is running. That's why they need 2 applications.

    Fair enough, but that still does not validate the extra hassle with a single position-independent binary, as opposed to two binaries plus whatever mechanism needed to choose between them.

    JW

    Graduate II
    June 14, 2021

    what does "thick" stand for?

    Code257Author
    Visitor II
    June 14, 2021

    In this case it would include functionality to access Application binary locations to do crc verification. It would also include Eeprom capabilities for upgrade control.

    Code257Author
    Visitor II
    August 1, 2021

    Hello again,

    I started to use M4 (Nucleo-L432KC) because it is a bit easier in some aspects. I made bootloader which blinks led in a fast speed and tries to load firmware. Made firmware which blinks led in a slow speed.

    In general it seems nobody ever shares full code about this, so here is my try:

    https://github.com/usvi/L432KC-BL-FW/

    What I currently have:

    L432KC_Bootloader, it works fine and jumps away just fine. In linker script start address is 0x8000000. The bootloader can even jump to itself (0x8000000).

    L432KC_Firmware_0x8005000, which has in linker script defined that flash begins from 0x8005000, also I have SCB->VTOR = 0x8005000; -fPIC is enabled in IDE. Made L432KC_Firmware_0x8005000 for reference to verify bootloader can jump to firmware. I flash the .bin file manually to 0x8005000 and it works.

    L432KC_Firmware_anywhere, which has in linker script start address 0x8000000. SCB->VTOR is not altered, so it is zero. -fPIC is enabled in IDE. I flash the .bin file manually to 0x8005000 and it does not work, bootloader tries to jump there but it crashes.

    Can anyone spot what I'm doing obviously wrong?

    Code257Author
    Visitor II
    August 14, 2021

    I fixed it. I will write an article about it "soon".

    Code257Author
    Visitor II
    September 13, 2021

    I have now fully working, dynamic, clean, interrupts-driven, self-verifying, multi-mode solutions for both F070RB and L432KC. I need still to find time to write that article. It seems that about 99% of the information of this on various forums and other places is inaccurate, misleading, lacking or wrong.