This commit is contained in:
Graham Sanderson 2025-12-08 15:07:30 +01:00 committed by GitHub
commit 295a377cef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 2606 additions and 2017 deletions

View file

@ -110,6 +110,7 @@ if (NOT PICO_BARE_METAL)
if (PICO_COMBINED_DOCS OR NOT PICO_RP2040)
pico_add_subdirectory(rp2_common/pico_sha256)
pico_add_subdirectory(rp2_common/pico_secure)
endif()
pico_add_subdirectory(rp2_common/pico_stdio_semihosting)

View file

@ -341,4 +341,42 @@ typedef struct cflash_flags {
#define CFLASH_OP_VALUE_READ _u(2)
#define CFLASH_OP_MAX _u(2)
#ifndef __riscv
/*! \brief Return a well known secure call code based on four ASCII characters
* \ingroup pico_bootrom
*
* These codes are used to call well known secure functions from non-secure code.
*
* NOTE: ASCII characters are all < 0x80, so will always start with `0b0xxx`, as required by the rom_secure_call() documentation.
*
* \param c1 the first character
* \param c2 the second character
* \param c3 the third character
* \param c4 the fourth character
* \return the 'code' to use in rom_secure_call(), and handled by rom_default_callback()
*/
#define SECURE_CALL_WELL_KNOWN_CODE(c1, c2, c3, c4) ((c1) | ((c2) << 8) | ((c3) << 16) | ((c4) << 24))
#define SECURE_CALL_stdio_out_chars SECURE_CALL_WELL_KNOWN_CODE('I', 'O', 'O', 'C')
#define SECURE_CALL_get_rand_64 SECURE_CALL_WELL_KNOWN_CODE('R', 'D', '6', '4')
#define SECURE_CALL_dma_allocate_unused_channel_for_nonsecure SECURE_CALL_WELL_KNOWN_CODE('D', 'A', 'C', 'H')
#define SECURE_CALL_user_irq_claim_unused_for_nonsecure SECURE_CALL_WELL_KNOWN_CODE('U', 'I', 'R', 'Q')
#define SECURE_CALL_clock_get_hz SECURE_CALL_WELL_KNOWN_CODE('C', 'K', 'G', 'H')
#define SECURE_CALL_pio_claim_unused_pio_for_nonsecure SECURE_CALL_WELL_KNOWN_CODE('P', 'I', 'O', 'C')
#define SECURE_CALL_pads_bank0_set_bits SECURE_CALL_WELL_KNOWN_CODE('P', '0', 'S', 'B')
#define SECURE_CALL_pads_bank0_clear_bits SECURE_CALL_WELL_KNOWN_CODE('P', '0', 'C', 'B')
#define SECURE_CALL_pads_bank0_write_masked SECURE_CALL_WELL_KNOWN_CODE('P', '0', 'W', 'M')
#define SECURE_CALL_pads_bank0_read SECURE_CALL_WELL_KNOWN_CODE('P', '0', 'R', 'D')
#define SECURE_CALL_reset_block_mask SECURE_CALL_WELL_KNOWN_CODE('R', 'T', 'R', 'M')
#define SECURE_CALL_unreset_block_mask SECURE_CALL_WELL_KNOWN_CODE('R', 'T', 'U', 'M')
#define SECURE_CALL_unreset_block_mask_wait_blocking SECURE_CALL_WELL_KNOWN_CODE('R', 'T', 'U', 'W')
#endif
#endif

View file

@ -42,9 +42,12 @@
#define PARAM_ASSERTIONS_ENABLED_HARDWARE_FLASH 0
#endif
#endif
#define FLASH_PAGE_SIZE (1u << 8)
#define FLASH_SECTOR_SIZE (1u << 12)
#define FLASH_BLOCK_SIZE (1u << 16)
#define FLASH_PAGE_SHIFT 8u
#define FLASH_PAGE_SIZE (1u << FLASH_PAGE_SHIFT)
#define FLASH_SECTOR_SHIFT 12u
#define FLASH_SECTOR_SIZE (1u << FLASH_SECTOR_SHIFT)
#define FLASH_BLOCK_SHIFT 16u
#define FLASH_BLOCK_SIZE (1u << FLASH_BLOCK_SHIFT)
#ifndef FLASH_UNIQUE_ID_SIZE_BYTES
#define FLASH_UNIQUE_ID_SIZE_BYTES 8

View file

@ -1,2 +1,6 @@
pico_simple_hardware_target(gpio)
pico_mirrored_target_link_libraries(hardware_gpio INTERFACE hardware_irq)
pico_mirrored_target_link_libraries(hardware_gpio INTERFACE hardware_irq)
if (PICO_RP2350)
pico_mirrored_target_link_libraries(hardware_gpio INTERFACE pico_bootrom)
endif()

View file

@ -27,7 +27,7 @@ static raw_irq_mask_type_t raw_irq_mask[NUM_CORES];
// Get the raw value from the pin, bypassing any muxing or overrides.
int gpio_get_pad(uint gpio) {
check_gpio_param(gpio);
hw_set_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_IE_BITS);
pads_bank0_set_bits(gpio, PADS_BANK0_GPIO0_IE_BITS);
return (io_bank0_hw->io[gpio].status & IO_BANK0_GPIO0_STATUS_INFROMPAD_BITS)
>> IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB;
}
@ -39,7 +39,7 @@ void gpio_set_function(uint gpio, gpio_function_t fn) {
check_gpio_param(gpio);
invalid_params_if(HARDWARE_GPIO, ((uint32_t)fn << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) & ~IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS);
// Set input enable on, output disable off
hw_write_masked(&pads_bank0_hw->io[gpio],
pads_bank0_write_masked(gpio,
PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS
);
@ -48,7 +48,7 @@ void gpio_set_function(uint gpio, gpio_function_t fn) {
io_bank0_hw->io[gpio].ctrl = fn << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
#if HAS_PADS_BANK0_ISOLATION
// Remove pad isolation now that the correct peripheral is in control of the pad
hw_clear_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_ISO_BITS);
pads_bank0_clear_bits(gpio, PADS_BANK0_GPIO0_ISO_BITS);
#endif
}
/// \end::gpio_set_function[]
@ -62,8 +62,8 @@ gpio_function_t gpio_get_function(uint gpio) {
// i.e. weak pull to whatever is current high/low state of GPIO.
void gpio_set_pulls(uint gpio, bool up, bool down) {
check_gpio_param(gpio);
hw_write_masked(
&pads_bank0_hw->io[gpio],
pads_bank0_write_masked(
gpio,
(bool_to_bit(up) << PADS_BANK0_GPIO0_PUE_LSB) | (bool_to_bit(down) << PADS_BANK0_GPIO0_PDE_LSB),
PADS_BANK0_GPIO0_PUE_BITS | PADS_BANK0_GPIO0_PDE_BITS
);
@ -106,20 +106,20 @@ void gpio_set_oeover(uint gpio, uint value) {
void gpio_set_input_hysteresis_enabled(uint gpio, bool enabled) {
check_gpio_param(gpio);
if (enabled)
hw_set_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_SCHMITT_BITS);
pads_bank0_set_bits(gpio, PADS_BANK0_GPIO0_SCHMITT_BITS);
else
hw_clear_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_SCHMITT_BITS);
pads_bank0_clear_bits(gpio, PADS_BANK0_GPIO0_SCHMITT_BITS);
}
bool gpio_is_input_hysteresis_enabled(uint gpio) {
check_gpio_param(gpio);
return (pads_bank0_hw->io[gpio] & PADS_BANK0_GPIO0_SCHMITT_BITS) != 0;
return (pads_bank0_read(gpio) & PADS_BANK0_GPIO0_SCHMITT_BITS) != 0;
}
void gpio_set_slew_rate(uint gpio, enum gpio_slew_rate slew) {
check_gpio_param(gpio);
hw_write_masked(&pads_bank0_hw->io[gpio],
pads_bank0_write_masked(gpio,
(uint)slew << PADS_BANK0_GPIO0_SLEWFAST_LSB,
PADS_BANK0_GPIO0_SLEWFAST_BITS
);
@ -127,7 +127,7 @@ void gpio_set_slew_rate(uint gpio, enum gpio_slew_rate slew) {
enum gpio_slew_rate gpio_get_slew_rate(uint gpio) {
check_gpio_param(gpio);
return (enum gpio_slew_rate)((pads_bank0_hw->io[gpio]
return (enum gpio_slew_rate)((pads_bank0_read(gpio)
& PADS_BANK0_GPIO0_SLEWFAST_BITS)
>> PADS_BANK0_GPIO0_SLEWFAST_LSB);
}
@ -137,7 +137,7 @@ enum gpio_slew_rate gpio_get_slew_rate(uint gpio) {
static_assert(PADS_BANK0_GPIO0_DRIVE_VALUE_8MA == GPIO_DRIVE_STRENGTH_8MA, "");
void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) {
check_gpio_param(gpio);
hw_write_masked(&pads_bank0_hw->io[gpio],
pads_bank0_write_masked(gpio,
(uint)drive << PADS_BANK0_GPIO0_DRIVE_LSB,
PADS_BANK0_GPIO0_DRIVE_BITS
);
@ -145,7 +145,7 @@ void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) {
enum gpio_drive_strength gpio_get_drive_strength(uint gpio) {
check_gpio_param(gpio);
return (enum gpio_drive_strength)((pads_bank0_hw->io[gpio]
return (enum gpio_drive_strength)((pads_bank0_read(gpio)
& PADS_BANK0_GPIO0_DRIVE_BITS)
>> PADS_BANK0_GPIO0_DRIVE_LSB);
}
@ -186,7 +186,7 @@ static void _gpio_set_irq_enabled(uint gpio, uint32_t events, bool enabled, io_b
void gpio_set_irq_enabled(uint gpio, uint32_t events, bool enabled) {
// either this call disables the interrupt or callback should already be set.
// this protects against enabling the interrupt without callback set
assert(!enabled || irq_has_handler(IO_IRQ_BANK0));
assert(!enabled || irq_has_handler(DEFAULT_IO_IRQ_BANK0));
// Separate mask/force/status per-core, so check which core called, and
// set the relevant IRQ controls.
@ -201,32 +201,32 @@ void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled
if (enabled) gpio_set_irq_callback(callback);
gpio_set_irq_enabled(gpio, events, enabled);
if (!enabled) gpio_set_irq_callback(callback);
if (enabled) irq_set_enabled(IO_IRQ_BANK0, true);
if (enabled) irq_set_enabled(DEFAULT_IO_IRQ_BANK0, true);
}
void gpio_set_irq_callback(gpio_irq_callback_t callback) {
uint core = get_core_num();
if (callbacks[core]) {
if (!callback) {
irq_remove_handler(IO_IRQ_BANK0, gpio_default_irq_handler);
irq_remove_handler(DEFAULT_IO_IRQ_BANK0, gpio_default_irq_handler);
}
callbacks[core] = callback;
} else if (callback) {
callbacks[core] = callback;
irq_add_shared_handler(IO_IRQ_BANK0, gpio_default_irq_handler, GPIO_IRQ_CALLBACK_ORDER_PRIORITY);
irq_add_shared_handler(DEFAULT_IO_IRQ_BANK0, gpio_default_irq_handler, GPIO_IRQ_CALLBACK_ORDER_PRIORITY);
}
}
void gpio_add_raw_irq_handler_with_order_priority_masked(uint32_t gpio_mask, irq_handler_t handler, uint8_t order_priority) {
hard_assert(!(raw_irq_mask[get_core_num()] & gpio_mask)); // should not add multiple handlers for the same event
raw_irq_mask[get_core_num()] |= gpio_mask;
irq_add_shared_handler(IO_IRQ_BANK0, handler, order_priority);
irq_add_shared_handler(DEFAULT_IO_IRQ_BANK0, handler, order_priority);
}
void gpio_add_raw_irq_handler_with_order_priority_masked64(uint64_t gpio_mask, irq_handler_t handler, uint8_t order_priority) {
hard_assert(!(raw_irq_mask[get_core_num()] & gpio_mask)); // should not add multiple handlers for the same event
raw_irq_mask[get_core_num()] |= (raw_irq_mask_type_t) gpio_mask;
irq_add_shared_handler(IO_IRQ_BANK0, handler, order_priority);
irq_add_shared_handler(DEFAULT_IO_IRQ_BANK0, handler, order_priority);
}
void gpio_add_raw_irq_handler_masked(uint32_t gpio_mask, irq_handler_t handler) {
@ -239,13 +239,13 @@ void gpio_add_raw_irq_handler_masked64(uint64_t gpio_mask, irq_handler_t handler
void gpio_remove_raw_irq_handler_masked(uint32_t gpio_mask, irq_handler_t handler) {
assert(raw_irq_mask[get_core_num()] & gpio_mask); // should not remove handlers that are not added
irq_remove_handler(IO_IRQ_BANK0, handler);
irq_remove_handler(DEFAULT_IO_IRQ_BANK0, handler);
raw_irq_mask[get_core_num()] &= ~gpio_mask;
}
void gpio_remove_raw_irq_handler_masked64(uint64_t gpio_mask, irq_handler_t handler) {
assert(raw_irq_mask[get_core_num()] & gpio_mask); // should not remove handlers that are not added
irq_remove_handler(IO_IRQ_BANK0, handler);
irq_remove_handler(DEFAULT_IO_IRQ_BANK0, handler);
raw_irq_mask[get_core_num()] &= (raw_irq_mask_type_t)~gpio_mask;
}
@ -267,9 +267,9 @@ void gpio_debug_pins_init(void) {
void gpio_set_input_enabled(uint gpio, bool enabled) {
check_gpio_param(gpio);
if (enabled)
hw_set_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_IE_BITS);
pads_bank0_set_bits(gpio, PADS_BANK0_GPIO0_IE_BITS);
else
hw_clear_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_IE_BITS);
pads_bank0_clear_bits(gpio, PADS_BANK0_GPIO0_IE_BITS);
}
void gpio_init(uint gpio) {

View file

@ -18,10 +18,27 @@
#define PICO_USE_GPIO_COPROCESSOR 1
#endif
#if PICO_NONSECURE
#define DEFAULT_IO_IRQ_BANK0 IO_IRQ_BANK0_NS
#else
#define DEFAULT_IO_IRQ_BANK0 IO_IRQ_BANK0
#endif
#if PICO_USE_GPIO_COPROCESSOR
#include "hardware/gpio_coproc.h"
#endif
#if PICO_SECURE || PICO_NONSECURE
#include "pico/bootrom.h" // uses helper functions due to Errata RP2350-E3
#else
#define pads_bank0_set_bits(gpio, bits) hw_set_bits(&pads_bank0_hw->io[gpio], bits)
#define pads_bank0_clear_bits(gpio, bits) hw_clear_bits(&pads_bank0_hw->io[gpio], bits)
#define pads_bank0_write_masked(gpio, bits, mask) hw_write_masked(&pads_bank0_hw->io[gpio], bits, mask)
#define pads_bank0_read(gpio) pads_bank0_hw->io[gpio]
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -312,7 +329,7 @@ static inline void gpio_pull_up(uint gpio) {
* \return true if the GPIO is pulled up
*/
static inline bool gpio_is_pulled_up(uint gpio) {
return (pads_bank0_hw->io[gpio] & PADS_BANK0_GPIO0_PUE_BITS) != 0;
return (pads_bank0_read(gpio) & PADS_BANK0_GPIO0_PUE_BITS) != 0;
}
/*! \brief Set specified GPIO to be pulled down.
@ -331,7 +348,7 @@ static inline void gpio_pull_down(uint gpio) {
* \return true if the GPIO is pulled down
*/
static inline bool gpio_is_pulled_down(uint gpio) {
return (pads_bank0_hw->io[gpio] & PADS_BANK0_GPIO0_PDE_BITS) != 0;
return (pads_bank0_read(gpio) & PADS_BANK0_GPIO0_PDE_BITS) != 0;
}
/*! \brief Disable pulls on specified GPIO

View file

@ -106,6 +106,9 @@
extern "C" {
#endif
#if PICO_NONSECURE
#include "pico/bootrom.h"
#else
static __force_inline void reset_block_reg_mask(io_rw_32 *reset, uint32_t mask) {
hw_set_bits(reset, mask);
}
@ -119,6 +122,7 @@ static __force_inline void unreset_block_reg_mask_wait_blocking(io_rw_32 *reset,
while (~*reset_done & mask)
tight_loop_contents();
}
#endif
/// \tag::reset_funcs[]

View file

@ -9,8 +9,8 @@ target_sources(pico_bootrom INTERFACE
)
target_link_libraries(pico_bootrom_headers INTERFACE boot_picoboot_headers boot_bootrom_headers)
pico_mirrored_target_link_libraries(pico_bootrom INTERFACE pico_base hardware_boot_lock pico_flash)
pico_mirrored_target_link_libraries(pico_bootrom INTERFACE pico_base hardware_boot_lock pico_flash hardware_flash pico_rand hardware_dma hardware_irq pico_stdio hardware_pio)
# bootrom.c includes boot/picobin.h
# bootrom_lock.c includes pico/runtime_init.h
target_link_libraries(pico_bootrom INTERFACE boot_picobin_headers pico_runtime_init_headers)
target_link_libraries(pico_bootrom INTERFACE boot_picobin_headers pico_runtime_init_headers)

View file

@ -9,7 +9,10 @@
#include "boot/picobin.h"
#if !PICO_RP2040
#include "hardware/rcp.h"
#include "hardware/flash.h"
#include "hardware/structs/qmi.h"
#endif
#include "pico/runtime_init.h"
/// \tag::table_lookup[]
@ -196,4 +199,460 @@ int rom_pick_ab_partition_during_update(uint32_t *workarea_base, uint32_t workar
return rc;
}
#endif
int rom_get_owned_partition(uint partition_num) {
int ret;
uint32_t buffer[(16 * 2) + 1] = {}; // maximum of 16 partitions, each with 2 words returned, plus 1
// Initially assume that the partition_num is the A partition
int partition_a_num = partition_num;
ret = rom_get_b_partition(partition_num);
if (ret < 0) {
// partition_num is actually the B partition, so read the A partition
ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (partition_num << 24));
if (ret < 0) return ret;
uint32_t flags_and_permissions = buffer[2];
if ((flags_and_permissions & PICOBIN_PARTITION_FLAGS_LINK_TYPE_BITS) >> PICOBIN_PARTITION_FLAGS_LINK_TYPE_LSB != PICOBIN_PARTITION_FLAGS_LINK_TYPE_A_PARTITION) return BOOTROM_ERROR_NOT_FOUND;
partition_a_num = (flags_and_permissions & PICOBIN_PARTITION_FLAGS_LINK_VALUE_BITS) >> PICOBIN_PARTITION_FLAGS_LINK_VALUE_LSB;
}
ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS);
if (ret < 0) return ret;
int num_partitions = (ret - 1) / 2;
int owned_a_num;
for (owned_a_num = 0; owned_a_num < num_partitions; owned_a_num++) {
uint32_t flags_and_permissions = buffer[owned_a_num * 2 + 2];
if (
(flags_and_permissions & PICOBIN_PARTITION_FLAGS_LINK_TYPE_BITS) >> PICOBIN_PARTITION_FLAGS_LINK_TYPE_LSB == PICOBIN_PARTITION_FLAGS_LINK_TYPE_OWNER_PARTITION &&
(flags_and_permissions & PICOBIN_PARTITION_FLAGS_LINK_VALUE_BITS) >> PICOBIN_PARTITION_FLAGS_LINK_VALUE_LSB == partition_a_num
) {
break;
}
}
if (owned_a_num == num_partitions) return BOOTROM_ERROR_NOT_FOUND;
if (partition_num == partition_a_num)
return owned_a_num;
else
return rom_get_b_partition(owned_a_num);
}
int rom_roll_qmi_to_partition(uint partition_num) {
uint32_t buffer[2 + 1] = {}; // 2 words for the partition location and flags, plus 1
int ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (partition_num << 24));
if (ret < 0) return ret;
uint32_t location_and_permissions = buffer[1];
uint32_t saddr = ((location_and_permissions >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & 0x1fffu) * FLASH_SECTOR_SIZE;
uint32_t eaddr = (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * FLASH_SECTOR_SIZE;
int32_t roll = (int32_t)saddr;
if (roll) {
if ((uint32_t)roll & (FLASH_SECTOR_SIZE - 1u)) return BOOTROM_ERROR_BAD_ALIGNMENT;
roll >>= FLASH_SECTOR_SHIFT;
int32_t size = (int32_t)((eaddr - saddr) >> FLASH_SECTOR_SHIFT);
for (uint i = 0; i < 4; i++) {
static_assert(4 * 1024 * 1024 / FLASH_SECTOR_SIZE == 0x400, "Expected 4 MiB / FLASH_SECTOR_SIZE = 0x400");
if (roll < 0) {
roll += 0x400;
qmi_hw->atrans[i] = 0;
} else {
int32_t this_size = MIN(size, 0x400);
qmi_hw->atrans[i] = (uint)((this_size << 16) | roll);
size -= this_size;
roll += this_size;
}
}
}
return BOOTROM_OK;
}
#if PICO_SECURE || PICO_NONSECURE
#include "hardware/structs/accessctrl.h"
int __noinline rom_secure_call(uint a, uint b, uint c, uint d, uint func) {
uint32_t secure_call = (uintptr_t)rom_func_lookup_inline(ROM_FUNC_SECURE_CALL);
register uint32_t r0 asm("r0") = a;
register uint32_t r1 asm("r1") = b;
register uint32_t r2 asm("r2") = c;
register uint32_t r3 asm("r3") = d;
register uint32_t r4 asm("r4") = func;
pico_default_asm_volatile(
"push {lr}\n"
"blx %0\n"
"pop {lr}\n"
: : "r" (secure_call), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4));
return (int)r0;
}
struct rom_secure_call_user_callback_slot {
uint16_t fn_mask;
rom_secure_call_callback_t callback;
} rom_secure_call_user_callback_slots[PICO_MAX_SECURE_CALL_USER_CALLBACKS];
int rom_secure_call_add_user_callback(rom_secure_call_callback_t callback, uint16_t fn_mask) {
int first_unused = PICO_MAX_SECURE_CALL_USER_CALLBACKS;
for (int i=0; i < PICO_MAX_SECURE_CALL_USER_CALLBACKS; i++) {
if (!rom_secure_call_user_callback_slots[i].fn_mask) {
if (first_unused == PICO_MAX_SECURE_CALL_USER_CALLBACKS) first_unused = i;
continue;
}
// Check new function is not an existing function mask
if (rom_secure_call_user_callback_slots[i].fn_mask == fn_mask) {
return BOOTROM_ERROR_INVALID_ARG;
}
}
if (first_unused == PICO_MAX_SECURE_CALL_USER_CALLBACKS) {
// No free slots
return BOOTROM_ERROR_BUFFER_TOO_SMALL;
}
rom_secure_call_user_callback_slots[first_unused].callback = callback;
rom_secure_call_user_callback_slots[first_unused].fn_mask = fn_mask;
return BOOTROM_OK;
}
void rom_secure_call_remove_user_callback(rom_secure_call_callback_t callback) {
for (int i=0; i < PICO_MAX_SECURE_CALL_USER_CALLBACKS; i++) {
if (rom_secure_call_user_callback_slots[i].callback == callback) {
rom_secure_call_user_callback_slots[i].callback = NULL;
rom_secure_call_user_callback_slots[i].fn_mask = 0;
return;
}
}
}
#if PICO_ALLOW_NONSECURE_STDIO
#include "pico/stdio/driver.h"
#if PICO_NONSECURE
static void stdio_nonsecure_out_chars(const char *buf, int length) {
rom_secure_call((uint32_t)buf, length, 0, 0, SECURE_CALL_stdio_out_chars);
}
int stdio_nonsecure_in_chars(char *buf, int length) {
return PICO_ERROR_NO_DATA;
}
static void stdio_nonsecure_out_flush(void) {}
stdio_driver_t stdio_nonsecure = {
.out_chars = stdio_nonsecure_out_chars,
.out_flush = stdio_nonsecure_out_flush,
.in_chars = stdio_nonsecure_in_chars,
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
.crlf_enabled = false, // CRLF is handled by the secure side
#endif
};
#if !PICO_RUNTIME_NO_INIT_NONSECURE_STDIO
void __weak runtime_init_nonsecure_stdio() {
stdio_set_driver_enabled(&stdio_nonsecure, true);
}
#endif
#if !PICO_RUNTIME_SKIP_INIT_NONSECURE_STDIO
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_nonsecure_stdio, PICO_RUNTIME_INIT_NONSECURE_STDIO);
#endif
#endif // PICO_NONSECURE
#endif // PICO_ALLOW_NONSECURE_STDIO
#if PICO_ALLOW_NONSECURE_RAND
#include "pico/rand.h"
#if PICO_NONSECURE
// override the weak definition
uint64_t get_rand_64(void) {
return rom_secure_call(0, 0, 0, 0, SECURE_CALL_get_rand_64);
}
#endif
#endif // PICO_ALLOW_NONSECURE_RAND
#if PICO_ALLOW_NONSECURE_DMA
#include "hardware/dma.h"
#if PICO_SECURE
static int dma_allocate_unused_channel_for_nonsecure(void) {
int chan = dma_claim_unused_channel(false);
if (chan < 0) return chan;
if (chan > PICO_NONSECURE_DMA_MAX_CHANNEL) {
dma_channel_unclaim(chan);
return -1;
}
hw_clear_bits(&dma_hw->seccfg_ch[chan], DMA_SECCFG_CH0_S_BITS | DMA_SECCFG_CH0_LOCK_BITS);
return chan;
}
#elif PICO_NONSECURE
int dma_request_unused_channels_from_secure(int num_channels) {
int i;
for (i = 0; i < num_channels; i++) {
int chan = rom_secure_call(0, 0, 0, 0, SECURE_CALL_dma_allocate_unused_channel_for_nonsecure);
if (chan < 0) break;
dma_channel_unclaim(chan);
}
return i;
}
#endif
#endif // PICO_ALLOW_NONSECURE_DMA
#if PICO_ALLOW_NONSECURE_USER_IRQ
#include "hardware/irq.h"
#if PICO_SECURE
static int user_irq_claim_unused_for_nonsecure() {
int bit = user_irq_claim_unused(false);
if (bit < 0) return bit;
irq_assign_to_ns(bit, true);
return bit;
}
#elif PICO_NONSECURE
int user_irq_request_unused_from_secure(int num_irqs) {
int i;
for (i = 0; i < num_irqs; i++) {
int irq = rom_secure_call(0, 0, 0, 0, SECURE_CALL_user_irq_claim_unused_for_nonsecure);
if (irq < 0) break;
user_irq_unclaim(irq);
}
return i;
}
#endif
#endif // PICO_ALLOW_NONSECURE_USER_IRQ
#if PICO_ALLOW_NONSECURE_PIO
#include "hardware/pio.h"
#include "hardware/irq.h"
#if PICO_SECURE
static int pio_claim_unused_pio_for_nonsecure(void) {
// Find completely unused PIO
uint pio;
for (pio = 0; pio < PICO_NONSECURE_PIO_MAX; pio++) {
// We need to claim an SM on the PIO
int8_t sm_index[NUM_PIO_STATE_MACHINES];
// on second pass, if there is one, we try and claim all the state machines so that we can change the GPIO base
uint num_claimed;
for(num_claimed = 0; num_claimed < NUM_PIO_STATE_MACHINES ; num_claimed++) {
sm_index[num_claimed] = (int8_t)pio_claim_unused_sm(pio_get_instance(pio), false);
if (sm_index[num_claimed] < 0) break;
}
if (num_claimed != NUM_PIO_STATE_MACHINES) {
// un-claim all the SMs
for (uint i = 0; i < num_claimed; i++) {
pio_sm_unclaim(pio_get_instance(pio), (uint) sm_index[i]);
}
continue;
}
break;
}
if (pio == PICO_NONSECURE_PIO_MAX) {
return -1;
}
// Accessctrl and IRQs
accessctrl_hw->pio[pio] |= 0xacce0000 | ACCESSCTRL_PIO0_NSP_BITS | ACCESSCTRL_PIO0_NSU_BITS;
static_assert(PIO0_IRQ_0 + 2 == PIO1_IRQ_0, "Expected 2 IRQs per PIO");
irq_assign_to_ns(PIO0_IRQ_0 + pio * 2, true);
irq_assign_to_ns(PIO0_IRQ_1 + pio * 2, true);
return pio;
}
#elif PICO_NONSECURE
int pio_request_unused_pio_from_secure(void) {
int pio = rom_secure_call(0, 0, 0, 0, SECURE_CALL_pio_claim_unused_pio_for_nonsecure);
if (pio < 0) return pio;
for (uint sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) {
pio_sm_unclaim(pio_get_instance(pio), sm);
}
return pio;
}
#endif
#endif // PICO_ALLOW_NONSECURE_PIO
#if !PICO_RUNTIME_NO_INIT_BOOTROM_API_CALLBACK
#include <stdio.h>
#include "hardware/clocks.h"
#include "hardware/resets.h"
#include "hardware/structs/accessctrl.h"
int rom_default_callback(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t fn) {
if (fn >> 31) {
// User callbacks all start with 0b1xxx, as specified by the rom_secure_call() documentation
for (int i=0; i < PICO_MAX_SECURE_CALL_USER_CALLBACKS; i++) {
if ((fn >> 16) == rom_secure_call_user_callback_slots[i].fn_mask) {
return rom_secure_call_user_callback_slots[i].callback(a, b, c, d, fn);
}
}
return BOOTROM_ERROR_INVALID_ARG;
}
switch (fn) {
#if PICO_ALLOW_NONSECURE_STDIO
case SECURE_CALL_stdio_out_chars: {
uint32_t ok = RCP_MASK_FALSE;
rom_validate_ns_buffer((char*)a, b, RCP_MASK_TRUE, &ok);
if (ok != RCP_MASK_TRUE) return BOOTROM_ERROR_NOT_PERMITTED;
stdio_put_string((char*)a, b, false, true);
stdio_flush();
return BOOTROM_OK;
}
#endif
#if PICO_ALLOW_NONSECURE_RAND
case SECURE_CALL_get_rand_64: {
return get_rand_64();
}
#endif
#if PICO_ALLOW_NONSECURE_DMA
case SECURE_CALL_dma_allocate_unused_channel_for_nonsecure: {
return dma_allocate_unused_channel_for_nonsecure();
}
#endif
#if PICO_ALLOW_NONSECURE_USER_IRQ
case SECURE_CALL_user_irq_claim_unused_for_nonsecure: {
return user_irq_claim_unused_for_nonsecure();
}
#endif
#if PICO_ALLOW_NONSECURE_PIO
case SECURE_CALL_pio_claim_unused_pio_for_nonsecure: {
return pio_claim_unused_pio_for_nonsecure();
}
#endif
#if PICO_ADD_NONSECURE_PADS_HELPER
case SECURE_CALL_pads_bank0_set_bits: {
if (accessctrl_hw->gpio_nsmask[a/32] & 1u << (a & 0x1fu)) {
return pads_bank0_set_bits(a, b);
} else {
return BOOTROM_ERROR_NOT_PERMITTED;
}
}
case SECURE_CALL_pads_bank0_clear_bits: {
if (accessctrl_hw->gpio_nsmask[a/32] & 1u << (a & 0x1fu)) {
return pads_bank0_clear_bits(a, b);
} else {
return BOOTROM_ERROR_NOT_PERMITTED;
}
}
case SECURE_CALL_pads_bank0_write_masked: {
if (accessctrl_hw->gpio_nsmask[a/32] & 1u << (a & 0x1fu)) {
return pads_bank0_write_masked(a, b, c);
} else {
return BOOTROM_ERROR_NOT_PERMITTED;
}
}
case SECURE_CALL_pads_bank0_read: {
if (accessctrl_hw->gpio_nsmask[a/32] & 1u << (a & 0x1fu)) {
return pads_bank0_read(a);
} else {
return BOOTROM_ERROR_NOT_PERMITTED;
}
}
#endif
case SECURE_CALL_clock_get_hz: {
return clock_get_hz(a);
}
#if PICO_ALLOW_NONSECURE_RESETS
case SECURE_CALL_reset_block_mask: {
if (a & ~PICO_ALLOW_NONSECURE_RESETS_MASK) return BOOTROM_ERROR_NOT_PERMITTED;
reset_block_mask(a);
return BOOTROM_OK;
}
case SECURE_CALL_unreset_block_mask: {
if (a & ~PICO_ALLOW_NONSECURE_RESETS_MASK) return BOOTROM_ERROR_NOT_PERMITTED;
unreset_block_mask(a);
return BOOTROM_OK;
}
case SECURE_CALL_unreset_block_mask_wait_blocking: {
if (a & ~PICO_ALLOW_NONSECURE_RESETS_MASK) return BOOTROM_ERROR_NOT_PERMITTED;
unreset_block_mask_wait_blocking(a);
return BOOTROM_OK;
}
#endif
default: {
printf("%d is not a supported rom function\n", fn);
return BOOTROM_ERROR_INVALID_ARG;
}
}
}
static int __attribute__((naked)) rom_default_asm_callback() {
pico_default_asm_volatile(
"push {r0, lr}\n"
"str r4, [sp]\n"
"bl rom_default_callback\n"
"pop {r1, pc}\n"
);
}
void __weak runtime_init_rom_set_default_callback() {
rom_set_rom_callback(BOOTROM_API_CALLBACK_secure_call, (bootrom_api_callback_generic_t) rom_default_asm_callback);
rom_set_ns_api_permission(BOOTROM_NS_API_secure_call, true);
}
#endif // !PICO_RUNTIME_NO_INIT_BOOTROM_API_CALLBACK
#if !PICO_RUNTIME_SKIP_INIT_BOOTROM_API_CALLBACK
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_rom_set_default_callback, PICO_RUNTIME_INIT_BOOTROM_API_CALLBACK);
#endif
#if !PICO_RUNTIME_NO_INIT_NONSECURE_CLAIMS
void __weak runtime_init_nonsecure_claims() {
#if PICO_ALLOW_NONSECURE_DMA
for(uint i = 0; i < NUM_DMA_CHANNELS; i++) {
dma_channel_claim(i);
}
#endif
#if PICO_ALLOW_NONSECURE_USER_IRQ
for (uint i = 0; i < NUM_USER_IRQS; i++) {
user_irq_claim(FIRST_USER_IRQ + i);
}
#endif
#if PICO_ALLOW_NONSECURE_PIO
for (uint pio = 0; pio < NUM_PIOS; pio++) {
for (uint sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) {
pio_sm_claim(pio_get_instance(pio), sm);
}
}
#endif
}
#endif
#if !PICO_RUNTIME_SKIP_INIT_NONSECURE_CLAIMS
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_nonsecure_claims, PICO_RUNTIME_INIT_NONSECURE_CLAIMS);
#endif
#if !PICO_RUNTIME_NO_INIT_NONSECURE_CLOCKS
#include "hardware/clocks.h"
void __weak runtime_init_nonsecure_clocks() {
// Set all clocks to the reported frequency from the secure side
for (uint i = 0; i < CLK_COUNT; i++) {
uint32_t hz = rom_secure_call(i, 0, 0, 0, SECURE_CALL_clock_get_hz);
clock_set_reported_hz(i, hz);
}
}
#endif
#if !PICO_RUNTIME_SKIP_INIT_NONSECURE_CLOCKS
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_nonsecure_clocks, PICO_RUNTIME_INIT_NONSECURE_CLOCKS);
#endif
#endif // PICO_SECURE || PICO_NONSECURE
#endif // !PICO_RP2040

View file

@ -21,6 +21,8 @@
#include <string.h>
#include "pico/bootrom/lock.h"
#include "pico/flash.h"
#include "hardware/structs/pads_bank0.h"
// ROM FUNCTION SIGNATURES
#if PICO_RP2040
@ -636,6 +638,21 @@ static inline int rom_flash_op(cflash_flags_t flags, uintptr_t addr, uint32_t si
}
}
/*!
* \brief Roll QMI to a partition
* \ingroup pico_bootrom
*
* Rolls the QMI to the specified partition, enabling access to the partition via the translated XIP windows.
*
* This is necessary when the partition is not stored at the flash address it was linked at, e.g. when
* using A/B partitions.
*
* \param partition_num the partition number
* \return BOOTROM_OK on success, otherwise a negative error code
*/
int rom_roll_qmi_to_partition(uint partition_num);
/*!
* \brief Writes data from a buffer into OTP, or reads data from OTP into a buffer
* \ingroup pico_bootrom
@ -798,15 +815,30 @@ int rom_pick_ab_partition_during_update(uint32_t *workarea_base, uint32_t workar
* \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.
* otherwise returns a negative error code.
*
* \param pi_a the A partition number
* \return >= 0 the index of the B partition
* BOOTROM_ERROR_NOT_FOUND if the partition number does not have a B partition
*/
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);
}
/*!
* \brief Get Owned Partition
* \ingroup pico_bootrom
*
* Returns the index of the matching owned partition if a partition table is present and loaded, and the partition number has an owned
* partition; otherwise returns a negative error code.
*
* \param partition_num the partition number
* \return >= 0 the index of the matching owned partition
* BOOTROM_ERROR_NOT_FOUND if the partition number does not have an owned partition
*/
int rom_get_owned_partition(uint partition_num);
// todo SECURE only
/*!
* \brief Get UF2 Target Partition
@ -1010,6 +1042,225 @@ static inline intptr_t rom_set_rom_callback(uint callback_num, bootrom_api_callb
return func(callback_num, funcptr);
}
#ifndef __riscv
int rom_secure_call(uint a, uint b, uint c, uint d, uint func);
int rom_default_callback(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t fn);
// PICO_CONFIG: PICO_MAX_SECURE_CALL_USER_CALLBACKS, Maximum number of secure call user callbacks, default=4, advanced=true, group=pico_bootrom
#ifndef PICO_MAX_SECURE_CALL_USER_CALLBACKS
#define PICO_MAX_SECURE_CALL_USER_CALLBACKS 4
#endif
/*! Callback function type for user handled rom_secure_call
* \ingroup pico_bootrom
*/
typedef int (*rom_secure_call_callback_t)(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t fn);
/*!
* \brief Add user ROM callback function
* \ingroup pico_bootrom
*
* Add a user callback for rom_secure_call called if using a function code starting with `0b1xxx`,
* which is a "unique" or "private" function as specified by the rom_secure_call() documentation.
*
* \param callback pointer to the callback function
* \param fn_mask first 16 bits of fn codes this callback handles
*/
int rom_secure_call_add_user_callback(rom_secure_call_callback_t callback, uint16_t fn_mask);
/*!
* \brief Remove user ROM callback function
* \ingroup pico_bootrom
*
* Remove a user callback for rom_secure_call which was previously added with rom_secure_call_add_user_callback()
*
* \param callback pointer to the callback function
*/
void rom_secure_call_remove_user_callback(rom_secure_call_callback_t callback);
// PICO_CONFIG: PICO_ALLOW_NONSECURE_STDIO, Allow non-secure to use stdio, type=bool, default=0, group=pico_bootrom
#ifndef PICO_ALLOW_NONSECURE_STDIO
#define PICO_ALLOW_NONSECURE_STDIO 0
#endif
// PICO_CONFIG: PICO_ALLOW_NONSECURE_RAND, Allow non-secure to request random numbers, type=bool, default=0, group=pico_bootrom
#ifndef PICO_ALLOW_NONSECURE_RAND
#define PICO_ALLOW_NONSECURE_RAND 0
#endif
// PICO_CONFIG: PICO_ALLOW_NONSECURE_DMA, Allow non-secure to request DMA channels, type=bool, default=0, group=hardware_dma
#ifndef PICO_ALLOW_NONSECURE_DMA
#define PICO_ALLOW_NONSECURE_DMA 0
#endif
// PICO_CONFIG: PICO_NONSECURE_DMA_MAX_CHANNEL, Max number of DMA channels that can be allocated to non-secure use, type=int, default=NUM_DMA_CHANNELS, group=hardware_dma
#ifndef PICO_NONSECURE_DMA_MAX_CHANNEL
#define PICO_NONSECURE_DMA_MAX_CHANNEL NUM_DMA_CHANNELS
#endif
#if PICO_ALLOW_NONSECURE_DMA && PICO_NONSECURE
/*! \brief Request unused dma channels from secure
* \ingroup hardware_dma
*
* \param num_channels the number of channels to request
* \return the number of channels provided
*/
int dma_request_unused_channels_from_secure(int num_channels);
#endif
// PICO_CONFIG: PICO_ALLOW_USER_IRQ, Allow non-secure to request user IRQs, type=bool, default=0, group=hardware_irq
#ifndef PICO_ALLOW_NONSECURE_USER_IRQ
#define PICO_ALLOW_NONSECURE_USER_IRQ 0
#endif
// PICO_CONFIG: PICO_NONSECURE_USER_IRQ_MIN, Lowest number user IRQ that can be allocated to non-secure use, type=int, default=FIRST_USER_IRQ, group=hardware_irq
#ifndef PICO_NONSECURE_USER_IRQ_MIN
#define PICO_NONSECURE_USER_IRQ_MIN FIRST_USER_IRQ
#endif
#if PICO_ALLOW_NONSECURE_USER_IRQ && PICO_NONSECURE
/*! \brief Request unused user IRQs from secure
* \ingroup hardware_irq
*
* \param num_irqs the number of IRQs to request
* \return the number of IRQs provided
*/
int user_irq_request_unused_from_secure(int num_irqs);
#endif
// PICO_CONFIG: PICO_ALLOW_NONSECURE_PIO, Allow non-secure to request PIOs, type=bool, default=0, group=hardware_pio
#ifndef PICO_ALLOW_NONSECURE_PIO
#define PICO_ALLOW_NONSECURE_PIO 0
#endif
// PICO_CONFIG: PICO_NONSECURE_PIO_MAX, Max number of PIOs that can be allocated to non-secure use, type=int, default=NUM_PIOS, group=hardware_pio
#ifndef PICO_NONSECURE_PIO_MAX
#define PICO_NONSECURE_PIO_MAX NUM_PIOS
#endif
#if PICO_ALLOW_NONSECURE_PIO && PICO_NONSECURE
/*! \brief Request unused PIO from secure
* \ingroup hardware_pio
*
* \return the PIO number
*/
int pio_request_unused_pio_from_secure(void);
#endif
// PICO_CONFIG: PICO_ALLOW_NONSECURE_GPIO, Allow non-secure to access GPIO, type=bool, default=0, group=hardware_gpio
#ifndef PICO_ALLOW_NONSECURE_GPIO
#define PICO_ALLOW_NONSECURE_GPIO 0
#endif
// PICO_CONFIG: PICO_ADD_NONSECURE_PADS_HELPER, Add non-secure helper functions for PADS_BANK0, type=bool, default=1 on RP2350A A2, group=hardware_pads
#ifndef PICO_ADD_NONSECURE_PADS_HELPER
#define PICO_ADD_NONSECURE_PADS_HELPER PICO_RP2350A && PICO_RP2350_A2_SUPPORTED && PICO_ALLOW_NONSECURE_GPIO
#endif
/*! \brief Set bits in PADS_BANK0
* \ingroup hardware_pads
*
* \param gpio the GPIO number
* \param bits the bits to set
*/
#if PICO_ADD_NONSECURE_PADS_HELPER && PICO_NONSECURE
static inline int pads_bank0_set_bits(uint gpio, uint bits) {
return rom_secure_call(gpio, bits, 0, 0, SECURE_CALL_pads_bank0_set_bits);
}
#else
static inline int pads_bank0_set_bits(uint gpio, uint bits) {
hw_set_bits(&pads_bank0_hw->io[gpio], bits);
return PICO_OK;
}
#endif
/*! \brief Clear bits in PADS_BANK0
* \ingroup hardware_pads
*
* \param gpio the GPIO number
* \param bits the bits to clear
*/
#if PICO_ADD_NONSECURE_PADS_HELPER && PICO_NONSECURE
static inline int pads_bank0_clear_bits(uint gpio, uint bits) {
return rom_secure_call(gpio, bits, 0, 0, SECURE_CALL_pads_bank0_clear_bits);
}
#else
static inline int pads_bank0_clear_bits(uint gpio, uint bits) {
hw_clear_bits(&pads_bank0_hw->io[gpio], bits);
return PICO_OK;
}
#endif
/*! \brief Write masked bits in PADS_BANK0
* \ingroup hardware_pads
*
* \param gpio the GPIO number
* \param bits the bits to write
* \param mask the mask
*/
#if PICO_ADD_NONSECURE_PADS_HELPER && PICO_NONSECURE
static inline int pads_bank0_write_masked(uint gpio, uint bits, uint mask) {
return rom_secure_call(gpio, bits, mask, 0, SECURE_CALL_pads_bank0_write_masked);
}
#else
static inline int pads_bank0_write_masked(uint gpio, uint bits, uint mask) {
hw_write_masked(&pads_bank0_hw->io[gpio], bits, mask);
return PICO_OK;
}
#endif
/*! \brief Read bits in PADS_BANK0
* \ingroup hardware_pads
*
* \param gpio the GPIO number
* \return the bits read
*/
#if PICO_ADD_NONSECURE_PADS_HELPER && PICO_NONSECURE
static inline int pads_bank0_read(uint gpio) {
return rom_secure_call(gpio, 0, 0, 0, SECURE_CALL_pads_bank0_read);
}
#else
static inline int pads_bank0_read(uint gpio) {
return pads_bank0_hw->io[gpio];
}
#endif
// PICO_CONFIG: PICO_ALLOW_NONSECURE_USB, Allow non-secure to access USB, type=bool, default=0, group=hardware_usb
#ifndef PICO_ALLOW_NONSECURE_USB
#define PICO_ALLOW_NONSECURE_USB 0
#endif
// PICO_CONFIG: PICO_ALLOW_NONSECURE_RESETS, Allow non-secure to access RESETS, type=bool, default=0, group=hardware_resets
#ifndef PICO_ALLOW_NONSECURE_RESETS
#define PICO_ALLOW_NONSECURE_RESETS 0
#endif
#if PICO_SECURE
#include "hardware/regs/resets.h"
// PICO_CONFIG: PICO_ALLOW_NONSECURE_RESETS_MASK, Mask of RESETS that can be accessed by non-secure, type=int, default=resets needed for other PICO_ALLOW_NONSECURE_* options, group=hardware_resets
#ifndef PICO_ALLOW_NONSECURE_RESETS_MASK
#define PICO_ALLOW_NONSECURE_RESETS_MASK (PICO_ALLOW_NONSECURE_USB ? RESETS_RESET_USBCTRL_BITS : 0)
#endif
#endif
#if PICO_ALLOW_NONSECURE_RESETS && PICO_NONSECURE
static inline int reset_block_reg_mask(__unused io_rw_32 *reset, uint32_t mask) {
return rom_secure_call(mask, 0, 0, 0, SECURE_CALL_reset_block_mask);
}
static inline int unreset_block_reg_mask(__unused io_rw_32 *reset, uint32_t mask) {
return rom_secure_call(mask, 0, 0, 0, SECURE_CALL_unreset_block_mask);
}
static inline int unreset_block_reg_mask_wait_blocking(__unused io_rw_32 *reset, __unused io_ro_32 *reset_done, uint32_t mask) {
return rom_secure_call(mask, 0, 0, 0, SECURE_CALL_unreset_block_mask_wait_blocking);
}
#endif
#endif // ifndef __riscv
#define BOOT_TYPE_NORMAL 0
#define BOOT_TYPE_BOOTSEL 2
#define BOOT_TYPE_RAM_IMAGE 3

View file

@ -62,6 +62,12 @@ embedded_block:
PICOBIN_IMAGE_TYPE_EXE_CPU_AS_BITS(ARM) | \
PICOBIN_IMAGE_TYPE_EXE_CHIP_AS_BITS(RP2040) | \
CRT0_TBYB_FLAG
#elif PICO_NONSECURE
.hword PICOBIN_IMAGE_TYPE_IMAGE_TYPE_AS_BITS(EXE) | \
PICOBIN_IMAGE_TYPE_EXE_SECURITY_AS_BITS(NS) | \
PICOBIN_IMAGE_TYPE_EXE_CPU_AS_BITS(ARM) | \
PICOBIN_IMAGE_TYPE_EXE_CHIP_AS_BITS(RP2350) | \
CRT0_TBYB_FLAG
#else
.hword PICOBIN_IMAGE_TYPE_IMAGE_TYPE_AS_BITS(EXE) | \
PICOBIN_IMAGE_TYPE_EXE_SECURITY_AS_BITS(S) | \

View file

@ -1,286 +1,4 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
INCLUDE "pico_flash_region.ld"
RAM(rwx) : ORIGIN = 0x21000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
KEEP(*(.embedded_end_block*))
PROVIDE(__flash_binary_end = .);
} > FLASH
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
/* Use blocked ram */
RAM_ORIGIN = 0x21000000;
INCLUDE "memmap_default.ld"

View file

@ -1,287 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
INCLUDE "pico_flash_region.ld"
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.flashtext : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
}
.rodata : {
/* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
/* Vector table goes first in RAM, to avoid large alignment hole */
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.text : {
__ram_text_start__ = .;
*(.init)
*(.text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
__ram_text_end__ = .;
} > RAM AT> FLASH
__ram_text_source__ = LOADADDR(.text);
. = ALIGN(4);
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
KEEP(*(.embedded_end_block*))
PROVIDE(__flash_binary_end = .);
} > FLASH
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
INCLUDE "rp2_common/memmap_copy_to_ram.ld"

View file

@ -1,286 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
INCLUDE "pico_flash_region.ld"
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
KEEP(*(.embedded_end_block*))
PROVIDE(__flash_binary_end = .);
} > FLASH
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
INCLUDE "rp2_common/memmap_default.ld"

View file

@ -1,249 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Note in NO_FLASH builds the entry point for both the bootrom, and debugger
entry (ELF entry point), are *first* in the image, and the vector table
follows immediately afterward. This is because the bootrom enters RAM
binaries directly at their lowest address (preferring main RAM over XIP
cache-as-SRAM if both are used).
*/
.text : {
__logical_binary_start = .;
__reset_start = .;
KEEP (*(.reset))
__reset_end = .;
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
. = ALIGN(256);
KEEP (*(.vectors))
*(.time_critical*)
*(.text*)
. = ALIGN(4);
*(.init)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
} > RAM
.rodata : {
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > RAM
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > RAM
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > RAM
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > RAM
__binary_info_end = .;
. = ALIGN(4);
.data : {
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM
PROVIDE(__data_end__ = .);
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
INCLUDE "rp2_common/memmap_no_flash.ld"

View file

@ -0,0 +1,8 @@
RAM_ORIGIN_DEFAULT = 0x20000000;
RAM_LENGTH_DEFAULT = 256k;
SCRATCH_X_ORIGIN_DEFAULT = 0x20040000;
SCRATCH_X_LENGTH_DEFAULT = 4k;
SCRATCH_Y_ORIGIN_DEFAULT = 0x20041000;
SCRATCH_Y_LENGTH_DEFAULT = 4k;
XIP_RAM_ORIGIN_DEFAULT = 0x15000000;
XIP_RAM_LENGTH_DEFAULT = 16k;

View file

@ -0,0 +1,101 @@
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.flashtext : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
}
.rodata : {
/* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
/* Vector table goes first in RAM, to avoid large alignment hole */
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.text : {
__ram_text_start__ = .;
*(.init)
*(.text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
__ram_text_end__ = .;
} > RAM AT> FLASH
__ram_text_source__ = LOADADDR(.text);
. = ALIGN(4);
}

View file

@ -0,0 +1,109 @@
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
}

View file

@ -0,0 +1,71 @@
SECTIONS
{
/* Note in NO_FLASH builds the entry point for both the bootrom, and debugger
entry (ELF entry point), are *first* in the image, and the vector table
follows immediately afterward. This is because the bootrom enters RAM
binaries directly at their lowest address (preferring main RAM over XIP
cache-as-SRAM if both are used).
*/
.text : {
__logical_binary_start = .;
__reset_start = .;
KEEP (*(.reset))
__reset_end = .;
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
. = ALIGN(256);
KEEP (*(.vectors))
*(.text*)
. = ALIGN(4);
*(.init)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
} > RAM
.rodata : {
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > RAM
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > RAM
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > RAM
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > RAM
__binary_info_end = .;
. = ALIGN(4);
}

View file

@ -0,0 +1,50 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
SECTIONS
{
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}

View file

@ -1,309 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
INCLUDE "pico_flash_region.ld"
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* On Arm, the bootrom expects a VT at the start of the
image by default; on RISC-V, the default is to enter the image at its
lowest address, so an IMAGE_DEF item is required to specify the
nondefault entry point. */
.flash_begin : {
__flash_binary_start = .;
} > FLASH
/* The bootrom will enter the image at the point indicated in your
IMAGE_DEF, which is usually the reset handler of your vector table.
The debugger will use the ELF entry point, which is the _entry_point
symbol, and in our case is *different from the bootrom's entry point.*
This is used to go back through the bootrom on debugger launches only,
to perform the same initial flash setup that would be performed on a
cold boot.
*/
.flashtext : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
. = ALIGN(4);
} > FLASH
/* Note the boot2 section is optional, and should be discarded if there is
no reference to it *inside* the binary, as it is not called by the
bootrom. (The bootrom performs a simple best-effort XIP setup and
leaves it to the binary to do anything more sophisticated.) However
there is still a size limit of 256 bytes, to ensure the boot2 can be
stored in boot RAM.
Really this is a "XIP setup function" -- the name boot2 is historic and
refers to its dual-purpose on RP2040, where it also handled vectoring
from the bootrom into the user image.
*/
.boot2 : {
__boot2_start__ = .;
*(.boot2)
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ <= 256,
"ERROR: Pico second stage bootloader must be no more than 256 bytes in size")
.rodata : {
/* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
/* Vector table goes first in RAM, to avoid large alignment hole */
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.text : {
__ram_text_start__ = .;
*(.init)
*(.text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
__ram_text_end__ = .;
} > RAM AT> FLASH
__ram_text_source__ = LOADADDR(.text);
. = ALIGN(4);
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
. = ALIGN(4);
*(.rodata*)
*(.srodata*)
. = ALIGN(4);
*(.data*)
*(.sdata*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
PROVIDE(__global_pointer$ = . + 2K);
*(.sbss*)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
KEEP(*(.embedded_end_block*))
PROVIDE(__flash_binary_end = .);
} > FLASH =0xaa
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary")
ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary")
/* todo assert on extra code */
}
INCLUDE "rp2_common/memmap_copy_to_ram.ld"

View file

@ -1,302 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
INCLUDE "pico_flash_region.ld"
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
.flash_begin : {
__flash_binary_start = .;
} > FLASH
/* The bootrom will enter the image at the point indicated in your
IMAGE_DEF, which is usually the reset handler of your vector table.
The debugger will use the ELF entry point, which is the _entry_point
symbol, and in our case is *different from the bootrom's entry point.*
This is used to go back through the bootrom on debugger launches only,
to perform the same initial flash setup that would be performed on a
cold boot.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*libgcc.a:cmse_nonsecure_call.o
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
/* Note the boot2 section is optional, and should be discarded if there is
no reference to it *inside* the binary, as it is not called by the
bootrom. (The bootrom performs a simple best-effort XIP setup and
leaves it to the binary to do anything more sophisticated.) However
there is still a size limit of 256 bytes, to ensure the boot2 can be
stored in boot RAM.
Really this is a "XIP setup function" -- the name boot2 is historic and
refers to its dual-purpose on RP2040, where it also handled vectoring
from the bootrom into the user image.
*/
.boot2 : {
__boot2_start__ = .;
*(.boot2)
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ <= 256,
"ERROR: Pico second stage bootloader must be no more than 256 bytes in size")
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
*(.srodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
*(.sdata*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
PROVIDE(__global_pointer$ = . + 2K);
*(.sbss*)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
.flash_end : {
KEEP(*(.embedded_end_block*))
PROVIDE(__flash_binary_end = .);
} > FLASH =0xaa
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary")
ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary")
/* todo assert on extra code */
}
INCLUDE "rp2_common/memmap_default.ld"

View file

@ -1,256 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Note unlike RP2040, we start the image with a vector table even for
NO_FLASH builds. On Arm, the bootrom expects a VT at the start of the
image by default; on RISC-V, the default is to enter the image at its
lowest address, so an IMAGE_DEF item is required to specify the
nondefault entry point. */
.text : {
__logical_binary_start = .;
/* Vectors require 512-byte alignment on v8-M when >48 IRQs are used,
so we would waste RAM if the vector table were not at the
start. */
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
__reset_start = .;
KEEP (*(.reset))
__reset_end = .;
*(.time_critical*)
*(.text*)
. = ALIGN(4);
*(.init)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
} > RAM
.rodata : {
. = ALIGN(4);
*(.rodata*)
*(.srodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > RAM
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > RAM
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > RAM
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > RAM
__binary_info_end = .;
. = ALIGN(4);
.data : {
__data_start__ = .;
*(vtable)
*(.data*)
*(.sdata*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM
PROVIDE(__data_end__ = .);
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
PROVIDE(__global_pointer$ = . + 2K);
*(.sbss*)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = ORIGIN(RAM) + LENGTH(RAM);
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary")
ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary")
/* todo assert on extra code */
}
INCLUDE "rp2_common/memmap_no_flash.ld"

View file

@ -0,0 +1,8 @@
RAM_ORIGIN_DEFAULT = 0x20000000;
RAM_LENGTH_DEFAULT = 512k;
SCRATCH_X_ORIGIN_DEFAULT = 0x20080000;
SCRATCH_X_LENGTH_DEFAULT = 4k;
SCRATCH_Y_ORIGIN_DEFAULT = 0x20081000;
SCRATCH_Y_LENGTH_DEFAULT = 4k;
XIP_RAM_ORIGIN_DEFAULT = 0x13FFC000;
XIP_RAM_LENGTH_DEFAULT = 16k;

View file

@ -0,0 +1,117 @@
SECTIONS
{
/* On Arm, the bootrom expects a VT at the start of the
image by default; on RISC-V, the default is to enter the image at its
lowest address, so an IMAGE_DEF item is required to specify the
nondefault entry point. */
.flash_begin : {
__flash_binary_start = .;
} > FLASH
/* The bootrom will enter the image at the point indicated in your
IMAGE_DEF, which is usually the reset handler of your vector table.
The debugger will use the ELF entry point, which is the _entry_point
symbol, and in our case is *different from the bootrom's entry point.*
This is used to go back through the bootrom on debugger launches only,
to perform the same initial flash setup that would be performed on a
cold boot.
*/
.flashtext : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
. = ALIGN(4);
} > FLASH
/* Note the boot2 section is optional, and should be discarded if there is
no reference to it *inside* the binary, as it is not called by the
bootrom. (The bootrom performs a simple best-effort XIP setup and
leaves it to the binary to do anything more sophisticated.) However
there is still a size limit of 256 bytes, to ensure the boot2 can be
stored in boot RAM.
Really this is a "XIP setup function" -- the name boot2 is historic and
refers to its dual-purpose on RP2040, where it also handled vectoring
from the bootrom into the user image.
*/
.boot2 : {
__boot2_start__ = .;
*(.boot2)
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ <= 256,
"ERROR: Pico second stage bootloader must be no more than 256 bytes in size")
.rodata : {
/* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
/* Vector table goes first in RAM, to avoid large alignment hole */
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.text : {
__ram_text_start__ = .;
*(.init)
*(.text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
__ram_text_end__ = .;
} > RAM AT> FLASH
__ram_text_source__ = LOADADDR(.text);
. = ALIGN(4);
}

View file

@ -0,0 +1,121 @@
SECTIONS
{
.flash_begin : {
__flash_binary_start = .;
} > FLASH
/* The bootrom will enter the image at the point indicated in your
IMAGE_DEF, which is usually the reset handler of your vector table.
The debugger will use the ELF entry point, which is the _entry_point
symbol, and in our case is *different from the bootrom's entry point.*
This is used to go back through the bootrom on debugger launches only,
to perform the same initial flash setup that would be performed on a
cold boot.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*libgcc.a:cmse_nonsecure_call.o
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
/* Note the boot2 section is optional, and should be discarded if there is
no reference to it *inside* the binary, as it is not called by the
bootrom. (The bootrom performs a simple best-effort XIP setup and
leaves it to the binary to do anything more sophisticated.) However
there is still a size limit of 256 bytes, to ensure the boot2 can be
stored in boot RAM.
Really this is a "XIP setup function" -- the name boot2 is historic and
refers to its dual-purpose on RP2040, where it also handled vectoring
from the bootrom into the user image.
*/
.boot2 : {
__boot2_start__ = .;
*(.boot2)
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ <= 256,
"ERROR: Pico second stage bootloader must be no more than 256 bytes in size")
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
*(.srodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
}

View file

@ -0,0 +1,73 @@
SECTIONS
{
/* Note unlike RP2040, we start the image with a vector table even for
NO_FLASH builds. On Arm, the bootrom expects a VT at the start of the
image by default; on RISC-V, the default is to enter the image at its
lowest address, so an IMAGE_DEF item is required to specify the
nondefault entry point. */
.text : {
__logical_binary_start = .;
/* Vectors require 512-byte alignment on v8-M when >48 IRQs are used,
so we would waste RAM if the vector table were not at the
start. */
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.embedded_block))
__embedded_block_end = .;
__reset_start = .;
KEEP (*(.reset))
__reset_end = .;
*(.text*)
. = ALIGN(4);
*(.init)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
} > RAM
.rodata : {
. = ALIGN(4);
*(.rodata*)
*(.srodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > RAM
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > RAM
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > RAM
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > RAM
__binary_info_end = .;
. = ALIGN(4);
}

View file

@ -0,0 +1,7 @@
SECTIONS
{
ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary")
ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary")
/* todo assert on extra code */
}

View file

@ -123,11 +123,11 @@ int cyw43_spi_init(cyw43_int_t *self) {
pio_sm_config config = SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC(bus_data->pio_offset);
sm_config_set_clkdiv_int_frac8(&config, cyw43_pio_clock_div_int, cyw43_pio_clock_div_frac8);
hw_write_masked(&pads_bank0_hw->io[CYW43_PIN_WL_CLOCK],
pads_bank0_write_masked(CYW43_PIN_WL_CLOCK,
(uint)PADS_DRIVE_STRENGTH << PADS_BANK0_GPIO0_DRIVE_LSB,
PADS_BANK0_GPIO0_DRIVE_BITS
);
hw_write_masked(&pads_bank0_hw->io[CYW43_PIN_WL_CLOCK],
pads_bank0_write_masked(CYW43_PIN_WL_CLOCK,
(uint)1 << PADS_BANK0_GPIO0_SLEWFAST_LSB,
PADS_BANK0_GPIO0_SLEWFAST_BITS
);

View file

@ -66,7 +66,7 @@ uint32_t cyw43_irq_init(__unused void *param) {
#endif
gpio_add_raw_irq_handler_with_order_priority(CYW43_PIN_WL_HOST_WAKE, cyw43_gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
cyw43_set_irq_enabled(true);
irq_set_enabled(IO_IRQ_BANK0, true);
irq_set_enabled(DEFAULT_IO_IRQ_BANK0, true);
return 0;
}

View file

@ -325,7 +325,7 @@ static void initialise_rand(void) {
spin_unlock(lock, save);
}
uint64_t get_rand_64(void) {
uint64_t __weak get_rand_64(void) {
if (!rng_initialised) {
// Do not provide 'RNs' until the system has been initialised. Note:
// The first initialisation can be quite time-consuming depending on

View file

@ -58,7 +58,12 @@ void runtime_run_per_core_initializers(void);
#define PICO_RUNTIME_INIT_FUNC(func, priority_string) __pre_init func, priority_string
#endif
#endif
#if PICO_NONSECURE
// hw init cannot be done by non-secure
#define PICO_RUNTIME_INIT_FUNC_HW(func, priority_string)
#else
#define PICO_RUNTIME_INIT_FUNC_HW(func, priority_string) PICO_RUNTIME_INIT_FUNC(func, priority_string)
#endif
#define PICO_RUNTIME_INIT_FUNC_RUNTIME(func, priority_string) PICO_RUNTIME_INIT_FUNC(func, priority_string)
// priority strings are of the form 00000->99999; we want the per core stuff all at the end, so prefix with ZZZZZ which is clearly after 99999
#define PICO_RUNTIME_INIT_FUNC_PER_CORE(func, priority_string) PICO_RUNTIME_INIT_FUNC(func, "ZZZZZ." priority_string)

View file

@ -64,13 +64,13 @@ extern "C" {
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_BOOTROM_RESET
#if PICO_RP2040 || (!LIB_PICO_MULTICORE && PICO_NO_FLASH)
#if PICO_RP2040 || (!LIB_PICO_MULTICORE && PICO_NO_FLASH) || PICO_NONSECURE
#define PICO_RUNTIME_SKIP_INIT_BOOTROM_RESET 1
#endif
#endif
#ifndef PICO_RUNTIME_NO_INIT_BOOTROM_RESET
#if PICO_RP2040 || (!LIB_PICO_MULTICORE && PICO_NO_FLASH)
#if PICO_RP2040 || (!LIB_PICO_MULTICORE && PICO_NO_FLASH) || PICO_NONSECURE
#define PICO_RUNTIME_NO_INIT_BOOTROM_RESET 1
#endif
#endif
@ -89,13 +89,13 @@ void runtime_init_bootrom_reset(void);
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_PER_CORE_BOOTROM_RESET, Skip calling of `runtime_init_per_core_bootrom_reset` function during per-core init, type=bool, default=1 on RP2040, group=pico_runtime_init
// PICO_CONFIG: PICO_RUNTIME_NO_INIT_PER_CORE_BOOTROM_RESET, Do not include SDK implementation of `runtime_init_per_core_bootrom_reset` function, type=bool, default=1 on RP2040, group=pico_runtime_init
#ifndef PICO_RUNTIME_SKIP_INIT_PER_CORE_BOOTROM_RESET
#if PICO_RP2040
#if PICO_RP2040 || PICO_NONSECURE
#define PICO_RUNTIME_SKIP_INIT_PER_CORE_BOOTROM_RESET 1
#endif
#endif
#ifndef PICO_RUNTIME_NO_INIT_PER_CORE_BOOTROM_RESET
#if PICO_RP2040
#if PICO_RP2040 || PICO_NONSECURE
#define PICO_RUNTIME_NO_INIT_PER_CORE_BOOTROM_RESET 1
#endif
#endif
@ -317,8 +317,12 @@ void runtime_init_spin_locks_reset(void);
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_BOOT_LOCKS_RESET
#if PICO_NONSECURE
#define PICO_RUNTIME_SKIP_INIT_BOOT_LOCKS_RESET 1
#else
#define PICO_RUNTIME_SKIP_INIT_BOOT_LOCKS_RESET 0
#endif
#endif
#ifndef __ASSEMBLER__
void runtime_init_boot_locks_reset(void);
#endif
@ -333,13 +337,113 @@ void runtime_init_boot_locks_reset(void);
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE
#if PICO_NONSECURE
#define PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE 1
#else
#define PICO_RUNTIME_SKIP_INIT_BOOTROM_LOCKING_ENABLE 0
#endif
#endif
#ifndef __ASSEMBLER__
void runtime_init_bootrom_locking_enable(void);
#endif
// ------------------------------
// Set default bootrom secure callback
// ------------------------------
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_BOOTROM_API_CALLBACK, Skip calling of `runtime_init_rom_set_default_callback` function during runtime init, type=bool, default=0, group=pico_runtime_init
#ifndef PICO_RUNTIME_INIT_BOOTROM_API_CALLBACK
#define PICO_RUNTIME_INIT_BOOTROM_API_CALLBACK "01020"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_BOOTROM_API_CALLBACK
#define PICO_RUNTIME_SKIP_INIT_BOOTROM_API_CALLBACK !PICO_SECURE
#endif
#ifndef PICO_RUNTIME_NO_INIT_BOOTROM_API_CALLBACK
#define PICO_RUNTIME_NO_INIT_BOOTROM_API_CALLBACK !PICO_SECURE
#endif
// ------------------------------
// Initialise non-secure claimed resources
// ------------------------------
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_NONSECURE_CLAIMS, Skip calling of `runtime_init_nonsecure_claims` function during runtime init, type=bool, default=0, group=pico_runtime_init
#ifndef PICO_RUNTIME_INIT_NONSECURE_CLAIMS
#define PICO_RUNTIME_INIT_NONSECURE_CLAIMS "01020"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_NONSECURE_CLAIMS
#define PICO_RUNTIME_SKIP_INIT_NONSECURE_CLAIMS !PICO_NONSECURE
#endif
#ifndef PICO_RUNTIME_NO_INIT_NONSECURE_CLAIMS
#define PICO_RUNTIME_NO_INIT_NONSECURE_CLAIMS !PICO_NONSECURE
#endif
// ------------------------------
// Initialise non-secure stdio
// ------------------------------
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_NONSECURE_STDIO, Skip calling of `runtime_init_nonsecure_stdio` function during runtime init, type=bool, default=0, group=pico_runtime_init
#ifndef PICO_RUNTIME_INIT_NONSECURE_STDIO
#define PICO_RUNTIME_INIT_NONSECURE_STDIO "20000"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_NONSECURE_STDIO
#define PICO_RUNTIME_SKIP_INIT_NONSECURE_STDIO !PICO_NONSECURE
#endif
#ifndef PICO_RUNTIME_NO_INIT_NONSECURE_STDIO
#define PICO_RUNTIME_NO_INIT_NONSECURE_STDIO !PICO_NONSECURE
#endif
// ------------------------------
// Initialise non-secure clocks
// ------------------------------
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_NONSECURE_CLOCKS, Skip calling of `runtime_init_nonsecure_clocks` function during runtime init, type=bool, default=0, group=pico_runtime_init
#ifndef PICO_RUNTIME_INIT_NONSECURE_CLOCKS
#define PICO_RUNTIME_INIT_NONSECURE_CLOCKS "00500"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_NONSECURE_CLOCKS
#define PICO_RUNTIME_SKIP_INIT_NONSECURE_CLOCKS !PICO_NONSECURE
#endif
#ifndef PICO_RUNTIME_NO_INIT_NONSECURE_CLOCKS
#define PICO_RUNTIME_NO_INIT_NONSECURE_CLOCKS !PICO_NONSECURE
#endif
// ------------------------------
// Initialise non-secure coprocessors
// ------------------------------
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_NONSECURE_COPROCESSORS, Skip calling of `runtime_init_nonsecure_coprocessors` function during runtime init, type=bool, default=0, group=pico_runtime_init
#ifndef PICO_RUNTIME_INIT_NONSECURE_COPROCESSORS
#define PICO_RUNTIME_INIT_NONSECURE_COPROCESSORS "00210"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_NONSECURE_COPROCESSORS
#define PICO_RUNTIME_SKIP_INIT_NONSECURE_COPROCESSORS !PICO_SECURE
#endif
#ifndef PICO_RUNTIME_NO_INIT_NONSECURE_COPROCESSORS
#define PICO_RUNTIME_NO_INIT_NONSECURE_COPROCESSORS !PICO_SECURE
#endif
// ------------------------------
// Initialise non-secure accessctrl
// ------------------------------
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_NONSECURE_ACCESSCTRL_AND_IRQS, Skip calling of `runtime_init_nonsecure_accessctrl_and_irqs` function during runtime init, type=bool, default=0, group=pico_runtime_init
#ifndef PICO_RUNTIME_INIT_NONSECURE_ACCESSCTRL_AND_IRQS
#define PICO_RUNTIME_INIT_NONSECURE_ACCESSCTRL_AND_IRQS "00220"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_NONSECURE_ACCESSCTRL_AND_IRQS
#define PICO_RUNTIME_SKIP_INIT_NONSECURE_ACCESSCTRL_AND_IRQS !PICO_SECURE
#endif
#ifndef PICO_RUNTIME_NO_INIT_NONSECURE_ACCESSCTRL_AND_IRQS
#define PICO_RUNTIME_NO_INIT_NONSECURE_ACCESSCTRL_AND_IRQS !PICO_SECURE
#endif
// PICO_RUNTIME_INIT_MUTEX is registered automatically by pico_sync
// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_MUTEX, Skip calling of `runtime_init_mutex` function during runtime init, type=bool, default=0, group=pico_runtime_init
// PICO_CONFIG: PICO_RUNTIME_NO_INIT_MUTEX, Do not include SDK implementation of `runtime_init_mutex` function, type=bool, default=0, group=pico_runtime_init

View file

@ -0,0 +1,14 @@
load("//bazel:defs.bzl", "compatible_with_rp2")
package(default_visibility = ["//visibility:public"])
cc_library(
name = "pico_secure",
srcs = ["secure.c"],
hdrs = ["include/pico/secure.h"],
includes = ["include"],
target_compatible_with = compatible_with_rp2(),
deps = [
"//src/rp2_common/pico_bootrom",
],
)

View file

@ -0,0 +1,160 @@
if (NOT TARGET pico_secure)
pico_add_library(pico_secure)
target_include_directories(pico_secure_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(pico_secure INTERFACE
${CMAKE_CURRENT_LIST_DIR}/secure.c)
pico_mirrored_target_link_libraries(pico_secure INTERFACE
hardware_exception
pico_bootrom)
# pico_set_security_options(SECURE_TARGET NONSECURE_TARGET <OPTIONS>...)
# \brief_nodesc\ Set matching security options for a secure and non-secure target
#
# Set matching security options for a secure and non-secure target, so they have a compatible set of features.
#
# Also sets PICO_SECURE=1 and PICO_NONSECURE=1 on the secure and non-secure targets respectively, along with
# any other required defines (eg PICO_USE_STACK_GUARDS=1 on the secure target).
#
# The options are:
# - STDIO: Allow non-secure to use secure stdio
# - RAND: Allow non-secure to get random numbers
# - DMA: Allow non-secure to request DMA channels
# - USER_IRQ: Allow non-secure to request user IRQs
# - PIO: Allow non-secure to request PIOs
# - GPIO: Allow non-secure to access GPIOs assigned to non-secure (eg with gpio_assign_to_ns)
# - USB: Allow non-secure to access USB
# - RESETS: Allow non-secure to access resets specified by PICO_ALLOW_NONSECURE_RESETS_MASK (automatically set if USB is set)
# - NONSECURE_TIMER <index>: Assign specified timer to non-secure
#
# \param\ SECURE_TARGET The secure target
# \param\ NONSECURE_TARGET The non-secure target
# \param\ OPTIONS The options to set
function(pico_set_security_options SECURE_TARGET NONSECURE_TARGET)
set(options STDIO RAND DMA USER_IRQ PIO GPIO USB RESETS)
set(oneValueArgs NONSECURE_TIMER)
cmake_parse_arguments(PARSE_ARGV 2 OPTS "${options}" "${oneValueArgs}" "")
target_compile_definitions(${SECURE_TARGET} PRIVATE
PICO_SECURE=1
# Stack guards are required
PICO_USE_STACK_GUARDS=1
)
target_compile_definitions(${NONSECURE_TARGET} PRIVATE
PICO_NONSECURE=1
)
# Options that require resets
if ((NOT OPTS_RESETS) AND OPTS_USB)
set(OPTS_RESETS 1)
endif()
foreach(arg IN LISTS options)
if (OPTS_${arg})
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_ALLOW_NONSECURE_${arg}=1)
target_compile_definitions(${NONSECURE_TARGET} PRIVATE PICO_ALLOW_NONSECURE_${arg}=1)
endif()
endforeach()
if (OPTS_NONSECURE_TIMER)
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_ASSIGN_NONSECURE_TIMER=${OPTS_NONSECURE_TIMER})
target_compile_definitions(${NONSECURE_TARGET} PRIVATE PICO_DEFAULT_TIMER=${OPTS_NONSECURE_TIMER})
endif()
endfunction()
# pico_set_security_ram_split(SECURE_TARGET NONSECURE_TARGET <OPTIONS>...)
# \brief_nodesc\ Set ram split for a secure and non-secure target
#
# Set ram split for a secure and non-secure target, so they don't use the same memory.
#
# Each split type option requires arguments to specify the memory sizes. You can only select
# one split type
#
# The split types available are:
# - SIMPLE <SECURE_LENGTH> <SECURE_SCRATCH_LENGTH>: Secure using start of main SRAM, NonSecure using end of main SRAM and scratch
# - SCRATCH_EACH <SECURE_LENGTH>: Secure using start of main SRAM plus scratch X as stack, NonSecure using end of main SRAM plus scratch Y as stack
# - SECURE_SCRATCH <SECURE_LENGTH> <NONSECURE_SCRATCH_LENGTH>: Secure using start of main SRAM plus all of scratch, NonSecure using end of main SRAM
#
# Additional options are:
# - NO_FLASH: Assumes NS VTOR is at start of it's SRAM region, rather than at the start of flash
#
# \param\ SECURE_TARGET The secure target
# \param\ NONSECURE_TARGET The non-secure target
# \param\ OPTIONS The options to set
function(pico_set_security_ram_split SECURE_TARGET NONSECURE_TARGET)
set(options NO_FLASH)
set(multiValueArgs SIMPLE SCRATCH_EACH SECURE_SCRATCH)
cmake_parse_arguments(PARSE_ARGV 2 OPTS "${options}" "" "${multiValueArgs}")
set(HAS_SPLIT_TYPE FALSE)
foreach(arg IN LISTS multiValueArgs)
if (OPTS_${arg})
if (HAS_SPLIT_TYPE)
message(FATAL_ERROR "Multiple split types passed to pico_set_security_ram_split")
endif()
set(HAS_SPLIT_TYPE TRUE)
endif()
endforeach()
if (NOT HAS_SPLIT_TYPE)
message(FATAL_ERROR "No split type passed to pico_set_security_ram_split")
endif()
if (OPTS_SIMPLE)
list(GET OPTS_SIMPLE 0 SECURE_LENGTH)
list(GET OPTS_SIMPLE 1 SECURE_SCRATCH_LENGTH)
pico_set_linker_script_var(${SECURE_TARGET} RAM_ORIGIN "RAM_ORIGIN_DEFAULT")
pico_set_linker_script_var(${SECURE_TARGET} RAM_LENGTH "${SECURE_LENGTH}-(${SECURE_SCRATCH_LENGTH}*2)")
pico_set_linker_script_var(${SECURE_TARGET} SCRATCH_X_ORIGIN "RAM_ORIGIN+RAM_LENGTH")
pico_set_linker_script_var(${SECURE_TARGET} SCRATCH_X_LENGTH ${SECURE_SCRATCH_LENGTH})
pico_set_linker_script_var(${SECURE_TARGET} SCRATCH_Y_ORIGIN "SCRATCH_X_ORIGIN+SCRATCH_X_LENGTH")
pico_set_linker_script_var(${SECURE_TARGET} SCRATCH_Y_LENGTH SCRATCH_X_LENGTH)
pico_set_linker_script_var(${NONSECURE_TARGET} RAM_ORIGIN "RAM_ORIGIN_DEFAULT+${SECURE_LENGTH}")
pico_set_linker_script_var(${NONSECURE_TARGET} RAM_LENGTH "RAM_LENGTH_DEFAULT-${SECURE_LENGTH}")
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_SECURITY_SPLIT_SIMPLE=1)
elseif(OPTS_SCRATCH_EACH)
list(GET OPTS_SCRATCH_EACH 0 SECURE_LENGTH)
pico_set_linker_script_var(${SECURE_TARGET} RAM_ORIGIN "RAM_ORIGIN_DEFAULT")
pico_set_linker_script_var(${SECURE_TARGET} RAM_LENGTH "${SECURE_LENGTH}-SCRATCH_X_LENGTH_DEFAULT")
pico_set_linker_script_var(${SECURE_TARGET} SCRATCH_X_ORIGIN "RAM_ORIGIN+RAM_LENGTH")
pico_set_linker_script_var(${SECURE_TARGET} SCRATCH_Y_ORIGIN "SCRATCH_X_ORIGIN_DEFAULT")
pico_set_linker_script_var(${NONSECURE_TARGET} RAM_ORIGIN "RAM_ORIGIN_DEFAULT+${SECURE_LENGTH}")
pico_set_linker_script_var(${NONSECURE_TARGET} RAM_LENGTH "RAM_LENGTH_DEFAULT-${SECURE_LENGTH}-SCRATCH_X_LENGTH_DEFAULT")
pico_set_linker_script_var(${NONSECURE_TARGET} SCRATCH_X_ORIGIN "RAM_ORIGIN+RAM_LENGTH")
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_SECURITY_SPLIT_SCRATCH_EACH=1)
elseif(OPTS_SECURE_SCRATCH)
list(GET OPTS_SECURE_SCRATCH 0 SECURE_LENGTH)
list(GET OPTS_SECURE_SCRATCH 1 NONSECURE_SCRATCH_LENGTH)
pico_set_linker_script_var(${SECURE_TARGET} RAM_LENGTH "${SECURE_LENGTH}")
pico_set_linker_script_var(${NONSECURE_TARGET} RAM_ORIGIN "RAM_ORIGIN_DEFAULT+${SECURE_LENGTH}")
pico_set_linker_script_var(${NONSECURE_TARGET} RAM_LENGTH "RAM_LENGTH_DEFAULT-${SECURE_LENGTH}-(${NONSECURE_SCRATCH_LENGTH}*2)")
pico_set_linker_script_var(${NONSECURE_TARGET} SCRATCH_X_ORIGIN "RAM_ORIGIN+RAM_LENGTH")
pico_set_linker_script_var(${NONSECURE_TARGET} SCRATCH_X_LENGTH ${NONSECURE_SCRATCH_LENGTH})
pico_set_linker_script_var(${NONSECURE_TARGET} SCRATCH_Y_ORIGIN "SCRATCH_X_ORIGIN+SCRATCH_X_LENGTH")
pico_set_linker_script_var(${NONSECURE_TARGET} SCRATCH_Y_LENGTH SCRATCH_X_LENGTH)
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_SECURITY_SPLIT_SECURE_SCRATCH=1)
endif()
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_SECURITY_SPLIT_CONFIGURED=1)
if (OPTS_NO_FLASH)
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_SECURITY_SPLIT_NO_FLASH=1)
else()
target_compile_definitions(${SECURE_TARGET} PRIVATE PICO_SECURITY_SPLIT_NO_FLASH=0)
endif()
endfunction()
endif()

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2025 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_SECURE_H
#define _PICO_SECURE_H
#include "pico.h"
#include "pico/bootrom.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief Launch non-secure binary
* \ingroup pico_secure
*
* \note The secure binary must have already set it's stack limits, using PICO_USE_STACK_GUARDS or similar
*
* \param vtor_address The vector table address of the non-secure binary
* \param stack_limit The stack limit of the non-secure binary
*/
void secure_launch_nonsecure_binary(uint32_t vtor_address, uint32_t stack_limit);
/*! \brief Configure SAU region
* \ingroup pico_secure
*
* \param region The region to configure
* \param base The base address of the region
* \param limit The limit address of the region
* \param enabled Whether the region is enabled
* \param nsc Whether the region is non-secure callable
*/
void secure_sau_configure_region(uint region, uint32_t base, uint32_t limit, bool enabled, bool nsc);
#if defined(PICO_SECURITY_SPLIT_CONFIGURED)
/*! \brief Configure default SAU regions
* \ingroup pico_secure
*
* Configures the default security split configuration, based on the split configured with pico_set_security_ram_split
*/
void secure_sau_configure_split(void);
/*! \brief Launch non-secure binary from default location
* \ingroup pico_secure
*
* Launches non-secure binary from the default location, based on the split configured with pico_set_security_ram_split
*/
void secure_launch_nonsecure_binary_default(void);
#endif
/*! \brief Set SAU enabled
* \ingroup pico_secure
*
* Set SAU enabled, with appropriate memory barriers
*
* \param enabled Whether the SAU is enabled
*/
void secure_sau_set_enabled(bool enabled);
typedef void (*secure_hardfault_callback_t)(void);
/*! \brief Install default hardfault handler
* \ingroup pico_secure
*
* \param callback The callback to call when a hardfault occurs after printing the information
*/
void secure_install_default_hardfault_handler(secure_hardfault_callback_t callback);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,239 @@
/*
* Copyright (c) 2025 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "pico/secure.h"
#include "pico/runtime_init.h"
#include "hardware/timer.h"
#include "hardware/irq.h"
#include "hardware/exception.h"
#include "hardware/structs/scb.h"
#include "hardware/structs/sau.h"
#include "hardware/structs/m33.h"
#include "hardware/structs/accessctrl.h"
void __attribute__((noreturn)) secure_launch_nonsecure_binary(uint32_t vtor_address, uint32_t stack_limit) {
uint32_t *vtor = (uint32_t*)vtor_address;
uint32_t stack_pointer = *(vtor + 0);
uint32_t entry_point = *(vtor + 1);
scb_ns_hw->vtor = vtor_address;
pico_default_asm(
"msr msp_ns, %0\n"
"msr msplim_ns, %1\n"
"movs r1, %2\n"
"bxns r1"
:
: "r" (stack_pointer),
"r" (stack_limit),
"r" (entry_point & ~1) // make sure thumb bit is clear for blxns
);
__builtin_unreachable();
}
void secure_sau_configure_region(uint region, uint32_t base, uint32_t limit, bool enabled, bool nsc) {
sau_hw->rnr = region;
sau_hw->rbar = base & M33_SAU_RBAR_BADDR_BITS;
sau_hw->rlar = ((limit-1) & M33_SAU_RLAR_LADDR_BITS) | (nsc ? M33_SAU_RLAR_NSC_BITS : 0) | (enabled ? M33_SAU_RLAR_ENABLE_BITS : 0);
}
void secure_sau_set_enabled(bool enabled) {
uint32_t save = save_and_disable_interrupts();
__dmb();
if (enabled)
sau_hw->ctrl |= M33_SAU_CTRL_ENABLE_BITS;
else
sau_hw->ctrl &= ~M33_SAU_CTRL_ENABLE_BITS;
__dsb();
__isb();
restore_interrupts_from_disabled(save);
}
#if defined(PICO_SECURITY_SPLIT_CONFIGURED)
static uint32_t nonsecure_ram_start = 0;
void secure_sau_configure_split() {
#if !PICO_SECURITY_SPLIT_NO_FLASH
// XIP is NS Code
secure_sau_configure_region(0, XIP_BASE, XIP_END, true, false);
#endif
#if defined(PICO_SECURITY_SPLIT_SIMPLE)
// SRAM after secure stack is NS data
extern uint32_t __StackTop;
secure_sau_configure_region(1, (uint32_t)&__StackTop, SRAM_END, true, false);
nonsecure_ram_start = (uint32_t)&__StackTop;
#elif defined(PICO_SECURITY_SPLIT_SCRATCH_EACH)
// Main SRAM after secure scratch X is NS data
extern uint32_t __StackOneTop;
secure_sau_configure_region(1, (uint32_t)&__StackOneTop, SRAM_STRIPED_END, true, false);
nonsecure_ram_start = (uint32_t)&__StackOneTop;
// Scratch after secure stack in NS stack
extern uint32_t __StackTop;
secure_sau_configure_region(2, (uint32_t)&__StackTop, SRAM_END, true, false);
#elif defined(PICO_SECURITY_SPLIT_SECURE_SCRATCH)
// Main SRAM after secure heap is NS data
extern uint32_t __HeapLimit;
secure_sau_configure_region(1, (uint32_t)&__HeapLimit, SRAM_STRIPED_END, true, false);
nonsecure_ram_start = (uint32_t)&__HeapLimit;
#endif
}
void __attribute__((noreturn)) secure_launch_nonsecure_binary_default() {
#if PICO_SECURITY_SPLIT_NO_FLASH
uint32_t nonsecure_vtor = nonsecure_ram_start;
#else
uint32_t nonsecure_vtor = XIP_BASE;
#endif
#if defined(PICO_SECURITY_SPLIT_SIMPLE)
// Nonsecure running from XIP, stack limit is bottom of scratch
secure_launch_nonsecure_binary(nonsecure_vtor, SRAM_SCRATCH_X_BASE);
#elif defined(PICO_SECURITY_SPLIT_SCRATCH_EACH)
// Nonsecure running from XIP, stack limit is bottom of scratch Y
secure_launch_nonsecure_binary(nonsecure_vtor, SRAM_SCRATCH_Y_BASE);
#elif defined(PICO_SECURITY_SPLIT_SECURE_SCRATCH)
// Nonsecure running from XIP, stack limit is secure heap limit
extern uint32_t __HeapLimit;
secure_launch_nonsecure_binary(nonsecure_vtor, (uint32_t)&__HeapLimit);
#endif
}
#endif
static secure_hardfault_callback_t hardfault_callback = NULL;
static void secure_hardfault_handler(void) {
printf("Hard fault occurred\n");
// # First eight values on stack will always be:
// # r0, r1, r2, r3, r12, LR, pc, xPSR
uint32_t sp;
pico_default_asm_volatile(
"mrs %0, msp_ns"
: "=r" (sp)
);
printf("sp: %08x\n", sp);
printf("r0: %08x\n", *((uint32_t*)sp + 0));
printf("r1: %08x\n", *((uint32_t*)sp + 1));
printf("r2: %08x\n", *((uint32_t*)sp + 2));
printf("r3: %08x\n", *((uint32_t*)sp + 3));
printf("r12: %08x\n", *((uint32_t*)sp + 4));
printf("lr: %08x\n", *((uint32_t*)sp + 5));
printf("pc: %08x\n", *((uint32_t*)sp + 6));
printf("xPSR: %08x\n", *((uint32_t*)sp + 7));
if (scb_hw->hfsr & M33_HFSR_DEBUGEVT_BITS) printf("HardFault: Debug Event\n");
if (scb_hw->hfsr & M33_HFSR_FORCED_BITS) printf("HardFault: Forced\n");
if (scb_hw->hfsr & M33_HFSR_VECTTBL_BITS) printf("HardFault: Vector Table Read Error\n");
if (m33_hw->sfsr & M33_SFSR_LSERR_BITS) printf("SecureFault: Error occurred during lazy state activation/deactivation\n");
if (m33_hw->sfsr & M33_SFSR_LSPERR_BITS) printf("SecureFault: Error occurred during lazy preservation of floating-point state\n");
if (m33_hw->sfsr & M33_SFSR_INVTRAN_BITS) printf("SecureFault: Secure branched to Non-secure code\n");
if (m33_hw->sfsr & M33_SFSR_AUVIOL_BITS) printf("SecureFault: Non-secure accessed Secure memory\n");
if (m33_hw->sfsr & M33_SFSR_INVER_BITS) printf("SecureFault: Invalid Non-secure exception state when returning\n");
if (m33_hw->sfsr & M33_SFSR_INVIS_BITS) printf("SecureFault: Invalid integrity signature in exception stack\n");
if (m33_hw->sfsr & M33_SFSR_INVEP_BITS) printf("SecureFault: Non-secure branched to Secure code\n");
if (m33_hw->sfsr & M33_SFSR_SFARVALID_BITS) printf("SecureFault address: %08x\n", m33_hw->sfar);
if (scb_hw->cfsr & M33_CFSR_UFSR_DIVBYZERO_BITS) printf("UsageFault: Division by zero\n");
if (scb_hw->cfsr & M33_CFSR_UFSR_UNALIGNED_BITS) printf("UsageFault: Unaligned access\n");
if (scb_hw->cfsr & M33_CFSR_UFSR_STKOF_BITS) printf("UsageFault: Stack overflow\n");
if (scb_hw->cfsr & M33_CFSR_UFSR_NOCP_BITS) printf("UsageFault: No Coprocessor\n");
if (scb_hw->cfsr & M33_CFSR_UFSR_INVPC_BITS) printf("UsageFault: Invalid PC\n");
if (scb_hw->cfsr & M33_CFSR_UFSR_INVSTATE_BITS) printf("UsageFault: Invalid state\n");
if (scb_hw->cfsr & M33_CFSR_UFSR_UNDEFINSTR_BITS) printf("UsageFault: Undefined instruction\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_LSPERR_BITS) printf("BusFault: Error occurred during lazy preservation of floating-point state\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_STKERR_BITS) printf("BusFault: Error occurred during exception entry stacking\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_UNSTKERR_BITS) printf("BusFault: Error occurred during exception return unstacking\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_IMPRECISERR_BITS) printf("BusFault: Imprecise data access error\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_PRECISERR_BITS) printf("BusFault: Precise data access error\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_IBUSERR_BITS) printf("BusFault: Bus fault on instruction prefetch\n");
if (scb_hw->cfsr & M33_CFSR_BFSR_BFARVALID_BITS) printf("BusFault address: %08x\n", scb_hw->bfar);
if (scb_hw->cfsr & M33_CFSR_MMFSR_BITS) printf("MemManageFault: %02x\n", scb_hw->cfsr & M33_CFSR_MMFSR_BITS);
if (scb_hw->cfsr & 0x80) printf("MemManageFault address: %08x\n", scb_hw->mmfar);
if (hardfault_callback) {
hardfault_callback();
}
}
void secure_install_default_hardfault_handler(secure_hardfault_callback_t callback) {
hardfault_callback = callback;
exception_set_exclusive_handler(HARDFAULT_EXCEPTION, secure_hardfault_handler);
}
#if !PICO_RUNTIME_NO_INIT_NONSECURE_COPROCESSORS
void __weak runtime_init_nonsecure_coprocessors() {
// Enable NS coprocessor access to anything secure has enabled
uint32_t cpacr = arm_cpu_hw->cpacr;
uint32_t nsacr = 0;
for (int i = 0; i < 16; i++) {
if (cpacr & (M33_CPACR_CP0_BITS << (i * M33_CPACR_CP1_LSB))) {
nsacr |= (0x1 << i);
}
}
arm_cpu_hw->nsacr |= nsacr;
}
#endif
#if !PICO_RUNTIME_SKIP_INIT_NONSECURE_COPROCESSORS
PICO_RUNTIME_INIT_FUNC_PER_CORE(runtime_init_nonsecure_coprocessors, PICO_RUNTIME_INIT_NONSECURE_COPROCESSORS);
#endif
#if !PICO_RUNTIME_NO_INIT_NONSECURE_ACCESSCTRL_AND_IRQS
void __weak runtime_init_nonsecure_accessctrl_and_irqs() {
rom_set_ns_api_permission(BOOTROM_NS_API_get_sys_info, true);
#if PICO_ALLOW_NONSECURE_DMA
accessctrl_hw->dma |= 0xacce0000 | ACCESSCTRL_DMA_NSP_BITS | ACCESSCTRL_DMA_NSU_BITS;
#endif
#ifdef PICO_ASSIGN_NONSECURE_TIMER
accessctrl_hw->timer[PICO_ASSIGN_NONSECURE_TIMER] |= 0xacce0000 | ACCESSCTRL_TIMER0_NSP_BITS | ACCESSCTRL_TIMER0_NSU_BITS;
static_assert(TIMER0_IRQ_0 + 4 == TIMER1_IRQ_0, "Expected 4 IRQs per TIMER");
irq_assign_to_ns(TIMER0_IRQ_0 + PICO_ASSIGN_NONSECURE_TIMER * 4, true);
irq_assign_to_ns(TIMER0_IRQ_1 + PICO_ASSIGN_NONSECURE_TIMER * 4, true);
irq_assign_to_ns(TIMER0_IRQ_2 + PICO_ASSIGN_NONSECURE_TIMER * 4, true);
irq_assign_to_ns(TIMER0_IRQ_3 + PICO_ASSIGN_NONSECURE_TIMER * 4, true);
#endif
#if PICO_ALLOW_NONSECURE_GPIO
accessctrl_hw->io_bank[0] |= 0xacce0000 | ACCESSCTRL_IO_BANK0_NSP_BITS | ACCESSCTRL_IO_BANK0_NSU_BITS;
irq_assign_to_ns(IO_IRQ_BANK0_NS, true);
#endif
#if PICO_ALLOW_NONSECURE_USB
accessctrl_hw->usbctrl |= 0xacce0000 | ACCESSCTRL_USBCTRL_NSP_BITS | ACCESSCTRL_USBCTRL_NSU_BITS;
irq_assign_to_ns(USBCTRL_IRQ, true);
#endif
}
#endif
#if !PICO_RUNTIME_SKIP_INIT_NONSECURE_ACCESSCTRL_AND_IRQS
PICO_RUNTIME_INIT_FUNC_HW(runtime_init_nonsecure_accessctrl_and_irqs, PICO_RUNTIME_INIT_NONSECURE_ACCESSCTRL_AND_IRQS);
#endif

View file

@ -70,6 +70,15 @@ if (NOT TARGET pico_standard_link)
set_target_properties(${TARGET} PROPERTIES PICO_TARGET_LINKER_SCRIPT ${LDSCRIPT})
endfunction()
# pico_set_linker_script_var(TARGET NAME VALUE)
# \brief\ Set the linker script for the target
#
# \param\ NAME Name of varAible to set
# \param\ VALUE Value of variable to set
function(pico_set_linker_script_var TARGET NAME VALUE)
set_property(TARGET ${TARGET} APPEND PROPERTY PICO_TARGET_LINKER_SCRIPT_VARS "--defsym=${NAME}=${VALUE}")
endfunction()
# pico_set_binary_type(TARGET TYPE)
# \brief\ Set the binary type for the target
#
@ -124,9 +133,19 @@ if (NOT TARGET pico_standard_link)
#math(EXPR PICO_FLASH_SIZE_BYTES_STRING "${PICO_FLASH_SIZE_BYTES}" OUTPUT_FORMAT HEXADECIMAL)
set(PICO_FLASH_SIZE_BYTES_STRING "${PICO_FLASH_SIZE_BYTES}")
configure_file(${CMAKE_CURRENT_LIST_DIR}/pico_flash_region.template.ld ${CMAKE_BINARY_DIR}/pico_flash_region.ld)
# add include path for linker scripts
# add include path for linker scripts to find it
target_link_options(pico_standard_link INTERFACE "LINKER:-L${CMAKE_BINARY_DIR}")
# add include path for main linker script sections
target_link_options(pico_standard_link INTERFACE "LINKER:-L${PICO_LINKER_SCRIPT_PATH}")
target_link_options(pico_standard_link INTERFACE "LINKER:-L${CMAKE_CURRENT_LIST_DIR}/scripts")
# add default locations script, so they can be referenced by pico_set_linker_script_var variables
target_link_options(pico_standard_link INTERFACE "LINKER:--script=${PICO_LINKER_SCRIPT_PATH}/platform/default_locations.ld")
# add variables set by pico_set_linker_script_var function
target_link_options(pico_standard_link INTERFACE "LINKER:$<JOIN:$<TARGET_PROPERTY:PICO_TARGET_LINKER_SCRIPT_VARS>,,>")
# LINKER script will be PICO_TARGET_LINKER_SCRIPT if set on target, or ${CMAKE_CURRENT_LIST_DIR}/memmap_foo.ld
# if PICO_TARGET_BINARY_TYPE is set to foo on the target, otherwise ${CMAKE_CURRENT_LIST_DIR}/memmap_${PICO_DEFAULT_BINARY_TYPE).ld
set(_LINKER_SCRIPT_EXPRESSION "$<IF:$<BOOL:$<TARGET_PROPERTY:PICO_TARGET_LINKER_SCRIPT>>,$<TARGET_PROPERTY:PICO_TARGET_LINKER_SCRIPT>,${PICO_LINKER_SCRIPT_PATH}/memmap_$<IF:$<STREQUAL:$<TARGET_PROPERTY:PICO_TARGET_BINARY_TYPE>,>,${PICO_DEFAULT_BINARY_TYPE},$<TARGET_PROPERTY:PICO_TARGET_BINARY_TYPE>>.ld>")

View file

@ -0,0 +1,16 @@
/* Include platform memory locations */
INCLUDE "rp2_common/set_memory_locations.ld"
/* Include memory regions used */
INCLUDE "rp2_common/memory_flash.ld"
INCLUDE "rp2_common/memory_ram.ld"
INCLUDE "rp2_common/memory_scratch.ld"
/* Include aliases for storage memory regions */
INCLUDE "rp2_common/memory_aliases_default.ld"
/* Define entry point symbol */
ENTRY(_entry_point)
/* Include default sections */
INCLUDE "rp2_common/sections_copy_to_ram.ld"

View file

@ -0,0 +1,16 @@
/* Include platform memory locations */
INCLUDE "rp2_common/set_memory_locations.ld"
/* Include memory regions used */
INCLUDE "rp2_common/memory_flash.ld"
INCLUDE "rp2_common/memory_ram.ld"
INCLUDE "rp2_common/memory_scratch.ld"
/* Include aliases for storage memory regions */
INCLUDE "rp2_common/memory_aliases_default.ld"
/* Define entry point symbol */
ENTRY(_entry_point)
/* Include default sections */
INCLUDE "rp2_common/sections_default.ld"

View file

@ -0,0 +1,15 @@
/* Include platform memory locations */
INCLUDE "rp2_common/set_memory_locations.ld"
/* Include memory regions used */
INCLUDE "rp2_common/memory_ram.ld"
INCLUDE "rp2_common/memory_scratch.ld"
/* Include aliases for no_flash storage memory regions (alias to themselves) */
INCLUDE "rp2_common/memory_aliases_no_flash.ld"
/* Define entry point symbol */
ENTRY(_entry_point)
/* Include no_flash sections */
INCLUDE "rp2_common/sections_no_flash.ld"

View file

@ -0,0 +1,3 @@
REGION_ALIAS("RAM_STORE", FLASH);
REGION_ALIAS("SCRATCH_X_STORE", FLASH);
REGION_ALIAS("SCRATCH_Y_STORE", FLASH);

View file

@ -0,0 +1,3 @@
REGION_ALIAS("RAM_STORE", RAM);
REGION_ALIAS("SCRATCH_X_STORE", SCRATCH_X);
REGION_ALIAS("SCRATCH_Y_STORE", SCRATCH_Y);

View file

@ -0,0 +1,4 @@
MEMORY
{
INCLUDE "pico_flash_region.ld"
}

View file

@ -0,0 +1,4 @@
MEMORY
{
RAM(rwx) : ORIGIN = RAM_ORIGIN, LENGTH = RAM_LENGTH
}

View file

@ -0,0 +1,5 @@
MEMORY
{
SCRATCH_X(rwx) : ORIGIN = SCRATCH_X_ORIGIN, LENGTH = SCRATCH_X_LENGTH
SCRATCH_Y(rwx) : ORIGIN = SCRATCH_Y_ORIGIN, LENGTH = SCRATCH_Y_LENGTH
}

View file

@ -0,0 +1,4 @@
MEMORY
{
XIP_RAM(rwx) : ORIGIN = XIP_RAM_ORIGIN, LENGTH = XIP_RAM_LENGTH
}

View file

@ -0,0 +1,83 @@
SECTIONS
{
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
. = ALIGN(4);
*(.rodata*)
*(.srodata*)
. = ALIGN(4);
*(.data*)
*(.sdata*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
PROVIDE(__global_pointer$ = . + 2K);
*(.sbss*)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
}

View file

@ -0,0 +1,72 @@
SECTIONS
{
.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
*(.sdata*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM AT> FLASH
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM AT> FLASH
PROVIDE(__data_end__ = .);
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
PROVIDE(__global_pointer$ = . + 2K);
*(.sbss*)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
}

View file

@ -0,0 +1,49 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
SECTIONS
{
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* picolibc and LLVM */
PROVIDE (__heap_start = __end__);
PROVIDE (__heap_end = __HeapLimit);
PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
/* llvm-libc */
PROVIDE (_end = __end__);
PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
/* todo assert on extra code */
}

View file

@ -0,0 +1,7 @@
SECTIONS
{
.flash_end : {
KEEP(*(.embedded_end_block*))
PROVIDE(__flash_binary_end = .);
} > FLASH =0xaa
}

View file

@ -0,0 +1,12 @@
SECTIONS
{
.heap DEFINED(HEAP_LOC) ? HEAP_LOC : . (NOLOAD):
{
__end__ = .;
end = __end__;
KEEP(*(.heap*))
} > RAM
/* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
__HeapLimit = DEFINED(HEAP_LIMIT) ? HEAP_LIMIT : ORIGIN(RAM) + LENGTH(RAM);
}

View file

@ -0,0 +1,82 @@
SECTIONS
{
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
*(.data*)
*(.sdata*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
} > RAM
.tdata : {
. = ALIGN(4);
*(.tdata .tdata.* .gnu.linkonce.td.*)
/* All data end */
__tdata_end = .;
} > RAM
PROVIDE(__data_end__ = .);
.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
__etext = LOADADDR(.data);
.tbss (NOLOAD) : {
. = ALIGN(4);
__bss_start__ = .;
__tls_base = .;
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
__tls_end = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(4);
__tbss_end = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
PROVIDE(__global_pointer$ = . + 2K);
*(.sbss*)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
}

View file

@ -0,0 +1,37 @@
SECTIONS
{
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT> SCRATCH_X_STORE
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT> SCRATCH_Y_STORE
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (NOLOAD):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (NOLOAD):
{
KEEP(*(.stack*))
} > SCRATCH_Y
}

View file

@ -0,0 +1,7 @@
INCLUDE "platform/section_copy_to_ram_text.ld"
INCLUDE "rp2_common/section_copy_to_ram_data.ld"
INCLUDE "rp2_common/section_heap.ld"
INCLUDE "rp2_common/section_scratch.ld"
INCLUDE "rp2_common/section_flash_end.ld"
INCLUDE "rp2_common/section_end.ld"
INCLUDE "platform/section_platform_end.ld"

View file

@ -0,0 +1,7 @@
INCLUDE "platform/section_default_text.ld"
INCLUDE "rp2_common/section_default_data.ld"
INCLUDE "rp2_common/section_heap.ld"
INCLUDE "rp2_common/section_scratch.ld"
INCLUDE "rp2_common/section_flash_end.ld"
INCLUDE "rp2_common/section_end.ld"
INCLUDE "platform/section_platform_end.ld"

View file

@ -0,0 +1,6 @@
INCLUDE "platform/section_no_flash_text.ld"
INCLUDE "rp2_common/section_no_flash_data.ld"
INCLUDE "rp2_common/section_heap.ld"
INCLUDE "rp2_common/section_scratch.ld"
INCLUDE "rp2_common/section_end.ld"
INCLUDE "platform/section_platform_end.ld"

View file

@ -0,0 +1,8 @@
RAM_ORIGIN = DEFINED(RAM_ORIGIN) ? RAM_ORIGIN : RAM_ORIGIN_DEFAULT;
RAM_LENGTH = DEFINED(RAM_LENGTH) ? RAM_LENGTH : RAM_LENGTH_DEFAULT;
SCRATCH_X_ORIGIN = DEFINED(SCRATCH_X_ORIGIN) ? SCRATCH_X_ORIGIN : SCRATCH_X_ORIGIN_DEFAULT;
SCRATCH_X_LENGTH = DEFINED(SCRATCH_X_LENGTH) ? SCRATCH_X_LENGTH : SCRATCH_X_LENGTH_DEFAULT;
SCRATCH_Y_ORIGIN = DEFINED(SCRATCH_Y_ORIGIN) ? SCRATCH_Y_ORIGIN : SCRATCH_Y_ORIGIN_DEFAULT;
SCRATCH_Y_LENGTH = DEFINED(SCRATCH_Y_LENGTH) ? SCRATCH_Y_LENGTH : SCRATCH_Y_LENGTH_DEFAULT;
XIP_RAM_ORIGIN = DEFINED(XIP_RAM_ORIGIN) ? XIP_RAM_ORIGIN : XIP_RAM_ORIGIN_DEFAULT;
XIP_RAM_LENGTH = DEFINED(XIP_RAM_LENGTH) ? XIP_RAM_LENGTH : XIP_RAM_LENGTH_DEFAULT;

View file

@ -130,8 +130,8 @@ void stdio_uart_deinit_full(struct uart_inst *uart, int tx_pin, int rx_pin) {
uart_deinit(uart_instance);
#if HAS_PADS_BANK0_ISOLATION
// Leave pads isolated
if (tx_pin >= 0) hw_set_bits(&pads_bank0_hw->io[tx_pin], PADS_BANK0_GPIO0_ISO_BITS);
if (rx_pin >= 0) hw_set_bits(&pads_bank0_hw->io[rx_pin], PADS_BANK0_GPIO0_ISO_BITS);
if (tx_pin >= 0) pads_bank0_set_bits(tx_pin, PADS_BANK0_GPIO0_ISO_BITS);
if (rx_pin >= 0) pads_bank0_set_bits(rx_pin, PADS_BANK0_GPIO0_ISO_BITS);
#else
((void)tx_pin);
((void)rx_pin);

View file

@ -214,6 +214,15 @@ if (NOT KITCHEN_SINK_NO_BINARY_TYPE_VARIANTS)
pico_add_extra_outputs(kitchen_sink_blocked_ram)
target_compile_definitions(kitchen_sink_blocked_ram PRIVATE KITCHEN_SINK_ID="blocked-ram binary")
endif()
add_executable(kitchen_sink_ram_custom ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c)
# Have ram start from 0x20020000 length 128k, and heap start from 0x20030000
pico_set_linker_script_var(kitchen_sink_ram_custom RAM_ORIGIN 0x20020000)
pico_set_linker_script_var(kitchen_sink_ram_custom RAM_LENGTH 128k)
pico_set_linker_script_var(kitchen_sink_ram_custom HEAP_LOC 0x20030000)
target_link_libraries(kitchen_sink_ram_custom kitchen_sink_libs kitchen_sink_options)
pico_add_extra_outputs(kitchen_sink_ram_custom)
target_compile_definitions(kitchen_sink_ram_custom PRIVATE KITCHEN_SINK_ID="custom ram binary")
endif()
add_executable(kitchen_sink_cpp ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink_cpp.cpp)

View file

@ -742,3 +742,40 @@ function(picotool_postprocess_binary TARGET)
endif()
endif()
endfunction()
# pico_concatenate_uf2_outputs(COMBINED_TARGET <TARGETS>...)
# \brief_nodesc\ Concatenate UF2 outputs into a single UF2 file
#
# Concatenate UF2 outputs from multiple targets into a single UF2 file, in the order they're passed to the function
#
# This can be used in conjunction with a partition table with no_reboot_on_uf2_download set on some partitions,
# to allow loading multiple partitions with a single UF2 file. For example, with this partition table you could concatenate
# rp2350-arm-ns and rp2350-arm-s binaries into a single UF2 file for loading into the main and owned partitions in one shot:
# ```
# partitions:
# 0(A) uf2 { 'rp2350-arm-s' }
# 1(B w/ 0) uf2 { 'rp2350-arm-s' }
# 2(A ob/ 0) uf2 { 'rp2350-arm-ns' }, no_reboot_on_uf2_download, ab_non_bootable_owner_affinity
# 3(B w/ 2) uf2 { 'rp2350-arm-ns' }, no_reboot_on_uf2_download, ab_non_bootable_owner_affinity
# ```
#
# NOTE: This does not modify the UF2 data structures, so the chip will still see multiple UF2 files,
# this just concatenates them into a single file on the host.
#
# \param\ COMBINED_TARGET The name of the combined UF2 target
# \param\ TARGETS The targets to combine
function(pico_concatenate_uf2_outputs COMBINED_TARGET)
list(TRANSFORM ARGN PREPEND ${CMAKE_CURRENT_BINARY_DIR}/ OUTPUT_VARIABLE UF2S)
list(TRANSFORM UF2S APPEND .uf2)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${COMBINED_TARGET}.uf2
COMMAND ${CMAKE_COMMAND} -E cat
${UF2S}
> ${CMAKE_CURRENT_BINARY_DIR}/${COMBINED_TARGET}.uf2
DEPENDS ${ARGN}
COMMAND_EXPAND_LISTS)
add_custom_target(${COMBINED_TARGET} ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${COMBINED_TARGET}.uf2)
endfunction()