gp2040ce-binary-tools/gp2040ce_bintools/storage.py

84 lines
2.6 KiB
Python
Raw Normal View History

2023-06-20 12:59:47 -05:00
"""Interact with the protobuf config from a picotool flash dump of a GP2040-CE board."""
import argparse
import logging
from gp2040ce_bintools import core_parser, get_config_pb2
logger = logging.getLogger(__name__)
FOOTER_SIZE = 12
FOOTER_MAGIC = b'\x65\xe3\xf1\xd2'
###############
# LIB METHODS #
###############
def get_config(content: bytes) -> dict:
"""Read the config from a GP2040-CE storage section.
Args:
content: bytes from a GP2040-CE board's storage section
Returns:
the parsed configuration
"""
size, _, _ = get_config_footer(content)
config_pb2 = get_config_pb2()
config = config_pb2.Config()
config.ParseFromString(content[-(size + FOOTER_SIZE):-FOOTER_SIZE])
logger.debug("parsed: %s", config)
return config
def get_config_footer(content: bytes) -> tuple[int, int, str]:
"""Confirm and retrieve the config footer from a series of bytes of GP2040-CE storage.
Args:
content: bytes from a GP2040-CE board's storage section
Returns:
the discovered config size, config CRC, and magic from the config footer
Raises:
ValueError: if the provided bytes are not a config footer
"""
# last 12 bytes are the footer
if len(content) < FOOTER_SIZE:
raise ValueError("provided content is not large enough to have a config footer!")
footer = content[-FOOTER_SIZE:]
if footer[-4:] != FOOTER_MAGIC:
raise ValueError("content's magic is not as expected!")
config_size = int.from_bytes(reversed(footer[:4]), 'big')
config_crc = int.from_bytes(reversed(footer[4:8]), 'big')
config_magic = f'0x{footer[8:12].hex()}'
# one last sanity check
if len(content) < config_size + FOOTER_SIZE:
raise ValueError("provided content is not large enough according to the config footer!")
logger.debug("detected footer (size:%s, crc:%s, magic:%s", config_size, config_crc, config_magic)
return config_size, config_crc, config_magic
############
# COMMANDS #
############
def visualize():
"""Print the contents of GP2040-CE's storage."""
parser = argparse.ArgumentParser(
description="Read the configuration section from a dump of a GP2040-CE board's storage section and print out "
"its contents.",
parents=[core_parser],
)
parser.add_argument('filename', help=".bin file of a GP2040-CE board's storage section, bytes 101FE000-10200000")
args, _ = parser.parse_known_args()
with open(args.filename, 'rb') as dump:
content = dump.read()
config = get_config(content)
print(config)