From 1966f6a71e19a7aa7ad3e5d31ca51e0b96cd4ae6 Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Wed, 3 Jan 2024 13:35:26 -0600 Subject: [PATCH] option to concatenate to combine a JSON config to a binary this allows for putting a JSON representation of a config into the user config area of a binary to be flashed on the board. this allows for conveying configs as simple JSON files and using them to convey specific binaries by parts this is the start of the support to use JSON files on a *new* section of the binary reserved for board default configs Signed-off-by: Brian S. Stephan --- gp2040ce_bintools/builder.py | 42 +++++++++++++----- .../test-binary-source-of-json-config.bin | Bin 0 -> 3770 bytes tests/test_builder.py | 21 +++++++++ tests/test_commands.py | 17 ++++++- 4 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 tests/test-files/test-binary-source-of-json-config.bin diff --git a/gp2040ce_bintools/builder.py b/gp2040ce_bintools/builder.py index 7daca37..35f9e8f 100644 --- a/gp2040ce_bintools/builder.py +++ b/gp2040ce_bintools/builder.py @@ -6,13 +6,14 @@ SPDX-License-Identifier: MIT import argparse import copy import logging +from typing import Optional from google.protobuf.message import Message from gp2040ce_bintools import core_parser from gp2040ce_bintools.rp2040 import get_bootsel_endpoints, read, write from gp2040ce_bintools.storage import (STORAGE_BINARY_LOCATION, STORAGE_BOOTSEL_ADDRESS, STORAGE_SIZE, - pad_config_to_storage_size, serialize_config_with_footer) + get_config_from_json, pad_config_to_storage_size, serialize_config_with_footer) logger = logging.getLogger(__name__) @@ -44,20 +45,34 @@ def combine_firmware_and_config(firmware_binary: bytearray, config_binary: bytea pad_config_to_storage_size(config_binary)) -def concatenate_firmware_and_storage_files(firmware_filename: str, storage_filename: str, +def concatenate_firmware_and_storage_files(firmware_filename: str, binary_user_config_filename: Optional[str] = None, + json_user_config_filename: Optional[str] = None, combined_filename: str = '', usb: bool = False, replace_extra: bool = False) -> None: """Open the provided binary files and combine them into one combined GP2040-CE with config file. Args: firmware_filename: filename of the firmware binary to read - storage_filename: filename of the storage section to read + binary_user_config_filename: filename of the user config section to read, in binary format + json_user_config_filename: filename of the user config section to read, in JSON format combined_filename: filename of where to write the combine binary replace_extra: if larger than normal firmware files should have their overage replaced """ - with open(firmware_filename, 'rb') as firmware, open(storage_filename, 'rb') as storage: - new_binary = combine_firmware_and_config(bytearray(firmware.read()), bytearray(storage.read()), - replace_extra=replace_extra) + new_binary = None + if binary_user_config_filename: + with open(firmware_filename, 'rb') as firmware, open(binary_user_config_filename, 'rb') as storage: + new_binary = combine_firmware_and_config(bytearray(firmware.read()), bytearray(storage.read()), + replace_extra=replace_extra) + elif json_user_config_filename: + with open(firmware_filename, 'rb') as firmware, open(json_user_config_filename, 'r') as json_file: + config = get_config_from_json(json_file.read()) + serialized_config = serialize_config_with_footer(config) + new_binary = combine_firmware_and_config(bytearray(firmware.read()), serialized_config, + replace_extra=replace_extra) + + if not new_binary: + raise ValueError("no means to create a binary was provided") + if combined_filename: with open(combined_filename, 'wb') as combined: combined.write(new_binary) @@ -187,13 +202,18 @@ def concatenate(): help="if the firmware file is larger than the location of storage, perhaps because it's " "actually a full board dump, overwrite its config section with the config binary") parser.add_argument('firmware_filename', help=".bin file of a GP2040-CE firmware, probably from a build") - parser.add_argument('config_filename', help=".bin file of a GP2040-CE board's storage section or config w/footer") - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('--usb', action='store_true', help="write the resulting firmware + storage to USB") - group.add_argument('--new-binary-filename', help="output .bin file of the resulting firmware + storage") + user_config_group = parser.add_mutually_exclusive_group(required=True) + user_config_group.add_argument('--binary-user-config-filename', + help=".bin file of a GP2040-CE user config w/footer") + user_config_group.add_argument('--json-user-config-filename', help=".json file of a GP2040-CE user config") + output_group = parser.add_mutually_exclusive_group(required=True) + output_group.add_argument('--usb', action='store_true', help="write the resulting firmware + storage to USB") + output_group.add_argument('--new-binary-filename', help="output .bin file of the resulting firmware + storage") args, _ = parser.parse_known_args() - concatenate_firmware_and_storage_files(args.firmware_filename, args.config_filename, + concatenate_firmware_and_storage_files(args.firmware_filename, + binary_user_config_filename=args.binary_user_config_filename, + json_user_config_filename=args.json_user_config_filename, combined_filename=args.new_binary_filename, usb=args.usb, replace_extra=args.replace_extra) diff --git a/tests/test-files/test-binary-source-of-json-config.bin b/tests/test-files/test-binary-source-of-json-config.bin new file mode 100644 index 0000000000000000000000000000000000000000..edfe31da5f50a81f26d1b0ad699ceca2f1c117af GIT binary patch literal 3770 zcmeHKUuauZ82`R=lis_eOS0&?YZjZ3f`qV@ENpA6rA>oi=!-F+mb}>2PHTm&R5xNN z>s@_lm}27aAq?4yFBW~+5C(l2Muv26E56vLE&hRyy)0vbh0R{Slir(ivaW-Hiij6J z^820h{k!MS_ucqU#=CpE5AQ#4X#d+i2i`h(=s?1s(U=c(7KSLqAqgqSU{)P=j^H>< zvVy}v(|)ih;j)A)s4O4^zDJi^17C-36vBqV{B8|>Ufs|+3AibY&ms}G=J4&ZNX)Hi zKEFj0Tn%-@$8CDK8nZ~6t0@*4;;ucqk>P4NPIl^s#z~&rXcige-c*ZBXp|f15QeEH znr@;wY{E#)R&1^aWMLRafK~(wU?3C`$_N#NSWL_LUeUARm!m&n{Mlp;Q$J&-ExT{{ znUTHYk;%?Nw}JbM_*@xZNG#hSFxnS4F6*?|%23eNu7O{pmka7znEBf^MX;!X2%>cH)_dw0~? zWxQ@0pp+iWUR|Eu@SZBo-z5KD(|m7v-n`+-s-e&dKv7_Df8Xc~_IpBgz*H0f@2su&_0M=iHJYPGF-!H?jdhRL zXi(zUtEO3R-(&#lM<$x3YFm@fZ{UX4a5Q=7hElf){i@%d$0~rBH$RtVY5sn)g)3`3 zN%7!Il%)K4!ZHJBW@ZgHBU$`EenTb|sqpmvid5xzEyHLc?o28v%s`e6Ya=_x9h1(2 zYG7Lt-DPxDq$<52;YA5wlJJs*s}jB@;S~w5O1LKBTN18wtga&%%OYGJmk^c^stDH*RuEPZY6!Ow>IjYaP-IYMP+_pZV3ENk z21^X846ZR)VX(@e#$c$j!{y*D!0({O0)allB5_?nYMIWsph*E~KiFo=h`h6{wKpmZ ztj^0{PYjL-I468i+DVZQU}Q%Yx1e>bKvoL9y~se z$#aqO_$PAUF?k+#4m>{3$zF@UE{D;*xojeSFg`eT^2o6_kB+6MjvjgU_=)$^$B#^n zO>DbS@+R>`;>rCzi$s+K=`R&dUqn}M+iOb|*#Fqs056gDX)sZ`Wm093?tY`BK7oF0 W7p2K$w{Xz|@F^y4AN}R-4}Sust|h+! literal 0 HcmV?d00001 diff --git a/tests/test_builder.py b/tests/test_builder.py index 0dc789f..67ba4ef 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -43,6 +43,27 @@ def test_concatenate_to_file(tmp_path): assert len(content) == 2 * 1024 * 1024 +@with_pb2s +def test_concatenate_user_json_to_file(tmp_path): + """Test that we write a file with firmware + JSON user config as expected.""" + tmp_file = os.path.join(tmp_path, 'concat.bin') + firmware_file = os.path.join(HERE, 'test-files', 'test-firmware.bin') + config_file = os.path.join(HERE, 'test-files', 'test-config.json') + concatenate_firmware_and_storage_files(firmware_file, json_user_config_filename=config_file, + combined_filename=tmp_file) + with open(tmp_file, 'rb') as file: + content = file.read() + assert len(content) == 2 * 1024 * 1024 + + +def test_concatenate_to_file_incomplete_args_is_error(tmp_path): + """Test that we bail properly if we weren't given all the necessary arguments to make a binary.""" + tmp_file = os.path.join(tmp_path, 'concat.bin') + firmware_file = os.path.join(HERE, 'test-files', 'test-firmware.bin') + with pytest.raises(ValueError): + concatenate_firmware_and_storage_files(firmware_file, combined_filename=tmp_file) + + def test_concatenate_to_usb(tmp_path): """Test that we write a file as expected.""" firmware_file = os.path.join(HERE, 'test-files', 'test-firmware.bin') diff --git a/tests/test_commands.py b/tests/test_commands.py index b2cbca1..1297cb6 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -43,14 +43,27 @@ def test_help_flag(): def test_concatenate_invocation(tmpdir): """Test that a normal invocation against a dump works.""" out_filename = os.path.join(tmpdir, 'out.bin') - _ = run(['concatenate', 'tests/test-files/test-firmware.bin', 'tests/test-files/test-storage-area.bin', - '--new-binary-filename', out_filename]) + _ = run(['concatenate', 'tests/test-files/test-firmware.bin', '--binary-user-config-filename', + 'tests/test-files/test-storage-area.bin', '--new-binary-filename', out_filename]) with open(out_filename, 'rb') as out_file, open('tests/test-files/test-storage-area.bin', 'rb') as storage_file: out = out_file.read() storage = storage_file.read() assert out[2080768:2097152] == storage +def test_concatenate_invocation_json(tmpdir): + """Test that a normal invocation with a firmware and a JSON file works.""" + out_filename = os.path.join(tmpdir, 'out.bin') + _ = run(['concatenate', '-P', 'tests/test-files/proto-files', 'tests/test-files/test-firmware.bin', + '--json-user-config-filename', 'tests/test-files/test-config.json', '--new-binary-filename', + out_filename]) + with open(out_filename, 'rb') as out_file, open('tests/test-files/test-binary-source-of-json-config.bin', + 'rb') as storage_file: + out = out_file.read() + storage = storage_file.read() + assert out[2093382:2097152] == storage + + def test_storage_dump_invocation(): """Test that a normal invocation against a dump works.""" result = run(['visualize-storage', '-P', 'tests/test-files/proto-files',