Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TBYB safe rom_pick_ab_partition function #1850

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions src/rp2_common/pico_bootrom/bootrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include "pico/bootrom.h"
#include "boot/picoboot.h"
#include "boot/picobin.h"
#if !PICO_RP2040
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strictly more idiomatic to use #if HAS_RP2040_RTC

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the RCP not the RTC - given the functions in this file are also guarded by !PICO_RP2040 I think this if fine to use here too?

#include "hardware/rcp.h"
#endif

/// \tag::table_lookup[]

Expand Down Expand Up @@ -59,6 +62,23 @@ void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin
}

#if !PICO_RP2040


// Generated from adding the following code into the bootrom
// scan_workarea_t* scan_workarea = (scan_workarea_t*)workarea;
// printf("VERSION_DOWNGRADE_ERASE_ADDR %08x\n", &(always->zero_init.version_downgrade_erase_flash_addr));
// printf("TBYB_FLAG_ADDR %08x\n", &(always->zero_init.tbyb_flag_flash_addr));
// printf("IMAGE_DEF_VERIFIED %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.verified) - (uint32_t)scan_workarea);
// printf("IMAGE_DEF_TBYB_FLAGGED %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.tbyb_flagged) - (uint32_t)scan_workarea);
// printf("IMAGE_DEF_BASE %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.enclosing_window.base) - (uint32_t)scan_workarea);
// printf("IMAGE_DEF_REL_BLOCK_OFFSET %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.window_rel_block_offset) - (uint32_t)scan_workarea);
#define VERSION_DOWNGRADE_ERASE_ADDR *(uint32_t*)0x400e0338
#define TBYB_FLAG_ADDR *(uint32_t*)0x400e0348
#define IMAGE_DEF_VERIFIED(scan_workarea) *(uint32_t*)(0x64 + (uint32_t)scan_workarea)
#define IMAGE_DEF_TBYB_FLAGGED(scan_workarea) *(bool*)(0x4c + (uint32_t)scan_workarea)
#define IMAGE_DEF_BASE(scan_workarea) *(uint32_t*)(0x54 + (uint32_t)scan_workarea)
#define IMAGE_DEF_REL_BLOCK_OFFSET(scan_workarea) *(uint32_t*)(0x5c + (uint32_t)scan_workarea)

bool rom_get_boot_random(uint32_t out[4]) {
uint32_t result[5];
rom_get_sys_info_fn func = (rom_get_sys_info_fn) rom_func_lookup_inline(ROM_FUNC_GET_SYS_INFO);
Expand Down Expand Up @@ -99,4 +119,62 @@ int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32
}
return PICO_ERROR_INSUFFICIENT_RESOURCES;
}

int rom_pick_ab_update_partition(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num) {
uint32_t flash_update_base = 0;
bool tbyb_boot = false;
uint32_t saved_erase_addr = 0;
if (rom_get_last_boot_type() == BOOT_TYPE_FLASH_UPDATE) {
// For a flash update boot, get the flash update base
boot_info_t boot_info = {};
int ret = rom_get_boot_info(&boot_info);
if (ret) {
flash_update_base = boot_info.reboot_params[0];
if (boot_info.tbyb_and_update_info & BOOT_TBYB_AND_UPDATE_FLAG_BUY_PENDING) {
// A buy is pending, so the main software has not been bought
tbyb_boot = true;
// Save the erase address, as this will be overwritten by rom_pick_ab_partition
saved_erase_addr = VERSION_DOWNGRADE_ERASE_ADDR;
}
}
}

int rc = rom_pick_ab_partition((uint8_t*)workarea_base, workarea_size, partition_a_num, flash_update_base);

if (IMAGE_DEF_VERIFIED(workarea_base) != RCP_MASK_TRUE) {
// Chosen partition failed verification
return BOOTROM_ERROR_NOT_FOUND;
}

if (IMAGE_DEF_TBYB_FLAGGED(workarea_base)) {
// The chosen partition is TBYB
if (tbyb_boot) {
// The boot partition is also TBYB - cannot update both, so prioritise boot partition
// Restore the erase address saved earlier
VERSION_DOWNGRADE_ERASE_ADDR = saved_erase_addr;
return BOOTROM_ERROR_NOT_PERMITTED;
} else {
// Update the tbyb flash address, so that explicit_buy will clear the flag for the chosen partition
TBYB_FLAG_ADDR =
IMAGE_DEF_BASE(workarea_base)
+ IMAGE_DEF_REL_BLOCK_OFFSET(workarea_base) + 4;
}
} else {
// The chosen partition is not TBYB
if (tbyb_boot && saved_erase_addr) {
// The boot partition was TBYB, and requires an erase
if (VERSION_DOWNGRADE_ERASE_ADDR) {
// But both the chosen partition requires an erase too
// As before, prioritise the boot partition, and restore it's saved erase_address
VERSION_DOWNGRADE_ERASE_ADDR = saved_erase_addr;
return BOOTROM_ERROR_NOT_PERMITTED;
} else {
// The chosen partition doesn't require an erase, so we're fine
VERSION_DOWNGRADE_ERASE_ADDR = saved_erase_addr;
}
}
}

return rc;
}
#endif
16 changes: 16 additions & 0 deletions src/rp2_common/pico_bootrom/include/pico/bootrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,22 @@ static inline int rom_get_last_boot_type(void) {
*/
int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32_t permissions);

/*! \brief Pick A/B partition for a separate partition
* \ingroup pico_bootrom
*
* This will perform extra checks to prevent disrupting a main image TBYB, and return errors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to reword this slightly? "This will ... return errors" 😉

*
* Also checks that the chosen partition contained a valid image
*
* \param workarea_base base address of work area
* \param workarea_size size of work area
* @param partition_a_num the A partition of the pair
* @return >= 0 the partition number picked
* BOOTROM_ERROR_NOT_PERMITTED if not possible to do an update correctly, eg if both main image and data image are TBYB
* BOOTROM_ERROR_NOT_FOUND if the chosen partition failed verification
*/
int rom_pick_ab_update_partition(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num);

#endif

#ifdef __cplusplus
Expand Down
Loading