methods to add/retrieve board config to binary
with this, the combining methods can combine both a board and user config into one binary, which is a step closer to having a command that does it, and then we can start removing BoardConfig.h from the firmware build and replace it with patched binaries. to test it, another retrieval method was added too, and this will probably be used for more dump commands or a better info or something like that Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
parent
bc64a6531b
commit
ef71db6f5e
@ -31,19 +31,29 @@ class FirmwareLengthError(ValueError):
|
|||||||
"""Exception raised when the firmware is too large to fit the known storage location."""
|
"""Exception raised when the firmware is too large to fit the known storage location."""
|
||||||
|
|
||||||
|
|
||||||
def combine_firmware_and_config(firmware_binary: bytearray, config_binary: bytearray,
|
def combine_firmware_and_config(firmware_binary: bytearray, board_config_binary: bytearray,
|
||||||
replace_extra: bool = False) -> bytearray:
|
user_config_binary: bytearray, replace_extra: bool = False) -> bytearray:
|
||||||
"""Given firmware and config binaries, combine the two to one, with proper offsets for GP2040-CE.
|
"""Given firmware and board and/or user config binaries, combine to one binary with proper offsets for GP2040-CE.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
firmware_binary: binary data of the raw GP2040-CE firmware, probably but not necessarily unpadded
|
firmware_binary: binary data of the raw GP2040-CE firmware, probably but not necessarily unpadded
|
||||||
config_binary: binary data of board config + footer, possibly padded to be a full storage section
|
board_config_binary: binary data of board config + footer, possibly padded to be a full storage section
|
||||||
|
user_config_binary: binary data of user config + footer, possibly padded to be a full storage section
|
||||||
replace_extra: if larger than normal firmware files should have their overage replaced
|
replace_extra: if larger than normal firmware files should have their overage replaced
|
||||||
Returns:
|
Returns:
|
||||||
the resulting correctly-offset binary suitable for a GP2040-CE board
|
the resulting correctly-offset binary suitable for a GP2040-CE board
|
||||||
"""
|
"""
|
||||||
return (pad_binary_up_to_user_config(firmware_binary, or_truncate=replace_extra) +
|
if not board_config_binary and not user_config_binary:
|
||||||
pad_config_to_storage_size(config_binary))
|
raise ValueError("at least one config binary must be provided!")
|
||||||
|
|
||||||
|
combined = copy.copy(firmware_binary)
|
||||||
|
if board_config_binary:
|
||||||
|
combined = (pad_binary_up_to_board_config(combined, or_truncate=replace_extra) +
|
||||||
|
pad_config_to_storage_size(board_config_binary))
|
||||||
|
if user_config_binary:
|
||||||
|
combined = (pad_binary_up_to_user_config(combined, or_truncate=replace_extra) +
|
||||||
|
pad_config_to_storage_size(user_config_binary))
|
||||||
|
return combined
|
||||||
|
|
||||||
|
|
||||||
def concatenate_firmware_and_storage_files(firmware_filename: str, binary_user_config_filename: Optional[str] = None,
|
def concatenate_firmware_and_storage_files(firmware_filename: str, binary_user_config_filename: Optional[str] = None,
|
||||||
@ -62,13 +72,13 @@ def concatenate_firmware_and_storage_files(firmware_filename: str, binary_user_c
|
|||||||
new_binary = None
|
new_binary = None
|
||||||
if binary_user_config_filename:
|
if binary_user_config_filename:
|
||||||
with open(firmware_filename, 'rb') as firmware, open(binary_user_config_filename, 'rb') as storage:
|
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()),
|
new_binary = combine_firmware_and_config(bytearray(firmware.read()), None, bytearray(storage.read()),
|
||||||
replace_extra=replace_extra)
|
replace_extra=replace_extra)
|
||||||
elif json_user_config_filename:
|
elif json_user_config_filename:
|
||||||
with open(firmware_filename, 'rb') as firmware, open(json_user_config_filename, 'r') as json_file:
|
with open(firmware_filename, 'rb') as firmware, open(json_user_config_filename, 'r') as json_file:
|
||||||
config = get_config_from_json(json_file.read())
|
config = get_config_from_json(json_file.read())
|
||||||
serialized_config = serialize_config_with_footer(config)
|
serialized_config = serialize_config_with_footer(config)
|
||||||
new_binary = combine_firmware_and_config(bytearray(firmware.read()), serialized_config,
|
new_binary = combine_firmware_and_config(bytearray(firmware.read()), None, serialized_config,
|
||||||
replace_extra=replace_extra)
|
replace_extra=replace_extra)
|
||||||
|
|
||||||
if not new_binary:
|
if not new_binary:
|
||||||
@ -162,7 +172,7 @@ def replace_config_in_binary(board_binary: bytearray, config_binary: bytearray)
|
|||||||
"""
|
"""
|
||||||
if len(board_binary) < USER_CONFIG_BINARY_LOCATION + STORAGE_SIZE:
|
if len(board_binary) < USER_CONFIG_BINARY_LOCATION + STORAGE_SIZE:
|
||||||
# this is functionally the same, since this doesn't sanity check the firmware
|
# this is functionally the same, since this doesn't sanity check the firmware
|
||||||
return combine_firmware_and_config(board_binary, config_binary)
|
return combine_firmware_and_config(board_binary, None, config_binary)
|
||||||
else:
|
else:
|
||||||
new_binary = bytearray(copy.copy(board_binary))
|
new_binary = bytearray(copy.copy(board_binary))
|
||||||
new_config = pad_config_to_storage_size(config_binary)
|
new_config = pad_config_to_storage_size(config_binary)
|
||||||
|
@ -194,6 +194,19 @@ def get_storage_section(content: bytes, address: int) -> bytes:
|
|||||||
return content[address:(address + STORAGE_SIZE)]
|
return content[address:(address + STORAGE_SIZE)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_board_storage_section(content: bytes) -> bytes:
|
||||||
|
"""Get the board storage area from what should be a whole board GP2040-CE dump.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: bytes of a GP2040-CE whole board dump
|
||||||
|
Returns:
|
||||||
|
the presumed storage section from the binary
|
||||||
|
Raises:
|
||||||
|
ConfigLengthError: if the provided bytes don't appear to have a storage section
|
||||||
|
"""
|
||||||
|
return get_storage_section(content, BOARD_CONFIG_BINARY_LOCATION)
|
||||||
|
|
||||||
|
|
||||||
def get_user_storage_section(content: bytes) -> bytes:
|
def get_user_storage_section(content: bytes) -> bytes:
|
||||||
"""Get the user storage area from what should be a whole board GP2040-CE dump.
|
"""Get the user storage area from what should be a whole board GP2040-CE dump.
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ from gp2040ce_bintools.builder import (FirmwareLengthError, combine_firmware_and
|
|||||||
concatenate_firmware_and_storage_files, get_gp2040ce_from_usb,
|
concatenate_firmware_and_storage_files, get_gp2040ce_from_usb,
|
||||||
pad_binary_up_to_board_config, pad_binary_up_to_user_config,
|
pad_binary_up_to_board_config, pad_binary_up_to_user_config,
|
||||||
replace_config_in_binary, write_new_config_to_filename, write_new_config_to_usb)
|
replace_config_in_binary, write_new_config_to_filename, write_new_config_to_usb)
|
||||||
from gp2040ce_bintools.storage import (get_config, get_config_footer, get_user_storage_section,
|
from gp2040ce_bintools.storage import (get_board_storage_section, get_config, get_config_footer,
|
||||||
serialize_config_with_footer)
|
get_user_storage_section, serialize_config_with_footer)
|
||||||
|
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
@ -96,33 +96,63 @@ def test_padding_firmware_to_board(firmware_binary):
|
|||||||
assert len(padded) == 2080768 - (16 * 1024)
|
assert len(padded) == 2080768 - (16 * 1024)
|
||||||
|
|
||||||
|
|
||||||
def test_firmware_plus_storage(firmware_binary, storage_dump):
|
def test_firmware_plus_storage_section(firmware_binary, storage_dump):
|
||||||
"""Test that combining firmware and storage produces a valid combined binary."""
|
"""Test that combining firmware and storage produces a valid combined binary."""
|
||||||
whole_board = combine_firmware_and_config(firmware_binary, storage_dump)
|
whole_board = combine_firmware_and_config(firmware_binary, None, storage_dump)
|
||||||
# if this is valid, we should be able to find the storage and footer again
|
# if this is valid, we should be able to find the storage and footer again
|
||||||
storage = get_user_storage_section(whole_board)
|
storage = get_user_storage_section(whole_board)
|
||||||
footer_size, _, _ = get_config_footer(storage)
|
footer_size, _, _ = get_config_footer(storage)
|
||||||
assert footer_size == 3309
|
assert footer_size == 3309
|
||||||
|
|
||||||
|
|
||||||
def test_firmware_plus_config_binary(firmware_binary, config_binary):
|
def test_firmware_plus_user_config_binary(firmware_binary, config_binary):
|
||||||
"""Test that combining firmware and storage produces a valid combined binary."""
|
"""Test that combining firmware and user config produces a valid combined binary."""
|
||||||
whole_board = combine_firmware_and_config(firmware_binary, config_binary)
|
whole_board = combine_firmware_and_config(firmware_binary, None, config_binary)
|
||||||
# if this is valid, we should be able to find the storage and footer again
|
# if this is valid, we should be able to find the storage and footer again
|
||||||
storage = get_user_storage_section(whole_board)
|
storage = get_user_storage_section(whole_board)
|
||||||
footer_size, _, _ = get_config_footer(storage)
|
footer_size, _, _ = get_config_footer(storage)
|
||||||
assert footer_size == 3309
|
assert footer_size == 3309
|
||||||
|
|
||||||
|
|
||||||
def test_chunky_firmware_plus_config_binary(config_binary):
|
def test_chunky_firmware_plus_user_config_binary(config_binary):
|
||||||
"""Test that combining giant firmware and storage produces a valid combined binary."""
|
"""Test that combining giant firmware and storage produces a valid combined binary."""
|
||||||
whole_board = combine_firmware_and_config(bytearray(b'\x00' * 4 * 1024 * 1024), config_binary, replace_extra=True)
|
whole_board = combine_firmware_and_config(bytearray(b'\x00' * 4 * 1024 * 1024), None, config_binary,
|
||||||
|
replace_extra=True)
|
||||||
# if this is valid, we should be able to find the storage and footer again
|
# if this is valid, we should be able to find the storage and footer again
|
||||||
storage = get_user_storage_section(whole_board)
|
storage = get_user_storage_section(whole_board)
|
||||||
footer_size, _, _ = get_config_footer(storage)
|
footer_size, _, _ = get_config_footer(storage)
|
||||||
assert footer_size == 3309
|
assert footer_size == 3309
|
||||||
|
|
||||||
|
|
||||||
|
def test_firmware_plus_board_config_binary(firmware_binary, config_binary):
|
||||||
|
"""Test that combining firmware and board config produces a valid combined binary."""
|
||||||
|
almost_whole_board = combine_firmware_and_config(firmware_binary, config_binary, None)
|
||||||
|
assert len(almost_whole_board) == (2 * 1024 * 1024) - (16 * 1024)
|
||||||
|
# if this is valid, we should be able to find the storage and footer again
|
||||||
|
storage = get_board_storage_section(almost_whole_board)
|
||||||
|
footer_size, _, _ = get_config_footer(storage)
|
||||||
|
assert footer_size == 3309
|
||||||
|
|
||||||
|
|
||||||
|
def test_firmware_plus_board_and_user_config_binary(firmware_binary, config_binary):
|
||||||
|
"""Test that combining firmware and both board and user configs produces a valid combined binary."""
|
||||||
|
whole_board = combine_firmware_and_config(firmware_binary, config_binary, config_binary)
|
||||||
|
assert len(whole_board) == 2 * 1024 * 1024
|
||||||
|
# if this is valid, we should be able to find the storage and footer again
|
||||||
|
storage = get_board_storage_section(whole_board)
|
||||||
|
footer_size, _, _ = get_config_footer(storage)
|
||||||
|
assert footer_size == 3309
|
||||||
|
storage = get_user_storage_section(whole_board)
|
||||||
|
footer_size, _, _ = get_config_footer(storage)
|
||||||
|
assert footer_size == 3309
|
||||||
|
|
||||||
|
|
||||||
|
def test_combine_must_get_at_least_one_config(firmware_binary):
|
||||||
|
"""Test that we error if we are asked to combine with nothing to combine."""
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
combine_firmware_and_config(firmware_binary, None, None)
|
||||||
|
|
||||||
|
|
||||||
def test_replace_config_in_binary(config_binary):
|
def test_replace_config_in_binary(config_binary):
|
||||||
"""Test that a config binary is placed in the storage location of a source binary to overwrite."""
|
"""Test that a config binary is placed in the storage location of a source binary to overwrite."""
|
||||||
whole_board = replace_config_in_binary(bytearray(b'\x00' * 3 * 1024 * 1024), config_binary)
|
whole_board = replace_config_in_binary(bytearray(b'\x00' * 3 * 1024 * 1024), config_binary)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user