Files
littlefs/scripts/dbgblock.py
Christopher Haster 8eea06286f Added optionally crc32c calculation to dbgblock.py
This is useful for debugging checksum mismatches on disk.

And since dbgblock.py has some relatively flexible options for slicing
the disk, this can be used to find the checksum of any on-disk data
pretty easily.
2023-12-17 22:16:41 -06:00

188 lines
5.1 KiB
Python
Executable File

#!/usr/bin/env python3
import os
# some ways of block geometry representations
# 512 -> 512
# 512x16 -> (512, 16)
# 0x200x10 -> (512, 16)
def bdgeom(s):
s = s.strip()
b = 10
if s.startswith('0x') or s.startswith('0X'):
s = s[2:]
b = 16
elif s.startswith('0o') or s.startswith('0O'):
s = s[2:]
b = 8
elif s.startswith('0b') or s.startswith('0B'):
s = s[2:]
b = 2
if 'x' in s:
s, s_ = s.split('x', 1)
return (int(s, b), int(s_, b))
else:
return int(s, b)
# parse some rbyd addr encodings
# 0xa -> [0xa]
# 0xa.c -> [(0xa, 0xc)]
# 0x{a,b} -> [0xa, 0xb]
# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)]
def rbydaddr(s):
s = s.strip()
b = 10
if s.startswith('0x') or s.startswith('0X'):
s = s[2:]
b = 16
elif s.startswith('0o') or s.startswith('0O'):
s = s[2:]
b = 8
elif s.startswith('0b') or s.startswith('0B'):
s = s[2:]
b = 2
trunk = None
if '.' in s:
s, s_ = s.split('.', 1)
trunk = int(s_, b)
if s.startswith('{') and '}' in s:
ss = s[1:s.find('}')].split(',')
else:
ss = [s]
addr = []
for s in ss:
if trunk is not None:
addr.append((int(s, b), trunk))
else:
addr.append(int(s, b))
return addr
def xxd(data, width=16):
for i in range(0, len(data), width):
yield '%-*s %-*s' % (
3*width,
' '.join('%02x' % b for b in data[i:i+width]),
width,
''.join(
b if b >= ' ' and b <= '~' else '.'
for b in map(chr, data[i:i+width])))
def crc32c(data, crc=0):
crc ^= 0xffffffff
for b in data:
crc ^= b
for j in range(8):
crc = (crc >> 1) ^ ((crc & 1) * 0x82f63b78)
return 0xffffffff ^ crc
def main(disk, block=None, *,
block_size=None,
block_count=None,
off=None,
size=None,
cksum=False):
# is bd geometry specified?
if isinstance(block_size, tuple):
block_size, block_count_ = block_size
if block_count is None:
block_count = block_count_
# flatten block, default to block 0
if not block:
block = [0]
if len(block) > 1:
print("error: more than one block address?",
file=sys.stderr)
sys.exit(-1)
block = block[0]
with open(disk, 'rb') as f:
# if block_size is omitted, assume the block device is one big block
if block_size is None:
f.seek(0, os.SEEK_END)
block_size = f.tell()
# block may also encode an offset
block, off, size = (
block[0] if isinstance(block, tuple) else block,
off[0] if isinstance(off, tuple)
else off if off is not None
else size[0] if isinstance(size, tuple) and len(size) > 1
else block[1] if isinstance(block, tuple)
else None,
size[1] - size[0] if isinstance(size, tuple) and len(size) > 1
else size[0] if isinstance(size, tuple)
else size if size is not None
else off[1] - off[0] if isinstance(off, tuple) and len(off) > 1
else block_size)
# print the header
print('block %s, size %d' % (
'0x%x.%x' % (block, off)
if off is not None
else '0x%x' % block,
size))
# read the block
f.seek((block * block_size) + (off or 0))
data = f.read(size)
# render the hex view
for o, line in enumerate(xxd(data)):
print('%08x: %s' % ((off or 0) + 16*o, line))
# render the checksum if requested
if cksum:
cksum = crc32c(data)
print('%8s: %08x' % ('crc32c', cksum))
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser(
description="Debug block devices.",
allow_abbrev=False)
parser.add_argument(
'disk',
help="File containing the block device.")
parser.add_argument(
'block',
nargs='?',
type=rbydaddr,
help="Block address.")
parser.add_argument(
'-B', '--block-size',
type=bdgeom,
help="Block size/geometry in bytes.")
parser.add_argument(
'--block-count',
type=lambda x: int(x, 0),
help="Block count in blocks.")
parser.add_argument(
'--off',
type=lambda x: tuple(
int(x, 0) if x.strip() else None
for x in x.split(',')),
help="Show a specific offset, may be a range.")
parser.add_argument(
'-n', '--size',
type=lambda x: tuple(
int(x, 0) if x.strip() else None
for x in x.split(',')),
help="Show this many bytes, may be a range.")
parser.add_argument(
'-x', '--cksum', '--crc32c',
action='store_true',
help="Calculate and show the crc32c of the data.")
sys.exit(main(**{k: v
for k, v in vars(parser.parse_intermixed_args()).items()
if v is not None}))