blob: b55e0988a83535c512c06a90dc0d8355f8680242 [file] [log] [blame]
/*
* 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 <string.h>
#include "pico/bootrom/lock.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
// Detect ROM size by testing for bus fault at +32k
uint result;
pico_default_asm_volatile (
"li %0, 0\n"
// Save and disable IRQs before touching trap vector
"csrr t2, mstatus\n"
"csrci mstatus, 0x8\n"
// Set up trap vector to skip the instruction which sets the %0 flag
"la t0, 1f\n"
"csrrw t0, mtvec, t0\n"
// This load will fault if the bootrom is no larger than 32k:
"li t1, 32 * 1024\n"
"lw t1, (t1)\n"
// No fault, so set return to true
"li %0, 1\n"
".p2align 2\n"
// Always end up back here, restore the trap table
"1:\n"
"csrw mtvec, t0\n"
// Now safe to restore interrupts
"csrw mstatus, t2\n"
: "=r" (result)
:
: "t0", "t1", "t2"
);
return result;
#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 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.
*
* \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 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() {
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() {
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() {
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() {
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
#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:
*
* REBOOT_TYPE_NORMAL - reboot into the normal boot path.
*
* REBOOT_TYPE_BOOTSEL - reboot into BOOTSEL mode.
* p0 - the GPIO number to use as an activity indicator (enabled by flag in p1).
* p1 - a set of flags:
* 0x01 : DISABLE_MSD_INTERFACE - Disable the BOOTSEL USB drive (see <<section_bootrom_mass_storage>>)
* 0x02 : DISABLE_PICOBOOT_INTERFACE - Disable the {picoboot} interface (see <<section_bootrom_picoboot>>).
* 0x10 : GPIO_PIN_ACTIVE_LOW - The GPIO in p0 is active low.
* 0x20 : GPIO_PIN_ENABLED - Enable the activity indicator on the specified GPIO.
*
* 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).
*
* REBOOT_TYPE_FLASH_UPDATE - variant of 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.
*
* 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:
*
* 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.
* 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.
* 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);
}
/*!
* \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) {
rom_flash_op_fn func = (rom_flash_op_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_OP);
if (!bootrom_try_acquire_lock(BOOTROM_LOCK_FLASH_OP))
return BOOTROM_ERROR_LOCK_REQUIRED;
int rc = func(flags, addr, size_bytes, buf);
bootrom_release_lock(BOOTROM_LOCK_FLASH_OP);
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 flags_and_partition 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 3064, so 3K 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 3064, so 3K 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.
*
* \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
*/
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 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 3064, so 3K 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 <<api-load_partition_table, load_partition_table()>> 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 3064, so 3K 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);
int rc = func(workarea_base, workarea_size, region_base, region_size);
bootrom_acquire_lock_blocking(BOOTROM_LOCK_ENABLE);
return rc;
}
// 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) {
rom_explicit_buy_fn func = (rom_explicit_buy_fn) rom_func_lookup_inline(ROM_FUNC_EXPLICIT_BUY);
return func(buffer, buffer_size);
}
#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);
}
#define BOOT_TYPE_NORMAL 0
#define BOOT_TYPE_BOOTSEL 2
#define BOOT_TYPE_RAM_IMAGE 3
#define BOOT_TYPE_FLASH_UPDATE 4
// values 8-15 are secure only
#define BOOT_TYPE_PC_SP 0xd
// ORed in if a bootloader chained into the image
#define BOOT_TYPE_CHAINED_FLAG 0x80
/*!
* \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