command to dump whole GP2040-CE image from USB
This commit is contained in:
parent
ef842032f1
commit
b7bb437ae8
11
README.md
11
README.md
|
@ -76,6 +76,17 @@ Sample usage:
|
||||||
% dump-config -P ~/proj/GP2040-CE/proto -P ~/proj/GP2040-CE/lib/nanopb/generator/proto --filename `date +%Y%m%d`-config-backup.bin
|
% dump-config -P ~/proj/GP2040-CE/proto -P ~/proj/GP2040-CE/lib/nanopb/generator/proto --filename `date +%Y%m%d`-config-backup.bin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### dump-gp2040ce
|
||||||
|
|
||||||
|
`dump-gp2040ce` replaces the need for picotool in order to make a copy of a board's full GP2040-CE image as a binary file.
|
||||||
|
This could be used with the other tools, or just to keep a backup.
|
||||||
|
|
||||||
|
Sample usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
% dump-gp2040ce `date +%Y%m%d`-backup.bin
|
||||||
|
```
|
||||||
|
|
||||||
### visualize-storage
|
### visualize-storage
|
||||||
|
|
||||||
`visualize-storage` reads a GP2040-CE board's configuration, either over USB or from a dump of the board's flash
|
`visualize-storage` reads a GP2040-CE board's configuration, either over USB or from a dump of the board's flash
|
||||||
|
|
|
@ -6,12 +6,15 @@ import logging
|
||||||
from google.protobuf.message import Message
|
from google.protobuf.message import Message
|
||||||
|
|
||||||
from gp2040ce_bintools import core_parser
|
from gp2040ce_bintools import core_parser
|
||||||
from gp2040ce_bintools.pico import write
|
from gp2040ce_bintools.pico import get_bootsel_endpoints, read, write
|
||||||
from gp2040ce_bintools.storage import (STORAGE_BINARY_LOCATION, STORAGE_MEMORY_ADDRESS, STORAGE_SIZE,
|
from gp2040ce_bintools.storage import (STORAGE_BINARY_LOCATION, STORAGE_MEMORY_ADDRESS, STORAGE_SIZE,
|
||||||
pad_config_to_storage_size, serialize_config_with_footer)
|
pad_config_to_storage_size, serialize_config_with_footer)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
GP2040CE_START_ADDRESS = 0x10000000
|
||||||
|
GP2040CE_SIZE = 2 * 1024 * 1024
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# LIBRARY ITEMS #
|
# LIBRARY ITEMS #
|
||||||
|
@ -48,6 +51,20 @@ def concatenate_firmware_and_storage_files(firmware_filename: str, storage_filen
|
||||||
combined.write(new_binary)
|
combined.write(new_binary)
|
||||||
|
|
||||||
|
|
||||||
|
def get_gp2040ce_from_usb() -> tuple[bytes, object, object]:
|
||||||
|
"""Read the firmware + config sections from a USB device.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the bytes from the board, along with the USB out and in endpoints for reference
|
||||||
|
"""
|
||||||
|
# open the USB device and get the config
|
||||||
|
endpoint_out, endpoint_in = get_bootsel_endpoints()
|
||||||
|
logger.debug("reading DEVICE ID %s:%s, bus %s, address %s", hex(endpoint_out.device.idVendor),
|
||||||
|
hex(endpoint_out.device.idProduct), endpoint_out.device.bus, endpoint_out.device.address)
|
||||||
|
content = read(endpoint_out, endpoint_in, GP2040CE_START_ADDRESS, GP2040CE_SIZE)
|
||||||
|
return content, endpoint_out, endpoint_in
|
||||||
|
|
||||||
|
|
||||||
def pad_firmware_up_to_storage(firmware: bytes) -> bytearray:
|
def pad_firmware_up_to_storage(firmware: bytes) -> bytearray:
|
||||||
"""Provide a copy of the firmware padded with zero bytes up to the provided position.
|
"""Provide a copy of the firmware padded with zero bytes up to the provided position.
|
||||||
|
|
||||||
|
@ -125,8 +142,11 @@ def write_new_config_to_usb(config: Message, endpoint_out: object, endpoint_in:
|
||||||
serialized = serialize_config_with_footer(config)
|
serialized = serialize_config_with_footer(config)
|
||||||
# we don't write the whole area, just the minimum from the end of the storage section
|
# we don't write the whole area, just the minimum from the end of the storage section
|
||||||
# nevertheless, the USB device needs writes to start at 256 byte boundaries
|
# nevertheless, the USB device needs writes to start at 256 byte boundaries
|
||||||
|
logger.debug("serialized: %s", serialized)
|
||||||
padding = 256 - (len(serialized) % 256)
|
padding = 256 - (len(serialized) % 256)
|
||||||
|
logger.debug("length: %s with %s bytes of padding", len(serialized), padding)
|
||||||
binary = bytearray(b'\x00' * padding) + serialized
|
binary = bytearray(b'\x00' * padding) + serialized
|
||||||
|
logger.debug("binary for writing: %s", binary)
|
||||||
write(endpoint_out, endpoint_in, STORAGE_MEMORY_ADDRESS + (STORAGE_SIZE - len(binary)), binary)
|
write(endpoint_out, endpoint_in, STORAGE_MEMORY_ADDRESS + (STORAGE_SIZE - len(binary)), binary)
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,3 +168,17 @@ def concatenate():
|
||||||
|
|
||||||
args, _ = parser.parse_known_args()
|
args, _ = parser.parse_known_args()
|
||||||
concatenate_firmware_and_storage_files(args.firmware_filename, args.config_filename, args.new_binary_filename)
|
concatenate_firmware_and_storage_files(args.firmware_filename, args.config_filename, args.new_binary_filename)
|
||||||
|
|
||||||
|
|
||||||
|
def dump_gp2040ce():
|
||||||
|
"""Copy the whole GP2040-CE section off of a BOOTSEL mode board."""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Read the GP2040-CE firmware + storage section off of a connected USB RP2040 in BOOTSEL mode.",
|
||||||
|
parents=[core_parser],
|
||||||
|
)
|
||||||
|
parser.add_argument('binary_filename', help="output .bin file of the resulting firmware + storage")
|
||||||
|
|
||||||
|
args, _ = parser.parse_known_args()
|
||||||
|
content, _, _ = get_gp2040ce_from_usb()
|
||||||
|
with open(args.binary_filename, 'wb') as out_file:
|
||||||
|
out_file.write(content)
|
||||||
|
|
|
@ -35,6 +35,7 @@ dev = ["bandit", "decorator", "flake8", "flake8-blind-except", "flake8-builtins"
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
concatenate = "gp2040ce_bintools.builder:concatenate"
|
concatenate = "gp2040ce_bintools.builder:concatenate"
|
||||||
dump-config = "gp2040ce_bintools.storage:dump_config"
|
dump-config = "gp2040ce_bintools.storage:dump_config"
|
||||||
|
dump-gp2040ce = "gp2040ce_bintools.builder:dump_gp2040ce"
|
||||||
edit-config = "gp2040ce_bintools.gui:edit_config"
|
edit-config = "gp2040ce_bintools.gui:edit_config"
|
||||||
visualize-storage = "gp2040ce_bintools.storage:visualize"
|
visualize-storage = "gp2040ce_bintools.storage:visualize"
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@ import pytest
|
||||||
from decorator import decorator
|
from decorator import decorator
|
||||||
|
|
||||||
from gp2040ce_bintools import get_config_pb2
|
from gp2040ce_bintools import get_config_pb2
|
||||||
from gp2040ce_bintools.builder import (FirmwareLengthError, combine_firmware_and_config, pad_firmware_up_to_storage,
|
from gp2040ce_bintools.builder import (FirmwareLengthError, combine_firmware_and_config, get_gp2040ce_from_usb,
|
||||||
replace_config_in_binary, write_new_config_to_filename, write_new_config_to_usb)
|
pad_firmware_up_to_storage, 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_storage_section, serialize_config_with_footer
|
from gp2040ce_bintools.storage import get_config, get_config_footer, get_storage_section, serialize_config_with_footer
|
||||||
|
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
@ -150,3 +151,19 @@ def test_write_new_config_to_usb(config_binary):
|
||||||
padded_serialized = bytearray(b'\x00' * 4) + serialized
|
padded_serialized = bytearray(b'\x00' * 4) + serialized
|
||||||
assert mock_write.call_args.args[2] % 256 == 0
|
assert mock_write.call_args.args[2] % 256 == 0
|
||||||
assert mock_write.call_args.args[3] == padded_serialized
|
assert mock_write.call_args.args[3] == padded_serialized
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_gp2040ce_from_usb():
|
||||||
|
"""Test we attempt to read from the proper location over USB."""
|
||||||
|
mock_out = mock.MagicMock()
|
||||||
|
mock_out.device.idVendor = 0xbeef
|
||||||
|
mock_out.device.idProduct = 0xcafe
|
||||||
|
mock_out.device.bus = 1
|
||||||
|
mock_out.device.address = 2
|
||||||
|
mock_in = mock.MagicMock()
|
||||||
|
with mock.patch('gp2040ce_bintools.builder.get_bootsel_endpoints', return_value=(mock_out, mock_in)) as mock_get:
|
||||||
|
with mock.patch('gp2040ce_bintools.builder.read') as mock_read:
|
||||||
|
config, _, _ = get_gp2040ce_from_usb()
|
||||||
|
|
||||||
|
mock_get.assert_called_once()
|
||||||
|
mock_read.assert_called_with(mock_out, mock_in, 0x10000000, 2 * 1024 * 1024)
|
||||||
|
|
Loading…
Reference in New Issue