Skip to content

Add rom_pick_ab_update_partition function #2182

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

Merged
Merged
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
1 change: 1 addition & 0 deletions src/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ alias(
"//src/rp2_common/boot_bootrom_headers:__pkg__",
"//src/rp2_common/hardware_boot_lock:__pkg__",
"//src/rp2_common/pico_flash:__pkg__",
"//src/rp2_common/hardware_rcp:__pkg__",
],
)

Expand Down
13 changes: 13 additions & 0 deletions src/rp2_common/hardware_rcp/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ load("//bazel:defs.bzl", "compatible_with_rp2")

package(default_visibility = ["//visibility:public"])

# Picotool needs this (transitively through
# //src/rp2_common/pico_bootrom:pico_bootrom_headers), so we can't strictly
# constrain compatibility.
cc_library(
name = "hardware_rcp_headers",
hdrs = ["include/hardware/rcp.h"],
includes = ["include"],
visibility = ["//src/rp2_common/pico_bootrom:__pkg__"],
deps = [
"//src:pico_platform_internal",
],
)

cc_library(
name = "hardware_rcp",
hdrs = ["include/hardware/rcp.h"],
Expand Down
9 changes: 6 additions & 3 deletions src/rp2_common/hardware_rcp/include/hardware/rcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
*/

// ----------------------------------------------------------------------------
// RCP instructions (this header is Arm-only)
#if defined(PICO_RP2350) && !defined(__riscv)
// RCP masks
#if !PICO_RP2040

#define RCP_MASK_TRUE _u(0xa500a500)
#define RCP_MASK_FALSE _u(0x00c300c3)
#define RCP_MASK_INTXOR _u(0x96009600)

// RCP instructions (these instructions are Arm-only)
#if HAS_REDUNDANCY_COPROCESSOR
// ----------------------------------------------------------------------------
// Macros and inline functions for use in C files
#ifndef __ASSEMBLER__
Expand Down Expand Up @@ -994,7 +996,8 @@ rcp_switch_u8_to_ch_cl rcp_canary_check_nodelay_impl \tag, \x
cdp p7, #0, c0, c0, c0, #1
.endm

#endif // !__riscv
#endif // HAS_REDUNDANCY_COPROCESSOR
#endif // !PICO_RP2040
#endif // __ASSEMBLER__
// ----------------------------------------------------------------------------

Expand Down
1 change: 1 addition & 0 deletions src/rp2_common/pico_bootrom/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ cc_library(
"//src/rp2_common/boot_bootrom_headers",
"//src/rp2_common/hardware_boot_lock:hardware_boot_lock_headers",
"//src/rp2_common/pico_flash:pico_flash_headers",
"//src/rp2_common/hardware_rcp:hardware_rcp_headers",
] + select({
"//bazel/constraint:host": ["//src/host/hardware_sync"],
"//conditions:default": ["//src/rp2_common/hardware_sync"],
Expand Down
88 changes: 88 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
#include "hardware/rcp.h"
#endif

/// \tag::table_lookup[]

Expand Down Expand Up @@ -108,4 +111,89 @@ 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) {
#if PICO_RP2350
// 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)
#else
// Prevent linting errors
#define VERSION_DOWNGRADE_ERASE_ADDR *(uint32_t*)NULL
#define TBYB_FLAG_ADDR *(uint32_t*)NULL
#define IMAGE_DEF_VERIFIED(scan_workarea) *(uint32_t*)(NULL + (uint32_t)scan_workarea)
#define IMAGE_DEF_TBYB_FLAGGED(scan_workarea) *(bool*)(NULL + (uint32_t)scan_workarea)
#define IMAGE_DEF_BASE(scan_workarea) *(uint32_t*)(NULL + (uint32_t)scan_workarea)
#define IMAGE_DEF_REL_BLOCK_OFFSET(scan_workarea) *(uint32_t*)(NULL + (uint32_t)scan_workarea)

panic_unsupported();
#endif

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
27 changes: 27 additions & 0 deletions src/rp2_common/pico_bootrom/include/pico/bootrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,9 @@ static inline int rom_load_partition_table(uint8_t *workarea_base, uint32_t work
*
* NOTE: This method does not look at owner partitions, only the A partition passed and it's corresponding B partition.
*
* NOTE: You should not call this method directly when performing a Flash Update Boot before calling `explicit_buy`, as it may prevent
* any version downgrade from occuring - instead \see rom_pick_ab_update_partition() which wraps this function.
*
* \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
Expand Down Expand Up @@ -1091,6 +1094,30 @@ 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 without disturbing any in progress update or TBYB boot
* \ingroup pico_bootrom
*
* This will call `rom_pick_ab_partition` using the `flash_update_boot_window_base` from the current boot, while performing extra checks to prevent disrupting
* a main image TBYB boot. It requires the same minimum workarea size as `rom_pick_ab_partition`.
* \see rom_pick_ab_partition()
*
* For example, if an `explicit_buy` is pending then calling `pick_ab_partition` would normally clear the saved flash erase address for the version downgrade,
* so the required erase of the other partition would not occur when `explicit_buy` is called - this function saves and restores that address to prevent this
* issue, and returns `BOOTROM_ERROR_NOT_PERMITTED` if the partition chosen by `pick_ab_partition` also requires a flash erase version downgrade (as you can't
* erase 2 partitions with one `explicit_buy` call).
*
* It also checks that the chosen partition contained a valid image (e.g. a signed image when using secure boot), and returns `BOOTROM_ERROR_NOT_FOUND`
* if it does not.
*
* \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, e.g. 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