├── CMakeLists.txt ├── README.md ├── bootloader.ld ├── combined.ld ├── gen_imghdr.py ├── main.c ├── mkasm.py ├── pico_sdk_import.cmake └── standalone.ld /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Derived from the Pico SDK, which carries the following 2 | # LICENSE.txt: 3 | # Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 4 | # 5 | # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 6 | # following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 9 | # disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 12 | # disclaimer in the documentation and/or other materials provided with the distribution. 13 | # 14 | # 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 15 | # derived from this software without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | cmake_minimum_required(VERSION 3.13) 26 | 27 | include(pico_sdk_import.cmake) 28 | 29 | project(test_project C CXX ASM) 30 | 31 | set(CMAKE_C_STANDARD 11) 32 | set(CMAKE_CXX_STANDARD 17) 33 | 34 | pico_sdk_init() 35 | 36 | # Build the bootloader as a standalone thing 37 | 38 | add_executable(bootloader main.c) 39 | 40 | function(target_cl_options option) 41 | target_compile_options(bootloader PRIVATE ${option}) 42 | target_link_options(bootloader PRIVATE ${option}) 43 | endfunction() 44 | 45 | target_cl_options("-Os") 46 | target_cl_options("-ffunction-sections") 47 | target_cl_options("-fdata-sections") 48 | target_link_options(bootloader PRIVATE "LINKER:--gc-sections") 49 | 50 | pico_add_extra_outputs(bootloader) 51 | pico_set_binary_type(bootloader copy_to_ram) 52 | 53 | set_target_properties(bootloader PROPERTIES COMPILE_FLAGS "-Wall") 54 | 55 | pico_set_linker_script(bootloader ${CMAKE_CURRENT_SOURCE_DIR}/bootloader.ld) 56 | 57 | target_link_libraries(bootloader 58 | pico_stdlib 59 | hardware_dma 60 | hardware_flash 61 | hardware_structs 62 | hardware_resets 63 | cmsis_core) 64 | 65 | set(BOOTLOADER_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "") 66 | 67 | # Build a library to embed into applications 68 | 69 | function(bootloader_define_library) 70 | set(NAME bootloader) 71 | set(ORIGINAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.bin) 72 | set(BIN_ASM ${CMAKE_CURRENT_BINARY_DIR}/${NAME}_bin.S) 73 | 74 | add_custom_target(${NAME}_bin DEPENDS ${ORIGINAL_BIN}) 75 | add_custom_command(OUTPUT ${ORIGINAL_BIN} DEPENDS ${NAME} COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${ORIGINAL_BIN}) 76 | 77 | find_package (Python3 REQUIRED COMPONENTS Interpreter) 78 | add_custom_target(${NAME}_bin_asm DEPENDS ${BIN_ASM}) 79 | add_custom_command(OUTPUT ${BIN_ASM} DEPENDS ${ORIGINAL_BIN} 80 | COMMAND ${Python3_EXECUTABLE} ${BOOTLOADER_DIR}/mkasm.py ${ORIGINAL_BIN} ${BIN_ASM} 81 | ) 82 | 83 | add_library(${NAME}_library INTERFACE) 84 | add_dependencies(${NAME}_library ${NAME}_bin_asm) 85 | # not strictly (or indeed actually) a link library, but this avoids dependency cycle 86 | target_link_libraries(${NAME}_library INTERFACE ${BIN_ASM}) 87 | endfunction() 88 | 89 | bootloader_define_library() 90 | 91 | # Provide a helper to build a combined target 92 | 93 | function(bootloader_build_combined NAME) 94 | set(APP ${NAME}_app) 95 | set(APP_BIN ${CMAKE_CURRENT_BINARY_DIR}/${APP}.bin) 96 | set(APP_HDR ${CMAKE_CURRENT_BINARY_DIR}/${APP}_hdr.bin) 97 | 98 | set(COMBINED ${NAME}_combined) 99 | 100 | target_link_libraries(${NAME} bootloader_library) 101 | 102 | pico_set_linker_script(${NAME} ${BOOTLOADER_DIR}/combined.ld) 103 | 104 | pico_add_bin_output(${NAME}) 105 | 106 | # TODO: The hard-coded 16k here is a bit nasty 107 | add_custom_target(${APP}_bin DEPENDS ${APP_BIN}) 108 | add_custom_command(OUTPUT ${APP_BIN} DEPENDS ${NAME}.bin 109 | COMMAND dd ibs=1k iseek=16 if=${NAME}.bin of=${APP_BIN} 110 | ) 111 | 112 | # TODO: The hard-coded address here is a bit nasty 113 | add_custom_target(${APP}_hdr DEPENDS ${APP}_bin) 114 | add_custom_command(OUTPUT ${APP_HDR} DEPENDS ${APP_BIN} 115 | COMMAND ${BOOTLOADER_DIR}/gen_imghdr.py -a 0x10004000 ${APP_BIN} ${APP_HDR} 116 | ) 117 | 118 | add_custom_target(${COMBINED} ALL DEPENDS ${APP_HDR}) 119 | add_custom_command(TARGET ${COMBINED} DEPENDS ${APP_HDR} 120 | COMMAND ${CMAKE_OBJCOPY} --update-section .app_hdr=${APP_HDR} ${NAME}.elf ${COMBINED}.elf 121 | ) 122 | add_custom_command(TARGET ${COMBINED} POST_BUILD 123 | COMMAND ${CMAKE_OBJCOPY} -Obinary ${COMBINED}.elf ${COMBINED}.bin 124 | ) 125 | endfunction() 126 | 127 | # Provide a helper to build a standalone target 128 | 129 | function(bootloader_build_standalone NAME) 130 | pico_set_linker_script(${NAME} ${BOOTLOADER_DIR}/standalone.ld) 131 | pico_add_bin_output(${NAME}) 132 | endfunction() 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RP2040 Serial Bootloader 2 | 3 | This is a bootloader for the RP2040 which enables code upload via UART. 4 | 5 | There's a more complete description at: https://blog.usedbytes.com/2021/12/pico-serial-bootloader/ 6 | 7 | There are currently two tools I know of which can be used to upload code to it: 8 | 9 | * [serial-flash](https://github.com/usedbytes/serial-flash) - my tool written in `go` 10 | * [pico-py-serial-flash](https://github.com/ConfedSolutions/pico-py-serial-flash) - a similar tool written in Python, contributed by another user. 11 | 12 | -------------------------------------------------------------------------------- /bootloader.ld: -------------------------------------------------------------------------------- 1 | /* Based on GCC ARM embedded samples. 2 | Defines the following symbols for use by code: 3 | __exidx_start 4 | __exidx_end 5 | __etext 6 | __data_start__ 7 | __preinit_array_start 8 | __preinit_array_end 9 | __init_array_start 10 | __init_array_end 11 | __fini_array_start 12 | __fini_array_end 13 | __data_end__ 14 | __bss_start__ 15 | __bss_end__ 16 | __end__ 17 | end 18 | __HeapLimit 19 | __StackLimit 20 | __StackTop 21 | __stack (== StackTop) 22 | */ 23 | 24 | /* Limit flash to 12k, so we can use 12-16k for the image header */ 25 | MEMORY 26 | { 27 | FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 12k 28 | RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 29 | SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k 30 | SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k 31 | } 32 | 33 | ENTRY(_entry_point) 34 | 35 | SECTIONS 36 | { 37 | /* Second stage bootloader is prepended to the image. It must be 256 bytes big 38 | and checksummed. It is usually built by the boot_stage2 target 39 | in the Raspberry Pi Pico SDK 40 | */ 41 | 42 | .flash_begin : { 43 | __flash_binary_start = .; 44 | } > FLASH 45 | 46 | .boot2 : { 47 | __boot2_start__ = .; 48 | KEEP (*(.boot2)) 49 | __boot2_end__ = .; 50 | } > FLASH 51 | 52 | ASSERT(__boot2_end__ - __boot2_start__ == 256, 53 | "ERROR: Pico second stage bootloader must be 256 bytes in size") 54 | 55 | /* The second stage will always enter the image at the start of .text. 56 | The debugger will use the ELF entry point, which is the _entry_point 57 | symbol if present, otherwise defaults to start of .text. 58 | This can be used to transfer control back to the bootrom on debugger 59 | launches only, to perform proper flash setup. 60 | */ 61 | 62 | .flashtext : { 63 | __logical_binary_start = .; 64 | KEEP (*(.vectors)) 65 | KEEP (*(.binary_info_header)) 66 | __binary_info_header_end = .; 67 | KEEP (*(.reset)) 68 | } 69 | 70 | .rodata : { 71 | /* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */ 72 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) 73 | . = ALIGN(4); 74 | } > FLASH 75 | 76 | .ARM.extab : 77 | { 78 | *(.ARM.extab* .gnu.linkonce.armextab.*) 79 | } > FLASH 80 | 81 | __exidx_start = .; 82 | .ARM.exidx : 83 | { 84 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 85 | } > FLASH 86 | __exidx_end = .; 87 | 88 | /* Machine inspectable binary information */ 89 | . = ALIGN(4); 90 | __binary_info_start = .; 91 | .binary_info : 92 | { 93 | KEEP(*(.binary_info.keep.*)) 94 | *(.binary_info.*) 95 | } > FLASH 96 | __binary_info_end = .; 97 | . = ALIGN(4); 98 | 99 | /* Vector table goes first in RAM, to avoid large alignment hole */ 100 | .ram_vector_table (COPY): { 101 | *(.ram_vector_table) 102 | } > RAM 103 | 104 | .text : { 105 | __ram_text_start__ = .; 106 | *(.init) 107 | *(.text*) 108 | *(.fini) 109 | /* Pull all c'tors into .text */ 110 | *crtbegin.o(.ctors) 111 | *crtbegin?.o(.ctors) 112 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 113 | *(SORT(.ctors.*)) 114 | *(.ctors) 115 | /* Followed by destructors */ 116 | *crtbegin.o(.dtors) 117 | *crtbegin?.o(.dtors) 118 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 119 | *(SORT(.dtors.*)) 120 | *(.dtors) 121 | 122 | *(.eh_frame*) 123 | . = ALIGN(4); 124 | __ram_text_end__ = .; 125 | } > RAM AT> FLASH 126 | __ram_text_source__ = LOADADDR(.text); 127 | 128 | 129 | .data : { 130 | __data_start__ = .; 131 | *(vtable) 132 | 133 | *(.time_critical*) 134 | 135 | . = ALIGN(4); 136 | *(.rodata*) 137 | . = ALIGN(4); 138 | 139 | *(.data*) 140 | 141 | . = ALIGN(4); 142 | *(.after_data.*) 143 | . = ALIGN(4); 144 | /* preinit data */ 145 | PROVIDE_HIDDEN (__mutex_array_start = .); 146 | KEEP(*(SORT(.mutex_array.*))) 147 | KEEP(*(.mutex_array)) 148 | PROVIDE_HIDDEN (__mutex_array_end = .); 149 | 150 | . = ALIGN(4); 151 | /* preinit data */ 152 | PROVIDE_HIDDEN (__preinit_array_start = .); 153 | KEEP(*(SORT(.preinit_array.*))) 154 | KEEP(*(.preinit_array)) 155 | PROVIDE_HIDDEN (__preinit_array_end = .); 156 | 157 | . = ALIGN(4); 158 | /* init data */ 159 | PROVIDE_HIDDEN (__init_array_start = .); 160 | KEEP(*(SORT(.init_array.*))) 161 | KEEP(*(.init_array)) 162 | PROVIDE_HIDDEN (__init_array_end = .); 163 | 164 | . = ALIGN(4); 165 | /* finit data */ 166 | PROVIDE_HIDDEN (__fini_array_start = .); 167 | *(SORT(.fini_array.*)) 168 | *(.fini_array) 169 | PROVIDE_HIDDEN (__fini_array_end = .); 170 | 171 | *(.jcr) 172 | . = ALIGN(4); 173 | /* All data end */ 174 | __data_end__ = .; 175 | } > RAM AT> FLASH 176 | /* __etext is the name of the .data init source pointer (...) */ 177 | __etext = LOADADDR(.data); 178 | 179 | .uninitialized_data (COPY): { 180 | . = ALIGN(4); 181 | *(.uninitialized_data*) 182 | } > RAM 183 | 184 | /* Start and end symbols must be word-aligned */ 185 | .scratch_x : { 186 | __scratch_x_start__ = .; 187 | *(.scratch_x.*) 188 | . = ALIGN(4); 189 | __scratch_x_end__ = .; 190 | } > SCRATCH_X AT > FLASH 191 | __scratch_x_source__ = LOADADDR(.scratch_x); 192 | 193 | .scratch_y : { 194 | __scratch_y_start__ = .; 195 | *(.scratch_y.*) 196 | . = ALIGN(4); 197 | __scratch_y_end__ = .; 198 | } > SCRATCH_Y AT > FLASH 199 | __scratch_y_source__ = LOADADDR(.scratch_y); 200 | 201 | .bss : { 202 | . = ALIGN(4); 203 | __bss_start__ = .; 204 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) 205 | *(COMMON) 206 | . = ALIGN(4); 207 | __bss_end__ = .; 208 | } > RAM 209 | 210 | .heap (COPY): 211 | { 212 | __end__ = .; 213 | end = __end__; 214 | *(.heap*) 215 | __HeapLimit = .; 216 | } > RAM 217 | 218 | /* .stack*_dummy section doesn't contains any symbols. It is only 219 | * used for linker to calculate size of stack sections, and assign 220 | * values to stack symbols later 221 | * 222 | * stack1 section may be empty/missing if platform_launch_core1 is not used */ 223 | 224 | /* by default we put core 0 stack at the end of scratch Y, so that if core 1 225 | * stack is not used then all of SCRATCH_X is free. 226 | */ 227 | .stack1_dummy (COPY): 228 | { 229 | *(.stack1*) 230 | } > SCRATCH_X 231 | .stack_dummy (COPY): 232 | { 233 | *(.stack*) 234 | } > SCRATCH_Y 235 | 236 | .flash_end : { 237 | __flash_binary_end = .; 238 | } > FLASH 239 | 240 | /* stack limit is poorly named, but historically is maximum heap ptr */ 241 | __StackLimit = ORIGIN(RAM) + LENGTH(RAM); 242 | __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); 243 | __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); 244 | __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); 245 | __StackBottom = __StackTop - SIZEOF(.stack_dummy); 246 | PROVIDE(__stack = __StackTop); 247 | 248 | /* Check if data + heap + stack exceeds RAM limit */ 249 | ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") 250 | 251 | ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") 252 | /* todo assert on extra code */ 253 | } 254 | 255 | -------------------------------------------------------------------------------- /combined.ld: -------------------------------------------------------------------------------- 1 | /* Based on GCC ARM embedded samples. 2 | Defines the following symbols for use by code: 3 | __exidx_start 4 | __exidx_end 5 | __etext 6 | __data_start__ 7 | __preinit_array_start 8 | __preinit_array_end 9 | __init_array_start 10 | __init_array_end 11 | __fini_array_start 12 | __fini_array_end 13 | __data_end__ 14 | __bss_start__ 15 | __bss_end__ 16 | __end__ 17 | end 18 | __HeapLimit 19 | __StackLimit 20 | __StackTop 21 | __stack (== StackTop) 22 | */ 23 | 24 | /* Skip 16kB at the start of flash, that's where our bootloader is */ 25 | MEMORY 26 | { 27 | FLASH_BL(rx) : ORIGIN = 0x10000000, LENGTH = 12k 28 | FLASH_IMGHDR(rx) : ORIGIN = 0x10000000 + 12k, LENGTH = 4k 29 | FLASH_APP(rx) : ORIGIN = 0x10000000 + 16k, LENGTH = 2048k - 16k 30 | RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 31 | SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k 32 | SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k 33 | } 34 | 35 | ENTRY(_entry_point) 36 | 37 | SECTIONS 38 | { 39 | .flash_begin : { 40 | __flash_binary_start = .; 41 | } > FLASH_APP 42 | 43 | /* Insert boot3, which is the combined boot2 + boot3 */ 44 | .boot3 : { 45 | KEEP (*(.boot3)) 46 | } > FLASH_BL 47 | 48 | /* 49 | * Name a section for the image header. 50 | * The contents will get replaced post-build 51 | */ 52 | .app_hdr : { 53 | LONG(0xdeaddead) 54 | LONG(0) 55 | LONG(0xdeaddead) 56 | } > FLASH_IMGHDR 57 | 58 | .text : { 59 | __logical_binary_start = .; 60 | KEEP (*(.vectors)) 61 | KEEP (*(.binary_info_header)) 62 | __binary_info_header_end = .; 63 | KEEP (*(.reset)) 64 | /* TODO revisit this now memset/memcpy/float in ROM */ 65 | /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from 66 | * FLASH ... we will include any thing excluded here in .data below by default */ 67 | *(.init) 68 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) 69 | *(.fini) 70 | /* Pull all c'tors into .text */ 71 | *crtbegin.o(.ctors) 72 | *crtbegin?.o(.ctors) 73 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 74 | *(SORT(.ctors.*)) 75 | *(.ctors) 76 | /* Followed by destructors */ 77 | *crtbegin.o(.dtors) 78 | *crtbegin?.o(.dtors) 79 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 80 | *(SORT(.dtors.*)) 81 | *(.dtors) 82 | 83 | *(.eh_frame*) 84 | . = ALIGN(4); 85 | } > FLASH_APP 86 | 87 | .rodata : { 88 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) 89 | . = ALIGN(4); 90 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) 91 | . = ALIGN(4); 92 | } > FLASH_APP 93 | 94 | .ARM.extab : 95 | { 96 | *(.ARM.extab* .gnu.linkonce.armextab.*) 97 | } > FLASH_APP 98 | 99 | __exidx_start = .; 100 | .ARM.exidx : 101 | { 102 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 103 | } > FLASH_APP 104 | __exidx_end = .; 105 | 106 | /* Machine inspectable binary information */ 107 | . = ALIGN(4); 108 | __binary_info_start = .; 109 | .binary_info : 110 | { 111 | KEEP(*(.binary_info.keep.*)) 112 | *(.binary_info.*) 113 | } > FLASH_APP 114 | __binary_info_end = .; 115 | . = ALIGN(4); 116 | 117 | /* End of .text-like segments */ 118 | __etext = .; 119 | 120 | .ram_vector_table (COPY): { 121 | *(.ram_vector_table) 122 | } > RAM 123 | 124 | .data : { 125 | __data_start__ = .; 126 | *(vtable) 127 | 128 | *(.time_critical*) 129 | 130 | /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ 131 | *(.text*) 132 | . = ALIGN(4); 133 | *(.rodata*) 134 | . = ALIGN(4); 135 | 136 | *(.data*) 137 | 138 | . = ALIGN(4); 139 | *(.after_data.*) 140 | . = ALIGN(4); 141 | /* preinit data */ 142 | PROVIDE_HIDDEN (__mutex_array_start = .); 143 | KEEP(*(SORT(.mutex_array.*))) 144 | KEEP(*(.mutex_array)) 145 | PROVIDE_HIDDEN (__mutex_array_end = .); 146 | 147 | . = ALIGN(4); 148 | /* preinit data */ 149 | PROVIDE_HIDDEN (__preinit_array_start = .); 150 | KEEP(*(SORT(.preinit_array.*))) 151 | KEEP(*(.preinit_array)) 152 | PROVIDE_HIDDEN (__preinit_array_end = .); 153 | 154 | . = ALIGN(4); 155 | /* init data */ 156 | PROVIDE_HIDDEN (__init_array_start = .); 157 | KEEP(*(SORT(.init_array.*))) 158 | KEEP(*(.init_array)) 159 | PROVIDE_HIDDEN (__init_array_end = .); 160 | 161 | . = ALIGN(4); 162 | /* finit data */ 163 | PROVIDE_HIDDEN (__fini_array_start = .); 164 | *(SORT(.fini_array.*)) 165 | *(.fini_array) 166 | PROVIDE_HIDDEN (__fini_array_end = .); 167 | 168 | *(.jcr) 169 | . = ALIGN(4); 170 | /* All data end */ 171 | __data_end__ = .; 172 | } > RAM AT> FLASH_APP 173 | 174 | .uninitialized_data (COPY): { 175 | . = ALIGN(4); 176 | *(.uninitialized_data*) 177 | } > RAM 178 | 179 | /* Start and end symbols must be word-aligned */ 180 | .scratch_x : { 181 | __scratch_x_start__ = .; 182 | *(.scratch_x.*) 183 | . = ALIGN(4); 184 | __scratch_x_end__ = .; 185 | } > SCRATCH_X AT > FLASH_APP 186 | __scratch_x_source__ = LOADADDR(.scratch_x); 187 | 188 | .scratch_y : { 189 | __scratch_y_start__ = .; 190 | *(.scratch_y.*) 191 | . = ALIGN(4); 192 | __scratch_y_end__ = .; 193 | } > SCRATCH_Y AT > FLASH_APP 194 | __scratch_y_source__ = LOADADDR(.scratch_y); 195 | 196 | .bss : { 197 | . = ALIGN(4); 198 | __bss_start__ = .; 199 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) 200 | *(COMMON) 201 | . = ALIGN(4); 202 | __bss_end__ = .; 203 | } > RAM 204 | 205 | .heap (COPY): 206 | { 207 | __end__ = .; 208 | end = __end__; 209 | *(.heap*) 210 | __HeapLimit = .; 211 | } > RAM 212 | 213 | /* .stack*_dummy section doesn't contains any symbols. It is only 214 | * used for linker to calculate size of stack sections, and assign 215 | * values to stack symbols later 216 | * 217 | * stack1 section may be empty/missing if platform_launch_core1 is not used */ 218 | 219 | /* by default we put core 0 stack at the end of scratch Y, so that if core 1 220 | * stack is not used then all of SCRATCH_X is free. 221 | */ 222 | .stack1_dummy (COPY): 223 | { 224 | *(.stack1*) 225 | } > SCRATCH_X 226 | .stack_dummy (COPY): 227 | { 228 | *(.stack*) 229 | } > SCRATCH_Y 230 | 231 | .flash_end : { 232 | __flash_binary_end = .; 233 | } > FLASH_APP 234 | 235 | /* stack limit is poorly named, but historically is maximum heap ptr */ 236 | __StackLimit = ORIGIN(RAM) + LENGTH(RAM); 237 | __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); 238 | __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); 239 | __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); 240 | __StackBottom = __StackTop - SIZEOF(.stack_dummy); 241 | PROVIDE(__stack = __StackTop); 242 | 243 | /* Check if data + heap + stack exceeds RAM limit */ 244 | ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") 245 | 246 | ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") 247 | /* todo assert on extra code */ 248 | } 249 | 250 | -------------------------------------------------------------------------------- /gen_imghdr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Derived from pad_checksum in the Pico SDK, which carries the following 3 | # LICENSE.txt: 4 | # Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 5 | # 6 | # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 7 | # following conditions are met: 8 | # 9 | # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 10 | # disclaimer. 11 | # 12 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 19 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | import argparse 27 | import binascii 28 | import struct 29 | import sys 30 | 31 | def any_int(x): 32 | try: 33 | return int(x, 0) 34 | except: 35 | raise argparse.ArgumentTypeError("expected an integer, not '{!r}'".format(x)) 36 | 37 | parser = argparse.ArgumentParser() 38 | parser.add_argument("ifile", help="Input application binary (binary)") 39 | parser.add_argument("ofile", help="Output header file (binary)") 40 | parser.add_argument("-a", "--addr", help="Load address of the application image", 41 | type=any_int, default=0x10004000) 42 | args = parser.parse_args() 43 | 44 | try: 45 | idata = open(args.ifile, "rb").read() 46 | except: 47 | sys.exit("Could not open input file '{}'".format(args.ifile)) 48 | 49 | vtor = args.addr 50 | size = len(idata) 51 | crc = binascii.crc32(idata) 52 | 53 | odata = vtor.to_bytes(4, byteorder='little') + size.to_bytes(4, byteorder='little') + crc.to_bytes(4, byteorder='little') 54 | 55 | try: 56 | with open(args.ofile, "wb") as ofile: 57 | ofile.write(odata) 58 | except: 59 | sys.exit("Could not open output file '{}'".format(args.ofile)) 60 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021 Brian Starkey 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #include 7 | #include 8 | 9 | #include "RP2040.h" 10 | #include "pico/time.h" 11 | #include "hardware/dma.h" 12 | #include "hardware/flash.h" 13 | #include "hardware/structs/dma.h" 14 | #include "hardware/structs/watchdog.h" 15 | #include "hardware/gpio.h" 16 | #include "hardware/resets.h" 17 | #include "hardware/uart.h" 18 | #include "hardware/watchdog.h" 19 | 20 | #ifdef DEBUG 21 | #include 22 | #include "pico/stdio_usb.h" 23 | #define DBG_PRINTF_INIT() stdio_usb_init() 24 | #define DBG_PRINTF(...) printf(__VA_ARGS__) 25 | #else 26 | #define DBG_PRINTF_INIT() { } 27 | #define DBG_PRINTF(...) { } 28 | #endif 29 | 30 | // The bootloader can be entered in three ways: 31 | // - BOOTLOADER_ENTRY_PIN is low 32 | // - Watchdog scratch[5] == BOOTLOADER_ENTRY_MAGIC && scratch[6] == ~BOOTLOADER_ENTRY_MAGIC 33 | // - No valid image header 34 | #define BOOTLOADER_ENTRY_PIN 15 35 | #define BOOTLOADER_ENTRY_MAGIC 0xb105f00d 36 | 37 | #define UART_TX_PIN 0 38 | #define UART_RX_PIN 1 39 | #define UART_BAUD 921600 40 | 41 | #define CMD_SYNC (('S' << 0) | ('Y' << 8) | ('N' << 16) | ('C' << 24)) 42 | #define CMD_READ (('R' << 0) | ('E' << 8) | ('A' << 16) | ('D' << 24)) 43 | #define CMD_CSUM (('C' << 0) | ('S' << 8) | ('U' << 16) | ('M' << 24)) 44 | #define CMD_CRC (('C' << 0) | ('R' << 8) | ('C' << 16) | ('C' << 24)) 45 | #define CMD_ERASE (('E' << 0) | ('R' << 8) | ('A' << 16) | ('S' << 24)) 46 | #define CMD_WRITE (('W' << 0) | ('R' << 8) | ('I' << 16) | ('T' << 24)) 47 | #define CMD_SEAL (('S' << 0) | ('E' << 8) | ('A' << 16) | ('L' << 24)) 48 | #define CMD_GO (('G' << 0) | ('O' << 8) | ('G' << 16) | ('O' << 24)) 49 | #define CMD_INFO (('I' << 0) | ('N' << 8) | ('F' << 16) | ('O' << 24)) 50 | #define CMD_REBOOT (('B' << 0) | ('O' << 8) | ('O' << 16) | ('T' << 24)) 51 | 52 | #define RSP_SYNC (('P' << 0) | ('I' << 8) | ('C' << 16) | ('O' << 24)) 53 | #define RSP_OK (('O' << 0) | ('K' << 8) | ('O' << 16) | ('K' << 24)) 54 | #define RSP_ERR (('E' << 0) | ('R' << 8) | ('R' << 16) | ('!' << 24)) 55 | 56 | #define IMAGE_HEADER_OFFSET (12 * 1024) 57 | 58 | #define WRITE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET + FLASH_SECTOR_SIZE) 59 | #define ERASE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET) 60 | #define FLASH_ADDR_MAX (XIP_BASE + PICO_FLASH_SIZE_BYTES) 61 | 62 | static void disable_interrupts(void) 63 | { 64 | SysTick->CTRL &= ~1; 65 | 66 | NVIC->ICER[0] = 0xFFFFFFFF; 67 | NVIC->ICPR[0] = 0xFFFFFFFF; 68 | } 69 | 70 | static void reset_peripherals(void) 71 | { 72 | reset_block(~( 73 | RESETS_RESET_IO_QSPI_BITS | 74 | RESETS_RESET_PADS_QSPI_BITS | 75 | RESETS_RESET_SYSCFG_BITS | 76 | RESETS_RESET_PLL_SYS_BITS 77 | )); 78 | } 79 | 80 | static void jump_to_vtor(uint32_t vtor) 81 | { 82 | // Derived from the Leaf Labs Cortex-M3 bootloader. 83 | // Copyright (c) 2010 LeafLabs LLC. 84 | // Modified 2021 Brian Starkey 85 | // Originally under The MIT License 86 | uint32_t reset_vector = *(volatile uint32_t *)(vtor + 0x04); 87 | 88 | SCB->VTOR = (volatile uint32_t)(vtor); 89 | 90 | asm volatile("msr msp, %0"::"g" 91 | (*(volatile uint32_t *)vtor)); 92 | asm volatile("bx %0"::"r" (reset_vector)); 93 | } 94 | 95 | static uint32_t handle_sync(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 96 | static uint32_t size_read(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); 97 | static uint32_t handle_read(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 98 | static uint32_t size_csum(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); 99 | static uint32_t handle_csum(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 100 | static uint32_t size_crc(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); 101 | static uint32_t handle_crc(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 102 | static uint32_t handle_erase(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 103 | static uint32_t size_write(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); 104 | static uint32_t handle_write(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 105 | static uint32_t handle_seal(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 106 | static uint32_t handle_go(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 107 | static uint32_t handle_info(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 108 | static uint32_t size_reboot(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); 109 | static uint32_t handle_reboot(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 110 | 111 | struct command_desc { 112 | uint32_t opcode; 113 | uint32_t nargs; 114 | uint32_t resp_nargs; 115 | uint32_t (*size)(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); 116 | uint32_t (*handle)(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); 117 | }; 118 | 119 | const struct command_desc cmds[] = { 120 | { 121 | .opcode = CMD_SYNC, 122 | .nargs = 0, 123 | .resp_nargs = 0, 124 | .size = NULL, 125 | .handle = &handle_sync, 126 | }, 127 | { 128 | // READ addr len 129 | // OKOK [data] 130 | .opcode = CMD_READ, 131 | .nargs = 2, 132 | .resp_nargs = 0, 133 | .size = &size_read, 134 | .handle = &handle_read, 135 | }, 136 | { 137 | // CSUM addr len 138 | // OKOK csum 139 | .opcode = CMD_CSUM, 140 | .nargs = 2, 141 | .resp_nargs = 1, 142 | .size = &size_csum, 143 | .handle = &handle_csum, 144 | }, 145 | { 146 | // CRCC addr len 147 | // OKOK crc 148 | .opcode = CMD_CRC, 149 | .nargs = 2, 150 | .resp_nargs = 1, 151 | .size = &size_crc, 152 | .handle = &handle_crc, 153 | }, 154 | { 155 | // ERAS addr len 156 | // OKOK 157 | .opcode = CMD_ERASE, 158 | .nargs = 2, 159 | .resp_nargs = 0, 160 | .size = NULL, 161 | .handle = &handle_erase, 162 | }, 163 | { 164 | // WRIT addr len [data] 165 | // OKOK crc 166 | .opcode = CMD_WRITE, 167 | .nargs = 2, 168 | .resp_nargs = 1, 169 | .size = &size_write, 170 | .handle = &handle_write, 171 | }, 172 | { 173 | // SEAL vtor len crc 174 | // OKOK 175 | .opcode = CMD_SEAL, 176 | .nargs = 3, 177 | .resp_nargs = 0, 178 | .size = NULL, 179 | .handle = &handle_seal, 180 | }, 181 | { 182 | // GOGO vtor 183 | // NO RESPONSE 184 | .opcode = CMD_GO, 185 | .nargs = 1, 186 | .resp_nargs = 0, 187 | .size = NULL, 188 | .handle = &handle_go, 189 | }, 190 | { 191 | // INFO 192 | // OKOK flash_start flash_size erase_size write_size max_data_len 193 | .opcode = CMD_INFO, 194 | .nargs = 0, 195 | .resp_nargs = 5, 196 | .size = NULL, 197 | .handle = &handle_info, 198 | }, 199 | { 200 | // BOOT to_bootloader 201 | // NO RESPONSE 202 | .opcode = CMD_REBOOT, 203 | .nargs = 1, 204 | .resp_nargs = 0, 205 | .size = &size_reboot, 206 | .handle = &handle_reboot, 207 | }, 208 | }; 209 | const unsigned int N_CMDS = (sizeof(cmds) / sizeof(cmds[0])); 210 | const uint32_t MAX_NARG = 5; 211 | const uint32_t MAX_DATA_LEN = 1024; //FLASH_SECTOR_SIZE; 212 | 213 | static bool is_error(uint32_t status) 214 | { 215 | return status == RSP_ERR; 216 | } 217 | 218 | static uint32_t handle_sync(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 219 | { 220 | return RSP_SYNC; 221 | } 222 | 223 | static uint32_t size_read(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) 224 | { 225 | uint32_t size = args_in[1]; 226 | if (size > MAX_DATA_LEN) { 227 | return RSP_ERR; 228 | } 229 | 230 | // TODO: Validate address 231 | 232 | *data_len_out = 0; 233 | *resp_data_len_out = size; 234 | 235 | return RSP_OK; 236 | } 237 | 238 | static uint32_t handle_read(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 239 | { 240 | uint32_t addr = args_in[0]; 241 | uint32_t size = args_in[1]; 242 | 243 | memcpy(resp_data_out, (void *)addr, size); 244 | 245 | return RSP_OK; 246 | } 247 | 248 | static uint32_t size_csum(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) 249 | { 250 | uint32_t addr = args_in[0]; 251 | uint32_t size = args_in[1]; 252 | 253 | if ((addr & 0x3) || (size & 0x3)) { 254 | // Must be aligned 255 | return RSP_ERR; 256 | } 257 | 258 | // TODO: Validate address 259 | 260 | *data_len_out = 0; 261 | *resp_data_len_out = 0; 262 | 263 | return RSP_OK; 264 | } 265 | 266 | static uint32_t handle_csum(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 267 | { 268 | uint32_t dummy_dest; 269 | uint32_t addr = args_in[0]; 270 | uint32_t size = args_in[1]; 271 | 272 | int channel = dma_claim_unused_channel(true); 273 | 274 | dma_channel_config c = dma_channel_get_default_config(channel); 275 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 276 | channel_config_set_read_increment(&c, true); 277 | channel_config_set_write_increment(&c, false); 278 | channel_config_set_sniff_enable(&c, true); 279 | 280 | dma_hw->sniff_data = 0; 281 | dma_sniffer_enable(channel, 0xf, true); 282 | 283 | dma_channel_configure(channel, &c, &dummy_dest, (void *)addr, size / 4, true); 284 | 285 | dma_channel_wait_for_finish_blocking(channel); 286 | 287 | dma_sniffer_disable(); 288 | dma_channel_unclaim(channel); 289 | 290 | *resp_args_out = dma_hw->sniff_data; 291 | 292 | return RSP_OK; 293 | } 294 | 295 | static uint32_t size_crc(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) 296 | { 297 | uint32_t addr = args_in[0]; 298 | uint32_t size = args_in[1]; 299 | 300 | if ((addr & 0x3) || (size & 0x3)) { 301 | // Must be aligned 302 | return RSP_ERR; 303 | } 304 | 305 | // TODO: Validate address 306 | 307 | *data_len_out = 0; 308 | *resp_data_len_out = 0; 309 | 310 | return RSP_OK; 311 | } 312 | 313 | // ptr must be 4-byte aligned and len must be a multiple of 4 314 | static uint32_t calc_crc32(void *ptr, uint32_t len) 315 | { 316 | uint32_t dummy_dest, crc; 317 | 318 | int channel = dma_claim_unused_channel(true); 319 | dma_channel_config c = dma_channel_get_default_config(channel); 320 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 321 | channel_config_set_read_increment(&c, true); 322 | channel_config_set_write_increment(&c, false); 323 | channel_config_set_sniff_enable(&c, true); 324 | 325 | // Seed the CRC calculation 326 | dma_hw->sniff_data = 0xffffffff; 327 | 328 | // Mode 1, then bit-reverse the result gives the same result as 329 | // golang's IEEE802.3 implementation 330 | dma_sniffer_enable(channel, 0x1, true); 331 | dma_hw->sniff_ctrl |= DMA_SNIFF_CTRL_OUT_REV_BITS; 332 | 333 | dma_channel_configure(channel, &c, &dummy_dest, ptr, len / 4, true); 334 | 335 | dma_channel_wait_for_finish_blocking(channel); 336 | 337 | // Read the result before resetting 338 | crc = dma_hw->sniff_data ^ 0xffffffff; 339 | 340 | dma_sniffer_disable(); 341 | dma_channel_unclaim(channel); 342 | 343 | return crc; 344 | } 345 | 346 | static uint32_t handle_crc(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 347 | { 348 | uint32_t addr = args_in[0]; 349 | uint32_t size = args_in[1]; 350 | 351 | resp_args_out[0] = calc_crc32((void *)addr, size); 352 | 353 | return RSP_OK; 354 | } 355 | 356 | static uint32_t handle_erase(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 357 | { 358 | uint32_t addr = args_in[0]; 359 | uint32_t size = args_in[1]; 360 | 361 | if ((addr < ERASE_ADDR_MIN) || (addr + size >= FLASH_ADDR_MAX)) { 362 | // Outside flash 363 | return RSP_ERR; 364 | } 365 | 366 | if ((addr & (FLASH_SECTOR_SIZE - 1)) || (size & (FLASH_SECTOR_SIZE - 1))) { 367 | // Must be aligned 368 | return RSP_ERR; 369 | } 370 | 371 | flash_range_erase(addr - XIP_BASE, size); 372 | 373 | return RSP_OK; 374 | } 375 | 376 | static uint32_t size_write(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) 377 | { 378 | uint32_t addr = args_in[0]; 379 | uint32_t size = args_in[1]; 380 | 381 | if ((addr < WRITE_ADDR_MIN) || (addr + size >= FLASH_ADDR_MAX)) { 382 | // Outside flash 383 | return RSP_ERR; 384 | } 385 | 386 | if ((addr & (FLASH_PAGE_SIZE - 1)) || (size & (FLASH_PAGE_SIZE -1))) { 387 | // Must be aligned 388 | return RSP_ERR; 389 | } 390 | 391 | if (size > MAX_DATA_LEN) { 392 | return RSP_ERR; 393 | } 394 | 395 | // TODO: Validate address 396 | 397 | *data_len_out = size; 398 | *resp_data_len_out = 0; 399 | 400 | return RSP_OK; 401 | } 402 | 403 | static uint32_t handle_write(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 404 | { 405 | uint32_t addr = args_in[0]; 406 | uint32_t size = args_in[1]; 407 | 408 | flash_range_program(addr - XIP_BASE, data_in, size); 409 | 410 | resp_args_out[0] = calc_crc32((void *)addr, size); 411 | 412 | return RSP_OK; 413 | } 414 | 415 | struct image_header { 416 | uint32_t vtor; 417 | uint32_t size; 418 | uint32_t crc; 419 | uint8_t pad[FLASH_PAGE_SIZE - (3 * 4)]; 420 | }; 421 | static_assert(sizeof(struct image_header) == FLASH_PAGE_SIZE, "image_header must be FLASH_PAGE_SIZE bytes"); 422 | 423 | static bool image_header_ok(struct image_header *hdr) 424 | { 425 | uint32_t *vtor = (uint32_t *)hdr->vtor; 426 | 427 | uint32_t calc = calc_crc32((void *)hdr->vtor, hdr->size); 428 | 429 | // CRC has to match 430 | if (calc != hdr->crc) { 431 | return false; 432 | } 433 | 434 | // Stack pointer needs to be in RAM 435 | if (vtor[0] < SRAM_BASE) { 436 | return false; 437 | } 438 | 439 | // Reset vector should be in the image, and thumb (bit 0 set) 440 | if ((vtor[1] < hdr->vtor) || (vtor[1] > hdr->vtor + hdr->size) || !(vtor[1] & 1)) { 441 | return false; 442 | } 443 | 444 | // Looks OK. 445 | return true; 446 | } 447 | 448 | 449 | static uint32_t handle_seal(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 450 | { 451 | struct image_header hdr = { 452 | .vtor = args_in[0], 453 | .size = args_in[1], 454 | .crc = args_in[2], 455 | }; 456 | 457 | if ((hdr.vtor & 0xff) || (hdr.size & 0x3)) { 458 | // Must be aligned 459 | return RSP_ERR; 460 | } 461 | 462 | if (!image_header_ok(&hdr)) { 463 | return RSP_ERR; 464 | } 465 | 466 | flash_range_erase(IMAGE_HEADER_OFFSET, FLASH_SECTOR_SIZE); 467 | flash_range_program(IMAGE_HEADER_OFFSET, (const uint8_t *)&hdr, sizeof(hdr)); 468 | 469 | struct image_header *check = (struct image_header *)(XIP_BASE + IMAGE_HEADER_OFFSET); 470 | if (memcmp(&hdr, check, sizeof(hdr))) { 471 | return RSP_ERR; 472 | } 473 | 474 | return RSP_OK; 475 | } 476 | 477 | static uint32_t handle_go(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 478 | { 479 | disable_interrupts(); 480 | 481 | reset_peripherals(); 482 | 483 | jump_to_vtor(args_in[0]); 484 | 485 | while(1); 486 | 487 | return RSP_ERR; 488 | } 489 | 490 | static uint32_t handle_info(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 491 | { 492 | resp_args_out[0] = WRITE_ADDR_MIN; 493 | resp_args_out[1] = (XIP_BASE + PICO_FLASH_SIZE_BYTES) - WRITE_ADDR_MIN; 494 | resp_args_out[2] = FLASH_SECTOR_SIZE; 495 | resp_args_out[3] = FLASH_PAGE_SIZE; 496 | resp_args_out[4] = MAX_DATA_LEN; 497 | 498 | return RSP_OK; 499 | } 500 | 501 | static void do_reboot(bool to_bootloader) 502 | { 503 | hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS); 504 | if (to_bootloader) { 505 | watchdog_hw->scratch[5] = BOOTLOADER_ENTRY_MAGIC; 506 | watchdog_hw->scratch[6] = ~BOOTLOADER_ENTRY_MAGIC; 507 | } else { 508 | watchdog_hw->scratch[5] = 0; 509 | watchdog_hw->scratch[6] = 0; 510 | } 511 | watchdog_reboot(0, 0, 0); 512 | while (1) { 513 | tight_loop_contents(); 514 | asm(""); 515 | } 516 | } 517 | 518 | static uint32_t size_reboot(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) 519 | { 520 | *data_len_out = 0; 521 | *resp_data_len_out = 0; 522 | 523 | return RSP_OK; 524 | } 525 | 526 | static uint32_t handle_reboot(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) 527 | { 528 | // Will never return 529 | do_reboot(args_in[0]); 530 | 531 | return RSP_ERR; 532 | } 533 | 534 | static const struct command_desc *find_command_desc(uint32_t opcode) 535 | { 536 | unsigned int i; 537 | 538 | for (i = 0; i < N_CMDS; i++) { 539 | if (cmds[i].opcode == opcode) { 540 | return &cmds[i]; 541 | } 542 | } 543 | 544 | return NULL; 545 | } 546 | 547 | struct cmd_context { 548 | uint8_t *uart_buf; 549 | const struct command_desc *desc; 550 | uint32_t opcode; 551 | uint32_t status; 552 | uint32_t *args; 553 | uint8_t *data; 554 | uint32_t *resp_args; 555 | uint8_t *resp_data; 556 | uint32_t data_len; 557 | uint32_t resp_data_len; 558 | }; 559 | 560 | enum state { 561 | STATE_WAIT_FOR_SYNC, 562 | STATE_READ_OPCODE, 563 | STATE_READ_ARGS, 564 | STATE_READ_DATA, 565 | STATE_HANDLE_DATA, 566 | STATE_ERROR, 567 | }; 568 | 569 | static enum state state_wait_for_sync(struct cmd_context *ctx) 570 | { 571 | int idx = 0; 572 | uint8_t *recv = (uint8_t *)&ctx->opcode; 573 | uint8_t *match = (uint8_t *)&ctx->status; 574 | 575 | ctx->status = CMD_SYNC; 576 | 577 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 578 | 579 | while (idx < sizeof(ctx->opcode)) { 580 | uart_read_blocking(uart0, &recv[idx], 1); 581 | gpio_xor_mask((1 << PICO_DEFAULT_LED_PIN)); 582 | 583 | if (recv[idx] != match[idx]) { 584 | // Start again 585 | idx = 0; 586 | } else { 587 | // Move on 588 | idx++; 589 | } 590 | } 591 | 592 | assert(ctx->opcode == CMD_SYNC); 593 | 594 | return STATE_READ_ARGS; 595 | } 596 | 597 | static enum state state_read_opcode(struct cmd_context *ctx) 598 | { 599 | uart_read_blocking(uart0, (uint8_t *)&ctx->opcode, sizeof(ctx->opcode)); 600 | 601 | return STATE_READ_ARGS; 602 | } 603 | 604 | static enum state state_read_args(struct cmd_context *ctx) 605 | { 606 | const struct command_desc *desc = find_command_desc(ctx->opcode); 607 | if (!desc) { 608 | // TODO: Error handler that can do args? 609 | ctx->status = RSP_ERR; 610 | return STATE_ERROR; 611 | } 612 | 613 | ctx->desc = desc; 614 | ctx->args = (uint32_t *)(ctx->uart_buf + sizeof(ctx->opcode)); 615 | ctx->data = (uint8_t *)(ctx->args + desc->nargs); 616 | ctx->resp_args = ctx->args; 617 | ctx->resp_data = (uint8_t *)(ctx->resp_args + desc->resp_nargs); 618 | 619 | uart_read_blocking(uart0, (uint8_t *)ctx->args, sizeof(*ctx->args) * desc->nargs); 620 | 621 | return STATE_READ_DATA; 622 | } 623 | 624 | static enum state state_read_data(struct cmd_context *ctx) 625 | { 626 | const struct command_desc *desc = ctx->desc; 627 | 628 | if (desc->size) { 629 | ctx->status = desc->size(ctx->args, &ctx->data_len, &ctx->resp_data_len); 630 | if (is_error(ctx->status)) { 631 | return STATE_ERROR; 632 | } 633 | } else { 634 | ctx->data_len = 0; 635 | ctx->resp_data_len = 0; 636 | } 637 | 638 | // TODO: Check sizes 639 | 640 | uart_read_blocking(uart0, (uint8_t *)ctx->data, ctx->data_len); 641 | 642 | return STATE_HANDLE_DATA; 643 | } 644 | 645 | static enum state state_handle_data(struct cmd_context *ctx) 646 | { 647 | const struct command_desc *desc = ctx->desc; 648 | 649 | if (desc->handle) { 650 | ctx->status = desc->handle(ctx->args, ctx->data, ctx->resp_args, ctx->resp_data); 651 | if (is_error(ctx->status)) { 652 | return STATE_ERROR; 653 | } 654 | } else { 655 | // TODO: Should we just assert(desc->handle)? 656 | ctx->status = RSP_OK; 657 | } 658 | 659 | size_t resp_len = sizeof(ctx->status) + (sizeof(*ctx->resp_args) * desc->resp_nargs) + ctx->resp_data_len; 660 | memcpy(ctx->uart_buf, &ctx->status, sizeof(ctx->status)); 661 | uart_write_blocking(uart0, ctx->uart_buf, resp_len); 662 | 663 | return STATE_READ_OPCODE; 664 | } 665 | 666 | static enum state state_error(struct cmd_context *ctx) 667 | { 668 | size_t resp_len = sizeof(ctx->status); 669 | memcpy(ctx->uart_buf, &ctx->status, sizeof(ctx->status)); 670 | uart_write_blocking(uart0, ctx->uart_buf, resp_len); 671 | 672 | return STATE_WAIT_FOR_SYNC; 673 | } 674 | 675 | static bool should_stay_in_bootloader() 676 | { 677 | bool wd_says_so = (watchdog_hw->scratch[5] == BOOTLOADER_ENTRY_MAGIC) && 678 | (watchdog_hw->scratch[6] == ~BOOTLOADER_ENTRY_MAGIC); 679 | 680 | return !gpio_get(BOOTLOADER_ENTRY_PIN) || wd_says_so; 681 | } 682 | 683 | int main(void) 684 | { 685 | gpio_init(PICO_DEFAULT_LED_PIN); 686 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 687 | gpio_put(PICO_DEFAULT_LED_PIN, 1); 688 | 689 | gpio_init(BOOTLOADER_ENTRY_PIN); 690 | gpio_pull_up(BOOTLOADER_ENTRY_PIN); 691 | gpio_set_dir(BOOTLOADER_ENTRY_PIN, 0); 692 | 693 | sleep_ms(10); 694 | 695 | struct image_header *hdr = (struct image_header *)(XIP_BASE + IMAGE_HEADER_OFFSET); 696 | 697 | if (!should_stay_in_bootloader() && image_header_ok(hdr)) { 698 | uint32_t vtor = *((uint32_t *)(XIP_BASE + IMAGE_HEADER_OFFSET)); 699 | disable_interrupts(); 700 | reset_peripherals(); 701 | jump_to_vtor(vtor); 702 | } 703 | 704 | DBG_PRINTF_INIT(); 705 | 706 | uart_init(uart0, UART_BAUD); 707 | gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); 708 | gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); 709 | uart_set_hw_flow(uart0, false, false); 710 | 711 | struct cmd_context ctx; 712 | uint8_t uart_buf[(sizeof(uint32_t) * (1 + MAX_NARG)) + MAX_DATA_LEN]; 713 | ctx.uart_buf = uart_buf; 714 | enum state state = STATE_WAIT_FOR_SYNC; 715 | 716 | while (1) { 717 | switch (state) { 718 | case STATE_WAIT_FOR_SYNC: 719 | DBG_PRINTF("wait_for_sync\n"); 720 | state = state_wait_for_sync(&ctx); 721 | DBG_PRINTF("wait_for_sync done\n"); 722 | break; 723 | case STATE_READ_OPCODE: 724 | DBG_PRINTF("read_opcode\n"); 725 | state = state_read_opcode(&ctx); 726 | DBG_PRINTF("read_opcode done\n"); 727 | break; 728 | case STATE_READ_ARGS: 729 | DBG_PRINTF("read_args\n"); 730 | state = state_read_args(&ctx); 731 | DBG_PRINTF("read_args done\n"); 732 | break; 733 | case STATE_READ_DATA: 734 | DBG_PRINTF("read_data\n"); 735 | state = state_read_data(&ctx); 736 | DBG_PRINTF("read_data done\n"); 737 | break; 738 | case STATE_HANDLE_DATA: 739 | DBG_PRINTF("handle_data\n"); 740 | state = state_handle_data(&ctx); 741 | DBG_PRINTF("handle_data done\n"); 742 | break; 743 | case STATE_ERROR: 744 | DBG_PRINTF("error\n"); 745 | state = state_error(&ctx); 746 | DBG_PRINTF("error done\n"); 747 | break; 748 | } 749 | } 750 | 751 | return 0; 752 | } 753 | -------------------------------------------------------------------------------- /mkasm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Derived from pad_checksum in the Pico SDK, which carries the following 3 | # LICENSE.txt: 4 | # Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 5 | # 6 | # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 7 | # following conditions are met: 8 | # 9 | # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 10 | # disclaimer. 11 | # 12 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 19 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | import argparse 27 | import binascii 28 | import struct 29 | import sys 30 | 31 | parser = argparse.ArgumentParser() 32 | parser.add_argument("ifile", help="Input file (binary)") 33 | parser.add_argument("ofile", help="Output file (assembly)") 34 | args = parser.parse_args() 35 | 36 | try: 37 | idata = open(args.ifile, "rb").read() 38 | except: 39 | sys.exit("Could not open input file '{}'".format(args.ifile)) 40 | 41 | odata = idata 42 | 43 | try: 44 | with open(args.ofile, "w") as ofile: 45 | ofile.write("// ASM-ified version of: {}\n\n".format(args.ifile)) 46 | ofile.write(".cpu cortex-m0plus\n") 47 | ofile.write(".thumb\n\n") 48 | ofile.write(".section .boot3, \"ax\"\n\n") 49 | for offs in range(0, len(odata), 16): 50 | chunk = odata[offs:min(offs + 16, len(odata))] 51 | ofile.write(".byte {}\n".format(", ".join("0x{:02x}".format(b) for b in chunk))) 52 | except: 53 | sys.exit("Could not open output file '{}'".format(args.ofile)) 54 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /standalone.ld: -------------------------------------------------------------------------------- 1 | /* Based on GCC ARM embedded samples. 2 | Defines the following symbols for use by code: 3 | __exidx_start 4 | __exidx_end 5 | __etext 6 | __data_start__ 7 | __preinit_array_start 8 | __preinit_array_end 9 | __init_array_start 10 | __init_array_end 11 | __fini_array_start 12 | __fini_array_end 13 | __data_end__ 14 | __bss_start__ 15 | __bss_end__ 16 | __end__ 17 | end 18 | __HeapLimit 19 | __StackLimit 20 | __StackTop 21 | __stack (== StackTop) 22 | */ 23 | 24 | /* Skip 16kB at the start of flash, that's where our bootloader is */ 25 | MEMORY 26 | { 27 | FLASH(rx) : ORIGIN = 0x10000000 + 16k, LENGTH = 2048k - 16k 28 | RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 29 | SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k 30 | SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k 31 | } 32 | 33 | ENTRY(_entry_point) 34 | 35 | SECTIONS 36 | { 37 | .flash_begin : { 38 | __flash_binary_start = .; 39 | } > FLASH 40 | 41 | /* boot2 would go here, but we don't want it */ 42 | 43 | .text : { 44 | __logical_binary_start = .; 45 | KEEP (*(.vectors)) 46 | KEEP (*(.binary_info_header)) 47 | __binary_info_header_end = .; 48 | KEEP (*(.reset)) 49 | /* TODO revisit this now memset/memcpy/float in ROM */ 50 | /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from 51 | * FLASH ... we will include any thing excluded here in .data below by default */ 52 | *(.init) 53 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) 54 | *(.fini) 55 | /* Pull all c'tors into .text */ 56 | *crtbegin.o(.ctors) 57 | *crtbegin?.o(.ctors) 58 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 59 | *(SORT(.ctors.*)) 60 | *(.ctors) 61 | /* Followed by destructors */ 62 | *crtbegin.o(.dtors) 63 | *crtbegin?.o(.dtors) 64 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 65 | *(SORT(.dtors.*)) 66 | *(.dtors) 67 | 68 | *(.eh_frame*) 69 | . = ALIGN(4); 70 | } > FLASH 71 | 72 | .rodata : { 73 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) 74 | . = ALIGN(4); 75 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) 76 | . = ALIGN(4); 77 | } > FLASH 78 | 79 | .ARM.extab : 80 | { 81 | *(.ARM.extab* .gnu.linkonce.armextab.*) 82 | } > FLASH 83 | 84 | __exidx_start = .; 85 | .ARM.exidx : 86 | { 87 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 88 | } > FLASH 89 | __exidx_end = .; 90 | 91 | /* Machine inspectable binary information */ 92 | . = ALIGN(4); 93 | __binary_info_start = .; 94 | .binary_info : 95 | { 96 | KEEP(*(.binary_info.keep.*)) 97 | *(.binary_info.*) 98 | } > FLASH 99 | __binary_info_end = .; 100 | . = ALIGN(4); 101 | 102 | /* End of .text-like segments */ 103 | __etext = .; 104 | 105 | .ram_vector_table (COPY): { 106 | *(.ram_vector_table) 107 | } > RAM 108 | 109 | .data : { 110 | __data_start__ = .; 111 | *(vtable) 112 | 113 | *(.time_critical*) 114 | 115 | /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ 116 | *(.text*) 117 | . = ALIGN(4); 118 | *(.rodata*) 119 | . = ALIGN(4); 120 | 121 | *(.data*) 122 | 123 | . = ALIGN(4); 124 | *(.after_data.*) 125 | . = ALIGN(4); 126 | /* preinit data */ 127 | PROVIDE_HIDDEN (__mutex_array_start = .); 128 | KEEP(*(SORT(.mutex_array.*))) 129 | KEEP(*(.mutex_array)) 130 | PROVIDE_HIDDEN (__mutex_array_end = .); 131 | 132 | . = ALIGN(4); 133 | /* preinit data */ 134 | PROVIDE_HIDDEN (__preinit_array_start = .); 135 | KEEP(*(SORT(.preinit_array.*))) 136 | KEEP(*(.preinit_array)) 137 | PROVIDE_HIDDEN (__preinit_array_end = .); 138 | 139 | . = ALIGN(4); 140 | /* init data */ 141 | PROVIDE_HIDDEN (__init_array_start = .); 142 | KEEP(*(SORT(.init_array.*))) 143 | KEEP(*(.init_array)) 144 | PROVIDE_HIDDEN (__init_array_end = .); 145 | 146 | . = ALIGN(4); 147 | /* finit data */ 148 | PROVIDE_HIDDEN (__fini_array_start = .); 149 | *(SORT(.fini_array.*)) 150 | *(.fini_array) 151 | PROVIDE_HIDDEN (__fini_array_end = .); 152 | 153 | *(.jcr) 154 | . = ALIGN(4); 155 | /* All data end */ 156 | __data_end__ = .; 157 | } > RAM AT> FLASH 158 | 159 | .uninitialized_data (COPY): { 160 | . = ALIGN(4); 161 | *(.uninitialized_data*) 162 | } > RAM 163 | 164 | /* Start and end symbols must be word-aligned */ 165 | .scratch_x : { 166 | __scratch_x_start__ = .; 167 | *(.scratch_x.*) 168 | . = ALIGN(4); 169 | __scratch_x_end__ = .; 170 | } > SCRATCH_X AT > FLASH 171 | __scratch_x_source__ = LOADADDR(.scratch_x); 172 | 173 | .scratch_y : { 174 | __scratch_y_start__ = .; 175 | *(.scratch_y.*) 176 | . = ALIGN(4); 177 | __scratch_y_end__ = .; 178 | } > SCRATCH_Y AT > FLASH 179 | __scratch_y_source__ = LOADADDR(.scratch_y); 180 | 181 | .bss : { 182 | . = ALIGN(4); 183 | __bss_start__ = .; 184 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) 185 | *(COMMON) 186 | . = ALIGN(4); 187 | __bss_end__ = .; 188 | } > RAM 189 | 190 | .heap (COPY): 191 | { 192 | __end__ = .; 193 | end = __end__; 194 | *(.heap*) 195 | __HeapLimit = .; 196 | } > RAM 197 | 198 | /* .stack*_dummy section doesn't contains any symbols. It is only 199 | * used for linker to calculate size of stack sections, and assign 200 | * values to stack symbols later 201 | * 202 | * stack1 section may be empty/missing if platform_launch_core1 is not used */ 203 | 204 | /* by default we put core 0 stack at the end of scratch Y, so that if core 1 205 | * stack is not used then all of SCRATCH_X is free. 206 | */ 207 | .stack1_dummy (COPY): 208 | { 209 | *(.stack1*) 210 | } > SCRATCH_X 211 | .stack_dummy (COPY): 212 | { 213 | *(.stack*) 214 | } > SCRATCH_Y 215 | 216 | .flash_end : { 217 | __flash_binary_end = .; 218 | } > FLASH 219 | 220 | /* stack limit is poorly named, but historically is maximum heap ptr */ 221 | __StackLimit = ORIGIN(RAM) + LENGTH(RAM); 222 | __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); 223 | __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); 224 | __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); 225 | __StackBottom = __StackTop - SIZEOF(.stack_dummy); 226 | PROVIDE(__stack = __StackTop); 227 | 228 | /* Check if data + heap + stack exceeds RAM limit */ 229 | ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") 230 | 231 | ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") 232 | /* todo assert on extra code */ 233 | } 234 | 235 | --------------------------------------------------------------------------------