-
Notifications
You must be signed in to change notification settings - Fork 0
/
uf2parse.py
127 lines (106 loc) · 4.04 KB
/
uf2parse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'''
uf2parse.py
parses a uf2 firmware file (filename as first argument),
writes binary payload into file (filename as second argument),
prints binary payload's sequence count, destination address, final size, crc32, sha256
See https://microsoft.github.io/uf2/
'''
import binascii
import sys
import hashlib
import traceback
class UF2:
def __init__(self, filename):
if filename[-4:] == ".uf2":
self.filename = filename
self.f = None
self.index = 0
def open(self):
self.f = open(self.filename, "rb")
def next(self):
answer = self.f.read(512)
self.index += 1
return answer
def parse(self, bytes_512, last_sequence=None):
first_magic = int.from_bytes(bytes_512[0:4], 'little')
second_magic = int.from_bytes(bytes_512[4:8], 'little')
flags = int.from_bytes(bytes_512[8:12], 'little')
address = int.from_bytes(bytes_512[12:16], 'little')
data_size = int.from_bytes(bytes_512[16:20], 'little')
sequence = int.from_bytes(bytes_512[20:24], 'little')
total_blocks = int.from_bytes(bytes_512[24:28], 'little')
fs_bfid_zero = int.from_bytes(bytes_512[28:32], 'little')
data = bytes_512[32:508]
final_magic = int.from_bytes(bytes_512[508:512], 'little')
assert first_magic == 0x0a324655
assert second_magic == 0x9e5d5157
if sequence: assert last_sequence + 1 == sequence
if sequence: assert sequence < total_blocks
assert final_magic == 0x0ab16f30
if data[data_size:-24] != b'\x00' * (476 - 24 - data_size):
print(binascii.hexlify(data))
assert data[data_size:-24] == b'\x00' * (476 - 24 - data_size)
if flags & 0x00000001: # not main flash
return None
if flags & 0x00001000: # file container
pass
if flags & 0x00002000: # familyID present
assert fs_bfid_zero != 0
if flags & 0x00004000: # MD5 checksum present
pass
if flags & 0x00008000: # extension tags present
pass
return (address, data_size, sequence, total_blocks, fs_bfid_zero, data)
def close(self):
if self.f:
self.f.close()
self.f = None
def parse(uf2_filename, raw_filename = None):
print(f"UF2: parsing '{uf2_filename}'")
if raw_filename:
print(f" saving payload as '{raw_filename}'...")
file_size = 0
file_hash = hashlib.sha256()
file_crc32 = binascii.crc32(b'')
try:
uf2 = UF2(uf2_filename)
uf2.open()
if raw_filename:
raw = open(raw_filename, "wb")
seq, last_seq, expected_addy = None, -1, None
while True:
b512 = uf2.next()
addy, size, seq, tot, fsize, data = uf2.parse(b512, seq)
if expected_addy: assert addy == expected_addy
assert last_seq + 1 == seq
if raw_filename:
raw.write(data[:size])
file_size += size
file_hash.update(data[:size])
file_crc32 = binascii.crc32(data[:size], file_crc32)
if seq + 1 == tot:
break
last_seq = seq
expected_addy = addy + size
except Exception as err:
traceback.print_exception(err)
finally:
uf2.close()
if raw_filename:
raw.close()
end_addy = addy + size
start_addy = end_addy - file_size
print(f" crude validation of {tot} 512 byte sequences")
print(f" destined for flash address {hex(start_addy)} to {hex(end_addy)}-1")
print(" file size: {} bytes, {} {}".format(file_size, hex(file_size),
", {} 4K blocks".format(file_size // 4096) if file_size % 4096 == 0 else ""
))
print(f" crc32: {file_crc32}, {hex(file_crc32)}")
print(f" sha256: {file_hash.hexdigest()}")
if __name__ == '__main__':
uf2_filename = sys.argv[1]
if len(sys.argv) > 2:
raw_filename = sys.argv[2]
else:
raw_filename = None
parse(uf2_filename, raw_filename)