diff --git a/.bazelignore b/.bazelignore
new file mode 100644
index 0000000..dd2e8d2
--- /dev/null
+++ b/.bazelignore
@@ -0,0 +1 @@
+lib/pico-sdk
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..4b19b28
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1 @@
+common --verbose_failures
diff --git a/.github/workflows/bazel_build.yml b/.github/workflows/bazel_build.yml
new file mode 100644
index 0000000..2e775c7
--- /dev/null
+++ b/.github/workflows/bazel_build.yml
@@ -0,0 +1,36 @@
+name: Bazel presubmit checks
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ bazel-build-check:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ fail-fast: false
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Get Bazel
+ uses: bazel-contrib/setup-bazel@0.9.0
+ with:
+ # Avoid downloading Bazel every time.
+ bazelisk-cache: true
+ # Store build cache per workflow.
+ disk-cache: ${{ github.workflow }}
+ # Share repository cache between workflows.
+ repository-cache: true
+ - name: Fetch latest Pico SDK
+ uses: actions/checkout@v4
+ with:
+ repository: raspberrypi/pico-sdk
+ ref: develop
+ fetch-depth: 0
+ path: lib/pico-sdk
+ - name: Bazel Picotool with develop pico-sdk
+ run: bazel build @picotool//:picotool --override_module=pico-sdk=lib/pico-sdk
diff --git a/.github/workflows/choco_packages.config b/.github/workflows/choco_packages.config
new file mode 100644
index 0000000..b2d3db1
--- /dev/null
+++ b/.github/workflows/choco_packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..85decb5
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,73 @@
+on:
+ push:
+ pull_request:
+
+jobs:
+ build:
+ # Prevent running twice for PRs from same repo
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ name: Build & Test
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ generator: ["Ninja", "Unix Makefiles"]
+ mbedtls: ["mbedtls", ""]
+ libusb: ["libusb", ""]
+ compile: ["compile", ""]
+ exclude:
+ - os: 'windows-latest'
+ generator: "Unix Makefiles"
+ - libusb: ""
+ compile: "compile"
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Install dependencies (Windows)
+ if: runner.os == 'Windows'
+ run: |
+ choco install -y .github/workflows/choco_packages.config
+ curl -L https://github.com/libusb/libusb/releases/download/v1.0.27/libusb-1.0.27.7z -o libusb.7z
+ 7z x libusb.7z -olibusb
+ - name: Set LIBUSB_ROOT (Windows)
+ if: runner.os == 'Windows'
+ shell: bash
+ run: echo "LIBUSB_ROOT=$(pwd)/libusb" >> "$GITHUB_ENV"
+
+ - name: Install dependencies (macOS)
+ if: runner.os == 'macOS'
+ run: |
+ brew install libusb ninja
+ brew install --cask gcc-arm-embedded
+
+ - name: Install dependencies (Linux)
+ if: runner.os == 'Linux'
+ run: sudo apt install cmake ninja-build python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib libusb-1.0-0-dev
+ - name: Checkout Pico SDK
+ uses: actions/checkout@v4
+ with:
+ repository: raspberrypi/pico-sdk
+ ref: develop
+ path: pico-sdk
+ submodules: ${{ !(!matrix.mbedtls) }}
+
+ - name: Build and Install
+ run: |
+ cmake -S . -B build -G "${{ matrix.generator }}" -D PICO_SDK_PATH="${{ github.workspace }}/pico-sdk" ${{ !matrix.libusb && '-D PICOTOOL_NO_LIBUSB=1' || '' }} ${{ matrix.compile && '-D USE_PRECOMPILED=false' || '' }}
+ cmake --build build
+ ${{ runner.os != 'Windows' && 'sudo' || '' }} cmake --install build
+ - name: Add to path (Windows)
+ if: runner.os == 'Windows'
+ run: echo "C:\Program Files (x86)\picotool\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+
+ - name: Test
+ run: |
+ picotool help
+ curl -L https://datasheets.raspberrypi.com/soft/blink.uf2 -o blink.uf2
+ curl -L https://datasheets.raspberrypi.com/soft/hello_world.uf2 -o hello_world.uf2
+ curl -L https://datasheets.raspberrypi.com/soft/flash_nuke.uf2 -o flash_nuke.uf2
+ picotool info -a blink.uf2
+ picotool info -a hello_world.uf2
+ picotool info -a flash_nuke.uf2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..804c311
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+build/*
+bazel-*
+
+# Ignore until https://github.com/bazelbuild/bazel/issues/20369 is fixed.
+MODULE.bazel.lock
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..d120f50
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,123 @@
+load("//bazel:defs.bzl", "otp_header_parse", "picotool_binary_data_header")
+
+package(default_visibility = ["//visibility:public"])
+
+PICOTOOL_SDK_VERSION_STRING = module_version() if module_version() != None else "0.0.1-WORKSPACE"
+
+picotool_binary_data_header(
+ name = "rp2350_rom",
+ src = "bootrom.end.bin",
+ out = "rp2350.rom.h",
+)
+
+# TODO: Make it possible to build the prebuilt from source.
+picotool_binary_data_header(
+ name = "xip_ram_perms_elf",
+ src = "//xip_ram_perms:xip_ram_perms_prebuilt",
+ out = "xip_ram_perms_elf.h",
+)
+
+# TODO: Make it possible to build the prebuilt from source.
+picotool_binary_data_header(
+ name = "flash_id_bin",
+ src = "//picoboot_flash_id:picoboot_flash_id_prebuilt",
+ out = "flash_id_bin.h",
+)
+
+cc_library(
+ name = "xip_ram_perms",
+ srcs = ["xip_ram_perms.cpp"],
+ hdrs = [
+ "xip_ram_perms.h",
+ "xip_ram_perms_elf.h",
+ ],
+ deps = [
+ "//bazel:data_locs",
+ "//lib/whereami",
+ ],
+)
+
+filegroup(
+ name = "data_locs_header",
+ srcs = ["data_locs.h"],
+)
+
+otp_header_parse(
+ name = "otp_header",
+ src = "@pico-sdk//src/rp2350/hardware_regs:otp_data_header",
+ out = "rp2350.json.h",
+ target_compatible_with = select({
+ "@rules_cc//cc/compiler:msvc-cl": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ }),
+)
+
+cc_binary(
+ name = "picotool",
+ srcs = [
+ "cli.h",
+ "clipp/clipp.h",
+ "main.cpp",
+ "otp.cpp",
+ "otp.h",
+ "rp2350.rom.h",
+ "xip_ram_perms.cpp",
+ ] + select({
+ # MSVC can't handle long strings, so use this manually generated
+ # header instead.
+ "@rules_cc//cc/compiler:msvc-cl": [],
+ "//conditions:default": ["rp2350.json.h"],
+ }),
+ copts = select({
+ "@rules_cc//cc/compiler:msvc-cl": [
+ "/std:c++20",
+ ],
+ "//conditions:default": [
+ "-fexceptions",
+ "-Wno-delete-non-abstract-non-virtual-dtor",
+ "-Wno-reorder-ctor",
+ "-Wno-unused-variable",
+ "-Wno-unused-but-set-variable",
+ ],
+ }),
+ defines = [
+ 'PICOTOOL_VERSION=\\"{}\\"'.format(PICOTOOL_SDK_VERSION_STRING),
+ 'SYSTEM_VERSION=\\"host\\"',
+ 'COMPILER_INFO=\\"local\\"',
+ "SUPPORT_A0=0",
+ "SUPPORT_A2=1",
+ "PICOTOOL_CODE_OTP=0",
+ # TODO: Make it possible to compile from source.
+ "USE_PRECOMPILED=1",
+ ],
+ # Windows does not behave nicely with the automagic force_dynamic_linkage_enabled.
+ dynamic_deps = select({
+ "@rules_libusb//:force_dynamic_linkage_enabled": ["@libusb//:libusb_dynamic"],
+ "//conditions:default": [],
+ }),
+ deps = [
+ ":xip_ram_perms",
+ "//bazel:data_locs",
+ "//bintool",
+ "//elf",
+ "//elf2uf2",
+ "//errors",
+ "//lib/nlohmann_json:json",
+ "//picoboot_connection",
+ "@libusb",
+ "@pico-sdk//src/common/boot_picobin_headers",
+ "@pico-sdk//src/common/boot_picoboot_headers",
+ "@pico-sdk//src/common/boot_uf2_headers",
+ "@pico-sdk//src/common/pico_base_headers",
+ "@pico-sdk//src/common/pico_binary_info",
+ "@pico-sdk//src/common/pico_usb_reset_interface_headers",
+ "@pico-sdk//src/rp2350/hardware_regs:otp_data",
+ "@pico-sdk//src/rp2_common/pico_bootrom:pico_bootrom_headers",
+ "@pico-sdk//src/rp2_common/pico_stdio_usb:reset_interface_headers",
+ ] + select({
+ # MSVC can't handle long strings, so use this manually generated
+ # header instead.
+ "@rules_cc//cc/compiler:msvc-cl": ["//otp_header_parser:pre_generated_otp_header"],
+ "//conditions:default": [],
+ }),
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff3e787..adb47ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,46 +19,327 @@ if (NOT EXISTS ${PICO_SDK_PATH})
endif ()
include(${PICO_SDK_PATH}/pico_sdk_version.cmake)
-if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
- message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
+if (PICO_SDK_VERSION_STRING VERSION_LESS "2.0.0")
+ message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.0.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
-set(CMAKE_CXX_STANDARD 14)
+# Set PICOTOOL_CODE_OTP to compile OTP definitions in - otherwise, they are included from JSON
+if (NOT PICOTOOL_CODE_OTP)
+ set(PICOTOOL_CODE_OTP 0)
+endif()
+
+# allow installing to flat dir
+include(GNUInstallDirs)
+if (PICOTOOL_FLAT_INSTALL)
+ set(INSTALL_CONFIGDIR picotool)
+ set(INSTALL_DATADIR picotool)
+ set(INSTALL_BINDIR picotool)
+else()
+ set(INSTALL_CONFIGDIR lib/cmake/picotool)
+ set(INSTALL_DATADIR ${CMAKE_INSTALL_DATADIR}/picotool)
+ set(INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR})
+endif()
+
+# todo better install paths for this
+set(DATA_LOCS "./" "${CMAKE_INSTALL_PREFIX}/${INSTALL_DATADIR}/")
+message(${DATA_LOCS})
+string(REGEX REPLACE ";" "\",\"" DATA_LOCS_VEC "${DATA_LOCS}")
+configure_file(data_locs.template.cpp ${CMAKE_CURRENT_BINARY_DIR}/data_locs.cpp)
+
+
+include(ExternalProject)
+
+if (MSVC)
+ set(CMAKE_CXX_STANDARD 20)
+else()
+ set(CMAKE_CXX_STANDARD 14)
+endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
-add_subdirectory(picoboot_connection)
-
-find_package(LIBUSB)
-if (NOT LIBUSB_FOUND)
- message(FATAL_ERROR "picotool cannot be built because libUSB is not found")
-else()
- add_subdirectory(${PICO_SDK_PATH}/src/common/pico_binary_info pico_binary_info)
- add_subdirectory(${PICO_SDK_PATH}/src/common/boot_uf2 boot_uf2_headers)
- add_subdirectory(${PICO_SDK_PATH}/src/common/boot_picoboot boot_picoboot_headers)
- add_subdirectory(${PICO_SDK_PATH}/src/common/pico_usb_reset_interface pico_usb_reset_interface)
- add_subdirectory(${PICO_SDK_PATH}/src/host/pico_platform pico_platform)
-
- add_executable(picotool main.cpp)
- set(PICOTOOL_VERSION 1.1.2)
- set(SYSTEM_VERSION "${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}")
- set(COMPILER_INFO "${CMAKE_C_COMPILER_ID}-${CMAKE_C_COMPILER_VERSION}, ${CMAKE_BUILD_TYPE}")
- target_compile_definitions(picotool PRIVATE
- PICOTOOL_VERSION="${PICOTOOL_VERSION}"
- SYSTEM_VERSION="${SYSTEM_VERSION}"
- COMPILER_INFO="${COMPILER_INFO}"
+if (NOT PICOTOOL_NO_LIBUSB)
+ # compile xip_ram_perms.elf
+ if (NOT DEFINED USE_PRECOMPILED)
+ set(USE_PRECOMPILED true)
+ endif()
+ ExternalProject_Add(xip_ram_perms
+ PREFIX xip_ram_perms
+ SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/xip_ram_perms
+ BINARY_DIR ${CMAKE_BINARY_DIR}/xip_ram_perms
+ CMAKE_ARGS
+ "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}"
+ "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}"
+ "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}"
+ "-DPICO_DEBUG_INFO_IN_RELEASE=OFF"
+ BUILD_ALWAYS 1 # todo remove this
+ INSTALL_COMMAND ""
)
- target_include_directories(picotool PRIVATE ${LIBUSB_INCLUDE_DIR})
- # todo, this is a bit of an abstraction failure; but don't want to rev the SDK just for this right now
- target_include_directories(picotool PRIVATE ${PICO_SDK_PATH}/src/rp2_common/pico_stdio_usb/include)
- target_link_libraries(picotool
- pico_binary_info
- boot_uf2_headers
- boot_picoboot_headers
- pico_platform_headers
- pico_usb_reset_interface_headers
- picoboot_connection_cxx
- ${LIBUSB_LIBRARIES})
- # allow `make install`
- install(TARGETS picotool RUNTIME DESTINATION bin)
+
+ set(XIP_RAM_PERMS_ELF ${CMAKE_BINARY_DIR}/xip_ram_perms/xip_ram_perms.elf)
+ add_executable(xip_ram_perms_elf IMPORTED)
+ add_dependencies(xip_ram_perms_elf xip_ram_perms)
+ set_property(TARGET xip_ram_perms_elf PROPERTY IMPORTED_LOCATION ${XIP_RAM_PERMS_ELF})
+ # copy xip_ram_perms.elf into build directory
+ add_custom_command(TARGET xip_ram_perms
+ COMMAND ${CMAKE_COMMAND} -E copy ${XIP_RAM_PERMS_ELF} ${CMAKE_BINARY_DIR}/xip_ram_perms.elf
+ DEPENDS xip_ram_perms
+ )
+
+ # compile flash_id
+ ExternalProject_Add(flash_id
+ PREFIX picoboot_flash_id
+ SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/picoboot_flash_id
+ BINARY_DIR ${CMAKE_BINARY_DIR}/picoboot_flash_id
+ CMAKE_ARGS
+ "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}"
+ "-DPICO_SDK_PATH:FILEPATH=${PICO_SDK_PATH}"
+ "-DUSE_PRECOMPILED:BOOL=${USE_PRECOMPILED}"
+ "-DPICO_DEBUG_INFO_IN_RELEASE=OFF"
+ BUILD_ALWAYS 1 # todo remove this
+ INSTALL_COMMAND ""
+ )
+
+ set(FLASH_ID_BIN ${CMAKE_BINARY_DIR}/picoboot_flash_id/flash_id.bin)
+ add_executable(flash_id_bin IMPORTED)
+ add_dependencies(flash_id_bin flash_id)
+ set_property(TARGET flash_id_bin PROPERTY IMPORTED_LOCATION ${FLASH_ID_BIN})
+ # copy flash_id.bin into build directory
+ add_custom_command(TARGET flash_id
+ COMMAND ${CMAKE_COMMAND} -E copy ${FLASH_ID_BIN} ${CMAKE_BINARY_DIR}/flash_id.bin
+ DEPENDS flash_id
+ )
+
+ # We want to generate headers from WELCOME.HTM etc.
+ ExternalProject_Add(otp_header_parser
+ PREFIX otp_header_parser
+ SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/otp_header_parser
+ BINARY_DIR ${CMAKE_BINARY_DIR}/otp_header_parser
+ CMAKE_ARGS "-DCODE_OTP=${PICOTOOL_CODE_OTP}"
+ BUILD_ALWAYS 1 # todo remove this
+ DOWNLOAD_COMMAND ""
+ INSTALL_COMMAND ""
+ )
+
+ add_executable(otp_header_parse IMPORTED)
+ # think this is the best way to do this - this should work in MSVC now, and possibly Xcode but that's untested
+ add_dependencies(otp_header_parse otp_header_parser)
+ get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+ if (is_multi_config)
+ # use the first config
+ list(GET CMAKE_CONFIGURATION_TYPES 0 tmp_config)
+ set_property(TARGET otp_header_parse PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/otp_header_parser/${tmp_config}/otp_header_parse)
+ else()
+ set_property(TARGET otp_header_parse PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/otp_header_parser/otp_header_parse)
+ endif()
+
+ if (PICOTOOL_CODE_OTP)
+ set(GENERATED_H ${CMAKE_CURRENT_BINARY_DIR}/otp_contents.h)
+ add_custom_target(generate_otp_header DEPENDS ${GENERATED_H})
+ add_custom_command(OUTPUT ${GENERATED_H}
+ COMMENT "Generating ${GENERATED_H}"
+ DEPENDS ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include/hardware/regs/otp_data.h
+ COMMAND otp_header_parse ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include/hardware/regs/otp_data.h ${GENERATED_H}
+ )
+ elseif(MSVC)
+ set(GENERATED_JSON ${CMAKE_CURRENT_BINARY_DIR}/rp2350_otp_contents.json)
+ add_custom_target(generate_otp_header DEPENDS ${GENERATED_JSON})
+ add_custom_command(OUTPUT ${GENERATED_JSON}
+ COMMENT "Generating ${GENERATED_JSON}"
+ DEPENDS ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include/hardware/regs/otp_data.h
+ COMMAND otp_header_parse ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include/hardware/regs/otp_data.h ${GENERATED_JSON}
+ )
+ # Cannot include json in the binary, as the string is too long, so needs to use pre-generated xxd output
+ configure_file(${CMAKE_CURRENT_LIST_DIR}/otp_header_parser/rp2350.json.h ${CMAKE_CURRENT_BINARY_DIR}/rp2350.json.h COPYONLY)
+ else()
+ set(GENERATED_JSON ${CMAKE_CURRENT_BINARY_DIR}/rp2350_otp_contents.json)
+ add_custom_target(generate_otp_header DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rp2350.json.h)
+ add_custom_command(OUTPUT ${GENERATED_JSON}
+ COMMENT "Generating ${GENERATED_JSON}"
+ DEPENDS ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include/hardware/regs/otp_data.h
+ COMMAND otp_header_parse ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include/hardware/regs/otp_data.h ${GENERATED_JSON}
+ )
+ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350.json.h
+ COMMAND ${CMAKE_COMMAND}
+ -D GENERATED_JSON=${GENERATED_JSON}
+ -P ${CMAKE_CURRENT_LIST_DIR}/cmake/jsonh.cmake
+ DEPENDS ${GENERATED_JSON}
+ COMMENT "Configuring rp2350.json.h"
+ VERBATIM)
+
+ endif()
+endif()
+
+add_custom_target(binary_data DEPENDS
+ ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h
+ ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h
+ ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h)
+add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h
+ COMMAND ${CMAKE_COMMAND}
+ -D BINARY_FILE=${CMAKE_CURRENT_LIST_DIR}/bootrom.end.bin
+ -D OUTPUT_NAME=rp2350.rom
+ -P ${CMAKE_CURRENT_LIST_DIR}/cmake/binh.cmake
+ COMMENT "Configuring rp2350.rom.h"
+ VERBATIM)
+add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h
+ COMMAND ${CMAKE_COMMAND}
+ -D BINARY_FILE=${XIP_RAM_PERMS_ELF}
+ -D OUTPUT_NAME=xip_ram_perms_elf
+ -P ${CMAKE_CURRENT_LIST_DIR}/cmake/binh.cmake
+ DEPENDS xip_ram_perms
+ COMMENT "Configuring xip_ram_perms_elf.h"
+ VERBATIM)
+add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h
+ COMMAND ${CMAKE_COMMAND}
+ -D BINARY_FILE=${FLASH_ID_BIN}
+ -D OUTPUT_NAME=flash_id_bin
+ -P ${CMAKE_CURRENT_LIST_DIR}/cmake/binh.cmake
+ DEPENDS flash_id
+ COMMENT "Configuring flash_id_bin.h"
+ VERBATIM)
+
+add_subdirectory(errors)
+
+add_subdirectory(picoboot_connection)
+add_subdirectory(elf)
+add_subdirectory(elf2uf2)
+
+# To configure mbedtls
+# todo make the configuration better
+set(MBEDTLS_CONFIG_FILE "mbedtls_config.h")
+add_compile_options(-I${CMAKE_SOURCE_DIR}/lib/include)
+
+add_subdirectory(lib)
+
+add_subdirectory(bintool)
+
+if (NOT PICOTOOL_NO_LIBUSB)
+ find_package(LIBUSB)
+ set(OTP_EXE otp.cpp)
+else()
+ set(OTP_EXE no_otp.cpp)
+endif()
+
+add_subdirectory(${PICO_SDK_PATH}/src/common/pico_binary_info pico_binary_info)
+add_subdirectory(${PICO_SDK_PATH}/src/common/boot_uf2_headers boot_uf2_headers)
+add_subdirectory(${PICO_SDK_PATH}/src/common/boot_picoboot_headers boot_picoboot_headers)
+add_subdirectory(${PICO_SDK_PATH}/src/common/boot_picobin_headers boot_picobin_headers)
+add_subdirectory(${PICO_SDK_PATH}/src/common/pico_usb_reset_interface_headers pico_usb_reset_interface_headers)
+add_subdirectory(${PICO_SDK_PATH}/src/rp2_common/boot_bootrom_headers boot_bootrom_headers)
+add_subdirectory(${PICO_SDK_PATH}/src/host/pico_platform pico_platform)
+
+add_library(regs_headers INTERFACE)
+target_include_directories(regs_headers INTERFACE ${PICO_SDK_PATH}/src/rp2350/hardware_regs/include)
+
+# Main picotool executable
+add_executable(picotool
+ data_locs.cpp
+ ${OTP_EXE}
+ main.cpp)
+if (NOT PICOTOOL_NO_LIBUSB)
+ target_sources(picotool PRIVATE xip_ram_perms.cpp)
+ add_dependencies(picotool generate_otp_header xip_ram_perms_elf binary_data)
+endif()
+set(PROJECT_VERSION 2.1.0)
+set(PICOTOOL_VERSION 2.1.0)
+set(SYSTEM_VERSION "${CMAKE_SYSTEM_NAME}")
+set(COMPILER_INFO "${CMAKE_C_COMPILER_ID}-${CMAKE_C_COMPILER_VERSION}, ${CMAKE_BUILD_TYPE}")
+target_compile_definitions(picotool PRIVATE
+ PICOTOOL_VERSION="${PICOTOOL_VERSION}"
+ SYSTEM_VERSION="${SYSTEM_VERSION}"
+ COMPILER_INFO="${COMPILER_INFO}"
+ SUPPORT_A2=1
+ CODE_OTP=${PICOTOOL_CODE_OTP}
+ )
+# for OTP info
+target_include_directories(picotool PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+# todo, this is a bit of an abstraction failure; but don't want to rev the SDK just for this right now
+target_include_directories(picotool PRIVATE ${PICO_SDK_PATH}/src/rp2_common/pico_stdio_usb/include)
+target_link_libraries(picotool
+ pico_binary_info
+ boot_uf2_headers
+ boot_picoboot_headers
+ boot_picobin_headers
+ boot_bootrom_headers
+ pico_platform_headers
+ pico_usb_reset_interface_headers
+ regs_headers
+ bintool
+ elf2uf2
+ errors
+ nlohmann_json
+ whereami)
+
+if (NOT TARGET mbedtls)
+ message("mbedtls not found - no signing/hashing support will be built")
+ target_compile_definitions(picotool PRIVATE HAS_MBEDTLS=0)
+else()
+ target_compile_definitions(picotool PRIVATE HAS_MBEDTLS=1)
+endif()
+
+if (NOT LIBUSB_FOUND)
+ if (PICOTOOL_NO_LIBUSB)
+ message("PICOTOOL_NO_LIBUSB is set - no USB support will be built")
+ else()
+ message("libUSB is not found - no USB support will be built")
+ endif()
+ target_compile_definitions(picotool PRIVATE HAS_LIBUSB=0)
+ target_link_libraries(picotool
+ picoboot_connection_header)
+else()
+ target_include_directories(picotool PRIVATE ${LIBUSB_INCLUDE_DIR})
+ target_compile_definitions(picotool PRIVATE HAS_LIBUSB=1)
+ target_link_libraries(picotool
+ picoboot_connection_cxx
+ ${LIBUSB_LIBRARIES})
+endif()
+
+# allow `make install`
+install(TARGETS picotool
+ EXPORT picotool-targets
+ RUNTIME DESTINATION ${INSTALL_BINDIR}
+)
+
+#Export the targets to a script
+install(EXPORT picotool-targets
+ FILE
+ picotoolTargets.cmake
+ DESTINATION
+ ${INSTALL_CONFIGDIR}
+)
+
+#Create a ConfigVersion.cmake file
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/picotoolConfigVersion.cmake
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion
+ ARCH_INDEPENDENT
+)
+
+configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake/picotoolConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/picotoolConfig.cmake
+ INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
+)
+
+#Install the config and configversion
+install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/picotoolConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/picotoolConfigVersion.cmake
+ DESTINATION ${INSTALL_CONFIGDIR}
+)
+
+if (NOT PICOTOOL_NO_LIBUSB)
+ if (NOT PICOTOOL_CODE_OTP)
+ #Install the otp json
+ install(FILES
+ ${GENERATED_JSON}
+ DESTINATION ${INSTALL_DATADIR}
+ )
+ endif()
+
+ #Install xip_ram_perms.elf
+ install(FILES
+ ${XIP_RAM_PERMS_ELF}
+ DESTINATION ${INSTALL_DATADIR}
+ )
endif()
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..eedd667
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,26 @@
+module(
+ name = "picotool",
+ version = "2.1.0",
+)
+
+bazel_dep(name = "rules_libusb", version = "0.1.0-rc1")
+bazel_dep(name = "pico-sdk", version = "2.1.0")
+bazel_dep(name = "rules_cc", version = "0.0.9")
+bazel_dep(name = "bazel_skylib", version = "1.6.1")
+bazel_dep(name = "rules_python", version = "0.22.1")
+bazel_dep(name = "platforms", version = "0.0.9")
+
+libusb = use_extension("@rules_libusb//:extensions.bzl", "libusb")
+libusb.source_release(min_version = "1.0.22")
+use_repo(libusb, "libusb")
+
+http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# TODO: Upstream a bazel build
+http_archive(
+ name = "mbedtls",
+ build_file = "//bazel:mbedtls.BUILD",
+ sha256 = "241c68402cef653e586be3ce28d57da24598eb0df13fcdea9d99bfce58717132",
+ strip_prefix = "mbedtls-2.28.8",
+ url = "https://github.com/Mbed-TLS/mbedtls/releases/download/v2.28.8/mbedtls-2.28.8.tar.bz2",
+)
diff --git a/README.md b/README.md
index e33e4ba..625259f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
## Building
-You need to set PICO_SDK_PATH in the environment, or pass it to cmake.
+You need to set PICO_SDK_PATH in the environment, or pass it to cmake with `-DPICO_SDK_PATH=/path/to/pico-sdk`. To use features such as signing or hashing, you will need to make sure the mbedtls submodule in the SDK is checked out - this can be done by running this from your SDK directory.
+
+```console
+git submodule update --init lib/mbedtls
+```
You also need to install `libusb-1.0`.
@@ -12,6 +16,17 @@ Use your favorite package tool to install dependencies. For example, on Ubuntu:
sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake
```
+> If libusb-1.0-0-dev is not installed, picotool still builds, but it omits all options that deal with managing a pico via USB (load, save, erase, verify, reboot). Builds that do not include USB support can be recognized because these commands also do not appear in the help command. The build output message 'libUSB is not found - no USB support will be built' also appears in the build logs.
+
+Then simply build like a normal CMake project:
+
+```console
+mkdir build
+cd build
+cmake ..
+make
+```
+
On Linux you can add udev rules in order to run picotool without sudo:
```console
@@ -53,93 +68,147 @@ No need to download libusb separately or set `LIBUSB_ROOT`.
pacman -S $MINGW_PACKAGE_PREFIX-{toolchain,cmake,libusb}
mkdir build
cd build
-MSYS2_ARG_CONV_EXCL=- cmake .. -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX
-make
-make install DESTDIR=/ # optional
+cmake .. -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX
+cmake --build .
```
+## Usage by the Raspberry Pi Pico SDK
+
+The Raspberry Pi Pico SDK ([pico-sdk](https://github.com/raspberrypi/pico-sdk)) version 2.0.0 and above uses `picotool` to do the ELF-to-UF2 conversion previously handled by the `elf2uf2` tool in the SDK. The SDK also uses `picotool` to hash and sign binaries.
+
+Whilst the SDK can download picotool on its own per project, if you have multiple projects or build configurations, it is preferable to install a single copy of `picotool` locally. This can be done most simply with `make install` or `cmake --install .`, using `sudo` if required; the SDK will use this installed version by default.
+
+> On some Linux systems, the `~/.local` prefix may be used for an install without `sudo`; from your build directory simply run
+> ```console
+> cmake -DCMAKE_INSTALL_PREFIX=~/.local ..
+> make install
+> ```
+> This will only work if `~/.local` is included in your `PATH`
+
+Alternatively, you can install to a custom path via:
+
+```console
+cmake -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR -DPICOTOOL_FLAT_INSTALL=1 ..
+make install
+```
+
+In order for the SDK to find `picotool` in this custom folder, you will usually need to set the `picotool_DIR` variable in your project. This can be achieved either by setting the `picotool_DIR` environment variable to `$MY_INSTALL_DIR/picotool`, by passing `-Dpicotool_DIR=$MY_INSTALL_DIR/picotool` to your `cmake` command, or by adding `set(picotool_DIR $MY_INSTALL_DIR/picotool)` to your CMakeLists.txt file.
+
+> See the [find_package documentation](https://cmake.org/cmake/help/latest/command/find_package.html#config-mode-search-procedure) for more details
+
## Overview
-`picotool` is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices when they are in BOOTSEL mode. (As of version 1.1 of `picotool` it is also possible to interact with RP2040 devices that are not in BOOTSEL mode, but are using USB stdio support from the Raspberry Pi Pico SDK by using the `-f` argument of `picotool`).
+`picotool` is a tool for working with RP2040/RP2350 binaries, and interacting with RP2040/RP2350 devices when they are in BOOTSEL mode. (As of version 1.1 of `picotool` it is also possible to interact with devices that are not in BOOTSEL mode, but are using USB stdio support from the Raspberry Pi Pico SDK by using the `-f` argument of `picotool`).
Note for additional documentation see https://rptl.io/pico-get-started
-```text
+```
$ picotool help
PICOTOOL:
- Tool for interacting with a RP2040 device in BOOTSEL mode, or with a RP2040 binary
+ Tool for interacting with RP-series device(s) in BOOTSEL mode, or with an RP-series binary
SYNOPSIS:
- picotool info [-b] [-p] [-d] [-l] [-a] [--bus ] [--address ] [-f] [-F]
- picotool info [-b] [-p] [-d] [-l] [-a] [-t ]
- picotool load [-n] [-N] [-u] [-v] [-x] [-t ] [-o ] [--bus ] [--address ] [-f] [-F]
- picotool save [-p] [--bus ] [--address ] [-f] [-F] [-t ]
- picotool save -a [--bus ] [--address ] [-f] [-F] [-t ]
- picotool save -r [--bus ] [--address ] [-f] [-F] [-t ]
- picotool verify [--bus ] [--address ] [-f] [-F] [-t ] [-r ] [-o ]
- picotool reboot [-a] [-u] [--bus ] [--address ] [-f] [-F]
- picotool version [-s]
+ picotool info [-b] [-m] [-p] [-d] [--debug] [-l] [-a] [device-selection]
+ picotool info [-b] [-m] [-p] [-d] [--debug] [-l] [-a] [-t ]
+ picotool config [-s ] [-g ] [device-selection]
+ picotool config [-s ] [-g ] [-t ]
+ picotool load [--ignore-partitions] [--family ] [-p ] [-n] [-N] [-u] [-v] [-x] [-t ] [-o ] [device-selection]
+ picotool encrypt [--quiet] [--verbose] [--hash] [--sign] [-t ] [-o ] [-t ] [-t ] [] [-t ]
+ picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [-t ] [-o ] [-t ] [] [-t ] [] [-t ] [--major ] [--minor ] [--rollback [..]]
+ picotool link [--quiet] [--verbose] [-t ] [-t ] [-t ] [] [-t ] [-p]
+ picotool save [-p] [-v] [--family ] [device-selection]
+ picotool save -a [-v] [--family ] [device-selection]
+ picotool save -r [-v] [--family ] [device-selection]
+ picotool erase [-a] [device-selection]
+ picotool erase -p [device-selection]
+ picotool erase -r [device-selection]
+ picotool verify [device-selection]
+ picotool reboot [-a] [-u] [-g ] [-c ] [device-selection]
+ picotool otp list|get|set|load|dump|permissions|white-label
+ picotool partition info|create
+ picotool uf2 info|convert
+ picotool version [-s] []
+ picotool coprodis [--quiet] [--verbose] [-t ] [-t ]
picotool help []
COMMANDS:
- info Display information from the target device(s) or file.
- Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode
- load Load the program / memory range stored in a file onto the device.
- save Save the program / memory stored in flash on the device to a file.
- verify Check that the device contents match those in the file.
- reboot Reboot the device
- version Display picotool version
- help Show general help or help for a specific command
+ info Display information from the target device(s) or file.
+ Without any arguments, this will display basic information for all connected RP-series devices in BOOTSEL mode
+ config Display or change program configuration settings from the target device(s) or file.
+ load Load the program / memory range stored in a file onto the device.
+ encrypt Encrypt the program.
+ seal Add final metadata to a binary, optionally including a hash and/or signature.
+ link Link multiple binaries into one block loop.
+ save Save the program / memory stored in flash on the device to a file.
+ erase Erase the program / memory stored in flash on the device.
+ verify Check that the device contents match those in the file.
+ reboot Reboot the device
+ otp Commands related to the RP2350 OTP (One-Time-Programmable) Memory
+ partition Commands related to RP2350 Partition Tables
+ uf2 Commands related to UF2 creation and status
+ version Display picotool version
+ coprodis Post-process coprocessor instructions in disassembly files.
+ help Show general help or help for a specific command
Use "picotool help " for more info
```
-Note commands that aren't acting on files require an RP2040 device in BOOTSEL mode to be connected.
+Note commands that aren't acting on files require a device in BOOTSEL mode to be connected.
## info
-So there is now _Binary Information_ support in the SDK which allows for easily storing compact information that `picotool`
+The is _Binary Information_ support in the SDK which allows for easily storing compact information that `picotool`
can find (See Binary Info section below). The info command is for reading this information.
-The information can be either read from one or more connected RP2040 devices in BOOTSEL mode, or from
+The information can be either read from one or more connected devices in BOOTSEL mode, or from
a file. This file can be an ELF, a UF2 or a BIN file.
-```text
+```
$ picotool help info
INFO:
Display information from the target device(s) or file.
- Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode
+ Without any arguments, this will display basic information for all connected RP-series devices in BOOTSEL mode
SYNOPSIS:
- picotool info [-b] [-p] [-d] [-l] [-a] [--bus ] [--address ] [-f] [-F]
- picotool info [-b] [-p] [-d] [-l] [-a] [-t ]
+ picotool info [-b] [-m] [-p] [-d] [--debug] [-l] [-a] [device-selection]
+ picotool info [-b] [-m] [-p] [-d] [--debug] [-l] [-a] [-t ]
OPTIONS:
Information to display
-b, --basic
Include basic information. This is the default
+ -m, --metadata
+ Include all metadata blocks
-p, --pins
Include pin information
-d, --device
Include device information
+ --debug
+ Include device debug information
-l, --build
Include build attributes
-a, --all
Include all information
TARGET SELECTION:
- To target one or more connected RP2040 device(s) in BOOTSEL mode (the default)
+ To target one or more connected RP-series device(s) in BOOTSEL mode (the default)
--bus
Filter devices by USB bus number
--address
Filter devices by USB device address
+ --vid
+ Filter by vendor id
+ --pid
+ Filter by product id
+ --ser
+ Filter by serial number
-f, --force
- Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
- the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
-F, --force-no-reboot
- Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
- the command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but
- without the RPI-RP2 drive mounted
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but without the
+ RPI-RP2 drive mounted
To target a file
The file name
@@ -151,14 +220,14 @@ Note the -f arguments vary slightly for Windows vs macOS / Unix platforms.
e.g.
-```text
+
$ picotool info
Program Information
name: hello_world
features: stdout to UART
```
-```text
+
$ picotool info -a
Program Information
name: hello_world
@@ -179,7 +248,7 @@ Device Information
ROM version: 2
```
-```text
+
$ picotool info -bp
Program Information
name: hello_world
@@ -190,7 +259,7 @@ Fixed Pin Information
21: UART1 RX
```
-```text
+
$ picotool info -a lcd_1602_i2c.uf2
File lcd_1602_i2c.uf2:
@@ -208,9 +277,85 @@ Build Information
build date: Dec 31 2020
```
+## config
+
+Config allows you to configure the binary info on a device, if it is configurable. Specifically, you can configure `bi_ptr_int32` and `bi_ptr_string`.
+
+```text
+$ picotool help config
+CONFIG:
+ Display or change program configuration settings from the target device(s) or file.
+
+SYNOPSIS:
+ picotool config [-s ] [-g ] [device-selection]
+ picotool config [-s ] [-g ] [-t ]
+
+OPTIONS:
+
+ Variable name
+
+ New value
+ -g
+ Filter by feature group
+
+TARGET SELECTION:
+ To target one or more connected RP-series device(s) in BOOTSEL mode (the default)
+ --bus
+ Filter devices by USB bus number
+ --address
+ Filter devices by USB device address
+ --vid
+ Filter by vendor id
+ --pid
+ Filter by product id
+ --ser
+ Filter by serial number
+ -f, --force
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
+ -F, --force-no-reboot
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but without the
+ RPI-RP2 drive mounted
+ To target a file
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+```
+
+```text
+$ picotool config
+n = 5
+name = "Billy"
+nonconst_pins:
+ default_pin = 3
+ default_name = "My First Pin"
+```
+
+```text
+$ picotool config -g nonconst_pins
+nonconst_pins:
+ default_pin = 3
+ default_name = "My First Pin"
+```
+
+```text
+$ picotool config -s name Jane
+name = "Billy"
+setting name -> "Jane"
+
+$ picotool config
+n = 5
+name = "Jane"
+nonconst_pins:
+ default_pin = 3
+ default_name = "My First Pin"
+```
+
## load
-Load allows you to write data from a file into flash
+`load` allows you to write data from a file onto the device (either writing to flash, or to RAM)
```text
$ picotool help load
@@ -218,16 +363,27 @@ LOAD:
Load the program / memory range stored in a file onto the device.
SYNOPSIS:
- picotool load [-n] [-N] [-u] [-v] [-x] [-t ] [-o ] [--bus ] [--address ] [-f] [-F]
+ picotool load [--ignore-partitions] [--family ] [-p ] [-n] [-N] [-u] [-v] [-x] [-t ] [-o
+ ] [device-selection]
OPTIONS:
Post load actions
+ --ignore-partitions
+ When writing flash data, ignore the partition table and write to absolute space
+ --family
+ Specify the family ID of the file to load
+
+ family ID to use for load
+ -p, --partition
+ Specify the partition to load into
+
+ partition to load into
-n, --no-overwrite
- When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence
- of the program in flash, the command fails
+ When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence of the
+ program in flash, the command fails
-N, --no-overwrite-unsafe
- When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence
- of the program in flash, the load continues anyway
+ When writing flash data, do not overwrite an existing program in flash. If picotool cannot determine the size/presence of the
+ program in flash, the load continues anyway
-u, --update
Skip writing flash sectors that already contain identical data
-v, --verify
@@ -249,13 +405,19 @@ OPTIONS:
Filter devices by USB bus number
--address
Filter devices by USB device address
+ --vid
+ Filter by vendor id
+ --pid
+ Filter by product id
+ --ser
+ Filter by serial number
-f, --force
- Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
- the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
-F, --force-no-reboot
- Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
- the command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but
- without the RPI-RP2 drive mounted
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but without the
+ RPI-RP2 drive mounted
```
e.g.
@@ -267,7 +429,7 @@ Loading into Flash: [==============================] 100%
## save
-Save allows you to save a range of memory or a program or the whole of flash from the device to a BIN file or a UF2 file
+`save` allows you to save a range of RAM, the program in flash, or an explicit range of flash from the device to a BIN file or a UF2 file.
```text
$ picotool help save
@@ -275,9 +437,9 @@ SAVE:
Save the program / memory stored in flash on the device to a file.
SYNOPSIS:
- picotool save [-p] [--bus ] [--address ] [-f] [-F] [-t ]
- picotool save -a [--bus ] [--address ] [-f] [-F] [-t ]
- picotool save -r [--bus ] [--address ] [-f] [-F] [-t ]
+ picotool save [-p] [-v] [--family ] [device-selection]
+ picotool save -a [-v] [--family ] [device-selection]
+ picotool save -r [-v] [--family ] [device-selection]
OPTIONS:
Selection of data to save
@@ -286,8 +448,94 @@ OPTIONS:
-a, --all
Save all of flash memory
-r, --range
- Save a range of memory. Note that UF2s always store complete 256 byte-aligned blocks of 256 bytes, and the range is
- expanded accordingly
+ Save a range of memory. Note that UF2s always store complete 256 byte-aligned blocks of 256 bytes, and the range is expanded
+ accordingly
+
+ The lower address bound in hex
+
+ The upper address bound in hex
+ Other
+ -v, --verify
+ Verify the data was saved correctly
+ --family
+ Specify the family ID to save the file as
+
+ family id to save file as
+ Source device selection
+ --bus
+ Filter devices by USB bus number
+ --address
+ Filter devices by USB device address
+ --vid
+ Filter by vendor id
+ --pid
+ Filter by product id
+ --ser
+ Filter by serial number
+ -f, --force
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
+ -F, --force-no-reboot
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but without the
+ RPI-RP2 drive mounted
+ File to save to
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+```
+
+e.g. first looking at what is on the device...
+
+```text
+$ picotool info
+Program Information
+name: lcd_1602_i2c
+web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
+```
+
+... saving it to a file ...
+```text
+$ picotool save spoon.uf2
+Saving file: [==============================] 100%
+Wrote 51200 bytes to spoon.uf2
+```
+
+... and looking at the file:
+```text
+$ picotool info spoon.uf2
+File spoon.uf2:
+Program Information
+name: lcd_1602_i2c
+web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
+```
+
+## erase
+
+`erase` allows you to erase all of flash, a partition of flash, or an explicit range of flash on the device.
+It defaults to erasing all of flash.
+
+```text
+$ picotool help erase
+ERASE:
+ Erase the program / memory stored in flash on the device.
+
+SYNOPSIS:
+ picotool erase [-a] [device-selection]
+ picotool erase [-p ] [device-selection]
+ picotool erase -r [device-selection]
+
+OPTIONS:
+ Selection of data to erase
+ -a, --all
+ Erase all of flash memory. This is the default
+ -p, --partition
+ Erase a partition
+
+ Partition number to erase
+ -r, --range
+ Erase a range of memory. Note that erases must be 4096 byte-aligned, so the range is expanded accordingly
The lower address bound in hex
@@ -297,39 +545,561 @@ OPTIONS:
Filter devices by USB bus number
--address
Filter devices by USB device address
+ --vid
+ Filter by vendor id
+ --pid
+ Filter by product id
+ --ser
+ Filter by serial number
-f, --force
- Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
- the command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be rebooted back to application mode
-F, --force-no-reboot
- Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing
- the command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but
- without the RPI-RP2 drive mounted
- File to save to
-
- The file name
- -t
- Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ Force a device not in BOOTSEL mode but running compatible code to reset so the command can be executed. After executing the
+ command (unless the command itself is a 'reboot') the device will be left connected and accessible to picotool, but without the
+ RPI-RP2 drive mounted
```
-e.g.
+e.g. first looking at what is on the device...
```text
$ picotool info
-Program Information
-name: lcd_1602_i2c
-web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
+Partition 0
+ Program Information
+ none
+
+Partition 1
+ Program Information
+ name: blink
+ web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/blink
+ features: UART stdin / stdout
+ binary start: 0x10000000
+ binary end: 0x1000a934
+ target chip: RP2350
+ image type: ARM Secure
```
+
+... then erase partition 1 ...
```text
-$ picotool save spoon.uf2
-Saving file: [==============================] 100%
-Wrote 51200 bytes to spoon.uf2
+$ picotool erase -p 1
+Erasing partition 1:
+ 0007f000->000fc000
+Erasing: [==============================] 100%
+Erased 512000 bytes
```
+
+... and looking at the device again:
```text
-$ picotool info spoon.uf2
-File spoon.uf2:
-Program Information
-name: lcd_1602_i2c
-web site: https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
+$ picotool info
+Partition 0
+ Program Information
+ none
+
+Partition 1
+ Program Information
+ none
+```
+
+## seal
+
+`seal` allows you to sign and/or hash a binary to run on RP2350.
+
+By default, it will just sign the binary, but this can be configured with the `--hash` and `--no-sign` arguments.
+
+Your signing key must be for the _secp256k1_ curve, in PEM format. You can create a .PEM file with:
+
+```bash
+openssl ecparam -name secp256k1 -genkey -out private.pem
+```
+
+```text
+$ picotool help seal
+SEAL:
+ Add final metadata to a binary, optionally including a hash and/or signature.
+
+SYNOPSIS:
+ picotool seal [--quiet] [--verbose] [--hash] [--sign] [--clear] [-t ] [-o ] [-t ] [] [-t
+ ] [] [-t ] [--major ] [--minor ] [--rollback [..]]
+
+OPTIONS:
+ --quiet
+ Don't print any output
+ --verbose
+ Print verbose output
+ --major
+ Add Major Version
+ --minor
+ Add Minor Version
+ --rollback [..]
+ Add Rollback Version
+ Configuration
+ --hash
+ Hash the file
+ --sign
+ Sign the file
+ --clear
+ Clear all of SRAM on load
+ File to load from
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ BIN file options
+ -o, --offset
+ Specify the load address for a BIN file
+
+ Load offset (memory address; default 0x10000000)
+ File to save to
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ Key file
+
+ The file name
+ -t
+ Specify file type (pem) explicitly, ignoring file extension
+ File to save OTP to (will edit existing file if it exists)
+
+ The file name
+ -t
+ Specify file type (json) explicitly, ignoring file extension
+```
+
+## encrypt
+
+`encrypt` allows you to encrypt and sign a binary for use on the RP2350. By default, it will sign the encrypted binary, but that can be configured similarly to `picotool sign`.
+
+The encrypted binary will have the following structure:
+
+- First metadata block (5 words)
+- IV (4 words)
+- Encrypted Binary
+- Padding to ensure the encrypted length is a multiple of 4 words
+- Signature metadata block
+
+The AES key must be provided as a .bin file of the 256 bit AES key to be used for encryption.
+
+```text
+$ picotool help encrypt
+ENCRYPT:
+ Encrypt the program.
+
+SYNOPSIS:
+ picotool encrypt [--quiet] [--verbose] [--hash] [--sign] [-t ] [-o ] [-t ] [-t ]
+ [] [-t ]
+
+OPTIONS:
+ --quiet
+ Don't print any output
+ --verbose
+ Print verbose output
+ Signing Configuration
+ --hash
+ Hash the encrypted file
+ --sign
+ Sign the encrypted file
+ File to load from
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ BIN file options
+ -o, --offset
+ Specify the load address for a BIN file
+
+ Load offset (memory address; default 0x10000000)
+ File to save to
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ AES Key
+
+ The file name
+ -t
+ Specify file type (bin) explicitly, ignoring file extension
+ Signing Key file
+
+ The file name
+ -t
+ Specify file type (pem) explicitly, ignoring file extension
+```
+
+## partition
+
+The `partition` commands allow you to interact with the partition tables on RP2350 devices, and also create them.
+
+### info
+
+```text
+$ picotool help partition info
+PARTITION INFO:
+ Print the device's partition table.
+
+SYNOPSIS:
+ picotool partition info -m [device-selection]
+
+OPTIONS:
+ -m [device-selection]
+```
+
+```text
+$ picotool partition info
+un-partitioned_space : S(rw) NSBOOT(rw) NS(rw), uf2 { absolute }
+partitions:
+ 0(A) 00002000->00201000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000000, "A", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
+ 1(B w/ 0) 00201000->00400000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000001, "B", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
+```
+
+```text
+$ picotool partition info -m rp2350-arm-s
+un-partitioned_space : S(rw) NSBOOT(rw) NS(rw), uf2 { absolute }
+partitions:
+ 0(A) 00002000->00201000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000000, "A", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
+ 1(B w/ 0) 00201000->00400000 S(rw) NSBOOT(rw) NS(rw), id=0000000000000001, "B", uf2 { rp2350-arm-s, rp2350-riscv }, arm_boot 1, riscv_boot 1
+Family ID 'rp2350-arm-s' can be downloaded in partition 0:
+ 00002000->00201000
+```
+
+### create
+
+This command allows you to create partition tables, and additionally embed them into the block loop if ELF files (for example, for bootloaders).
+By default, all partition tables are hashed, and you can also sign them. The schema for this JSON file is [here](json/schemas/partition-table-schema.json).
+
+```text
+$ picotool help partition create
+PARTITION CREATE:
+ Create a partition table from json
+
+SYNOPSIS:
+ picotool partition create [--quiet] [--verbose] [-t ] [-t ] [[-o ] [--family ]]
+ [] [-t ] [[--sign ] [-t ] [--no-hash] [--singleton]] [[--abs-block] []]
+
+OPTIONS:
+ --quiet
+ Don't print any output
+ --verbose
+ Print verbose output
+ partition table JSON
+
+ The file name
+ -t
+ Specify file type (json) explicitly, ignoring file extension
+ output file
+
+ The file name
+ -t
+ Specify file type (uf2 | elf | bin) explicitly, ignoring file extension
+ UF2 output options
+ -o, --offset
+ Specify the load address for UF2 file output
+
+ Load offset (memory address; default 0x10000000)
+ --family
+ Specify the family if for UF2 file output
+
+ family ID for UF2 (default absolute)
+ embed partition table into bootloader ELF
+
+ The file name
+ -t
+ Specify file type (elf) explicitly, ignoring file extension
+ Partition Table Options
+ --sign
+ The file name
+ -t
+ Specify file type (pem) explicitly, ignoring file extension
+ --no-hash
+ Don't hash the partition table
+ --singleton
+ Singleton partition table
+ Errata RP2350-E10 Fix
+ --abs-block
+ Enforce support for an absolute block
+
+ absolute block location (default to 0x10ffff00)
+```
+
+## uf2
+
+The `uf2` commands allow for creation of UF2s, and can provide information if a UF2 download has failed.
+
+### convert
+
+This command replaces the elf2uf2 functionality that was previously in the Raspberry Pi Pico SDK. It will attempt to auto-detect the family ID, but if this fails you can specify one manually with the `--family` argument.
+
+```text
+picotool help uf2 convert
+UF2 CONVERT:
+ Convert ELF/BIN to UF2.
+
+SYNOPSIS:
+ picotool uf2 convert [--quiet] [--verbose] [-t ] [-t ] [-o