Compare commits

...

72 commits

Author SHA1 Message Date
graham sanderson
df21059f7c bump picotool version and SDK dependency to 2.1.0 2024-11-24 19:46:57 -06:00
Chris Burton
3a476024ad
fix typo (#179)
* fix typo

* fix grammar

---------

Co-authored-by: Graham Sanderson <graham.sanderson@gmail.com>
2024-11-22 12:17:08 -06:00
William Vinnicombe
6c3f0901c4 Update help commands in the README to show their current output 2024-11-21 16:34:43 +00:00
will-v-pi
fa69a49bfb
Add option to ignore already-set bits in otp set command (#175)
Adds -s, --set-bits option to otp set command
2024-11-21 16:13:11 +00:00
will-v-pi
4a403bb277
Add all metadata blocks to info (#173)
* Add info for all metadata blocks with -m

* Add printing of bootloader info
2024-11-21 16:10:27 +00:00
will-v-pi
081a386153
Add an entry point when signing Arm images (#163)
Reads the entry point and stack pointer from the vector table
2024-11-21 16:09:17 +00:00
will-v-pi
fb85aca4cf
Add JSON schemas (#176)
* Add partition table; and otp permissions, whitelabel, contents and settings JSON schemas

* Move example json files into json folder, with schemas in json/schemas
2024-11-20 14:58:47 +00:00
will-v-pi
dcff4d08d1
List field desciptions if field is matching (#174)
* List field desciptions if field matching (fixes #134)
2024-11-20 14:23:49 +00:00
Daniel Schaefer
2f2e8dffdb
Allow using thirdparty VID for reboot interface (#177)
With this change you can reboot a device with third party VID/PID from application firmware into bootloader with:

```
picotool reboot --vid 0x32ac --pid 0x001f -f -u
```

Signed-off-by: Daniel Schaefer <dhs@frame.work>
2024-11-20 10:25:22 +00:00
Andrew Scheller
877282d19d
Fix macOS CI builds (#178)
By removing cmake and pkg-config from the dependencies
2024-11-19 19:14:04 -06:00
will-v-pi
7350867a23
Align Saving, Loading and Verifying progress bars (#170)
* Align all progress bars

* Prevent saving/loading from unstriped SRAM

* Fix saving/verifying range to bin file
2024-11-19 17:51:19 +00:00
William Vinnicombe
78c9bd121b Update abs_block
Add RP2 ignored extension to errata E10 abs_block, to make it more identifiable
2024-11-12 17:26:33 +00:00
Tobias Simetsreiter
ae9a188b4d
lowercase 'Windows.h' in main.cpp for mingw32 support (#168) 2024-11-12 14:08:44 +00:00
will-v-pi
dc9b5494fe
Always print serial number when unable to find a device with specific serial number (#164)
Previously it would not print the serial number if a device wasn't found after a -f reboot (see https://forums.raspberrypi.com/viewtopic.php?t=378682)
2024-11-07 14:37:08 +00:00
William Vinnicombe
0f9977ea71 Fix parsing of partition IDs
Partition IDs are unsigned 64-bit integers, but were being parsed as signed integers, so were out of range if the first bit was set.
2024-11-07 13:45:40 +00:00
armandomontanez
a86abb73b5
Fixes and add presubmit (#166)
* Fix Bazel build and add presubmit checks

Fixes a variable name and adds Bazel presubmit checks to ensure the
Bazel build stays healthy.

* Remove Windows from presubmit checks for now

MSVC is tripping up on statement expressions in timer.h.
2024-11-05 12:42:41 -06:00
armandomontanez
f41f7fa450
[Bazel] Get MSVC working (#157)
Fixes the Windows MSVC build for Picotool.
2024-10-30 10:58:29 +00:00
armandomontanez
3ea1bb5d3e
[Bazel] Infer PICOTOOL_VERSION define from module version (#156)
Makes the PICOTOOL_VERSION define use the value of module_version() to
reduce duplication of version strings in the Bazel build.
2024-10-30 10:57:17 +00:00
Charlie Birks
439062512e
Fix coprodis to not drop the last instruction and the rest of the file (#159) 2024-10-29 18:34:20 +00:00
will-v-pi
19226d169b
Fix info and config commands for packaged binaries (#158)
Remap according to the load_map before searching for the binary info
2024-10-29 18:31:34 +00:00
William Vinnicombe
0dcea9c2bb Require 0x before hexadecimal family IDs
Fixes #161
2024-10-29 14:41:43 +00:00
graham sanderson
afb5f26532 fix otp_load_command 2024-10-26 15:56:00 -05:00
William Vinnicombe
c2fca5a6a7 Fix RP2350 higher pin functions
The pin_functions_rp2350 array was missing a PIO2 row, causing the higher functions to be displayed incorrectly by picotool info

Fix this, and add an "Unknown pin function" printout if pin function is not known
2024-10-24 17:30:22 +01:00
will-v-pi
c4550ad0a7
Fix segfault when unable to connect to stdio_usb device (#155)
Add dr_vidpid_stdio_usb_cant_connect when searching for devices, to detect USB devices that failed to open separately from ones that did

Fixes #151
2024-10-22 14:55:19 +01:00
will-v-pi
de42044be7
Use RP-series when referring to RP2040/RP2350 (#154)
* Use correct device name when known, and RP-series if not known
2024-10-17 09:32:50 +01:00
will-v-pi
0d259e7f76
Fix otp dump command, and otp get for lock rows (#153) 2024-10-14 17:33:50 +01:00
will-v-pi
b62ead341f
Add support for encrypting elfs with section holes within segments (#150) 2024-10-14 15:44:21 +01:00
William Vinnicombe
afdfa928d2 Don't track RP2040 no_flash serial number when rebooting
The RP2040 USB serial number is all EEs when running a no_flash binary, which will then change once booted into bootsel mode, so don't track it for the reboot

Fixes #144
2024-09-18 18:09:31 +01:00
josch
7e2f756a00
{xip_ram_perms,picoboot_flash_id}/CMakeLists.txt: unset environment variables CFLAGS, CXXFLAGS and LDFLAGS (#140)
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.
2024-09-18 17:40:02 +01:00
Andrew Gordon
333a03b819
Fix compilation on FreeBSD 13.2 and later. (#133)
The main change is to portable_endian.h which seems to be out-of-date
and not maintained upstream.  This change also impacts OpenBSD,
but a check of current OpenBSD git repository suggests it is correct there
also.
Finally, <cuchar> is excluded in main.cpp (as it already was for __Apple__);
this will probably not be needed in later FreeBSD releases once <cuchar>
has been picked up from more recent llvm.
2024-09-18 17:38:33 +01:00
William Vinnicombe
fb9a4f0d30 Fix "Manually-specified variables were not used by the project" warning
Add PICO_DEBUG_INFO_IN_RELEASE to the used variables in xip_ram_perms and picoboot_flash_id
2024-09-13 13:48:41 +01:00
armandomontanez
08bbcf7b42
Fix Bazel build breakages (#136)
* Fix Bazel build breakages

Fixes some build breakages related to changes to the Pico SDK structure
and the addition of picoboot_flash_id.

* Add TODO for building flash_id.bin from source in Bazel
2024-09-10 18:44:02 -05:00
William Vinnicombe
d0d0f29af3 Remove debug info from xip_ram_perms.elf (#139)
This embedded local paths of the build machine into the elf file, which was undesirable behaviour
2024-09-09 16:58:32 +01:00
will-v-pi
3ff7c3e710
Add github actions compile test (#131)
* Add build & test workflow

* Ensure no picotool needed for xip_ram_perms and flash_id compilation

* Still check LIBUSB_ROOT if pkgconfig libusb not found
2024-09-09 11:08:26 +01:00
Andrew Scheller
971ee85176
Use 4-space indent instead of 8-space indent (#132)
For commonality with other CMakeLists.txt files
2024-09-03 16:20:39 -05:00
William Vinnicombe
154692d6bf Merge branch 'master' into develop 2024-09-03 12:01:42 +01:00
Koji KITAYAMA
1721716c5e
Fix compile errors when using clang-x86_64-pc-windows-msvc (#129) 2024-09-02 11:44:10 +01:00
David Grayson
bf33c6ddd7
README.md: Improve the MSYS2 instructions (#128)
It's better to let MSYS2 use its default CMake generator, which
is Ninja.

The instructions assumed that Makefiles were being used,
because they said "make install", but we don't want to use Make on
MSYS2, so I fixed that.
2024-09-02 11:39:45 +01:00
will-v-pi
9fa08571cb
Replace .dll.a libusb with .a (#126) 2024-08-30 15:33:37 +01:00
will-v-pi
818d3bcf51
Add verify option to picotool save (#125)
Fixes #113
2024-08-30 15:33:09 +01:00
will-v-pi
fb2e6b9b97
Add support for multi-family UF2s to picotool info (#122) 2024-08-30 15:31:27 +01:00
will-v-pi
f0232cd544
Fix loading into PSRAM (#121)
Skip the flash size checks
2024-08-30 15:30:49 +01:00
graham sanderson
930fcb6108 fixup for latest pico-sdk develop/ 2024-08-29 14:20:20 -05:00
Andrew Scheller
94a96f3af1
Small refactoring of the hex_string functions (#124) 2024-08-28 17:49:52 +01:00
will-v-pi
05ae05532a
Support multiple tries when using -f/F (#116)
This is necessary for WSL, or other cases where it takes more time to detect the device after reboot
2024-08-28 16:47:33 +01:00
Andrew Scheller
b4590fa43b
Use string constants for the family names (#123)
Instead of having hard-coded strings scattered through the code
2024-08-28 16:31:22 +01:00
William Vinnicombe
6ad9c2352c Use lib instead of CMAKE_INSTALL_LIBDIR
Fixes #117
2024-08-23 15:45:23 +01:00
William Vinnicombe
3027a54423 Don't hash pts by default with no mbedtls 2024-08-20 15:09:21 +01:00
William Vinnicombe
f5064f76fd Fix compilation with HAS_MBEDTLS=0
Move checksum calculation into bintool
2024-08-20 12:01:06 +01:00
William Vinnicombe
4545546271 Revert "Target abs-block at CS1 by default"
Bug discovered where larger UF2 files with an abs-block targeting
CS1 fail to download - this is not seen when it targets CS0 so
revert this change for now

This reverts commit ef03bd2d90.
2024-08-20 11:09:45 +01:00
William Vinnicombe
a98a52e5d7 Fixes #114 - Allow specifying family_id when saving
Auto-detects from the binary if not specified

Also extend save range to cover metadata blocks at the end
2024-08-19 10:11:48 +01:00
William Vinnicombe
4a523ede0c Fix hashing a pre-existing load_map with clearing 2024-08-19 09:09:35 +01:00
will-v-pi
e66f13bb2f
Improve Usage by the SDK section in README.md
Reword and add some clarifying notes.
Also mention `~/.local` install option on Linux, which fixes raspberrypi/pico-sdk#1827
2024-08-16 10:30:01 +01:00
William Vinnicombe
29f5b0bec7 Add signing of UF2s
Also fix picotool info bug for files with unknown families
2024-08-15 11:19:21 +01:00
Wyatt Hepler
9bb1ab41ef
Accept ELFs with an OS/ABI of ELFOSABI_GNU (#111)
RP2 ELF files typically have their EI_OSABI field set to ELFOSABI_NONE.
However, when Clang uses GNU extensions, it sets EI_OSABI to
ELFOSABI_GNU. The binary is still fully compatible with picotool;
ELFOSABI_GNU just indicates that GNU extensions are used in the ELF.
2024-08-15 11:01:26 +01:00
will-v-pi
4086910226
Add note to README about picotool_DIR environment variable
Also add link to the find_package documentation for more details
2024-08-15 10:42:48 +01:00
William Vinnicombe
29c5431092 Add picotool erase command
Defaults to erasing all of flash, with options for erasing a range or a partition
2024-08-14 17:47:43 +01:00
Darwin
f98850637b
Clarify picotool builds when libusb is not present. (#109)
Add note to README about picotool builds without libusb missing all the usb interface commands.
2024-08-14 12:09:15 +01:00
William Vinnicombe
ef03bd2d90 Target abs-block at CS1 by default
This still works around E10, but without actually writing to CS0 flash
2024-08-14 11:56:40 +01:00
William Vinnicombe
e1bfb36497 Support hex partition IDs in JSON 2024-08-14 11:01:19 +01:00
William Vinnicombe
4ea7e634d4 Support UF2s with arbitrary family IDs
Don't check fo a valid family ID when loading a UF2 file
2024-08-14 10:54:00 +01:00
William Vinnicombe
deecc43c5f Errata E10 absolute block is only required for flash UF2s 2024-08-13 15:42:38 +01:00
William Vinnicombe
468ba2f5c4 Errata E9 was changed to E10 2024-08-13 15:05:24 +01:00
William Vinnicombe
6b63c0636b Only warn about no block loop if binary info found
Improves 3c45783, by removing warning with empty flash
2024-08-13 13:57:56 +01:00
Ferdinand Bachmann
0c8dcc16e3
Compile picoboot_flash_id_cmd from source and add objdump (#107)
Add full compilation of picoboot_flash_id_cmd from source and add objdump

Uses same mechanism as xip_ram_perms

---------

Co-authored-by: William Vinnicombe <william.vinnicombe@raspberrypi.com>
Co-authored-by: will-v-pi <108662275+will-v-pi@users.noreply.github.com>
2024-08-13 11:36:32 +01:00
William Vinnicombe
3783fb42c0 Fix #110 - Improve message when PICOTOOL_NO_LIBUSB is set 2024-08-13 11:16:52 +01:00
William Vinnicombe
3c457839d1 Display embedded block info if there is no binary info
Useful for encrypted binaries and data
2024-08-13 09:35:50 +01:00
William Vinnicombe
51d83031fa Improve family ID auto-detection
* If no block loop, then check the checksum to detect RP2040 binaries
* If no IMAGE_DEF, then assume partition table
* Check for partition table, when throwing error that file cannot be loaded onto the device
2024-08-13 09:33:18 +01:00
graham sanderson
8a9af99ab1 release 2.0.0 - RP2350 and SDK2.0.0 changes 2024-08-08 05:55:42 -05:00
armandomontanez
b19b6aa6c7
Add Bazel build (#101)
* Add initial Bazel build

* Add README for Bazel build

* Use external registry for Bazel modules

* Fix Bazel build on Windows

Removes an unused .bzl file and fixes incompatible copts on Windows.

* Prepare for BCR-hosted rules_libusb

* Explicitly enable exceptions in Bazel build

* Ready for BCR-provided dependencies

* Set Bazel versions to 1.1.3-rc1

* Add Windows linking fix

* Link to Bazelisk in Bazel getting started instructions

* Apply buildifier formatting fixes

* Add missing platforms dependency

Adds a missing bzlmod dep on `platforms` to fix https://pwbug.dev/258836641

---------

Co-authored-by: Ted Pudlik <tpudlik@gmail.com>
2024-07-16 23:03:28 -05:00
Earle F. Philhower, III
e461b2b6f9
Fix fread unused_result warning (#92)
* Fix fread unused_result warning

Check the read of the source binary succeeded, and if not generate an
exception and let the user know.

Fixes #91

* Use fail() to signal file read error
2024-01-03 15:19:36 -06:00
graham sanderson
5fa2c20007 start 1.1.3 dev 2023-06-13 16:54:18 -05:00
97 changed files with 75411 additions and 863 deletions

1
.bazelignore Normal file
View file

@ -0,0 +1 @@
lib/pico-sdk

1
.bazelrc Normal file
View file

@ -0,0 +1 @@
common --verbose_failures

36
.github/workflows/bazel_build.yml vendored Normal file
View 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

View 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
View 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
View 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
View 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": [],
}),
)

View file

@ -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
View 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",
)

974
README.md

File diff suppressed because it is too large Load diff

0
WORKSPACE Normal file
View file

21
bazel/BUILD.bazel Normal file
View 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
View 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
View 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
View file

@ -0,0 +1,5 @@
#include <vector>
#include <string>
// TODO: Finish this.
std::vector<std::string> data_locs = {};

40
bazel/defs.bzl Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

42
bintool/bintool.h Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

230
cli.h
View file

@ -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) {
}

View file

@ -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
View 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
View 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
View 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)

View file

@ -0,0 +1,3 @@
if (NOT TARGET picotool)
include("${CMAKE_CURRENT_LIST_DIR}/picotoolTargets.cmake")
endif()

View file

@ -0,0 +1,6 @@
#include <string>
const std::string rp2350_json = R"""(
@FILE_CONTENT@
)""";

5
data_locs.h Normal file
View 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
View file

@ -0,0 +1,7 @@
#include <vector>
#include <string>
std::vector<std::string> data_locs = {
"${DATA_LOCS_VEC}"
};

60
elf.h
View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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 &section_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> &sections(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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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]
}
]
}

View 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
View 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"
}
}

View 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
}
}

View 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
}

View 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}$"
}
}
}
}
}

View 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
}

View 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
View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
package(default_visibility = ["//visibility:public"])
cc_library(
name = "json",
hdrs = ["single_include/nlohmann/json.hpp"],
includes = ["single_include"],
)

View 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}>
)

View 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.

File diff suppressed because it is too large Load diff

14
lib/whereami/BUILD.bazel Normal file
View 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 = ["."],
)

View 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
View 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
View 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
View 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
View 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

6775
main.cpp

File diff suppressed because it is too large Load diff

23
no_otp.cpp Normal file
View 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
View 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
View 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

View 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"],
)

View 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()

View 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;
}

File diff suppressed because it is too large Load diff

View 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",
],
)

View file

@ -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

View file

@ -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

View file

@ -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));
}

View file

@ -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); });
}

View file

@ -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;
};

View 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.

View 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

Binary file not shown.

View 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);
}

View file

@ -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
View 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
View 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
View 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",
],
)

View 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
View 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

Binary file not shown.