Firmware update from firmware not possible #545

Closed
opened 2021-08-24 14:51:07 -05:00 by geurtv · 6 comments
geurtv commented 2021-08-24 14:51:07 -05:00 (Migrated from github.com)

I'm trying to update the RP2040 firmware from firmware running on that same RP2040. I download new firmware (via USB) into an unused region in flash and when done copy that data to the start of the flash and then reboot.

So the function that copies from flash to flash and then waits for reboot must run entirely from memory and never ever from flash. This, however, is currently not possible because the function rom_func_lookup, which both flash_range_erase and flash_range_program call, is still in flash.

When I wrap rom_func_lookup with a __not_in_flash_func (and make sure the compiler doesn't use memcpy in flash-to-flash) it all works fine.

I'm trying to update the RP2040 firmware from firmware running on that same RP2040. I download new firmware (via USB) into an unused region in flash and when done copy that data to the start of the flash and then reboot. So the function that copies from flash to flash and then waits for reboot must run entirely from memory and never ever from flash. This, however, is currently not possible because the function `rom_func_lookup`, which both `flash_range_erase` and `flash_range_program` call, is still in flash. When I wrap `rom_func_lookup` with a `__not_in_flash_func` (and make sure the compiler doesn't use `memcpy` in flash-to-flash) it all works fine.
kilograham commented 2021-08-24 15:04:50 -05:00 (Migrated from github.com)

Ah yes, that is a weird oversight

Ah yes, that is a weird oversight
lurch commented 2021-08-25 03:31:10 -05:00 (Migrated from github.com)

Is any of the discussion in #410 applicable here?

Is any of the discussion in #410 applicable here?
geurtv commented 2021-08-25 03:51:44 -05:00 (Migrated from github.com)

Hmm, hadn't noticed that issue. Yes, I'd say this is a duplicate.

Hmm, hadn't noticed that issue. Yes, I'd say this is a duplicate.
Wren6991 commented 2021-09-06 04:36:21 -05:00 (Migrated from github.com)

When I wrap rom_func_lookup with a __not_in_flash_func (and make sure the compiler doesn't use memcpy in flash-to-flash) it all works fine.

It might be worth adding that decorator, though the user in #410 also saw functions like this one fail to inline, which was surprising:

static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) {
    return (((uint)c2) << 8u) | (uint)c1;
}

Erasing the image you are currently XIPing from is pretty fraught, for the reasons mentioned in #410. With all the dependencies of hardware_flash either in RAM or force-inlined, it's still dangerous, because there is a period of many milliseconds where you have no bootable image in flash, because it just erased itself, so if you lose power, you brick your board until you can access the USB boot and reflash.

Slightly cleaner approach is to have a self-contained stub program in e.g. the first 4k of flash which:

  • Runs on every flash boot, because it's entered by the second stage
  • Checks watchdog registers to see if it should do a flash->flash copy, to install a new program image
  • Checks for a valid image immediately following it in flash, and jumps in in the same way that boot2 jumps in
  • If there is no valid image, go to a rescue/bootloader mode so that a new image can be installed

You then need to build your actual program to install and run at an offset into flash. You can take a copy of the default linker script, edit this line

MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k

To say

FLASH(rx) : ORIGIN = 0x10000000 + 4k, LENGTH = 2048k - 4k

And add a pico_set_linker_script() to your CMake file to select your modified linker script. This tells the linker to build your main program as though the first 4k of flash doesn't exist, so your main program can coexist with the bootloader stub at the start of flash.

> When I wrap rom_func_lookup with a __not_in_flash_func (and make sure the compiler doesn't use memcpy in flash-to-flash) it all works fine. It might be worth adding that decorator, though the user in #410 also saw functions like this one fail to inline, which was surprising: ```c static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) { return (((uint)c2) << 8u) | (uint)c1; } ``` Erasing the image you are currently XIPing from is pretty fraught, for the reasons mentioned in #410. With all the dependencies of `hardware_flash` either in RAM or force-inlined, it's still dangerous, because there is a period of many milliseconds where you have *no* bootable image in flash, because it just erased itself, so if you lose power, you brick your board until you can access the USB boot and reflash. Slightly cleaner approach is to have a self-contained stub program in e.g. the first 4k of flash which: - Runs on every flash boot, because it's entered by the second stage - Checks watchdog registers to see if it should do a flash->flash copy, to install a new program image - Checks for a valid image immediately following it in flash, and jumps in in the same way that boot2 jumps in - If there is no valid image, go to a rescue/bootloader mode so that a new image can be installed You then need to build your actual program to install and run at an offset into flash. You can take a copy of the [default linker script](https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_standard_link/memmap_default.ld), edit this line ``` MEMORY { FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k ``` To say ``` FLASH(rx) : ORIGIN = 0x10000000 + 4k, LENGTH = 2048k - 4k ``` And add a `pico_set_linker_script()` to your CMake file to select your modified linker script. This tells the linker to build your main program as though the first 4k of flash doesn't exist, so your main program can coexist with the bootloader stub at the start of flash.
kilograham commented 2021-10-04 16:23:08 -05:00 (Migrated from github.com)

This really should have been a macro but I don't want to change it now in case there is some weird reliance in existing code, but I am changing it to __force_inline which should solve the problem

This really should have been a macro but I don't want to change it now in case there is some weird reliance in existing code, but I am changing it to `__force_inline` which should solve the problem
kilograham commented 2021-10-12 09:17:42 -05:00 (Migrated from github.com)

merged into develop

merged into develop
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#545
No description provided.