improve storage parsing by using footer for sanity
magic is checked for sanity, and then the size is used for reading the whole section rather than simply ignoring null bytes closes #2
This commit is contained in:
parent
8404ec0d54
commit
0c7ed1fcea
|
@ -112,13 +112,8 @@ forcedSetupOptions {
|
|||
|
||||
### Dumping the storage section
|
||||
|
||||
The storage section of a GP2040-CE board starts at `0x101FE000`. A current limitation of the **visualize-storage** tool
|
||||
is that it can only read the Protobuf serialized data, not the footer that is also used as part of the storage engine.
|
||||
As such, currently, the binary is expected to be truncated slightly. This will be improved to read the storage footer in
|
||||
the near future, but for now, know your `picotool` invocation will be slightly different.
|
||||
|
||||
To dump your board's storage:
|
||||
The storage section of a GP2040-CE board is a reserved 8 KB starting at `0x101FE000`. To dump your board's storage:
|
||||
|
||||
```
|
||||
% picotool save -r 101FE000 101FFFF4 memory.bin
|
||||
% picotool save -r 101FE000 10200000 memory.bin
|
||||
```
|
||||
|
|
|
@ -1,35 +1,73 @@
|
|||
"""Interact with the protobuf config from a picotool flash dump of a GP2040-CE board."""
|
||||
import argparse
|
||||
import pprint
|
||||
import logging
|
||||
|
||||
from gp2040ce_bintools import core_parser, get_config_pb2
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_config(filename):
|
||||
"""Load the protobuf section of an flash and display the contents."""
|
||||
with open(filename, 'rb') as dump:
|
||||
# read off the unused space
|
||||
while True:
|
||||
byte = dump.read(1)
|
||||
if byte != b'\x00':
|
||||
break
|
||||
content = byte + dump.read()
|
||||
FOOTER_SIZE = 12
|
||||
FOOTER_MAGIC = b'\x65\xe3\xf1\xd2'
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
config.ParseFromString(content[-(size+FOOTER_SIZE):-FOOTER_SIZE])
|
||||
logger.debug("parsed: %s", config)
|
||||
return config
|
||||
|
||||
|
||||
def visualize():
|
||||
"""Pretty print the contents of GP2040-CE's storage."""
|
||||
"""Print the contents of GP2040-CE's storage."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Read a the configuration storage section from a GP2040-CE board dump and print out its contents.",
|
||||
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-101FFFF4 "
|
||||
"(e.g. picotool save -r 101FE000 101FFFF4 memory.bin")
|
||||
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(args.filename)
|
||||
pprint.pprint(config)
|
||||
config = get_config(content)
|
||||
print(config)
|
||||
|
|
Loading…
Reference in New Issue