From 9845591b54c6cca4425a309c9aa1ac2c86d27c1c Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Tue, 26 May 2026 17:33:48 -0500 Subject: [PATCH 01/10] Add docs and optional work around to rom_reset_usb_boot* for RP2350A2 QFN60 Arm to reboot into RISC-V when acitivity LED is requested Also renamed rp2040_rom_version rp2350_rom_version for consistency (it doesn't appear to be used) --- .../pico_platform/include/pico/platform.h | 6 +++--- src/rp2_common/pico_bootrom/bootrom.c | 20 +++++++++++++++++-- .../pico_bootrom/include/pico/bootrom.h | 13 ++++++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/rp2350/pico_platform/include/pico/platform.h b/src/rp2350/pico_platform/include/pico/platform.h index 67cb80ebc9..20606550fb 100644 --- a/src/rp2350/pico_platform/include/pico/platform.h +++ b/src/rp2350/pico_platform/include/pico/platform.h @@ -201,11 +201,11 @@ static inline uint8_t rp2040_chip_version(void) { return 2; } -/*! \brief Returns the RP2040 rom version number +/*! \brief Returns the RP2350 rom version number * \ingroup pico_platform - * @return the RP2040 rom version number (1 for RP2040-B0, 2 for RP2040-B1, 3 for RP2040-B2) + * @return the RP2350 rom version number (2 for RP2350-A2, 3 for RP2350-A3, 3 for RP2350-A4) */ -static inline uint8_t rp2040_rom_version(void) { +static inline uint8_t rp2350_rom_version(void) { GCC_Pragma("GCC diagnostic push") GCC_Pragma("GCC diagnostic ignored \"-Warray-bounds\"") return *(uint8_t*)0x13; diff --git a/src/rp2_common/pico_bootrom/bootrom.c b/src/rp2_common/pico_bootrom/bootrom.c index 766574802f..25812ecb77 100644 --- a/src/rp2_common/pico_bootrom/bootrom.c +++ b/src/rp2_common/pico_bootrom/bootrom.c @@ -11,6 +11,8 @@ #include "hardware/rcp.h" #endif +// PICO_CONFIG: PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG, Workaround RP2350A-A2 (QFN60) bug not displaying USB boot activity LED under Arm by making rom_reset_usb_boot functions reboot to RISC-V when the activity LED is specified, type=bool, default=0, group=pico_bootrom + /// \tag::table_lookup[] void *rom_func_lookup(uint32_t code) { @@ -31,6 +33,19 @@ bool rom_funcs_lookup(uint32_t *table, unsigned int count) { return ok; } +// The activity LED on A2 QDN60 chips doesn't work in Arm mode, so boot into RISC-V if the user +// really, really wants the activity LED +#if PICO_RP2350_A2_SUPPORTED && PICO_RP2350A && !PICO_RISCV && PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG +#define rom_reboot_workaround(flags, delay_ms, p0, p1) ({ \ + if (rp2350_rom_version() == 2 && ((p0) & BOOTSEL_FLAG_GPIO_PIN_SPECIFIED)) \ + rom_reboot((flags) | REBOOT2_FLAG_REBOOT_TO_RISCV, delay_ms, p0, p1); \ + else \ + rom_reboot(flags, delay_ms, p0, p1); \ +}) +#else +#define rom_reboot_workaround(flags, delay_ms, p0, p1) rom_reboot(flags, delay_ms, p0, p1) +#endif + void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask) { #ifdef ROM_FUNC_RESET_USB_BOOT @@ -43,7 +58,7 @@ void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin // the parameter is actually the gpio number, but we only care if BOOTSEL_FLAG_GPIO_PIN_SPECIFIED usb_activity_gpio_pin_mask = (uint32_t)__builtin_ctz(usb_activity_gpio_pin_mask); } - rom_reboot(REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, 10, flags, usb_activity_gpio_pin_mask); + rom_reboot_workaround(REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, 10, flags, usb_activity_gpio_pin_mask); __builtin_unreachable(); #else panic_unsupported(); @@ -57,13 +72,14 @@ void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pi func(usb_activity_gpio_pin < 0 ? 0 : (1u << usb_activity_gpio_pin), disable_interface_mask); #elif defined(ROM_FUNC_REBOOT) uint32_t flags = disable_interface_mask; + uint reboot2_flags = REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS; if (usb_activity_gpio_pin >= 0) { flags |= BOOTSEL_FLAG_GPIO_PIN_SPECIFIED; if (usb_activity_gpio_pin_active_low) { flags |= BOOTSEL_FLAG_GPIO_PIN_ACTIVE_LOW; } } - rom_reboot(REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, 10, flags, (uint)usb_activity_gpio_pin); + rom_reboot_workaround(reboot2_flags, 10, flags, (uint)usb_activity_gpio_pin); __builtin_unreachable(); #else panic_unsupported(); diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index 667cece174..9a9c245c0e 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -250,7 +250,11 @@ static __force_inline void *rom_data_lookup_inline(uint32_t code) { * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, * and to limit the USB interfaces exposed. * - * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise a single bit set indicating which + * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define + * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * the activity LED correctly. + * + * * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which * GPIO pin should be set to output and raised whenever there is mass storage activity * from the host. * \param disable_interface_mask value to control exposed interfaces @@ -272,13 +276,18 @@ static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activit * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, * and to limit the USB interfaces exposed. * + * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define + * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * the activity LED correctly. + * * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none * from the host. * \param disable_interface_mask value to control exposed interfaces * - 0 To enable both interfaces (as per a cold boot) * - 1 To disable the USB Mass Storage Interface * - 2 To disable the USB PICOBOOT Interface - * \param usb_activity_gpio_pin_active_low Activity GPIO is active low (ignored on RP2040) + * \param usb_activity_gpio_pin_active_low Activity GPIO is active low (ignored on RP2040). A bug in the bootrom of RP2350 + * A4 chips means this parameter has no effect on that version of the RP2350. */ void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pin, uint32_t disable_interface_mask, bool usb_activity_gpio_pin_active_low); From 04a33ce267a8f36471c518b73d9a918111b28fcd Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Tue, 26 May 2026 17:37:45 -0500 Subject: [PATCH 02/10] remove changes from previous version of the fix --- src/rp2_common/pico_bootrom/bootrom.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rp2_common/pico_bootrom/bootrom.c b/src/rp2_common/pico_bootrom/bootrom.c index 25812ecb77..574b29541a 100644 --- a/src/rp2_common/pico_bootrom/bootrom.c +++ b/src/rp2_common/pico_bootrom/bootrom.c @@ -72,14 +72,13 @@ void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pi func(usb_activity_gpio_pin < 0 ? 0 : (1u << usb_activity_gpio_pin), disable_interface_mask); #elif defined(ROM_FUNC_REBOOT) uint32_t flags = disable_interface_mask; - uint reboot2_flags = REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS; if (usb_activity_gpio_pin >= 0) { flags |= BOOTSEL_FLAG_GPIO_PIN_SPECIFIED; if (usb_activity_gpio_pin_active_low) { flags |= BOOTSEL_FLAG_GPIO_PIN_ACTIVE_LOW; } } - rom_reboot_workaround(reboot2_flags, 10, flags, (uint)usb_activity_gpio_pin); + rom_reboot_workaround(REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, 10, flags, (uint)usb_activity_gpio_pin); __builtin_unreachable(); #else panic_unsupported(); From 11e3bb5717aec335ea6d1280e7effd5d797aac83 Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Tue, 26 May 2026 17:38:29 -0500 Subject: [PATCH 03/10] reorder if --- src/rp2_common/pico_bootrom/bootrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rp2_common/pico_bootrom/bootrom.c b/src/rp2_common/pico_bootrom/bootrom.c index 574b29541a..c899918d03 100644 --- a/src/rp2_common/pico_bootrom/bootrom.c +++ b/src/rp2_common/pico_bootrom/bootrom.c @@ -37,7 +37,7 @@ bool rom_funcs_lookup(uint32_t *table, unsigned int count) { // really, really wants the activity LED #if PICO_RP2350_A2_SUPPORTED && PICO_RP2350A && !PICO_RISCV && PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG #define rom_reboot_workaround(flags, delay_ms, p0, p1) ({ \ - if (rp2350_rom_version() == 2 && ((p0) & BOOTSEL_FLAG_GPIO_PIN_SPECIFIED)) \ + if (((p0) & BOOTSEL_FLAG_GPIO_PIN_SPECIFIED) && rp2350_rom_version() == 2) \ rom_reboot((flags) | REBOOT2_FLAG_REBOOT_TO_RISCV, delay_ms, p0, p1); \ else \ rom_reboot(flags, delay_ms, p0, p1); \ From f9da7c0848ee1feab08dc313cb9e958fd79e26be Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Tue, 26 May 2026 17:39:02 -0500 Subject: [PATCH 04/10] oops --- src/rp2350/pico_platform/include/pico/platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rp2350/pico_platform/include/pico/platform.h b/src/rp2350/pico_platform/include/pico/platform.h index 20606550fb..059b2d9991 100644 --- a/src/rp2350/pico_platform/include/pico/platform.h +++ b/src/rp2350/pico_platform/include/pico/platform.h @@ -203,7 +203,7 @@ static inline uint8_t rp2040_chip_version(void) { /*! \brief Returns the RP2350 rom version number * \ingroup pico_platform - * @return the RP2350 rom version number (2 for RP2350-A2, 3 for RP2350-A3, 3 for RP2350-A4) + * @return the RP2350 rom version number (2 for RP2350-A2, 3 for RP2350-A3, 4 for RP2350-A4) */ static inline uint8_t rp2350_rom_version(void) { GCC_Pragma("GCC diagnostic push") From 8ee2043f80443d100ff40f90e1306268fae42e27 Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Thu, 28 May 2026 15:44:18 -0500 Subject: [PATCH 05/10] Apply suggestions from code review Co-authored-by: Andrew Scheller Co-authored-by: will-v-pi <108662275+will-v-pi@users.noreply.github.com> --- src/rp2350/pico_platform/include/pico/platform.h | 2 +- src/rp2_common/pico_bootrom/bootrom.c | 2 +- src/rp2_common/pico_bootrom/include/pico/bootrom.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rp2350/pico_platform/include/pico/platform.h b/src/rp2350/pico_platform/include/pico/platform.h index 059b2d9991..5ade2804e3 100644 --- a/src/rp2350/pico_platform/include/pico/platform.h +++ b/src/rp2350/pico_platform/include/pico/platform.h @@ -189,7 +189,7 @@ __force_inline static bool pico_processor_state_is_nonsecure(void) { /*! \brief Returns the RP2350 chip revision number * \ingroup pico_platform - * @return the RP2350 chip revision number (1 for B0/B1, 2 for B2) + * @return the RP2350 chip revision number (2 for A2, 3 for A3/A4) */ uint8_t rp2350_chip_version(void); diff --git a/src/rp2_common/pico_bootrom/bootrom.c b/src/rp2_common/pico_bootrom/bootrom.c index c899918d03..fae36a6b8d 100644 --- a/src/rp2_common/pico_bootrom/bootrom.c +++ b/src/rp2_common/pico_bootrom/bootrom.c @@ -33,7 +33,7 @@ bool rom_funcs_lookup(uint32_t *table, unsigned int count) { return ok; } -// The activity LED on A2 QDN60 chips doesn't work in Arm mode, so boot into RISC-V if the user +// The activity LED on RP2350 A2 QFN60 chips doesn't work in Arm mode, so boot into RISC-V if the user // really, really wants the activity LED #if PICO_RP2350_A2_SUPPORTED && PICO_RP2350A && !PICO_RISCV && PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG #define rom_reboot_workaround(flags, delay_ms, p0, p1) ({ \ diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index 9a9c245c0e..480ebc24cf 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -254,7 +254,7 @@ static __force_inline void *rom_data_lookup_inline(uint32_t code) { * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display * the activity LED correctly. * - * * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which + * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which * GPIO pin should be set to output and raised whenever there is mass storage activity * from the host. * \param disable_interface_mask value to control exposed interfaces From 90142b9dfd9b035dfa704a742f630d6cd6ced222 Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Thu, 28 May 2026 15:45:29 -0500 Subject: [PATCH 06/10] reference correct errata --- .../pico_bootrom/include/pico/bootrom.h | 2256 ++++++++--------- 1 file changed, 1128 insertions(+), 1128 deletions(-) diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index 480ebc24cf..1593ec5c0b 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -1,1129 +1,1129 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef _PICO_BOOTROM_H -#define _PICO_BOOTROM_H - -#include "pico.h" -#include "pico/bootrom_constants.h" - -/** \file bootrom.h - * \defgroup pico_bootrom pico_bootrom - * \brief Access to functions and data in the bootrom - * - * This header may be included by assembly code - */ - -#ifndef __ASSEMBLER__ -#include -#include "pico/bootrom/lock.h" -#include "pico/flash.h" -// ROM FUNCTION SIGNATURES - -#if PICO_RP2040 -typedef uint32_t (*rom_popcount32_fn)(uint32_t); -typedef uint32_t (*rom_reverse32_fn)(uint32_t); -typedef uint32_t (*rom_clz32_fn)(uint32_t); -typedef uint32_t (*rom_ctz32_fn)(uint32_t); -typedef uint8_t *(*rom_memset_fn)(uint8_t *, uint8_t, uint32_t); -typedef uint32_t *(*rom_memset4_fn)(uint32_t *, uint8_t, uint32_t); -typedef uint32_t *(*rom_memcpy_fn)(uint8_t *, const uint8_t *, uint32_t); -typedef uint32_t *(*rom_memcpy44_fn)(uint32_t *, const uint32_t *, uint32_t); -#endif -typedef void __attribute__((noreturn)) (*rom_reset_usb_boot_fn)(uint32_t, uint32_t); -typedef int (*rom_reboot_fn)(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1); -typedef rom_reset_usb_boot_fn reset_usb_boot_fn; // kept for backwards compatibility -typedef void (*rom_connect_internal_flash_fn)(void); -typedef void (*rom_flash_exit_xip_fn)(void); -typedef void (*rom_flash_range_erase_fn)(uint32_t, size_t, uint32_t, uint8_t); -typedef void (*rom_flash_range_program_fn)(uint32_t, const uint8_t*, size_t); -typedef void (*rom_flash_flush_cache_fn)(void); -typedef void (*rom_flash_enter_cmd_xip_fn)(void); -#if !PICO_RP2040 -typedef void (*rom_bootrom_state_reset_fn)(uint32_t flags); -typedef void (*rom_flash_reset_address_trans_fn)(void); -typedef void (*rom_flash_select_xip_read_mode_fn)(bootrom_xip_mode_t mode, uint8_t clkdiv); -typedef int (*rom_get_sys_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags); -typedef int (*rom_get_partition_table_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags); -typedef int (*rom_explicit_buy_fn)(uint8_t *buffer, uint32_t buffer_size); -typedef void* (*rom_validate_ns_buffer_fn)(const void *addr, uint32_t size, uint32_t write, uint32_t *ok); -/** - * @return BOOTROM_OK if successful - * BOOTROM_ERROR_INVALID_ARG if ns_api_num is out of range - */ -typedef intptr_t (*rom_set_rom_callback_fn)(uint callback_num, bootrom_api_callback_generic_t funcptr); -typedef int (*rom_chain_image_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t window_base, uint32_t window_size); -typedef int (*rom_load_partition_table_fn)(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload); -typedef int (*rom_pick_ab_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base); -typedef int (*rom_get_b_partition_fn)(uint pi_a); -typedef int (*rom_get_uf2_target_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out); -typedef int (*rom_func_otp_access_fn)(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd); -// Apply the address translation currently specified in QMI_ATRANSx ("rolling window" hardware -// translation). Need to take care using this on the boot path, as the QMI may not yet have been -// set up, but this should be suitable for translating system bus addresses into flash storage -// addresses in user callbacks. Returns all-ones for an invalid address, which is also an invalid -// flash storage address, so invalidity is propagated. -typedef intptr_t (*rom_flash_runtime_to_storage_addr_fn)(uintptr_t flash_runtime_addr); - -// Perform the specified erase/program/read operation, translating addresses according to -// QMI_ATRANSx if necessary, and checking flash permissions based on the resident partition table -// and the specified effective security level. `addr` may be either a flash runtime address or a -// flash storage address, depending on the ASPACE given in `flags`. -// -// NOTE: This function does not validate the buffer for NS access. This must be validated before -// calling if the caller is reachable from a Secure Gateway. -typedef int (*rom_flash_op_fn)(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf); - -#ifndef __riscv -typedef int (*rom_set_ns_api_permission_fn)(uint ns_api_num, bool allowed); -/** - * Note this is not strictly a C function; you must pass the function you are calling in r4 - * @param in_r4 - * `0b0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "well known" function selector; do not use for your own methods - * `0b10xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "unique" function selector intended to be unlikely to clash with others'. - * The lower 30 bits should be chosen at random - * `0b11xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "private" function selector intended for use by tightly coupled NS and S code - * - * @return whatever the secure call returns - * BOOTROM_ERROR_INVALID_STATE if no secure handler has been set from the secure side - * via rom_set_rom_callback_fn(BOOTROM_API_CALLBACK_secure_call, ...) - */ -typedef int (*rom_func_secure_call)(uintptr_t a0, ...); -#endif - -#ifdef __riscv -typedef struct { - uint32_t *base; - uint32_t size; -} bootrom_stack_t; -// passed in, and out. -typedef int (*rom_set_bootrom_stack_fn)(bootrom_stack_t *stack); -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/*! \brief Return a bootrom lookup code based on two ASCII characters - * \ingroup pico_bootrom - * - * These codes are uses to lookup data or function addresses in the bootrom - * - * \param c1 the first character - * \param c2 the second character - * \return the 'code' to use in rom_func_lookup() or rom_data_lookup() - */ -static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) { - return ROM_TABLE_CODE((uint32_t) c1, (uint32_t) c2); -} - -/*! - * \brief Lookup a bootrom function by its code - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the function, or NULL if the code does not match any bootrom function - */ -void *rom_func_lookup(uint32_t code); - -/*! - * \brief Lookup a bootrom data address by its code - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the data, or NULL if the code does not match any bootrom function - */ -void *rom_data_lookup(uint32_t code); - -/*! - * \brief Helper function to lookup the addresses of multiple bootrom functions - * \ingroup pico_bootrom - * - * This method looks up the 'codes' in the table, and convert each table entry to the looked up - * function pointer, if there is a function for that code in the bootrom. - * - * \param table an IN/OUT array, elements are codes on input, function pointers on success. - * \param count the number of elements in the table - * \return true if all the codes were found, and converted to function pointers, false otherwise - */ -bool rom_funcs_lookup(uint32_t *table, unsigned int count); - -// Bootrom function: rom_table_lookup -// Returns the 32 bit pointer into the ROM if found or NULL otherwise. -#if PICO_RP2040 -typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code); -#else -typedef void *(*rom_table_lookup_fn)(uint32_t code, uint32_t mask); -#endif - -#if PICO_C_COMPILER_IS_GNU && (__GNUC__ >= 12) -// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer -__force_inline static void *rom_hword_as_ptr(uint16_t rom_address) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" - return (void *)(uintptr_t)*(uint16_t *)(uintptr_t)rom_address; -#pragma GCC diagnostic pop -} -#else -// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer -#define rom_hword_as_ptr(rom_address) (void *)(uintptr_t)(*(uint16_t *)(uintptr_t)(rom_address)) -#endif - -#ifdef __riscv -static __force_inline bool rom_size_is_64k(void) { -#ifdef RASPBERRYPI_AMETHYST_FPGA - return *(uint16_t*)0x14 >= 0x8000; -#else - return false; -#endif -} -#endif - -/*! - * \brief Lookup a bootrom function by code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the function, or NULL if the code does not match any bootrom function - */ -#pragma GCC diagnostic push -// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not -#pragma GCC diagnostic ignored "-Warray-bounds" -static __force_inline void *rom_func_lookup_inline(uint32_t code) { -#if PICO_RP2040 - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); - uint16_t *func_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_FUNC_TABLE_OFFSET); - return rom_table_lookup(func_table, code); -#else -#ifdef __riscv - uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; - // on RISC-V the code (a jmp) is actually embedded in the table - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_ENTRY_OFFSET + rom_offset_adjust); - return rom_table_lookup(code, RT_FLAG_FUNC_RISCV); -#else - // on ARM the function pointer is stored in the table, so we dereference it - // via lookup() rather than lookup_entry() - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); - if (pico_processor_state_is_nonsecure()) { - return rom_table_lookup(code, RT_FLAG_FUNC_ARM_NONSEC); - } else { - return rom_table_lookup(code, RT_FLAG_FUNC_ARM_SEC); - } -#endif -#endif -} -#pragma GCC diagnostic pop - -/*! - * \brief Lookup a bootrom data address by its code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the data, or NULL if the code does not match any bootrom data - */ -#pragma GCC diagnostic push -// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not -#pragma GCC diagnostic ignored "-Warray-bounds" -static __force_inline void *rom_data_lookup_inline(uint32_t code) { -#if PICO_RP2040 - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); - uint16_t *data_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_DATA_TABLE_OFFSET); - return rom_table_lookup(data_table, code); -#else -#ifdef __riscv - uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET + rom_offset_adjust); -#else - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); -#endif - return rom_table_lookup(code, RT_FLAG_DATA); -#endif -} -#pragma GCC diagnostic pop - -/*! - * \brief Reboot the device into BOOTSEL mode - * \ingroup pico_bootrom - * - * This function reboots the device into the BOOTSEL mode ('usb boot"). - * - * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, - * and to limit the USB interfaces exposed. - * - * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display - * the activity LED correctly. - * +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_BOOTROM_H +#define _PICO_BOOTROM_H + +#include "pico.h" +#include "pico/bootrom_constants.h" + +/** \file bootrom.h + * \defgroup pico_bootrom pico_bootrom + * \brief Access to functions and data in the bootrom + * + * This header may be included by assembly code + */ + +#ifndef __ASSEMBLER__ +#include +#include "pico/bootrom/lock.h" +#include "pico/flash.h" +// ROM FUNCTION SIGNATURES + +#if PICO_RP2040 +typedef uint32_t (*rom_popcount32_fn)(uint32_t); +typedef uint32_t (*rom_reverse32_fn)(uint32_t); +typedef uint32_t (*rom_clz32_fn)(uint32_t); +typedef uint32_t (*rom_ctz32_fn)(uint32_t); +typedef uint8_t *(*rom_memset_fn)(uint8_t *, uint8_t, uint32_t); +typedef uint32_t *(*rom_memset4_fn)(uint32_t *, uint8_t, uint32_t); +typedef uint32_t *(*rom_memcpy_fn)(uint8_t *, const uint8_t *, uint32_t); +typedef uint32_t *(*rom_memcpy44_fn)(uint32_t *, const uint32_t *, uint32_t); +#endif +typedef void __attribute__((noreturn)) (*rom_reset_usb_boot_fn)(uint32_t, uint32_t); +typedef int (*rom_reboot_fn)(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1); +typedef rom_reset_usb_boot_fn reset_usb_boot_fn; // kept for backwards compatibility +typedef void (*rom_connect_internal_flash_fn)(void); +typedef void (*rom_flash_exit_xip_fn)(void); +typedef void (*rom_flash_range_erase_fn)(uint32_t, size_t, uint32_t, uint8_t); +typedef void (*rom_flash_range_program_fn)(uint32_t, const uint8_t*, size_t); +typedef void (*rom_flash_flush_cache_fn)(void); +typedef void (*rom_flash_enter_cmd_xip_fn)(void); +#if !PICO_RP2040 +typedef void (*rom_bootrom_state_reset_fn)(uint32_t flags); +typedef void (*rom_flash_reset_address_trans_fn)(void); +typedef void (*rom_flash_select_xip_read_mode_fn)(bootrom_xip_mode_t mode, uint8_t clkdiv); +typedef int (*rom_get_sys_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags); +typedef int (*rom_get_partition_table_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags); +typedef int (*rom_explicit_buy_fn)(uint8_t *buffer, uint32_t buffer_size); +typedef void* (*rom_validate_ns_buffer_fn)(const void *addr, uint32_t size, uint32_t write, uint32_t *ok); +/** + * @return BOOTROM_OK if successful + * BOOTROM_ERROR_INVALID_ARG if ns_api_num is out of range + */ +typedef intptr_t (*rom_set_rom_callback_fn)(uint callback_num, bootrom_api_callback_generic_t funcptr); +typedef int (*rom_chain_image_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t window_base, uint32_t window_size); +typedef int (*rom_load_partition_table_fn)(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload); +typedef int (*rom_pick_ab_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base); +typedef int (*rom_get_b_partition_fn)(uint pi_a); +typedef int (*rom_get_uf2_target_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out); +typedef int (*rom_func_otp_access_fn)(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd); +// Apply the address translation currently specified in QMI_ATRANSx ("rolling window" hardware +// translation). Need to take care using this on the boot path, as the QMI may not yet have been +// set up, but this should be suitable for translating system bus addresses into flash storage +// addresses in user callbacks. Returns all-ones for an invalid address, which is also an invalid +// flash storage address, so invalidity is propagated. +typedef intptr_t (*rom_flash_runtime_to_storage_addr_fn)(uintptr_t flash_runtime_addr); + +// Perform the specified erase/program/read operation, translating addresses according to +// QMI_ATRANSx if necessary, and checking flash permissions based on the resident partition table +// and the specified effective security level. `addr` may be either a flash runtime address or a +// flash storage address, depending on the ASPACE given in `flags`. +// +// NOTE: This function does not validate the buffer for NS access. This must be validated before +// calling if the caller is reachable from a Secure Gateway. +typedef int (*rom_flash_op_fn)(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf); + +#ifndef __riscv +typedef int (*rom_set_ns_api_permission_fn)(uint ns_api_num, bool allowed); +/** + * Note this is not strictly a C function; you must pass the function you are calling in r4 + * @param in_r4 + * `0b0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "well known" function selector; do not use for your own methods + * `0b10xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "unique" function selector intended to be unlikely to clash with others'. + * The lower 30 bits should be chosen at random + * `0b11xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "private" function selector intended for use by tightly coupled NS and S code + * + * @return whatever the secure call returns + * BOOTROM_ERROR_INVALID_STATE if no secure handler has been set from the secure side + * via rom_set_rom_callback_fn(BOOTROM_API_CALLBACK_secure_call, ...) + */ +typedef int (*rom_func_secure_call)(uintptr_t a0, ...); +#endif + +#ifdef __riscv +typedef struct { + uint32_t *base; + uint32_t size; +} bootrom_stack_t; +// passed in, and out. +typedef int (*rom_set_bootrom_stack_fn)(bootrom_stack_t *stack); +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \brief Return a bootrom lookup code based on two ASCII characters + * \ingroup pico_bootrom + * + * These codes are uses to lookup data or function addresses in the bootrom + * + * \param c1 the first character + * \param c2 the second character + * \return the 'code' to use in rom_func_lookup() or rom_data_lookup() + */ +static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) { + return ROM_TABLE_CODE((uint32_t) c1, (uint32_t) c2); +} + +/*! + * \brief Lookup a bootrom function by its code + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the function, or NULL if the code does not match any bootrom function + */ +void *rom_func_lookup(uint32_t code); + +/*! + * \brief Lookup a bootrom data address by its code + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the data, or NULL if the code does not match any bootrom function + */ +void *rom_data_lookup(uint32_t code); + +/*! + * \brief Helper function to lookup the addresses of multiple bootrom functions + * \ingroup pico_bootrom + * + * This method looks up the 'codes' in the table, and convert each table entry to the looked up + * function pointer, if there is a function for that code in the bootrom. + * + * \param table an IN/OUT array, elements are codes on input, function pointers on success. + * \param count the number of elements in the table + * \return true if all the codes were found, and converted to function pointers, false otherwise + */ +bool rom_funcs_lookup(uint32_t *table, unsigned int count); + +// Bootrom function: rom_table_lookup +// Returns the 32 bit pointer into the ROM if found or NULL otherwise. +#if PICO_RP2040 +typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code); +#else +typedef void *(*rom_table_lookup_fn)(uint32_t code, uint32_t mask); +#endif + +#if PICO_C_COMPILER_IS_GNU && (__GNUC__ >= 12) +// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer +__force_inline static void *rom_hword_as_ptr(uint16_t rom_address) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + return (void *)(uintptr_t)*(uint16_t *)(uintptr_t)rom_address; +#pragma GCC diagnostic pop +} +#else +// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer +#define rom_hword_as_ptr(rom_address) (void *)(uintptr_t)(*(uint16_t *)(uintptr_t)(rom_address)) +#endif + +#ifdef __riscv +static __force_inline bool rom_size_is_64k(void) { +#ifdef RASPBERRYPI_AMETHYST_FPGA + return *(uint16_t*)0x14 >= 0x8000; +#else + return false; +#endif +} +#endif + +/*! + * \brief Lookup a bootrom function by code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the function, or NULL if the code does not match any bootrom function + */ +#pragma GCC diagnostic push +// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not +#pragma GCC diagnostic ignored "-Warray-bounds" +static __force_inline void *rom_func_lookup_inline(uint32_t code) { +#if PICO_RP2040 + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); + uint16_t *func_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_FUNC_TABLE_OFFSET); + return rom_table_lookup(func_table, code); +#else +#ifdef __riscv + uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; + // on RISC-V the code (a jmp) is actually embedded in the table + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_ENTRY_OFFSET + rom_offset_adjust); + return rom_table_lookup(code, RT_FLAG_FUNC_RISCV); +#else + // on ARM the function pointer is stored in the table, so we dereference it + // via lookup() rather than lookup_entry() + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); + if (pico_processor_state_is_nonsecure()) { + return rom_table_lookup(code, RT_FLAG_FUNC_ARM_NONSEC); + } else { + return rom_table_lookup(code, RT_FLAG_FUNC_ARM_SEC); + } +#endif +#endif +} +#pragma GCC diagnostic pop + +/*! + * \brief Lookup a bootrom data address by its code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the data, or NULL if the code does not match any bootrom data + */ +#pragma GCC diagnostic push +// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not +#pragma GCC diagnostic ignored "-Warray-bounds" +static __force_inline void *rom_data_lookup_inline(uint32_t code) { +#if PICO_RP2040 + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); + uint16_t *data_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_DATA_TABLE_OFFSET); + return rom_table_lookup(data_table, code); +#else +#ifdef __riscv + uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET + rom_offset_adjust); +#else + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); +#endif + return rom_table_lookup(code, RT_FLAG_DATA); +#endif +} +#pragma GCC diagnostic pop + +/*! + * \brief Reboot the device into BOOTSEL mode + * \ingroup pico_bootrom + * + * This function reboots the device into the BOOTSEL mode ('usb boot"). + * + * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, + * and to limit the USB interfaces exposed. + * + * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define + * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * the activity LED correctly. + * * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which - * GPIO pin should be set to output and raised whenever there is mass storage activity - * from the host. - * \param disable_interface_mask value to control exposed interfaces - * - 0 To enable both interfaces (as per a cold boot) - * - 1 To disable the USB Mass Storage Interface - * - 2 To disable the USB PICOBOOT Interface - */ -void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask); -static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask) { - rom_reset_usb_boot(usb_activity_gpio_pin_mask, disable_interface_mask); -} - -/*! - * \brief Reboot the device into BOOTSEL mode - * \ingroup pico_bootrom - * - * This function reboots the device into the BOOTSEL mode ('usb boot"). - * - * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, - * and to limit the USB interfaces exposed. - * - * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display - * the activity LED correctly. - * - * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none - * from the host. - * \param disable_interface_mask value to control exposed interfaces - * - 0 To enable both interfaces (as per a cold boot) - * - 1 To disable the USB Mass Storage Interface - * - 2 To disable the USB PICOBOOT Interface - * \param usb_activity_gpio_pin_active_low Activity GPIO is active low (ignored on RP2040). A bug in the bootrom of RP2350 - * A4 chips means this parameter has no effect on that version of the RP2350. - */ -void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pin, uint32_t disable_interface_mask, bool usb_activity_gpio_pin_active_low); - -/*! - * \brief Connect the SSI/QMI to the QSPI pads - * \ingroup pico_bootrom - * - * Restore all QSPI pad controls to their default state, and connect the SSI/QMI peripheral to the QSPI pads. - * - * \if rp2350_specific - * On RP2350 if a secondary flash chip select GPIO has been configured via OTP OTP_DATA_FLASH_DEVINFO, or by writing to the runtime - * copy of FLASH_DEVINFO in bootram, then this bank 0 GPIO is also initialised and the QMI peripheral is connected. Otherwise, - * bank 0 IOs are untouched. - * \endif - */ -static inline void rom_connect_internal_flash(void) { - rom_connect_internal_flash_fn func = (rom_connect_internal_flash_fn) rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); - func(); -} - -/*! - * \brief Return the QSPI device from its XIP state to a serial command state - * \ingroup pico_bootrom - * - * \if rp2040_specific - * On RP2040, first set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in Section 2.8.1.2 - * of the datasheet. Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be cleared before returning - * the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This function configures the SSI with a fixed SCK clock divisor of /6. - * \endif - * - * \if rp2350_specific - * On RP2350, Initialise the QMI for serial operations (direct mode), and also initialise a basic XIP mode, where the QMI will perform - * 03h serial read commands at low speed (CLKDIV=12) in response to XIP reads. - * - * Then, issue a sequence to the QSPI device on chip select 0, designed to return it from continuous read mode ("XIP mode") and/or - * QPI mode to a state where it will accept serial commands. This is necessary after system reset to restore the QSPI device to a known - * state, because resetting RP2350 does not reset attached QSPI devices. It is also necessary when user code, having already performed - * some continuous-read-mode or QPI-mode accesses, wishes to return the QSPI device to a state where it will accept the serial erase and - * programming commands issued by the bootrom's flash access functions. - * - * If a GPIO for the secondary chip select is configured via FLASH_DEVINFO, then the XIP exit sequence is also issued to chip select 1. - * - * The QSPI device should be accessible for XIP reads after calling this function; the name flash_exit_xip refers to returning the QSPI - * device from its XIP state to a serial command state. - * \endif - */ -static inline void rom_flash_exit_xip(void) { - rom_flash_exit_xip_fn func = (rom_flash_exit_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); - func(); -} - -/*! - * \brief Erase bytes in flash - * \ingroup pico_bootrom - * - * Erase count bytes, starting at addr (offset from start of flash). Optionally, pass a block erase command e.g. D8h block erase, - * and the size of the block erased by this command - this function will use the larger block erase where possible, for much higher - * erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of 4096 bytes. - * - * This is a low-level flash API, and no validation of the arguments is performed. - * - * \if rp2350_specific - * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, and can transparently - * apply a runtime-to-storage address translation. - * - * The QSPI device must be in a serial command state before calling this API, which can be achieved by calling rom_connect_internal_flash() - * followed by rom_flash_exit_xip(). After the erase, the flash cache should be flushed via rom_flash_flush_cache() to ensure the modified - * flash data is visible to cached XIP accesses. - * - * Finally, the original XIP mode should be restored by copying the saved XIP setup function from bootram into SRAM, and executing it: - * the bootrom provides a default function which restores the flash mode/clkdiv discovered during flash scanning, and user programs can - * override this with their own XIP setup function. - * - * For the duration of the erase operation, QMI is in direct mode and attempting to access XIP from DMA, the debugger or the other core will - * return a bus fault. XIP becomes accessible again once the function returns. - * \endif - * - * \param addr the offset from start of flash to be erased - * \param count number of bytes to erase - * \param block_size optional size of block erased by block_cmd - * \param block_cmd optional block erase command e.g. D8h block erase - */ -static inline void rom_flash_range_erase(uint32_t addr, size_t count, uint32_t block_size, uint8_t block_cmd) { - rom_flash_range_erase_fn func = (rom_flash_range_erase_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_ERASE); - func(addr, count, block_size, block_cmd); -} - -/*! - * \brief Program bytes in flash - * \ingroup pico_bootrom - * - * Program data to a range of flash addresses starting at addr (offset from the start of flash) and count bytes in size. addr must be - * aligned to a 256-byte boundary, and count must be a multiple of 256. - * - * This is a low-level flash API, and no validation of the arguments is performed. - * - * \if rp2350_specific - * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, - * and can transparently apply a runtime-to-storage address translation. - * - * The QSPI device must be in a serial command state before calling this API - see notes on rom_flash_range_erase - * \endif - * - * \param addr the offset from start of flash to be erased - * \param data buffer containing the data to be written - * \param count number of bytes to erase - */ -static inline void rom_flash_range_program(uint32_t addr, const uint8_t *data, size_t count) { - rom_flash_range_program_fn func = (rom_flash_range_program_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); - func(addr, data, count); -} - -/*! - * \brief Flush the XIP cache - * \ingroup pico_bootrom - * - * \if rp2040_specific - * Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can drive the flash chip select as normal. - * \endif - * - * \if rp2350_specific - * Flush the entire XIP cache, by issuing an invalidate by set/way maintenance operation to every cache line. This ensures that flash - * program/erase operations are visible to subsequent cached XIP reads. - * - * Note that this unpins pinned cache lines, which may interfere with cache-as-SRAM use of the XIP cache. - * - * No other operations are performed. - * \endif - */ -static inline void rom_flash_flush_cache(void) { - rom_flash_flush_cache_fn func = (rom_flash_flush_cache_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); - func(); -} - -/*! - * \brief Configure the SSI/QMI with a standard command - * \ingroup pico_bootrom - * - * Configure the SSI/QMI to generate a standard 03h serial read command, with 24 address bits, upon each XIP access. This is a slow XIP - * configuration, but is widely supported. CLKDIV is set to 12 on RP2350. The debugger may call this function to ensure that flash is - * readable following a program/erase operation. - * - * Note that the same setup is performed by flash_exit_xip(), and the RP2350 flash program/erase functions do not leave XIP in an - * inaccessible state, so calls to this function are largely redundant on RP2350. It is provided on RP2350 for compatibility with RP2040. - */ -static inline void rom_flash_enter_cmd_xip(void) { - rom_flash_enter_cmd_xip_fn func = (rom_flash_enter_cmd_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_ENTER_CMD_XIP); - func(); -} - -#if !PICO_RP2040 || PICO_COMBINED_DOCS -#ifdef __riscv -/*! - * \brief Give the bootrom a new stack - * \ingroup pico_bootrom - * - * Most bootrom functions are written just once, in Arm code, to save space. As a result these functions are emulated when - * running under the RISC-V architecture. This is largely transparent to the user, however the stack used by the Arm emulation - * is separate from the calling user's stack, and is stored in boot RAM but is of quite limited size. When using certain of the more - * complex APIs or if nesting bootrom calls from within IRQs, you may need to provide a large stack. - * - * This method allows the caller to specify a region of RAM to use as the stack for the current core by passing a pointer to two values: the word aligned base address, - * and the size in bytes (multiple of 4). - * - * The method fills in the previous base/size values into the passed array before returning. - * - * \param stack bootrom_stack_t struct containing base and size - */ -static inline int rom_set_bootrom_stack(bootrom_stack_t *stack) { - rom_set_bootrom_stack_fn func = (rom_set_bootrom_stack_fn) rom_func_lookup_inline(ROM_FUNC_SET_BOOTROM_STACK); - return func(stack); -} -#endif - -/*! - * \brief Reboot using the watchdog - * \ingroup pico_bootrom - * - * Resets the chip and uses the watchdog facility to restart. - * - * The delay_ms is the millisecond delay before the reboot occurs. Note: by default this method is asynchronous - * (unless NO_RETURN_ON_SUCCESS is set - see below), so the method will return and the reboot will happen this many milliseconds later. - * - * The flags field contains one of the following values: - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL - reboot into the normal boot path. - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL - reboot into BOOTSEL mode. - * p0 - a set of flags: - * 0x01 : DISABLE_MSD_INTERFACE - Disable the BOOTSEL USB drive (see <>) - * 0x02 : DISABLE_PICOBOOT_INTERFACE - Disable the {picoboot} interface (see <>). - * 0x10 : GPIO_PIN_ACTIVE_LOW - The GPIO specified in p1 is active low (GPIO_PIN_SPECIFIED must also be set). - * 0x20 : GPIO_PIN_SPECIFIED - Enable the activity indicator on the GPIO specified in p1. - * p1 - the GPIO number to use as an activity indicator (enabled by GPIO_PIN_SPECIFIED flag in p0). - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE - reboot into an image in RAM. The region of RAM or XIP RAM is searched for an image to run. This is the type - * of reboot used when a RAM UF2 is dragged onto the BOOTSEL USB drive. - * p0 - the region start address (word-aligned). - * p1 - the region size (word-aligned). - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_FLASH_UPDATE - variant of \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL to use when flash has been updated. This is the type - * of reboot used after dragging a flash UF2 onto the BOOTSEL USB drive. - * p0 - the address of the start of the region of flash that was updated. If this address matches the start address of a partition or slot, then that - * partition or slot is treated preferentially during boot (when there is a choice). This type of boot facilitates TBYB and version downgrades. - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_PC_SP - reboot to a specific PC and SP. Note: this is not allowed in the ARM-NS variant. - * p0 - the initial program counter (PC) to start executing at. This must have the lowest bit set for Arm and clear for RISC-V - * p1 - the initial stack pointer (SP). - * - * All of the above, can have optional flags ORed in: - * - * \ref REBOOT2_FLAG_REBOOT_TO_ARM - switch both cores to the Arm architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the Arm architecture is not supported. - * \ref REBOOT2_FLAG_REBOOT_TO_RISCV - switch both cores to the RISC-V architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the RISC-V architecture is not supported. - * \ref REBOOT2_FLAG_NO_RETURN_ON_SUCCESS - the watchdog h/w is asynchronous. Setting this bit forces this method not to return if the reboot is successfully initiated. - * - * \param flags the reboot flags, as detailed above - * \param delay_ms millisecond delay before the reboot occurs - * \param p0 parameter 0, depends on flags - * \param p1 parameter 1, depends on flags - */ -static inline int rom_reboot(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1) { - rom_reboot_fn func = (rom_reboot_fn) rom_func_lookup_inline(ROM_FUNC_REBOOT); - return func(flags, delay_ms, p0, p1); -} - -bool rom_get_boot_random(uint32_t out[4]); - -/*! - * \brief Reset bootrom state - * \ingroup pico_bootrom - * - * Resets internal bootrom state, based on the following flags: - * - * STATE_RESET_CURRENT_CORE - Resets any internal bootrom state for the current core into a clean state. - * This method should be called prior to calling any other bootrom APIs on the current core, - * and is called automatically by the bootrom during normal boot of core 0 and launch of code on core 1. - * - * STATE_RESET_OTHER_CORE - Resets any internal bootrom state for the other core into a clean state. This is generally called by - * a debugger when resetting the state of one core via code running on the other. - * - * STATE_RESET_GLOBAL_STATE - Resets all non core-specific state, including: - * Disables access to bootrom APIs from ARM-NS - * Unlocks all BOOT spinlocks - * Clears any secure code callbacks - * - * Note: the sdk calls this method on runtime initialisation to put the bootrom into a known state. This - * allows the program to function correctly if it is entered (e.g. from a debugger) without taking the usual boot path (which - * resets the state appropriately itself). - * - * \param flags flags, as detailed above - */ -static inline void rom_bootrom_state_reset(uint32_t flags) { - rom_bootrom_state_reset_fn func = (rom_bootrom_state_reset_fn) rom_func_lookup_inline(ROM_FUNC_BOOTROM_STATE_RESET); - return func(flags); -} - -/*! - * \brief Reset address translation - * \ingroup pico_bootrom - * - * Restore the QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7, to their reset state. This makes the - * runtime-to-storage address map an identity map, i.e. the mapped and unmapped address are equal, and the entire space is - * fully mapped. - */ -static inline void rom_flash_reset_address_trans(void) { - rom_flash_reset_address_trans_fn func = (rom_flash_reset_address_trans_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RESET_ADDRESS_TRANS); - func(); -} - -/*! - * \brief Configure QMI in a XIP read mode - * \ingroup pico_bootrom - * - * Configure QMI for one of a small menu of XIP read modes supported by the bootrom. This mode is configured for both memory - * windows (both chip selects), and the clock divisor is also applied to direct mode. - * - * \param mode bootrom_xip_mode_t mode to use - * \param clkdiv clock divider - */ -static inline void rom_flash_select_xip_read_mode(bootrom_xip_mode_t mode, uint8_t clkdiv) { - rom_flash_select_xip_read_mode_fn func = (rom_flash_select_xip_read_mode_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_SELECT_XIP_READ_MODE); - func(mode, clkdiv); -} - -typedef struct { - cflash_flags_t flags; - uintptr_t addr; - uint32_t size_bytes; - uint8_t *buf; - int *res; -} rom_helper_flash_op_params_t; - -static inline void rom_helper_flash_op(void *param) { - const rom_helper_flash_op_params_t *op = (const rom_helper_flash_op_params_t *)param; - rom_flash_op_fn func = (rom_flash_op_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_OP); - *(op->res) = func(op->flags, op->addr, op->size_bytes, op->buf); -} - -/*! - * \brief Perform a flash read, erase, or program operation - * \ingroup pico_bootrom - * - * The flash operation is bounds-checked against the known flash devices specified by the runtime value of FLASH_DEVINFO, - * stored in bootram. This is initialised by the bootrom to the OTP value OTP_DATA_FLASH_DEVINFO, if - * OTP_DATA_BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is set; otherwise it is initialised to 16 MiB for chip select 0 and 0 bytes - * for chip select 1. FLASH_DEVINFO can be updated at runtime by writing to its location in bootram, the pointer to which - * can be looked up in the ROM table. - * - * If a resident partition table is in effect, then the flash operation is also checked against the partition permissions. - * The Secure version of this function can specify the caller's effective security level (Secure, Non-secure, bootloader) - * using the CFLASH_SECLEVEL_BITS bitfield of the flags argument, whereas the Non-secure function is always checked against - * the Non-secure permissions for the partition. Flash operations which span two partitions are not allowed, and will fail - * address validation. - * - * If OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED is set, erase operations will use a D8h 64 kiB block erase command where - * possible (without erasing outside the specified region), for faster erase time. Otherwise, only 20h 4 kiB sector erase - * commands are used. - * - * Optionally, this API can translate addr from flash runtime addresses to flash storage addresses, according to the - * translation currently configured by QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7. For example, an - * image stored at a +2 MiB offset in flash (but mapped at XIP address 0 at runtime), writing to an offset of +1 MiB into - * the image, will write to a physical flash storage address of 3 MiB. Translation is enabled by setting the - * CFLASH_ASPACE_BITS bitfield in the flags argument. - * - * When translation is enabled, flash operations which cross address holes in the XIP runtime address space (created by - * non-maximum ATRANSx_SIZE) will return an error response. This check may tear: the transfer may be partially performed - * before encountering an address hole and ultimately returning failure. - * - * When translation is enabled, flash operations are permitted to cross chip select boundaries, provided this does not - * span an ATRANS address hole. When translation is disabled, the entire operation must target a single flash chip select - * (as determined by bits 24 and upward of the address), else address validation will fail. - * - * \param flags controls the security level, address space, and flash operation - * \param addr the address of the first flash byte to be accessed, ranging from XIP_BASE to XIP_BASE + 0x1ffffff - * \param size_bytes size of buf, in bytes - * \param buf contains data to be written to flash, for program operations, and data read back from flash, for read operations - */ -static inline int rom_flash_op(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf) { - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_FLASH_OP)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = 0; - rom_helper_flash_op_params_t params = { - .flags = flags, - .addr = addr, - .size_bytes = size_bytes, - .buf = buf, - .res = &rc - }; - int flash_rc = flash_safe_execute(rom_helper_flash_op, ¶ms, UINT32_MAX); - bootrom_release_lock(BOOTROM_LOCK_FLASH_OP); - if (flash_rc != PICO_OK) { - return flash_rc; - } else { - return rc; - } -} - -/*! - * \brief Writes data from a buffer into OTP, or reads data from OTP into a buffer - * \ingroup pico_bootrom - * - * The buffer must be aligned to 2 bytes or 4 bytes according to the IS_ECC flag. - * - * This method will read and write rows until the first row it encounters that fails a key or permission check at which - * it will return BOOTROM_ERROR_NOT_PERMITTED. - * - * Writing will also stop at the first row where an attempt is made to set an OTP bit from a 1 to a 0, and - * BOOTROM_ERROR_UNSUPPORTED_MODIFICATION will be returned. - * - * If all rows are read/written successfully, then BOOTROM_OK will be returned. - * - * \param buf buffer to read to/write from - * \param buf_len size of buf - * \param cmd OTP command to execute - * - 0x0000ffff - ROW_NUMBER: 16 low bits are row number (0-4095) - * - 0x00010000 - IS_WRITE: if set, do a write (not a read) - * - 0x00020000 - IS_ECC: if this bit is set, each value in the buffer is 2 bytes and ECC is used when read/writing from 24 - * bit value in OTP. If this bit is not set, each value in the buffer is 4 bytes, the low 24-bits of which are written - * to or read from OTP. - - */ -static inline int rom_func_otp_access(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd) { - rom_func_otp_access_fn func = (rom_func_otp_access_fn) rom_func_lookup_inline(ROM_FUNC_OTP_ACCESS); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_OTP)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(buf, buf_len, cmd); - bootrom_release_lock(BOOTROM_LOCK_OTP); - return rc; -} - -/*! - * \brief Fills a buffer with information from the partition table - * \ingroup pico_bootrom - * - * Fills a buffer with information from the partition table. Note that this API is also used to return information over the - * picoboot interface. - * - * On success, the buffer is filled, and the number of words filled in the buffer is returned. If the partition table - * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return BOOTROM_ERROR_NO_DATA, and you - * should load the partition table via load_partition_table() first. - * - * Note that not all data from the partition table is kept resident in memory by the bootrom due to size constraints. - * To protect against changes being made in flash after the bootrom has loaded the resident portion, the bootrom keeps - * a hash of the partition table as of the time it loaded it. If the hash has changed by the time this method is called, - * then it will return BOOTROM_ERROR_INVALID_STATE. - * - * The information returned is chosen by the partition_and_flags parameter; the first word in the returned buffer, - * is the (sub)set of those flags that the API supports. You should always check this value before interpreting - * the buffer. - * - * Following the first word, returns words of data for each present flag in order. With the exception of PT_INFO, - * all the flags select "per partition" information, so each field is returned in flag order for one partition after - * the next. The special SINGLE_PARTITION flag indicates that data for only a single partition is required. - * - * \param out_buffer buffer to write data to - * \param out_buffer_word_size size of out_buffer, in words - * \param partition_and_flags partition number and flags - */ -static inline int rom_get_partition_table_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags) { - rom_get_partition_table_info_fn func = (rom_get_partition_table_info_fn) rom_func_lookup_inline(ROM_FUNC_GET_PARTITION_TABLE_INFO); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(out_buffer, out_buffer_word_size, partition_and_flags); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -// todo SECURE only -/*! - * \brief Loads the current partition table from flash, if present - * \ingroup pico_bootrom - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size - * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * If force_reload is false, then this method will return BOOTROM_OK immediately if the bootrom is loaded, otherwise it will - * reload the partition table if it has been loaded already, allowing for the partition table to be updated in a running program. - * - * \param workarea_base base address of work area - * \param workarea_size size of work area - * \param force_reload force reloading of the partition table - */ -static inline int rom_load_partition_table(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload) { - rom_load_partition_table_fn func = (rom_load_partition_table_fn) rom_func_lookup_inline(ROM_FUNC_LOAD_PARTITION_TABLE); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(workarea_base, workarea_size, force_reload); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -// todo SECURE only -/*! - * \brief Pick a partition from an A/B pair - * \ingroup pico_bootrom - * - * Determines which of the partitions has the "better" IMAGE_DEF. In the case of executable images, this is the one that would be booted - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should bye word aligned, and of sufficient size - * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * The passed partition number can be any valid partition number other than the "B" partition of an A/B pair. - * - * This method returns a negative error code, or the partition number of the picked partition if (i.e. partition_a_num or the - * number of its "B" partition if any). - * - * 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 \ref rom_pick_ab_partition_during_update() 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 - * \param flash_update_boot_window_base the flash update base, to pick that partition instead of the normally "better" partition - * \return >= 0 the chosen partition number out of the A/B pair - */ -static inline int rom_pick_ab_partition(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base) { - rom_pick_ab_partition_fn func = (rom_pick_ab_partition_fn) rom_func_lookup_inline(ROM_FUNC_PICK_AB_PARTITION); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(workarea_base, workarea_size, partition_a_num, flash_update_boot_window_base); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -/*! \brief Pick A/B partition without disturbing any in progress Flash Update boot or TBYB boot - * \ingroup pico_bootrom - * - * This will perform the same function as \ref 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 \ref rom_pick_ab_partition(). - * - * This should be used instead of \ref rom_pick_ab_partition() when performing a Flash Update Boot before calling \ref rom_explicit_buy(), and can still be - * used without issue when a Flash Update Boot is not in progress. - * - * This function is necessary because if an `explicit_buy` is pending then calling `pick_ab_partition` would 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 two partitions with one `explicit_buy` call). - * - * This function 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 by \ref rom_pick_ab_partition() - * 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_partition_during_update(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num); - -/*! - * \brief Get B partition - * \ingroup pico_bootrom - * - * Returns the index of the B partition of partition A if a partition table is present and loaded, and there is a partition A with a B partition; - * otherwise returns BOOTROM_ERROR_NOT_FOUND. - * - * \param pi_a the A partition number - */ -static inline int rom_get_b_partition(uint pi_a) { - rom_get_b_partition_fn func = (rom_get_b_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_B_PARTITION); - return func(pi_a); -} - -// todo SECURE only -/*! - * \brief Get UF2 Target Partition - * \ingroup pico_bootrom - * - * This method performs the same operation to decide on a target partition for a UF2 family ID as when a UF2 is dragged onto the USB - * drive in BOOTSEL mode. - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size - * or `BOOTROM_ERROR_INSUFFICIENT_RESOURCES` will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * If the partition table - * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return `BOOTROM_ERROR_PRECONDITION_NOT_MET`, and you - * should load the partition table via <> first. - * - * \param workarea_base base address of work area - * \param workarea_size size of work area - * \param family_id the family ID to place - * \param partition_out pointer to the resident_partition_t to fill with the partition data - */ -static inline int rom_get_uf2_target_partition(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out) { - rom_get_uf2_target_partition_fn func = (rom_get_uf2_target_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_UF2_TARGET_PARTITION); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(workarea_base, workarea_size, family_id, partition_out); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -/*! - * \brief Translate runtime to storage address - * \ingroup pico_bootrom - * - * Applies the address translation currently configured by QMI address translation registers. - * - * Translating an address outside of the XIP runtime address window, or beyond the bounds of an ATRANSx_SIZE field, returns BOOTROM_ERROR_INVALID_ADDRESS, - * which is not a valid flash storage address. Otherwise, return the storage address which QMI would access when presented with the runtime address addr. - * This is effectively a virtual-to-physical address translation for QMI. - * - * \param flash_runtime_addr the address to translate - */ -static inline intptr_t rom_flash_runtime_to_storage_addr(uintptr_t flash_runtime_addr) { - rom_flash_runtime_to_storage_addr_fn func = (rom_flash_runtime_to_storage_addr_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RUNTIME_TO_STORAGE_ADDR); - return func(flash_runtime_addr); -} - -// todo SECURE only -/*! - * \brief Chain into a launchable image - * \ingroup pico_bootrom - * - * Searches a memory region for a launchable image, and executes it if possible. - * - * The region_base and region_size specify a word-aligned, word-multiple-sized area of RAM, XIP RAM or flash to search. - * The first 4 kiB of the region must contain the start of a Block Loop with an IMAGE_DEF. If the new image is launched, - * the call does not return otherwise an error is returned. - * - * The region_base is signed, as a negative value can be passed, which indicates that the (negated back to positive value) - * is both the region_base and the base of the "flash update" region. - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should be word aligned, and of sufficient size - * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * NOTE: This method is primarily expected to be used when implementing bootloaders. - * - * NOTE: When chaining into an image, the OTP_DATA_BOOT_FLAGS0_ROLLBACK_REQUIRED flag will not be set, to prevent invalidating a bootloader - * without a rollback version by booting a binary which has one. - * - * \param workarea_base base address of work area - * \param workarea_size size of work area - * \param region_base base address of image - * \param region_size size of window containing image - */ -static inline int rom_chain_image(uint8_t *workarea_base, uint32_t workarea_size, uint32_t region_base, uint32_t region_size) { - rom_chain_image_fn func = (rom_chain_image_fn) rom_func_lookup_inline(ROM_FUNC_CHAIN_IMAGE); - bootrom_release_lock(BOOTROM_LOCK_ENABLE); - uint32_t interrupt_flags = save_and_disable_interrupts(); - int rc = func(workarea_base, workarea_size, region_base, region_size); - restore_interrupts_from_disabled(interrupt_flags); - bootrom_acquire_lock_blocking(BOOTROM_LOCK_ENABLE); - return rc; -} - -typedef struct { - uint8_t *buffer; - uint32_t buffer_size; - int *res; -} rom_helper_explicit_buy_params_t; - -static inline void rom_helper_explicit_buy(void *param) { - const rom_helper_explicit_buy_params_t *op = (const rom_helper_explicit_buy_params_t *)param; - rom_explicit_buy_fn func = (rom_explicit_buy_fn) rom_func_lookup_inline(ROM_FUNC_EXPLICIT_BUY); - *(op->res) = func(op->buffer, op->buffer_size); -} - -// todo SECURE only -/*! - * \brief Buy an image - * \ingroup pico_bootrom - * - * Perform an "explicit" buy of an executable launched via an IMAGE_DEF which was "explicit buy" flagged. A "flash update" - * boot of such an image is a way to have the image execute once, but only become the "current" image if it calls - * back into the bootrom via this call. - * - * This call may perform the following: - * - * - Erase and rewrite the part of flash containing the "explicit buy" flag in order to clear said flag. - * - Erase the first sector of the other partition in an A/B partition scenario, if this new IMAGE_DEF is a version downgrade - * (so this image will boot again when not doing a "flash update" boot) - * - Update the rollback version in OTP if the chip is secure, and a rollback version is present in the image. - * - * NOTE: The device may reboot while updating the rollback version, if multiple rollback rows need to be written - this occurs - * when the version crosses a multiple of 24 (for example upgrading from version 23 to 25 requires a reboot, but 23 to 24 or 24 to 25 doesn't). - * The application should therefore be prepared to reboot when calling this function, if rollback versions are in use. - * - * Note that the first of the above requires 4 kiB of scratch space, so you should pass a word aligned buffer of at least 4 kiB to this method, - * or it will return BOOTROM_ERROR_INSUFFICIENT_RESOURCES if the "explicit buy" flag needs to be cleared. - * - * \param buffer base address of scratch space - * \param buffer_size size of scratch space - */ -static inline int rom_explicit_buy(uint8_t *buffer, uint32_t buffer_size) { - int rc = 0; - rom_helper_explicit_buy_params_t params = { - .buffer = buffer, - .buffer_size = buffer_size, - .res = &rc - }; - int flash_rc = flash_safe_execute(rom_helper_explicit_buy, ¶ms, UINT32_MAX); - if (flash_rc != PICO_OK) { - return flash_rc; - } else { - return rc; - } -} - -#ifndef __riscv -/*! - * \brief Set NS API Permission - * \ingroup pico_bootrom - * - * Allow or disallow the specific NS API (note all NS APIs default to disabled). - * - * ns_api_num configures ARM-NS access to the given API. When an NS API is disabled, - * calling it will return BOOTROM_ERROR_NOT_PERMITTED. - * - * NOTE: All permissions default to disallowed after a reset. - * - * \param ns_api_num ns api number - * \param allowed permission - */ -static inline int rom_set_ns_api_permission(uint ns_api_num, bool allowed) { - rom_set_ns_api_permission_fn func = (rom_set_ns_api_permission_fn) rom_func_lookup_inline(ROM_FUNC_SET_NS_API_PERMISSION); - return func(ns_api_num, allowed); -} -#endif - -// todo SECURE only -/*! - * \brief Validate NS Buffer - * \ingroup pico_bootrom - * - * Utility method that can be used by secure ARM code to validate a buffer passed to it from Non-secure code. - * - * Both the write parameter and the (out) result parameter ok are RCP booleans, so 0xa500a500 for true, and 0x00c300c3 - * for false. This enables hardening of this function, and indeed the write parameter must be one of these values or the RCP - * will hang the system. - * - * For success, the entire buffer must fit in range XIP_BASE -> SRAM_END, and must be accessible by the Non-secure - * caller according to SAU + NS MPU (privileged or not based on current processor IPSR and NS CONTROL flag). Buffers - * in USB RAM are also allowed if access is granted to NS via ACCESSCTRL. - * - * \param addr buffer address - * \param size buffer size - * \param write rcp boolean, true if writeable - * \param ok rcp boolean result - */ -static inline void* rom_validate_ns_buffer(const void *addr, uint32_t size, uint32_t write, uint32_t *ok) { - rom_validate_ns_buffer_fn func = (rom_validate_ns_buffer_fn) rom_func_lookup_inline(ROM_FUNC_VALIDATE_NS_BUFFER); - return func(addr, size, write, ok); -} - -/*! - * \brief Set ROM callback function - * \ingroup pico_bootrom - * - * The only currently supported callback_number is 0 which sets the callback used for the secure_call API. - * - * A callback pointer of 0 deletes the callback function, a positive callback pointer (all valid function pointers are on RP2350) - * sets the callback function, but a negative callback pointer can be passed to get the old value without setting a new value. - * - * If successful, returns >=0 (the existing value of the function pointer on entry to the function). - * - * \param callback_num the callback number to set - only 0 is supported on RP2350 - * \param funcptr pointer to the callback function - */ -static inline intptr_t rom_set_rom_callback(uint callback_num, bootrom_api_callback_generic_t funcptr) { - rom_set_rom_callback_fn func = (rom_set_rom_callback_fn) rom_func_lookup_inline(ROM_FUNC_SET_ROM_CALLBACK); - return func(callback_num, funcptr); -} - -/*! - * \brief Get system information - * \ingroup pico_bootrom - * - * Fills a buffer with various system information. Note that this API is also used to return information over the picoboot interface. - * - * On success, the buffer is filled, and the number of words filled in the buffer is returned. - * - * The information returned is chosen by the flags parameter; the first word in the returned buffer, - * is the (sub)set of those flags that the API supports. You should always check this value before interpreting - * the buffer. - * - * "Boot Diagnostic" information is intended to help identify the cause of a failed boot, or booting into an unexpected binary. - * This information can be retrieved via picoboot after a watchdog reboot, however it will not survive - * a reset via the RUN pin or POWMAN reset. - * - * There is only one word of diagnostic information. What it records is based on the pp selection above, which - * is itself set as a parameter when rebooting programmatically into a normal boot. - * - * To get diagnostic info, pp must refer to a slot or an "A" partition; image diagnostics are automatically selected on boot - * from OTP or RAM image, or when chain_image() is called.) - * - * The diagnostic word thus contains data for either slot 0 and slot 1, or the "A" partition (and its "B" partition if it has one). The low half word - * of the diagnostic word contains information from slot 0 or partition A; the high half word contains information from slot 1 or partition B. - * - * To get a full picture of a failed boot involving slots and multiple partitions, the device can be rebooted - * multiple times to gather the information. - * - * \param out_buffer buffer to write data to - * \param out_buffer_word_size size of out_buffer, in words - * \param flags flags - */ -static inline int rom_get_sys_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags) { - rom_get_sys_info_fn func = (rom_get_sys_info_fn)rom_func_lookup_inline(ROM_FUNC_GET_SYS_INFO); - return func(out_buffer, out_buffer_word_size, flags); -} - -typedef struct { - union { - struct __packed { - int8_t diagnostic_partition_index; // used BOOT_PARTITION constants - uint8_t boot_type; - int8_t partition; - uint8_t tbyb_and_update_info; - }; - uint32_t boot_word; - }; - uint32_t boot_diagnostic; - uint32_t reboot_params[2]; -} boot_info_t; - -static inline int rom_get_boot_info(boot_info_t *info) { - uint32_t result[5]; - int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); - if (words_returned == (sizeof(result)/sizeof(result[0])) && result[0] == SYS_INFO_BOOT_INFO) { - memcpy(info, &result[1], sizeof(boot_info_t)); - return true; - } else { - return false; - } -} - -static inline int rom_get_last_boot_type_with_chained_flag(void) { - uint32_t result[5]; - int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); - if (words_returned == count_of(result) && result[0] == SYS_INFO_BOOT_INFO) { - // todo use struct - return (int)((result[1] & 0xff00u) >> 8); - } else { - return PICO_ERROR_INVALID_DATA; - } -} - -// BOOT_TYPE_NORMAL 0x0 -// BOOT_TYPE_BOOTSEL 0x2 -// BOOT_TYPE_RAM_IMAGE 0x3 -// BOOT_TYPE_FLASH_UPDATE 0x4 -// BOOT_TYPE_PC_SP 0xd -static inline int rom_get_last_boot_type(void) { - int rc = rom_get_last_boot_type_with_chained_flag(); - if (rc >= 0) rc &= ~BOOT_TYPE_CHAINED_FLAG; - return rc; -} - -/*! \brief Add a runtime partition to the partition table to specify flash permissions - * \ingroup pico_bootrom - * - * Note that a partition is added to the runtime view of the partition table maintained by the bootrom if there is space to do so - * - * Note that these permissions cannot override the permissions for any pre-existing partitions, as permission matches are made on a first partition found basis. - * - * @param start_offset the start_offset into flash in bytes (must be a multiple of 4K) - * @param size the size in byte (must be a multiple of 4K) - * @param permissions the bitwise OR of permissions from PICOBIN_PARTITION_PERMISSION_ constants, e.g. \ref PICOBIN_PARTITION_PERMISSION_S_R_BITS from boot/picobin.h - * @return >= 0 the partition number added if - * PICO_ERROR_BAD_ALIGNMENT if the start_offset or size aren't multiples of 4K. - * PICO_ERROR_INVALID_ARG if the start_offset or size are out of range, or invalid permission bits are set. - */ -int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32_t permissions); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif // !__ASSEMBLER__ -#endif + * GPIO pin should be set to output and raised whenever there is mass storage activity + * from the host. + * \param disable_interface_mask value to control exposed interfaces + * - 0 To enable both interfaces (as per a cold boot) + * - 1 To disable the USB Mass Storage Interface + * - 2 To disable the USB PICOBOOT Interface + */ +void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask); +static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask) { + rom_reset_usb_boot(usb_activity_gpio_pin_mask, disable_interface_mask); +} + +/*! + * \brief Reboot the device into BOOTSEL mode + * \ingroup pico_bootrom + * + * This function reboots the device into the BOOTSEL mode ('usb boot"). + * + * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, + * and to limit the USB interfaces exposed. + * + * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define + * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * the activity LED correctly. + * + * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none + * from the host. + * \param disable_interface_mask value to control exposed interfaces + * - 0 To enable both interfaces (as per a cold boot) + * - 1 To disable the USB Mass Storage Interface + * - 2 To disable the USB PICOBOOT Interface + * \param usb_activity_gpio_pin_active_low Activity GPIO is active low (ignored on RP2040). A bug in the bootrom of RP2350 + * A4 chips means this parameter has no effect on that version of the RP2350. + */ +void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pin, uint32_t disable_interface_mask, bool usb_activity_gpio_pin_active_low); + +/*! + * \brief Connect the SSI/QMI to the QSPI pads + * \ingroup pico_bootrom + * + * Restore all QSPI pad controls to their default state, and connect the SSI/QMI peripheral to the QSPI pads. + * + * \if rp2350_specific + * On RP2350 if a secondary flash chip select GPIO has been configured via OTP OTP_DATA_FLASH_DEVINFO, or by writing to the runtime + * copy of FLASH_DEVINFO in bootram, then this bank 0 GPIO is also initialised and the QMI peripheral is connected. Otherwise, + * bank 0 IOs are untouched. + * \endif + */ +static inline void rom_connect_internal_flash(void) { + rom_connect_internal_flash_fn func = (rom_connect_internal_flash_fn) rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); + func(); +} + +/*! + * \brief Return the QSPI device from its XIP state to a serial command state + * \ingroup pico_bootrom + * + * \if rp2040_specific + * On RP2040, first set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in Section 2.8.1.2 + * of the datasheet. Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be cleared before returning + * the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This function configures the SSI with a fixed SCK clock divisor of /6. + * \endif + * + * \if rp2350_specific + * On RP2350, Initialise the QMI for serial operations (direct mode), and also initialise a basic XIP mode, where the QMI will perform + * 03h serial read commands at low speed (CLKDIV=12) in response to XIP reads. + * + * Then, issue a sequence to the QSPI device on chip select 0, designed to return it from continuous read mode ("XIP mode") and/or + * QPI mode to a state where it will accept serial commands. This is necessary after system reset to restore the QSPI device to a known + * state, because resetting RP2350 does not reset attached QSPI devices. It is also necessary when user code, having already performed + * some continuous-read-mode or QPI-mode accesses, wishes to return the QSPI device to a state where it will accept the serial erase and + * programming commands issued by the bootrom's flash access functions. + * + * If a GPIO for the secondary chip select is configured via FLASH_DEVINFO, then the XIP exit sequence is also issued to chip select 1. + * + * The QSPI device should be accessible for XIP reads after calling this function; the name flash_exit_xip refers to returning the QSPI + * device from its XIP state to a serial command state. + * \endif + */ +static inline void rom_flash_exit_xip(void) { + rom_flash_exit_xip_fn func = (rom_flash_exit_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); + func(); +} + +/*! + * \brief Erase bytes in flash + * \ingroup pico_bootrom + * + * Erase count bytes, starting at addr (offset from start of flash). Optionally, pass a block erase command e.g. D8h block erase, + * and the size of the block erased by this command - this function will use the larger block erase where possible, for much higher + * erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of 4096 bytes. + * + * This is a low-level flash API, and no validation of the arguments is performed. + * + * \if rp2350_specific + * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, and can transparently + * apply a runtime-to-storage address translation. + * + * The QSPI device must be in a serial command state before calling this API, which can be achieved by calling rom_connect_internal_flash() + * followed by rom_flash_exit_xip(). After the erase, the flash cache should be flushed via rom_flash_flush_cache() to ensure the modified + * flash data is visible to cached XIP accesses. + * + * Finally, the original XIP mode should be restored by copying the saved XIP setup function from bootram into SRAM, and executing it: + * the bootrom provides a default function which restores the flash mode/clkdiv discovered during flash scanning, and user programs can + * override this with their own XIP setup function. + * + * For the duration of the erase operation, QMI is in direct mode and attempting to access XIP from DMA, the debugger or the other core will + * return a bus fault. XIP becomes accessible again once the function returns. + * \endif + * + * \param addr the offset from start of flash to be erased + * \param count number of bytes to erase + * \param block_size optional size of block erased by block_cmd + * \param block_cmd optional block erase command e.g. D8h block erase + */ +static inline void rom_flash_range_erase(uint32_t addr, size_t count, uint32_t block_size, uint8_t block_cmd) { + rom_flash_range_erase_fn func = (rom_flash_range_erase_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_ERASE); + func(addr, count, block_size, block_cmd); +} + +/*! + * \brief Program bytes in flash + * \ingroup pico_bootrom + * + * Program data to a range of flash addresses starting at addr (offset from the start of flash) and count bytes in size. addr must be + * aligned to a 256-byte boundary, and count must be a multiple of 256. + * + * This is a low-level flash API, and no validation of the arguments is performed. + * + * \if rp2350_specific + * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, + * and can transparently apply a runtime-to-storage address translation. + * + * The QSPI device must be in a serial command state before calling this API - see notes on rom_flash_range_erase + * \endif + * + * \param addr the offset from start of flash to be erased + * \param data buffer containing the data to be written + * \param count number of bytes to erase + */ +static inline void rom_flash_range_program(uint32_t addr, const uint8_t *data, size_t count) { + rom_flash_range_program_fn func = (rom_flash_range_program_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); + func(addr, data, count); +} + +/*! + * \brief Flush the XIP cache + * \ingroup pico_bootrom + * + * \if rp2040_specific + * Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can drive the flash chip select as normal. + * \endif + * + * \if rp2350_specific + * Flush the entire XIP cache, by issuing an invalidate by set/way maintenance operation to every cache line. This ensures that flash + * program/erase operations are visible to subsequent cached XIP reads. + * + * Note that this unpins pinned cache lines, which may interfere with cache-as-SRAM use of the XIP cache. + * + * No other operations are performed. + * \endif + */ +static inline void rom_flash_flush_cache(void) { + rom_flash_flush_cache_fn func = (rom_flash_flush_cache_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); + func(); +} + +/*! + * \brief Configure the SSI/QMI with a standard command + * \ingroup pico_bootrom + * + * Configure the SSI/QMI to generate a standard 03h serial read command, with 24 address bits, upon each XIP access. This is a slow XIP + * configuration, but is widely supported. CLKDIV is set to 12 on RP2350. The debugger may call this function to ensure that flash is + * readable following a program/erase operation. + * + * Note that the same setup is performed by flash_exit_xip(), and the RP2350 flash program/erase functions do not leave XIP in an + * inaccessible state, so calls to this function are largely redundant on RP2350. It is provided on RP2350 for compatibility with RP2040. + */ +static inline void rom_flash_enter_cmd_xip(void) { + rom_flash_enter_cmd_xip_fn func = (rom_flash_enter_cmd_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_ENTER_CMD_XIP); + func(); +} + +#if !PICO_RP2040 || PICO_COMBINED_DOCS +#ifdef __riscv +/*! + * \brief Give the bootrom a new stack + * \ingroup pico_bootrom + * + * Most bootrom functions are written just once, in Arm code, to save space. As a result these functions are emulated when + * running under the RISC-V architecture. This is largely transparent to the user, however the stack used by the Arm emulation + * is separate from the calling user's stack, and is stored in boot RAM but is of quite limited size. When using certain of the more + * complex APIs or if nesting bootrom calls from within IRQs, you may need to provide a large stack. + * + * This method allows the caller to specify a region of RAM to use as the stack for the current core by passing a pointer to two values: the word aligned base address, + * and the size in bytes (multiple of 4). + * + * The method fills in the previous base/size values into the passed array before returning. + * + * \param stack bootrom_stack_t struct containing base and size + */ +static inline int rom_set_bootrom_stack(bootrom_stack_t *stack) { + rom_set_bootrom_stack_fn func = (rom_set_bootrom_stack_fn) rom_func_lookup_inline(ROM_FUNC_SET_BOOTROM_STACK); + return func(stack); +} +#endif + +/*! + * \brief Reboot using the watchdog + * \ingroup pico_bootrom + * + * Resets the chip and uses the watchdog facility to restart. + * + * The delay_ms is the millisecond delay before the reboot occurs. Note: by default this method is asynchronous + * (unless NO_RETURN_ON_SUCCESS is set - see below), so the method will return and the reboot will happen this many milliseconds later. + * + * The flags field contains one of the following values: + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL - reboot into the normal boot path. + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL - reboot into BOOTSEL mode. + * p0 - a set of flags: + * 0x01 : DISABLE_MSD_INTERFACE - Disable the BOOTSEL USB drive (see <>) + * 0x02 : DISABLE_PICOBOOT_INTERFACE - Disable the {picoboot} interface (see <>). + * 0x10 : GPIO_PIN_ACTIVE_LOW - The GPIO specified in p1 is active low (GPIO_PIN_SPECIFIED must also be set). + * 0x20 : GPIO_PIN_SPECIFIED - Enable the activity indicator on the GPIO specified in p1. + * p1 - the GPIO number to use as an activity indicator (enabled by GPIO_PIN_SPECIFIED flag in p0). + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE - reboot into an image in RAM. The region of RAM or XIP RAM is searched for an image to run. This is the type + * of reboot used when a RAM UF2 is dragged onto the BOOTSEL USB drive. + * p0 - the region start address (word-aligned). + * p1 - the region size (word-aligned). + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_FLASH_UPDATE - variant of \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL to use when flash has been updated. This is the type + * of reboot used after dragging a flash UF2 onto the BOOTSEL USB drive. + * p0 - the address of the start of the region of flash that was updated. If this address matches the start address of a partition or slot, then that + * partition or slot is treated preferentially during boot (when there is a choice). This type of boot facilitates TBYB and version downgrades. + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_PC_SP - reboot to a specific PC and SP. Note: this is not allowed in the ARM-NS variant. + * p0 - the initial program counter (PC) to start executing at. This must have the lowest bit set for Arm and clear for RISC-V + * p1 - the initial stack pointer (SP). + * + * All of the above, can have optional flags ORed in: + * + * \ref REBOOT2_FLAG_REBOOT_TO_ARM - switch both cores to the Arm architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the Arm architecture is not supported. + * \ref REBOOT2_FLAG_REBOOT_TO_RISCV - switch both cores to the RISC-V architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the RISC-V architecture is not supported. + * \ref REBOOT2_FLAG_NO_RETURN_ON_SUCCESS - the watchdog h/w is asynchronous. Setting this bit forces this method not to return if the reboot is successfully initiated. + * + * \param flags the reboot flags, as detailed above + * \param delay_ms millisecond delay before the reboot occurs + * \param p0 parameter 0, depends on flags + * \param p1 parameter 1, depends on flags + */ +static inline int rom_reboot(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1) { + rom_reboot_fn func = (rom_reboot_fn) rom_func_lookup_inline(ROM_FUNC_REBOOT); + return func(flags, delay_ms, p0, p1); +} + +bool rom_get_boot_random(uint32_t out[4]); + +/*! + * \brief Reset bootrom state + * \ingroup pico_bootrom + * + * Resets internal bootrom state, based on the following flags: + * + * STATE_RESET_CURRENT_CORE - Resets any internal bootrom state for the current core into a clean state. + * This method should be called prior to calling any other bootrom APIs on the current core, + * and is called automatically by the bootrom during normal boot of core 0 and launch of code on core 1. + * + * STATE_RESET_OTHER_CORE - Resets any internal bootrom state for the other core into a clean state. This is generally called by + * a debugger when resetting the state of one core via code running on the other. + * + * STATE_RESET_GLOBAL_STATE - Resets all non core-specific state, including: + * Disables access to bootrom APIs from ARM-NS + * Unlocks all BOOT spinlocks + * Clears any secure code callbacks + * + * Note: the sdk calls this method on runtime initialisation to put the bootrom into a known state. This + * allows the program to function correctly if it is entered (e.g. from a debugger) without taking the usual boot path (which + * resets the state appropriately itself). + * + * \param flags flags, as detailed above + */ +static inline void rom_bootrom_state_reset(uint32_t flags) { + rom_bootrom_state_reset_fn func = (rom_bootrom_state_reset_fn) rom_func_lookup_inline(ROM_FUNC_BOOTROM_STATE_RESET); + return func(flags); +} + +/*! + * \brief Reset address translation + * \ingroup pico_bootrom + * + * Restore the QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7, to their reset state. This makes the + * runtime-to-storage address map an identity map, i.e. the mapped and unmapped address are equal, and the entire space is + * fully mapped. + */ +static inline void rom_flash_reset_address_trans(void) { + rom_flash_reset_address_trans_fn func = (rom_flash_reset_address_trans_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RESET_ADDRESS_TRANS); + func(); +} + +/*! + * \brief Configure QMI in a XIP read mode + * \ingroup pico_bootrom + * + * Configure QMI for one of a small menu of XIP read modes supported by the bootrom. This mode is configured for both memory + * windows (both chip selects), and the clock divisor is also applied to direct mode. + * + * \param mode bootrom_xip_mode_t mode to use + * \param clkdiv clock divider + */ +static inline void rom_flash_select_xip_read_mode(bootrom_xip_mode_t mode, uint8_t clkdiv) { + rom_flash_select_xip_read_mode_fn func = (rom_flash_select_xip_read_mode_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_SELECT_XIP_READ_MODE); + func(mode, clkdiv); +} + +typedef struct { + cflash_flags_t flags; + uintptr_t addr; + uint32_t size_bytes; + uint8_t *buf; + int *res; +} rom_helper_flash_op_params_t; + +static inline void rom_helper_flash_op(void *param) { + const rom_helper_flash_op_params_t *op = (const rom_helper_flash_op_params_t *)param; + rom_flash_op_fn func = (rom_flash_op_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_OP); + *(op->res) = func(op->flags, op->addr, op->size_bytes, op->buf); +} + +/*! + * \brief Perform a flash read, erase, or program operation + * \ingroup pico_bootrom + * + * The flash operation is bounds-checked against the known flash devices specified by the runtime value of FLASH_DEVINFO, + * stored in bootram. This is initialised by the bootrom to the OTP value OTP_DATA_FLASH_DEVINFO, if + * OTP_DATA_BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is set; otherwise it is initialised to 16 MiB for chip select 0 and 0 bytes + * for chip select 1. FLASH_DEVINFO can be updated at runtime by writing to its location in bootram, the pointer to which + * can be looked up in the ROM table. + * + * If a resident partition table is in effect, then the flash operation is also checked against the partition permissions. + * The Secure version of this function can specify the caller's effective security level (Secure, Non-secure, bootloader) + * using the CFLASH_SECLEVEL_BITS bitfield of the flags argument, whereas the Non-secure function is always checked against + * the Non-secure permissions for the partition. Flash operations which span two partitions are not allowed, and will fail + * address validation. + * + * If OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED is set, erase operations will use a D8h 64 kiB block erase command where + * possible (without erasing outside the specified region), for faster erase time. Otherwise, only 20h 4 kiB sector erase + * commands are used. + * + * Optionally, this API can translate addr from flash runtime addresses to flash storage addresses, according to the + * translation currently configured by QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7. For example, an + * image stored at a +2 MiB offset in flash (but mapped at XIP address 0 at runtime), writing to an offset of +1 MiB into + * the image, will write to a physical flash storage address of 3 MiB. Translation is enabled by setting the + * CFLASH_ASPACE_BITS bitfield in the flags argument. + * + * When translation is enabled, flash operations which cross address holes in the XIP runtime address space (created by + * non-maximum ATRANSx_SIZE) will return an error response. This check may tear: the transfer may be partially performed + * before encountering an address hole and ultimately returning failure. + * + * When translation is enabled, flash operations are permitted to cross chip select boundaries, provided this does not + * span an ATRANS address hole. When translation is disabled, the entire operation must target a single flash chip select + * (as determined by bits 24 and upward of the address), else address validation will fail. + * + * \param flags controls the security level, address space, and flash operation + * \param addr the address of the first flash byte to be accessed, ranging from XIP_BASE to XIP_BASE + 0x1ffffff + * \param size_bytes size of buf, in bytes + * \param buf contains data to be written to flash, for program operations, and data read back from flash, for read operations + */ +static inline int rom_flash_op(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf) { + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_FLASH_OP)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = 0; + rom_helper_flash_op_params_t params = { + .flags = flags, + .addr = addr, + .size_bytes = size_bytes, + .buf = buf, + .res = &rc + }; + int flash_rc = flash_safe_execute(rom_helper_flash_op, ¶ms, UINT32_MAX); + bootrom_release_lock(BOOTROM_LOCK_FLASH_OP); + if (flash_rc != PICO_OK) { + return flash_rc; + } else { + return rc; + } +} + +/*! + * \brief Writes data from a buffer into OTP, or reads data from OTP into a buffer + * \ingroup pico_bootrom + * + * The buffer must be aligned to 2 bytes or 4 bytes according to the IS_ECC flag. + * + * This method will read and write rows until the first row it encounters that fails a key or permission check at which + * it will return BOOTROM_ERROR_NOT_PERMITTED. + * + * Writing will also stop at the first row where an attempt is made to set an OTP bit from a 1 to a 0, and + * BOOTROM_ERROR_UNSUPPORTED_MODIFICATION will be returned. + * + * If all rows are read/written successfully, then BOOTROM_OK will be returned. + * + * \param buf buffer to read to/write from + * \param buf_len size of buf + * \param cmd OTP command to execute + * - 0x0000ffff - ROW_NUMBER: 16 low bits are row number (0-4095) + * - 0x00010000 - IS_WRITE: if set, do a write (not a read) + * - 0x00020000 - IS_ECC: if this bit is set, each value in the buffer is 2 bytes and ECC is used when read/writing from 24 + * bit value in OTP. If this bit is not set, each value in the buffer is 4 bytes, the low 24-bits of which are written + * to or read from OTP. + + */ +static inline int rom_func_otp_access(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd) { + rom_func_otp_access_fn func = (rom_func_otp_access_fn) rom_func_lookup_inline(ROM_FUNC_OTP_ACCESS); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_OTP)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(buf, buf_len, cmd); + bootrom_release_lock(BOOTROM_LOCK_OTP); + return rc; +} + +/*! + * \brief Fills a buffer with information from the partition table + * \ingroup pico_bootrom + * + * Fills a buffer with information from the partition table. Note that this API is also used to return information over the + * picoboot interface. + * + * On success, the buffer is filled, and the number of words filled in the buffer is returned. If the partition table + * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return BOOTROM_ERROR_NO_DATA, and you + * should load the partition table via load_partition_table() first. + * + * Note that not all data from the partition table is kept resident in memory by the bootrom due to size constraints. + * To protect against changes being made in flash after the bootrom has loaded the resident portion, the bootrom keeps + * a hash of the partition table as of the time it loaded it. If the hash has changed by the time this method is called, + * then it will return BOOTROM_ERROR_INVALID_STATE. + * + * The information returned is chosen by the partition_and_flags parameter; the first word in the returned buffer, + * is the (sub)set of those flags that the API supports. You should always check this value before interpreting + * the buffer. + * + * Following the first word, returns words of data for each present flag in order. With the exception of PT_INFO, + * all the flags select "per partition" information, so each field is returned in flag order for one partition after + * the next. The special SINGLE_PARTITION flag indicates that data for only a single partition is required. + * + * \param out_buffer buffer to write data to + * \param out_buffer_word_size size of out_buffer, in words + * \param partition_and_flags partition number and flags + */ +static inline int rom_get_partition_table_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags) { + rom_get_partition_table_info_fn func = (rom_get_partition_table_info_fn) rom_func_lookup_inline(ROM_FUNC_GET_PARTITION_TABLE_INFO); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(out_buffer, out_buffer_word_size, partition_and_flags); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +// todo SECURE only +/*! + * \brief Loads the current partition table from flash, if present + * \ingroup pico_bootrom + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size + * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * If force_reload is false, then this method will return BOOTROM_OK immediately if the bootrom is loaded, otherwise it will + * reload the partition table if it has been loaded already, allowing for the partition table to be updated in a running program. + * + * \param workarea_base base address of work area + * \param workarea_size size of work area + * \param force_reload force reloading of the partition table + */ +static inline int rom_load_partition_table(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload) { + rom_load_partition_table_fn func = (rom_load_partition_table_fn) rom_func_lookup_inline(ROM_FUNC_LOAD_PARTITION_TABLE); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(workarea_base, workarea_size, force_reload); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +// todo SECURE only +/*! + * \brief Pick a partition from an A/B pair + * \ingroup pico_bootrom + * + * Determines which of the partitions has the "better" IMAGE_DEF. In the case of executable images, this is the one that would be booted + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should bye word aligned, and of sufficient size + * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * The passed partition number can be any valid partition number other than the "B" partition of an A/B pair. + * + * This method returns a negative error code, or the partition number of the picked partition if (i.e. partition_a_num or the + * number of its "B" partition if any). + * + * 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 \ref rom_pick_ab_partition_during_update() 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 + * \param flash_update_boot_window_base the flash update base, to pick that partition instead of the normally "better" partition + * \return >= 0 the chosen partition number out of the A/B pair + */ +static inline int rom_pick_ab_partition(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base) { + rom_pick_ab_partition_fn func = (rom_pick_ab_partition_fn) rom_func_lookup_inline(ROM_FUNC_PICK_AB_PARTITION); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(workarea_base, workarea_size, partition_a_num, flash_update_boot_window_base); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +/*! \brief Pick A/B partition without disturbing any in progress Flash Update boot or TBYB boot + * \ingroup pico_bootrom + * + * This will perform the same function as \ref 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 \ref rom_pick_ab_partition(). + * + * This should be used instead of \ref rom_pick_ab_partition() when performing a Flash Update Boot before calling \ref rom_explicit_buy(), and can still be + * used without issue when a Flash Update Boot is not in progress. + * + * This function is necessary because if an `explicit_buy` is pending then calling `pick_ab_partition` would 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 two partitions with one `explicit_buy` call). + * + * This function 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 by \ref rom_pick_ab_partition() + * 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_partition_during_update(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num); + +/*! + * \brief Get B partition + * \ingroup pico_bootrom + * + * Returns the index of the B partition of partition A if a partition table is present and loaded, and there is a partition A with a B partition; + * otherwise returns BOOTROM_ERROR_NOT_FOUND. + * + * \param pi_a the A partition number + */ +static inline int rom_get_b_partition(uint pi_a) { + rom_get_b_partition_fn func = (rom_get_b_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_B_PARTITION); + return func(pi_a); +} + +// todo SECURE only +/*! + * \brief Get UF2 Target Partition + * \ingroup pico_bootrom + * + * This method performs the same operation to decide on a target partition for a UF2 family ID as when a UF2 is dragged onto the USB + * drive in BOOTSEL mode. + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size + * or `BOOTROM_ERROR_INSUFFICIENT_RESOURCES` will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * If the partition table + * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return `BOOTROM_ERROR_PRECONDITION_NOT_MET`, and you + * should load the partition table via <> first. + * + * \param workarea_base base address of work area + * \param workarea_size size of work area + * \param family_id the family ID to place + * \param partition_out pointer to the resident_partition_t to fill with the partition data + */ +static inline int rom_get_uf2_target_partition(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out) { + rom_get_uf2_target_partition_fn func = (rom_get_uf2_target_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_UF2_TARGET_PARTITION); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(workarea_base, workarea_size, family_id, partition_out); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +/*! + * \brief Translate runtime to storage address + * \ingroup pico_bootrom + * + * Applies the address translation currently configured by QMI address translation registers. + * + * Translating an address outside of the XIP runtime address window, or beyond the bounds of an ATRANSx_SIZE field, returns BOOTROM_ERROR_INVALID_ADDRESS, + * which is not a valid flash storage address. Otherwise, return the storage address which QMI would access when presented with the runtime address addr. + * This is effectively a virtual-to-physical address translation for QMI. + * + * \param flash_runtime_addr the address to translate + */ +static inline intptr_t rom_flash_runtime_to_storage_addr(uintptr_t flash_runtime_addr) { + rom_flash_runtime_to_storage_addr_fn func = (rom_flash_runtime_to_storage_addr_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RUNTIME_TO_STORAGE_ADDR); + return func(flash_runtime_addr); +} + +// todo SECURE only +/*! + * \brief Chain into a launchable image + * \ingroup pico_bootrom + * + * Searches a memory region for a launchable image, and executes it if possible. + * + * The region_base and region_size specify a word-aligned, word-multiple-sized area of RAM, XIP RAM or flash to search. + * The first 4 kiB of the region must contain the start of a Block Loop with an IMAGE_DEF. If the new image is launched, + * the call does not return otherwise an error is returned. + * + * The region_base is signed, as a negative value can be passed, which indicates that the (negated back to positive value) + * is both the region_base and the base of the "flash update" region. + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should be word aligned, and of sufficient size + * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * NOTE: This method is primarily expected to be used when implementing bootloaders. + * + * NOTE: When chaining into an image, the OTP_DATA_BOOT_FLAGS0_ROLLBACK_REQUIRED flag will not be set, to prevent invalidating a bootloader + * without a rollback version by booting a binary which has one. + * + * \param workarea_base base address of work area + * \param workarea_size size of work area + * \param region_base base address of image + * \param region_size size of window containing image + */ +static inline int rom_chain_image(uint8_t *workarea_base, uint32_t workarea_size, uint32_t region_base, uint32_t region_size) { + rom_chain_image_fn func = (rom_chain_image_fn) rom_func_lookup_inline(ROM_FUNC_CHAIN_IMAGE); + bootrom_release_lock(BOOTROM_LOCK_ENABLE); + uint32_t interrupt_flags = save_and_disable_interrupts(); + int rc = func(workarea_base, workarea_size, region_base, region_size); + restore_interrupts_from_disabled(interrupt_flags); + bootrom_acquire_lock_blocking(BOOTROM_LOCK_ENABLE); + return rc; +} + +typedef struct { + uint8_t *buffer; + uint32_t buffer_size; + int *res; +} rom_helper_explicit_buy_params_t; + +static inline void rom_helper_explicit_buy(void *param) { + const rom_helper_explicit_buy_params_t *op = (const rom_helper_explicit_buy_params_t *)param; + rom_explicit_buy_fn func = (rom_explicit_buy_fn) rom_func_lookup_inline(ROM_FUNC_EXPLICIT_BUY); + *(op->res) = func(op->buffer, op->buffer_size); +} + +// todo SECURE only +/*! + * \brief Buy an image + * \ingroup pico_bootrom + * + * Perform an "explicit" buy of an executable launched via an IMAGE_DEF which was "explicit buy" flagged. A "flash update" + * boot of such an image is a way to have the image execute once, but only become the "current" image if it calls + * back into the bootrom via this call. + * + * This call may perform the following: + * + * - Erase and rewrite the part of flash containing the "explicit buy" flag in order to clear said flag. + * - Erase the first sector of the other partition in an A/B partition scenario, if this new IMAGE_DEF is a version downgrade + * (so this image will boot again when not doing a "flash update" boot) + * - Update the rollback version in OTP if the chip is secure, and a rollback version is present in the image. + * + * NOTE: The device may reboot while updating the rollback version, if multiple rollback rows need to be written - this occurs + * when the version crosses a multiple of 24 (for example upgrading from version 23 to 25 requires a reboot, but 23 to 24 or 24 to 25 doesn't). + * The application should therefore be prepared to reboot when calling this function, if rollback versions are in use. + * + * Note that the first of the above requires 4 kiB of scratch space, so you should pass a word aligned buffer of at least 4 kiB to this method, + * or it will return BOOTROM_ERROR_INSUFFICIENT_RESOURCES if the "explicit buy" flag needs to be cleared. + * + * \param buffer base address of scratch space + * \param buffer_size size of scratch space + */ +static inline int rom_explicit_buy(uint8_t *buffer, uint32_t buffer_size) { + int rc = 0; + rom_helper_explicit_buy_params_t params = { + .buffer = buffer, + .buffer_size = buffer_size, + .res = &rc + }; + int flash_rc = flash_safe_execute(rom_helper_explicit_buy, ¶ms, UINT32_MAX); + if (flash_rc != PICO_OK) { + return flash_rc; + } else { + return rc; + } +} + +#ifndef __riscv +/*! + * \brief Set NS API Permission + * \ingroup pico_bootrom + * + * Allow or disallow the specific NS API (note all NS APIs default to disabled). + * + * ns_api_num configures ARM-NS access to the given API. When an NS API is disabled, + * calling it will return BOOTROM_ERROR_NOT_PERMITTED. + * + * NOTE: All permissions default to disallowed after a reset. + * + * \param ns_api_num ns api number + * \param allowed permission + */ +static inline int rom_set_ns_api_permission(uint ns_api_num, bool allowed) { + rom_set_ns_api_permission_fn func = (rom_set_ns_api_permission_fn) rom_func_lookup_inline(ROM_FUNC_SET_NS_API_PERMISSION); + return func(ns_api_num, allowed); +} +#endif + +// todo SECURE only +/*! + * \brief Validate NS Buffer + * \ingroup pico_bootrom + * + * Utility method that can be used by secure ARM code to validate a buffer passed to it from Non-secure code. + * + * Both the write parameter and the (out) result parameter ok are RCP booleans, so 0xa500a500 for true, and 0x00c300c3 + * for false. This enables hardening of this function, and indeed the write parameter must be one of these values or the RCP + * will hang the system. + * + * For success, the entire buffer must fit in range XIP_BASE -> SRAM_END, and must be accessible by the Non-secure + * caller according to SAU + NS MPU (privileged or not based on current processor IPSR and NS CONTROL flag). Buffers + * in USB RAM are also allowed if access is granted to NS via ACCESSCTRL. + * + * \param addr buffer address + * \param size buffer size + * \param write rcp boolean, true if writeable + * \param ok rcp boolean result + */ +static inline void* rom_validate_ns_buffer(const void *addr, uint32_t size, uint32_t write, uint32_t *ok) { + rom_validate_ns_buffer_fn func = (rom_validate_ns_buffer_fn) rom_func_lookup_inline(ROM_FUNC_VALIDATE_NS_BUFFER); + return func(addr, size, write, ok); +} + +/*! + * \brief Set ROM callback function + * \ingroup pico_bootrom + * + * The only currently supported callback_number is 0 which sets the callback used for the secure_call API. + * + * A callback pointer of 0 deletes the callback function, a positive callback pointer (all valid function pointers are on RP2350) + * sets the callback function, but a negative callback pointer can be passed to get the old value without setting a new value. + * + * If successful, returns >=0 (the existing value of the function pointer on entry to the function). + * + * \param callback_num the callback number to set - only 0 is supported on RP2350 + * \param funcptr pointer to the callback function + */ +static inline intptr_t rom_set_rom_callback(uint callback_num, bootrom_api_callback_generic_t funcptr) { + rom_set_rom_callback_fn func = (rom_set_rom_callback_fn) rom_func_lookup_inline(ROM_FUNC_SET_ROM_CALLBACK); + return func(callback_num, funcptr); +} + +/*! + * \brief Get system information + * \ingroup pico_bootrom + * + * Fills a buffer with various system information. Note that this API is also used to return information over the picoboot interface. + * + * On success, the buffer is filled, and the number of words filled in the buffer is returned. + * + * The information returned is chosen by the flags parameter; the first word in the returned buffer, + * is the (sub)set of those flags that the API supports. You should always check this value before interpreting + * the buffer. + * + * "Boot Diagnostic" information is intended to help identify the cause of a failed boot, or booting into an unexpected binary. + * This information can be retrieved via picoboot after a watchdog reboot, however it will not survive + * a reset via the RUN pin or POWMAN reset. + * + * There is only one word of diagnostic information. What it records is based on the pp selection above, which + * is itself set as a parameter when rebooting programmatically into a normal boot. + * + * To get diagnostic info, pp must refer to a slot or an "A" partition; image diagnostics are automatically selected on boot + * from OTP or RAM image, or when chain_image() is called.) + * + * The diagnostic word thus contains data for either slot 0 and slot 1, or the "A" partition (and its "B" partition if it has one). The low half word + * of the diagnostic word contains information from slot 0 or partition A; the high half word contains information from slot 1 or partition B. + * + * To get a full picture of a failed boot involving slots and multiple partitions, the device can be rebooted + * multiple times to gather the information. + * + * \param out_buffer buffer to write data to + * \param out_buffer_word_size size of out_buffer, in words + * \param flags flags + */ +static inline int rom_get_sys_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags) { + rom_get_sys_info_fn func = (rom_get_sys_info_fn)rom_func_lookup_inline(ROM_FUNC_GET_SYS_INFO); + return func(out_buffer, out_buffer_word_size, flags); +} + +typedef struct { + union { + struct __packed { + int8_t diagnostic_partition_index; // used BOOT_PARTITION constants + uint8_t boot_type; + int8_t partition; + uint8_t tbyb_and_update_info; + }; + uint32_t boot_word; + }; + uint32_t boot_diagnostic; + uint32_t reboot_params[2]; +} boot_info_t; + +static inline int rom_get_boot_info(boot_info_t *info) { + uint32_t result[5]; + int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); + if (words_returned == (sizeof(result)/sizeof(result[0])) && result[0] == SYS_INFO_BOOT_INFO) { + memcpy(info, &result[1], sizeof(boot_info_t)); + return true; + } else { + return false; + } +} + +static inline int rom_get_last_boot_type_with_chained_flag(void) { + uint32_t result[5]; + int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); + if (words_returned == count_of(result) && result[0] == SYS_INFO_BOOT_INFO) { + // todo use struct + return (int)((result[1] & 0xff00u) >> 8); + } else { + return PICO_ERROR_INVALID_DATA; + } +} + +// BOOT_TYPE_NORMAL 0x0 +// BOOT_TYPE_BOOTSEL 0x2 +// BOOT_TYPE_RAM_IMAGE 0x3 +// BOOT_TYPE_FLASH_UPDATE 0x4 +// BOOT_TYPE_PC_SP 0xd +static inline int rom_get_last_boot_type(void) { + int rc = rom_get_last_boot_type_with_chained_flag(); + if (rc >= 0) rc &= ~BOOT_TYPE_CHAINED_FLAG; + return rc; +} + +/*! \brief Add a runtime partition to the partition table to specify flash permissions + * \ingroup pico_bootrom + * + * Note that a partition is added to the runtime view of the partition table maintained by the bootrom if there is space to do so + * + * Note that these permissions cannot override the permissions for any pre-existing partitions, as permission matches are made on a first partition found basis. + * + * @param start_offset the start_offset into flash in bytes (must be a multiple of 4K) + * @param size the size in byte (must be a multiple of 4K) + * @param permissions the bitwise OR of permissions from PICOBIN_PARTITION_PERMISSION_ constants, e.g. \ref PICOBIN_PARTITION_PERMISSION_S_R_BITS from boot/picobin.h + * @return >= 0 the partition number added if + * PICO_ERROR_BAD_ALIGNMENT if the start_offset or size aren't multiples of 4K. + * PICO_ERROR_INVALID_ARG if the start_offset or size are out of range, or invalid permission bits are set. + */ +int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32_t permissions); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif // !__ASSEMBLER__ +#endif From 8467bc84a34fead5b999e21de716dfa8966832f3 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 29 May 2026 13:51:01 +0100 Subject: [PATCH 07/10] Switch bootrom.h back to LF --- .../pico_bootrom/include/pico/bootrom.h | 2258 ++++++++--------- 1 file changed, 1129 insertions(+), 1129 deletions(-) diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index 1593ec5c0b..9498cec3c0 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -1,1129 +1,1129 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef _PICO_BOOTROM_H -#define _PICO_BOOTROM_H - -#include "pico.h" -#include "pico/bootrom_constants.h" - -/** \file bootrom.h - * \defgroup pico_bootrom pico_bootrom - * \brief Access to functions and data in the bootrom - * - * This header may be included by assembly code - */ - -#ifndef __ASSEMBLER__ -#include -#include "pico/bootrom/lock.h" -#include "pico/flash.h" -// ROM FUNCTION SIGNATURES - -#if PICO_RP2040 -typedef uint32_t (*rom_popcount32_fn)(uint32_t); -typedef uint32_t (*rom_reverse32_fn)(uint32_t); -typedef uint32_t (*rom_clz32_fn)(uint32_t); -typedef uint32_t (*rom_ctz32_fn)(uint32_t); -typedef uint8_t *(*rom_memset_fn)(uint8_t *, uint8_t, uint32_t); -typedef uint32_t *(*rom_memset4_fn)(uint32_t *, uint8_t, uint32_t); -typedef uint32_t *(*rom_memcpy_fn)(uint8_t *, const uint8_t *, uint32_t); -typedef uint32_t *(*rom_memcpy44_fn)(uint32_t *, const uint32_t *, uint32_t); -#endif -typedef void __attribute__((noreturn)) (*rom_reset_usb_boot_fn)(uint32_t, uint32_t); -typedef int (*rom_reboot_fn)(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1); -typedef rom_reset_usb_boot_fn reset_usb_boot_fn; // kept for backwards compatibility -typedef void (*rom_connect_internal_flash_fn)(void); -typedef void (*rom_flash_exit_xip_fn)(void); -typedef void (*rom_flash_range_erase_fn)(uint32_t, size_t, uint32_t, uint8_t); -typedef void (*rom_flash_range_program_fn)(uint32_t, const uint8_t*, size_t); -typedef void (*rom_flash_flush_cache_fn)(void); -typedef void (*rom_flash_enter_cmd_xip_fn)(void); -#if !PICO_RP2040 -typedef void (*rom_bootrom_state_reset_fn)(uint32_t flags); -typedef void (*rom_flash_reset_address_trans_fn)(void); -typedef void (*rom_flash_select_xip_read_mode_fn)(bootrom_xip_mode_t mode, uint8_t clkdiv); -typedef int (*rom_get_sys_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags); -typedef int (*rom_get_partition_table_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags); -typedef int (*rom_explicit_buy_fn)(uint8_t *buffer, uint32_t buffer_size); -typedef void* (*rom_validate_ns_buffer_fn)(const void *addr, uint32_t size, uint32_t write, uint32_t *ok); -/** - * @return BOOTROM_OK if successful - * BOOTROM_ERROR_INVALID_ARG if ns_api_num is out of range - */ -typedef intptr_t (*rom_set_rom_callback_fn)(uint callback_num, bootrom_api_callback_generic_t funcptr); -typedef int (*rom_chain_image_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t window_base, uint32_t window_size); -typedef int (*rom_load_partition_table_fn)(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload); -typedef int (*rom_pick_ab_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base); -typedef int (*rom_get_b_partition_fn)(uint pi_a); -typedef int (*rom_get_uf2_target_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out); -typedef int (*rom_func_otp_access_fn)(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd); -// Apply the address translation currently specified in QMI_ATRANSx ("rolling window" hardware -// translation). Need to take care using this on the boot path, as the QMI may not yet have been -// set up, but this should be suitable for translating system bus addresses into flash storage -// addresses in user callbacks. Returns all-ones for an invalid address, which is also an invalid -// flash storage address, so invalidity is propagated. -typedef intptr_t (*rom_flash_runtime_to_storage_addr_fn)(uintptr_t flash_runtime_addr); - -// Perform the specified erase/program/read operation, translating addresses according to -// QMI_ATRANSx if necessary, and checking flash permissions based on the resident partition table -// and the specified effective security level. `addr` may be either a flash runtime address or a -// flash storage address, depending on the ASPACE given in `flags`. -// -// NOTE: This function does not validate the buffer for NS access. This must be validated before -// calling if the caller is reachable from a Secure Gateway. -typedef int (*rom_flash_op_fn)(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf); - -#ifndef __riscv -typedef int (*rom_set_ns_api_permission_fn)(uint ns_api_num, bool allowed); -/** - * Note this is not strictly a C function; you must pass the function you are calling in r4 - * @param in_r4 - * `0b0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "well known" function selector; do not use for your own methods - * `0b10xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "unique" function selector intended to be unlikely to clash with others'. - * The lower 30 bits should be chosen at random - * `0b11xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "private" function selector intended for use by tightly coupled NS and S code - * - * @return whatever the secure call returns - * BOOTROM_ERROR_INVALID_STATE if no secure handler has been set from the secure side - * via rom_set_rom_callback_fn(BOOTROM_API_CALLBACK_secure_call, ...) - */ -typedef int (*rom_func_secure_call)(uintptr_t a0, ...); -#endif - -#ifdef __riscv -typedef struct { - uint32_t *base; - uint32_t size; -} bootrom_stack_t; -// passed in, and out. -typedef int (*rom_set_bootrom_stack_fn)(bootrom_stack_t *stack); -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/*! \brief Return a bootrom lookup code based on two ASCII characters - * \ingroup pico_bootrom - * - * These codes are uses to lookup data or function addresses in the bootrom - * - * \param c1 the first character - * \param c2 the second character - * \return the 'code' to use in rom_func_lookup() or rom_data_lookup() - */ -static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) { - return ROM_TABLE_CODE((uint32_t) c1, (uint32_t) c2); -} - -/*! - * \brief Lookup a bootrom function by its code - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the function, or NULL if the code does not match any bootrom function - */ -void *rom_func_lookup(uint32_t code); - -/*! - * \brief Lookup a bootrom data address by its code - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the data, or NULL if the code does not match any bootrom function - */ -void *rom_data_lookup(uint32_t code); - -/*! - * \brief Helper function to lookup the addresses of multiple bootrom functions - * \ingroup pico_bootrom - * - * This method looks up the 'codes' in the table, and convert each table entry to the looked up - * function pointer, if there is a function for that code in the bootrom. - * - * \param table an IN/OUT array, elements are codes on input, function pointers on success. - * \param count the number of elements in the table - * \return true if all the codes were found, and converted to function pointers, false otherwise - */ -bool rom_funcs_lookup(uint32_t *table, unsigned int count); - -// Bootrom function: rom_table_lookup -// Returns the 32 bit pointer into the ROM if found or NULL otherwise. -#if PICO_RP2040 -typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code); -#else -typedef void *(*rom_table_lookup_fn)(uint32_t code, uint32_t mask); -#endif - -#if PICO_C_COMPILER_IS_GNU && (__GNUC__ >= 12) -// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer -__force_inline static void *rom_hword_as_ptr(uint16_t rom_address) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" - return (void *)(uintptr_t)*(uint16_t *)(uintptr_t)rom_address; -#pragma GCC diagnostic pop -} -#else -// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer -#define rom_hword_as_ptr(rom_address) (void *)(uintptr_t)(*(uint16_t *)(uintptr_t)(rom_address)) -#endif - -#ifdef __riscv -static __force_inline bool rom_size_is_64k(void) { -#ifdef RASPBERRYPI_AMETHYST_FPGA - return *(uint16_t*)0x14 >= 0x8000; -#else - return false; -#endif -} -#endif - -/*! - * \brief Lookup a bootrom function by code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the function, or NULL if the code does not match any bootrom function - */ -#pragma GCC diagnostic push -// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not -#pragma GCC diagnostic ignored "-Warray-bounds" -static __force_inline void *rom_func_lookup_inline(uint32_t code) { -#if PICO_RP2040 - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); - uint16_t *func_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_FUNC_TABLE_OFFSET); - return rom_table_lookup(func_table, code); -#else -#ifdef __riscv - uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; - // on RISC-V the code (a jmp) is actually embedded in the table - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_ENTRY_OFFSET + rom_offset_adjust); - return rom_table_lookup(code, RT_FLAG_FUNC_RISCV); -#else - // on ARM the function pointer is stored in the table, so we dereference it - // via lookup() rather than lookup_entry() - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); - if (pico_processor_state_is_nonsecure()) { - return rom_table_lookup(code, RT_FLAG_FUNC_ARM_NONSEC); - } else { - return rom_table_lookup(code, RT_FLAG_FUNC_ARM_SEC); - } -#endif -#endif -} -#pragma GCC diagnostic pop - -/*! - * \brief Lookup a bootrom data address by its code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage - * \ingroup pico_bootrom - * \param code the code - * \return a pointer to the data, or NULL if the code does not match any bootrom data - */ -#pragma GCC diagnostic push -// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not -#pragma GCC diagnostic ignored "-Warray-bounds" -static __force_inline void *rom_data_lookup_inline(uint32_t code) { -#if PICO_RP2040 - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); - uint16_t *data_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_DATA_TABLE_OFFSET); - return rom_table_lookup(data_table, code); -#else -#ifdef __riscv - uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET + rom_offset_adjust); -#else - rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); -#endif - return rom_table_lookup(code, RT_FLAG_DATA); -#endif -} -#pragma GCC diagnostic pop - -/*! - * \brief Reboot the device into BOOTSEL mode - * \ingroup pico_bootrom - * - * This function reboots the device into the BOOTSEL mode ('usb boot"). - * - * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, - * and to limit the USB interfaces exposed. - * - * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display - * the activity LED correctly. - * - * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which - * GPIO pin should be set to output and raised whenever there is mass storage activity - * from the host. - * \param disable_interface_mask value to control exposed interfaces - * - 0 To enable both interfaces (as per a cold boot) - * - 1 To disable the USB Mass Storage Interface - * - 2 To disable the USB PICOBOOT Interface - */ -void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask); -static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask) { - rom_reset_usb_boot(usb_activity_gpio_pin_mask, disable_interface_mask); -} - -/*! - * \brief Reboot the device into BOOTSEL mode - * \ingroup pico_bootrom - * - * This function reboots the device into the BOOTSEL mode ('usb boot"). - * - * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, - * and to limit the USB interfaces exposed. - * - * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display - * the activity LED correctly. - * - * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none - * from the host. - * \param disable_interface_mask value to control exposed interfaces - * - 0 To enable both interfaces (as per a cold boot) - * - 1 To disable the USB Mass Storage Interface - * - 2 To disable the USB PICOBOOT Interface - * \param usb_activity_gpio_pin_active_low Activity GPIO is active low (ignored on RP2040). A bug in the bootrom of RP2350 - * A4 chips means this parameter has no effect on that version of the RP2350. - */ -void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pin, uint32_t disable_interface_mask, bool usb_activity_gpio_pin_active_low); - -/*! - * \brief Connect the SSI/QMI to the QSPI pads - * \ingroup pico_bootrom - * - * Restore all QSPI pad controls to their default state, and connect the SSI/QMI peripheral to the QSPI pads. - * - * \if rp2350_specific - * On RP2350 if a secondary flash chip select GPIO has been configured via OTP OTP_DATA_FLASH_DEVINFO, or by writing to the runtime - * copy of FLASH_DEVINFO in bootram, then this bank 0 GPIO is also initialised and the QMI peripheral is connected. Otherwise, - * bank 0 IOs are untouched. - * \endif - */ -static inline void rom_connect_internal_flash(void) { - rom_connect_internal_flash_fn func = (rom_connect_internal_flash_fn) rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); - func(); -} - -/*! - * \brief Return the QSPI device from its XIP state to a serial command state - * \ingroup pico_bootrom - * - * \if rp2040_specific - * On RP2040, first set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in Section 2.8.1.2 - * of the datasheet. Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be cleared before returning - * the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This function configures the SSI with a fixed SCK clock divisor of /6. - * \endif - * - * \if rp2350_specific - * On RP2350, Initialise the QMI for serial operations (direct mode), and also initialise a basic XIP mode, where the QMI will perform - * 03h serial read commands at low speed (CLKDIV=12) in response to XIP reads. - * - * Then, issue a sequence to the QSPI device on chip select 0, designed to return it from continuous read mode ("XIP mode") and/or - * QPI mode to a state where it will accept serial commands. This is necessary after system reset to restore the QSPI device to a known - * state, because resetting RP2350 does not reset attached QSPI devices. It is also necessary when user code, having already performed - * some continuous-read-mode or QPI-mode accesses, wishes to return the QSPI device to a state where it will accept the serial erase and - * programming commands issued by the bootrom's flash access functions. - * - * If a GPIO for the secondary chip select is configured via FLASH_DEVINFO, then the XIP exit sequence is also issued to chip select 1. - * - * The QSPI device should be accessible for XIP reads after calling this function; the name flash_exit_xip refers to returning the QSPI - * device from its XIP state to a serial command state. - * \endif - */ -static inline void rom_flash_exit_xip(void) { - rom_flash_exit_xip_fn func = (rom_flash_exit_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); - func(); -} - -/*! - * \brief Erase bytes in flash - * \ingroup pico_bootrom - * - * Erase count bytes, starting at addr (offset from start of flash). Optionally, pass a block erase command e.g. D8h block erase, - * and the size of the block erased by this command - this function will use the larger block erase where possible, for much higher - * erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of 4096 bytes. - * - * This is a low-level flash API, and no validation of the arguments is performed. - * - * \if rp2350_specific - * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, and can transparently - * apply a runtime-to-storage address translation. - * - * The QSPI device must be in a serial command state before calling this API, which can be achieved by calling rom_connect_internal_flash() - * followed by rom_flash_exit_xip(). After the erase, the flash cache should be flushed via rom_flash_flush_cache() to ensure the modified - * flash data is visible to cached XIP accesses. - * - * Finally, the original XIP mode should be restored by copying the saved XIP setup function from bootram into SRAM, and executing it: - * the bootrom provides a default function which restores the flash mode/clkdiv discovered during flash scanning, and user programs can - * override this with their own XIP setup function. - * - * For the duration of the erase operation, QMI is in direct mode and attempting to access XIP from DMA, the debugger or the other core will - * return a bus fault. XIP becomes accessible again once the function returns. - * \endif - * - * \param addr the offset from start of flash to be erased - * \param count number of bytes to erase - * \param block_size optional size of block erased by block_cmd - * \param block_cmd optional block erase command e.g. D8h block erase - */ -static inline void rom_flash_range_erase(uint32_t addr, size_t count, uint32_t block_size, uint8_t block_cmd) { - rom_flash_range_erase_fn func = (rom_flash_range_erase_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_ERASE); - func(addr, count, block_size, block_cmd); -} - -/*! - * \brief Program bytes in flash - * \ingroup pico_bootrom - * - * Program data to a range of flash addresses starting at addr (offset from the start of flash) and count bytes in size. addr must be - * aligned to a 256-byte boundary, and count must be a multiple of 256. - * - * This is a low-level flash API, and no validation of the arguments is performed. - * - * \if rp2350_specific - * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, - * and can transparently apply a runtime-to-storage address translation. - * - * The QSPI device must be in a serial command state before calling this API - see notes on rom_flash_range_erase - * \endif - * - * \param addr the offset from start of flash to be erased - * \param data buffer containing the data to be written - * \param count number of bytes to erase - */ -static inline void rom_flash_range_program(uint32_t addr, const uint8_t *data, size_t count) { - rom_flash_range_program_fn func = (rom_flash_range_program_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); - func(addr, data, count); -} - -/*! - * \brief Flush the XIP cache - * \ingroup pico_bootrom - * - * \if rp2040_specific - * Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can drive the flash chip select as normal. - * \endif - * - * \if rp2350_specific - * Flush the entire XIP cache, by issuing an invalidate by set/way maintenance operation to every cache line. This ensures that flash - * program/erase operations are visible to subsequent cached XIP reads. - * - * Note that this unpins pinned cache lines, which may interfere with cache-as-SRAM use of the XIP cache. - * - * No other operations are performed. - * \endif - */ -static inline void rom_flash_flush_cache(void) { - rom_flash_flush_cache_fn func = (rom_flash_flush_cache_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); - func(); -} - -/*! - * \brief Configure the SSI/QMI with a standard command - * \ingroup pico_bootrom - * - * Configure the SSI/QMI to generate a standard 03h serial read command, with 24 address bits, upon each XIP access. This is a slow XIP - * configuration, but is widely supported. CLKDIV is set to 12 on RP2350. The debugger may call this function to ensure that flash is - * readable following a program/erase operation. - * - * Note that the same setup is performed by flash_exit_xip(), and the RP2350 flash program/erase functions do not leave XIP in an - * inaccessible state, so calls to this function are largely redundant on RP2350. It is provided on RP2350 for compatibility with RP2040. - */ -static inline void rom_flash_enter_cmd_xip(void) { - rom_flash_enter_cmd_xip_fn func = (rom_flash_enter_cmd_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_ENTER_CMD_XIP); - func(); -} - -#if !PICO_RP2040 || PICO_COMBINED_DOCS -#ifdef __riscv -/*! - * \brief Give the bootrom a new stack - * \ingroup pico_bootrom - * - * Most bootrom functions are written just once, in Arm code, to save space. As a result these functions are emulated when - * running under the RISC-V architecture. This is largely transparent to the user, however the stack used by the Arm emulation - * is separate from the calling user's stack, and is stored in boot RAM but is of quite limited size. When using certain of the more - * complex APIs or if nesting bootrom calls from within IRQs, you may need to provide a large stack. - * - * This method allows the caller to specify a region of RAM to use as the stack for the current core by passing a pointer to two values: the word aligned base address, - * and the size in bytes (multiple of 4). - * - * The method fills in the previous base/size values into the passed array before returning. - * - * \param stack bootrom_stack_t struct containing base and size - */ -static inline int rom_set_bootrom_stack(bootrom_stack_t *stack) { - rom_set_bootrom_stack_fn func = (rom_set_bootrom_stack_fn) rom_func_lookup_inline(ROM_FUNC_SET_BOOTROM_STACK); - return func(stack); -} -#endif - -/*! - * \brief Reboot using the watchdog - * \ingroup pico_bootrom - * - * Resets the chip and uses the watchdog facility to restart. - * - * The delay_ms is the millisecond delay before the reboot occurs. Note: by default this method is asynchronous - * (unless NO_RETURN_ON_SUCCESS is set - see below), so the method will return and the reboot will happen this many milliseconds later. - * - * The flags field contains one of the following values: - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL - reboot into the normal boot path. - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL - reboot into BOOTSEL mode. - * p0 - a set of flags: - * 0x01 : DISABLE_MSD_INTERFACE - Disable the BOOTSEL USB drive (see <>) - * 0x02 : DISABLE_PICOBOOT_INTERFACE - Disable the {picoboot} interface (see <>). - * 0x10 : GPIO_PIN_ACTIVE_LOW - The GPIO specified in p1 is active low (GPIO_PIN_SPECIFIED must also be set). - * 0x20 : GPIO_PIN_SPECIFIED - Enable the activity indicator on the GPIO specified in p1. - * p1 - the GPIO number to use as an activity indicator (enabled by GPIO_PIN_SPECIFIED flag in p0). - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE - reboot into an image in RAM. The region of RAM or XIP RAM is searched for an image to run. This is the type - * of reboot used when a RAM UF2 is dragged onto the BOOTSEL USB drive. - * p0 - the region start address (word-aligned). - * p1 - the region size (word-aligned). - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_FLASH_UPDATE - variant of \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL to use when flash has been updated. This is the type - * of reboot used after dragging a flash UF2 onto the BOOTSEL USB drive. - * p0 - the address of the start of the region of flash that was updated. If this address matches the start address of a partition or slot, then that - * partition or slot is treated preferentially during boot (when there is a choice). This type of boot facilitates TBYB and version downgrades. - * - * \ref REBOOT2_FLAG_REBOOT_TYPE_PC_SP - reboot to a specific PC and SP. Note: this is not allowed in the ARM-NS variant. - * p0 - the initial program counter (PC) to start executing at. This must have the lowest bit set for Arm and clear for RISC-V - * p1 - the initial stack pointer (SP). - * - * All of the above, can have optional flags ORed in: - * - * \ref REBOOT2_FLAG_REBOOT_TO_ARM - switch both cores to the Arm architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the Arm architecture is not supported. - * \ref REBOOT2_FLAG_REBOOT_TO_RISCV - switch both cores to the RISC-V architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the RISC-V architecture is not supported. - * \ref REBOOT2_FLAG_NO_RETURN_ON_SUCCESS - the watchdog h/w is asynchronous. Setting this bit forces this method not to return if the reboot is successfully initiated. - * - * \param flags the reboot flags, as detailed above - * \param delay_ms millisecond delay before the reboot occurs - * \param p0 parameter 0, depends on flags - * \param p1 parameter 1, depends on flags - */ -static inline int rom_reboot(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1) { - rom_reboot_fn func = (rom_reboot_fn) rom_func_lookup_inline(ROM_FUNC_REBOOT); - return func(flags, delay_ms, p0, p1); -} - -bool rom_get_boot_random(uint32_t out[4]); - -/*! - * \brief Reset bootrom state - * \ingroup pico_bootrom - * - * Resets internal bootrom state, based on the following flags: - * - * STATE_RESET_CURRENT_CORE - Resets any internal bootrom state for the current core into a clean state. - * This method should be called prior to calling any other bootrom APIs on the current core, - * and is called automatically by the bootrom during normal boot of core 0 and launch of code on core 1. - * - * STATE_RESET_OTHER_CORE - Resets any internal bootrom state for the other core into a clean state. This is generally called by - * a debugger when resetting the state of one core via code running on the other. - * - * STATE_RESET_GLOBAL_STATE - Resets all non core-specific state, including: - * Disables access to bootrom APIs from ARM-NS - * Unlocks all BOOT spinlocks - * Clears any secure code callbacks - * - * Note: the sdk calls this method on runtime initialisation to put the bootrom into a known state. This - * allows the program to function correctly if it is entered (e.g. from a debugger) without taking the usual boot path (which - * resets the state appropriately itself). - * - * \param flags flags, as detailed above - */ -static inline void rom_bootrom_state_reset(uint32_t flags) { - rom_bootrom_state_reset_fn func = (rom_bootrom_state_reset_fn) rom_func_lookup_inline(ROM_FUNC_BOOTROM_STATE_RESET); - return func(flags); -} - -/*! - * \brief Reset address translation - * \ingroup pico_bootrom - * - * Restore the QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7, to their reset state. This makes the - * runtime-to-storage address map an identity map, i.e. the mapped and unmapped address are equal, and the entire space is - * fully mapped. - */ -static inline void rom_flash_reset_address_trans(void) { - rom_flash_reset_address_trans_fn func = (rom_flash_reset_address_trans_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RESET_ADDRESS_TRANS); - func(); -} - -/*! - * \brief Configure QMI in a XIP read mode - * \ingroup pico_bootrom - * - * Configure QMI for one of a small menu of XIP read modes supported by the bootrom. This mode is configured for both memory - * windows (both chip selects), and the clock divisor is also applied to direct mode. - * - * \param mode bootrom_xip_mode_t mode to use - * \param clkdiv clock divider - */ -static inline void rom_flash_select_xip_read_mode(bootrom_xip_mode_t mode, uint8_t clkdiv) { - rom_flash_select_xip_read_mode_fn func = (rom_flash_select_xip_read_mode_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_SELECT_XIP_READ_MODE); - func(mode, clkdiv); -} - -typedef struct { - cflash_flags_t flags; - uintptr_t addr; - uint32_t size_bytes; - uint8_t *buf; - int *res; -} rom_helper_flash_op_params_t; - -static inline void rom_helper_flash_op(void *param) { - const rom_helper_flash_op_params_t *op = (const rom_helper_flash_op_params_t *)param; - rom_flash_op_fn func = (rom_flash_op_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_OP); - *(op->res) = func(op->flags, op->addr, op->size_bytes, op->buf); -} - -/*! - * \brief Perform a flash read, erase, or program operation - * \ingroup pico_bootrom - * - * The flash operation is bounds-checked against the known flash devices specified by the runtime value of FLASH_DEVINFO, - * stored in bootram. This is initialised by the bootrom to the OTP value OTP_DATA_FLASH_DEVINFO, if - * OTP_DATA_BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is set; otherwise it is initialised to 16 MiB for chip select 0 and 0 bytes - * for chip select 1. FLASH_DEVINFO can be updated at runtime by writing to its location in bootram, the pointer to which - * can be looked up in the ROM table. - * - * If a resident partition table is in effect, then the flash operation is also checked against the partition permissions. - * The Secure version of this function can specify the caller's effective security level (Secure, Non-secure, bootloader) - * using the CFLASH_SECLEVEL_BITS bitfield of the flags argument, whereas the Non-secure function is always checked against - * the Non-secure permissions for the partition. Flash operations which span two partitions are not allowed, and will fail - * address validation. - * - * If OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED is set, erase operations will use a D8h 64 kiB block erase command where - * possible (without erasing outside the specified region), for faster erase time. Otherwise, only 20h 4 kiB sector erase - * commands are used. - * - * Optionally, this API can translate addr from flash runtime addresses to flash storage addresses, according to the - * translation currently configured by QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7. For example, an - * image stored at a +2 MiB offset in flash (but mapped at XIP address 0 at runtime), writing to an offset of +1 MiB into - * the image, will write to a physical flash storage address of 3 MiB. Translation is enabled by setting the - * CFLASH_ASPACE_BITS bitfield in the flags argument. - * - * When translation is enabled, flash operations which cross address holes in the XIP runtime address space (created by - * non-maximum ATRANSx_SIZE) will return an error response. This check may tear: the transfer may be partially performed - * before encountering an address hole and ultimately returning failure. - * - * When translation is enabled, flash operations are permitted to cross chip select boundaries, provided this does not - * span an ATRANS address hole. When translation is disabled, the entire operation must target a single flash chip select - * (as determined by bits 24 and upward of the address), else address validation will fail. - * - * \param flags controls the security level, address space, and flash operation - * \param addr the address of the first flash byte to be accessed, ranging from XIP_BASE to XIP_BASE + 0x1ffffff - * \param size_bytes size of buf, in bytes - * \param buf contains data to be written to flash, for program operations, and data read back from flash, for read operations - */ -static inline int rom_flash_op(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf) { - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_FLASH_OP)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = 0; - rom_helper_flash_op_params_t params = { - .flags = flags, - .addr = addr, - .size_bytes = size_bytes, - .buf = buf, - .res = &rc - }; - int flash_rc = flash_safe_execute(rom_helper_flash_op, ¶ms, UINT32_MAX); - bootrom_release_lock(BOOTROM_LOCK_FLASH_OP); - if (flash_rc != PICO_OK) { - return flash_rc; - } else { - return rc; - } -} - -/*! - * \brief Writes data from a buffer into OTP, or reads data from OTP into a buffer - * \ingroup pico_bootrom - * - * The buffer must be aligned to 2 bytes or 4 bytes according to the IS_ECC flag. - * - * This method will read and write rows until the first row it encounters that fails a key or permission check at which - * it will return BOOTROM_ERROR_NOT_PERMITTED. - * - * Writing will also stop at the first row where an attempt is made to set an OTP bit from a 1 to a 0, and - * BOOTROM_ERROR_UNSUPPORTED_MODIFICATION will be returned. - * - * If all rows are read/written successfully, then BOOTROM_OK will be returned. - * - * \param buf buffer to read to/write from - * \param buf_len size of buf - * \param cmd OTP command to execute - * - 0x0000ffff - ROW_NUMBER: 16 low bits are row number (0-4095) - * - 0x00010000 - IS_WRITE: if set, do a write (not a read) - * - 0x00020000 - IS_ECC: if this bit is set, each value in the buffer is 2 bytes and ECC is used when read/writing from 24 - * bit value in OTP. If this bit is not set, each value in the buffer is 4 bytes, the low 24-bits of which are written - * to or read from OTP. - - */ -static inline int rom_func_otp_access(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd) { - rom_func_otp_access_fn func = (rom_func_otp_access_fn) rom_func_lookup_inline(ROM_FUNC_OTP_ACCESS); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_OTP)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(buf, buf_len, cmd); - bootrom_release_lock(BOOTROM_LOCK_OTP); - return rc; -} - -/*! - * \brief Fills a buffer with information from the partition table - * \ingroup pico_bootrom - * - * Fills a buffer with information from the partition table. Note that this API is also used to return information over the - * picoboot interface. - * - * On success, the buffer is filled, and the number of words filled in the buffer is returned. If the partition table - * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return BOOTROM_ERROR_NO_DATA, and you - * should load the partition table via load_partition_table() first. - * - * Note that not all data from the partition table is kept resident in memory by the bootrom due to size constraints. - * To protect against changes being made in flash after the bootrom has loaded the resident portion, the bootrom keeps - * a hash of the partition table as of the time it loaded it. If the hash has changed by the time this method is called, - * then it will return BOOTROM_ERROR_INVALID_STATE. - * - * The information returned is chosen by the partition_and_flags parameter; the first word in the returned buffer, - * is the (sub)set of those flags that the API supports. You should always check this value before interpreting - * the buffer. - * - * Following the first word, returns words of data for each present flag in order. With the exception of PT_INFO, - * all the flags select "per partition" information, so each field is returned in flag order for one partition after - * the next. The special SINGLE_PARTITION flag indicates that data for only a single partition is required. - * - * \param out_buffer buffer to write data to - * \param out_buffer_word_size size of out_buffer, in words - * \param partition_and_flags partition number and flags - */ -static inline int rom_get_partition_table_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags) { - rom_get_partition_table_info_fn func = (rom_get_partition_table_info_fn) rom_func_lookup_inline(ROM_FUNC_GET_PARTITION_TABLE_INFO); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(out_buffer, out_buffer_word_size, partition_and_flags); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -// todo SECURE only -/*! - * \brief Loads the current partition table from flash, if present - * \ingroup pico_bootrom - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size - * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * If force_reload is false, then this method will return BOOTROM_OK immediately if the bootrom is loaded, otherwise it will - * reload the partition table if it has been loaded already, allowing for the partition table to be updated in a running program. - * - * \param workarea_base base address of work area - * \param workarea_size size of work area - * \param force_reload force reloading of the partition table - */ -static inline int rom_load_partition_table(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload) { - rom_load_partition_table_fn func = (rom_load_partition_table_fn) rom_func_lookup_inline(ROM_FUNC_LOAD_PARTITION_TABLE); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(workarea_base, workarea_size, force_reload); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -// todo SECURE only -/*! - * \brief Pick a partition from an A/B pair - * \ingroup pico_bootrom - * - * Determines which of the partitions has the "better" IMAGE_DEF. In the case of executable images, this is the one that would be booted - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should bye word aligned, and of sufficient size - * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * The passed partition number can be any valid partition number other than the "B" partition of an A/B pair. - * - * This method returns a negative error code, or the partition number of the picked partition if (i.e. partition_a_num or the - * number of its "B" partition if any). - * - * 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 \ref rom_pick_ab_partition_during_update() 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 - * \param flash_update_boot_window_base the flash update base, to pick that partition instead of the normally "better" partition - * \return >= 0 the chosen partition number out of the A/B pair - */ -static inline int rom_pick_ab_partition(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base) { - rom_pick_ab_partition_fn func = (rom_pick_ab_partition_fn) rom_func_lookup_inline(ROM_FUNC_PICK_AB_PARTITION); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(workarea_base, workarea_size, partition_a_num, flash_update_boot_window_base); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -/*! \brief Pick A/B partition without disturbing any in progress Flash Update boot or TBYB boot - * \ingroup pico_bootrom - * - * This will perform the same function as \ref 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 \ref rom_pick_ab_partition(). - * - * This should be used instead of \ref rom_pick_ab_partition() when performing a Flash Update Boot before calling \ref rom_explicit_buy(), and can still be - * used without issue when a Flash Update Boot is not in progress. - * - * This function is necessary because if an `explicit_buy` is pending then calling `pick_ab_partition` would 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 two partitions with one `explicit_buy` call). - * - * This function 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 by \ref rom_pick_ab_partition() - * 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_partition_during_update(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num); - -/*! - * \brief Get B partition - * \ingroup pico_bootrom - * - * Returns the index of the B partition of partition A if a partition table is present and loaded, and there is a partition A with a B partition; - * otherwise returns BOOTROM_ERROR_NOT_FOUND. - * - * \param pi_a the A partition number - */ -static inline int rom_get_b_partition(uint pi_a) { - rom_get_b_partition_fn func = (rom_get_b_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_B_PARTITION); - return func(pi_a); -} - -// todo SECURE only -/*! - * \brief Get UF2 Target Partition - * \ingroup pico_bootrom - * - * This method performs the same operation to decide on a target partition for a UF2 family ID as when a UF2 is dragged onto the USB - * drive in BOOTSEL mode. - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size - * or `BOOTROM_ERROR_INSUFFICIENT_RESOURCES` will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * If the partition table - * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return `BOOTROM_ERROR_PRECONDITION_NOT_MET`, and you - * should load the partition table via <> first. - * - * \param workarea_base base address of work area - * \param workarea_size size of work area - * \param family_id the family ID to place - * \param partition_out pointer to the resident_partition_t to fill with the partition data - */ -static inline int rom_get_uf2_target_partition(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out) { - rom_get_uf2_target_partition_fn func = (rom_get_uf2_target_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_UF2_TARGET_PARTITION); - if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) - return BOOTROM_ERROR_LOCK_REQUIRED; - int rc = func(workarea_base, workarea_size, family_id, partition_out); - bootrom_release_lock(BOOTROM_LOCK_SHA_256); - return rc; -} - -/*! - * \brief Translate runtime to storage address - * \ingroup pico_bootrom - * - * Applies the address translation currently configured by QMI address translation registers. - * - * Translating an address outside of the XIP runtime address window, or beyond the bounds of an ATRANSx_SIZE field, returns BOOTROM_ERROR_INVALID_ADDRESS, - * which is not a valid flash storage address. Otherwise, return the storage address which QMI would access when presented with the runtime address addr. - * This is effectively a virtual-to-physical address translation for QMI. - * - * \param flash_runtime_addr the address to translate - */ -static inline intptr_t rom_flash_runtime_to_storage_addr(uintptr_t flash_runtime_addr) { - rom_flash_runtime_to_storage_addr_fn func = (rom_flash_runtime_to_storage_addr_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RUNTIME_TO_STORAGE_ADDR); - return func(flash_runtime_addr); -} - -// todo SECURE only -/*! - * \brief Chain into a launchable image - * \ingroup pico_bootrom - * - * Searches a memory region for a launchable image, and executes it if possible. - * - * The region_base and region_size specify a word-aligned, word-multiple-sized area of RAM, XIP RAM or flash to search. - * The first 4 kiB of the region must contain the start of a Block Loop with an IMAGE_DEF. If the new image is launched, - * the call does not return otherwise an error is returned. - * - * The region_base is signed, as a negative value can be passed, which indicates that the (negated back to positive value) - * is both the region_base and the base of the "flash update" region. - * - * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. - * As a result it requires a user provided memory buffer as a work area. The work area should be word aligned, and of sufficient size - * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. - * - * NOTE: This method is primarily expected to be used when implementing bootloaders. - * - * NOTE: When chaining into an image, the OTP_DATA_BOOT_FLAGS0_ROLLBACK_REQUIRED flag will not be set, to prevent invalidating a bootloader - * without a rollback version by booting a binary which has one. - * - * \param workarea_base base address of work area - * \param workarea_size size of work area - * \param region_base base address of image - * \param region_size size of window containing image - */ -static inline int rom_chain_image(uint8_t *workarea_base, uint32_t workarea_size, uint32_t region_base, uint32_t region_size) { - rom_chain_image_fn func = (rom_chain_image_fn) rom_func_lookup_inline(ROM_FUNC_CHAIN_IMAGE); - bootrom_release_lock(BOOTROM_LOCK_ENABLE); - uint32_t interrupt_flags = save_and_disable_interrupts(); - int rc = func(workarea_base, workarea_size, region_base, region_size); - restore_interrupts_from_disabled(interrupt_flags); - bootrom_acquire_lock_blocking(BOOTROM_LOCK_ENABLE); - return rc; -} - -typedef struct { - uint8_t *buffer; - uint32_t buffer_size; - int *res; -} rom_helper_explicit_buy_params_t; - -static inline void rom_helper_explicit_buy(void *param) { - const rom_helper_explicit_buy_params_t *op = (const rom_helper_explicit_buy_params_t *)param; - rom_explicit_buy_fn func = (rom_explicit_buy_fn) rom_func_lookup_inline(ROM_FUNC_EXPLICIT_BUY); - *(op->res) = func(op->buffer, op->buffer_size); -} - -// todo SECURE only -/*! - * \brief Buy an image - * \ingroup pico_bootrom - * - * Perform an "explicit" buy of an executable launched via an IMAGE_DEF which was "explicit buy" flagged. A "flash update" - * boot of such an image is a way to have the image execute once, but only become the "current" image if it calls - * back into the bootrom via this call. - * - * This call may perform the following: - * - * - Erase and rewrite the part of flash containing the "explicit buy" flag in order to clear said flag. - * - Erase the first sector of the other partition in an A/B partition scenario, if this new IMAGE_DEF is a version downgrade - * (so this image will boot again when not doing a "flash update" boot) - * - Update the rollback version in OTP if the chip is secure, and a rollback version is present in the image. - * - * NOTE: The device may reboot while updating the rollback version, if multiple rollback rows need to be written - this occurs - * when the version crosses a multiple of 24 (for example upgrading from version 23 to 25 requires a reboot, but 23 to 24 or 24 to 25 doesn't). - * The application should therefore be prepared to reboot when calling this function, if rollback versions are in use. - * - * Note that the first of the above requires 4 kiB of scratch space, so you should pass a word aligned buffer of at least 4 kiB to this method, - * or it will return BOOTROM_ERROR_INSUFFICIENT_RESOURCES if the "explicit buy" flag needs to be cleared. - * - * \param buffer base address of scratch space - * \param buffer_size size of scratch space - */ -static inline int rom_explicit_buy(uint8_t *buffer, uint32_t buffer_size) { - int rc = 0; - rom_helper_explicit_buy_params_t params = { - .buffer = buffer, - .buffer_size = buffer_size, - .res = &rc - }; - int flash_rc = flash_safe_execute(rom_helper_explicit_buy, ¶ms, UINT32_MAX); - if (flash_rc != PICO_OK) { - return flash_rc; - } else { - return rc; - } -} - -#ifndef __riscv -/*! - * \brief Set NS API Permission - * \ingroup pico_bootrom - * - * Allow or disallow the specific NS API (note all NS APIs default to disabled). - * - * ns_api_num configures ARM-NS access to the given API. When an NS API is disabled, - * calling it will return BOOTROM_ERROR_NOT_PERMITTED. - * - * NOTE: All permissions default to disallowed after a reset. - * - * \param ns_api_num ns api number - * \param allowed permission - */ -static inline int rom_set_ns_api_permission(uint ns_api_num, bool allowed) { - rom_set_ns_api_permission_fn func = (rom_set_ns_api_permission_fn) rom_func_lookup_inline(ROM_FUNC_SET_NS_API_PERMISSION); - return func(ns_api_num, allowed); -} -#endif - -// todo SECURE only -/*! - * \brief Validate NS Buffer - * \ingroup pico_bootrom - * - * Utility method that can be used by secure ARM code to validate a buffer passed to it from Non-secure code. - * - * Both the write parameter and the (out) result parameter ok are RCP booleans, so 0xa500a500 for true, and 0x00c300c3 - * for false. This enables hardening of this function, and indeed the write parameter must be one of these values or the RCP - * will hang the system. - * - * For success, the entire buffer must fit in range XIP_BASE -> SRAM_END, and must be accessible by the Non-secure - * caller according to SAU + NS MPU (privileged or not based on current processor IPSR and NS CONTROL flag). Buffers - * in USB RAM are also allowed if access is granted to NS via ACCESSCTRL. - * - * \param addr buffer address - * \param size buffer size - * \param write rcp boolean, true if writeable - * \param ok rcp boolean result - */ -static inline void* rom_validate_ns_buffer(const void *addr, uint32_t size, uint32_t write, uint32_t *ok) { - rom_validate_ns_buffer_fn func = (rom_validate_ns_buffer_fn) rom_func_lookup_inline(ROM_FUNC_VALIDATE_NS_BUFFER); - return func(addr, size, write, ok); -} - -/*! - * \brief Set ROM callback function - * \ingroup pico_bootrom - * - * The only currently supported callback_number is 0 which sets the callback used for the secure_call API. - * - * A callback pointer of 0 deletes the callback function, a positive callback pointer (all valid function pointers are on RP2350) - * sets the callback function, but a negative callback pointer can be passed to get the old value without setting a new value. - * - * If successful, returns >=0 (the existing value of the function pointer on entry to the function). - * - * \param callback_num the callback number to set - only 0 is supported on RP2350 - * \param funcptr pointer to the callback function - */ -static inline intptr_t rom_set_rom_callback(uint callback_num, bootrom_api_callback_generic_t funcptr) { - rom_set_rom_callback_fn func = (rom_set_rom_callback_fn) rom_func_lookup_inline(ROM_FUNC_SET_ROM_CALLBACK); - return func(callback_num, funcptr); -} - -/*! - * \brief Get system information - * \ingroup pico_bootrom - * - * Fills a buffer with various system information. Note that this API is also used to return information over the picoboot interface. - * - * On success, the buffer is filled, and the number of words filled in the buffer is returned. - * - * The information returned is chosen by the flags parameter; the first word in the returned buffer, - * is the (sub)set of those flags that the API supports. You should always check this value before interpreting - * the buffer. - * - * "Boot Diagnostic" information is intended to help identify the cause of a failed boot, or booting into an unexpected binary. - * This information can be retrieved via picoboot after a watchdog reboot, however it will not survive - * a reset via the RUN pin or POWMAN reset. - * - * There is only one word of diagnostic information. What it records is based on the pp selection above, which - * is itself set as a parameter when rebooting programmatically into a normal boot. - * - * To get diagnostic info, pp must refer to a slot or an "A" partition; image diagnostics are automatically selected on boot - * from OTP or RAM image, or when chain_image() is called.) - * - * The diagnostic word thus contains data for either slot 0 and slot 1, or the "A" partition (and its "B" partition if it has one). The low half word - * of the diagnostic word contains information from slot 0 or partition A; the high half word contains information from slot 1 or partition B. - * - * To get a full picture of a failed boot involving slots and multiple partitions, the device can be rebooted - * multiple times to gather the information. - * - * \param out_buffer buffer to write data to - * \param out_buffer_word_size size of out_buffer, in words - * \param flags flags - */ -static inline int rom_get_sys_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags) { - rom_get_sys_info_fn func = (rom_get_sys_info_fn)rom_func_lookup_inline(ROM_FUNC_GET_SYS_INFO); - return func(out_buffer, out_buffer_word_size, flags); -} - -typedef struct { - union { - struct __packed { - int8_t diagnostic_partition_index; // used BOOT_PARTITION constants - uint8_t boot_type; - int8_t partition; - uint8_t tbyb_and_update_info; - }; - uint32_t boot_word; - }; - uint32_t boot_diagnostic; - uint32_t reboot_params[2]; -} boot_info_t; - -static inline int rom_get_boot_info(boot_info_t *info) { - uint32_t result[5]; - int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); - if (words_returned == (sizeof(result)/sizeof(result[0])) && result[0] == SYS_INFO_BOOT_INFO) { - memcpy(info, &result[1], sizeof(boot_info_t)); - return true; - } else { - return false; - } -} - -static inline int rom_get_last_boot_type_with_chained_flag(void) { - uint32_t result[5]; - int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); - if (words_returned == count_of(result) && result[0] == SYS_INFO_BOOT_INFO) { - // todo use struct - return (int)((result[1] & 0xff00u) >> 8); - } else { - return PICO_ERROR_INVALID_DATA; - } -} - -// BOOT_TYPE_NORMAL 0x0 -// BOOT_TYPE_BOOTSEL 0x2 -// BOOT_TYPE_RAM_IMAGE 0x3 -// BOOT_TYPE_FLASH_UPDATE 0x4 -// BOOT_TYPE_PC_SP 0xd -static inline int rom_get_last_boot_type(void) { - int rc = rom_get_last_boot_type_with_chained_flag(); - if (rc >= 0) rc &= ~BOOT_TYPE_CHAINED_FLAG; - return rc; -} - -/*! \brief Add a runtime partition to the partition table to specify flash permissions - * \ingroup pico_bootrom - * - * Note that a partition is added to the runtime view of the partition table maintained by the bootrom if there is space to do so - * - * Note that these permissions cannot override the permissions for any pre-existing partitions, as permission matches are made on a first partition found basis. - * - * @param start_offset the start_offset into flash in bytes (must be a multiple of 4K) - * @param size the size in byte (must be a multiple of 4K) - * @param permissions the bitwise OR of permissions from PICOBIN_PARTITION_PERMISSION_ constants, e.g. \ref PICOBIN_PARTITION_PERMISSION_S_R_BITS from boot/picobin.h - * @return >= 0 the partition number added if - * PICO_ERROR_BAD_ALIGNMENT if the start_offset or size aren't multiples of 4K. - * PICO_ERROR_INVALID_ARG if the start_offset or size are out of range, or invalid permission bits are set. - */ -int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32_t permissions); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif // !__ASSEMBLER__ -#endif +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_BOOTROM_H +#define _PICO_BOOTROM_H + +#include "pico.h" +#include "pico/bootrom_constants.h" + +/** \file bootrom.h + * \defgroup pico_bootrom pico_bootrom + * \brief Access to functions and data in the bootrom + * + * This header may be included by assembly code + */ + +#ifndef __ASSEMBLER__ +#include +#include "pico/bootrom/lock.h" +#include "pico/flash.h" +// ROM FUNCTION SIGNATURES + +#if PICO_RP2040 +typedef uint32_t (*rom_popcount32_fn)(uint32_t); +typedef uint32_t (*rom_reverse32_fn)(uint32_t); +typedef uint32_t (*rom_clz32_fn)(uint32_t); +typedef uint32_t (*rom_ctz32_fn)(uint32_t); +typedef uint8_t *(*rom_memset_fn)(uint8_t *, uint8_t, uint32_t); +typedef uint32_t *(*rom_memset4_fn)(uint32_t *, uint8_t, uint32_t); +typedef uint32_t *(*rom_memcpy_fn)(uint8_t *, const uint8_t *, uint32_t); +typedef uint32_t *(*rom_memcpy44_fn)(uint32_t *, const uint32_t *, uint32_t); +#endif +typedef void __attribute__((noreturn)) (*rom_reset_usb_boot_fn)(uint32_t, uint32_t); +typedef int (*rom_reboot_fn)(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1); +typedef rom_reset_usb_boot_fn reset_usb_boot_fn; // kept for backwards compatibility +typedef void (*rom_connect_internal_flash_fn)(void); +typedef void (*rom_flash_exit_xip_fn)(void); +typedef void (*rom_flash_range_erase_fn)(uint32_t, size_t, uint32_t, uint8_t); +typedef void (*rom_flash_range_program_fn)(uint32_t, const uint8_t*, size_t); +typedef void (*rom_flash_flush_cache_fn)(void); +typedef void (*rom_flash_enter_cmd_xip_fn)(void); +#if !PICO_RP2040 +typedef void (*rom_bootrom_state_reset_fn)(uint32_t flags); +typedef void (*rom_flash_reset_address_trans_fn)(void); +typedef void (*rom_flash_select_xip_read_mode_fn)(bootrom_xip_mode_t mode, uint8_t clkdiv); +typedef int (*rom_get_sys_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags); +typedef int (*rom_get_partition_table_info_fn)(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags); +typedef int (*rom_explicit_buy_fn)(uint8_t *buffer, uint32_t buffer_size); +typedef void* (*rom_validate_ns_buffer_fn)(const void *addr, uint32_t size, uint32_t write, uint32_t *ok); +/** + * @return BOOTROM_OK if successful + * BOOTROM_ERROR_INVALID_ARG if ns_api_num is out of range + */ +typedef intptr_t (*rom_set_rom_callback_fn)(uint callback_num, bootrom_api_callback_generic_t funcptr); +typedef int (*rom_chain_image_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t window_base, uint32_t window_size); +typedef int (*rom_load_partition_table_fn)(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload); +typedef int (*rom_pick_ab_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base); +typedef int (*rom_get_b_partition_fn)(uint pi_a); +typedef int (*rom_get_uf2_target_partition_fn)(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out); +typedef int (*rom_func_otp_access_fn)(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd); +// Apply the address translation currently specified in QMI_ATRANSx ("rolling window" hardware +// translation). Need to take care using this on the boot path, as the QMI may not yet have been +// set up, but this should be suitable for translating system bus addresses into flash storage +// addresses in user callbacks. Returns all-ones for an invalid address, which is also an invalid +// flash storage address, so invalidity is propagated. +typedef intptr_t (*rom_flash_runtime_to_storage_addr_fn)(uintptr_t flash_runtime_addr); + +// Perform the specified erase/program/read operation, translating addresses according to +// QMI_ATRANSx if necessary, and checking flash permissions based on the resident partition table +// and the specified effective security level. `addr` may be either a flash runtime address or a +// flash storage address, depending on the ASPACE given in `flags`. +// +// NOTE: This function does not validate the buffer for NS access. This must be validated before +// calling if the caller is reachable from a Secure Gateway. +typedef int (*rom_flash_op_fn)(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf); + +#ifndef __riscv +typedef int (*rom_set_ns_api_permission_fn)(uint ns_api_num, bool allowed); +/** + * Note this is not strictly a C function; you must pass the function you are calling in r4 + * @param in_r4 + * `0b0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "well known" function selector; do not use for your own methods + * `0b10xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "unique" function selector intended to be unlikely to clash with others'. + * The lower 30 bits should be chosen at random + * `0b11xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx` - a "private" function selector intended for use by tightly coupled NS and S code + * + * @return whatever the secure call returns + * BOOTROM_ERROR_INVALID_STATE if no secure handler has been set from the secure side + * via rom_set_rom_callback_fn(BOOTROM_API_CALLBACK_secure_call, ...) + */ +typedef int (*rom_func_secure_call)(uintptr_t a0, ...); +#endif + +#ifdef __riscv +typedef struct { + uint32_t *base; + uint32_t size; +} bootrom_stack_t; +// passed in, and out. +typedef int (*rom_set_bootrom_stack_fn)(bootrom_stack_t *stack); +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \brief Return a bootrom lookup code based on two ASCII characters + * \ingroup pico_bootrom + * + * These codes are uses to lookup data or function addresses in the bootrom + * + * \param c1 the first character + * \param c2 the second character + * \return the 'code' to use in rom_func_lookup() or rom_data_lookup() + */ +static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) { + return ROM_TABLE_CODE((uint32_t) c1, (uint32_t) c2); +} + +/*! + * \brief Lookup a bootrom function by its code + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the function, or NULL if the code does not match any bootrom function + */ +void *rom_func_lookup(uint32_t code); + +/*! + * \brief Lookup a bootrom data address by its code + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the data, or NULL if the code does not match any bootrom function + */ +void *rom_data_lookup(uint32_t code); + +/*! + * \brief Helper function to lookup the addresses of multiple bootrom functions + * \ingroup pico_bootrom + * + * This method looks up the 'codes' in the table, and convert each table entry to the looked up + * function pointer, if there is a function for that code in the bootrom. + * + * \param table an IN/OUT array, elements are codes on input, function pointers on success. + * \param count the number of elements in the table + * \return true if all the codes were found, and converted to function pointers, false otherwise + */ +bool rom_funcs_lookup(uint32_t *table, unsigned int count); + +// Bootrom function: rom_table_lookup +// Returns the 32 bit pointer into the ROM if found or NULL otherwise. +#if PICO_RP2040 +typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code); +#else +typedef void *(*rom_table_lookup_fn)(uint32_t code, uint32_t mask); +#endif + +#if PICO_C_COMPILER_IS_GNU && (__GNUC__ >= 12) +// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer +__force_inline static void *rom_hword_as_ptr(uint16_t rom_address) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + return (void *)(uintptr_t)*(uint16_t *)(uintptr_t)rom_address; +#pragma GCC diagnostic pop +} +#else +// Convert a 16 bit pointer stored at the given rom address into a 32 bit pointer +#define rom_hword_as_ptr(rom_address) (void *)(uintptr_t)(*(uint16_t *)(uintptr_t)(rom_address)) +#endif + +#ifdef __riscv +static __force_inline bool rom_size_is_64k(void) { +#ifdef RASPBERRYPI_AMETHYST_FPGA + return *(uint16_t*)0x14 >= 0x8000; +#else + return false; +#endif +} +#endif + +/*! + * \brief Lookup a bootrom function by code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the function, or NULL if the code does not match any bootrom function + */ +#pragma GCC diagnostic push +// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not +#pragma GCC diagnostic ignored "-Warray-bounds" +static __force_inline void *rom_func_lookup_inline(uint32_t code) { +#if PICO_RP2040 + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); + uint16_t *func_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_FUNC_TABLE_OFFSET); + return rom_table_lookup(func_table, code); +#else +#ifdef __riscv + uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; + // on RISC-V the code (a jmp) is actually embedded in the table + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_ENTRY_OFFSET + rom_offset_adjust); + return rom_table_lookup(code, RT_FLAG_FUNC_RISCV); +#else + // on ARM the function pointer is stored in the table, so we dereference it + // via lookup() rather than lookup_entry() + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); + if (pico_processor_state_is_nonsecure()) { + return rom_table_lookup(code, RT_FLAG_FUNC_ARM_NONSEC); + } else { + return rom_table_lookup(code, RT_FLAG_FUNC_ARM_SEC); + } +#endif +#endif +} +#pragma GCC diagnostic pop + +/*! + * \brief Lookup a bootrom data address by its code. This method is forcibly inlined into the caller for FLASH/RAM sensitive code usage + * \ingroup pico_bootrom + * \param code the code + * \return a pointer to the data, or NULL if the code does not match any bootrom data + */ +#pragma GCC diagnostic push +// diagnostic: GCC thinks near-zero value is a null pointer member access, but it's not +#pragma GCC diagnostic ignored "-Warray-bounds" +static __force_inline void *rom_data_lookup_inline(uint32_t code) { +#if PICO_RP2040 + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET); + uint16_t *data_table = (uint16_t *) rom_hword_as_ptr(BOOTROM_DATA_TABLE_OFFSET); + return rom_table_lookup(data_table, code); +#else +#ifdef __riscv + uint32_t rom_offset_adjust = rom_size_is_64k() ? 32 * 1024 : 0; + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET + rom_offset_adjust); +#else + rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn) (uintptr_t)*(uint16_t*)(BOOTROM_TABLE_LOOKUP_OFFSET); +#endif + return rom_table_lookup(code, RT_FLAG_DATA); +#endif +} +#pragma GCC diagnostic pop + +/*! + * \brief Reboot the device into BOOTSEL mode + * \ingroup pico_bootrom + * + * This function reboots the device into the BOOTSEL mode ('usb boot"). + * + * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, + * and to limit the USB interfaces exposed. + * + * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define + * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * the activity LED correctly. + * + * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which + * GPIO pin should be set to output and raised whenever there is mass storage activity + * from the host. + * \param disable_interface_mask value to control exposed interfaces + * - 0 To enable both interfaces (as per a cold boot) + * - 1 To disable the USB Mass Storage Interface + * - 2 To disable the USB PICOBOOT Interface + */ +void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask); +static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activity_gpio_pin_mask, uint32_t disable_interface_mask) { + rom_reset_usb_boot(usb_activity_gpio_pin_mask, disable_interface_mask); +} + +/*! + * \brief Reboot the device into BOOTSEL mode + * \ingroup pico_bootrom + * + * This function reboots the device into the BOOTSEL mode ('usb boot"). + * + * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, + * and to limit the USB interfaces exposed. + * + * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define + * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * the activity LED correctly. + * + * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none + * from the host. + * \param disable_interface_mask value to control exposed interfaces + * - 0 To enable both interfaces (as per a cold boot) + * - 1 To disable the USB Mass Storage Interface + * - 2 To disable the USB PICOBOOT Interface + * \param usb_activity_gpio_pin_active_low Activity GPIO is active low (ignored on RP2040). A bug in the bootrom of RP2350 + * A4 chips means this parameter has no effect on that version of the RP2350. + */ +void __attribute__((noreturn)) rom_reset_usb_boot_extra(int usb_activity_gpio_pin, uint32_t disable_interface_mask, bool usb_activity_gpio_pin_active_low); + +/*! + * \brief Connect the SSI/QMI to the QSPI pads + * \ingroup pico_bootrom + * + * Restore all QSPI pad controls to their default state, and connect the SSI/QMI peripheral to the QSPI pads. + * + * \if rp2350_specific + * On RP2350 if a secondary flash chip select GPIO has been configured via OTP OTP_DATA_FLASH_DEVINFO, or by writing to the runtime + * copy of FLASH_DEVINFO in bootram, then this bank 0 GPIO is also initialised and the QMI peripheral is connected. Otherwise, + * bank 0 IOs are untouched. + * \endif + */ +static inline void rom_connect_internal_flash(void) { + rom_connect_internal_flash_fn func = (rom_connect_internal_flash_fn) rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); + func(); +} + +/*! + * \brief Return the QSPI device from its XIP state to a serial command state + * \ingroup pico_bootrom + * + * \if rp2040_specific + * On RP2040, first set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in Section 2.8.1.2 + * of the datasheet. Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be cleared before returning + * the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This function configures the SSI with a fixed SCK clock divisor of /6. + * \endif + * + * \if rp2350_specific + * On RP2350, Initialise the QMI for serial operations (direct mode), and also initialise a basic XIP mode, where the QMI will perform + * 03h serial read commands at low speed (CLKDIV=12) in response to XIP reads. + * + * Then, issue a sequence to the QSPI device on chip select 0, designed to return it from continuous read mode ("XIP mode") and/or + * QPI mode to a state where it will accept serial commands. This is necessary after system reset to restore the QSPI device to a known + * state, because resetting RP2350 does not reset attached QSPI devices. It is also necessary when user code, having already performed + * some continuous-read-mode or QPI-mode accesses, wishes to return the QSPI device to a state where it will accept the serial erase and + * programming commands issued by the bootrom's flash access functions. + * + * If a GPIO for the secondary chip select is configured via FLASH_DEVINFO, then the XIP exit sequence is also issued to chip select 1. + * + * The QSPI device should be accessible for XIP reads after calling this function; the name flash_exit_xip refers to returning the QSPI + * device from its XIP state to a serial command state. + * \endif + */ +static inline void rom_flash_exit_xip(void) { + rom_flash_exit_xip_fn func = (rom_flash_exit_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); + func(); +} + +/*! + * \brief Erase bytes in flash + * \ingroup pico_bootrom + * + * Erase count bytes, starting at addr (offset from start of flash). Optionally, pass a block erase command e.g. D8h block erase, + * and the size of the block erased by this command - this function will use the larger block erase where possible, for much higher + * erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of 4096 bytes. + * + * This is a low-level flash API, and no validation of the arguments is performed. + * + * \if rp2350_specific + * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, and can transparently + * apply a runtime-to-storage address translation. + * + * The QSPI device must be in a serial command state before calling this API, which can be achieved by calling rom_connect_internal_flash() + * followed by rom_flash_exit_xip(). After the erase, the flash cache should be flushed via rom_flash_flush_cache() to ensure the modified + * flash data is visible to cached XIP accesses. + * + * Finally, the original XIP mode should be restored by copying the saved XIP setup function from bootram into SRAM, and executing it: + * the bootrom provides a default function which restores the flash mode/clkdiv discovered during flash scanning, and user programs can + * override this with their own XIP setup function. + * + * For the duration of the erase operation, QMI is in direct mode and attempting to access XIP from DMA, the debugger or the other core will + * return a bus fault. XIP becomes accessible again once the function returns. + * \endif + * + * \param addr the offset from start of flash to be erased + * \param count number of bytes to erase + * \param block_size optional size of block erased by block_cmd + * \param block_cmd optional block erase command e.g. D8h block erase + */ +static inline void rom_flash_range_erase(uint32_t addr, size_t count, uint32_t block_size, uint8_t block_cmd) { + rom_flash_range_erase_fn func = (rom_flash_range_erase_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_ERASE); + func(addr, count, block_size, block_cmd); +} + +/*! + * \brief Program bytes in flash + * \ingroup pico_bootrom + * + * Program data to a range of flash addresses starting at addr (offset from the start of flash) and count bytes in size. addr must be + * aligned to a 256-byte boundary, and count must be a multiple of 256. + * + * This is a low-level flash API, and no validation of the arguments is performed. + * + * \if rp2350_specific + * See rom_flash_op on RP2350 for a higher-level API which checks alignment, flash bounds and partition permissions, + * and can transparently apply a runtime-to-storage address translation. + * + * The QSPI device must be in a serial command state before calling this API - see notes on rom_flash_range_erase + * \endif + * + * \param addr the offset from start of flash to be erased + * \param data buffer containing the data to be written + * \param count number of bytes to erase + */ +static inline void rom_flash_range_program(uint32_t addr, const uint8_t *data, size_t count) { + rom_flash_range_program_fn func = (rom_flash_range_program_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); + func(addr, data, count); +} + +/*! + * \brief Flush the XIP cache + * \ingroup pico_bootrom + * + * \if rp2040_specific + * Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can drive the flash chip select as normal. + * \endif + * + * \if rp2350_specific + * Flush the entire XIP cache, by issuing an invalidate by set/way maintenance operation to every cache line. This ensures that flash + * program/erase operations are visible to subsequent cached XIP reads. + * + * Note that this unpins pinned cache lines, which may interfere with cache-as-SRAM use of the XIP cache. + * + * No other operations are performed. + * \endif + */ +static inline void rom_flash_flush_cache(void) { + rom_flash_flush_cache_fn func = (rom_flash_flush_cache_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); + func(); +} + +/*! + * \brief Configure the SSI/QMI with a standard command + * \ingroup pico_bootrom + * + * Configure the SSI/QMI to generate a standard 03h serial read command, with 24 address bits, upon each XIP access. This is a slow XIP + * configuration, but is widely supported. CLKDIV is set to 12 on RP2350. The debugger may call this function to ensure that flash is + * readable following a program/erase operation. + * + * Note that the same setup is performed by flash_exit_xip(), and the RP2350 flash program/erase functions do not leave XIP in an + * inaccessible state, so calls to this function are largely redundant on RP2350. It is provided on RP2350 for compatibility with RP2040. + */ +static inline void rom_flash_enter_cmd_xip(void) { + rom_flash_enter_cmd_xip_fn func = (rom_flash_enter_cmd_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_ENTER_CMD_XIP); + func(); +} + +#if !PICO_RP2040 || PICO_COMBINED_DOCS +#ifdef __riscv +/*! + * \brief Give the bootrom a new stack + * \ingroup pico_bootrom + * + * Most bootrom functions are written just once, in Arm code, to save space. As a result these functions are emulated when + * running under the RISC-V architecture. This is largely transparent to the user, however the stack used by the Arm emulation + * is separate from the calling user's stack, and is stored in boot RAM but is of quite limited size. When using certain of the more + * complex APIs or if nesting bootrom calls from within IRQs, you may need to provide a large stack. + * + * This method allows the caller to specify a region of RAM to use as the stack for the current core by passing a pointer to two values: the word aligned base address, + * and the size in bytes (multiple of 4). + * + * The method fills in the previous base/size values into the passed array before returning. + * + * \param stack bootrom_stack_t struct containing base and size + */ +static inline int rom_set_bootrom_stack(bootrom_stack_t *stack) { + rom_set_bootrom_stack_fn func = (rom_set_bootrom_stack_fn) rom_func_lookup_inline(ROM_FUNC_SET_BOOTROM_STACK); + return func(stack); +} +#endif + +/*! + * \brief Reboot using the watchdog + * \ingroup pico_bootrom + * + * Resets the chip and uses the watchdog facility to restart. + * + * The delay_ms is the millisecond delay before the reboot occurs. Note: by default this method is asynchronous + * (unless NO_RETURN_ON_SUCCESS is set - see below), so the method will return and the reboot will happen this many milliseconds later. + * + * The flags field contains one of the following values: + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL - reboot into the normal boot path. + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL - reboot into BOOTSEL mode. + * p0 - a set of flags: + * 0x01 : DISABLE_MSD_INTERFACE - Disable the BOOTSEL USB drive (see <>) + * 0x02 : DISABLE_PICOBOOT_INTERFACE - Disable the {picoboot} interface (see <>). + * 0x10 : GPIO_PIN_ACTIVE_LOW - The GPIO specified in p1 is active low (GPIO_PIN_SPECIFIED must also be set). + * 0x20 : GPIO_PIN_SPECIFIED - Enable the activity indicator on the GPIO specified in p1. + * p1 - the GPIO number to use as an activity indicator (enabled by GPIO_PIN_SPECIFIED flag in p0). + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE - reboot into an image in RAM. The region of RAM or XIP RAM is searched for an image to run. This is the type + * of reboot used when a RAM UF2 is dragged onto the BOOTSEL USB drive. + * p0 - the region start address (word-aligned). + * p1 - the region size (word-aligned). + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_FLASH_UPDATE - variant of \ref REBOOT2_FLAG_REBOOT_TYPE_NORMAL to use when flash has been updated. This is the type + * of reboot used after dragging a flash UF2 onto the BOOTSEL USB drive. + * p0 - the address of the start of the region of flash that was updated. If this address matches the start address of a partition or slot, then that + * partition or slot is treated preferentially during boot (when there is a choice). This type of boot facilitates TBYB and version downgrades. + * + * \ref REBOOT2_FLAG_REBOOT_TYPE_PC_SP - reboot to a specific PC and SP. Note: this is not allowed in the ARM-NS variant. + * p0 - the initial program counter (PC) to start executing at. This must have the lowest bit set for Arm and clear for RISC-V + * p1 - the initial stack pointer (SP). + * + * All of the above, can have optional flags ORed in: + * + * \ref REBOOT2_FLAG_REBOOT_TO_ARM - switch both cores to the Arm architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the Arm architecture is not supported. + * \ref REBOOT2_FLAG_REBOOT_TO_RISCV - switch both cores to the RISC-V architecture (rather than leaving them as is). The call will fail with BOOTROM_ERROR_INVALID_STATE if the RISC-V architecture is not supported. + * \ref REBOOT2_FLAG_NO_RETURN_ON_SUCCESS - the watchdog h/w is asynchronous. Setting this bit forces this method not to return if the reboot is successfully initiated. + * + * \param flags the reboot flags, as detailed above + * \param delay_ms millisecond delay before the reboot occurs + * \param p0 parameter 0, depends on flags + * \param p1 parameter 1, depends on flags + */ +static inline int rom_reboot(uint32_t flags, uint32_t delay_ms, uint32_t p0, uint32_t p1) { + rom_reboot_fn func = (rom_reboot_fn) rom_func_lookup_inline(ROM_FUNC_REBOOT); + return func(flags, delay_ms, p0, p1); +} + +bool rom_get_boot_random(uint32_t out[4]); + +/*! + * \brief Reset bootrom state + * \ingroup pico_bootrom + * + * Resets internal bootrom state, based on the following flags: + * + * STATE_RESET_CURRENT_CORE - Resets any internal bootrom state for the current core into a clean state. + * This method should be called prior to calling any other bootrom APIs on the current core, + * and is called automatically by the bootrom during normal boot of core 0 and launch of code on core 1. + * + * STATE_RESET_OTHER_CORE - Resets any internal bootrom state for the other core into a clean state. This is generally called by + * a debugger when resetting the state of one core via code running on the other. + * + * STATE_RESET_GLOBAL_STATE - Resets all non core-specific state, including: + * Disables access to bootrom APIs from ARM-NS + * Unlocks all BOOT spinlocks + * Clears any secure code callbacks + * + * Note: the sdk calls this method on runtime initialisation to put the bootrom into a known state. This + * allows the program to function correctly if it is entered (e.g. from a debugger) without taking the usual boot path (which + * resets the state appropriately itself). + * + * \param flags flags, as detailed above + */ +static inline void rom_bootrom_state_reset(uint32_t flags) { + rom_bootrom_state_reset_fn func = (rom_bootrom_state_reset_fn) rom_func_lookup_inline(ROM_FUNC_BOOTROM_STATE_RESET); + return func(flags); +} + +/*! + * \brief Reset address translation + * \ingroup pico_bootrom + * + * Restore the QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7, to their reset state. This makes the + * runtime-to-storage address map an identity map, i.e. the mapped and unmapped address are equal, and the entire space is + * fully mapped. + */ +static inline void rom_flash_reset_address_trans(void) { + rom_flash_reset_address_trans_fn func = (rom_flash_reset_address_trans_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RESET_ADDRESS_TRANS); + func(); +} + +/*! + * \brief Configure QMI in a XIP read mode + * \ingroup pico_bootrom + * + * Configure QMI for one of a small menu of XIP read modes supported by the bootrom. This mode is configured for both memory + * windows (both chip selects), and the clock divisor is also applied to direct mode. + * + * \param mode bootrom_xip_mode_t mode to use + * \param clkdiv clock divider + */ +static inline void rom_flash_select_xip_read_mode(bootrom_xip_mode_t mode, uint8_t clkdiv) { + rom_flash_select_xip_read_mode_fn func = (rom_flash_select_xip_read_mode_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_SELECT_XIP_READ_MODE); + func(mode, clkdiv); +} + +typedef struct { + cflash_flags_t flags; + uintptr_t addr; + uint32_t size_bytes; + uint8_t *buf; + int *res; +} rom_helper_flash_op_params_t; + +static inline void rom_helper_flash_op(void *param) { + const rom_helper_flash_op_params_t *op = (const rom_helper_flash_op_params_t *)param; + rom_flash_op_fn func = (rom_flash_op_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_OP); + *(op->res) = func(op->flags, op->addr, op->size_bytes, op->buf); +} + +/*! + * \brief Perform a flash read, erase, or program operation + * \ingroup pico_bootrom + * + * The flash operation is bounds-checked against the known flash devices specified by the runtime value of FLASH_DEVINFO, + * stored in bootram. This is initialised by the bootrom to the OTP value OTP_DATA_FLASH_DEVINFO, if + * OTP_DATA_BOOT_FLAGS0_FLASH_DEVINFO_ENABLE is set; otherwise it is initialised to 16 MiB for chip select 0 and 0 bytes + * for chip select 1. FLASH_DEVINFO can be updated at runtime by writing to its location in bootram, the pointer to which + * can be looked up in the ROM table. + * + * If a resident partition table is in effect, then the flash operation is also checked against the partition permissions. + * The Secure version of this function can specify the caller's effective security level (Secure, Non-secure, bootloader) + * using the CFLASH_SECLEVEL_BITS bitfield of the flags argument, whereas the Non-secure function is always checked against + * the Non-secure permissions for the partition. Flash operations which span two partitions are not allowed, and will fail + * address validation. + * + * If OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED is set, erase operations will use a D8h 64 kiB block erase command where + * possible (without erasing outside the specified region), for faster erase time. Otherwise, only 20h 4 kiB sector erase + * commands are used. + * + * Optionally, this API can translate addr from flash runtime addresses to flash storage addresses, according to the + * translation currently configured by QMI address translation registers, QMI_ATRANS0 through QMI_ATRANS7. For example, an + * image stored at a +2 MiB offset in flash (but mapped at XIP address 0 at runtime), writing to an offset of +1 MiB into + * the image, will write to a physical flash storage address of 3 MiB. Translation is enabled by setting the + * CFLASH_ASPACE_BITS bitfield in the flags argument. + * + * When translation is enabled, flash operations which cross address holes in the XIP runtime address space (created by + * non-maximum ATRANSx_SIZE) will return an error response. This check may tear: the transfer may be partially performed + * before encountering an address hole and ultimately returning failure. + * + * When translation is enabled, flash operations are permitted to cross chip select boundaries, provided this does not + * span an ATRANS address hole. When translation is disabled, the entire operation must target a single flash chip select + * (as determined by bits 24 and upward of the address), else address validation will fail. + * + * \param flags controls the security level, address space, and flash operation + * \param addr the address of the first flash byte to be accessed, ranging from XIP_BASE to XIP_BASE + 0x1ffffff + * \param size_bytes size of buf, in bytes + * \param buf contains data to be written to flash, for program operations, and data read back from flash, for read operations + */ +static inline int rom_flash_op(cflash_flags_t flags, uintptr_t addr, uint32_t size_bytes, uint8_t *buf) { + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_FLASH_OP)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = 0; + rom_helper_flash_op_params_t params = { + .flags = flags, + .addr = addr, + .size_bytes = size_bytes, + .buf = buf, + .res = &rc + }; + int flash_rc = flash_safe_execute(rom_helper_flash_op, ¶ms, UINT32_MAX); + bootrom_release_lock(BOOTROM_LOCK_FLASH_OP); + if (flash_rc != PICO_OK) { + return flash_rc; + } else { + return rc; + } +} + +/*! + * \brief Writes data from a buffer into OTP, or reads data from OTP into a buffer + * \ingroup pico_bootrom + * + * The buffer must be aligned to 2 bytes or 4 bytes according to the IS_ECC flag. + * + * This method will read and write rows until the first row it encounters that fails a key or permission check at which + * it will return BOOTROM_ERROR_NOT_PERMITTED. + * + * Writing will also stop at the first row where an attempt is made to set an OTP bit from a 1 to a 0, and + * BOOTROM_ERROR_UNSUPPORTED_MODIFICATION will be returned. + * + * If all rows are read/written successfully, then BOOTROM_OK will be returned. + * + * \param buf buffer to read to/write from + * \param buf_len size of buf + * \param cmd OTP command to execute + * - 0x0000ffff - ROW_NUMBER: 16 low bits are row number (0-4095) + * - 0x00010000 - IS_WRITE: if set, do a write (not a read) + * - 0x00020000 - IS_ECC: if this bit is set, each value in the buffer is 2 bytes and ECC is used when read/writing from 24 + * bit value in OTP. If this bit is not set, each value in the buffer is 4 bytes, the low 24-bits of which are written + * to or read from OTP. + + */ +static inline int rom_func_otp_access(uint8_t *buf, uint32_t buf_len, otp_cmd_t cmd) { + rom_func_otp_access_fn func = (rom_func_otp_access_fn) rom_func_lookup_inline(ROM_FUNC_OTP_ACCESS); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_OTP)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(buf, buf_len, cmd); + bootrom_release_lock(BOOTROM_LOCK_OTP); + return rc; +} + +/*! + * \brief Fills a buffer with information from the partition table + * \ingroup pico_bootrom + * + * Fills a buffer with information from the partition table. Note that this API is also used to return information over the + * picoboot interface. + * + * On success, the buffer is filled, and the number of words filled in the buffer is returned. If the partition table + * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return BOOTROM_ERROR_NO_DATA, and you + * should load the partition table via load_partition_table() first. + * + * Note that not all data from the partition table is kept resident in memory by the bootrom due to size constraints. + * To protect against changes being made in flash after the bootrom has loaded the resident portion, the bootrom keeps + * a hash of the partition table as of the time it loaded it. If the hash has changed by the time this method is called, + * then it will return BOOTROM_ERROR_INVALID_STATE. + * + * The information returned is chosen by the partition_and_flags parameter; the first word in the returned buffer, + * is the (sub)set of those flags that the API supports. You should always check this value before interpreting + * the buffer. + * + * Following the first word, returns words of data for each present flag in order. With the exception of PT_INFO, + * all the flags select "per partition" information, so each field is returned in flag order for one partition after + * the next. The special SINGLE_PARTITION flag indicates that data for only a single partition is required. + * + * \param out_buffer buffer to write data to + * \param out_buffer_word_size size of out_buffer, in words + * \param partition_and_flags partition number and flags + */ +static inline int rom_get_partition_table_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t partition_and_flags) { + rom_get_partition_table_info_fn func = (rom_get_partition_table_info_fn) rom_func_lookup_inline(ROM_FUNC_GET_PARTITION_TABLE_INFO); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(out_buffer, out_buffer_word_size, partition_and_flags); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +// todo SECURE only +/*! + * \brief Loads the current partition table from flash, if present + * \ingroup pico_bootrom + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size + * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * If force_reload is false, then this method will return BOOTROM_OK immediately if the bootrom is loaded, otherwise it will + * reload the partition table if it has been loaded already, allowing for the partition table to be updated in a running program. + * + * \param workarea_base base address of work area + * \param workarea_size size of work area + * \param force_reload force reloading of the partition table + */ +static inline int rom_load_partition_table(uint8_t *workarea_base, uint32_t workarea_size, bool force_reload) { + rom_load_partition_table_fn func = (rom_load_partition_table_fn) rom_func_lookup_inline(ROM_FUNC_LOAD_PARTITION_TABLE); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(workarea_base, workarea_size, force_reload); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +// todo SECURE only +/*! + * \brief Pick a partition from an A/B pair + * \ingroup pico_bootrom + * + * Determines which of the partitions has the "better" IMAGE_DEF. In the case of executable images, this is the one that would be booted + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should bye word aligned, and of sufficient size + * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * The passed partition number can be any valid partition number other than the "B" partition of an A/B pair. + * + * This method returns a negative error code, or the partition number of the picked partition if (i.e. partition_a_num or the + * number of its "B" partition if any). + * + * 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 \ref rom_pick_ab_partition_during_update() 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 + * \param flash_update_boot_window_base the flash update base, to pick that partition instead of the normally "better" partition + * \return >= 0 the chosen partition number out of the A/B pair + */ +static inline int rom_pick_ab_partition(uint8_t *workarea_base, uint32_t workarea_size, uint partition_a_num, uint32_t flash_update_boot_window_base) { + rom_pick_ab_partition_fn func = (rom_pick_ab_partition_fn) rom_func_lookup_inline(ROM_FUNC_PICK_AB_PARTITION); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(workarea_base, workarea_size, partition_a_num, flash_update_boot_window_base); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +/*! \brief Pick A/B partition without disturbing any in progress Flash Update boot or TBYB boot + * \ingroup pico_bootrom + * + * This will perform the same function as \ref 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 \ref rom_pick_ab_partition(). + * + * This should be used instead of \ref rom_pick_ab_partition() when performing a Flash Update Boot before calling \ref rom_explicit_buy(), and can still be + * used without issue when a Flash Update Boot is not in progress. + * + * This function is necessary because if an `explicit_buy` is pending then calling `pick_ab_partition` would 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 two partitions with one `explicit_buy` call). + * + * This function 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 by \ref rom_pick_ab_partition() + * 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_partition_during_update(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num); + +/*! + * \brief Get B partition + * \ingroup pico_bootrom + * + * Returns the index of the B partition of partition A if a partition table is present and loaded, and there is a partition A with a B partition; + * otherwise returns BOOTROM_ERROR_NOT_FOUND. + * + * \param pi_a the A partition number + */ +static inline int rom_get_b_partition(uint pi_a) { + rom_get_b_partition_fn func = (rom_get_b_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_B_PARTITION); + return func(pi_a); +} + +// todo SECURE only +/*! + * \brief Get UF2 Target Partition + * \ingroup pico_bootrom + * + * This method performs the same operation to decide on a target partition for a UF2 family ID as when a UF2 is dragged onto the USB + * drive in BOOTSEL mode. + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should byte word-aligned and of sufficient size + * or `BOOTROM_ERROR_INSUFFICIENT_RESOURCES` will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * If the partition table + * has not been loaded (e.g. from a watchdog or RAM boot), then this method will return `BOOTROM_ERROR_PRECONDITION_NOT_MET`, and you + * should load the partition table via <> first. + * + * \param workarea_base base address of work area + * \param workarea_size size of work area + * \param family_id the family ID to place + * \param partition_out pointer to the resident_partition_t to fill with the partition data + */ +static inline int rom_get_uf2_target_partition(uint8_t *workarea_base, uint32_t workarea_size, uint32_t family_id, resident_partition_t *partition_out) { + rom_get_uf2_target_partition_fn func = (rom_get_uf2_target_partition_fn) rom_func_lookup_inline(ROM_FUNC_GET_UF2_TARGET_PARTITION); + if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256)) + return BOOTROM_ERROR_LOCK_REQUIRED; + int rc = func(workarea_base, workarea_size, family_id, partition_out); + bootrom_release_lock(BOOTROM_LOCK_SHA_256); + return rc; +} + +/*! + * \brief Translate runtime to storage address + * \ingroup pico_bootrom + * + * Applies the address translation currently configured by QMI address translation registers. + * + * Translating an address outside of the XIP runtime address window, or beyond the bounds of an ATRANSx_SIZE field, returns BOOTROM_ERROR_INVALID_ADDRESS, + * which is not a valid flash storage address. Otherwise, return the storage address which QMI would access when presented with the runtime address addr. + * This is effectively a virtual-to-physical address translation for QMI. + * + * \param flash_runtime_addr the address to translate + */ +static inline intptr_t rom_flash_runtime_to_storage_addr(uintptr_t flash_runtime_addr) { + rom_flash_runtime_to_storage_addr_fn func = (rom_flash_runtime_to_storage_addr_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RUNTIME_TO_STORAGE_ADDR); + return func(flash_runtime_addr); +} + +// todo SECURE only +/*! + * \brief Chain into a launchable image + * \ingroup pico_bootrom + * + * Searches a memory region for a launchable image, and executes it if possible. + * + * The region_base and region_size specify a word-aligned, word-multiple-sized area of RAM, XIP RAM or flash to search. + * The first 4 kiB of the region must contain the start of a Block Loop with an IMAGE_DEF. If the new image is launched, + * the call does not return otherwise an error is returned. + * + * The region_base is signed, as a negative value can be passed, which indicates that the (negated back to positive value) + * is both the region_base and the base of the "flash update" region. + * + * This method potentially requires similar complexity to the boot path in terms of picking amongst versions, checking signatures etc. + * As a result it requires a user provided memory buffer as a work area. The work area should be word aligned, and of sufficient size + * or BOOTROM_ERROR_INSUFFICIENT_RESOURCES will be returned. The work area size currently required is 3264, so 3.25K is a good choice. + * + * NOTE: This method is primarily expected to be used when implementing bootloaders. + * + * NOTE: When chaining into an image, the OTP_DATA_BOOT_FLAGS0_ROLLBACK_REQUIRED flag will not be set, to prevent invalidating a bootloader + * without a rollback version by booting a binary which has one. + * + * \param workarea_base base address of work area + * \param workarea_size size of work area + * \param region_base base address of image + * \param region_size size of window containing image + */ +static inline int rom_chain_image(uint8_t *workarea_base, uint32_t workarea_size, uint32_t region_base, uint32_t region_size) { + rom_chain_image_fn func = (rom_chain_image_fn) rom_func_lookup_inline(ROM_FUNC_CHAIN_IMAGE); + bootrom_release_lock(BOOTROM_LOCK_ENABLE); + uint32_t interrupt_flags = save_and_disable_interrupts(); + int rc = func(workarea_base, workarea_size, region_base, region_size); + restore_interrupts_from_disabled(interrupt_flags); + bootrom_acquire_lock_blocking(BOOTROM_LOCK_ENABLE); + return rc; +} + +typedef struct { + uint8_t *buffer; + uint32_t buffer_size; + int *res; +} rom_helper_explicit_buy_params_t; + +static inline void rom_helper_explicit_buy(void *param) { + const rom_helper_explicit_buy_params_t *op = (const rom_helper_explicit_buy_params_t *)param; + rom_explicit_buy_fn func = (rom_explicit_buy_fn) rom_func_lookup_inline(ROM_FUNC_EXPLICIT_BUY); + *(op->res) = func(op->buffer, op->buffer_size); +} + +// todo SECURE only +/*! + * \brief Buy an image + * \ingroup pico_bootrom + * + * Perform an "explicit" buy of an executable launched via an IMAGE_DEF which was "explicit buy" flagged. A "flash update" + * boot of such an image is a way to have the image execute once, but only become the "current" image if it calls + * back into the bootrom via this call. + * + * This call may perform the following: + * + * - Erase and rewrite the part of flash containing the "explicit buy" flag in order to clear said flag. + * - Erase the first sector of the other partition in an A/B partition scenario, if this new IMAGE_DEF is a version downgrade + * (so this image will boot again when not doing a "flash update" boot) + * - Update the rollback version in OTP if the chip is secure, and a rollback version is present in the image. + * + * NOTE: The device may reboot while updating the rollback version, if multiple rollback rows need to be written - this occurs + * when the version crosses a multiple of 24 (for example upgrading from version 23 to 25 requires a reboot, but 23 to 24 or 24 to 25 doesn't). + * The application should therefore be prepared to reboot when calling this function, if rollback versions are in use. + * + * Note that the first of the above requires 4 kiB of scratch space, so you should pass a word aligned buffer of at least 4 kiB to this method, + * or it will return BOOTROM_ERROR_INSUFFICIENT_RESOURCES if the "explicit buy" flag needs to be cleared. + * + * \param buffer base address of scratch space + * \param buffer_size size of scratch space + */ +static inline int rom_explicit_buy(uint8_t *buffer, uint32_t buffer_size) { + int rc = 0; + rom_helper_explicit_buy_params_t params = { + .buffer = buffer, + .buffer_size = buffer_size, + .res = &rc + }; + int flash_rc = flash_safe_execute(rom_helper_explicit_buy, ¶ms, UINT32_MAX); + if (flash_rc != PICO_OK) { + return flash_rc; + } else { + return rc; + } +} + +#ifndef __riscv +/*! + * \brief Set NS API Permission + * \ingroup pico_bootrom + * + * Allow or disallow the specific NS API (note all NS APIs default to disabled). + * + * ns_api_num configures ARM-NS access to the given API. When an NS API is disabled, + * calling it will return BOOTROM_ERROR_NOT_PERMITTED. + * + * NOTE: All permissions default to disallowed after a reset. + * + * \param ns_api_num ns api number + * \param allowed permission + */ +static inline int rom_set_ns_api_permission(uint ns_api_num, bool allowed) { + rom_set_ns_api_permission_fn func = (rom_set_ns_api_permission_fn) rom_func_lookup_inline(ROM_FUNC_SET_NS_API_PERMISSION); + return func(ns_api_num, allowed); +} +#endif + +// todo SECURE only +/*! + * \brief Validate NS Buffer + * \ingroup pico_bootrom + * + * Utility method that can be used by secure ARM code to validate a buffer passed to it from Non-secure code. + * + * Both the write parameter and the (out) result parameter ok are RCP booleans, so 0xa500a500 for true, and 0x00c300c3 + * for false. This enables hardening of this function, and indeed the write parameter must be one of these values or the RCP + * will hang the system. + * + * For success, the entire buffer must fit in range XIP_BASE -> SRAM_END, and must be accessible by the Non-secure + * caller according to SAU + NS MPU (privileged or not based on current processor IPSR and NS CONTROL flag). Buffers + * in USB RAM are also allowed if access is granted to NS via ACCESSCTRL. + * + * \param addr buffer address + * \param size buffer size + * \param write rcp boolean, true if writeable + * \param ok rcp boolean result + */ +static inline void* rom_validate_ns_buffer(const void *addr, uint32_t size, uint32_t write, uint32_t *ok) { + rom_validate_ns_buffer_fn func = (rom_validate_ns_buffer_fn) rom_func_lookup_inline(ROM_FUNC_VALIDATE_NS_BUFFER); + return func(addr, size, write, ok); +} + +/*! + * \brief Set ROM callback function + * \ingroup pico_bootrom + * + * The only currently supported callback_number is 0 which sets the callback used for the secure_call API. + * + * A callback pointer of 0 deletes the callback function, a positive callback pointer (all valid function pointers are on RP2350) + * sets the callback function, but a negative callback pointer can be passed to get the old value without setting a new value. + * + * If successful, returns >=0 (the existing value of the function pointer on entry to the function). + * + * \param callback_num the callback number to set - only 0 is supported on RP2350 + * \param funcptr pointer to the callback function + */ +static inline intptr_t rom_set_rom_callback(uint callback_num, bootrom_api_callback_generic_t funcptr) { + rom_set_rom_callback_fn func = (rom_set_rom_callback_fn) rom_func_lookup_inline(ROM_FUNC_SET_ROM_CALLBACK); + return func(callback_num, funcptr); +} + +/*! + * \brief Get system information + * \ingroup pico_bootrom + * + * Fills a buffer with various system information. Note that this API is also used to return information over the picoboot interface. + * + * On success, the buffer is filled, and the number of words filled in the buffer is returned. + * + * The information returned is chosen by the flags parameter; the first word in the returned buffer, + * is the (sub)set of those flags that the API supports. You should always check this value before interpreting + * the buffer. + * + * "Boot Diagnostic" information is intended to help identify the cause of a failed boot, or booting into an unexpected binary. + * This information can be retrieved via picoboot after a watchdog reboot, however it will not survive + * a reset via the RUN pin or POWMAN reset. + * + * There is only one word of diagnostic information. What it records is based on the pp selection above, which + * is itself set as a parameter when rebooting programmatically into a normal boot. + * + * To get diagnostic info, pp must refer to a slot or an "A" partition; image diagnostics are automatically selected on boot + * from OTP or RAM image, or when chain_image() is called.) + * + * The diagnostic word thus contains data for either slot 0 and slot 1, or the "A" partition (and its "B" partition if it has one). The low half word + * of the diagnostic word contains information from slot 0 or partition A; the high half word contains information from slot 1 or partition B. + * + * To get a full picture of a failed boot involving slots and multiple partitions, the device can be rebooted + * multiple times to gather the information. + * + * \param out_buffer buffer to write data to + * \param out_buffer_word_size size of out_buffer, in words + * \param flags flags + */ +static inline int rom_get_sys_info(uint32_t *out_buffer, uint32_t out_buffer_word_size, uint32_t flags) { + rom_get_sys_info_fn func = (rom_get_sys_info_fn)rom_func_lookup_inline(ROM_FUNC_GET_SYS_INFO); + return func(out_buffer, out_buffer_word_size, flags); +} + +typedef struct { + union { + struct __packed { + int8_t diagnostic_partition_index; // used BOOT_PARTITION constants + uint8_t boot_type; + int8_t partition; + uint8_t tbyb_and_update_info; + }; + uint32_t boot_word; + }; + uint32_t boot_diagnostic; + uint32_t reboot_params[2]; +} boot_info_t; + +static inline int rom_get_boot_info(boot_info_t *info) { + uint32_t result[5]; + int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); + if (words_returned == (sizeof(result)/sizeof(result[0])) && result[0] == SYS_INFO_BOOT_INFO) { + memcpy(info, &result[1], sizeof(boot_info_t)); + return true; + } else { + return false; + } +} + +static inline int rom_get_last_boot_type_with_chained_flag(void) { + uint32_t result[5]; + int words_returned = rom_get_sys_info(result, 5, SYS_INFO_BOOT_INFO); + if (words_returned == count_of(result) && result[0] == SYS_INFO_BOOT_INFO) { + // todo use struct + return (int)((result[1] & 0xff00u) >> 8); + } else { + return PICO_ERROR_INVALID_DATA; + } +} + +// BOOT_TYPE_NORMAL 0x0 +// BOOT_TYPE_BOOTSEL 0x2 +// BOOT_TYPE_RAM_IMAGE 0x3 +// BOOT_TYPE_FLASH_UPDATE 0x4 +// BOOT_TYPE_PC_SP 0xd +static inline int rom_get_last_boot_type(void) { + int rc = rom_get_last_boot_type_with_chained_flag(); + if (rc >= 0) rc &= ~BOOT_TYPE_CHAINED_FLAG; + return rc; +} + +/*! \brief Add a runtime partition to the partition table to specify flash permissions + * \ingroup pico_bootrom + * + * Note that a partition is added to the runtime view of the partition table maintained by the bootrom if there is space to do so + * + * Note that these permissions cannot override the permissions for any pre-existing partitions, as permission matches are made on a first partition found basis. + * + * @param start_offset the start_offset into flash in bytes (must be a multiple of 4K) + * @param size the size in byte (must be a multiple of 4K) + * @param permissions the bitwise OR of permissions from PICOBIN_PARTITION_PERMISSION_ constants, e.g. \ref PICOBIN_PARTITION_PERMISSION_S_R_BITS from boot/picobin.h + * @return >= 0 the partition number added if + * PICO_ERROR_BAD_ALIGNMENT if the start_offset or size aren't multiples of 4K. + * PICO_ERROR_INVALID_ARG if the start_offset or size are out of range, or invalid permission bits are set. + */ +int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32_t permissions); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif // !__ASSEMBLER__ +#endif From 9a959339519e2bdf022c0c638da5d86eb5ace6f3 Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Mon, 1 Jun 2026 13:07:22 -0500 Subject: [PATCH 08/10] Apply suggestions from code review Co-authored-by: will-v-pi <108662275+will-v-pi@users.noreply.github.com> Co-authored-by: Andrew Scheller --- src/rp2_common/pico_bootrom/include/pico/bootrom.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index 9498cec3c0..c5cc95e602 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -250,9 +250,11 @@ static __force_inline void *rom_data_lookup_inline(uint32_t code) { * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, * and to limit the USB interfaces exposed. * - * \note On RP2350A-A2 chips, there is a bootrom bug that prevents the activity LED working under Arm. You can define + * \if rp2350_specific + * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display * the activity LED correctly. + * \endif * * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which * GPIO pin should be set to output and raised whenever there is mass storage activity @@ -276,12 +278,13 @@ static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activit * Facilities are provided to enable an "activity light" via GPIO attached LED for the USB Mass Storage Device, * and to limit the USB interfaces exposed. * + * \if rp2350_specific * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display * the activity LED correctly. + * \endif * * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none - * from the host. * \param disable_interface_mask value to control exposed interfaces * - 0 To enable both interfaces (as per a cold boot) * - 1 To disable the USB Mass Storage Interface From f85d2df3d4dc2b9b7eda2487e528171af8b44177 Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Mon, 1 Jun 2026 13:11:41 -0500 Subject: [PATCH 09/10] Rename PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG->PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG and default it to 1 on RP2350 --- src/rp2_common/pico_bootrom/bootrom.c | 4 +--- src/rp2_common/pico_bootrom/include/pico/bootrom.h | 11 +++++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rp2_common/pico_bootrom/bootrom.c b/src/rp2_common/pico_bootrom/bootrom.c index fae36a6b8d..c9e59bfdad 100644 --- a/src/rp2_common/pico_bootrom/bootrom.c +++ b/src/rp2_common/pico_bootrom/bootrom.c @@ -11,8 +11,6 @@ #include "hardware/rcp.h" #endif -// PICO_CONFIG: PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG, Workaround RP2350A-A2 (QFN60) bug not displaying USB boot activity LED under Arm by making rom_reset_usb_boot functions reboot to RISC-V when the activity LED is specified, type=bool, default=0, group=pico_bootrom - /// \tag::table_lookup[] void *rom_func_lookup(uint32_t code) { @@ -35,7 +33,7 @@ bool rom_funcs_lookup(uint32_t *table, unsigned int count) { // The activity LED on RP2350 A2 QFN60 chips doesn't work in Arm mode, so boot into RISC-V if the user // really, really wants the activity LED -#if PICO_RP2350_A2_SUPPORTED && PICO_RP2350A && !PICO_RISCV && PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG +#if PICO_RP2350_A2_SUPPORTED && PICO_RP2350A && !PICO_RISCV && PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG #define rom_reboot_workaround(flags, delay_ms, p0, p1) ({ \ if (((p0) & BOOTSEL_FLAG_GPIO_PIN_SPECIFIED) && rp2350_rom_version() == 2) \ rom_reboot((flags) | REBOOT2_FLAG_REBOOT_TO_RISCV, delay_ms, p0, p1); \ diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index c5cc95e602..6516d83834 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -241,6 +241,13 @@ static __force_inline void *rom_data_lookup_inline(uint32_t code) { } #pragma GCC diagnostic pop +#if PICO_RP2350 +// PICO_CONFIG: PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG, Workaround RP2350A-A2 (QFN60) bug not displaying USB boot activity LED under Arm by making rom_reset_usb_boot functions reboot to RISC-V when the activity LED is specified, type=bool, default=1, group=pico_bootrom +#ifndef PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG +#define PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG 1 +#endif +#endif + /*! * \brief Reboot the device into BOOTSEL mode * \ingroup pico_bootrom @@ -252,7 +259,7 @@ static __force_inline void *rom_data_lookup_inline(uint32_t code) { * * \if rp2350_specific * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display * the activity LED correctly. * \endif * @@ -280,7 +287,7 @@ static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activit * * \if rp2350_specific * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display + * PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display * the activity LED correctly. * \endif * From 7d1fb778f03de0faa9334f26ad728938262100a6 Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Wed, 3 Jun 2026 11:46:59 -0500 Subject: [PATCH 10/10] Apply suggestions from code review Co-authored-by: will-v-pi <108662275+will-v-pi@users.noreply.github.com> --- src/rp2_common/pico_bootrom/include/pico/bootrom.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h index 6516d83834..6223d92f4b 100644 --- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h +++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h @@ -258,9 +258,9 @@ static __force_inline void *rom_data_lookup_inline(uint32_t code) { * and to limit the USB interfaces exposed. * * \if rp2350_specific - * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display - * the activity LED correctly. + * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. + * PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG=1 is defined by default to have this method reboot to + * RISC-V USB boot to display the activity LED correctly. * \endif * * \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise, a single bit set indicating which @@ -286,9 +286,9 @@ static inline void __attribute__((noreturn)) reset_usb_boot(uint32_t usb_activit * and to limit the USB interfaces exposed. * * \if rp2350_specific - * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. You can define - * PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG=1 to have this method reboot to RISC-V USB boot to display - * the activity LED correctly. + * \note On RP2350A-A2 chips, errata RP2350-E3 prevents the activity LED working under Arm. + * PICO_BOOTROM_WORKAROUND_RP2350_A2_ACTIVITY_LED_BUG=1 is defined by default to have this method reboot to + * RISC-V USB boot to display the activity LED correctly. * \endif * * \param usb_activity_gpio_pin GPIO pin to be used as an activitiy pin, or -1 for none