mirror of
https://codeberg.org/libreboot/picotool.git
synced 2025-07-02 23:39:52 +00:00
Compare commits
72 commits
Author | SHA1 | Date | |
---|---|---|---|
|
df21059f7c | ||
|
3a476024ad | ||
|
6c3f0901c4 | ||
|
fa69a49bfb | ||
|
4a403bb277 | ||
|
081a386153 | ||
|
fb85aca4cf | ||
|
dcff4d08d1 | ||
|
2f2e8dffdb | ||
|
877282d19d | ||
|
7350867a23 | ||
|
78c9bd121b | ||
|
ae9a188b4d | ||
|
dc9b5494fe | ||
|
0f9977ea71 | ||
|
a86abb73b5 | ||
|
f41f7fa450 | ||
|
3ea1bb5d3e | ||
|
439062512e | ||
|
19226d169b | ||
|
0dcea9c2bb | ||
|
afb5f26532 | ||
|
c2fca5a6a7 | ||
|
c4550ad0a7 | ||
|
de42044be7 | ||
|
0d259e7f76 | ||
|
b62ead341f | ||
|
afdfa928d2 | ||
|
7e2f756a00 | ||
|
333a03b819 | ||
|
fb9a4f0d30 | ||
|
08bbcf7b42 | ||
|
d0d0f29af3 | ||
|
3ff7c3e710 | ||
|
971ee85176 | ||
|
154692d6bf | ||
|
1721716c5e | ||
|
bf33c6ddd7 | ||
|
9fa08571cb | ||
|
818d3bcf51 | ||
|
fb2e6b9b97 | ||
|
f0232cd544 | ||
|
930fcb6108 | ||
|
94a96f3af1 | ||
|
05ae05532a | ||
|
b4590fa43b | ||
|
6ad9c2352c | ||
|
3027a54423 | ||
|
f5064f76fd | ||
|
4545546271 | ||
|
a98a52e5d7 | ||
|
4a523ede0c | ||
|
e66f13bb2f | ||
|
29f5b0bec7 | ||
|
9bb1ab41ef | ||
|
4086910226 | ||
|
29c5431092 | ||
|
f98850637b | ||
|
ef03bd2d90 | ||
|
e1bfb36497 | ||
|
4ea7e634d4 | ||
|
deecc43c5f | ||
|
468ba2f5c4 | ||
|
6b63c0636b | ||
|
0c8dcc16e3 | ||
|
3783fb42c0 | ||
|
3c457839d1 | ||
|
51d83031fa | ||
|
8a9af99ab1 | ||
|
b19b6aa6c7 | ||
|
e461b2b6f9 | ||
|
5fa2c20007 |
97 changed files with 75411 additions and 863 deletions
1
.bazelignore
Normal file
1
.bazelignore
Normal file
|
@ -0,0 +1 @@
|
|||
lib/pico-sdk
|
1
.bazelrc
Normal file
1
.bazelrc
Normal file
|
@ -0,0 +1 @@
|
|||
common --verbose_failures
|
36
.github/workflows/bazel_build.yml
vendored
Normal file
36
.github/workflows/bazel_build.yml
vendored
Normal file
|
@ -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
|
7
.github/workflows/choco_packages.config
vendored
Normal file
7
.github/workflows/choco_packages.config
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="gcc-arm-embedded" version="10.2.1" />
|
||||
<package id="cmake" version="3.25.2" installArguments="ADD_CMAKE_TO_PATH=System" />
|
||||
<package id="mingw" version="12.2.0" />
|
||||
<package id="ninja" version="1.11.1" />
|
||||
</packages>
|
73
.github/workflows/test.yml
vendored
Normal file
73
.github/workflows/test.yml
vendored
Normal file
|
@ -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
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
build/*
|
||||
bazel-*
|
||||
|
||||
# Ignore until https://github.com/bazelbuild/bazel/issues/20369 is fixed.
|
||||
MODULE.bazel.lock
|
123
BUILD.bazel
Normal file
123
BUILD.bazel
Normal file
|
@ -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": [],
|
||||
}),
|
||||
)
|
353
CMakeLists.txt
353
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()
|
||||
|
|
26
MODULE.bazel
Normal file
26
MODULE.bazel
Normal file
|
@ -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",
|
||||
)
|
0
WORKSPACE
Normal file
0
WORKSPACE
Normal file
21
bazel/BUILD.bazel
Normal file
21
bazel/BUILD.bazel
Normal file
|
@ -0,0 +1,21 @@
|
|||
load("@rules_python//python:defs.bzl", "py_binary")
|
||||
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
|
||||
cc_library(
|
||||
name = "data_locs",
|
||||
srcs = ["data_locs.cpp"],
|
||||
hdrs = ["//:data_locs_header"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = ["@rules_cc//cc/runfiles"],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "binh",
|
||||
srcs = ["binh.py"],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "jsonh",
|
||||
srcs = ["jsonh.py"],
|
||||
)
|
49
bazel/README.md
Normal file
49
bazel/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
## Prerequisites
|
||||
|
||||
You'll need Bazel (v7.0.0 or higher) or Bazelisk (a self-updating Bazel
|
||||
launcher) to build the Pico SDK.
|
||||
|
||||
We strongly recommend you set up
|
||||
[Bazelisk](https://bazel.build/install/bazelisk).
|
||||
|
||||
### Linux
|
||||
|
||||
Use your favorite package tool to install dependencies. For example, on Ubuntu:
|
||||
|
||||
```console
|
||||
sudo apt install build-essential libudev-dev
|
||||
```
|
||||
|
||||
On Linux you can add udev rules in order to run picotool without sudo:
|
||||
|
||||
```console
|
||||
sudo cp udev/99-picotool.rules /etc/udev/rules.d/
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
To build on macOS, you'll need to ensure Xcode is installed.
|
||||
|
||||
```console
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
To build on Windows, you must install [Visual Studio for Desktop Development With C++](https://visualstudio.microsoft.com/vs/features/cplusplus/).
|
||||
|
||||
## Building picotool
|
||||
|
||||
From the root of the picotool repository, run Bazel with the following command:
|
||||
|
||||
```console
|
||||
bazelisk build //:picotool
|
||||
```
|
||||
|
||||
## Running picotool
|
||||
|
||||
To run picotool, run the binary built by Bazel:
|
||||
|
||||
```console
|
||||
./bazel-bin/picotool
|
||||
```
|
71
bazel/binh.py
Normal file
71
bazel/binh.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
"""Generate a header that provides a binary file as an array."""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
_BYTES_PER_LINE = 32
|
||||
|
||||
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
)
|
||||
parser.add_argument(
|
||||
"data",
|
||||
type=argparse.FileType("rb"),
|
||||
help="Path to data file to generate a header for",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=argparse.FileType("wb"),
|
||||
default=sys.stdout.buffer,
|
||||
help="Output file path. Defaults to stdout.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def generate_header(data, output):
|
||||
var_name = Path(output.name).stem.replace(".", "_")
|
||||
include_guard = f"_{var_name.upper()}_H"
|
||||
prefix_lines = (
|
||||
f"#ifndef {include_guard}",
|
||||
f"#define {include_guard}",
|
||||
"",
|
||||
"#include <stddef.h>",
|
||||
"",
|
||||
f"const unsigned char {var_name}[] = {{",
|
||||
)
|
||||
output.write("\n".join(prefix_lines).encode())
|
||||
|
||||
bytes_written = 0
|
||||
while True:
|
||||
b = data.read(1)
|
||||
if b == b"":
|
||||
break
|
||||
if bytes_written % _BYTES_PER_LINE == 0:
|
||||
output.write("\n ".encode())
|
||||
output.write(f"0x{int.from_bytes(b, 'little'):02x}, ".encode())
|
||||
bytes_written += 1
|
||||
|
||||
suffix_lines = (
|
||||
"",
|
||||
"};",
|
||||
"",
|
||||
f"const size_t {var_name}_SIZE = {bytes_written};",
|
||||
"",
|
||||
f"#endif // {include_guard}",
|
||||
"",
|
||||
)
|
||||
output.write("\n".join(suffix_lines).encode())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(generate_header(**vars(_parse_args())))
|
5
bazel/data_locs.cpp
Normal file
5
bazel/data_locs.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// TODO: Finish this.
|
||||
std::vector<std::string> data_locs = {};
|
40
bazel/defs.bzl
Normal file
40
bazel/defs.bzl
Normal file
|
@ -0,0 +1,40 @@
|
|||
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
|
||||
|
||||
def picotool_binary_data_header(name, src, out, **kwargs):
|
||||
run_binary(
|
||||
name = name,
|
||||
srcs = [src],
|
||||
outs = [out],
|
||||
args = [
|
||||
"$(location {})".format(src),
|
||||
"-o=$(location {})".format(out),
|
||||
],
|
||||
tool = "@picotool//bazel:binh",
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def otp_header_parse(name, src, out, **kwargs):
|
||||
json_path = out + ".json"
|
||||
run_binary(
|
||||
name = name + "_json",
|
||||
srcs = [src],
|
||||
outs = [json_path],
|
||||
args = [
|
||||
"$(location {})".format(src),
|
||||
"$(location {})".format(json_path),
|
||||
],
|
||||
tool = "@picotool//otp_header_parser:otp_header_parser",
|
||||
**kwargs
|
||||
)
|
||||
|
||||
run_binary(
|
||||
name = name,
|
||||
srcs = [json_path],
|
||||
outs = [out],
|
||||
args = [
|
||||
"$(location {})".format(json_path),
|
||||
"-o=$(location {})".format(out),
|
||||
],
|
||||
tool = "@picotool//bazel:jsonh",
|
||||
**kwargs
|
||||
)
|
60
bazel/jsonh.py
Normal file
60
bazel/jsonh.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
"""Generate a header that provides a JSON file as a string."""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
_BYTES_PER_LINE = 32
|
||||
|
||||
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
)
|
||||
parser.add_argument(
|
||||
"data",
|
||||
type=argparse.FileType("rb"),
|
||||
help="Path to data file to generate a header for",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=argparse.FileType("wb"),
|
||||
default=sys.stdout.buffer,
|
||||
help="Output file path. Defaults to stdout.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def generate_header(data, output):
|
||||
var_name = Path(output.name).stem.replace(".", "_")
|
||||
include_guard = f"_{var_name.upper()}_H"
|
||||
prefix_lines = (
|
||||
f"#ifndef {include_guard}",
|
||||
f"#define {include_guard}",
|
||||
"",
|
||||
"#include <string>",
|
||||
"",
|
||||
f'const std::string {var_name} = R"""(',
|
||||
)
|
||||
output.write("\n".join(prefix_lines).encode())
|
||||
|
||||
output.write(data.read())
|
||||
|
||||
suffix_lines = (
|
||||
')""";',
|
||||
"",
|
||||
f"#endif // {include_guard}",
|
||||
"",
|
||||
)
|
||||
output.write("\n".join(suffix_lines).encode())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(generate_header(**vars(_parse_args())))
|
18
bazel/mbedtls.BUILD
Normal file
18
bazel/mbedtls.BUILD
Normal file
|
@ -0,0 +1,18 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "mbedtls",
|
||||
srcs = glob(["library/*.c"]),
|
||||
hdrs = glob(
|
||||
include = [
|
||||
"include/**/*.h",
|
||||
"library/*.h",
|
||||
],
|
||||
),
|
||||
includes = ["include"],
|
||||
linkopts = select({
|
||||
"@rules_cc//cc/compiler:msvc-cl": ["-DEFAULTLIB:AdvAPI32.Lib"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
deps = ["@picotool//lib:mbedtls_config"],
|
||||
)
|
35
bintool/BUILD.bazel
Normal file
35
bintool/BUILD.bazel
Normal file
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "bintool",
|
||||
srcs = [
|
||||
"bintool.cpp",
|
||||
"mbedtls_wrapper.c",
|
||||
],
|
||||
hdrs = [
|
||||
"bintool.h",
|
||||
"mbedtls_wrapper.h",
|
||||
"metadata.h",
|
||||
],
|
||||
copts = select({
|
||||
"@rules_cc//cc/compiler:msvc-cl": ["/std:c++20"],
|
||||
"@platforms//os:windows": [],
|
||||
"//conditions:default": [
|
||||
"-Wno-unused-variable",
|
||||
],
|
||||
}),
|
||||
defines = [
|
||||
"HAS_MBEDTLS=1", # Bazel build always has mbedtls.
|
||||
],
|
||||
includes = ["."],
|
||||
# In the CMake build, there's a workaround where this library is built with
|
||||
# NO_PICO_PLATFORM, but that define shouldn't propagate to other
|
||||
# dependencies.
|
||||
local_defines = ["NO_PICO_PLATFORM=1"],
|
||||
deps = [
|
||||
"//elf",
|
||||
"//errors",
|
||||
"@mbedtls",
|
||||
"@pico-sdk//src/common/boot_picobin_headers",
|
||||
],
|
||||
)
|
32
bintool/CMakeLists.txt
Normal file
32
bintool/CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
if (NOT TARGET mbedtls)
|
||||
message("lib/mbedtls submodule needs to be initialized for bintool hashing/signing")
|
||||
add_library(bintool STATIC
|
||||
bintool.cpp)
|
||||
target_compile_definitions(bintool PRIVATE
|
||||
NO_PICO_PLATFORM=1
|
||||
HAS_MBEDTLS=0
|
||||
)
|
||||
|
||||
target_include_directories(bintool PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(bintool PUBLIC
|
||||
elf
|
||||
errors
|
||||
boot_picobin_headers)
|
||||
else()
|
||||
add_library(bintool STATIC
|
||||
bintool.cpp
|
||||
mbedtls_wrapper.c)
|
||||
target_compile_definitions(bintool PRIVATE
|
||||
NO_PICO_PLATFORM=1
|
||||
HAS_MBEDTLS=1
|
||||
)
|
||||
|
||||
target_include_directories(bintool PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(bintool PUBLIC
|
||||
mbedtls
|
||||
elf
|
||||
errors
|
||||
boot_picobin_headers)
|
||||
endif()
|
1028
bintool/bintool.cpp
Normal file
1028
bintool/bintool.cpp
Normal file
File diff suppressed because it is too large
Load diff
42
bintool/bintool.h
Normal file
42
bintool/bintool.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#if HAS_MBEDTLS
|
||||
#include "mbedtls_wrapper.h"
|
||||
#endif
|
||||
#include "elf_file.h"
|
||||
#include "metadata.h"
|
||||
|
||||
typedef enum verified_t {
|
||||
none,
|
||||
failed,
|
||||
passed
|
||||
} verified_t;
|
||||
|
||||
// Common
|
||||
#if HAS_MBEDTLS
|
||||
int read_keys(const std::string &filename, public_t *public_key, private_t *private_key);
|
||||
void hash_andor_sign_block(block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, std::vector<uint8_t> to_hash = {});
|
||||
#endif
|
||||
|
||||
// Elfs
|
||||
std::unique_ptr<block> find_first_block(elf_file *elf);
|
||||
block place_new_block(elf_file *elf, std::unique_ptr<block> &first_block);
|
||||
#if HAS_MBEDTLS
|
||||
int hash_andor_sign(elf_file *elf, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false);
|
||||
int encrypt(elf_file *elf, block *new_block, const private_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign);
|
||||
#endif
|
||||
|
||||
// Bins
|
||||
typedef std::function<void(std::vector<uint8_t> &bin, uint32_t size)> get_more_bin_cb;
|
||||
std::unique_ptr<block> find_first_block(std::vector<uint8_t> bin, uint32_t storage_addr);
|
||||
std::unique_ptr<block> get_last_block(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, get_more_bin_cb more_cb = nullptr);
|
||||
std::vector<std::unique_ptr<block>> get_all_blocks(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, get_more_bin_cb more_cb = nullptr);
|
||||
block place_new_block(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block);
|
||||
uint32_t calc_checksum(std::vector<uint8_t> bin);
|
||||
#if HAS_MBEDTLS
|
||||
std::vector<uint8_t> hash_andor_sign(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, bool clear_sram = false);
|
||||
std::vector<uint8_t> encrypt(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const private_t aes_key, const public_t public_key, const private_t private_key, bool hash_value, bool sign);
|
||||
void verify_block(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *block, verified_t &hash_verified, verified_t &sig_verified);
|
||||
#endif
|
242
bintool/mbedtls_wrapper.c
Normal file
242
bintool/mbedtls_wrapper.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
#include "mbedtls_wrapper.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#if ENABLE_DEBUG_LOG
|
||||
#define DEBUG_LOG(...) printf(__VA_ARGS__)
|
||||
static void dump_buf(const char *title, const unsigned char *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
DEBUG_LOG("%s", title);
|
||||
for (i = 0; i < len; i++) {
|
||||
DEBUG_LOG("%c%c", "0123456789ABCDEF" [buf[i] / 16],
|
||||
"0123456789ABCDEF" [buf[i] % 16]);
|
||||
}
|
||||
DEBUG_LOG("\n");
|
||||
}
|
||||
|
||||
static void dump_pubkey(const char *title, mbedtls_ecdsa_context *key)
|
||||
{
|
||||
unsigned char buf[300];
|
||||
size_t len;
|
||||
|
||||
if (mbedtls_ecp_point_write_binary(&key->grp, &key->Q,
|
||||
MBEDTLS_ECP_PF_UNCOMPRESSED, &len, buf, sizeof(buf)) != 0) {
|
||||
DEBUG_LOG("internal error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dump_buf(title, buf, len);
|
||||
}
|
||||
#else
|
||||
#define DEBUG_LOG(...) ((void)0)
|
||||
#define dump_buf(...) ((void)0)
|
||||
#define dump_pubkey(...) ((void)0)
|
||||
#endif
|
||||
|
||||
void mb_sha256_buffer(const uint8_t *data, size_t len, message_digest_t *digest_out) {
|
||||
mbedtls_sha256(data, len, digest_out->bytes, 0);
|
||||
}
|
||||
|
||||
void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const private_t *key, iv_t *iv) {
|
||||
mbedtls_aes_context aes;
|
||||
|
||||
assert(len % 16 == 0);
|
||||
|
||||
mbedtls_aes_setkey_enc(&aes, key->bytes, 256);
|
||||
uint8_t stream_block[16] = {0};
|
||||
size_t nc_off = 0;
|
||||
mbedtls_aes_crypt_ctr(&aes, len, &nc_off, iv->bytes, stream_block, data, data_out);
|
||||
}
|
||||
|
||||
void raw_to_der(signature_t *sig) {
|
||||
// todo make this der - currently ber
|
||||
unsigned char r[33];
|
||||
r[0] = 0;
|
||||
memcpy(r+1, sig->bytes, 32);
|
||||
unsigned char s[33];
|
||||
s[0] = 0;
|
||||
memcpy(s+1, sig->bytes + 32, 32);
|
||||
|
||||
int8_t r_len_dec = 0;
|
||||
if (r[1] & 0x80) {
|
||||
// Needs padding
|
||||
r_len_dec = -1;
|
||||
} else {
|
||||
for (int i=1; i < 32; i++) {
|
||||
if (r[i] != 0) {
|
||||
break;
|
||||
}
|
||||
r_len_dec++;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t s_len_dec = 0;
|
||||
if (s[1] & 0x80) {
|
||||
// Needs padding
|
||||
s_len_dec = -1;
|
||||
} else {
|
||||
for (int i=1; i < 32; i++) {
|
||||
if (s[i] != 0) {
|
||||
break;
|
||||
}
|
||||
s_len_dec++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write it out
|
||||
sig->der[0] = 0x30;
|
||||
sig->der[1] = 68 - r_len_dec - s_len_dec;
|
||||
sig->der[2] = 0x02;
|
||||
sig->der[3] = 32 - r_len_dec;
|
||||
uint8_t b2 = sig->der[3];
|
||||
memcpy(sig->der + 4, r + 1 + r_len_dec, b2);
|
||||
sig->der[4 + b2] = 0x02;
|
||||
sig->der[5 + b2] = 32 - s_len_dec;
|
||||
uint8_t b3 = sig->der[5 + b2];
|
||||
memcpy(sig->der + 6 + b2, s + 1 + s_len_dec, b3);
|
||||
|
||||
|
||||
sig->der_len = 6 + b2 + b3;
|
||||
}
|
||||
|
||||
|
||||
void der_to_raw(signature_t *sig) {
|
||||
assert(sig->der[0] == 0x30);
|
||||
assert(sig->der[2] == 0x02);
|
||||
uint8_t b2 = sig->der[3];
|
||||
assert(sig->der[4 + b2] == 0x02);
|
||||
uint8_t b3 = sig->der[5 + b2];
|
||||
|
||||
assert(sig->der_len == 6u + b2 + b3);
|
||||
|
||||
unsigned char r[32];
|
||||
if (b2 == 33) {
|
||||
memcpy(r, sig->der + 4 + 1, 32);
|
||||
} else if (b2 == 32) {
|
||||
memcpy(r, sig->der + 4, 32);
|
||||
} else {
|
||||
memset(r, 0, sizeof(r));
|
||||
memcpy(r + (32 - b2), sig->der + 4, (32 - b2));
|
||||
}
|
||||
|
||||
unsigned char s[32];
|
||||
if (b3 == 33) {
|
||||
memcpy(s, sig->der + 6 + b2 + 1, 32);
|
||||
} else if (b3 == 32) {
|
||||
memcpy(s, sig->der + 6 + b2, 32);
|
||||
} else {
|
||||
memset(s, 0, sizeof(r));
|
||||
memcpy(s + (32 - b3), sig->der + 6 + b2, (32 - b3));
|
||||
}
|
||||
|
||||
memset(sig->bytes, 0, sizeof(sig->bytes));
|
||||
memcpy(sig->bytes, r, sizeof(r));
|
||||
memcpy(sig->bytes + 32, s, sizeof(s));
|
||||
}
|
||||
|
||||
|
||||
void mb_sign_sha256(const uint8_t *entropy, size_t entropy_size, const message_digest_t *m, const public_t *p, const private_t *d, signature_t *out) {
|
||||
int ret = 1;
|
||||
mbedtls_ecdsa_context ctx_sign;
|
||||
mbedtls_entropy_context entropy_ctx;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
|
||||
mbedtls_ecdsa_init(&ctx_sign);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
|
||||
memset(out->der, 0, sizeof(out->der));
|
||||
|
||||
DEBUG_LOG("\n . Seeding the random number generator...");
|
||||
fflush(stdout);
|
||||
|
||||
mbedtls_entropy_init(&entropy_ctx);
|
||||
// mbedtls_entropy_update_manual(&entropy, entropy_in, entropy_size);
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy_ctx,
|
||||
(const unsigned char *) entropy,
|
||||
entropy_size)) != 0) {
|
||||
DEBUG_LOG(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(" ok\n");
|
||||
|
||||
DEBUG_LOG(" . Loading key pair...");
|
||||
fflush(stdout);
|
||||
|
||||
mbedtls_ecp_group_load(&ctx_sign.grp, MBEDTLS_ECP_DP_SECP256K1);
|
||||
mbedtls_mpi_read_binary(&ctx_sign.d, (unsigned char*)d, 32);
|
||||
mbedtls_mpi_read_binary(&ctx_sign.Q.X, (unsigned char*)p, 32);
|
||||
mbedtls_mpi_read_binary(&ctx_sign.Q.Y, (unsigned char*)p + 32, 32);
|
||||
// Z must be 1
|
||||
mbedtls_mpi_add_int(&ctx_sign.Q.Z, &ctx_sign.Q.Z, 1);
|
||||
|
||||
DEBUG_LOG(" ok (key size: %d bits)\n", (int) ctx_sign.grp.pbits);
|
||||
|
||||
ret = mbedtls_ecp_check_pub_priv(&ctx_sign, &ctx_sign);
|
||||
DEBUG_LOG("Pub Priv Returned %d\n", ret);
|
||||
|
||||
dump_pubkey(" + Public key: ", &ctx_sign);
|
||||
|
||||
dump_buf(" + Hash: ", m->bytes, sizeof(m->bytes));
|
||||
|
||||
DEBUG_LOG(" . Signing message hash...");
|
||||
fflush(stdout);
|
||||
|
||||
if ((ret = mbedtls_ecdsa_write_signature(&ctx_sign, MBEDTLS_MD_SHA256,
|
||||
m->bytes, sizeof(m->bytes),
|
||||
out->der, &out->der_len,
|
||||
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) {
|
||||
DEBUG_LOG(" failed\n ! mbedtls_ecdsa_write_signature returned %d\n", ret);
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(" ok (signature length = %u)\n", (unsigned int) out->der_len);
|
||||
|
||||
dump_buf(" + DER Signature: ", out->der, out->der_len);
|
||||
|
||||
// Populate raw signature value from der
|
||||
der_to_raw(out);
|
||||
|
||||
dump_buf(" + Raw Signature: ", (unsigned char*)out, 64);
|
||||
}
|
||||
|
||||
uint32_t mb_verify_signature_secp256k1(
|
||||
signature_t signature[1],
|
||||
const public_t public_key[1],
|
||||
const message_digest_t digest[1]) {
|
||||
|
||||
int ret = 1;
|
||||
mbedtls_ecdsa_context ctx_verify;
|
||||
unsigned char hash[32];
|
||||
memcpy(hash, digest, sizeof(hash));
|
||||
if (signature->der_len == 0) {
|
||||
raw_to_der(signature);
|
||||
}
|
||||
|
||||
mbedtls_ecdsa_init(&ctx_verify);
|
||||
|
||||
mbedtls_ecp_group_load(&ctx_verify.grp, MBEDTLS_ECP_DP_SECP256K1);
|
||||
mbedtls_mpi_read_binary(&ctx_verify.Q.X, public_key->bytes, 32);
|
||||
mbedtls_mpi_read_binary(&ctx_verify.Q.Y, public_key->bytes + 32, 32);
|
||||
// Z must be 1
|
||||
mbedtls_mpi_add_int(&ctx_verify.Q.Z, &ctx_verify.Q.Z, 1);
|
||||
|
||||
/*
|
||||
* Verify signature
|
||||
*/
|
||||
DEBUG_LOG(" . Verifying signature...");
|
||||
fflush(stdout);
|
||||
|
||||
if ((ret = mbedtls_ecdsa_read_signature(&ctx_verify,
|
||||
hash, sizeof(hash),
|
||||
signature->der, signature->der_len)) != 0) {
|
||||
DEBUG_LOG(" failed\n ! mbedtls_ecdsa_read_signature returned -%x\n", -ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEBUG_LOG(" ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
60
bintool/mbedtls_wrapper.h
Normal file
60
bintool/mbedtls_wrapper.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#undef MBEDTLS_ECDSA_DETERMINISTIC
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/pk.h>
|
||||
#include <mbedtls/ecp.h>
|
||||
#include <mbedtls/aes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define _Static_assert static_assert
|
||||
#endif
|
||||
|
||||
typedef struct signature {
|
||||
/** An array 64 bytes making up 2 256-bit values. */
|
||||
uint8_t bytes[64];
|
||||
uint8_t der[MBEDTLS_ECDSA_MAX_LEN];
|
||||
size_t der_len;
|
||||
} signature_t; /**< Convenience typedef */
|
||||
|
||||
typedef struct message_digest {
|
||||
/** An array 32 bytes making up the 256-bit value. */
|
||||
uint8_t bytes[32];
|
||||
} message_digest_t; /**< Convenience typedef */
|
||||
|
||||
typedef struct iv {
|
||||
/** An array 16 bytes random data. */
|
||||
uint8_t bytes[16];
|
||||
} iv_t; /**< Convenience typedef */
|
||||
|
||||
typedef signature_t public_t;
|
||||
typedef message_digest_t private_t;
|
||||
|
||||
void mb_sha256_buffer(const uint8_t *data, size_t len, message_digest_t *digest_out);
|
||||
void mb_aes256_buffer(const uint8_t *data, size_t len, uint8_t *data_out, const private_t *key, iv_t *iv);
|
||||
void mb_sign_sha256(const uint8_t *entropy, size_t entropy_size, const message_digest_t *m, const public_t *p, const private_t *d, signature_t *out);
|
||||
|
||||
uint32_t mb_verify_signature_secp256k1(
|
||||
signature_t signature[1],
|
||||
const public_t public_key[1],
|
||||
const message_digest_t digest[1]);
|
||||
|
||||
#define sha256_buffer mb_sha256_buffer
|
||||
#define aes256_buffer mb_aes256_buffer
|
||||
#define sign_sha256 mb_sign_sha256
|
||||
#define verify_signature_secp256k1 mb_verify_signature_secp256k1
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
707
bintool/metadata.h
Normal file
707
bintool/metadata.h
Normal file
|
@ -0,0 +1,707 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <iterator>
|
||||
|
||||
#include "boot/picobin.h"
|
||||
#include <map>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "elf_file.h"
|
||||
|
||||
#if ENABLE_DEBUG_LOG
|
||||
#define DEBUG_LOG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_LOG(...) ((void)0)
|
||||
#endif
|
||||
|
||||
struct item;
|
||||
|
||||
template<typename InputIterator> std::vector<uint32_t> lsb_bytes_to_words(InputIterator begin, InputIterator end) {
|
||||
using InputType = typename std::iterator_traits<InputIterator>::value_type;
|
||||
static_assert(sizeof(InputType) == 1, "");
|
||||
std::vector<uint32_t> rc;
|
||||
size_t size = end - begin;
|
||||
assert(!(size & 3));
|
||||
if (size) {
|
||||
rc.reserve(size / 4);
|
||||
for(auto it = begin; it < end; ) {
|
||||
uint32_t word = *it++;
|
||||
word |= (*it++) << 8;
|
||||
word |= (*it++) << 16;
|
||||
word |= (*it++) << 24;
|
||||
rc.push_back(word);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename InputIterator, typename OutputType = std::uint8_t> std::vector<OutputType> words_to_lsb_bytes(InputIterator begin, InputIterator end) {
|
||||
static_assert(sizeof(OutputType) == 1, "");
|
||||
std::vector<OutputType> rc;
|
||||
size_t size = end - begin;
|
||||
if (size) {
|
||||
rc.reserve(size * 4);
|
||||
for(auto it = begin; it < end; ) {
|
||||
uint32_t word = *it++;
|
||||
rc.push_back(word & 0xff);
|
||||
rc.push_back((word >> 8) & 0xff);
|
||||
rc.push_back((word >> 16) & 0xff);
|
||||
rc.push_back((word >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
struct item_writer_context {
|
||||
item_writer_context(uint32_t base_addr) : base_addr(base_addr), word_offset(0) {}
|
||||
std::map<std::shared_ptr<item>, uint32_t> item_word_offsets;
|
||||
uint32_t base_addr;
|
||||
uint32_t word_offset;
|
||||
};
|
||||
|
||||
struct item {
|
||||
explicit item() {};
|
||||
virtual ~item() = default;
|
||||
virtual uint8_t type() const = 0;
|
||||
|
||||
static uint32_t decode_size(uint32_t item_header) {
|
||||
uint32_t size;
|
||||
if (item_header & 0x80) {
|
||||
size = (uint16_t)(item_header >> 8);
|
||||
} else {
|
||||
size = (uint8_t)(item_header >> 8);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
virtual std::vector<uint32_t> to_words(item_writer_context &ctx) const = 0;
|
||||
virtual uint32_t encode_type_and_size(unsigned int size) const {
|
||||
assert(size < PICOBIN_MAX_BLOCK_SIZE);
|
||||
if (size < 256) {
|
||||
return (size << 8u) | type();
|
||||
} else {
|
||||
// todo byte order
|
||||
return (size << 8u) | 0x80 | type();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// always use single byte size
|
||||
struct single_byte_size_item : public item {
|
||||
virtual uint32_t encode_type_and_size(unsigned int size) const override {
|
||||
assert(size < PICOBIN_MAX_BLOCK_SIZE);
|
||||
assert(size < 128);
|
||||
assert(!(type() & 128));
|
||||
return (size << 8u) | type();
|
||||
}
|
||||
};
|
||||
|
||||
// always use double byte size
|
||||
struct double_byte_size_item : public item {
|
||||
virtual uint32_t encode_type_and_size(unsigned int size) const override {
|
||||
assert(size < PICOBIN_MAX_BLOCK_SIZE);
|
||||
assert(type() & 128);
|
||||
// todo byte order
|
||||
return (size << 8u) | 0x80 | type();
|
||||
}
|
||||
};
|
||||
|
||||
struct ignored_item : public double_byte_size_item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_2BS_IGNORED; }
|
||||
ignored_item() = default;
|
||||
|
||||
explicit ignored_item(uint32_t size, std::vector<uint32_t> data) : size(size), data(data) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
uint32_t size = item::decode_size(header);
|
||||
std::vector<uint32_t> data;
|
||||
for (unsigned int i=1; i < size; i++) {
|
||||
data.push_back(*it++);
|
||||
}
|
||||
return std::make_shared<ignored_item>(size, data);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
std::vector<uint32_t> ret;
|
||||
ret.push_back(encode_type_and_size(size));
|
||||
ret.insert(ret.end(), data.begin(), data.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t size;
|
||||
std::vector<uint32_t> data;
|
||||
};
|
||||
|
||||
enum image_type_image_type {
|
||||
type_invalid=PICOBIN_IMAGE_TYPE_IMAGE_TYPE_INVALID,
|
||||
type_exe=PICOBIN_IMAGE_TYPE_IMAGE_TYPE_EXE,
|
||||
type_data=PICOBIN_IMAGE_TYPE_IMAGE_TYPE_DATA
|
||||
};
|
||||
|
||||
enum image_type_exe_security {
|
||||
sec_unspecified=PICOBIN_IMAGE_TYPE_EXE_SECURITY_UNSPECIFIED,
|
||||
sec_ns=PICOBIN_IMAGE_TYPE_EXE_SECURITY_NS,
|
||||
sec_s=PICOBIN_IMAGE_TYPE_EXE_SECURITY_S
|
||||
};
|
||||
|
||||
enum image_type_exe_cpu {
|
||||
cpu_arm=PICOBIN_IMAGE_TYPE_EXE_CPU_ARM,
|
||||
cpu_riscv=PICOBIN_IMAGE_TYPE_EXE_CPU_RISCV,
|
||||
cpu_varmulet=PICOBIN_IMAGE_TYPE_EXE_CPU_VARMULET
|
||||
};
|
||||
|
||||
enum image_type_exe_chip {
|
||||
chip_rp2040=PICOBIN_IMAGE_TYPE_EXE_CHIP_RP2040,
|
||||
chip_rp2350=PICOBIN_IMAGE_TYPE_EXE_CHIP_RP2350
|
||||
};
|
||||
|
||||
struct image_type_item : public single_byte_size_item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE; }
|
||||
image_type_item() = default;
|
||||
|
||||
explicit image_type_item(uint16_t flags) : flags(flags) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I it, I end, uint32_t header) {
|
||||
return std::make_shared<image_type_item>(header >> 16);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
return {encode_type_and_size(1) | (flags << 16)};
|
||||
}
|
||||
|
||||
image_type_image_type image_type() const { return static_cast<image_type_image_type>((flags & PICOBIN_IMAGE_TYPE_IMAGE_TYPE_BITS) >> PICOBIN_IMAGE_TYPE_IMAGE_TYPE_LSB); }
|
||||
image_type_exe_security security() const { return static_cast<image_type_exe_security>((flags & PICOBIN_IMAGE_TYPE_EXE_SECURITY_BITS) >> PICOBIN_IMAGE_TYPE_EXE_SECURITY_LSB); }
|
||||
image_type_exe_cpu cpu() const { return static_cast<image_type_exe_cpu>((flags & PICOBIN_IMAGE_TYPE_EXE_CPU_BITS) >> PICOBIN_IMAGE_TYPE_EXE_CPU_LSB); }
|
||||
image_type_exe_chip chip() const { return static_cast<image_type_exe_chip>((flags & PICOBIN_IMAGE_TYPE_EXE_CHIP_BITS) >> PICOBIN_IMAGE_TYPE_EXE_CHIP_LSB); }
|
||||
bool tbyb() const { return flags & PICOBIN_IMAGE_TYPE_EXE_TBYB_BITS; }
|
||||
|
||||
uint16_t flags;
|
||||
};
|
||||
|
||||
struct partition_table_item : public single_byte_size_item {
|
||||
struct partition {
|
||||
uint8_t permissions = 0;
|
||||
uint16_t first_sector = 0;
|
||||
uint16_t last_sector = 0;
|
||||
uint32_t flags = 0;
|
||||
uint64_t id;
|
||||
std::string name;
|
||||
std::vector<uint32_t> extra_families;
|
||||
|
||||
std::vector<uint32_t> to_words() const {
|
||||
std::vector<uint32_t> ret = {
|
||||
((permissions << PICOBIN_PARTITION_PERMISSIONS_LSB) & PICOBIN_PARTITION_PERMISSIONS_BITS)
|
||||
| ((first_sector << PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS)
|
||||
| ((last_sector << PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS),
|
||||
((permissions << PICOBIN_PARTITION_PERMISSIONS_LSB) & PICOBIN_PARTITION_PERMISSIONS_BITS)
|
||||
| flags,
|
||||
};
|
||||
if (flags & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS) {
|
||||
ret.push_back((uint32_t)id);
|
||||
ret.push_back((uint32_t)(id >> 32));
|
||||
}
|
||||
if (extra_families.size() > 0) {
|
||||
assert(extra_families.size() == (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS) >> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB);
|
||||
ret.insert(ret.end(), extra_families.begin(), extra_families.end());
|
||||
}
|
||||
if (name.size() > 0) {
|
||||
char size = name.size();
|
||||
std::vector<char> name_vec = {size};
|
||||
for (char c : name) {
|
||||
name_vec.push_back(c);
|
||||
}
|
||||
while (name_vec.size() % 4 != 0) name_vec.push_back(0);
|
||||
while (name_vec.size() > 0) {
|
||||
ret.push_back(*(uint32_t*)name_vec.data());
|
||||
name_vec.erase(name_vec.begin(), name_vec.begin() + 4);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_PARTITION_TABLE; }
|
||||
partition_table_item() = default;
|
||||
|
||||
explicit partition_table_item(uint32_t unpartitioned_flags, bool singleton) : unpartitioned_flags(unpartitioned_flags), singleton(singleton) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
uint32_t size = decode_size(header);
|
||||
uint8_t singleton_count = header >> 24;
|
||||
bool singleton = singleton_count & 0x80;
|
||||
uint8_t partition_count = singleton_count & 0x0f;
|
||||
uint32_t unpartitioned_flags = *it++;
|
||||
|
||||
auto pt = std::make_shared<partition_table_item>(unpartitioned_flags, singleton);
|
||||
|
||||
std::vector<uint32_t> data;
|
||||
for (unsigned int i=2; i < size; i++) {
|
||||
data.push_back(*it++);
|
||||
}
|
||||
int i=0;
|
||||
while (i < data.size()) {
|
||||
partition new_p;
|
||||
uint32_t permissions_locations = data[i++];
|
||||
new_p.permissions = (permissions_locations & PICOBIN_PARTITION_PERMISSIONS_BITS) >> PICOBIN_PARTITION_PERMISSIONS_LSB;
|
||||
new_p.first_sector = (permissions_locations & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB;
|
||||
new_p.last_sector = (permissions_locations & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB;
|
||||
|
||||
uint32_t permissions_flags = data[i++];
|
||||
uint8_t permissions2 = (permissions_flags & PICOBIN_PARTITION_PERMISSIONS_BITS) >> PICOBIN_PARTITION_PERMISSIONS_LSB;
|
||||
if (new_p.permissions != permissions2) {
|
||||
printf("Permissions mismatch %02x %02x\n", new_p.permissions, permissions2);
|
||||
assert(false);
|
||||
}
|
||||
new_p.flags = permissions_flags & (~PICOBIN_PARTITION_PERMISSIONS_BITS);
|
||||
|
||||
if (new_p.flags & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS) {
|
||||
new_p.id = (uint64_t)data[i++] | ((uint64_t)data[i++] << 32);
|
||||
}
|
||||
|
||||
uint8_t num_extra_families = (new_p.flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS) >> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB;
|
||||
for (int fam=0; fam < num_extra_families; fam++) {
|
||||
new_p.extra_families.push_back(data[i++]);
|
||||
}
|
||||
|
||||
if (new_p.flags & PICOBIN_PARTITION_FLAGS_HAS_NAME_BITS) {
|
||||
auto bytes = words_to_lsb_bytes(data.begin() + i++, data.end());
|
||||
int name_size = bytes[0];
|
||||
// This works neatly - accounts for the size byte at the start
|
||||
i += name_size / 4;
|
||||
new_p.name = std::string((char*)(bytes.data() + 1), name_size);
|
||||
}
|
||||
|
||||
pt->partitions.push_back(new_p);
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
std::vector<uint32_t> partition_words;
|
||||
for (auto p : partitions) {
|
||||
auto words = p.to_words();
|
||||
partition_words.insert(partition_words.end(), words.begin(), words.end());
|
||||
}
|
||||
std::vector<uint32_t> ret = {
|
||||
encode_type_and_size(2 + partition_words.size()) | (singleton << 31) | ((uint8_t)partitions.size() << 24),
|
||||
unpartitioned_flags
|
||||
};
|
||||
ret.insert(ret.end(), partition_words.begin(), partition_words.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t unpartitioned_flags;
|
||||
bool singleton;
|
||||
std::vector<partition> partitions;
|
||||
};
|
||||
|
||||
struct vector_table_item : public single_byte_size_item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_VECTOR_TABLE; }
|
||||
vector_table_item() = default;
|
||||
|
||||
explicit vector_table_item(uint32_t addr) : addr(addr) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
if (decode_size(header) != 2) {
|
||||
printf("Bad block size %d for vector table item\n", decode_size(header));
|
||||
assert(false);
|
||||
}
|
||||
return std::make_shared<vector_table_item>(*it++);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
return {encode_type_and_size(2), addr};
|
||||
}
|
||||
|
||||
uint32_t addr;
|
||||
};
|
||||
|
||||
struct rolling_window_delta_item : public single_byte_size_item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_ROLLING_WINDOW_DELTA; }
|
||||
rolling_window_delta_item() = default;
|
||||
|
||||
explicit rolling_window_delta_item(uint32_t addr) : addr(addr) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
if (decode_size(header) != 2) {
|
||||
printf("Bad block size %d for rolling window delta item\n", decode_size(header));
|
||||
assert(false);
|
||||
}
|
||||
return std::make_shared<rolling_window_delta_item>(*it++);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
return {encode_type_and_size(2), (uint32_t)addr};
|
||||
}
|
||||
|
||||
int32_t addr;
|
||||
};
|
||||
|
||||
struct entry_point_item : public single_byte_size_item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_ENTRY_POINT; }
|
||||
entry_point_item() = default;
|
||||
|
||||
explicit entry_point_item(uint32_t ep, uint32_t sp) : ep(ep), sp(sp) {}
|
||||
explicit entry_point_item(uint32_t ep, uint32_t sp, uint32_t splim) : ep(ep), sp(sp), splim(splim), splim_set(true) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
uint32_t size = decode_size(header);
|
||||
uint32_t ep = *it++;
|
||||
uint32_t sp = *it++;
|
||||
if (size == 3) {
|
||||
return std::make_shared<entry_point_item>(ep, sp);
|
||||
} else if (size == 4) {
|
||||
uint32_t splim = *it++;
|
||||
return std::make_shared<entry_point_item>(ep, sp, splim);
|
||||
} else {
|
||||
printf("Bad block size %d for entry point item\n", size);
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
std::vector<uint32_t> ret = {encode_type_and_size(splim_set ? 4 : 3), ep, sp};
|
||||
if (splim_set) ret.push_back(splim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t ep;
|
||||
uint32_t sp;
|
||||
uint32_t splim;
|
||||
bool splim_set = false;
|
||||
};
|
||||
|
||||
struct load_map_item : public item {
|
||||
struct entry {
|
||||
uint32_t storage_address;
|
||||
uint32_t runtime_address;
|
||||
uint32_t size;
|
||||
};
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_LOAD_MAP; }
|
||||
load_map_item() = default;
|
||||
|
||||
explicit load_map_item(bool absolute, std::vector<entry> entries) : absolute(absolute), entries(entries) {}
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header, uint32_t current_addr) {
|
||||
uint8_t num_entries = (header >> 24) & 0x7f;
|
||||
bool absolute = (header >> 24) & 0x80;
|
||||
std::vector<entry> entries;
|
||||
for (int i=0; i < num_entries; i++) {
|
||||
uint32_t storage_address = (uint32_t)*it++;
|
||||
uint32_t runtime_address = (uint32_t)*it++;
|
||||
uint32_t size = (uint32_t)*it++;
|
||||
if (storage_address != 0) {
|
||||
if (absolute) {
|
||||
size -= runtime_address;
|
||||
} else {
|
||||
storage_address += current_addr;
|
||||
}
|
||||
}
|
||||
entries.push_back({
|
||||
storage_address,
|
||||
runtime_address,
|
||||
size
|
||||
});
|
||||
}
|
||||
|
||||
return std::make_shared<load_map_item>(absolute, entries);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
assert(entries.size() < 256);
|
||||
std::vector<uint32_t> rc = {
|
||||
encode_type_and_size(1 + 3 * entries.size()) | (((uint8_t)entries.size())<<24) | (absolute ? 1 << 31 : 0)
|
||||
};
|
||||
for(const auto &entry : entries) {
|
||||
// todo byte order
|
||||
if (absolute) {
|
||||
rc.push_back(entry.storage_address);
|
||||
rc.push_back(entry.runtime_address);
|
||||
if (entry.storage_address != 0) {
|
||||
rc.push_back(entry.runtime_address + entry.size);
|
||||
} else {
|
||||
rc.push_back(entry.size);
|
||||
}
|
||||
} else {
|
||||
if (entry.storage_address != 0) {
|
||||
rc.push_back(entry.storage_address - ctx.base_addr - ctx.word_offset * 4);
|
||||
} else {
|
||||
rc.push_back(entry.storage_address);
|
||||
}
|
||||
rc.push_back(entry.runtime_address);
|
||||
rc.push_back(entry.size);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool absolute;
|
||||
std::vector<entry> entries;
|
||||
};
|
||||
|
||||
struct version_item : public item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_VERSION; }
|
||||
version_item() = default;
|
||||
explicit version_item(uint16_t major, uint16_t minor) : version_item(major, minor, 0, {}) {}
|
||||
version_item(uint16_t major, uint16_t minor, uint16_t rollback, std::vector<uint16_t> otp_rows) :
|
||||
rollback(rollback), otp_rows(std::move(otp_rows)) {
|
||||
// NOTE: A linux sysroot might define `major` and `minor` as macros so we
|
||||
// can't use regular initialization in the form `major(major)` since we woudn't
|
||||
// be able to tell if we're calling a macro or not.
|
||||
this->major = major;
|
||||
this->minor = minor;
|
||||
}
|
||||
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
// todo validate
|
||||
uint32_t major_minor = *it++;
|
||||
uint16_t major = major_minor >> 16;
|
||||
uint16_t minor = major_minor & 0xffff;
|
||||
unsigned int otp_row_count = header >> 24;
|
||||
uint16_t rollback = 0;
|
||||
std::vector<uint16_t> otp_rows;
|
||||
if (otp_row_count) {
|
||||
unsigned int pair = *it++;
|
||||
// todo endian
|
||||
rollback = (uint16_t) pair;
|
||||
for(unsigned int i=0;i<otp_row_count;i++) {
|
||||
if (i&1) pair = *it++;
|
||||
otp_rows.push_back((uint16_t)(pair >> ((1^(i&1))*16)));
|
||||
}
|
||||
}
|
||||
return std::make_shared<version_item>(major, minor, rollback, otp_rows);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
assert(otp_rows.size() < 256);
|
||||
unsigned int size = 2 + (!otp_rows.empty() + otp_rows.size() + 1) / 2;
|
||||
std::vector<uint32_t> rc = {
|
||||
(uint32_t)(encode_type_and_size(size) | (otp_rows.size() << 24))
|
||||
};
|
||||
uint32_t major_minor = (uint16_t)major << 16 | (uint16_t)minor;
|
||||
rc.push_back(major_minor);
|
||||
if (!otp_rows.empty()) {
|
||||
rc.push_back(rollback);
|
||||
for(unsigned int i=0;i<otp_rows.size();i++) {
|
||||
if (i&1) {
|
||||
rc.push_back(otp_rows[i]);
|
||||
} else {
|
||||
rc[rc.size()-1] |= otp_rows[i] << 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t rollback;
|
||||
std::vector<uint16_t> otp_rows;
|
||||
};
|
||||
|
||||
struct hash_def_item : public single_byte_size_item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_HASH_DEF; }
|
||||
hash_def_item() = default;
|
||||
|
||||
explicit hash_def_item(uint8_t hash_type) : hash_type(hash_type) {}
|
||||
explicit hash_def_item(uint8_t hash_type, uint16_t block_words_to_hash) : hash_type(hash_type), block_words_to_hash(block_words_to_hash) {}
|
||||
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
uint8_t hash_type = (header & 0xff000000) >> 24;
|
||||
uint16_t block_words_to_hash = *it++;
|
||||
return std::make_shared<hash_def_item>(hash_type, block_words_to_hash);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
return {
|
||||
encode_type_and_size(2) | (hash_type << 24),
|
||||
block_words_to_hash == 0 ? (ctx.word_offset + 2) : block_words_to_hash
|
||||
};
|
||||
}
|
||||
|
||||
uint8_t hash_type;
|
||||
uint16_t block_words_to_hash = 0;
|
||||
};
|
||||
|
||||
struct signature_item : public item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_SIGNATURE; }
|
||||
signature_item() = default;
|
||||
|
||||
explicit signature_item(uint8_t sig_type) : sig_type(sig_type) {}
|
||||
explicit signature_item(uint8_t sig_type, std::vector<uint8_t> signature_bytes, std::vector<uint8_t> public_key_bytes) : sig_type(sig_type), signature_bytes(signature_bytes), public_key_bytes(public_key_bytes) {}
|
||||
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
auto sig_block_size = decode_size(header);
|
||||
uint8_t sig_type = (header & 0xff000000) >> 24;
|
||||
assert(sig_block_size == 0x21);
|
||||
|
||||
std::vector<uint8_t> signature_bytes;
|
||||
std::vector<uint8_t> public_key_bytes;
|
||||
|
||||
{
|
||||
std::vector<uint32_t> words;
|
||||
for (int i=0; i < 16; i++) {
|
||||
words.push_back(*it++);
|
||||
}
|
||||
auto bytes = words_to_lsb_bytes(words.begin(), words.end());
|
||||
std::copy(bytes.begin(), bytes.end(), std::back_inserter(public_key_bytes));
|
||||
}
|
||||
{
|
||||
std::vector<uint32_t> words;
|
||||
for (int i=0; i < 16; i++) {
|
||||
words.push_back(*it++);
|
||||
}
|
||||
auto bytes = words_to_lsb_bytes(words.begin(), words.end());
|
||||
std::copy(bytes.begin(), bytes.end(), std::back_inserter(signature_bytes));
|
||||
}
|
||||
return std::make_shared<signature_item>(sig_type, signature_bytes, public_key_bytes);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
assert(signature_bytes.size() % 4 == 0);
|
||||
assert(public_key_bytes.size() % 4 == 0);
|
||||
std::vector<uint32_t> rc = {
|
||||
encode_type_and_size(1 + public_key_bytes.size()/4 + signature_bytes.size()/4) | (sig_type << 24),
|
||||
};
|
||||
auto words = lsb_bytes_to_words(public_key_bytes.begin(), public_key_bytes.end());
|
||||
std::copy(words.begin(), words.end(), std::back_inserter(rc));
|
||||
words = lsb_bytes_to_words(signature_bytes.begin(), signature_bytes.end());
|
||||
std::copy(words.begin(), words.end(), std::back_inserter(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint8_t sig_type;
|
||||
std::vector<uint8_t> signature_bytes;
|
||||
std::vector<uint8_t> public_key_bytes;
|
||||
};
|
||||
|
||||
struct hash_value_item : public item {
|
||||
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_HASH_VALUE; }
|
||||
hash_value_item() = default;
|
||||
|
||||
explicit hash_value_item(std::vector<uint8_t> hash_bytes) : hash_bytes(hash_bytes) {}
|
||||
|
||||
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
|
||||
auto hash_size = decode_size(header) - 1;
|
||||
std::vector<uint32_t> hash_words;
|
||||
for (unsigned int i=0; i < hash_size; i++) {
|
||||
hash_words.push_back(*it++);
|
||||
}
|
||||
return std::make_shared<hash_value_item>(words_to_lsb_bytes(hash_words.begin(), hash_words.end()));
|
||||
}
|
||||
|
||||
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
|
||||
assert(hash_bytes.size() % 4 == 0);
|
||||
std::vector<uint32_t> rc = {
|
||||
encode_type_and_size(1 + hash_bytes.size()/4),
|
||||
};
|
||||
auto words = lsb_bytes_to_words(hash_bytes.begin(), hash_bytes.end());
|
||||
std::copy(words.begin(), words.end(), std::back_inserter(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> hash_bytes;
|
||||
};
|
||||
|
||||
|
||||
struct block {
|
||||
explicit block(uint32_t physical_addr, uint32_t next_block_rel=0, uint32_t next_block_rel_index=0, std::vector<std::shared_ptr<item>> items = {})
|
||||
: physical_addr(physical_addr),
|
||||
next_block_rel(next_block_rel),
|
||||
next_block_rel_index(next_block_rel_index),
|
||||
items( std::move(items)) {}
|
||||
template <typename I> static std::unique_ptr<block> parse(uint32_t physical_addr, I next_block_rel_loc, I it, I end) {
|
||||
I block_base = it;
|
||||
uint32_t current_addr = physical_addr + 4; // for the ffffded3
|
||||
std::vector<std::shared_ptr<item>> items;
|
||||
while (it < end) {
|
||||
auto item_base = it;
|
||||
uint32_t header = *it++;
|
||||
uint32_t size = item::decode_size(header);
|
||||
std::shared_ptr<item> i;
|
||||
switch ((uint8_t)header) {
|
||||
case PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE:
|
||||
i = image_type_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_PARTITION_TABLE:
|
||||
i = partition_table_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_1BS_VECTOR_TABLE:
|
||||
i = vector_table_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_1BS_ROLLING_WINDOW_DELTA:
|
||||
i = rolling_window_delta_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_1BS_VERSION:
|
||||
i = version_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_1BS_ENTRY_POINT:
|
||||
i = entry_point_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_LOAD_MAP:
|
||||
i = load_map_item::parse(it, end, header, current_addr);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_1BS_HASH_DEF:
|
||||
i = hash_def_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_HASH_VALUE:
|
||||
i = hash_value_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_SIGNATURE:
|
||||
i = signature_item::parse(it, end, header);
|
||||
break;
|
||||
case PICOBIN_BLOCK_ITEM_2BS_IGNORED:
|
||||
i = ignored_item::parse(it, end, header);
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG("Ignoring block type: %02x\n", (uint8_t)header);
|
||||
i = ignored_item::parse(it, end, header);
|
||||
break;
|
||||
}
|
||||
if (i) {
|
||||
items.push_back(i);
|
||||
}
|
||||
current_addr += (size*4);
|
||||
if (it != item_base + size) {
|
||||
printf("WARNING: item at %08x had wrong size %x (expected %x)\n", (unsigned int)(physical_addr + (item_base - block_base) * 4), (int)(it - item_base), (int)size);
|
||||
it = item_base + size;
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
uint32_t next_block_rel = *next_block_rel_loc;
|
||||
uint32_t next_block_rel_index = next_block_rel_loc - block_base + 1;
|
||||
return std::make_unique<block>(physical_addr, next_block_rel, next_block_rel_index, items);
|
||||
}
|
||||
std::vector<uint32_t> to_words() {
|
||||
std::vector<uint32_t> words;
|
||||
words.push_back(PICOBIN_BLOCK_MARKER_START);
|
||||
item_writer_context ctx(physical_addr);
|
||||
for(const auto &item : items) {
|
||||
ctx.word_offset = words.size();
|
||||
ctx.item_word_offsets[item] = ctx.word_offset;
|
||||
const auto item_words = item->to_words(ctx);
|
||||
std::copy(item_words.begin(), item_words.end(), std::back_inserter(words));
|
||||
}
|
||||
// todo should use a real item struct
|
||||
|
||||
assert(words.size() <= PICOBIN_MAX_BLOCK_SIZE - 3);
|
||||
// todo byte order
|
||||
words.push_back(PICOBIN_BLOCK_ITEM_2BS_LAST | (words.size() - 1) << 8);
|
||||
words.push_back(next_block_rel);
|
||||
words.push_back(PICOBIN_BLOCK_MARKER_END);
|
||||
return words;
|
||||
}
|
||||
|
||||
template <typename I> std::shared_ptr<I> get_item() {
|
||||
I tmp = I();
|
||||
uint8_t type = tmp.type();
|
||||
auto it = std::find_if(items.begin(), items.end(), [type](std::shared_ptr<item> i) { return i->type() == type; });
|
||||
if (it != std::end(items)) {
|
||||
return std::dynamic_pointer_cast<I>(*it);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t physical_addr;
|
||||
uint32_t next_block_rel;
|
||||
uint32_t next_block_rel_index;
|
||||
std::vector<std::shared_ptr<item>> items;
|
||||
};
|
BIN
bootrom.end.bin
Normal file
BIN
bootrom.end.bin
Normal file
Binary file not shown.
230
cli.h
230
cli.h
|
@ -117,6 +117,7 @@ namespace cli {
|
|||
settings_holder(const settings_holder &other) {
|
||||
settings = other.settings->copy();
|
||||
}
|
||||
settings_holder& operator=(const settings_holder&) = default;
|
||||
void save_into() {
|
||||
settings->save_into();
|
||||
}
|
||||
|
@ -174,6 +175,7 @@ namespace cli {
|
|||
|
||||
struct matchable {
|
||||
matchable() = default;
|
||||
virtual ~matchable() = default;
|
||||
|
||||
explicit matchable(string name) : _name(std::move(name)) {}
|
||||
|
||||
|
@ -203,6 +205,10 @@ namespace cli {
|
|||
return _force_expand_help;
|
||||
}
|
||||
|
||||
string collapse_synopsys() const {
|
||||
return _collapse_synopsys;
|
||||
}
|
||||
|
||||
string doc() const {
|
||||
return _doc;
|
||||
}
|
||||
|
@ -226,6 +232,7 @@ namespace cli {
|
|||
int _max = 1;
|
||||
bool _doc_non_optional = false;
|
||||
bool _force_expand_help = false;
|
||||
string _collapse_synopsys = "";
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
|
@ -274,6 +281,11 @@ namespace cli {
|
|||
return *static_cast<D *>(this);
|
||||
}
|
||||
|
||||
D &collapse_synopsys(string v) {
|
||||
_collapse_synopsys = v;
|
||||
return *static_cast<D *>(this);
|
||||
}
|
||||
|
||||
D &max(int v) {
|
||||
_max = v;
|
||||
return *static_cast<D *>(this);
|
||||
|
@ -394,56 +406,126 @@ namespace cli {
|
|||
});
|
||||
return *this;
|
||||
}
|
||||
template<typename T> value &add_to(T &t) {
|
||||
// note we cannot capture "this"
|
||||
on_action([&t](const string& value) {
|
||||
t.push_back(value);
|
||||
return "";
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct integer : public value_base<integer> {
|
||||
explicit integer(string name) : value_base(std::move(name)) {}
|
||||
|
||||
template<typename T>
|
||||
static std::string parse_string(std::string value, T& out) {
|
||||
size_t pos = 0;
|
||||
long lvalue = std::numeric_limits<long>::max();
|
||||
int64_t base = 10;
|
||||
if (value.find("0x") == 0) {
|
||||
value = value.substr(2);
|
||||
base = 16;
|
||||
} else if (value.find("0b") == 0) {
|
||||
value = value.substr(2);
|
||||
base = 2;
|
||||
}
|
||||
try {
|
||||
if (std::is_signed<T>()) {
|
||||
lvalue = std::stoll(value, &pos, base);
|
||||
} else {
|
||||
lvalue = std::stoull(value, &pos, base);
|
||||
}
|
||||
if (pos != value.length()) {
|
||||
return "Garbage after integer value: " + value.substr(pos);
|
||||
}
|
||||
} catch (std::invalid_argument&) {
|
||||
return value + " is not a valid integer";
|
||||
} catch (std::out_of_range&) {
|
||||
return value + " is out of range";
|
||||
}
|
||||
if (lvalue != (int64_t)lvalue) {
|
||||
return value + " is too big";
|
||||
}
|
||||
out = (int64_t)lvalue;
|
||||
return "";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
integer &set(T &t) {
|
||||
int min = _min_value;
|
||||
int max = _max_value;
|
||||
int64_t min = _min_value;
|
||||
int64_t max = _max_value;
|
||||
int64_t invalid_bits = _invalid_bits;
|
||||
std::string invalid_bits_error = _invalid_bits_error;
|
||||
string nm = "<" + name() + ">";
|
||||
// note we cannot capture "this"
|
||||
on_action([&t, min, max, nm](const string& value) {
|
||||
size_t pos = 0;
|
||||
long lvalue = std::numeric_limits<long>::max();
|
||||
try {
|
||||
lvalue = std::stol(value, &pos);
|
||||
if (pos != value.length()) {
|
||||
return "Garbage after integer value: " + value.substr(pos);
|
||||
}
|
||||
} catch (std::invalid_argument&) {
|
||||
return value + " is not a valid integer";
|
||||
} catch (std::out_of_range&) {
|
||||
}
|
||||
if (lvalue != (int)lvalue) {
|
||||
return value + " is too big";
|
||||
}
|
||||
t = (int)lvalue;
|
||||
on_action([&t, min, max, nm, invalid_bits, invalid_bits_error](const string& value) {
|
||||
int64_t tmp = 0;
|
||||
std::string err = parse_string(value, tmp);
|
||||
t = tmp;
|
||||
if (!err.empty()) return err;
|
||||
if (t < min) {
|
||||
return nm + " must be >= " + std::to_string(min);
|
||||
}
|
||||
if (t > max) {
|
||||
return nm + " must be <= " + std::to_string(max);
|
||||
}
|
||||
if (t & invalid_bits) {
|
||||
return nm + " " + invalid_bits_error;
|
||||
}
|
||||
return string("");
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
integer& min_value(int v) {
|
||||
template<typename T>
|
||||
integer &add_to(T &t) {
|
||||
int64_t min = _min_value;
|
||||
int64_t max = _max_value;
|
||||
int64_t invalid_bits = _invalid_bits;
|
||||
std::string invalid_bits_error = _invalid_bits_error;
|
||||
string nm = "<" + name() + ">";
|
||||
// note we cannot capture "this"
|
||||
on_action([&t, min, max, nm, invalid_bits, invalid_bits_error](const string& value) {
|
||||
int64_t tmp = 0;
|
||||
std::string err = parse_string(value, tmp);
|
||||
if (!err.empty()) return err;
|
||||
if (tmp < min) {
|
||||
return nm + " must be >= " + std::to_string(min);
|
||||
}
|
||||
if (tmp > max) {
|
||||
return nm + " must be <= " + std::to_string(max);
|
||||
}
|
||||
if (tmp & invalid_bits) {
|
||||
return nm + " " + invalid_bits_error;
|
||||
}
|
||||
t.push_back(tmp);
|
||||
return string("");
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
integer& min_value(int64_t v) {
|
||||
_min_value = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
integer& max_value(int v) {
|
||||
integer& max_value(int64_t v) {
|
||||
_max_value = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int _min_value = 0;
|
||||
int _max_value = std::numeric_limits<int>::max();
|
||||
integer& invalid_bits(int64_t bits, std::string error) {
|
||||
_invalid_bits = bits;
|
||||
_invalid_bits_error = error;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int64_t _min_value = 0;
|
||||
int64_t _max_value = std::numeric_limits<int64_t>::max();
|
||||
std::string _invalid_bits_error;
|
||||
int64_t _invalid_bits = 0;
|
||||
};
|
||||
|
||||
struct hex : public value_base<hex> {
|
||||
|
@ -488,6 +570,46 @@ namespace cli {
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
hex &add_to(T &t) {
|
||||
unsigned int min = _min_value;
|
||||
unsigned int max = _max_value;
|
||||
string nm = "<" + name() + ">";
|
||||
// note we cannot capture "this"
|
||||
on_action([&t, min, max, nm](string value) {
|
||||
auto ovalue = value;
|
||||
if (value.find("0x") == 0) value = value.substr(2);
|
||||
size_t pos = 0;
|
||||
long lvalue = std::numeric_limits<long>::max();
|
||||
try {
|
||||
lvalue = std::stoul(value, &pos, 16);
|
||||
if (pos != value.length()) {
|
||||
return "Garbage after hex value: " + value.substr(pos);
|
||||
}
|
||||
} catch (std::invalid_argument&) {
|
||||
return ovalue + " is not a valid hex value";
|
||||
} catch (std::out_of_range&) {
|
||||
}
|
||||
if (lvalue != (unsigned int)lvalue) {
|
||||
return value + " is not a valid 32 bit value";
|
||||
}
|
||||
unsigned int tmp = (unsigned int)lvalue;
|
||||
if (tmp < min) {
|
||||
std::stringstream ss;
|
||||
ss << nm << " must be >= 0x" << std::hex << std::to_string(min);
|
||||
return ss.str();
|
||||
}
|
||||
if (tmp > max) {
|
||||
std::stringstream ss;
|
||||
ss << nm << " must be <= 0x" << std::hex << std::to_string(max);
|
||||
return ss.str();
|
||||
}
|
||||
t.push_back(tmp);
|
||||
return string("");
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
hex& min_value(unsigned int v) {
|
||||
_min_value = v;
|
||||
return *this;
|
||||
|
@ -507,6 +629,7 @@ namespace cli {
|
|||
sequence,
|
||||
set,
|
||||
exclusive,
|
||||
collapse,
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -516,7 +639,7 @@ namespace cli {
|
|||
explicit group(const T &t) : type(set), elements{t.to_ptr()} {}
|
||||
|
||||
template<class Matchable, class... Matchables>
|
||||
group(Matchable m, Matchable ms...) : elements{m, ms}, type(set) {}
|
||||
group(Matchable m, Matchable ms...) : type(set), elements{m, ms} {}
|
||||
|
||||
group &set_type(group_type t) {
|
||||
type = t;
|
||||
|
@ -542,23 +665,30 @@ namespace cli {
|
|||
case set:
|
||||
case sequence: {
|
||||
std::vector<std::vector<string>> tmp{{}};
|
||||
for (auto &x : elements) {
|
||||
auto xs = x->synopsys();
|
||||
if (xs.size() == 1) {
|
||||
for (auto &s : tmp) {
|
||||
s.push_back(decorate(*x, xs[0]));
|
||||
}
|
||||
} else {
|
||||
auto save = tmp;
|
||||
tmp.clear();
|
||||
for (auto &v : save) {
|
||||
for (auto &s : xs) {
|
||||
auto nv = v;
|
||||
nv.push_back(decorate(*x, s));
|
||||
tmp.push_back(nv);
|
||||
if (_collapse_synopsys.empty()) {
|
||||
for (auto &x : elements) {
|
||||
auto xs = x->synopsys();
|
||||
if (xs.size() == 1) {
|
||||
for (auto &s : tmp) {
|
||||
s.push_back(decorate(*x, xs[0]));
|
||||
}
|
||||
} else {
|
||||
auto save = tmp;
|
||||
tmp.clear();
|
||||
for (auto &v : save) {
|
||||
for (auto &s : xs) {
|
||||
auto nv = v;
|
||||
nv.push_back(decorate(*x, s));
|
||||
tmp.push_back(nv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::vector<std::string> xs = {"[" + _collapse_synopsys + "]"};
|
||||
for (auto &s : tmp) {
|
||||
s.push_back(xs[0]);
|
||||
}
|
||||
}
|
||||
for (const auto &v : tmp) {
|
||||
rc.push_back(join(v, " "));
|
||||
|
@ -580,17 +710,17 @@ namespace cli {
|
|||
return rc;
|
||||
}
|
||||
|
||||
group operator|(const group &g) {
|
||||
return matchable_derived::operator|(g);
|
||||
}
|
||||
|
||||
group operator&(const group &g) {
|
||||
return matchable_derived::operator&(g);
|
||||
}
|
||||
|
||||
group operator+(const group &g) {
|
||||
return matchable_derived::operator+(g);
|
||||
}
|
||||
// group operator|(const group &g) {
|
||||
// return matchable_derived::operator|(g);
|
||||
// }
|
||||
//
|
||||
// group operator&(const group &g) {
|
||||
// return matchable_derived::operator&(g);
|
||||
// }
|
||||
//
|
||||
// group operator+(const group &g) {
|
||||
// return matchable_derived::operator+(g);
|
||||
// }
|
||||
|
||||
bool no_match_beats_error() const {
|
||||
return _no_match_beats_error;
|
||||
|
@ -776,8 +906,8 @@ namespace cli {
|
|||
|
||||
private:
|
||||
string _major_group;
|
||||
vector<std::shared_ptr<matchable>> elements;
|
||||
group_type type;
|
||||
vector<std::shared_ptr<matchable>> elements;
|
||||
bool _no_match_beats_error = true;
|
||||
};
|
||||
|
||||
|
@ -860,7 +990,7 @@ namespace cli {
|
|||
return update_stats(match_type::match, matchable);
|
||||
}
|
||||
|
||||
template<typename S> struct typed_settings : public opaque_settings {
|
||||
template<typename S> struct typed_settings final : public opaque_settings {
|
||||
explicit typed_settings(S& settings) : root_settings(settings), settings(settings) {
|
||||
}
|
||||
|
||||
|
|
|
@ -8,23 +8,60 @@
|
|||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
# Set LIBUSB_ROOT if specified
|
||||
if (LIBUSB_ROOT)
|
||||
set(ENV{LIBUSB_ROOT} ${LIBUSB_ROOT})
|
||||
endif()
|
||||
|
||||
if (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
# in cache already
|
||||
set(LIBUSB_FOUND TRUE)
|
||||
else (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
IF (NOT MSVC)
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
find_package(PkgConfig)
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the find_path() and find_library() calls. Might fail, pkg-config
|
||||
# might not be installed, e.g. for Windows systems
|
||||
find_package(PkgConfig)
|
||||
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_LIBUSB libusb-1.0)
|
||||
ENDIF ()
|
||||
FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
|
||||
HINTS $ENV{LIBUSB_ROOT}/include/libusb-1.0
|
||||
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS})
|
||||
FIND_LIBRARY(LIBUSB_LIBRARIES NAMES libusb-1.0 usb-1.0 usb
|
||||
HINTS $ENV{LIBUSB_ROOT}/VS2019/MS32/static
|
||||
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if (NOT PC_LIBUSB_FOUND)
|
||||
# As the pkg-config was not found we are probably building under windows.
|
||||
# Determine the architecture of the host, to choose right library
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
if (CMAKE_SIZEOF_VOID_P GREATER 4)
|
||||
set(ARCHITECTURE 64)
|
||||
else()
|
||||
set(ARCHITECTURE 32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(PC_LIBUSB_INCLUDEDIR_HINT $ENV{LIBUSB_ROOT}/include)
|
||||
|
||||
if (MINGW OR CYGWIN)
|
||||
set(PC_LIBUSB_LIBDIR_HINT $ENV{LIBUSB_ROOT}/MinGW${ARCHITECTURE}/static)
|
||||
elseif(MSVC)
|
||||
set(PC_LIBUSB_LIBDIR_HINT $ENV{LIBUSB_ROOT}/VS2019/MS${ARCHITECTURE}/static)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
find_path(LIBUSB_INCLUDE_DIR libusb.h
|
||||
HINTS ${PC_LIBUSB_INCLUDEDIR_HINT}
|
||||
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES libusb-1.0)
|
||||
|
||||
find_library(LIBUSB_LIBRARIES NAMES libusb-1.0 usb-1.0 usb
|
||||
HINTS ${PC_LIBUSB_LIBDIR_HINT}
|
||||
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIR)
|
||||
|
||||
# Don't use .dll.a libraries, as they require the .dll file to be in the correct location
|
||||
# Replace with .a for static linking instead
|
||||
string(REPLACE ".dll.a" ".a" LIBUSB_LIBRARIES ${LIBUSB_LIBRARIES})
|
||||
|
||||
MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
|
||||
endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
|
|
5
cmake/bin.template.h
Normal file
5
cmake/bin.template.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <stddef.h>
|
||||
|
||||
const unsigned char @C_NAME@[] = {@FILE_CONTENT@};
|
||||
|
||||
const size_t @C_NAME@_SIZE = @BIN_LENGTH@;
|
28
cmake/binh.cmake
Normal file
28
cmake/binh.cmake
Normal file
|
@ -0,0 +1,28 @@
|
|||
file(READ ${BINARY_FILE} FILE_CONTENT HEX)
|
||||
string(LENGTH ${FILE_CONTENT} FILE_CONTENT_LENGTH)
|
||||
math(EXPR BIN_LENGTH "${FILE_CONTENT_LENGTH} / 2")
|
||||
|
||||
math(EXPR offset "0")
|
||||
|
||||
while(FILE_CONTENT_LENGTH GREATER 0)
|
||||
|
||||
if(FILE_CONTENT_LENGTH GREATER 32)
|
||||
math(EXPR length "32")
|
||||
else()
|
||||
math(EXPR length "${FILE_CONTENT_LENGTH}")
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${FILE_CONTENT} ${offset} ${length} line)
|
||||
set(lines "${lines}\n${line}")
|
||||
|
||||
math(EXPR FILE_CONTENT_LENGTH "${FILE_CONTENT_LENGTH} - ${length}")
|
||||
math(EXPR offset "${offset} + ${length}")
|
||||
endwhile()
|
||||
|
||||
set(FILE_CONTENT "${lines}")
|
||||
|
||||
# adds '0x' prefix and comma suffix before and after every byte respectively
|
||||
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " FILE_CONTENT ${FILE_CONTENT})
|
||||
string(MAKE_C_IDENTIFIER "${OUTPUT_NAME}" C_NAME)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/bin.template.h ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}.h @ONLY)
|
2
cmake/jsonh.cmake
Normal file
2
cmake/jsonh.cmake
Normal file
|
@ -0,0 +1,2 @@
|
|||
file(READ ${GENERATED_JSON} FILE_CONTENT)
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/rp2350.json.template.h ${CMAKE_CURRENT_BINARY_DIR}/rp2350.json.h @ONLY)
|
3
cmake/picotoolConfig.cmake
Normal file
3
cmake/picotoolConfig.cmake
Normal file
|
@ -0,0 +1,3 @@
|
|||
if (NOT TARGET picotool)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/picotoolTargets.cmake")
|
||||
endif()
|
6
cmake/rp2350.json.template.h
Normal file
6
cmake/rp2350.json.template.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
const std::string rp2350_json = R"""(
|
||||
@FILE_CONTENT@
|
||||
)""";
|
5
data_locs.h
Normal file
5
data_locs.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
extern std::vector<std::string> data_locs;
|
7
data_locs.template.cpp
Normal file
7
data_locs.template.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
std::vector<std::string> data_locs = {
|
||||
"${DATA_LOCS_VEC}"
|
||||
};
|
60
elf.h
60
elf.h
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _ELF_H
|
||||
#define _ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ELF_MAGIC 0x464c457fu
|
||||
|
||||
#define EM_ARM 0x28u
|
||||
|
||||
#define EF_ARM_ABI_FLOAT_HARD 0x00000400u
|
||||
|
||||
#define PT_LOAD 0x00000001u
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct elf_header {
|
||||
uint32_t magic;
|
||||
uint8_t arch_class;
|
||||
uint8_t endianness;
|
||||
uint8_t version;
|
||||
uint8_t abi;
|
||||
uint8_t abi_version;
|
||||
uint8_t _pad[7];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version2;
|
||||
};
|
||||
|
||||
struct elf32_header {
|
||||
struct elf_header common;
|
||||
uint32_t entry;
|
||||
uint32_t ph_offset;
|
||||
uint32_t sh_offset;
|
||||
uint32_t flags;
|
||||
uint16_t eh_size;
|
||||
uint16_t ph_entry_size;
|
||||
uint16_t ph_num;
|
||||
uint16_t sh_entry_size;
|
||||
uint16_t sh_num;
|
||||
uint16_t sh_str_index;
|
||||
};
|
||||
|
||||
struct elf32_ph_entry {
|
||||
uint32_t type;
|
||||
uint32_t offset;
|
||||
uint32_t vaddr;
|
||||
uint32_t paddr;
|
||||
uint32_t filez;
|
||||
uint32_t memsz;
|
||||
uint32_t flags;
|
||||
uint32_t align;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
20
elf/BUILD.bazel
Normal file
20
elf/BUILD.bazel
Normal file
|
@ -0,0 +1,20 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "elf",
|
||||
srcs = ["elf_file.cpp"],
|
||||
hdrs = [
|
||||
"addresses.h",
|
||||
"elf.h",
|
||||
"elf_file.h",
|
||||
"portable_endian.h",
|
||||
],
|
||||
copts = select({
|
||||
"@platforms//os:windows": [],
|
||||
"//conditions:default": [
|
||||
"-Wno-unused-function",
|
||||
],
|
||||
}),
|
||||
includes = ["."],
|
||||
deps = ["//errors"],
|
||||
)
|
6
elf/CMakeLists.txt
Normal file
6
elf/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
add_library(elf STATIC
|
||||
elf_file.cpp)
|
||||
|
||||
target_include_directories(elf PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(elf PRIVATE errors)
|
94
elf/addresses.h
Normal file
94
elf/addresses.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef _ADDRESSES_H
|
||||
#define _ADDRESSES_H
|
||||
|
||||
#define ROM_START 0x00000000 // same as ROM_BASE in addressmap.h
|
||||
#define ROM_END_RP2040 0x00004000
|
||||
#define ROM_END_RP2350 0x00008000
|
||||
// todo amy based on what sort of elf (also this breaks RP2040 builds?)
|
||||
#define FLASH_START 0x10000000 // same as XIP_MAIN_BASE in addressmap.h
|
||||
#define FLASH_END_RP2040 0x11000000 // +32 MiB -- remainder has no external devices mapped
|
||||
#define FLASH_END_RP2350 0x12000000 // +32 MiB -- remainder has no external devices mapped
|
||||
// todo amy based on what sort of elf
|
||||
#define XIP_SRAM_START_RP2040 0x15000000
|
||||
#define XIP_SRAM_END_RP2040 0x15004000
|
||||
#define XIP_SRAM_START_RP2350 0x13ffc000 // same as XIP_SRAM_BASE in addressmap.h
|
||||
#define XIP_SRAM_END_RP2350 0x14000000 // same as XIP_SRAM_END in addressmap.h
|
||||
|
||||
#define SRAM_START 0x20000000 // same as SRAM_BASE in addressmap.h
|
||||
#define SRAM_END_RP2040 0x20042000
|
||||
#define SRAM_END_RP2350 0x20082000
|
||||
// todo amy no more banked alias
|
||||
#define MAIN_RAM_BANKED_START 0x21000000
|
||||
#define MAIN_RAM_BANKED_END 0x21040000
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef IGNORE
|
||||
#endif
|
||||
|
||||
// Address ranges for RP2040/RP2350
|
||||
struct address_range {
|
||||
enum type {
|
||||
CONTENTS, // may have contents
|
||||
NO_CONTENTS, // must be uninitialized
|
||||
IGNORE // will be ignored
|
||||
};
|
||||
address_range(uint32_t from, uint32_t to, type type) : from(from), to(to), type(type) {}
|
||||
address_range() : address_range(0, 0, IGNORE) {}
|
||||
uint32_t from;
|
||||
uint32_t to;
|
||||
type type;
|
||||
};
|
||||
|
||||
typedef std::vector<address_range> address_ranges;
|
||||
|
||||
|
||||
const address_ranges rp2040_address_ranges_flash {
|
||||
address_range(FLASH_START, FLASH_END_RP2040, address_range::type::CONTENTS),
|
||||
address_range(SRAM_START, SRAM_END_RP2040, address_range::type::NO_CONTENTS),
|
||||
address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
|
||||
};
|
||||
|
||||
const address_ranges rp2040_address_ranges_ram {
|
||||
address_range(SRAM_START, SRAM_END_RP2040, address_range::type::CONTENTS),
|
||||
address_range(XIP_SRAM_START_RP2040, XIP_SRAM_END_RP2040, address_range::type::CONTENTS),
|
||||
address_range(ROM_START, ROM_END_RP2040, address_range::type::IGNORE) // for now we ignore the bootrom if present
|
||||
};
|
||||
|
||||
const address_ranges rp2350_address_ranges_flash {
|
||||
address_range(FLASH_START, FLASH_END_RP2350, address_range::type::CONTENTS),
|
||||
address_range(SRAM_START, SRAM_END_RP2350, address_range::type::NO_CONTENTS),
|
||||
address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
|
||||
};
|
||||
|
||||
const address_ranges rp2350_address_ranges_ram {
|
||||
address_range(SRAM_START, SRAM_END_RP2350, address_range::type::CONTENTS),
|
||||
address_range(XIP_SRAM_START_RP2350, XIP_SRAM_END_RP2350, address_range::type::CONTENTS),
|
||||
address_range(ROM_START, ROM_END_RP2350, address_range::type::IGNORE) // for now we ignore the bootrom if present
|
||||
};
|
||||
|
||||
static bool is_address_valid(const address_ranges& valid_ranges, uint32_t addr) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to > addr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_address_initialized(const address_ranges& valid_ranges, uint32_t addr) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to > addr) {
|
||||
return address_range::type::CONTENTS == range.type;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
126
elf/elf.h
Normal file
126
elf/elf.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _ELF_H
|
||||
#define _ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ELF_MAGIC 0x464c457fu
|
||||
|
||||
#define EM_ARM 0x28u
|
||||
#define EM_RISCV 0xf3u
|
||||
|
||||
#define EF_ARM_ABI_FLOAT_HARD 0x00000400u
|
||||
|
||||
#define PT_LOAD 0x00000001u
|
||||
|
||||
#define PF_X 0x1u
|
||||
#define PF_W 0x2u
|
||||
#define PF_R 0x4u
|
||||
|
||||
#define SHT_NULL 0x00000000u
|
||||
#define SHT_PROGBITS 0x00000001u
|
||||
#define SHT_NOBITS 0x00000008u
|
||||
#define SHF_ALLOC 0x00000002u
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct elf_header {
|
||||
uint32_t magic;
|
||||
uint8_t arch_class;
|
||||
uint8_t endianness;
|
||||
uint8_t version;
|
||||
uint8_t abi;
|
||||
uint8_t abi_version;
|
||||
uint8_t _pad[7];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version2;
|
||||
};
|
||||
|
||||
struct elf32_header {
|
||||
struct elf_header common;
|
||||
uint32_t entry;
|
||||
uint32_t ph_offset;
|
||||
uint32_t sh_offset;
|
||||
uint32_t flags;
|
||||
uint16_t eh_size;
|
||||
uint16_t ph_entry_size;
|
||||
uint16_t ph_num;
|
||||
uint16_t sh_entry_size;
|
||||
uint16_t sh_num;
|
||||
uint16_t sh_str_index;
|
||||
};
|
||||
|
||||
struct elf32_ph_entry {
|
||||
#ifdef __cplusplus
|
||||
uint32_t physical_address(void) const {return paddr;};
|
||||
uint32_t physical_size(void) const {return filez;};
|
||||
uint32_t virtual_address(void) const {return vaddr;};
|
||||
uint32_t virtual_size(void) const {return memsz;};
|
||||
bool is_load(void) const {return type == 0x1;};
|
||||
#endif
|
||||
uint32_t type;
|
||||
uint32_t offset;
|
||||
uint32_t vaddr;
|
||||
uint32_t paddr;
|
||||
uint32_t filez;
|
||||
uint32_t memsz;
|
||||
uint32_t flags;
|
||||
uint32_t align;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef elf32_ph_entry Segment;
|
||||
inline std::string to_string(const elf32_ph_entry &ph) {
|
||||
std::stringstream ss;
|
||||
ss << "segment paddr " << std::hex << ph.paddr << " vaddr " << std::hex << ph.vaddr;
|
||||
return ss.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct elf32_sh_entry {
|
||||
#ifdef __cplusplus
|
||||
uint32_t virtual_address(void) const {return addr;};
|
||||
#endif
|
||||
uint32_t name;
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
uint32_t addr;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t link;
|
||||
uint32_t info;
|
||||
uint32_t addralign;
|
||||
uint32_t entsize;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef elf32_sh_entry Section;
|
||||
inline std::string to_string(const elf32_sh_entry &sh) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << sh.addr;
|
||||
return ss.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct elf32_sym_entry {
|
||||
uint32_t name;
|
||||
uint32_t value;
|
||||
uint32_t size;
|
||||
uint8_t info;
|
||||
uint8_t other;
|
||||
uint16_t shndx;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
548
elf/elf_file.cpp
Normal file
548
elf/elf_file.cpp
Normal file
|
@ -0,0 +1,548 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "elf.h"
|
||||
#include "elf_file.h"
|
||||
#include "errors.h"
|
||||
|
||||
#include "portable_endian.h"
|
||||
|
||||
// tsk namespace is polluted on windows
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
void eh_he(elf32_header &eh) {
|
||||
// Swap to host endianness
|
||||
eh.common.magic = le32toh(eh.common.magic);
|
||||
eh.common.type = le16toh(eh.common.type);
|
||||
eh.common.machine = le16toh(eh.common.machine);
|
||||
eh.common.version2 = le32toh(eh.common.version2);
|
||||
eh.entry = le32toh(eh.entry);
|
||||
eh.ph_offset = le32toh(eh.ph_offset);
|
||||
eh.sh_offset = le32toh(eh.sh_offset);
|
||||
eh.flags = le32toh(eh.flags);
|
||||
eh.eh_size = le16toh(eh.eh_size);
|
||||
eh.ph_entry_size = le16toh(eh.ph_entry_size);
|
||||
eh.ph_num = le16toh(eh.ph_num);
|
||||
eh.sh_entry_size = le16toh(eh.sh_entry_size);
|
||||
eh.sh_num = le16toh(eh.sh_num);
|
||||
eh.sh_str_index = le16toh(eh.sh_str_index);
|
||||
}
|
||||
void eh_le(elf32_header &eh) {
|
||||
// Swap to little endianness
|
||||
eh.common.magic = htole32(eh.common.magic);
|
||||
eh.common.type = htole16(eh.common.type);
|
||||
eh.common.machine = htole16(eh.common.machine);
|
||||
eh.common.version2 = htole32(eh.common.version2);
|
||||
eh.entry = htole32(eh.entry);
|
||||
eh.ph_offset = htole32(eh.ph_offset);
|
||||
eh.sh_offset = htole32(eh.sh_offset);
|
||||
eh.flags = htole32(eh.flags);
|
||||
eh.eh_size = htole16(eh.eh_size);
|
||||
eh.ph_entry_size = htole16(eh.ph_entry_size);
|
||||
eh.ph_num = htole16(eh.ph_num);
|
||||
eh.sh_entry_size = htole16(eh.sh_entry_size);
|
||||
eh.sh_num = htole16(eh.sh_num);
|
||||
eh.sh_str_index = htole16(eh.sh_str_index);
|
||||
}
|
||||
void ph_he(elf32_ph_entry &ph) {
|
||||
// Swap to host endianness
|
||||
ph.type = le32toh(ph.type);
|
||||
ph.offset = le32toh(ph.offset);
|
||||
ph.vaddr = le32toh(ph.vaddr);
|
||||
ph.paddr = le32toh(ph.paddr);
|
||||
ph.filez = le32toh(ph.filez);
|
||||
ph.memsz = le32toh(ph.memsz);
|
||||
ph.flags = le32toh(ph.flags);
|
||||
ph.align = le32toh(ph.align);
|
||||
}
|
||||
void ph_le(elf32_ph_entry &ph) {
|
||||
// Swap to little endianness
|
||||
ph.type = htole32(ph.type);
|
||||
ph.offset = htole32(ph.offset);
|
||||
ph.vaddr = htole32(ph.vaddr);
|
||||
ph.paddr = htole32(ph.paddr);
|
||||
ph.filez = htole32(ph.filez);
|
||||
ph.memsz = htole32(ph.memsz);
|
||||
ph.flags = htole32(ph.flags);
|
||||
ph.align = htole32(ph.align);
|
||||
}
|
||||
void sh_he(elf32_sh_entry &sh) {
|
||||
// Swap to host endianness
|
||||
sh.name = le32toh(sh.name);
|
||||
sh.type = le32toh(sh.type);
|
||||
sh.flags = le32toh(sh.flags);
|
||||
sh.addr = le32toh(sh.addr);
|
||||
sh.offset = le32toh(sh.offset);
|
||||
sh.size = le32toh(sh.size);
|
||||
sh.link = le32toh(sh.link);
|
||||
sh.info = le32toh(sh.info);
|
||||
sh.addralign = le32toh(sh.addralign);
|
||||
sh.entsize = le32toh(sh.entsize);
|
||||
}
|
||||
void sh_le(elf32_sh_entry &sh) {
|
||||
// Swap to little endianness
|
||||
sh.name = htole32(sh.name);
|
||||
sh.type = htole32(sh.type);
|
||||
sh.flags = htole32(sh.flags);
|
||||
sh.addr = htole32(sh.addr);
|
||||
sh.offset = htole32(sh.offset);
|
||||
sh.size = htole32(sh.size);
|
||||
sh.link = htole32(sh.link);
|
||||
sh.info = htole32(sh.info);
|
||||
sh.addralign = htole32(sh.addralign);
|
||||
sh.entsize = htole32(sh.entsize);
|
||||
}
|
||||
void sym_he(elf32_sym_entry &sym) {
|
||||
// Swap to host endianness
|
||||
sym.name = le32toh(sym.name);
|
||||
sym.value = le32toh(sym.value);
|
||||
sym.size = le32toh(sym.size);
|
||||
sym.info = le32toh(sym.info);
|
||||
sym.other = le32toh(sym.other);
|
||||
sym.shndx = le32toh(sym.shndx);
|
||||
}
|
||||
void sym_le(elf32_sym_entry &sym) {
|
||||
// Swap to little endianness
|
||||
sym.name = htole32(sym.name);
|
||||
sym.value = htole32(sym.value);
|
||||
sym.size = htole32(sym.size);
|
||||
sym.info = htole32(sym.info);
|
||||
sym.other = htole32(sym.other);
|
||||
sym.shndx = htole32(sym.shndx);
|
||||
}
|
||||
|
||||
// Checks whether an ELF header is compatible with RP2040 / RP3050
|
||||
// Returns zero on success
|
||||
int rp_check_elf_header(const elf32_header &eh) {
|
||||
if (eh.common.magic != ELF_MAGIC) {
|
||||
fail(ERROR_FORMAT, "Not an ELF file");
|
||||
}
|
||||
if (eh.common.version != 1 || eh.common.version2 != 1) {
|
||||
fail(ERROR_FORMAT, "Unrecognized ELF version");
|
||||
}
|
||||
if (eh.common.arch_class != 1 || eh.common.endianness != 1) {
|
||||
fail(ERROR_INCOMPATIBLE, "Require 32 bit little-endian ELF");
|
||||
}
|
||||
if (eh.eh_size != sizeof(struct elf32_header)) {
|
||||
fail(ERROR_FORMAT, "Invalid ELF32 format");
|
||||
}
|
||||
if (eh.common.machine != EM_ARM && eh.common.machine != EM_RISCV) {
|
||||
fail(ERROR_FORMAT, "Not an Arm or RISC-V executable");
|
||||
}
|
||||
// Accept either ELFOSABI_NONE or ELFOSABI_GNU for EI_OSABI. Compilers may
|
||||
// set the OS/ABI field to ELFOSABI_GNU when they use GNU features, such as
|
||||
// the SHF_GNU_RETAIN section flag, but the binary is still compatible.
|
||||
if (eh.common.abi != 0 /* NONE */ && eh.common.abi != 3 /* GNU */) {
|
||||
fail(ERROR_INCOMPATIBLE, "Unrecognized ABI");
|
||||
}
|
||||
// todo amy not sure if this should be expected or not - we have HARD float in clang only for now
|
||||
if (eh.flags & EF_ARM_ABI_FLOAT_HARD) {
|
||||
// fail(ERROR_INCOMPATIBLE, "HARD-FLOAT not supported");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine binary type (flash or ram)
|
||||
int rp_determine_binary_type(const elf32_header &eh, const std::vector<elf32_ph_entry>& entries, address_ranges flash_range, address_ranges ram_range, bool *ram_style) {
|
||||
for(const auto &entry : entries) {
|
||||
if (entry.type == PT_LOAD && entry.memsz) {
|
||||
unsigned int mapped_size = std::min(entry.filez, entry.memsz);
|
||||
if (mapped_size) {
|
||||
// we back convert the entrypoint from a VADDR to a PADDR to see if it originates in flash, and if
|
||||
// so call THAT a flash binary.
|
||||
if (eh.entry >= entry.vaddr && eh.entry < entry.vaddr + mapped_size) {
|
||||
uint32_t effective_entry = eh.entry + entry.paddr - entry.vaddr;
|
||||
if (is_address_initialized(ram_range, effective_entry)) {
|
||||
*ram_style = true;
|
||||
return 0;
|
||||
} else if (is_address_initialized(flash_range, effective_entry)) {
|
||||
*ram_style = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fail(ERROR_INCOMPATIBLE, "entry point is not in mapped part of file");
|
||||
return ERROR_INCOMPATIBLE;
|
||||
}
|
||||
|
||||
void elf_file::read_bytes(unsigned offset, unsigned length, void *dest) {
|
||||
if (offset + length > elf_bytes.size()) {
|
||||
fail(ERROR_FORMAT, "ELF File Read from 0x%x with size 0x%x exceeds the file size 0x%x", offset, length, elf_bytes.size());
|
||||
}
|
||||
memcpy(dest, &elf_bytes[offset], length);
|
||||
}
|
||||
|
||||
int elf_file::read_header(void) {
|
||||
read_bytes(0, sizeof(eh), &eh);
|
||||
eh_he(eh); // swap to Host for processing
|
||||
return rp_check_elf_header(eh);
|
||||
}
|
||||
|
||||
// Flattens the data in the section array the elf_bytes blob
|
||||
void elf_file::flatten(void) {
|
||||
elf_bytes.resize(sizeof(eh));
|
||||
auto eh_out = eh;
|
||||
eh_le(eh_out); // swap to LE for writing
|
||||
memcpy(&elf_bytes[0], &eh_out, sizeof(eh_out));
|
||||
|
||||
elf_bytes.resize(std::max(eh.ph_offset + sizeof(elf32_ph_entry) * eh.ph_num, elf_bytes.size()));
|
||||
auto ph_entries_out = ph_entries;
|
||||
for (auto ph : ph_entries_out) {
|
||||
ph_le(ph); // swap to LE for writing
|
||||
}
|
||||
memcpy(&elf_bytes[eh.ph_offset], &ph_entries_out[0], sizeof(elf32_ph_entry) * eh.ph_num);
|
||||
|
||||
elf_bytes.resize(std::max(eh.sh_offset + sizeof(elf32_sh_entry) * eh.sh_num, elf_bytes.size()));
|
||||
auto sh_entries_out = sh_entries;
|
||||
for (auto sh : sh_entries_out) {
|
||||
sh_le(sh); // swap to LE for writing
|
||||
}
|
||||
memcpy(&elf_bytes[eh.sh_offset], &sh_entries_out[0], sizeof(elf32_sh_entry) * eh.sh_num);
|
||||
|
||||
int idx = 0;
|
||||
for (const auto &sh : sh_entries) {
|
||||
if (sh.size && sh.type != SHT_NOBITS) {
|
||||
elf_bytes.resize(std::max(sh.offset + sh.size, (uint32_t)elf_bytes.size()));
|
||||
memcpy(&elf_bytes[sh.offset], &sh_data[idx][0], sh.size);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
for (const auto &ph : ph_entries) {
|
||||
if (ph.filez) {
|
||||
elf_bytes.resize(std::max(ph.offset + ph.filez, (uint32_t)elf_bytes.size()));
|
||||
memcpy(&elf_bytes[ph.offset], &ph_data[idx][0], ph.filez);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (verbose) printf("Elf file size %zu\n", elf_bytes.size());
|
||||
}
|
||||
|
||||
void elf_file::write(std::shared_ptr<std::iostream> out) {
|
||||
flatten();
|
||||
out->exceptions(std::iostream::failbit | std::iostream::badbit);
|
||||
if (verbose) printf("Writing %lu bytes to file\n", elf_bytes.size());
|
||||
out->write(reinterpret_cast<const char*>(&elf_bytes[0]), elf_bytes.size());
|
||||
}
|
||||
|
||||
void elf_file::read_sh(void) {
|
||||
if (verbose) printf("%s sh offset %u #entries %d\n", __func__, eh.sh_offset, eh.sh_num);
|
||||
if (eh.sh_num) {
|
||||
sh_entries.resize(eh.sh_num);
|
||||
read_bytes(eh.sh_offset, sizeof(elf32_sh_entry) * eh.sh_num, &sh_entries[0]);
|
||||
for (auto sh : sh_entries) {
|
||||
sh_he(sh); // swap to Host for processing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the section data from the internal byte array into discrete sections.
|
||||
// This is used after modifying segments but before inserting new segments
|
||||
void elf_file::read_sh_data(void) {
|
||||
int sh_idx = 0;
|
||||
sh_data.resize(eh.sh_num);
|
||||
for (const auto &sh: sh_entries) {
|
||||
if (sh.size && sh.type != SHT_NOBITS) {
|
||||
sh_data[sh_idx].resize(sh.size);
|
||||
read_bytes(sh.offset, sh.size, &sh_data[sh_idx][0]);
|
||||
}
|
||||
sh_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
void elf_file::read_ph_data(void) {
|
||||
int ph_idx = 0;
|
||||
ph_data.resize(eh.ph_num);
|
||||
for (const auto &ph: ph_entries) {
|
||||
if (ph.filez) {
|
||||
ph_data[ph_idx].resize(ph.filez);
|
||||
read_bytes(ph.offset, ph.filez, &ph_data[ph_idx][0]);
|
||||
}
|
||||
ph_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string elf_file::section_name(uint32_t sh_name) const {
|
||||
if (!eh.sh_str_index || eh.sh_str_index > eh.sh_num)
|
||||
return "";
|
||||
|
||||
if (sh_name > sh_data[eh.sh_str_index].size())
|
||||
return "";
|
||||
|
||||
const char * str =(const char *) &sh_data[eh.sh_str_index][0];
|
||||
return &str[sh_name];
|
||||
}
|
||||
|
||||
const elf32_sh_entry* elf_file::get_section(const std::string &sh_name) {
|
||||
for (unsigned int i = 0; i < sh_entries.size(); i++) {
|
||||
if (section_name(sh_entries[i].name) == sh_name) {
|
||||
return &sh_entries[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t elf_file::get_symbol(const std::string &sym_name) {
|
||||
auto sym_tab = get_section(".symtab");
|
||||
auto str_tab = get_section(".strtab");
|
||||
if (!sym_tab || !str_tab) {
|
||||
return 0;
|
||||
}
|
||||
auto data = content(*sym_tab);
|
||||
auto strings = content(*str_tab);
|
||||
const char * str =(const char *) strings.data();
|
||||
for (unsigned int i=0; i < sym_tab->size / sizeof(elf32_sym_entry); i++) {
|
||||
elf32_sym_entry sym;
|
||||
memcpy(&sym, data.data() + i*sizeof(elf32_sym_entry), sizeof(elf32_sym_entry));
|
||||
sym_he(sym); // swap to Host for processing
|
||||
if (&str[sym.name] == sym_name) {
|
||||
return sym.value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t elf_file::append_section_name(const std::string &sh_name_str) {
|
||||
// Create byte array with new section name
|
||||
std::vector<uint8_t> name_bytes(sh_name_str.begin(), sh_name_str.end());
|
||||
name_bytes.push_back(0);
|
||||
|
||||
// Append the byte array to section header table remembering the offset
|
||||
// of the start of the string for the new section
|
||||
elf32_sh_entry &shstrtab = sh_entries[eh.sh_str_index];
|
||||
std::vector<uint8_t> &shstrtab_data = sh_data[eh.sh_str_index];
|
||||
sh_entries[eh.sh_str_index].size += name_bytes.size();
|
||||
uint32_t sh_name = shstrtab_data.size();
|
||||
shstrtab_data.insert(shstrtab_data.end(), name_bytes.begin(), name_bytes.end());
|
||||
|
||||
// Move offsets for anything stored after the resized section header table
|
||||
for (auto &sh: sh_entries) {
|
||||
if (sh.offset > shstrtab.offset)
|
||||
sh.offset += name_bytes.size();
|
||||
}
|
||||
for (auto &ph: ph_entries) {
|
||||
if (ph.offset > shstrtab.offset)
|
||||
ph.offset += name_bytes.size();
|
||||
}
|
||||
return sh_name;
|
||||
}
|
||||
|
||||
void elf_file::dump(void) const {
|
||||
for (const auto &ph: ph_entries) {
|
||||
printf("PH offset %08x vaddr %08x paddr %08x size %08x type %08x\n",
|
||||
ph.offset, ph.vaddr, ph.paddr, ph.memsz, ph.type);
|
||||
}
|
||||
|
||||
int sh_idx = 0;
|
||||
for (const auto &sh: sh_entries) {
|
||||
printf("SH[%d] %20s addr %08x offset %08x size %08x type %08x\n",
|
||||
sh_idx, section_name(sh.name).c_str(), sh.addr, sh.offset, sh.size, sh.type);
|
||||
sh_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
void elf_file::move_all(int dist) {
|
||||
if (verbose) printf("Incrementing all paddr by %d\n", dist);
|
||||
for (auto &ph: ph_entries) {
|
||||
ph.paddr += dist;
|
||||
}
|
||||
}
|
||||
|
||||
void elf_file::read_ph(void) {
|
||||
if (verbose) printf("%s ph offset %u #entries %d\n", __func__, eh.ph_offset, eh.ph_num);
|
||||
if (eh.ph_num) {
|
||||
ph_entries.resize(eh.ph_num);
|
||||
read_bytes(eh.ph_offset, sizeof(elf32_ph_entry) * eh.ph_num, &ph_entries[0]);
|
||||
for (auto ph : ph_entries) {
|
||||
ph_he(ph); // swap to Host for processing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int elf_file::read_file(std::shared_ptr<std::iostream> file) {
|
||||
int rc = 0;
|
||||
try {
|
||||
elf_bytes = read_binfile(file);
|
||||
int rc = read_header();
|
||||
if (!rc) {
|
||||
read_ph();
|
||||
read_sh();
|
||||
}
|
||||
read_sh_data();
|
||||
read_ph_data();
|
||||
}
|
||||
catch (const std::ios_base::failure &e) {
|
||||
std::cerr << "Failed to read elf file" << std::endl;
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint32_t elf_file::lowest_section_offset(void) const {
|
||||
uint32_t offset = eh.sh_offset; // Section header offset is after the data
|
||||
for (const auto &sh: sh_entries) {
|
||||
if (sh.type != SHT_NULL && sh.offset > 0 && sh.offset < offset) {
|
||||
offset = sh.offset;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
uint32_t elf_file::highest_section_offset(void) const {
|
||||
uint32_t offset = 0; // Section header offset is after the data
|
||||
for (const auto &sh: sh_entries) {
|
||||
if (sh.type != SHT_NULL && sh.offset > 0 && sh.offset >= offset) {
|
||||
offset = sh.offset + sh.size;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> elf_file::content(const elf32_ph_entry &ph) const {
|
||||
std::vector<uint8_t> content;
|
||||
std::copy(elf_bytes.begin() + ph.offset, elf_bytes.begin() + ph.offset + ph.filez, std::back_inserter(content));
|
||||
return content;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> elf_file::content(const elf32_sh_entry &sh) const {
|
||||
std::vector<uint8_t> content;
|
||||
std::copy(elf_bytes.begin() + sh.offset, elf_bytes.begin() + sh.offset + sh.size, std::back_inserter(content));
|
||||
return content;
|
||||
}
|
||||
|
||||
void elf_file::content(const elf32_ph_entry &ph, const std::vector<uint8_t> &content) {
|
||||
if (!editable) return;
|
||||
assert(content.size() <= ph.filez);
|
||||
if (verbose) printf("Update segment content offset %x content size %zx physical size %x\n", ph.offset, content.size(), ph.filez);
|
||||
memcpy(&elf_bytes[ph.offset], &content[0], std::min(content.size(), (size_t) ph.filez));
|
||||
read_sh_data(); // Extract the sections after modifying the content
|
||||
read_ph_data();
|
||||
}
|
||||
|
||||
void elf_file::content(const elf32_sh_entry &sh, const std::vector<uint8_t> &content) {
|
||||
if (!editable) return;
|
||||
assert(content.size() <= sh.size);
|
||||
if (verbose) printf("Update section content offset %x content size %zx section size %x\n", sh.offset, content.size(), sh.size);
|
||||
memcpy(&elf_bytes[sh.offset], &content[0], std::min(content.size(), (size_t) sh.size));
|
||||
read_sh_data(); // Extract the sections after modifying the content
|
||||
read_ph_data();
|
||||
}
|
||||
|
||||
const elf32_ph_entry* elf_file::segment_from_physical_address(uint32_t paddr) {
|
||||
for (int i = 0; i < eh.ph_num; i++) {
|
||||
if (paddr >= ph_entries[i].paddr && paddr < ph_entries[i].paddr + ph_entries[i].filez) {
|
||||
if (verbose) printf("segment %d contains physical address %x\n", i, paddr);
|
||||
return &ph_entries[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const elf32_ph_entry* elf_file::segment_from_virtual_address(uint32_t vaddr) {
|
||||
for (int i = 0; i < eh.ph_num; i++) {
|
||||
if (vaddr >= ph_entries[i].vaddr && vaddr < ph_entries[i].vaddr + ph_entries[i].memsz) {
|
||||
if (verbose) printf("segment %d contains virtual address %x\n", i, vaddr);
|
||||
return &ph_entries[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Appends a new segment and section - filled with zeros
|
||||
// Use content to replace the content
|
||||
const elf32_ph_entry& elf_file::append_segment(uint32_t vaddr, uint32_t paddr, uint32_t size, const std::string &name) {
|
||||
elf32_ph_entry ph;
|
||||
read_sh_data(); // Convert the section data back into discreet chunks
|
||||
uint32_t sh_name = append_section_name(name);
|
||||
|
||||
ph.type = PT_LOAD;
|
||||
ph.flags = PF_R; // Readable segment
|
||||
ph.paddr = paddr;
|
||||
ph.vaddr = vaddr;
|
||||
ph.filez = size;
|
||||
ph.memsz = size;
|
||||
ph.align = 2;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "new segment " << name <<
|
||||
" paddr " << std::hex << paddr <<
|
||||
" vaddr " << std::hex << vaddr <<
|
||||
" size " << std::hex << size << std::endl;
|
||||
}
|
||||
ph_entries.push_back(ph);
|
||||
eh.ph_num++;
|
||||
|
||||
// There's normally space between the end of the program header table and the start of data to
|
||||
// squeeze in another program header. If not, shuffle everything by 4K;
|
||||
uint32_t lso = lowest_section_offset();
|
||||
if (lso < eh.ph_offset + eh.ph_entry_size * eh.ph_num) {
|
||||
|
||||
// Move the segment offsets
|
||||
for (auto &ph: ph_entries) {
|
||||
ph.offset += 0x1000;
|
||||
}
|
||||
|
||||
// Move section header table and each section offset
|
||||
eh.sh_offset += 0x1000;
|
||||
for (auto &sh : sh_entries) {
|
||||
sh.offset += 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the new signature section
|
||||
elf32_sh_entry sh = {};
|
||||
sh.name = sh_name;
|
||||
sh.type = SHT_PROGBITS;
|
||||
sh.flags = SHF_ALLOC;
|
||||
sh.addr = ph.vaddr;
|
||||
sh.size = size;
|
||||
uint32_t hso = highest_section_offset();
|
||||
sh.offset = hso;
|
||||
|
||||
// Add the new segment for the signature and point to offset in file for data
|
||||
sh_entries.push_back(sh);
|
||||
sh_data.push_back(std::vector<uint8_t>(size));
|
||||
ph_entries.back().offset = sh.offset;
|
||||
ph_data.push_back(std::vector<uint8_t>(size));
|
||||
|
||||
eh.sh_offset = sh.offset + sh.size;
|
||||
eh.sh_num++;
|
||||
|
||||
if (verbose) printf("%s sig offset %08x num sections %u\n", __func__, sh.offset, eh.sh_num);
|
||||
flatten();
|
||||
return ph_entries.back();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> elf_file::read_binfile(std::shared_ptr<std::iostream> in) {
|
||||
std::vector<uint8_t> data;
|
||||
in->exceptions(std::iostream::failbit | std::iostream::badbit);
|
||||
in->seekg(0, in->end);
|
||||
data.resize(in->tellg());
|
||||
in->seekg(0, in->beg);
|
||||
in->read(reinterpret_cast<char *>(&data[0]), data.size());
|
||||
return data;
|
||||
}
|
74
elf/elf_file.h
Normal file
74
elf/elf_file.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef ELF_FILE_H
|
||||
#define ELF_FILE_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "elf.h"
|
||||
|
||||
#include "addresses.h"
|
||||
|
||||
class elf_file {
|
||||
public:
|
||||
elf_file(bool verbose = false) : verbose(verbose) {};
|
||||
int read_file(std::shared_ptr<std::iostream> file);
|
||||
void write(std::shared_ptr<std::iostream> file);
|
||||
|
||||
const elf32_ph_entry& append_segment(uint32_t vaddr, uint32_t paddr, uint32_t size, const std::string §ion_name);
|
||||
|
||||
const elf32_header &header(void) const {return eh;}
|
||||
const std::vector<elf32_ph_entry> &segments(void) const {return ph_entries;}
|
||||
const std::vector<elf32_sh_entry> §ions(void) const {return sh_entries;}
|
||||
|
||||
std::vector<uint8_t> content(const elf32_ph_entry &ph) const;
|
||||
std::vector<uint8_t> content(const elf32_sh_entry &sh) const;
|
||||
void content(const elf32_ph_entry &ph, const std::vector<uint8_t> &content);
|
||||
void content(const elf32_sh_entry &sh, const std::vector<uint8_t> &content);
|
||||
|
||||
uint32_t lowest_section_offset(void) const;
|
||||
uint32_t highest_section_offset(void) const;
|
||||
const elf32_sh_entry* get_section(const std::string &sh_name);
|
||||
uint32_t get_symbol(const std::string &sym_name);
|
||||
const std::string section_name(uint32_t sh_name) const;
|
||||
const elf32_ph_entry* segment_from_physical_address(uint32_t paddr);
|
||||
const elf32_ph_entry* segment_from_virtual_address(uint32_t vaddr);
|
||||
void dump(void) const;
|
||||
|
||||
void move_all(int dist);
|
||||
|
||||
static std::vector<uint8_t> read_binfile(std::shared_ptr<std::iostream> file);
|
||||
|
||||
bool editable = true;
|
||||
private:
|
||||
int read_header(void);
|
||||
void read_ph(void);
|
||||
void read_sh(void);
|
||||
void read_sh_data(void);
|
||||
void read_ph_data(void);
|
||||
void read_bytes(unsigned offset, unsigned length, void *dest);
|
||||
uint32_t append_section_name(const std::string &sh_name_str);
|
||||
void flatten(void);
|
||||
|
||||
private:
|
||||
elf32_header eh;
|
||||
std::vector<uint8_t> elf_bytes;
|
||||
std::vector<elf32_ph_entry> ph_entries;
|
||||
std::vector<elf32_sh_entry> sh_entries;
|
||||
std::vector<std::vector<uint8_t>> sh_data;
|
||||
std::vector<std::vector<uint8_t>> ph_data;
|
||||
bool verbose;
|
||||
};
|
||||
int rp_check_elf_header(const elf32_header &eh);
|
||||
int rp_determine_binary_type(const elf32_header &eh, const std::vector<elf32_ph_entry>& entries, address_ranges flash_range, address_ranges ram_range, bool *ram_style);
|
||||
#endif
|
167
elf/portable_endian.h
Normal file
167
elf/portable_endian.h
Normal file
|
@ -0,0 +1,167 @@
|
|||
// Sourced from https://gist.github.com/panzi/6856583
|
||||
|
||||
// "License": Public Domain
|
||||
// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
|
||||
// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to
|
||||
// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it
|
||||
// an example on how to get the endian conversion functions on different platforms.
|
||||
|
||||
#ifndef PORTABLE_ENDIAN_H__
|
||||
#define PORTABLE_ENDIAN_H__
|
||||
|
||||
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
|
||||
|
||||
# define __WINDOWS__
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
|
||||
# include <endian.h>
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
# include <libkern/OSByteOrder.h>
|
||||
|
||||
# define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
# define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
# define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
# define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
|
||||
# define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
# define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
# define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
# define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
|
||||
# define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
# define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
# define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
# define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
|
||||
# include <sys/endian.h>
|
||||
|
||||
#ifndef be16toh
|
||||
# define be16toh(x) betoh16(x)
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
# define le16toh(x) letoh16(x)
|
||||
#endif
|
||||
#ifndef be32toh
|
||||
# define be32toh(x) betoh32(x)
|
||||
#endif
|
||||
#ifndef le32toh
|
||||
# define le32toh(x) letoh32(x)
|
||||
#endif
|
||||
|
||||
#elif defined(__WINDOWS__)
|
||||
|
||||
# include <winsock2.h>
|
||||
# ifdef __GNUC__
|
||||
# include <sys/param.h>
|
||||
# endif
|
||||
|
||||
# if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
||||
# define htobe16(x) htons(x)
|
||||
# define htole16(x) (x)
|
||||
# define be16toh(x) ntohs(x)
|
||||
# define le16toh(x) (x)
|
||||
|
||||
# define htobe32(x) htonl(x)
|
||||
# define htole32(x) (x)
|
||||
# define be32toh(x) ntohl(x)
|
||||
# define le32toh(x) (x)
|
||||
|
||||
# define htobe64(x) htonll(x)
|
||||
# define htole64(x) (x)
|
||||
# define be64toh(x) ntohll(x)
|
||||
# define le64toh(x) (x)
|
||||
|
||||
# elif BYTE_ORDER == BIG_ENDIAN
|
||||
|
||||
/* that would be xbox 360 */
|
||||
# define htobe16(x) (x)
|
||||
# define htole16(x) __builtin_bswap16(x)
|
||||
# define be16toh(x) (x)
|
||||
# define le16toh(x) __builtin_bswap16(x)
|
||||
|
||||
# define htobe32(x) (x)
|
||||
# define htole32(x) __builtin_bswap32(x)
|
||||
# define be32toh(x) (x)
|
||||
# define le32toh(x) __builtin_bswap32(x)
|
||||
|
||||
# define htobe64(x) (x)
|
||||
# define htole64(x) __builtin_bswap64(x)
|
||||
# define be64toh(x) (x)
|
||||
# define le64toh(x) __builtin_bswap64(x)
|
||||
|
||||
# else
|
||||
|
||||
# error byte order not supported
|
||||
|
||||
# endif
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#elif defined(__QNXNTO__)
|
||||
|
||||
# include <gulliver.h>
|
||||
|
||||
# define __LITTLE_ENDIAN 1234
|
||||
# define __BIG_ENDIAN 4321
|
||||
# define __PDP_ENDIAN 3412
|
||||
|
||||
# if defined(__BIGENDIAN__)
|
||||
|
||||
# define __BYTE_ORDER __BIG_ENDIAN
|
||||
|
||||
# define htobe16(x) (x)
|
||||
# define htobe32(x) (x)
|
||||
# define htobe64(x) (x)
|
||||
|
||||
# define htole16(x) ENDIAN_SWAP16(x)
|
||||
# define htole32(x) ENDIAN_SWAP32(x)
|
||||
# define htole64(x) ENDIAN_SWAP64(x)
|
||||
|
||||
# elif defined(__LITTLEENDIAN__)
|
||||
|
||||
# define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
|
||||
# define htole16(x) (x)
|
||||
# define htole32(x) (x)
|
||||
# define htole64(x) (x)
|
||||
|
||||
# define htobe16(x) ENDIAN_SWAP16(x)
|
||||
# define htobe32(x) ENDIAN_SWAP32(x)
|
||||
# define htobe64(x) ENDIAN_SWAP64(x)
|
||||
|
||||
# else
|
||||
|
||||
# error byte order not supported
|
||||
|
||||
# endif
|
||||
|
||||
# define be16toh(x) ENDIAN_BE16(x)
|
||||
# define be32toh(x) ENDIAN_BE32(x)
|
||||
# define be64toh(x) ENDIAN_BE64(x)
|
||||
# define le16toh(x) ENDIAN_LE16(x)
|
||||
# define le32toh(x) ENDIAN_LE32(x)
|
||||
# define le64toh(x) ENDIAN_LE64(x)
|
||||
|
||||
#else
|
||||
|
||||
# error platform not supported
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
20
elf2uf2/BUILD.bazel
Normal file
20
elf2uf2/BUILD.bazel
Normal file
|
@ -0,0 +1,20 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "elf2uf2",
|
||||
srcs = ["elf2uf2.cpp"],
|
||||
hdrs = ["elf2uf2.h"],
|
||||
copts = select({
|
||||
"@platforms//os:windows": [],
|
||||
"//conditions:default": [
|
||||
"-Wno-unused-function",
|
||||
"-Wno-unused-variable",
|
||||
],
|
||||
}),
|
||||
includes = ["."],
|
||||
deps = [
|
||||
"//elf",
|
||||
"//errors",
|
||||
"@pico-sdk//src/common/boot_uf2_headers",
|
||||
],
|
||||
)
|
6
elf2uf2/CMakeLists.txt
Normal file
6
elf2uf2/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
add_library(elf2uf2 STATIC
|
||||
elf2uf2.cpp)
|
||||
|
||||
target_include_directories(elf2uf2 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(elf2uf2 PRIVATE boot_uf2_headers elf errors)
|
344
elf2uf2/elf2uf2.cpp
Normal file
344
elf2uf2/elf2uf2.cpp
Normal file
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "elf2uf2.h"
|
||||
#include "errors.h"
|
||||
|
||||
#define FLASH_SECTOR_ERASE_SIZE 4096u
|
||||
|
||||
static bool verbose;
|
||||
|
||||
static void fail_read_error() {
|
||||
fail(ERROR_READ_FAILED, "Failed to read input file");
|
||||
}
|
||||
|
||||
static void fail_write_error() {
|
||||
fail(ERROR_WRITE_FAILED, "Failed to write output file");
|
||||
}
|
||||
|
||||
struct page_fragment {
|
||||
page_fragment(uint32_t file_offset, uint32_t page_offset, uint32_t bytes) : file_offset(file_offset), page_offset(page_offset), bytes(bytes) {}
|
||||
uint32_t file_offset;
|
||||
uint32_t page_offset;
|
||||
uint32_t bytes;
|
||||
};
|
||||
|
||||
int check_address_range(const address_ranges& valid_ranges, uint32_t addr, uint32_t vaddr, uint32_t size, bool uninitialized, address_range &ar) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to >= addr + size) {
|
||||
if (range.type == address_range::type::NO_CONTENTS && !uninitialized) {
|
||||
fail(ERROR_INCOMPATIBLE, "ELF contains memory contents for uninitialized memory at %p", addr);
|
||||
}
|
||||
ar = range;
|
||||
if (verbose) {
|
||||
printf("%s segment %08x->%08x (%08x->%08x)\n", uninitialized ? "Uninitialized" : "Mapped", addr,
|
||||
addr + size, vaddr, vaddr+size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fail(ERROR_INCOMPATIBLE, "Memory segment %08x->%08x is outside of valid address range for device", addr, addr+size);
|
||||
return ERROR_INCOMPATIBLE;
|
||||
}
|
||||
|
||||
int check_elf32_ph_entries(const std::vector<elf32_ph_entry>& entries, const address_ranges& valid_ranges, std::map<uint32_t, std::vector<page_fragment>>& pages) {
|
||||
for(const auto & entry : entries) {
|
||||
if (entry.type == PT_LOAD && entry.memsz) {
|
||||
address_range ar;
|
||||
int rc;
|
||||
unsigned int mapped_size = std::min(entry.filez, entry.memsz);
|
||||
if (mapped_size) {
|
||||
rc = check_address_range(valid_ranges, entry.paddr, entry.vaddr, mapped_size, false, ar);
|
||||
if (rc) return rc;
|
||||
// we don't download uninitialized, generally it is BSS and should be zero-ed by crt0.S, or it may be COPY areas which are undefined
|
||||
if (ar.type != address_range::type::CONTENTS) {
|
||||
if (verbose) printf(" ignored\n");
|
||||
continue;
|
||||
}
|
||||
unsigned int addr = entry.paddr;
|
||||
unsigned int remaining = mapped_size;
|
||||
unsigned int file_offset = entry.offset;
|
||||
while (remaining) {
|
||||
unsigned int off = addr & (UF2_PAGE_SIZE - 1);
|
||||
unsigned int len = std::min(remaining, UF2_PAGE_SIZE - off);
|
||||
auto &fragments = pages[addr - off]; // list of fragments
|
||||
// note if filesz is zero, we want zero init which is handled because the
|
||||
// statement above creates an empty page fragment list
|
||||
// check overlap with any existing fragments
|
||||
for (const auto &fragment : fragments) {
|
||||
if ((off < fragment.page_offset + fragment.bytes) !=
|
||||
((off + len) <= fragment.page_offset)) {
|
||||
fail(ERROR_FORMAT, "In memory segments overlap");
|
||||
}
|
||||
}
|
||||
fragments.push_back(
|
||||
page_fragment{file_offset,off,len});
|
||||
addr += len;
|
||||
file_offset += len;
|
||||
remaining -= len;
|
||||
}
|
||||
}
|
||||
if (entry.memsz > entry.filez) {
|
||||
// we have some uninitialized data too
|
||||
rc = check_address_range(valid_ranges, entry.paddr + entry.filez, entry.vaddr + entry.filez, entry.memsz - entry.filez, true,
|
||||
ar);
|
||||
if (rc) return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int realize_page(std::shared_ptr<std::iostream> in, const std::vector<page_fragment> &fragments, uint8_t *buf, unsigned int buf_len) {
|
||||
assert(buf_len >= UF2_PAGE_SIZE);
|
||||
for(auto& frag : fragments) {
|
||||
assert(frag.page_offset < UF2_PAGE_SIZE && frag.page_offset + frag.bytes <= UF2_PAGE_SIZE);
|
||||
in->seekg(frag.file_offset, in->beg);
|
||||
if (in->fail()) {
|
||||
fail_read_error();
|
||||
}
|
||||
in->read((char*)buf + frag.page_offset, frag.bytes);
|
||||
if (in->fail()) {
|
||||
fail_read_error();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_address_mapped(const std::map<uint32_t, std::vector<page_fragment>>& pages, uint32_t addr) {
|
||||
uint32_t page = addr & ~(UF2_PAGE_SIZE - 1);
|
||||
if (!pages.count(page)) return false;
|
||||
// todo check actual address within page
|
||||
return true;
|
||||
}
|
||||
|
||||
uf2_block gen_abs_block(uint32_t abs_block_loc) {
|
||||
uf2_block block;
|
||||
block.magic_start0 = UF2_MAGIC_START0;
|
||||
block.magic_start1 = UF2_MAGIC_START1;
|
||||
block.flags = UF2_FLAG_FAMILY_ID_PRESENT | UF2_FLAG_EXTENSION_FLAGS_PRESENT;
|
||||
block.payload_size = UF2_PAGE_SIZE;
|
||||
block.num_blocks = 2;
|
||||
block.file_size = ABSOLUTE_FAMILY_ID;
|
||||
block.magic_end = UF2_MAGIC_END;
|
||||
block.target_addr = abs_block_loc;
|
||||
block.block_no = 0;
|
||||
memset(block.data, 0, sizeof(block.data));
|
||||
memset(block.data, 0xef, UF2_PAGE_SIZE);
|
||||
*(uint32_t*)&(block.data[UF2_PAGE_SIZE]) = UF2_EXTENSION_RP2_IGNORE_BLOCK;
|
||||
return block;
|
||||
}
|
||||
|
||||
bool check_abs_block(uf2_block block) {
|
||||
return std::all_of(block.data, block.data + UF2_PAGE_SIZE, [](uint8_t i) { return i == 0xef; }) &&
|
||||
block.magic_start0 == UF2_MAGIC_START0 &&
|
||||
block.magic_start1 == UF2_MAGIC_START1 &&
|
||||
(block.flags & ~UF2_FLAG_EXTENSION_FLAGS_PRESENT) == UF2_FLAG_FAMILY_ID_PRESENT &&
|
||||
block.payload_size == UF2_PAGE_SIZE &&
|
||||
block.num_blocks == 2 &&
|
||||
block.file_size == ABSOLUTE_FAMILY_ID &&
|
||||
block.magic_end == UF2_MAGIC_END &&
|
||||
block.block_no == 0 &&
|
||||
!(block.flags & UF2_FLAG_EXTENSION_FLAGS_PRESENT && *(uint32_t*)&(block.data[UF2_PAGE_SIZE]) != UF2_EXTENSION_RP2_IGNORE_BLOCK);
|
||||
}
|
||||
|
||||
int pages2uf2(std::map<uint32_t, std::vector<page_fragment>>& pages, std::shared_ptr<std::iostream> in, std::shared_ptr<std::iostream> out, uint32_t family_id, uint32_t abs_block_loc=0) {
|
||||
// RP2350-E10: add absolute block to start of flash UF2s, targeting end of flash by default
|
||||
if (family_id != ABSOLUTE_FAMILY_ID && family_id != RP2040_FAMILY_ID && abs_block_loc) {
|
||||
uint32_t base_addr = pages.begin()->first;
|
||||
address_ranges flash_range = rp2350_address_ranges_flash;
|
||||
if (is_address_initialized(flash_range, base_addr)) {
|
||||
uf2_block block = gen_abs_block(abs_block_loc);
|
||||
out->write((char*)&block, sizeof(uf2_block));
|
||||
if (out->fail()) {
|
||||
fail_write_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
uf2_block block;
|
||||
unsigned int page_num = 0;
|
||||
block.magic_start0 = UF2_MAGIC_START0;
|
||||
block.magic_start1 = UF2_MAGIC_START1;
|
||||
block.flags = UF2_FLAG_FAMILY_ID_PRESENT;
|
||||
block.payload_size = UF2_PAGE_SIZE;
|
||||
block.num_blocks = (uint32_t)pages.size();
|
||||
block.file_size = family_id;
|
||||
block.magic_end = UF2_MAGIC_END;
|
||||
for(auto& page_entry : pages) {
|
||||
block.target_addr = page_entry.first;
|
||||
block.block_no = page_num++;
|
||||
if (verbose) {
|
||||
printf("Page %d / %d %08x%s\n", block.block_no, block.num_blocks, block.target_addr,
|
||||
page_entry.second.empty() ? " (padding)": "");
|
||||
}
|
||||
memset(block.data, 0, sizeof(block.data));
|
||||
int rc = realize_page(in, page_entry.second, block.data, sizeof(block.data));
|
||||
if (rc) return rc;
|
||||
out->write((char*)&block, sizeof(uf2_block));
|
||||
if (out->fail()) {
|
||||
fail_write_error();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bin2uf2(std::shared_ptr<std::iostream> in, std::shared_ptr<std::iostream> out, uint32_t address, uint32_t family_id, uint32_t abs_block_loc) {
|
||||
std::map<uint32_t, std::vector<page_fragment>> pages;
|
||||
|
||||
in->seekg(0, in->end);
|
||||
if (in->fail()) {
|
||||
fail_read_error();
|
||||
}
|
||||
int size = in->tellg();
|
||||
if (size <= 0) {
|
||||
fail_read_error();
|
||||
}
|
||||
|
||||
unsigned int addr = address;
|
||||
unsigned int remaining = size;
|
||||
unsigned int file_offset = 0;
|
||||
while (remaining) {
|
||||
unsigned int off = addr & (UF2_PAGE_SIZE - 1);
|
||||
unsigned int len = std::min(remaining, UF2_PAGE_SIZE - off);
|
||||
auto &fragments = pages[addr - off]; // list of fragments
|
||||
// note if filesz is zero, we want zero init which is handled because the
|
||||
// statement above creates an empty page fragment list
|
||||
// check overlap with any existing fragments
|
||||
for (const auto &fragment : fragments) {
|
||||
if ((off < fragment.page_offset + fragment.bytes) !=
|
||||
((off + len) <= fragment.page_offset)) {
|
||||
fail(ERROR_FORMAT, "In memory segments overlap");
|
||||
}
|
||||
}
|
||||
fragments.push_back(
|
||||
page_fragment{file_offset,off,len});
|
||||
addr += len;
|
||||
file_offset += len;
|
||||
remaining -= len;
|
||||
}
|
||||
|
||||
return pages2uf2(pages, in, out, family_id, abs_block_loc);
|
||||
}
|
||||
|
||||
int elf2uf2(std::shared_ptr<std::iostream> in, std::shared_ptr<std::iostream> out, uint32_t family_id, uint32_t package_addr, uint32_t abs_block_loc) {
|
||||
elf_file elf;
|
||||
std::map<uint32_t, std::vector<page_fragment>> pages;
|
||||
|
||||
int rc = elf.read_file(in);
|
||||
bool ram_style = false;
|
||||
address_ranges valid_ranges = {};
|
||||
address_ranges flash_range; address_ranges ram_range;
|
||||
if (family_id == RP2040_FAMILY_ID) {
|
||||
flash_range = rp2040_address_ranges_flash;
|
||||
ram_range = rp2040_address_ranges_ram;
|
||||
} else {
|
||||
flash_range = rp2350_address_ranges_flash;
|
||||
ram_range = rp2350_address_ranges_ram;
|
||||
}
|
||||
if (!rc) {
|
||||
rc = rp_determine_binary_type(elf.header(), elf.segments(), flash_range, ram_range, &ram_style);
|
||||
if (!rc) {
|
||||
if (verbose) {
|
||||
if (ram_style) {
|
||||
printf("Detected RAM binary\n");
|
||||
} else {
|
||||
printf("Detected FLASH binary\n");
|
||||
}
|
||||
}
|
||||
valid_ranges = ram_style ? ram_range : flash_range;
|
||||
rc = check_elf32_ph_entries(elf.segments(), valid_ranges, pages);
|
||||
}
|
||||
}
|
||||
if (rc) return rc;
|
||||
if (pages.empty()) {
|
||||
fail(ERROR_INCOMPATIBLE, "The input file has no memory pages");
|
||||
}
|
||||
// No Thumb bit on RISC-V
|
||||
elf32_header eh = elf.header();
|
||||
uint32_t thumb_bit = eh.common.machine == EM_ARM ? 0x1u : 0x0u;
|
||||
if (ram_style) {
|
||||
uint32_t expected_ep_main_ram = UINT32_MAX;
|
||||
uint32_t expected_ep_xip_sram = UINT32_MAX;
|
||||
for(auto& page_entry : pages) {
|
||||
if ( ((page_entry.first >= SRAM_START) && (page_entry.first < ram_range[0].to)) && (page_entry.first < expected_ep_main_ram) ) {
|
||||
expected_ep_main_ram = page_entry.first | thumb_bit;
|
||||
} else if ( ((page_entry.first >= ram_range[1].from) && (page_entry.first < ram_range[1].to)) && (page_entry.first < expected_ep_xip_sram) ) {
|
||||
expected_ep_xip_sram = page_entry.first | thumb_bit;
|
||||
}
|
||||
}
|
||||
uint32_t expected_ep = (UINT32_MAX != expected_ep_main_ram) ? expected_ep_main_ram : expected_ep_xip_sram;
|
||||
if (eh.entry == expected_ep_xip_sram && family_id == RP2040_FAMILY_ID) {
|
||||
fail(ERROR_INCOMPATIBLE, "RP2040 B0/B1/B2 Boot ROM does not support direct entry into XIP_SRAM\n");
|
||||
} else if (eh.entry != expected_ep && family_id == RP2040_FAMILY_ID) {
|
||||
fail(ERROR_INCOMPATIBLE, "A RP2040 RAM binary should have an entry point at the beginning: %08x (not %08x)\n", expected_ep, eh.entry);
|
||||
}
|
||||
static_assert(0 == (SRAM_START & (UF2_PAGE_SIZE - 1)), "");
|
||||
// currently don't require this as entry point is now at the start, we don't know where reset vector is
|
||||
// todo can be re-enabled for RP2350
|
||||
#if 0
|
||||
uint8_t buf[UF2_PAGE_SIZE];
|
||||
rc = realize_page(in, pages[SRAM_START], buf, sizeof(buf));
|
||||
if (rc) return rc;
|
||||
uint32_t sp = ((uint32_t *)buf)[0];
|
||||
uint32_t ip = ((uint32_t *)buf)[1];
|
||||
if (!is_address_mapped(pages, ip)) {
|
||||
fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: reset vector %08x is not in mapped memory",
|
||||
SRAM_START, ip);
|
||||
}
|
||||
if (!is_address_valid(valid_ranges, sp - 4)) {
|
||||
fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: stack pointer %08x is not in RAM",
|
||||
SRAM_START, sp);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// Fill in empty dummy uf2 pages to align the binary to flash sectors (except for the last sector which we don't
|
||||
// need to pad, and choose not to to avoid making all SDK UF2s bigger)
|
||||
// That workaround is required because the bootrom uses the block number for erase sector calculations:
|
||||
// https://github.com/raspberrypi/pico-bootrom/blob/c09c7f08550e8a36fc38dc74f8873b9576de99eb/bootrom/virtual_disk.c#L205
|
||||
|
||||
std::set<uint32_t> touched_sectors;
|
||||
for (auto& page_entry : pages) {
|
||||
uint32_t sector = page_entry.first / FLASH_SECTOR_ERASE_SIZE;
|
||||
touched_sectors.insert(sector);
|
||||
}
|
||||
|
||||
uint32_t last_page = pages.rbegin()->first;
|
||||
for (uint32_t sector : touched_sectors) {
|
||||
for (uint32_t page = sector * FLASH_SECTOR_ERASE_SIZE; page < (sector + 1) * FLASH_SECTOR_ERASE_SIZE; page += UF2_PAGE_SIZE) {
|
||||
if (page < last_page) {
|
||||
// Create a dummy page, if it does not exist yet. note that all present pages are first
|
||||
// zeroed before they are filled with any contents, so a dummy page will be all zeros.
|
||||
auto &dummy = pages[page];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (package_addr) {
|
||||
// Package binary at address
|
||||
uint32_t base_addr = pages.begin()->first;
|
||||
int32_t package_delta = package_addr - base_addr;
|
||||
if (verbose) printf("Base %x\n", base_addr);
|
||||
|
||||
auto copy_pages = pages;
|
||||
pages.clear();
|
||||
for (auto page : copy_pages) {
|
||||
pages[page.first + package_delta] = page.second;
|
||||
}
|
||||
}
|
||||
|
||||
return pages2uf2(pages, in, out, family_id, abs_block_loc);
|
||||
}
|
28
elf2uf2/elf2uf2.h
Normal file
28
elf2uf2/elf2uf2.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _ELF2UF2_H
|
||||
#define _ELF2UF2_H
|
||||
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include "boot/uf2.h"
|
||||
|
||||
#include "elf_file.h"
|
||||
|
||||
// we require 256 (as this is the page size supported by the device)
|
||||
#define LOG2_PAGE_SIZE 8u
|
||||
#define UF2_PAGE_SIZE (1u << LOG2_PAGE_SIZE)
|
||||
|
||||
|
||||
bool check_abs_block(uf2_block block);
|
||||
int bin2uf2(std::shared_ptr<std::iostream> in, std::shared_ptr<std::iostream> out, uint32_t address, uint32_t family_id, uint32_t abs_block_loc=0);
|
||||
int elf2uf2(std::shared_ptr<std::iostream> in, std::shared_ptr<std::iostream> out, uint32_t family_id, uint32_t package_addr=0, uint32_t abs_block_loc=0);
|
||||
|
||||
|
||||
#endif
|
8
errors/BUILD.bazel
Normal file
8
errors/BUILD.bazel
Normal file
|
@ -0,0 +1,8 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "errors",
|
||||
srcs = ["errors.cpp"],
|
||||
hdrs = ["errors.h"],
|
||||
includes = ["."],
|
||||
)
|
3
errors/CMakeLists.txt
Normal file
3
errors/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
add_library(errors STATIC errors.cpp)
|
||||
|
||||
target_include_directories(errors PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
16
errors/errors.cpp
Normal file
16
errors/errors.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <cstdarg>
|
||||
|
||||
#include "errors.h"
|
||||
|
||||
void fail(int code, std::string msg) {
|
||||
throw command_failure(code, std::move(msg));
|
||||
}
|
||||
|
||||
void fail(int code, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
static char error_msg[512];
|
||||
vsnprintf(error_msg, sizeof(error_msg), format, args);
|
||||
va_end(args);
|
||||
fail(code, std::string(error_msg));
|
||||
}
|
41
errors/errors.h
Normal file
41
errors/errors.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef _ERRORS_H
|
||||
#define _ERRORS_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef ERROR_CANCELLED
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#define ERROR_ARGS (-1)
|
||||
#define ERROR_FORMAT (-2)
|
||||
#define ERROR_INCOMPATIBLE (-3)
|
||||
#define ERROR_READ_FAILED (-4)
|
||||
#define ERROR_WRITE_FAILED (-5)
|
||||
#define ERROR_USB (-6)
|
||||
#define ERROR_NO_DEVICE (-7)
|
||||
#define ERROR_NOT_POSSIBLE (-8)
|
||||
#define ERROR_CONNECTION (-9)
|
||||
#define ERROR_CANCELLED (-10)
|
||||
#define ERROR_VERIFICATION_FAILED (-11)
|
||||
#define ERROR_UNKNOWN (-99)
|
||||
|
||||
|
||||
struct command_failure : std::exception {
|
||||
command_failure(int code, std::string s) : c(code), s(std::move(s)) {}
|
||||
|
||||
const char *what() const noexcept override {
|
||||
return s.c_str();
|
||||
}
|
||||
|
||||
int code() const { return c; }
|
||||
private:
|
||||
int c;
|
||||
std::string s;
|
||||
};
|
||||
|
||||
|
||||
void fail(int code, std::string msg);
|
||||
void fail(int code, const char *format, ...);
|
||||
|
||||
#endif
|
37
json/default-pt.json
Normal file
37
json/default-pt.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/raspberrypi/picotool/develop/json/schemas/partition-table-schema.json",
|
||||
"version": [1, 0],
|
||||
"unpartitioned": {
|
||||
"families": ["absolute"],
|
||||
"permissions": {
|
||||
"secure": "rw",
|
||||
"nonsecure": "rw",
|
||||
"bootloader": "rw"
|
||||
}
|
||||
},
|
||||
"partitions": [
|
||||
{
|
||||
"name": "A",
|
||||
"id": 0,
|
||||
"size": "2044K",
|
||||
"families": ["rp2350-arm-s", "rp2350-riscv"],
|
||||
"permissions": {
|
||||
"secure": "rw",
|
||||
"nonsecure": "rw",
|
||||
"bootloader": "rw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "B",
|
||||
"id": 1,
|
||||
"size": "2044K",
|
||||
"families": ["rp2350-arm-s", "rp2350-riscv"],
|
||||
"permissions": {
|
||||
"secure": "rw",
|
||||
"nonsecure": "rw",
|
||||
"bootloader": "rw"
|
||||
},
|
||||
"link": ["a", 0]
|
||||
}
|
||||
]
|
||||
}
|
27
json/sample-permissions.json
Normal file
27
json/sample-permissions.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/raspberrypi/picotool/develop/json/schemas/permissions-schema.json",
|
||||
"10": {
|
||||
"no_key_state": 0,
|
||||
"key_r": 0,
|
||||
"key_w": 1,
|
||||
"lock_bl": 3,
|
||||
"lock_ns": 3,
|
||||
"lock_s": 2
|
||||
},
|
||||
"11": {
|
||||
"no_key_state": 0,
|
||||
"key_r": 1,
|
||||
"key_w": 5,
|
||||
"lock_bl": 3,
|
||||
"lock_ns": 3,
|
||||
"lock_s": 3
|
||||
},
|
||||
"12": {
|
||||
"no_key_state": 0,
|
||||
"key_r": 0,
|
||||
"key_w": 0,
|
||||
"lock_bl": 3,
|
||||
"lock_ns": 3,
|
||||
"lock_s": 3
|
||||
}
|
||||
}
|
26
json/sample-wl.json
Normal file
26
json/sample-wl.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/raspberrypi/picotool/develop/json/schemas/whitelabel-schema.json",
|
||||
"device": {
|
||||
"vid": "0x2e8b",
|
||||
"pid": "0x000e",
|
||||
"bcd": 2.15,
|
||||
"lang_id": "0x0c09",
|
||||
"manufacturer": "zß水🍌 Test's Pis",
|
||||
"product": "Test RP2350?",
|
||||
"serial_number": "notnecessarilyanumber",
|
||||
"max_power": "0x20",
|
||||
"attributes": "0xe0"
|
||||
},
|
||||
"scsi": {
|
||||
"vendor": "TestPi",
|
||||
"product": "MyPi",
|
||||
"version": "v897"
|
||||
},
|
||||
"volume": {
|
||||
"label": "TestPi Boot",
|
||||
"redirect_url": "https://www.raspberrypi.com/news/",
|
||||
"redirect_name": "Some News About Stuff",
|
||||
"model": "My Test Pi",
|
||||
"board_id": "TPI-RP2350"
|
||||
}
|
||||
}
|
77
json/schemas/otp-contents-schema.json
Normal file
77
json/schemas/otp-contents-schema.json
Normal file
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "OTP Contents",
|
||||
"description": "Defined contents of the RP-series device OTP",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"description": "OTP Row",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"crit": {
|
||||
"description": "Critical Row (use three-of-eight vote encoding)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"description": "Row Description",
|
||||
"type": "string"
|
||||
},
|
||||
"ecc": {
|
||||
"description": "ECC Row",
|
||||
"type": "boolean"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Fields within row",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "Field Description",
|
||||
"type": "string"
|
||||
},
|
||||
"mask": {
|
||||
"description": "Field Bit Mask",
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"description": "Field Name",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["description", "mask", "name"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"mask": {
|
||||
"description": "Row Bit Mask",
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"description": "Row Name",
|
||||
"type": "string"
|
||||
},
|
||||
"redundancy": {
|
||||
"description": "Number of redundant rows",
|
||||
"type": "integer"
|
||||
},
|
||||
"row": {
|
||||
"description": "OTP Row",
|
||||
"type": "integer"
|
||||
},
|
||||
"seq_index": {
|
||||
"description": "Sequence Index",
|
||||
"type": "integer"
|
||||
},
|
||||
"seq_length": {
|
||||
"description": "Sequence Length",
|
||||
"type": "integer"
|
||||
},
|
||||
"seq_prefix": {
|
||||
"description": "Sequence Prefix",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["crit", "description"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
50
json/schemas/otp-schema.json
Normal file
50
json/schemas/otp-schema.json
Normal file
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "OTP Settings",
|
||||
"description": "OTP Settings",
|
||||
"type": "object",
|
||||
"properties": {"$schema": {}},
|
||||
"patternProperties": {
|
||||
"^\\d{1,2}:\\d{1,2}$": {
|
||||
"description": "Generic OTP Row",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ecc": {
|
||||
"description": "Protect with ECC",
|
||||
"type": "boolean"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value to write",
|
||||
"type": ["array", "string", "integer"],
|
||||
"pattern": "^0x[0-9a-fA-F]{1,6}$",
|
||||
"items": {
|
||||
"description": "Data Byte",
|
||||
"type": ["string", "integer"],
|
||||
"pattern": "^0x[0-9a-fA-F]{1,2}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["ecc", "value"]
|
||||
},
|
||||
"^[\\d\\w_]+$": {
|
||||
"description": "Defined OTP Row",
|
||||
"type": ["object", "array", "string", "integer"],
|
||||
"pattern": "^0x[0-9a-fA-F]{1,6}$",
|
||||
"items": {
|
||||
"description": "Data Byte",
|
||||
"type": ["string", "integer"],
|
||||
"pattern": "^0x[0-9a-fA-F]{1,2}$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[\\d\\w_]+$": {
|
||||
"description": "OTP Field",
|
||||
"type": ["string", "integer"],
|
||||
"pattern": "^0x[0-9a-fA-F]{1,6}$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
158
json/schemas/partition-table-schema.json
Normal file
158
json/schemas/partition-table-schema.json
Normal file
|
@ -0,0 +1,158 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Partition Table",
|
||||
"description": "Layout of the partition table",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {},
|
||||
"version": {
|
||||
"description": "Partition Table Version",
|
||||
"type": "array",
|
||||
"prefixItems": [
|
||||
{
|
||||
"description": "Major Version",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"description": "Minor Version",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"unpartitioned": {
|
||||
"description": "Unpartitioned space UF2 families and permissions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"families": {
|
||||
"description": "UF2 families accepted",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"data",
|
||||
"absolute",
|
||||
"rp2040",
|
||||
"rp2350-arm-s",
|
||||
"rp2350-arm-ns",
|
||||
"rp2350-riscv"
|
||||
]
|
||||
}
|
||||
},
|
||||
"permissions": {"$ref": "#/$defs/permissions"}
|
||||
},
|
||||
"required": ["permissions", "families"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"partitions": {
|
||||
"description": "Partitions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Partition Name",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "Partition ID",
|
||||
"type": ["integer", "string"],
|
||||
"minimum": 0,
|
||||
"exclusiveMaximum": 18446744073709551616,
|
||||
"pattern": "^0x[0-9a-fA-F]{1,16}$",
|
||||
"examples": [
|
||||
"0xDED3FFFF01234567",
|
||||
29,
|
||||
"0xdeadbeef"
|
||||
]
|
||||
},
|
||||
"start": {
|
||||
"description": "Partition Start",
|
||||
"type": ["integer", "string"],
|
||||
"minimum": 0,
|
||||
"pattern": "^\\d+(k|K)$"
|
||||
},
|
||||
"size": {
|
||||
"description": "Partition Size",
|
||||
"type": ["integer", "string"],
|
||||
"minimum": 0,
|
||||
"pattern": "^\\d+(k|K)$"
|
||||
},
|
||||
"families": {
|
||||
"description": "UF2 families accepted",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^data|absolute|rp2040|rp2350-arm-s|rp2350-arm-ns|rp2350-riscv|0x[0-9a-fA-F]{1,8}$",
|
||||
"examples": [
|
||||
"data",
|
||||
"absolute",
|
||||
"rp2040",
|
||||
"rp2350-arm-s",
|
||||
"rp2350-arm-ns",
|
||||
"rp2350-riscv"
|
||||
]
|
||||
}
|
||||
},
|
||||
"permissions": {"$ref": "#/$defs/permissions"},
|
||||
"link": {
|
||||
"type": "array",
|
||||
"prefixItems": [
|
||||
{
|
||||
"description": "Link Type",
|
||||
"enum": ["a", "owner" , "none"]
|
||||
},
|
||||
{
|
||||
"description": "Link Value",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"no_reboot_on_uf2_download": {
|
||||
"description": "Don't reboot after UF2 is downloaded",
|
||||
"type": "boolean"
|
||||
},
|
||||
"ab_non_bootable_owner_affinity": {
|
||||
"description": "Pick the non-bootable owner instead",
|
||||
"type": "boolean"
|
||||
},
|
||||
"ignored_during_riscv_boot": {
|
||||
"description": "Ignore this partition during Risc-V boot",
|
||||
"type": "boolean"
|
||||
},
|
||||
"ignored_during_arm_boot": {
|
||||
"description": "Ignore this partition during Arm boot",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["size", "permissions", "families"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["unpartitioned", "partitions"],
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"permissions": {
|
||||
"description": "Permissions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secure": {
|
||||
"description": "Secure Permissions",
|
||||
"type": "string",
|
||||
"pattern": "^(r|w){0,2}$"
|
||||
},
|
||||
"nonsecure": {
|
||||
"description": "Non-Secure Permissions",
|
||||
"type": "string",
|
||||
"pattern": "^(r|w){0,2}$"
|
||||
},
|
||||
"bootloader": {
|
||||
"description": "Bootloader Permissions",
|
||||
"type": "string",
|
||||
"pattern": "^(r|w){0,2}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
json/schemas/permissions-schema.json
Normal file
52
json/schemas/permissions-schema.json
Normal file
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "OTP Permissions",
|
||||
"description": "Setup of OTP page permissions",
|
||||
"type": "object",
|
||||
"properties": {"$schema": {}},
|
||||
"patternProperties": {
|
||||
"^[0-6][0-9]$": {
|
||||
"description": "OTP Page Permissions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"no_key_state": {
|
||||
"description": "State when at least one key is registered for this page and no matching key has been entered: 0 -> read_only, 1 -> inaccessible",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"key_r": {
|
||||
"description": "Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 6
|
||||
},
|
||||
"key_w": {
|
||||
"description": "Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 6
|
||||
},
|
||||
"lock_bl": {
|
||||
"description": "Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions: 0 -> read_write, 1 -> read_only, 2 -> Do not use (behaves the same as incaccessible), 3 -> inaccessible",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"lock_ns": {
|
||||
"description": "Lock state for Non-secure accesses to this page: 0 -> read_write, 1 -> read_only, 2 -> Do not use (behaves the same as incaccessible), 3 -> inaccessible",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"lock_s": {
|
||||
"description": "Lock state for Secure accesses to this page: 0 -> read_write, 1 -> read_only, 2 -> Do not use (behaves the same as incaccessible), 3 -> inaccessible",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
124
json/schemas/whitelabel-schema.json
Normal file
124
json/schemas/whitelabel-schema.json
Normal file
|
@ -0,0 +1,124 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "White Labelling",
|
||||
"description": "White Labelling Configuration, see section 5.7 in the RP2350 datasheet for more details",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {},
|
||||
"device": {
|
||||
"description": "Device Properties",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vid": {
|
||||
"description": "Vendor ID",
|
||||
"type": "string",
|
||||
"pattern": "^0x[0-9a-fA-F]{4}$"
|
||||
},
|
||||
"pid": {
|
||||
"description": "Product ID",
|
||||
"type": "string",
|
||||
"pattern": "^0x[0-9a-fA-F]{4}$"
|
||||
},
|
||||
"bcd": {
|
||||
"description": "Device Revision",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99
|
||||
},
|
||||
"lang_id": {
|
||||
"description": "Language ID",
|
||||
"type": "string",
|
||||
"pattern": "^0x[0-9a-fA-F]{4}$"
|
||||
},
|
||||
"manufacturer": {
|
||||
"description": "Manufacturer Name (can contain unicode)",
|
||||
"type": "string",
|
||||
"maxLength": 30
|
||||
},
|
||||
"product": {
|
||||
"description": "Product Name (can contain unicode)",
|
||||
"type": "string",
|
||||
"maxLength": 30
|
||||
},
|
||||
"serial_number": {
|
||||
"description": "Serial Number (can contain unicode)",
|
||||
"type": "string",
|
||||
"maxLength": 30
|
||||
},
|
||||
"max_power": {
|
||||
"description": "Max power consumption, in 2mA units",
|
||||
"type": ["integer", "string"],
|
||||
"maximum": 255,
|
||||
"pattern": "^0x[0-9a-fA-F]{1,2}$"
|
||||
},
|
||||
"attributes": {
|
||||
"description": "Device attributes: bit 7 must be 1, bit 6 is self-powered, bit 5 is remote wakeup, bits 0-4 must be 0",
|
||||
"type": ["integer", "string"],
|
||||
"minimum": 128,
|
||||
"maximum": 224,
|
||||
"pattern": "^0x[8aceACE]{1}0$"
|
||||
}
|
||||
},
|
||||
"dependentRequired": {
|
||||
"max_power": ["attributes"],
|
||||
"attributes": ["max_power"]
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"scsi": {
|
||||
"description": "SCSI Inquiry Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vendor": {
|
||||
"description": "SCSI Vendor",
|
||||
"type": "string",
|
||||
"maxLength": 8
|
||||
},
|
||||
"product": {
|
||||
"description": "SCSI Product",
|
||||
"type": "string",
|
||||
"maxLength": 16
|
||||
},
|
||||
"version": {
|
||||
"description": "SCSI Version",
|
||||
"type": "string",
|
||||
"maxLength": 4
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"volume": {
|
||||
"description": "MSD Volume Configuration",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"description": "Volume Label",
|
||||
"type": "string",
|
||||
"maxLength": 11
|
||||
},
|
||||
"redirect_url": {
|
||||
"description": "INDEX.HTM Redirect URL",
|
||||
"type": "string",
|
||||
"maxLength": 127
|
||||
},
|
||||
"redirect_name": {
|
||||
"description": "INDEX.HTM Redirect Name",
|
||||
"type": "string",
|
||||
"maxLength": 127
|
||||
},
|
||||
"model": {
|
||||
"description": "INFO_UF2.TXT Model Name",
|
||||
"type": "string",
|
||||
"maxLength": 127
|
||||
},
|
||||
"board_id": {
|
||||
"description": "INFO_UF2.TXT Board ID",
|
||||
"type": "string",
|
||||
"maxLength": 127
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
6
lib/BUILD.bazel
Normal file
6
lib/BUILD.bazel
Normal file
|
@ -0,0 +1,6 @@
|
|||
cc_library(
|
||||
name = "mbedtls_config",
|
||||
hdrs = ["include/mbedtls_config.h"],
|
||||
includes = ["include"],
|
||||
visibility = ["@mbedtls//:__subpackages__"],
|
||||
)
|
27
lib/CMakeLists.txt
Normal file
27
lib/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Ensure submodules are initialised - no longer needed as there are no submodules
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
# Update submodules as needed
|
||||
option(GIT_SUBMODULE "Check submodules during build" OFF)
|
||||
if(GIT_SUBMODULE)
|
||||
message(STATUS "Submodule update")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init lib/mbedtls
|
||||
WORKING_DIRECTORY ${PICO_SDK_PATH}
|
||||
RESULT_VARIABLE GIT_SUBMOD_RESULT)
|
||||
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
|
||||
message("git submodule update --init lib/mbedtls failed with ${GIT_SUBMOD_RESULT}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
||||
add_subdirectory(nlohmann_json EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(whereami EXCLUDE_FROM_ALL)
|
||||
|
||||
if(EXISTS "${PICO_SDK_PATH}/lib/mbedtls/CMakeLists.txt")
|
||||
option(ENABLE_PROGRAMS "Build Mbed TLS programs." OFF)
|
||||
option(ENABLE_TESTING "Build Mbed TLS tests." OFF)
|
||||
add_subdirectory(${PICO_SDK_PATH}/lib/mbedtls mbedtls EXCLUDE_FROM_ALL)
|
||||
endif()
|
4217
lib/include/mbedtls_config.h
Normal file
4217
lib/include/mbedtls_config.h
Normal file
File diff suppressed because it is too large
Load diff
7
lib/nlohmann_json/BUILD.bazel
Normal file
7
lib/nlohmann_json/BUILD.bazel
Normal file
|
@ -0,0 +1,7 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "json",
|
||||
hdrs = ["single_include/nlohmann/json.hpp"],
|
||||
includes = ["single_include"],
|
||||
)
|
126
lib/nlohmann_json/CMakeLists.txt
Normal file
126
lib/nlohmann_json/CMakeLists.txt
Normal file
|
@ -0,0 +1,126 @@
|
|||
cmake_minimum_required(VERSION 3.1...3.14)
|
||||
|
||||
##
|
||||
## PROJECT
|
||||
## name and version
|
||||
##
|
||||
project(nlohmann_json VERSION 3.11.3 LANGUAGES CXX)
|
||||
|
||||
##
|
||||
## MAIN_PROJECT CHECK
|
||||
## determine if nlohmann_json is built as a subproject (using add_subdirectory) or if it is the main project
|
||||
##
|
||||
set(MAIN_PROJECT OFF)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(MAIN_PROJECT ON)
|
||||
endif()
|
||||
|
||||
##
|
||||
## INCLUDE
|
||||
##
|
||||
##
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
|
||||
include(ExternalProject)
|
||||
|
||||
##
|
||||
## OPTIONS
|
||||
##
|
||||
|
||||
if (POLICY CMP0077)
|
||||
# Allow CMake 3.13+ to override options when using FetchContent / add_subdirectory.
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
endif ()
|
||||
|
||||
# VERSION_GREATER_EQUAL is not available in CMake 3.1
|
||||
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13))
|
||||
set(JSON_BuildTests_INIT ON)
|
||||
else()
|
||||
set(JSON_BuildTests_INIT OFF)
|
||||
endif()
|
||||
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT})
|
||||
option(JSON_CI "Enable CI build targets." OFF)
|
||||
option(JSON_Diagnostics "Use extended diagnostic messages." OFF)
|
||||
option(JSON_GlobalUDLs "Place use-defined string literals in the global namespace." ON)
|
||||
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
|
||||
option(JSON_DisableEnumSerialization "Disable default integer enum serialization." OFF)
|
||||
option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
|
||||
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
|
||||
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
||||
|
||||
if (JSON_CI)
|
||||
include(ci)
|
||||
endif ()
|
||||
|
||||
##
|
||||
## CONFIGURATION
|
||||
##
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME})
|
||||
set(NLOHMANN_JSON_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}" CACHE INTERNAL "")
|
||||
set(NLOHMANN_JSON_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(NLOHMANN_JSON_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
|
||||
set(NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE "cmake/config.cmake.in")
|
||||
set(NLOHMANN_JSON_CMAKE_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Config.cmake")
|
||||
set(NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Targets.cmake")
|
||||
set(NLOHMANN_JSON_PKGCONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/pkgconfig")
|
||||
|
||||
if (JSON_MultipleHeaders)
|
||||
set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/include/")
|
||||
message(STATUS "Using the multi-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}")
|
||||
else()
|
||||
set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/single_include/")
|
||||
message(STATUS "Using the single-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}")
|
||||
endif()
|
||||
|
||||
if (NOT JSON_ImplicitConversions)
|
||||
message(STATUS "Implicit conversions are disabled")
|
||||
endif()
|
||||
|
||||
if (JSON_DisableEnumSerialization)
|
||||
message(STATUS "Enum integer serialization is disabled")
|
||||
endif()
|
||||
|
||||
if (JSON_LegacyDiscardedValueComparison)
|
||||
message(STATUS "Legacy discarded value comparison enabled")
|
||||
endif()
|
||||
|
||||
if (JSON_Diagnostics)
|
||||
message(STATUS "Diagnostics enabled")
|
||||
endif()
|
||||
|
||||
if (JSON_SystemInclude)
|
||||
set(NLOHMANN_JSON_SYSTEM_INCLUDE "SYSTEM")
|
||||
endif()
|
||||
|
||||
##
|
||||
## TARGET
|
||||
## create target and add include path
|
||||
##
|
||||
add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)
|
||||
add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.8.0")
|
||||
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_range_for)
|
||||
else()
|
||||
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(
|
||||
${NLOHMANN_JSON_TARGET_NAME}
|
||||
INTERFACE
|
||||
$<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>
|
||||
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
|
||||
$<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>
|
||||
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
|
||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
${NLOHMANN_JSON_TARGET_NAME}
|
||||
${NLOHMANN_JSON_SYSTEM_INCLUDE} INTERFACE
|
||||
$<BUILD_INTERFACE:${NLOHMANN_JSON_INCLUDE_BUILD_DIR}>
|
||||
$<INSTALL_INTERFACE:${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}>
|
||||
)
|
21
lib/nlohmann_json/LICENSE.MIT
Normal file
21
lib/nlohmann_json/LICENSE.MIT
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2013-2022 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
24766
lib/nlohmann_json/single_include/nlohmann/json.hpp
Normal file
24766
lib/nlohmann_json/single_include/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load diff
14
lib/whereami/BUILD.bazel
Normal file
14
lib/whereami/BUILD.bazel
Normal file
|
@ -0,0 +1,14 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "whereami",
|
||||
srcs = [
|
||||
"whereami++.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"whereami.c",
|
||||
"whereami.h",
|
||||
"whereami++.h",
|
||||
],
|
||||
includes = ["."],
|
||||
)
|
7
lib/whereami/CMakeLists.txt
Normal file
7
lib/whereami/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
add_library(whereami INTERFACE)
|
||||
|
||||
target_sources(whereami INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/whereami.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/whereami++.cpp)
|
||||
|
||||
target_include_directories(whereami INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
101
lib/whereami/whereami++.cpp
Normal file
101
lib/whereami/whereami++.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
// https://github.com/gpakosz/whereami
|
||||
|
||||
// in case you want to #include "whereami++.cpp" in a larger compilation unit
|
||||
#if !defined(WHEREAMIPP_H)
|
||||
#include <whereami++.h>
|
||||
#endif
|
||||
|
||||
#define WHEREAMI_H
|
||||
#define WAI_FUNCSPEC
|
||||
#define WAI_PREFIX(function) function
|
||||
#include "whereami.c"
|
||||
|
||||
namespace whereami {
|
||||
|
||||
whereami_string_t whereami_path_t::dirname() const
|
||||
{
|
||||
return _path.substr(0, _dirname_length);
|
||||
}
|
||||
|
||||
whereami_string_t whereami_path_t::basename() const
|
||||
{
|
||||
return _path.substr(_dirname_length + 1);
|
||||
}
|
||||
|
||||
#if defined(WHEREAMI_CXX11)
|
||||
whereami_path_t::operator whereami_string_t() &&
|
||||
{
|
||||
return std::move(_path);
|
||||
}
|
||||
|
||||
whereami_path_t::operator whereami_string_t() const &
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
#else
|
||||
whereami_path_t::operator const whereami_string_t&() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WHEREAMI_CXX11)
|
||||
whereami_path_t::whereami_path_t(whereami_string_t&& path, int dirname_length) noexcept
|
||||
: _path(std::move(path)), _dirname_length(dirname_length)
|
||||
{
|
||||
}
|
||||
#else
|
||||
whereami_path_t::whereami_path_t(whereami_string_t& path, int dirname_length)
|
||||
: _path(path), _dirname_length(dirname_length)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(WHEREAMI_DISABLE_OSTREAM)
|
||||
std::ostream& operator<<(std::ostream& os, const whereami_path_t& path)
|
||||
{
|
||||
return os << path._path;
|
||||
}
|
||||
|
||||
#endif
|
||||
WAI_FUNCSPEC
|
||||
whereami_path_t getExecutablePath()
|
||||
{
|
||||
whereami_string_t path;
|
||||
int dirname_length = -1;
|
||||
|
||||
int length = ::WAI_PREFIX(getExecutablePath)(0, 0, 0);
|
||||
if (length != -1)
|
||||
{
|
||||
path.resize(length);
|
||||
::WAI_PREFIX(getExecutablePath)(&path[0], length, &dirname_length);
|
||||
}
|
||||
|
||||
#if defined(WHEREAMI_CXX11)
|
||||
return whereami_path_t(std::move(path), dirname_length);
|
||||
#else
|
||||
return whereami_path_t(path, dirname_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
WAI_FUNCSPEC
|
||||
whereami_path_t getModulePath()
|
||||
{
|
||||
whereami_string_t path;
|
||||
int dirname_length = -1;
|
||||
|
||||
int length = ::WAI_PREFIX(getModulePath)(0, 0, 0);
|
||||
if (length != -1)
|
||||
{
|
||||
path.resize(length);
|
||||
::WAI_PREFIX(getModulePath)(&path[0], length, &dirname_length);
|
||||
}
|
||||
|
||||
#if defined(WHEREAMI_CXX11)
|
||||
return whereami_path_t(std::move(path), dirname_length);
|
||||
#else
|
||||
return whereami_path_t(path, dirname_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace whereami
|
66
lib/whereami/whereami++.h
Normal file
66
lib/whereami/whereami++.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// https://github.com/gpakosz/whereami
|
||||
|
||||
#ifndef WHEREAMIPP_H
|
||||
#define WHEREAMIPP_H
|
||||
|
||||
#if !defined(WHEREAMI_STRING_T)
|
||||
#include <string>
|
||||
typedef std::string whereami_string_t;
|
||||
#else
|
||||
typedef WHEREAMI_STRING_T whereami_string_t;
|
||||
#endif
|
||||
#if !defined(WHEREAMI_DISABLE_OSTREAM)
|
||||
#include <ostream>
|
||||
#endif
|
||||
|
||||
#if (defined (__cplusplus) && (__cplusplus > 199711L)) || (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706))
|
||||
#define WHEREAMI_CXX11
|
||||
#endif
|
||||
|
||||
namespace whereami {
|
||||
|
||||
class whereami_path_t
|
||||
{
|
||||
public:
|
||||
#if defined(WHEREAMI_CXX11)
|
||||
operator whereami_string_t() &&;
|
||||
operator whereami_string_t() const &;
|
||||
#else
|
||||
operator const whereami_string_t&() const;
|
||||
#endif
|
||||
whereami_string_t dirname() const;
|
||||
whereami_string_t basename() const;
|
||||
|
||||
private:
|
||||
whereami_path_t();
|
||||
|
||||
#if defined(WHEREAMI_CXX11)
|
||||
whereami_path_t(whereami_string_t&& path, int dirname_length) noexcept;
|
||||
#else
|
||||
whereami_path_t(whereami_string_t& path, int dirname_length);
|
||||
#endif
|
||||
|
||||
friend whereami_path_t getExecutablePath();
|
||||
friend whereami_path_t getModulePath();
|
||||
#if !defined(WHEREAMI_DISABLE_OSTREAM)
|
||||
friend std::ostream& operator<<(std::ostream& os, const whereami_path_t& path);
|
||||
#endif
|
||||
|
||||
whereami_string_t _path;
|
||||
int _dirname_length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the path to the current executable.
|
||||
*/
|
||||
whereami_path_t getExecutablePath();
|
||||
|
||||
/**
|
||||
* Returns the path to the current module.
|
||||
*/
|
||||
whereami_path_t getModulePath();
|
||||
|
||||
} // namespace whereami
|
||||
|
||||
#endif // #ifndef WHEREAMIPP_H
|
||||
|
801
lib/whereami/whereami.c
Normal file
801
lib/whereami/whereami.c
Normal file
|
@ -0,0 +1,801 @@
|
|||
// https://github.com/gpakosz/whereami
|
||||
|
||||
// in case you want to #include "whereami.c" in a larger compilation unit
|
||||
#if !defined(WHEREAMI_H)
|
||||
#include <whereami.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
#undef _DEFAULT_SOURCE
|
||||
#define _DEFAULT_SOURCE
|
||||
#elif defined(__APPLE__)
|
||||
#undef _DARWIN_C_SOURCE
|
||||
#define _DARWIN_C_SOURCE
|
||||
#define _DARWIN_BETTER_REALPATH
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_MALLOC)
|
||||
#define WAI_MALLOC(size) malloc(size)
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_FREE)
|
||||
#define WAI_FREE(p) free(p)
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_REALLOC)
|
||||
#define WAI_REALLOC(p, size) realloc(p, size)
|
||||
#endif
|
||||
|
||||
#ifndef WAI_NOINLINE
|
||||
#if defined(_MSC_VER)
|
||||
#define WAI_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__)
|
||||
#define WAI_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#error unsupported compiler
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define WAI_RETURN_ADDRESS() _ReturnAddress()
|
||||
#elif defined(__GNUC__)
|
||||
#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||
#else
|
||||
#error unsupported compiler
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push, 3)
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
wchar_t buffer1[MAX_PATH];
|
||||
wchar_t buffer2[MAX_PATH];
|
||||
wchar_t* path = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
DWORD size;
|
||||
int length_, length__;
|
||||
|
||||
size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
|
||||
|
||||
if (size == 0)
|
||||
break;
|
||||
else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
|
||||
{
|
||||
DWORD size_ = size;
|
||||
do
|
||||
{
|
||||
wchar_t* path_;
|
||||
|
||||
path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
|
||||
if (!path_)
|
||||
break;
|
||||
size_ *= 2;
|
||||
path = path_;
|
||||
size = GetModuleFileNameW(module, path, size_);
|
||||
}
|
||||
while (size == size_);
|
||||
|
||||
if (size == size_)
|
||||
break;
|
||||
}
|
||||
else
|
||||
path = buffer1;
|
||||
|
||||
if (!_wfullpath(buffer2, path, MAX_PATH))
|
||||
break;
|
||||
length_ = (int)wcslen(buffer2);
|
||||
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
|
||||
|
||||
if (length__ == 0)
|
||||
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
|
||||
if (length__ == 0)
|
||||
break;
|
||||
|
||||
if (length__ <= capacity && dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length__ - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '\\')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
length = length__;
|
||||
}
|
||||
|
||||
if (path != buffer1)
|
||||
WAI_FREE(path);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
|
||||
}
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
HMODULE module;
|
||||
int length = -1;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4054)
|
||||
#endif
|
||||
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
{
|
||||
length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/limits.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if !defined(WAI_PROC_SELF_EXE)
|
||||
#if defined(__sun)
|
||||
#define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
|
||||
#else
|
||||
#define WAI_PROC_SELF_EXE "/proc/self/exe"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
resolved = realpath(WAI_PROC_SELF_EXE, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
#if !defined(WAI_PROC_SELF_MAPS_RETRY)
|
||||
#define WAI_PROC_SELF_MAPS_RETRY 5
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_PROC_SELF_MAPS)
|
||||
#if defined(__sun)
|
||||
#define WAI_PROC_SELF_MAPS "/proc/self/map"
|
||||
#else
|
||||
#define WAI_PROC_SELF_MAPS "/proc/self/maps"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) || defined(ANDROID)
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
int length = -1;
|
||||
FILE* maps = NULL;
|
||||
|
||||
for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
|
||||
{
|
||||
maps = fopen(WAI_PROC_SELF_MAPS, "r");
|
||||
if (!maps)
|
||||
break;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
|
||||
uint64_t low, high;
|
||||
char perms[5];
|
||||
uint64_t offset;
|
||||
uint32_t major, minor;
|
||||
char path[PATH_MAX];
|
||||
uint32_t inode;
|
||||
|
||||
if (!fgets(buffer, sizeof(buffer), maps))
|
||||
break;
|
||||
|
||||
if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
|
||||
{
|
||||
uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
|
||||
if (low <= addr && addr <= high)
|
||||
{
|
||||
char* resolved;
|
||||
|
||||
resolved = realpath(path, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
#if defined(__ANDROID__) || defined(ANDROID)
|
||||
if (length > 4
|
||||
&&buffer[length - 1] == 'k'
|
||||
&&buffer[length - 2] == 'p'
|
||||
&&buffer[length - 3] == 'a'
|
||||
&&buffer[length - 4] == '.')
|
||||
{
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
length = -1; // retry
|
||||
break;
|
||||
}
|
||||
|
||||
char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (begin == MAP_FAILED)
|
||||
{
|
||||
close(fd);
|
||||
length = -1; // retry
|
||||
break;
|
||||
}
|
||||
|
||||
char* p = begin + offset - 30; // minimum size of local file header
|
||||
while (p >= begin) // scan backwards
|
||||
{
|
||||
if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found
|
||||
{
|
||||
uint16_t length_ = *((uint16_t*)(p + 26));
|
||||
|
||||
if (length + 2 + length_ < (int)sizeof(buffer))
|
||||
{
|
||||
memcpy(&buffer[length], "!/", 2);
|
||||
memcpy(&buffer[length + 2], p + 30, length_);
|
||||
length += 2 + length_;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
--p;
|
||||
}
|
||||
|
||||
munmap(begin, offset);
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(maps);
|
||||
maps = NULL;
|
||||
|
||||
if (length != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[PATH_MAX];
|
||||
char buffer2[PATH_MAX];
|
||||
char* path = buffer1;
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
uint32_t size = (uint32_t)sizeof(buffer1);
|
||||
if (_NSGetExecutablePath(path, &size) == -1)
|
||||
{
|
||||
path = (char*)WAI_MALLOC(size);
|
||||
if (!_NSGetExecutablePath(path, &size))
|
||||
break;
|
||||
}
|
||||
|
||||
resolved = realpath(path, buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path != buffer1)
|
||||
WAI_FREE(path);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||
{
|
||||
resolved = realpath(info.dli_fname, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__QNXNTO__)
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if !defined(WAI_PROC_SELF_EXE)
|
||||
#define WAI_PROC_SELF_EXE "/proc/self/exefile"
|
||||
#endif
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[PATH_MAX];
|
||||
char buffer2[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
FILE* self_exe = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
self_exe = fopen(WAI_PROC_SELF_EXE, "r");
|
||||
if (!self_exe)
|
||||
break;
|
||||
|
||||
if (!fgets(buffer1, sizeof(buffer1), self_exe))
|
||||
break;
|
||||
|
||||
resolved = realpath(buffer1, buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(self_exe);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||
{
|
||||
resolved = realpath(info.dli_fname, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[4096];
|
||||
char buffer2[PATH_MAX];
|
||||
char buffer3[PATH_MAX];
|
||||
char** argv = (char**)buffer1;
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
|
||||
size_t size;
|
||||
|
||||
if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0)
|
||||
break;
|
||||
|
||||
if (size > sizeof(buffer1))
|
||||
{
|
||||
argv = (char**)WAI_MALLOC(size);
|
||||
if (!argv)
|
||||
break;
|
||||
}
|
||||
|
||||
if (sysctl(mib, 4, argv, &size, NULL, 0) != 0)
|
||||
break;
|
||||
|
||||
if (strchr(argv[0], '/'))
|
||||
{
|
||||
resolved = realpath(argv[0], buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* PATH = getenv("PATH");
|
||||
if (!PATH)
|
||||
break;
|
||||
|
||||
size_t argv0_length = strlen(argv[0]);
|
||||
|
||||
const char* begin = PATH;
|
||||
while (1)
|
||||
{
|
||||
const char* separator = strchr(begin, ':');
|
||||
const char* end = separator ? separator : begin + strlen(begin);
|
||||
|
||||
if (end - begin > 0)
|
||||
{
|
||||
if (*(end -1) == '/')
|
||||
--end;
|
||||
|
||||
if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2))
|
||||
{
|
||||
memcpy(buffer2, begin, end - begin);
|
||||
buffer2[end - begin] = '/';
|
||||
memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1);
|
||||
|
||||
resolved = realpath(buffer2, buffer3);
|
||||
if (resolved)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!separator)
|
||||
break;
|
||||
|
||||
begin = ++separator;
|
||||
}
|
||||
|
||||
if (!resolved)
|
||||
break;
|
||||
}
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argv != (char**)buffer1)
|
||||
WAI_FREE(argv);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[PATH_MAX];
|
||||
char buffer2[PATH_MAX];
|
||||
char* path = buffer1;
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
#if defined(__NetBSD__)
|
||||
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
|
||||
#else
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||
#endif
|
||||
size_t size = sizeof(buffer1);
|
||||
|
||||
if (sysctl(mib, 4, path, &size, NULL, 0) != 0)
|
||||
break;
|
||||
|
||||
resolved = realpath(path, buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||
{
|
||||
resolved = realpath(info.dli_fname, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error unsupported platform
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
64
lib/whereami/whereami.h
Normal file
64
lib/whereami/whereami.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
// https://github.com/gpakosz/whereami
|
||||
|
||||
#ifndef WHEREAMI_H
|
||||
#define WHEREAMI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef WAI_FUNCSPEC
|
||||
#define WAI_FUNCSPEC
|
||||
#endif
|
||||
#ifndef WAI_PREFIX
|
||||
#define WAI_PREFIX(function) wai_##function
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the path to the current executable.
|
||||
*
|
||||
* Usage:
|
||||
* - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to
|
||||
* retrieve the length of the path
|
||||
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
|
||||
* - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the
|
||||
* path
|
||||
* - add a terminal NUL character with `path[length] = '\0';`
|
||||
*
|
||||
* @param out destination buffer, optional
|
||||
* @param capacity destination buffer capacity
|
||||
* @param dirname_length optional recipient for the length of the dirname part
|
||||
* of the path.
|
||||
*
|
||||
* @return the length of the executable path on success (without a terminal NUL
|
||||
* character), otherwise `-1`
|
||||
*/
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length);
|
||||
|
||||
/**
|
||||
* Returns the path to the current module.
|
||||
*
|
||||
* Usage:
|
||||
* - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve
|
||||
* the length of the path
|
||||
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
|
||||
* - call `wai_getModulePath(path, length, NULL)` again to retrieve the path
|
||||
* - add a terminal NUL character with `path[length] = '\0';`
|
||||
*
|
||||
* @param out destination buffer, optional
|
||||
* @param capacity destination buffer capacity
|
||||
* @param dirname_length optional recipient for the length of the dirname part
|
||||
* of the path.
|
||||
*
|
||||
* @return the length of the module path on success (without a terminal NUL
|
||||
* character), otherwise `-1`
|
||||
*/
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #ifndef WHEREAMI_H
|
23
no_otp.cpp
Normal file
23
no_otp.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include "otp.h"
|
||||
|
||||
template <typename T>
|
||||
std::basic_string<T> lowercase(const std::basic_string<T>& s)
|
||||
{
|
||||
std::basic_string<T> s2 = s;
|
||||
std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
|
||||
return s2;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::basic_string<T> uppercase(const std::basic_string<T>& s)
|
||||
{
|
||||
std::basic_string<T> s2 = s;
|
||||
std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
|
||||
return s2;
|
||||
}
|
||||
|
||||
void init_otp(std::map<uint32_t, otp_reg> &otp_regs, std::vector<std::string> extra_otp_files) {}
|
84
otp.cpp
Normal file
84
otp.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
|
||||
#include "otp.h"
|
||||
|
||||
#include "whereami++.h"
|
||||
|
||||
#if CODE_OTP
|
||||
#include "otp_contents.h"
|
||||
#else
|
||||
#include "data_locs.h"
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEBUG_LOG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_LOG(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#include "rp2350.json.h"
|
||||
|
||||
template <typename T>
|
||||
std::basic_string<T> lowercase(const std::basic_string<T>& s)
|
||||
{
|
||||
std::basic_string<T> s2 = s;
|
||||
std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
|
||||
return s2;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::basic_string<T> uppercase(const std::basic_string<T>& s)
|
||||
{
|
||||
std::basic_string<T> s2 = s;
|
||||
std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
|
||||
return s2;
|
||||
}
|
||||
|
||||
void init_otp(std::map<uint32_t, otp_reg> &otp_regs, std::vector<std::string> extra_otp_files) {
|
||||
#if CODE_OTP
|
||||
std::transform(otp_reg_list.begin(), otp_reg_list.end(), std::inserter(otp_regs, otp_regs.end()), [](const otp_reg& r) { return std::make_pair( r.row, r); });
|
||||
#elif 0
|
||||
// search same directory as executable
|
||||
whereami::whereami_path_t executablePath = whereami::getExecutablePath();
|
||||
std::string local_loc = executablePath.dirname() + "/";
|
||||
if (std::find(data_locs.begin(), data_locs.end(), local_loc) == data_locs.end()) {
|
||||
data_locs.insert(data_locs.begin(), local_loc);
|
||||
}
|
||||
std::string local_loc_debug = executablePath.dirname() + "/../";
|
||||
if (std::find(data_locs.begin(), data_locs.end(), local_loc_debug) == data_locs.end()) {
|
||||
data_locs.insert(data_locs.begin(), local_loc_debug);
|
||||
}
|
||||
// and search ../ in case exe in subdirectory Debug
|
||||
for (auto loc : data_locs) {
|
||||
std::string filename = loc + "rp2350_otp_contents.json";
|
||||
std::ifstream i(filename);
|
||||
if (i.good()) {
|
||||
printf("Picking OTP JSON file %s\n", filename.c_str());
|
||||
json j;
|
||||
i >> j;
|
||||
std::transform(j.begin(), j.end(), std::inserter(otp_regs, otp_regs.end()), [](const otp_reg& r) { return std::make_pair( r.row, r); });
|
||||
break;
|
||||
} else {
|
||||
DEBUG_LOG("Can't find JSON file %s\n", filename.c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
json j = json::parse(rp2350_json);
|
||||
std::transform(j.begin(), j.end(), std::inserter(otp_regs, otp_regs.end()), [](const otp_reg& r) { return std::make_pair( r.row, r); });
|
||||
#endif
|
||||
|
||||
for (auto filename : extra_otp_files) {
|
||||
std::ifstream i(filename);
|
||||
if (i.good()) {
|
||||
printf("Adding OTP JSON file %s\n", filename.c_str());
|
||||
json j;
|
||||
i >> j;
|
||||
std::transform(j.begin(), j.end(), std::inserter(otp_regs, otp_regs.end()), [](const otp_reg& r) { return std::make_pair( r.row, r); });
|
||||
} else {
|
||||
printf("Can't find JSON file %s\n", filename.c_str());
|
||||
}
|
||||
}
|
||||
}
|
97
otp.h
Normal file
97
otp.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _OTP_H
|
||||
#define _OTP_H
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
template <typename T> std::basic_string<T> lowercase(const std::basic_string<T>& s);
|
||||
template <typename T> std::basic_string<T> uppercase(const std::basic_string<T>& s);
|
||||
|
||||
struct otp_field {
|
||||
otp_field() = default;
|
||||
otp_field(std::string name, uint32_t mask) : name(std::move(name)), mask(mask) {
|
||||
upper_name = uppercase(this->name);
|
||||
}
|
||||
otp_field(std::string name, uint32_t mask, std::string description) : otp_field(std::move(name), mask) {
|
||||
this->description = std::move(description);
|
||||
}
|
||||
std::string name;
|
||||
std::string upper_name;
|
||||
uint32_t mask;
|
||||
std::string description;
|
||||
|
||||
friend void to_json(json& nlohmann_json_j, const otp_field& nlohmann_json_t) {
|
||||
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, name, mask, description))
|
||||
}
|
||||
|
||||
friend void from_json(const json& nlohmann_json_j, otp_field& nlohmann_json_t) {
|
||||
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, name, mask, description))
|
||||
nlohmann_json_t.upper_name = uppercase(nlohmann_json_t.name);
|
||||
}
|
||||
};
|
||||
|
||||
struct otp_reg {
|
||||
otp_reg() = default;
|
||||
explicit otp_reg(std::string name, uint32_t row, uint32_t mask) : name(std::move(name)), row(row), mask(mask) {
|
||||
upper_name = uppercase(this->name);
|
||||
}
|
||||
otp_reg& with_ecc() { assert(!redundancy); ecc = true; return *this; }
|
||||
otp_reg& with_crit() { assert(!ecc); crit = true; return *this; }
|
||||
otp_reg& with_redundancy(int r) { assert(!ecc); redundancy = r; return *this; }
|
||||
otp_reg& with_description(std::string d) { description = std::move(d); return *this; }
|
||||
otp_reg& with_field(const otp_field& field) { fields.push_back(field); return *this; }
|
||||
otp_reg& with_sequence(std::string prefix, int index, int length) { seq_prefix = std::move(prefix); seq_index = index; seq_length = length; return *this; }
|
||||
std::string name;
|
||||
std::string upper_name;
|
||||
std::string description;
|
||||
uint32_t row = 0xffffffff;
|
||||
uint32_t mask = 0;
|
||||
bool ecc = false;
|
||||
bool crit = false;
|
||||
unsigned int redundancy = 0;
|
||||
unsigned int seq_length = 0;
|
||||
unsigned int seq_index = 0;
|
||||
std::string seq_prefix;
|
||||
std::vector<otp_field> fields;
|
||||
|
||||
friend void to_json(json& nlohmann_json_j, const otp_reg& nlohmann_json_t) {
|
||||
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, name, description, ecc, crit, fields, mask, redundancy, row, seq_index, seq_length, seq_prefix))
|
||||
}
|
||||
|
||||
friend void from_json(const json& nlohmann_json_j, otp_reg& nlohmann_json_t) {
|
||||
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, name, description, row))
|
||||
if (nlohmann_json_j.contains("ecc")) nlohmann_json_j.at("ecc").get_to(nlohmann_json_t.ecc);
|
||||
if (nlohmann_json_j.contains("crit")) nlohmann_json_j.at("crit").get_to(nlohmann_json_t.crit);
|
||||
if (nlohmann_json_j.contains("redundancy")) nlohmann_json_j.at("redundancy").get_to(nlohmann_json_t.redundancy);
|
||||
|
||||
if (nlohmann_json_j.contains("fields")) nlohmann_json_j.at("fields").get_to(nlohmann_json_t.fields);
|
||||
if (nlohmann_json_j.contains("mask")){
|
||||
nlohmann_json_j.at("mask").get_to(nlohmann_json_t.mask);
|
||||
} else {
|
||||
nlohmann_json_t.mask = nlohmann_json_t.ecc ? 0xffff : 0xffffff;
|
||||
}
|
||||
|
||||
if (nlohmann_json_j.contains("seq_length") || nlohmann_json_j.contains("seq_index") || nlohmann_json_j.contains("seq_prefix")) {
|
||||
nlohmann_json_j.at("seq_length").get_to(nlohmann_json_t.seq_length);
|
||||
nlohmann_json_j.at("seq_index").get_to(nlohmann_json_t.seq_index);
|
||||
nlohmann_json_j.at("seq_prefix").get_to(nlohmann_json_t.seq_prefix);
|
||||
}
|
||||
nlohmann_json_t.upper_name = uppercase(nlohmann_json_t.name);
|
||||
}
|
||||
};
|
||||
|
||||
void init_otp(std::map<uint32_t, otp_reg> &otp_regs, std::vector<std::string> extra_otp_files = {});
|
||||
|
||||
#endif
|
19
otp_header_parser/BUILD.bazel
Normal file
19
otp_header_parser/BUILD.bazel
Normal file
|
@ -0,0 +1,19 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "pre_generated_otp_header",
|
||||
includes = ["."],
|
||||
hdrs = ["rp2350.json.h"],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "otp_header_parser",
|
||||
srcs = ["otp_header_parse.cpp"],
|
||||
copts = select({
|
||||
"@platforms//os:windows": [],
|
||||
"//conditions:default": [
|
||||
"-Wno-unused-variable",
|
||||
],
|
||||
}),
|
||||
deps = ["//lib/nlohmann_json:json"],
|
||||
)
|
18
otp_header_parser/CMakeLists.txt
Normal file
18
otp_header_parser/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
PROJECT(otp_header_parser CXX)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
add_executable(otp_header_parse otp_header_parse.cpp)
|
||||
|
||||
if (CODE_OTP)
|
||||
target_compile_definitions(otp_header_parse PRIVATE CODE_OTP=${CODE_OTP})
|
||||
endif()
|
||||
|
||||
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
||||
add_subdirectory(../lib/nlohmann_json ${CMAKE_CURRENT_BINARY_DIR}/../lib/nlohmann_json EXCLUDE_FROM_ALL)
|
||||
|
||||
if (NOT TARGET nlohmann_json)
|
||||
message(FATAL_ERROR "JSON library not found - check you have all the submodules")
|
||||
else()
|
||||
target_link_libraries(otp_header_parse nlohmann_json)
|
||||
endif()
|
389
otp_header_parser/otp_header_parse.cpp
Normal file
389
otp_header_parser/otp_header_parse.cpp
Normal file
|
@ -0,0 +1,389 @@
|
|||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
// missing __builtins on windows
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# include <intrin.h>
|
||||
# define __builtin_popcount __popcnt
|
||||
static __forceinline int __builtin_ctz(unsigned x) {
|
||||
unsigned long r;
|
||||
_BitScanForward(&r, x);
|
||||
return (int)r;
|
||||
}
|
||||
#endif
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// todo values?
|
||||
|
||||
static void usage() {
|
||||
std::cerr << "usage: otp_header_parser <otp_data.h filename> <output header filename>" << std::endl;
|
||||
}
|
||||
|
||||
enum {
|
||||
ERROR_ARGS = 1,
|
||||
ERROR_INPUT = 2,
|
||||
ERROR_UNKNOWN = 3,
|
||||
};
|
||||
|
||||
struct otp_field {
|
||||
otp_field() = default;
|
||||
std::string name;
|
||||
uint32_t mask = 0;
|
||||
std::string description;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(otp_field, name, mask, description)
|
||||
};
|
||||
|
||||
struct otp_reg {
|
||||
otp_reg() = default;
|
||||
uint32_t row = 0xffffffff;
|
||||
uint32_t mask = 0;
|
||||
int redundancy = 1;
|
||||
bool ecc = false;
|
||||
bool crit = false;
|
||||
std::string seq_prefix;
|
||||
int seq_length = 0;
|
||||
int seq_index = 0;
|
||||
std::string description;
|
||||
std::string name;
|
||||
std::vector<otp_field> fields;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(otp_reg, row, mask, redundancy, ecc, crit, seq_prefix, seq_length, seq_index, description, name, fields)
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &outs, const otp_reg &e) {
|
||||
auto &x = outs << "row=" << std::hex << e.row << ", mask=" << e.mask << ", ecc=" << e.ecc << ", rdncy=" << std::dec << e.redundancy;
|
||||
if (e.description.empty()) {
|
||||
return x;
|
||||
} else {
|
||||
return x << ", '" << e.description << "'";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &outs, const std::pair<std::string, otp_reg> &p) {
|
||||
const auto&e = p.second;
|
||||
return outs << p.first << "(" << e << ")";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &outs, const otp_field &f) {
|
||||
auto &x = outs << f.name << " mask=" << std::hex << f.mask;
|
||||
if (f.description.empty()) {
|
||||
return x;
|
||||
} else {
|
||||
return x << ", '" << f.description << "'";
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, otp_reg> otp_regs;
|
||||
|
||||
bool starts_with(std::string const &fullString, std::string const &prefix) {
|
||||
if (fullString.length() >= prefix.length()) {
|
||||
return (0 == fullString.compare(0, prefix.length(), prefix));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ends_with(std::string const &fullString, std::string const &ending) {
|
||||
if (fullString.length() >= ending.length()) {
|
||||
return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string trim(std::string str)
|
||||
{
|
||||
str.erase(str.find_last_not_of(' ')+1);
|
||||
str.erase(0, str.find_first_not_of(' '));
|
||||
return str;
|
||||
}
|
||||
|
||||
bool valid_description(std::string description) {
|
||||
if (description.empty()) return false;
|
||||
if (description == "None") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
return ERROR_ARGS;
|
||||
}
|
||||
try {
|
||||
std::ifstream infile(argv[1]);
|
||||
std::string line;
|
||||
std::regex define_u_regex(R"(#define[\s]+([^\s]*)[\s]+_u\(0x(.*)\))");
|
||||
std::regex reg_regex(R"(// Register[\s]+:[\s]+(.*)[\s]*)");
|
||||
std::regex field_regex(R"(// Field[\s]+:[\s]+(.*)[\s]*)");
|
||||
std::regex desc_regex("// Description[\\s]+:[\\s]+(.*)");
|
||||
std::regex ecc_regex("//.*\\(ECC\\).*");
|
||||
std::regex rbit_regex(R"(//.*\(RBIT-\([0-9]\)\).*)");
|
||||
std::regex rn_regex(".*_R[0-9]$");
|
||||
std::regex zeroth_regex("([a-zA-Z_0-9]*[a-zA-Z_])0$");
|
||||
enum {
|
||||
REGISTER,
|
||||
FIELD,
|
||||
NONE
|
||||
} type = NONE;
|
||||
bool ecc;
|
||||
std::string reg_name;
|
||||
std::string field_name;
|
||||
std::string comment;
|
||||
int expected_redundancy;
|
||||
otp_reg reg;
|
||||
otp_field field;
|
||||
while (std::getline(infile, line)) {
|
||||
// std::cout << "LINE " << line << std::endl;
|
||||
std::smatch smatch;
|
||||
if (starts_with(line, "// ======")) {
|
||||
// start of a new register (or end of file)
|
||||
if (reg.mask) {
|
||||
if (reg.redundancy != expected_redundancy) {
|
||||
cerr << reg_name << " redundancy count mismatch " << reg.redundancy << " != " << expected_redundancy << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
if (!field.name.empty()) {
|
||||
reg.fields.push_back(field);
|
||||
}
|
||||
otp_regs.emplace(reg_name, reg);
|
||||
reg = otp_reg();
|
||||
}
|
||||
expected_redundancy = 1;
|
||||
reg_name = "";
|
||||
field.name = "";
|
||||
type = NONE;
|
||||
} else if (starts_with(line, "// ------")) {
|
||||
if (type == FIELD && !field.name.empty()) {
|
||||
reg.fields.push_back(field);
|
||||
}
|
||||
// we are between regs/fields
|
||||
type = NONE;
|
||||
} else if (std::regex_match(line, smatch, reg_regex)) {
|
||||
type = REGISTER;
|
||||
field_name = "";
|
||||
reg_name = smatch[1].str();
|
||||
reg.name = reg_name;
|
||||
} else if (std::regex_match(line, smatch, field_regex)) {
|
||||
field_name = smatch[1];
|
||||
if (!starts_with(field_name, reg_name+"_")) {
|
||||
cerr << "ERROR: field name " << field_name << " is not prefixed with expected " << reg_name << "_" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
type = FIELD;
|
||||
field.name = field_name.substr(reg_name.length() + 1);
|
||||
field.mask = 0;
|
||||
field.description = "";
|
||||
} else if (std::regex_match( line, smatch, define_u_regex)) {
|
||||
const auto& define_name = smatch[1].str();
|
||||
const auto& define_hex = smatch[2].str();
|
||||
if (reg_name.empty()) {
|
||||
std::cerr << "Got define '" << define_name << "' outside of register" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
if (!starts_with(define_name, reg_name + "_")) {
|
||||
std::cerr << "Got define '" << define_name << "' which doesn't start with " << reg_name << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
uint32_t define_value = std::stoul(define_hex, nullptr, 16);
|
||||
if (define_name == reg_name + "_ROW") {
|
||||
reg.row = define_value;
|
||||
} else if (type == REGISTER && define_name == reg_name + "_BITS") {
|
||||
reg.mask = define_value;
|
||||
} else if (type == FIELD && define_name == field_name + "_BITS") {
|
||||
field.mask = define_value;
|
||||
} else if (ends_with(define_name, "_BITS")) {
|
||||
cout << " " << define_name << " : " << define_hex << std::endl;
|
||||
}
|
||||
} else if (std::regex_match( line, smatch, desc_regex)) {
|
||||
if (type == REGISTER) {
|
||||
reg.description = smatch[1];
|
||||
} else if (type == FIELD) {
|
||||
field.description = smatch[1];
|
||||
}
|
||||
} else if (starts_with(line, "// ")) {
|
||||
if (type == REGISTER) {
|
||||
reg.description += " " + trim(line.substr(2));
|
||||
} else if (type == FIELD) {
|
||||
field.description += " " + trim(line.substr(2));
|
||||
}
|
||||
}
|
||||
if (std::regex_match(line, smatch, ecc_regex)) {
|
||||
if (type == REGISTER) {
|
||||
reg.ecc = true;
|
||||
} else {
|
||||
cerr << "ERROR: found (ECC) directive outside of register description" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
}
|
||||
if (std::regex_match(line, smatch, rbit_regex)) {
|
||||
if (type == REGISTER) {
|
||||
expected_redundancy = smatch[1].str()[0] - '0';
|
||||
} else {
|
||||
cerr << "ERROR: found (RBIT) directive outside of register description" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = otp_regs.cbegin(); it != otp_regs.cend();) {
|
||||
const auto &name = it->first;
|
||||
std::smatch smatch;
|
||||
if (std::regex_match(name, smatch, rn_regex)) {
|
||||
int n = name[name.length() - 1] - '0';
|
||||
auto it2 = otp_regs.find(name.substr(0, name.length() - 3));
|
||||
if (it2 != otp_regs.end()) {
|
||||
if (it->second.row != it2->second.row + n) {
|
||||
cerr << "ERROR " << *it << " has redundancy relationship to " << *it2
|
||||
<< " but offsets are wrong" << std::endl;
|
||||
// return ERROR_INPUT;
|
||||
}
|
||||
if (it2->second.redundancy != n) {
|
||||
// should appear in order
|
||||
cerr << "ERROR out of order redundant field " << name << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
it2->second.redundancy++;
|
||||
otp_regs.erase(it++);
|
||||
if (it2->second.redundancy == 8) {
|
||||
// for the crit rows
|
||||
it2->second.crit = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
for (auto it = otp_regs.begin(); it != otp_regs.end(); it++) {
|
||||
const auto &name = it->first;
|
||||
std::smatch smatch;
|
||||
if (std::regex_match(name, smatch, zeroth_regex)) {
|
||||
auto prefix = smatch[1].str();
|
||||
uint32_t relmask = 0;
|
||||
uint32_t relmask_nofields = 0;
|
||||
// cout << "HAHAH " << prefix << " from " << name << std::endl;
|
||||
std::vector<std::string> names;
|
||||
for (auto &e: otp_regs) {
|
||||
if (starts_with(e.first, prefix)) {
|
||||
// cout << e.first << std::endl;
|
||||
// cout << " '" << e.first.substr(prefix.length()) << "'" << std::endl;
|
||||
uint32_t index;
|
||||
try {
|
||||
index = std::stoul(e.first.substr(prefix.length()));
|
||||
} catch (std::invalid_argument &ex) {
|
||||
// not a numeric suffix
|
||||
continue;
|
||||
}
|
||||
if (e.second.row != it->second.row +index * it->second.redundancy) {
|
||||
cerr << "ERROR " << e << " has sequential relationship to " << *it
|
||||
<< " but offsets are wrong" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
e.second.seq_index = (int)index;
|
||||
relmask |= 1u << index;
|
||||
if (e.second.fields.empty()) relmask_nofields |= 1u << index;
|
||||
names.push_back(e.first);
|
||||
}
|
||||
}
|
||||
assert(relmask);
|
||||
if (relmask > 1) {
|
||||
if (relmask_nofields & 1) {
|
||||
if (ends_with(prefix, "_")) prefix = prefix.substr(0, prefix.length()-1);
|
||||
if (relmask != relmask_nofields) {
|
||||
cerr << "ERROR " << prefix << " sequence a subset of members register which have fields" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
int len = __builtin_ctz(~relmask);
|
||||
if (relmask != (1u << len) - 1) {
|
||||
cerr << "ERROR " << prefix << " sequence is missing members" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
for (const auto &n: names) {
|
||||
otp_regs[n].seq_prefix = prefix;
|
||||
otp_regs[n].seq_length = len;
|
||||
}
|
||||
assert(len);
|
||||
} else {
|
||||
for (const auto &n: names) {
|
||||
otp_regs[n].seq_index = 0; // unset index
|
||||
}
|
||||
// cerr << "WARNING " << prefix << " is ignored as a sequential register as it has fields" << std::endl;
|
||||
}
|
||||
} else {
|
||||
cerr << "WARNING " << it->first << " ends in 0 but is not part of a sequnce" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(!reg.mask); // we should have seen a // ==== at the end
|
||||
for (const auto &e : otp_regs) {
|
||||
if (e.second.redundancy > 1 && e.second.ecc) {
|
||||
cerr << e.first << " has both redundancy and ECC" << std::endl;
|
||||
return ERROR_INPUT;
|
||||
}
|
||||
}
|
||||
std::ofstream out_file(argv[2]);
|
||||
#if CODE_OTP
|
||||
out_file << "// GENERATE FILE; DO NOT EDIT // " << std::endl << std::endl;
|
||||
out_file << "#pragma once" << std::endl;
|
||||
out_file << "std::vector<otp_reg> otp_reg_list = {" << std::endl;
|
||||
for (const auto &e : otp_regs) {
|
||||
const auto &r = e.second;
|
||||
out_file << " otp_reg(\"" << e.first << "\", 0x" << std::hex << r.row << ", 0x" << r.mask << ")";
|
||||
if (r.ecc) {
|
||||
out_file << std::endl << " .with_ecc()";
|
||||
}
|
||||
if (r.redundancy > 1) {
|
||||
out_file << std::endl << " .with_redundancy(" << std::dec << r.redundancy << ")";
|
||||
}
|
||||
if (r.crit) {
|
||||
out_file << std::endl << " .with_crit()";
|
||||
}
|
||||
if (valid_description(r.description)) {
|
||||
out_file << std::endl << " .with_description(\"" << r.description << "\")";
|
||||
}
|
||||
if (!r.seq_prefix.empty()) {
|
||||
out_file << std::endl << " .with_sequence(\"" << std::dec << r.seq_prefix << "\", " << r.seq_index << ", " << r.seq_length << ")";
|
||||
}
|
||||
for(const auto &f : r.fields) {
|
||||
out_file << std::endl << " .with_field(otp_field(\"" << f.name << "\", 0x" << std:: hex << f.mask;
|
||||
if (valid_description(f.description)) {
|
||||
out_file << ", \"" << f.description << "\"";
|
||||
}
|
||||
out_file << "))";
|
||||
}
|
||||
out_file << ",";
|
||||
out_file << std::endl;
|
||||
}
|
||||
out_file << "};" << std::endl;
|
||||
#else
|
||||
json j;
|
||||
|
||||
std::vector<otp_reg> otp_regs_vec;
|
||||
for(auto const& e: otp_regs)
|
||||
otp_regs_vec.push_back(e.second);
|
||||
j = otp_regs_vec;
|
||||
out_file << std::setw(4) << j << std::endl;
|
||||
#endif
|
||||
} catch (std::exception &e) {
|
||||
cerr << "ERROR: " << e.what() << "\n\n";
|
||||
return ERROR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
31174
otp_header_parser/rp2350.json.h
Normal file
31174
otp_header_parser/rp2350.json.h
Normal file
File diff suppressed because it is too large
Load diff
24
picoboot_connection/BUILD.bazel
Normal file
24
picoboot_connection/BUILD.bazel
Normal file
|
@ -0,0 +1,24 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "picoboot_connection",
|
||||
srcs = [
|
||||
"picoboot_connection.c",
|
||||
"picoboot_connection_cxx.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"picoboot_connection.h",
|
||||
"picoboot_connection_cxx.h",
|
||||
"//:flash_id_bin.h",
|
||||
],
|
||||
defines = ["HAS_LIBUSB=1"], # Bazel build always has libusb.
|
||||
includes = ["."],
|
||||
deps = [
|
||||
"//elf",
|
||||
"@libusb",
|
||||
"@pico-sdk//src/common/boot_picoboot_headers",
|
||||
"@pico-sdk//src/rp2_common/boot_bootrom_headers",
|
||||
"@pico-sdk//src/rp2_common/pico_bootrom:pico_bootrom_headers",
|
||||
"@pico-sdk//src/rp2_common/pico_stdio_usb:reset_interface_headers",
|
||||
],
|
||||
)
|
|
@ -1,9 +1,12 @@
|
|||
add_library(picoboot_connection_header INTERFACE)
|
||||
target_include_directories(picoboot_connection_header INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
add_library(picoboot_connection INTERFACE)
|
||||
|
||||
target_sources(picoboot_connection INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/picoboot_connection.c)
|
||||
|
||||
target_include_directories(picoboot_connection INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(picoboot_connection INTERFACE picoboot_connection_header)
|
||||
|
||||
add_library(picoboot_connection_cxx INTERFACE)
|
||||
target_sources(picoboot_connection_cxx INTERFACE
|
||||
|
|
|
@ -7,29 +7,33 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "picoboot_connection.h"
|
||||
#include "boot/bootrom_constants.h"
|
||||
#include "pico/stdio_usb/reset_interface.h"
|
||||
|
||||
#if false && !defined(NDEBUG)
|
||||
#define output(format,...) printf(format, __VA_ARGS__)
|
||||
#if ENABLE_DEBUG_LOG
|
||||
#include <stdio.h>
|
||||
#define output(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define output(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
static bool verbose;
|
||||
static bool definitely_exclusive;
|
||||
static enum {
|
||||
XIP_UNKOWN,
|
||||
XIP_ACTIVE,
|
||||
XIP_INACTIVE,
|
||||
} xip_state;
|
||||
|
||||
// todo test sparse binary (well actually two range is this)
|
||||
|
||||
#define VENDOR_ID_RASPBERRY_PI 0x2e8au
|
||||
#define PRODUCT_ID_RP2_USBBOOT 0x0003u
|
||||
#define PRODUCT_ID_PICOPROBE 0x0004u
|
||||
#define PRODUCT_ID_MICROPYTHON 0x0005u
|
||||
#define PRODUCT_ID_STDIO_USB 0x000au
|
||||
|
||||
uint32_t crc32_for_byte(uint32_t remainder) {
|
||||
const uint32_t POLYNOMIAL = 0x4C11DB7;
|
||||
remainder <<= 24u;
|
||||
for (uint bit = 8; bit > 0; bit--) {
|
||||
for (unsigned int bit = 8; bit > 0; bit--) {
|
||||
if (remainder & 0x80000000)
|
||||
remainder = (remainder << 1) ^ POLYNOMIAL;
|
||||
else
|
||||
|
@ -38,47 +42,68 @@ uint32_t crc32_for_byte(uint32_t remainder) {
|
|||
return remainder;
|
||||
}
|
||||
|
||||
uint32_t crc32_sw(const uint8_t *buf, uint count, uint32_t crc) {
|
||||
uint32_t crc32_sw(const uint8_t *buf, unsigned int count, uint32_t crc) {
|
||||
static uint32_t table[0x100];
|
||||
if (!table[1]) {
|
||||
for (uint i = 0; i < count_of(table); i++) {
|
||||
for (unsigned int i = 0; i < count_of(table); i++) {
|
||||
table[i] = crc32_for_byte(i);
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
crc = (crc << 8u) ^ table[(uint8_t) ((crc >> 24u) ^ buf[i])];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint interface;
|
||||
uint out_ep;
|
||||
uint in_ep;
|
||||
unsigned int interface;
|
||||
unsigned int out_ep;
|
||||
unsigned int in_ep;
|
||||
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle) {
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, model_t *model, int vid, int pid, const char* ser) {
|
||||
struct libusb_device_descriptor desc;
|
||||
struct libusb_config_descriptor *config;
|
||||
|
||||
definitely_exclusive = false;
|
||||
*dev_handle = NULL;
|
||||
*model = unknown;
|
||||
int ret = libusb_get_device_descriptor(device, &desc);
|
||||
enum picoboot_device_result res = dr_vidpid_unknown;
|
||||
if (ret && verbose) {
|
||||
output("Failed to read device descriptor");
|
||||
output("Failed to read device descriptor\n");
|
||||
}
|
||||
if (!ret) {
|
||||
if (desc.idVendor != VENDOR_ID_RASPBERRY_PI) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
switch (desc.idProduct) {
|
||||
case PRODUCT_ID_MICROPYTHON:
|
||||
return dr_vidpid_micropython;
|
||||
case PRODUCT_ID_PICOPROBE:
|
||||
return dr_vidpid_picoprobe;
|
||||
case PRODUCT_ID_STDIO_USB:
|
||||
return dr_vidpid_stdio_usb;
|
||||
case PRODUCT_ID_RP2_USBBOOT:
|
||||
break;
|
||||
default:
|
||||
if (pid >= 0) {
|
||||
bool match_vid = (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid) == desc.idVendor;
|
||||
bool match_pid = pid == desc.idProduct;
|
||||
if (!(match_vid && match_pid)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
} else if (vid != 0) { // ignore vid/pid filtering if no pid and vid == 0
|
||||
if (desc.idVendor != (vid < 0 ? VENDOR_ID_RASPBERRY_PI : (unsigned int)vid)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
switch (desc.idProduct) {
|
||||
case PRODUCT_ID_MICROPYTHON:
|
||||
return dr_vidpid_micropython;
|
||||
case PRODUCT_ID_PICOPROBE:
|
||||
return dr_vidpid_picoprobe;
|
||||
case PRODUCT_ID_RP2040_STDIO_USB:
|
||||
*model = rp2040;
|
||||
res = dr_vidpid_stdio_usb;
|
||||
break;
|
||||
case PRODUCT_ID_STDIO_USB:
|
||||
*model = rp2350;
|
||||
res = dr_vidpid_stdio_usb;
|
||||
break;
|
||||
case PRODUCT_ID_RP2040_USBBOOT:
|
||||
*model = rp2040;
|
||||
break;
|
||||
case PRODUCT_ID_RP2350_USBBOOT:
|
||||
*model = rp2350;
|
||||
break;
|
||||
default:
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
}
|
||||
ret = libusb_get_active_config_descriptor(device, &config);
|
||||
if (ret && verbose) {
|
||||
|
@ -92,7 +117,40 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d
|
|||
output("Failed to open device %d\n", ret);
|
||||
}
|
||||
if (ret) {
|
||||
return dr_vidpid_bootrom_cant_connect;
|
||||
if (vid == 0 || strlen(ser) != 0) {
|
||||
// didn't check vid or ser, so treat as unknown
|
||||
return dr_vidpid_unknown;
|
||||
} else if (res == dr_vidpid_stdio_usb) {
|
||||
return dr_vidpid_stdio_usb_cant_connect;
|
||||
} else {
|
||||
return dr_vidpid_bootrom_cant_connect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret && res == dr_vidpid_stdio_usb) {
|
||||
if (strlen(ser) != 0) {
|
||||
// Check USB serial number
|
||||
char ser_str[128];
|
||||
libusb_get_string_descriptor_ascii(*dev_handle, desc.iSerialNumber, (unsigned char*)ser_str, sizeof(ser_str));
|
||||
if (strcmp(ser, ser_str)) {
|
||||
return dr_vidpid_unknown;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime reset interface with thirdparty VID
|
||||
if (!ret) {
|
||||
for (int i = 0; i < config->bNumInterfaces; i++) {
|
||||
if (config->interface[i].altsetting[0].bInterfaceClass == 0xff &&
|
||||
config->interface[i].altsetting[0].bInterfaceSubClass == RESET_INTERFACE_SUBCLASS &&
|
||||
config->interface[i].altsetting[0].bInterfaceProtocol == RESET_INTERFACE_PROTOCOL) {
|
||||
return dr_vidpid_stdio_usb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,14 +172,48 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d
|
|||
if (verbose) output("Failed to claim interface\n");
|
||||
return dr_vidpid_bootrom_no_interface;
|
||||
}
|
||||
|
||||
return dr_vidpid_bootrom_ok;
|
||||
} else {
|
||||
if (verbose) output("Did not find PICOBOOT interface\n");
|
||||
return dr_vidpid_bootrom_no_interface;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (*model == unknown) {
|
||||
struct picoboot_get_info_cmd info_cmd;
|
||||
info_cmd.bType = PICOBOOT_GET_INFO_SYS,
|
||||
info_cmd.dParams[0] = (uint32_t) (SYS_INFO_CHIP_INFO);
|
||||
uint32_t word_buf[64];
|
||||
// RP2040 doesn't have this function, so returns non-zero
|
||||
int info_ret = picoboot_get_info(*dev_handle, &info_cmd, (uint8_t*)word_buf, sizeof(word_buf));
|
||||
if (info_ret) {
|
||||
*model = rp2040;
|
||||
} else {
|
||||
*model = rp2350;
|
||||
}
|
||||
}
|
||||
if (strlen(ser) != 0) {
|
||||
if (*model == rp2040) {
|
||||
// Check flash ID, as USB serial number is not unique
|
||||
uint64_t ser_num = strtoull(ser, NULL, 16);
|
||||
uint64_t id = 0;
|
||||
int id_ret = picoboot_flash_id(*dev_handle, &id);
|
||||
if (verbose) output("Flash ID %"PRIX64"\n", id);
|
||||
if (id_ret || (ser_num != id)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
} else {
|
||||
// Check USB serial number
|
||||
char ser_str[128];
|
||||
libusb_get_string_descriptor_ascii(*dev_handle, desc.iSerialNumber, (unsigned char*)ser_str, sizeof(ser_str));
|
||||
if (strcmp(ser, ser_str)) {
|
||||
return dr_vidpid_unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dr_vidpid_bootrom_ok;
|
||||
}
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (*dev_handle) {
|
||||
|
@ -169,6 +261,7 @@ int picoboot_reset(libusb_device_handle *usb_device) {
|
|||
return ret;
|
||||
}
|
||||
if (verbose) output(" ...ok\n");
|
||||
definitely_exclusive = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -198,7 +291,7 @@ int picoboot_cmd_status(libusb_device_handle *usb_device, struct picoboot_cmd_st
|
|||
|
||||
int one_time_bulk_timeout;
|
||||
|
||||
int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uint8_t *buffer, uint buf_size) {
|
||||
int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uint8_t *buffer, unsigned int buf_size) {
|
||||
int sent = 0;
|
||||
int ret;
|
||||
|
||||
|
@ -212,6 +305,10 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin
|
|||
return ret;
|
||||
}
|
||||
|
||||
int saved_xip_state = xip_state;
|
||||
bool saved_exclusive = definitely_exclusive;
|
||||
xip_state = XIP_UNKOWN;
|
||||
definitely_exclusive = false;
|
||||
int timeout = 10000;
|
||||
if (one_time_bulk_timeout) {
|
||||
timeout = one_time_bulk_timeout;
|
||||
|
@ -250,6 +347,42 @@ int picoboot_cmd(libusb_device_handle *usb_device, struct picoboot_cmd *cmd, uin
|
|||
if (verbose) output("zero length in\n");
|
||||
ret = libusb_bulk_transfer(usb_device, in_ep, spoon, 1, &received, cmd->dTransferLength == 0 ? timeout : 3000);
|
||||
}
|
||||
if (!ret) {
|
||||
// do our defensive best to keep the xip_state up to date
|
||||
switch (cmd->bCmdId) {
|
||||
case PC_EXIT_XIP:
|
||||
xip_state = XIP_INACTIVE;
|
||||
break;
|
||||
case PC_ENTER_CMD_XIP:
|
||||
xip_state = XIP_ACTIVE;
|
||||
break;
|
||||
case PC_READ:
|
||||
case PC_WRITE:
|
||||
// whitelist PC_READ and PC_WRITE as not affecting xip state
|
||||
xip_state = saved_xip_state;
|
||||
break;
|
||||
default:
|
||||
xip_state = XIP_UNKOWN;
|
||||
break;
|
||||
}
|
||||
// do our defensive best to keep the exclusive var up to date
|
||||
switch (cmd->bCmdId) {
|
||||
case PC_EXCLUSIVE_ACCESS:
|
||||
definitely_exclusive = cmd->exclusive_cmd.bExclusive;
|
||||
break;
|
||||
case PC_ENTER_CMD_XIP:
|
||||
case PC_EXIT_XIP:
|
||||
case PC_READ:
|
||||
case PC_WRITE:
|
||||
// whitelist PC_READ and PC_WRITE as not affecting xip state
|
||||
definitely_exclusive = saved_exclusive;
|
||||
break;
|
||||
default:
|
||||
definitely_exclusive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -264,11 +397,16 @@ int picoboot_exclusive_access(libusb_device_handle *usb_device, uint8_t exclusiv
|
|||
}
|
||||
|
||||
int picoboot_exit_xip(libusb_device_handle *usb_device) {
|
||||
if (definitely_exclusive && xip_state == XIP_INACTIVE) {
|
||||
if (verbose) output("Skipping EXIT_XIP");
|
||||
return 0;
|
||||
}
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("EXIT_XIP\n");
|
||||
cmd.bCmdId = PC_EXIT_XIP;
|
||||
cmd.bCmdSize = 0;
|
||||
cmd.dTransferLength = 0;
|
||||
xip_state = XIP_INACTIVE;
|
||||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
|
@ -278,12 +416,13 @@ int picoboot_enter_cmd_xip(libusb_device_handle *usb_device) {
|
|||
cmd.bCmdId = PC_ENTER_CMD_XIP;
|
||||
cmd.bCmdSize = 0;
|
||||
cmd.dTransferLength = 0;
|
||||
xip_state = XIP_ACTIVE;
|
||||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp, uint32_t delay_ms) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("REBOOT %08x %08x %u\n", (uint) pc, (uint) sp, (uint) delay_ms);
|
||||
if (verbose) output("REBOOT %08x %08x %u\n", (unsigned int) pc, (unsigned int) sp, (unsigned int) delay_ms);
|
||||
cmd.bCmdId = PC_REBOOT;
|
||||
cmd.bCmdSize = sizeof(cmd.reboot_cmd);
|
||||
cmd.dTransferLength = 0;
|
||||
|
@ -293,11 +432,21 @@ int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp,
|
|||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
int picoboot_reboot2(libusb_device_handle *usb_device, struct picoboot_reboot2_cmd *reboot_cmd) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("REBOOT %08x %08x %08x %u\n", (unsigned int)reboot_cmd->dFlags, (unsigned int) reboot_cmd->dParam0, (unsigned int) reboot_cmd->dParam1, (unsigned int) reboot_cmd->dDelayMS);
|
||||
cmd.bCmdId = PC_REBOOT2;
|
||||
cmd.bCmdSize = sizeof(cmd.reboot2_cmd);
|
||||
cmd.reboot2_cmd = *reboot_cmd;
|
||||
cmd.dTransferLength = 0;
|
||||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr) {
|
||||
struct picoboot_cmd cmd;
|
||||
// shouldn't be necessary any more
|
||||
// addr |= 1u; // Thumb bit
|
||||
if (verbose) output("EXEC %08x\n", (uint) addr);
|
||||
if (verbose) output("EXEC %08x\n", (unsigned int) addr);
|
||||
cmd.bCmdId = PC_EXEC;
|
||||
cmd.bCmdSize = sizeof(cmd.address_only_cmd);
|
||||
cmd.dTransferLength = 0;
|
||||
|
@ -305,9 +454,22 @@ int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr) {
|
|||
return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
}
|
||||
|
||||
// int picoboot_exec2(libusb_device_handle *usb_device, struct picoboot_exec2_cmd *exec2_cmd) {
|
||||
// struct picoboot_cmd cmd;
|
||||
// // shouldn't be necessary any more
|
||||
// // addr |= 1u; // Thumb bit
|
||||
// //if (verbose) output("EXEC2 %08x\n", (unsigned int) exec2_cmd->scan_base);
|
||||
// cmd.bCmdId = PC_EXEC2;
|
||||
// cmd.bCmdSize = sizeof(cmd.exec2_cmd);
|
||||
// cmd.dTransferLength = 0;
|
||||
// cmd.exec2_cmd = *exec2_cmd;
|
||||
// return picoboot_cmd(usb_device, &cmd, NULL, 0);
|
||||
// } // currently unused
|
||||
|
||||
|
||||
int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("FLASH_ERASE %08x+%08x\n", (uint) addr, (uint) len);
|
||||
if (verbose) output("FLASH_ERASE %08x+%08x\n", (unsigned int) addr, (unsigned int) len);
|
||||
cmd.bCmdId = PC_FLASH_ERASE;
|
||||
cmd.bCmdSize = sizeof(cmd.range_cmd);
|
||||
cmd.range_cmd.dAddr = addr;
|
||||
|
@ -318,7 +480,7 @@ int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32
|
|||
|
||||
int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("VECTOR %08x\n", (uint) addr);
|
||||
if (verbose) output("VECTOR %08x\n", (unsigned int) addr);
|
||||
cmd.bCmdId = PC_VECTORIZE_FLASH;
|
||||
cmd.bCmdSize = sizeof(cmd.address_only_cmd);
|
||||
cmd.range_cmd.dAddr = addr;
|
||||
|
@ -328,7 +490,7 @@ int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr) {
|
|||
|
||||
int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("WRITE %08x+%08x\n", (uint) addr, (uint) len);
|
||||
if (verbose) output("WRITE %08x+%08x\n", (unsigned int) addr, (unsigned int) len);
|
||||
cmd.bCmdId = PC_WRITE;
|
||||
cmd.bCmdSize = sizeof(cmd.range_cmd);
|
||||
cmd.range_cmd.dAddr = addr;
|
||||
|
@ -338,7 +500,7 @@ int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buf
|
|||
|
||||
int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len) {
|
||||
memset(buffer, 0xaa, len);
|
||||
if (verbose) output("READ %08x+%08x\n", (uint) addr, (uint) len);
|
||||
if (verbose) output("READ %08x+%08x\n", (unsigned int) addr, (unsigned int) len);
|
||||
struct picoboot_cmd cmd;
|
||||
cmd.bCmdId = PC_READ;
|
||||
cmd.bCmdSize = sizeof(cmd.range_cmd);
|
||||
|
@ -357,8 +519,47 @@ int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buff
|
|||
return ret;
|
||||
}
|
||||
|
||||
int picoboot_otp_write(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("OTP WRITE %04x+%08x ecc=%d\n", (unsigned int) otp_cmd->wRow, otp_cmd->wRowCount, otp_cmd->bEcc);
|
||||
cmd.bCmdId = PC_OTP_WRITE;
|
||||
#ifdef _MSC_VER
|
||||
cmd.bCmdSize = 5; // for some reason with MSVC, and only with picoboot_otp_cmd, the size is 6 not 5??
|
||||
#else
|
||||
cmd.bCmdSize = sizeof(cmd.otp_cmd);
|
||||
#endif
|
||||
cmd.otp_cmd = *otp_cmd;
|
||||
cmd.dTransferLength = len;
|
||||
one_time_bulk_timeout = 5000 + len * 5;
|
||||
return picoboot_cmd(usb_device, &cmd, buffer, len);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int picoboot_otp_read(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
|
||||
struct picoboot_cmd cmd;
|
||||
if (verbose) output("OTP READ %04x+%08x ecc=%d\n", (unsigned int) otp_cmd->wRow, otp_cmd->wRowCount, otp_cmd->bEcc);
|
||||
cmd.bCmdId = PC_OTP_READ;
|
||||
#ifdef _MSC_VER
|
||||
cmd.bCmdSize = 5; // for some reason with MSVC, and only with picoboot_otp_cmd, the size is 6 not 5??
|
||||
#else
|
||||
cmd.bCmdSize = sizeof(cmd.otp_cmd);
|
||||
#endif
|
||||
cmd.otp_cmd = *otp_cmd;
|
||||
cmd.dTransferLength = len;
|
||||
return picoboot_cmd(usb_device, &cmd, buffer, len);
|
||||
}
|
||||
|
||||
int picoboot_get_info(libusb_device_handle *usb_device, struct picoboot_get_info_cmd *get_info_cmd, uint8_t *buffer, uint32_t len) {
|
||||
if (verbose) output("GET_INFO\n");
|
||||
struct picoboot_cmd cmd;
|
||||
cmd.bCmdId = PC_GET_INFO;
|
||||
cmd.bCmdSize = sizeof(cmd.get_info_cmd);
|
||||
cmd.get_info_cmd = *get_info_cmd;
|
||||
cmd.dTransferLength = len;
|
||||
int ret = picoboot_cmd(usb_device, &cmd, buffer, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// Peek/poke via EXEC
|
||||
|
||||
// 00000000 <poke>:
|
||||
|
@ -376,6 +577,7 @@ static const size_t picoboot_poke_cmd_len = 8;
|
|||
static const uint8_t picoboot_poke_cmd[] = {
|
||||
0x01, 0x48, 0x02, 0x49, 0x08, 0x60, 0x70, 0x47
|
||||
};
|
||||
#define PICOBOOT_POKE_CMD_PROG_SIZE (size_t)(8 + 8)
|
||||
|
||||
// 00000000 <peek>:
|
||||
// 0: 4802 ldr r0, [pc, #8] ; (c <inout>)
|
||||
|
@ -391,19 +593,88 @@ static const size_t picoboot_peek_cmd_len = 12;
|
|||
static const uint8_t picoboot_peek_cmd[] = {
|
||||
0x02, 0x48, 0x00, 0x68, 0x79, 0x46, 0x48, 0x60, 0x70, 0x47, 0xc0, 0x46
|
||||
};
|
||||
#define PICOBOOT_PEEK_CMD_PROG_SIZE (size_t)(12 + 4)
|
||||
|
||||
// 00000000 <flash_get_unique_id_raw>:
|
||||
// 0: a002 add r0, pc, #8 @ (adr r0, c <FLASH_RUID_DATA_BYTES+0x4>)
|
||||
// 2: a106 add r1, pc, #24 @ (adr r1, 1c <FLASH_RUID_TOTAL_BYTES+0xf>)
|
||||
// 4: 4a00 ldr r2, [pc, #0] @ (8 <FLASH_RUID_DATA_BYTES>)
|
||||
// 6: e011 b.n 2c <flash_do_cmd>
|
||||
// 8: 0000000d .word 0x0000000d
|
||||
// c: 0000004b .word 0x0000004b
|
||||
// ...
|
||||
//
|
||||
// 0000002c <flash_do_cmd>:
|
||||
// 2c: 2380 movs r3, #128 @ 0x80
|
||||
// 2e: b5f0 push {r4, r5, r6, r7, lr}
|
||||
// 30: 4e17 ldr r6, [pc, #92] @ (90 <FLASH_RUID_CMD+0x45>)
|
||||
// 32: 009b lsls r3, r3, #2
|
||||
// 34: 6834 ldr r4, [r6, #0]
|
||||
// 36: 4063 eors r3, r4
|
||||
// 38: 24c0 movs r4, #192 @ 0xc0
|
||||
// 3a: 00a4 lsls r4, r4, #2
|
||||
// 3c: 4023 ands r3, r4
|
||||
// 3e: 4c15 ldr r4, [pc, #84] @ (94 <FLASH_RUID_CMD+0x49>)
|
||||
// 40: 6023 str r3, [r4, #0]
|
||||
// 42: 24c0 movs r4, #192 @ 0xc0
|
||||
// 44: 0013 movs r3, r2
|
||||
// 46: 0564 lsls r4, r4, #21
|
||||
// 48: 0017 movs r7, r2
|
||||
// 4a: 431f orrs r7, r3
|
||||
// 4c: d106 bne.n 5c <FLASH_RUID_CMD+0x11>
|
||||
// 4e: 23c0 movs r3, #192 @ 0xc0
|
||||
// 50: 6832 ldr r2, [r6, #0]
|
||||
// 52: 009b lsls r3, r3, #2
|
||||
// 54: 4393 bics r3, r2
|
||||
// 56: 4a0f ldr r2, [pc, #60] @ (94 <FLASH_RUID_CMD+0x49>)
|
||||
// 58: 6013 str r3, [r2, #0]
|
||||
// 5a: bdf0 pop {r4, r5, r6, r7, pc}
|
||||
// 5c: 2508 movs r5, #8
|
||||
// 5e: 6aa7 ldr r7, [r4, #40] @ 0x28
|
||||
// 60: 403d ands r5, r7
|
||||
// 62: 46ac mov ip, r5
|
||||
// 64: 2502 movs r5, #2
|
||||
// 66: 422f tst r7, r5
|
||||
// 68: d008 beq.n 7c <FLASH_RUID_CMD+0x31>
|
||||
// 6a: 2a00 cmp r2, #0
|
||||
// 6c: d006 beq.n 7c <FLASH_RUID_CMD+0x31>
|
||||
// 6e: 1a9f subs r7, r3, r2
|
||||
// 70: 2f0d cmp r7, #13
|
||||
// 72: d803 bhi.n 7c <FLASH_RUID_CMD+0x31>
|
||||
// 74: 7807 ldrb r7, [r0, #0]
|
||||
// 76: 3a01 subs r2, #1
|
||||
// 78: 6627 str r7, [r4, #96] @ 0x60
|
||||
// 7a: 3001 adds r0, #1
|
||||
// 7c: 4665 mov r5, ip
|
||||
// 7e: 2d00 cmp r5, #0
|
||||
// 80: d0e2 beq.n 48 <flash_do_cmd+0x1c>
|
||||
// 82: 2b00 cmp r3, #0
|
||||
// 84: d0e0 beq.n 48 <flash_do_cmd+0x1c>
|
||||
// 86: 6e27 ldr r7, [r4, #96] @ 0x60
|
||||
// 88: 3b01 subs r3, #1
|
||||
// 8a: 700f strb r7, [r1, #0]
|
||||
// 8c: 3101 adds r1, #1
|
||||
// 8e: e7db b.n 48 <flash_do_cmd+0x1c>
|
||||
// 90: 4001800c .word 0x4001800c
|
||||
// 94: 4001900c .word 0x4001900c
|
||||
|
||||
#include "flash_id_bin.h"
|
||||
#define PICOBOOT_FLASH_ID_CMD_PROG_SIZE (const size_t)(152)
|
||||
|
||||
// TODO better place for this e.g. the USB DPRAM location the controller has already put it in
|
||||
#define PEEK_POKE_CODE_LOC 0x20000000u
|
||||
|
||||
#define FLASH_ID_CODE_LOC 0x15000000 // XIP_SRAM_BASE on RP2040, as we're not using XIP so probably fine
|
||||
#define FLASH_ID_UID_ADDR (FLASH_ID_CODE_LOC + 28 + 1 + 4)
|
||||
|
||||
int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data) {
|
||||
const size_t prog_size = picoboot_poke_cmd_len + 8;
|
||||
uint8_t prog[prog_size];
|
||||
uint8_t prog[PICOBOOT_POKE_CMD_PROG_SIZE];
|
||||
output("POKE (D)%08x -> (A)%08x\n", data, addr);
|
||||
memcpy(prog, picoboot_poke_cmd, picoboot_poke_cmd_len);
|
||||
*(uint32_t *) (prog + picoboot_poke_cmd_len) = data;
|
||||
*(uint32_t *) (prog + picoboot_poke_cmd_len + 4) = addr;
|
||||
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, prog_size);
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, PICOBOOT_POKE_CMD_PROG_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
return picoboot_exec(usb_device, PEEK_POKE_CODE_LOC);
|
||||
|
@ -411,13 +682,12 @@ int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data
|
|||
|
||||
// TODO haven't checked the store goes to the right address :)
|
||||
int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *data) {
|
||||
const size_t prog_size = picoboot_peek_cmd_len + 4;
|
||||
uint8_t prog[prog_size];
|
||||
uint8_t prog[PICOBOOT_PEEK_CMD_PROG_SIZE];
|
||||
output("PEEK %08x\n", addr);
|
||||
memcpy(prog, picoboot_peek_cmd, picoboot_peek_cmd_len);
|
||||
*(uint32_t *) (prog + picoboot_peek_cmd_len) = addr;
|
||||
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, prog_size);
|
||||
int ret = picoboot_write(usb_device, PEEK_POKE_CODE_LOC, prog, PICOBOOT_PEEK_CMD_PROG_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = picoboot_exec(usb_device, PEEK_POKE_CODE_LOC);
|
||||
|
@ -425,4 +695,37 @@ int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *dat
|
|||
return ret;
|
||||
return picoboot_read(usb_device, PEEK_POKE_CODE_LOC + picoboot_peek_cmd_len, (uint8_t *) data, sizeof(uint32_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
int picoboot_flash_id(libusb_device_handle *usb_device, uint64_t *data) {
|
||||
picoboot_exclusive_access(usb_device, 1);
|
||||
assert(PICOBOOT_FLASH_ID_CMD_PROG_SIZE == flash_id_bin_SIZE);
|
||||
uint8_t prog[PICOBOOT_FLASH_ID_CMD_PROG_SIZE];
|
||||
uint64_t id;
|
||||
output("GET FLASH ID\n");
|
||||
memcpy(prog, flash_id_bin, flash_id_bin_SIZE);
|
||||
|
||||
// ensure XIP is exited before executing
|
||||
int ret = picoboot_exit_xip(usb_device);
|
||||
if (ret)
|
||||
goto flash_id_return;
|
||||
ret = picoboot_write(usb_device, FLASH_ID_CODE_LOC, prog, PICOBOOT_FLASH_ID_CMD_PROG_SIZE);
|
||||
if (ret)
|
||||
goto flash_id_return;
|
||||
ret = picoboot_exec(usb_device, FLASH_ID_CODE_LOC);
|
||||
if (ret)
|
||||
goto flash_id_return;
|
||||
ret = picoboot_read(usb_device, FLASH_ID_UID_ADDR, (uint8_t *) &id, sizeof(uint64_t));
|
||||
*data = (((id & 0x00000000000000FF) << 56) |
|
||||
((id & 0x000000000000FF00) << 40) |
|
||||
((id & 0x0000000000FF0000) << 24) |
|
||||
((id & 0x00000000FF000000) << 8) |
|
||||
((id & 0x000000FF00000000) >> 8) |
|
||||
((id & 0x0000FF0000000000) >> 24) |
|
||||
((id & 0x00FF000000000000) >> 40) |
|
||||
((id & 0xFF00000000000000) >> 56));
|
||||
|
||||
flash_id_return:
|
||||
picoboot_exclusive_access(usb_device, 0);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -10,9 +10,21 @@
|
|||
// todo we should use fully encapsulate libusb
|
||||
|
||||
#include <assert.h>
|
||||
#if HAS_LIBUSB
|
||||
#include <libusb.h>
|
||||
#endif
|
||||
#include "boot/picoboot.h"
|
||||
|
||||
#include "addresses.h"
|
||||
|
||||
#define VENDOR_ID_RASPBERRY_PI 0x2e8au
|
||||
#define PRODUCT_ID_RP2040_USBBOOT 0x0003u
|
||||
#define PRODUCT_ID_PICOPROBE 0x0004u
|
||||
#define PRODUCT_ID_MICROPYTHON 0x0005u
|
||||
#define PRODUCT_ID_STDIO_USB 0x0009u
|
||||
#define PRODUCT_ID_RP2040_STDIO_USB 0x000au
|
||||
#define PRODUCT_ID_RP2350_USBBOOT 0x000fu
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -26,9 +38,23 @@ enum picoboot_device_result {
|
|||
dr_vidpid_unknown,
|
||||
dr_error,
|
||||
dr_vidpid_stdio_usb,
|
||||
dr_vidpid_stdio_usb_cant_connect,
|
||||
};
|
||||
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle);
|
||||
typedef enum {
|
||||
rp2040,
|
||||
rp2350,
|
||||
unknown
|
||||
} model_t;
|
||||
|
||||
typedef enum {
|
||||
rp2350_a2,
|
||||
rp2350_unknown
|
||||
} rp2350_version_t;
|
||||
|
||||
#if HAS_LIBUSB
|
||||
// note that vid and pid are filters, unless both are specified in which case a device with that VID and PID is allowed for RP2350
|
||||
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, model_t *model, int vid, int pid, const char* ser);
|
||||
|
||||
int picoboot_reset(libusb_device_handle *usb_device);
|
||||
int picoboot_cmd_status_verbose(libusb_device_handle *usb_device, struct picoboot_cmd_status *status,
|
||||
|
@ -38,29 +64,26 @@ int picoboot_exclusive_access(libusb_device_handle *usb_device, uint8_t exclusiv
|
|||
int picoboot_enter_cmd_xip(libusb_device_handle *usb_device);
|
||||
int picoboot_exit_xip(libusb_device_handle *usb_device);
|
||||
int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp, uint32_t delay_ms);
|
||||
int picoboot_reboot2(libusb_device_handle *usb_device, struct picoboot_reboot2_cmd *reboot_cmd);
|
||||
int picoboot_get_info(libusb_device_handle *usb_device, struct picoboot_get_info_cmd *cmd, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr);
|
||||
// int picoboot_exec2(libusb_device_handle *usb_device, struct picoboot_exec2_cmd *exec2_cmd); // currently unused
|
||||
int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32_t len);
|
||||
int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr);
|
||||
int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_otp_write(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_otp_read(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
|
||||
int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data);
|
||||
int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *data);
|
||||
|
||||
#define ROM_START 0x00000000
|
||||
#define ROM_END 0x00004000
|
||||
#define FLASH_START 0x10000000
|
||||
#define FLASH_END 0x11000000 // this is maximum
|
||||
#define XIP_SRAM_BASE 0x15000000
|
||||
#define XIP_SRAM_END 0x15004000
|
||||
|
||||
#define SRAM_START 0x20000000
|
||||
#define SRAM_END 0x20042000
|
||||
|
||||
#define SRAM_UNSTRIPED_START 0x21000000
|
||||
#define SRAM_UNSTRIPED_END 0x21040000
|
||||
int picoboot_flash_id(libusb_device_handle *usb_device, uint64_t *data);
|
||||
#endif
|
||||
|
||||
// we require 256 (as this is the page size supported by the device)
|
||||
#define LOG2_PAGE_SIZE 8u
|
||||
#ifdef PAGE_SIZE
|
||||
#undef PAGE_SIZE
|
||||
#endif
|
||||
#define PAGE_SIZE (1u << LOG2_PAGE_SIZE)
|
||||
#define FLASH_SECTOR_ERASE_SIZE 4096u
|
||||
|
||||
|
@ -74,27 +97,44 @@ enum memory_type {
|
|||
};
|
||||
|
||||
// inclusive of ends
|
||||
static inline enum memory_type get_memory_type(uint32_t addr) {
|
||||
if (addr >= ROM_START && addr <= ROM_END) {
|
||||
return rom;
|
||||
}
|
||||
if (addr >= FLASH_START && addr <= FLASH_END) {
|
||||
static inline enum memory_type get_memory_type(uint32_t addr, model_t model) {
|
||||
if (addr >= FLASH_START && addr <= FLASH_END_RP2040) {
|
||||
return flash;
|
||||
}
|
||||
if (addr >= SRAM_START && addr <= SRAM_END) {
|
||||
if (addr >= ROM_START && addr <= ROM_END_RP2040) {
|
||||
return rom;
|
||||
}
|
||||
if (addr >= SRAM_START && addr <= SRAM_END_RP2040) {
|
||||
return sram;
|
||||
}
|
||||
if (addr >= SRAM_UNSTRIPED_START && addr <= SRAM_UNSTRIPED_END) {
|
||||
if (model == rp2350) {
|
||||
if (addr >= FLASH_START && addr <= FLASH_END_RP2350) {
|
||||
return flash;
|
||||
}
|
||||
if (addr >= ROM_START && addr <= ROM_END_RP2350) {
|
||||
return rom;
|
||||
}
|
||||
if (addr >= SRAM_START && addr <= SRAM_END_RP2350) {
|
||||
return sram;
|
||||
}
|
||||
}
|
||||
if (addr >= MAIN_RAM_BANKED_START && addr <= MAIN_RAM_BANKED_END) {
|
||||
return sram_unstriped;
|
||||
}
|
||||
if (addr >= XIP_SRAM_BASE && addr <= XIP_SRAM_END) {
|
||||
return xip_sram;
|
||||
if (model == rp2040) {
|
||||
if (addr >= XIP_SRAM_START_RP2040 && addr <= XIP_SRAM_END_RP2040) {
|
||||
return xip_sram;
|
||||
}
|
||||
} else if (model == rp2350) {
|
||||
if (addr >= XIP_SRAM_START_RP2350 && addr <= XIP_SRAM_END_RP2350) {
|
||||
return xip_sram;
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
static inline bool is_transfer_aligned(uint32_t addr) {
|
||||
enum memory_type t = get_memory_type(addr);
|
||||
static inline bool is_transfer_aligned(uint32_t addr, model_t model) {
|
||||
enum memory_type t = get_memory_type(addr, model);
|
||||
return t != invalid && !(t == flash && addr & (PAGE_SIZE-1));
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,16 @@ std::map<enum picoboot_status, const char *> status_code_strings = {
|
|||
{picoboot_status::PICOBOOT_INVALID_TRANSFER_LENGTH, "invalid transfer length"},
|
||||
{picoboot_status::PICOBOOT_REBOOTING, "rebooting"},
|
||||
{picoboot_status::PICOBOOT_UNKNOWN_CMD, "unknown cmd"},
|
||||
{picoboot_status::PICOBOOT_INVALID_STATE, "invalid state"},
|
||||
{picoboot_status::PICOBOOT_INVALID_ADDRESS, "invalid argument"},
|
||||
{picoboot_status::PICOBOOT_NOT_PERMITTED, "permission failure"},
|
||||
{picoboot_status::PICOBOOT_INVALID_ARG, "invalid arg"},
|
||||
{picoboot_status::PICOBOOT_BUFFER_TOO_SMALL, "buffer too small"},
|
||||
{picoboot_status::PICOBOOT_PRECONDITION_NOT_MET, "precondition not met (pt not loaded)"},
|
||||
{picoboot_status::PICOBOOT_MODIFIED_DATA, "modified data (pt modified since load)"},
|
||||
{picoboot_status::PICOBOOT_INVALID_DATA, "data is invalid"},
|
||||
{picoboot_status::PICOBOOT_NOT_FOUND, "not found"},
|
||||
{picoboot_status::PICOBOOT_UNSUPPORTED_MODIFICATION, "unsupported modification (attempt to clear otp bits)"},
|
||||
};
|
||||
|
||||
const char *command_failure::what() const noexcept {
|
||||
|
@ -51,6 +61,7 @@ template <typename F> void connection::wrap_call(F&& func) {
|
|||
status.dStatusCode = 0;
|
||||
rc = picoboot_cmd_status(device, &status);
|
||||
if (!rc) {
|
||||
reset(); // so we can continue
|
||||
throw command_failure(status.dStatusCode ? (int)status.dStatusCode : PICOBOOT_UNKNOWN_ERROR);
|
||||
}
|
||||
throw connection_error(rc);
|
||||
|
@ -77,10 +88,22 @@ void connection::reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms) {
|
|||
wrap_call([&] { return picoboot_reboot(device, pc, sp, delay_ms); });
|
||||
}
|
||||
|
||||
void connection::reboot2(struct picoboot_reboot2_cmd *cmd) {
|
||||
wrap_call([&] { return picoboot_reboot2(device, cmd); });
|
||||
}
|
||||
|
||||
void connection::get_info(struct picoboot_get_info_cmd *get_info_cmd, uint8_t *buffer, uint32_t len) {
|
||||
wrap_call([&] { return picoboot_get_info(device, get_info_cmd, buffer, len); });
|
||||
}
|
||||
|
||||
void connection::exec(uint32_t addr) {
|
||||
wrap_call([&] { return picoboot_exec(device, addr); });
|
||||
}
|
||||
|
||||
// void connection::exec2(struct picoboot_exec2_cmd *cmd) {
|
||||
// wrap_call([&] { return picoboot_exec2(device, cmd); });
|
||||
// } // currently unused
|
||||
|
||||
void connection::flash_erase(uint32_t addr, uint32_t len) {
|
||||
wrap_call([&] { return picoboot_flash_erase(device, addr, len); });
|
||||
}
|
||||
|
@ -96,3 +119,15 @@ void connection::write(uint32_t addr, uint8_t *buffer, uint32_t len) {
|
|||
void connection::read(uint32_t addr, uint8_t *buffer, uint32_t len) {
|
||||
wrap_call([&] { return picoboot_read(device, addr, buffer, len); });
|
||||
}
|
||||
|
||||
void connection::otp_write(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
|
||||
wrap_call([&] { return picoboot_otp_write(device, otp_cmd, buffer, len); });
|
||||
}
|
||||
|
||||
void connection::otp_read(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len) {
|
||||
wrap_call([&] { return picoboot_otp_read(device, otp_cmd, buffer, len); });
|
||||
}
|
||||
|
||||
void connection::flash_id(uint64_t &data) {
|
||||
wrap_call([&] { return picoboot_flash_id(device, &data); });
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace picoboot {
|
|||
|
||||
const char *what() const noexcept override;
|
||||
|
||||
int get_code() const { return code; }
|
||||
private:
|
||||
int code;
|
||||
};
|
||||
|
@ -27,7 +28,7 @@ namespace picoboot {
|
|||
};
|
||||
|
||||
struct connection {
|
||||
explicit connection(libusb_device_handle *device, bool exclusive = true) : device(device), exclusive(exclusive) {
|
||||
explicit connection(libusb_device_handle *device, model_t model, bool exclusive = true) : device(device), model(model), exclusive(exclusive) {
|
||||
// do a device reset in case it was left in a bad state
|
||||
reset();
|
||||
if (exclusive) exclusive_access(EXCLUSIVE);
|
||||
|
@ -45,7 +46,10 @@ namespace picoboot {
|
|||
void enter_cmd_xip();
|
||||
void exit_xip();
|
||||
void reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms);
|
||||
void reboot2(struct picoboot_reboot2_cmd *cmd);
|
||||
void exec(uint32_t addr);
|
||||
// void exec2(struct picoboot_exec2_cmd *cmd); // currently unused
|
||||
void get_info(struct picoboot_get_info_cmd *get_info_cmd, uint8_t *buffer, uint32_t len);
|
||||
void flash_erase(uint32_t addr, uint32_t len);
|
||||
void vector(uint32_t addr);
|
||||
void write(uint32_t addr, uint8_t *buffer, uint32_t len);
|
||||
|
@ -53,6 +57,11 @@ namespace picoboot {
|
|||
write(addr, bytes.data(), bytes.size());
|
||||
}
|
||||
void read(uint32_t addr, uint8_t *buffer, uint32_t len);
|
||||
void otp_write(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
|
||||
void otp_read(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
|
||||
void flash_id(uint64_t &data);
|
||||
|
||||
model_t get_model() const { return model; }
|
||||
std::vector<uint8_t> read_bytes(uint32_t addr, uint32_t len) {
|
||||
std::vector<uint8_t> bytes(len);
|
||||
read(addr, bytes.data(), len);
|
||||
|
@ -61,6 +70,7 @@ namespace picoboot {
|
|||
private:
|
||||
template <typename F> void wrap_call(F&& func);
|
||||
libusb_device_handle *device;
|
||||
model_t model;
|
||||
bool exclusive;
|
||||
};
|
||||
|
||||
|
|
8
picoboot_flash_id/BUILD.bazel
Normal file
8
picoboot_flash_id/BUILD.bazel
Normal file
|
@ -0,0 +1,8 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "picoboot_flash_id_prebuilt",
|
||||
srcs = ["flash_id.bin"],
|
||||
)
|
||||
|
||||
# TODO: Make it possible to build flash_id.bin from source.
|
36
picoboot_flash_id/CMakeLists.txt
Normal file
36
picoboot_flash_id/CMakeLists.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
if (NOT USE_PRECOMPILED)
|
||||
set(PICO_NO_PICOTOOL 1)
|
||||
|
||||
# default build type
|
||||
set(CMAKE_BUILD_TYPE "MinSizeRel" CACHE STRING "build type")
|
||||
|
||||
# If the user set these environment variables to influence the picotool
|
||||
# build, unset them here so that they do not influence the pico-sdk
|
||||
# build. This is especially required for flags that are not supported
|
||||
# by arm-none-eabi compilers.
|
||||
unset(ENV{CFLAGS})
|
||||
unset(ENV{CXXFLAGS})
|
||||
unset(ENV{LDFLAGS})
|
||||
|
||||
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||||
project(flash_id C CXX ASM)
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(flash_id flash_id.c)
|
||||
target_link_libraries(flash_id PRIVATE
|
||||
hardware_regs hardware_structs hardware_flash_headers
|
||||
)
|
||||
target_link_options(flash_id PRIVATE -nostartfiles -nodefaultlibs -Ttext=0)
|
||||
pico_add_bin_output(flash_id)
|
||||
pico_add_dis_output(flash_id)
|
||||
else()
|
||||
project(flash_id C CXX ASM)
|
||||
message("Using precompiled flash_id.bin")
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/flash_id.bin ${CMAKE_CURRENT_BINARY_DIR}/flash_id.bin COPYONLY)
|
||||
# Use manually specified variables
|
||||
set(NULL ${CMAKE_MAKE_PROGRAM})
|
||||
set(NULL ${PICO_SDK_PATH})
|
||||
set(NULL ${PICO_DEBUG_INFO_IN_RELEASE})
|
||||
endif()
|
BIN
picoboot_flash_id/flash_id.bin
Executable file
BIN
picoboot_flash_id/flash_id.bin
Executable file
Binary file not shown.
70
picoboot_flash_id/flash_id.c
Normal file
70
picoboot_flash_id/flash_id.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "hardware/regs/io_qspi.h"
|
||||
#include "hardware/structs/ioqspi.h"
|
||||
#include "hardware/structs/ssi.h"
|
||||
#include "hardware/flash.h"
|
||||
|
||||
asm(
|
||||
".macro static_assert value, msg\n"
|
||||
".if !(\\value)\n"
|
||||
".err \\msg\n"
|
||||
".endif\n"
|
||||
".endm\n"
|
||||
|
||||
".set FLASH_RUID_CMD, 0x4b\n"
|
||||
".set FLASH_RUID_DUMMY_BYTES, 4\n"
|
||||
".set FLASH_RUID_DATA_BYTES, 8\n"
|
||||
".set FLASH_RUID_TOTAL_BYTES, (1 + FLASH_RUID_DUMMY_BYTES + FLASH_RUID_DATA_BYTES)\n"
|
||||
);
|
||||
|
||||
void flash_do_cmd(const uint8_t * txbuf, uint8_t *rxbuf, size_t count);
|
||||
void __attribute__((naked)) flash_get_unique_id_raw(void) {
|
||||
asm(
|
||||
".Lflash_get_unique_id_raw:\n"
|
||||
"adr r0, .Ltxbuf\n"
|
||||
"adr r1, .Lrxbuf\n"
|
||||
"ldr r2, .Lbuflen\n"
|
||||
"b flash_do_cmd\n"
|
||||
".Lbuflen:\n"
|
||||
".word FLASH_RUID_TOTAL_BYTES\n"
|
||||
".Ltxbuf:\n"
|
||||
".byte FLASH_RUID_CMD\n"
|
||||
".zero (FLASH_RUID_TOTAL_BYTES - 1)\n"
|
||||
".zero (16 - FLASH_RUID_TOTAL_BYTES)\n"
|
||||
".Lrxbuf:\n"
|
||||
".zero FLASH_RUID_TOTAL_BYTES\n"
|
||||
".zero (16 - FLASH_RUID_TOTAL_BYTES)\n"
|
||||
"static_assert ((.Lrxbuf - flash_get_unique_id_raw) == 28), \"rxbuf offset incorrect\"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) flash_cs_force(_Bool high) {
|
||||
uint32_t field_val = high ?
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH :
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
|
||||
hw_write_masked(&ioqspi_hw->io[1].ctrl,
|
||||
field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS
|
||||
);
|
||||
}
|
||||
|
||||
void flash_do_cmd(const uint8_t * txbuf, uint8_t *rxbuf, size_t count) {
|
||||
flash_cs_force(0);
|
||||
size_t tx_remaining = count;
|
||||
size_t rx_remaining = count;
|
||||
// We may be interrupted -- don't want FIFO to overflow if we're distracted.
|
||||
const size_t max_in_flight = 16 - 2;
|
||||
while (tx_remaining || rx_remaining) {
|
||||
uint32_t flags = ssi_hw->sr;
|
||||
bool can_put = !!(flags & SSI_SR_TFNF_BITS);
|
||||
bool can_get = !!(flags & SSI_SR_RFNE_BITS);
|
||||
if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) {
|
||||
ssi_hw->dr0 = *txbuf++;
|
||||
--tx_remaining;
|
||||
}
|
||||
if (can_get && rx_remaining) {
|
||||
*rxbuf++ = (uint8_t)ssi_hw->dr0;
|
||||
--rx_remaining;
|
||||
}
|
||||
}
|
||||
flash_cs_force(1);
|
||||
}
|
|
@ -1,10 +1,24 @@
|
|||
SUBSYSTEM=="usb", \
|
||||
ATTRS{idVendor}=="2e8a", \
|
||||
ATTRS{idProduct}=="0003", \
|
||||
TAG+="uaccess" \
|
||||
MODE="660", \
|
||||
GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", \
|
||||
ATTRS{idVendor}=="2e8a", \
|
||||
ATTRS{idProduct}=="0009", \
|
||||
TAG+="uaccess" \
|
||||
MODE="660", \
|
||||
GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", \
|
||||
ATTRS{idVendor}=="2e8a", \
|
||||
ATTRS{idProduct}=="000a", \
|
||||
TAG+="uaccess" \
|
||||
MODE="660", \
|
||||
GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", \
|
||||
ATTRS{idVendor}=="2e8a", \
|
||||
ATTRS{idProduct}=="000f", \
|
||||
TAG+="uaccess" \
|
||||
MODE="660", \
|
||||
GROUP="plugdev"
|
||||
|
|
39
xip_ram_perms.cpp
Normal file
39
xip_ram_perms.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
#include "xip_ram_perms.h"
|
||||
#include "xip_ram_perms_elf.h"
|
||||
|
||||
#include "data_locs.h"
|
||||
|
||||
#include "whereami++.h"
|
||||
|
||||
|
||||
std::shared_ptr<std::iostream> get_xip_ram_perms() {
|
||||
// search same directory as executable
|
||||
whereami::whereami_path_t executablePath = whereami::getExecutablePath();
|
||||
std::string local_loc = executablePath.dirname() + "/";
|
||||
if (std::find(data_locs.begin(), data_locs.end(), local_loc) == data_locs.end()) {
|
||||
data_locs.insert(data_locs.begin(), local_loc);
|
||||
}
|
||||
|
||||
for (auto loc : data_locs) {
|
||||
std::string filename = loc + "xip_ram_perms.elf";
|
||||
std::ifstream i(filename);
|
||||
if (i.good()) {
|
||||
printf("Picking file %s\n", filename.c_str());
|
||||
auto file = std::make_shared<std::fstream>(filename, std::ios::in|std::ios::binary);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to embedded xip_ram_perms.elf file
|
||||
printf("Could not find xip_ram_perms.elf file - using embedded binary\n");
|
||||
auto tmp = std::make_shared<std::stringstream>();
|
||||
tmp->write(reinterpret_cast<const char*>(xip_ram_perms_elf), xip_ram_perms_elf_SIZE);
|
||||
return tmp;
|
||||
}
|
12
xip_ram_perms.h
Normal file
12
xip_ram_perms.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
|
||||
std::shared_ptr<std::iostream> get_xip_ram_perms();
|
17
xip_ram_perms/BUILD.bazel
Normal file
17
xip_ram_perms/BUILD.bazel
Normal file
|
@ -0,0 +1,17 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "xip_ram_perms_prebuilt",
|
||||
srcs = ["xip_ram_perms.elf"],
|
||||
)
|
||||
|
||||
# TODO: Make this work.
|
||||
cc_library(
|
||||
name = "xip_ram_perms",
|
||||
srcs = ["set_perms.c"],
|
||||
tags = ["manual"],
|
||||
deps = [
|
||||
"//:xip_ram_perms",
|
||||
"@pico-sdk//src/rp2_common/pico_stdlib",
|
||||
],
|
||||
)
|
53
xip_ram_perms/CMakeLists.txt
Normal file
53
xip_ram_perms/CMakeLists.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
if (NOT USE_PRECOMPILED)
|
||||
set(PICO_PLATFORM rp2350-arm-s)
|
||||
|
||||
set(PICO_NO_PICOTOOL 1)
|
||||
|
||||
# If the user set these environment variables to influence the picotool
|
||||
# build, unset them here so that they do not influence the pico-sdk
|
||||
# build. This is especially required for flags that are not supported
|
||||
# by arm-none-eabi compilers.
|
||||
unset(ENV{CFLAGS})
|
||||
unset(ENV{CXXFLAGS})
|
||||
unset(ENV{LDFLAGS})
|
||||
|
||||
# Pull in SDK (must be before project)
|
||||
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||||
|
||||
project(xip_ram_perms C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
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()
|
||||
|
||||
# Initialize the SDK
|
||||
pico_sdk_init()
|
||||
|
||||
# XIP Ram OTP Perm Setter
|
||||
add_executable(xip_ram_perms
|
||||
set_perms.c
|
||||
)
|
||||
|
||||
target_link_libraries(xip_ram_perms
|
||||
pico_stdlib
|
||||
)
|
||||
|
||||
pico_set_binary_type(xip_ram_perms no_flash)
|
||||
# create linker script to run from 0x20070000
|
||||
file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_no_flash.ld LINKER_SCRIPT)
|
||||
string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k" "RAM(rwx) : ORIGIN = 0x13ffc000, LENGTH = 16k" LINKER_SCRIPT "${LINKER_SCRIPT}")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/memmap_xip_ram.ld "${LINKER_SCRIPT}")
|
||||
pico_set_linker_script(xip_ram_perms ${CMAKE_CURRENT_BINARY_DIR}/memmap_xip_ram.ld)
|
||||
else()
|
||||
project(xip_ram_perms C CXX ASM)
|
||||
message("Using precompiled xip_ram_perms.elf")
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/xip_ram_perms.elf ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms.elf COPYONLY)
|
||||
# Use manually specified variables
|
||||
set(NULL ${CMAKE_MAKE_PROGRAM})
|
||||
set(NULL ${PICO_SDK_PATH})
|
||||
set(NULL ${PICO_DEBUG_INFO_IN_RELEASE})
|
||||
endif()
|
192
xip_ram_perms/set_perms.c
Normal file
192
xip_ram_perms/set_perms.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/regs/otp_data.h"
|
||||
|
||||
#include "pico/binary_info.h"
|
||||
|
||||
|
||||
// Very rough - seems to be broken in xip_sram
|
||||
#define sleep_ms(x) for(int i=0; i<x*20000; i++) asm volatile("nop");
|
||||
|
||||
|
||||
int main() {
|
||||
bi_decl(bi_program_feature_group(0x1234, 0x5678, "led_config"));
|
||||
bi_decl(bi_ptr_int32(0x1234, 0x5678, led, PICO_DEFAULT_LED_PIN));
|
||||
|
||||
gpio_init(led);
|
||||
gpio_set_dir(led, GPIO_OUT);
|
||||
gpio_put(led, true);
|
||||
|
||||
bi_decl(bi_program_feature_group(0x1111, 0x2222, "otp_page_permissions"));
|
||||
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page0, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page1, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page2, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page3, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page4, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page5, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page6, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page7, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page8, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page9, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page10, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page11, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page12, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page13, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page14, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page15, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page16, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page17, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page18, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page19, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page20, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page21, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page22, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page23, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page24, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page25, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page26, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page27, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page28, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page29, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page30, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page31, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page32, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page33, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page34, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page35, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page36, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page37, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page38, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page39, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page40, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page41, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page42, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page43, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page44, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page45, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page46, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page47, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page48, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page49, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page50, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page51, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page52, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page53, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page54, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page55, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page56, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page57, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page58, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page59, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page60, 0));
|
||||
bi_decl(bi_ptr_int32(0x1111, 0x2222, page61, 0));
|
||||
|
||||
uint32_t pages[62] = {
|
||||
page0,
|
||||
page1,
|
||||
page2,
|
||||
page3,
|
||||
page4,
|
||||
page5,
|
||||
page6,
|
||||
page7,
|
||||
page8,
|
||||
page9,
|
||||
page10,
|
||||
page11,
|
||||
page12,
|
||||
page13,
|
||||
page14,
|
||||
page15,
|
||||
page16,
|
||||
page17,
|
||||
page18,
|
||||
page19,
|
||||
page20,
|
||||
page21,
|
||||
page22,
|
||||
page23,
|
||||
page24,
|
||||
page25,
|
||||
page26,
|
||||
page27,
|
||||
page28,
|
||||
page29,
|
||||
page30,
|
||||
page31,
|
||||
page32,
|
||||
page33,
|
||||
page34,
|
||||
page35,
|
||||
page36,
|
||||
page37,
|
||||
page38,
|
||||
page39,
|
||||
page40,
|
||||
page41,
|
||||
page42,
|
||||
page43,
|
||||
page44,
|
||||
page45,
|
||||
page46,
|
||||
page47,
|
||||
page48,
|
||||
page49,
|
||||
page50,
|
||||
page51,
|
||||
page52,
|
||||
page53,
|
||||
page54,
|
||||
page55,
|
||||
page56,
|
||||
page57,
|
||||
page58,
|
||||
page59,
|
||||
page60,
|
||||
page61,
|
||||
};
|
||||
|
||||
otp_cmd_t cmd;
|
||||
static __attribute__((aligned(4))) uint8_t otp_buffer[8];
|
||||
for (int i=0; i < 62; i++) {
|
||||
if (pages[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
uint8_t perms0 = pages[i] & 0xff;
|
||||
uint8_t perms1 = (pages[i] >> 16) & 0xff;
|
||||
|
||||
// printf("Unlocking\n");
|
||||
// for (int i=0; i<4; i++) {
|
||||
// uint32_t key_i = ((i*2+1) << 24) | ((i*2+1) << 16) |
|
||||
// (i*2 << 8) | i*2;
|
||||
// otp_hw->crt_key_w[i] = key_i;
|
||||
// printf("%08x\n", key_i);
|
||||
// }
|
||||
cmd.flags = (OTP_CMD_ROW_BITS & (OTP_DATA_PAGE0_LOCK0_ROW + i*2)) | OTP_CMD_WRITE_BITS;
|
||||
|
||||
otp_buffer[0] = perms0;
|
||||
otp_buffer[1] = perms0;
|
||||
otp_buffer[2] = perms0;
|
||||
otp_buffer[3] = 0;
|
||||
otp_buffer[4] = perms1;
|
||||
otp_buffer[5] = perms1;
|
||||
otp_buffer[6] = perms1;
|
||||
otp_buffer[7] = 0;
|
||||
|
||||
int8_t num = rom_func_otp_access(otp_buffer, sizeof(otp_buffer), cmd);
|
||||
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
gpio_put(led, false);
|
||||
|
||||
reset_usb_boot(0, 0);
|
||||
}
|
BIN
xip_ram_perms/xip_ram_perms.elf
Executable file
BIN
xip_ram_perms/xip_ram_perms.elf
Executable file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue