-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbl602-flasher.py
235 lines (201 loc) · 6.52 KB
/
bl602-flasher.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
from serial import Serial
from tqdm import tqdm
import binascii
import hashlib
import struct
import time
import sys
import os
def if_read(ser, data_len):
data = bytearray(0)
received = 0
while received < data_len:
tmp = ser.read(data_len - received)
if len(tmp) == 0:
break
else:
data += tmp
received += len(tmp)
if len(data) != data_len:
return (0, data)
return (1, data)
def reset(ser):
ser.setRTS(0)
time.sleep(0.2)
reset_cnt = 2
while reset_cnt > 0:
ser.setRTS(1)
time.sleep(0.005)
ser.setRTS(0)
time.sleep(0.1)
ser.setRTS(1)
time.sleep(0.005)
ser.setRTS(0)
time.sleep(0.005)
reset_cnt -= 1
def handshake(ser):
ser.setRTS(1)
time.sleep(0.2)
ser.setRTS(0)
time.sleep(0.05)
ser.setRTS(1)
ser.setDTR(1)
time.sleep(0.1)
ser.setDTR(0)
time.sleep(0.1)
def expect_ok(ser):
data = ser.read(2)
if len(data) < 2:
raise Exception('No answer')
if data[0] != 0x4f or data[1] != 0x4b:
err = ser.read(2)
raise ValueError(binascii.hexlify(err))
def expect_data(ser):
expect_ok(ser)
len = ser.read(2)
len = struct.unpack('<h', len)[0]
data = ser.read(len)
return data
def cmd_load_seg_header(ser, file):
header = file.read(0x10)
ser.write(b'\x17\x00\x10\x00' + header)
data = expect_data(ser)
seg_addr, seg_len = struct.unpack('<II', data[0:8])
print(f'{seg_len} bytes @ {hex(seg_addr)}')
return seg_len
def cmd_load_seg_data(ser, data):
ser.write(b'\x18\x00' + struct.pack('<H', len(data)) + data)
expect_ok(ser)
def cmd_load_boot_header(ser, file):
header = file.read(0xb0)
ser.write(b'\x11\x00\xb0\x00' + header)
expect_ok(ser)
def cmd_check_image(ser):
ser.write(b'\x19\x00\x00\x00')
expect_ok(ser)
def cmd_run_image(ser):
ser.write(b'\x1a\x00\x00\x00')
expect_ok(ser)
def load_image(ser, file):
image = open(file, 'rb')
cmd_load_boot_header(ser, image)
total = cmd_load_seg_header(ser, image)
sent = 0
with tqdm(total=total, unit='byte', unit_scale=True) as pbar:
while sent != total:
chunk = image.read(min(total-sent, 4080))
cmd_load_seg_data(ser, chunk)
sent = sent + len(chunk)
pbar.update(len(chunk))
cmd_check_image(ser)
cmd_run_image(ser)
def empty_buffer(ser):
timeout = ser.timeout
ser.timeout = 0.1
if_read(ser, 10000)
ser.timeout = timeout
def send_sync(ser):
empty_buffer(ser)
ser.write(b'\x55' * int(0.006 * ser.baudrate / 10))
expect_ok(ser)
def efl_write_cmd(ser, id, payload = b''):
plen = len(payload)
plen_data = struct.pack('<h', plen)
checksum = struct.pack('<h', sum(plen_data + payload) & 0xff)[0:1]
data = bytes([id]) + checksum + plen_data + payload
ser.write(data)
def efl_cmd_read_memory(ser, addr):
# there is a length parameter here but it doesn't seem to work correctly
efl_write_cmd(ser, 0x51, struct.pack('<II', addr, 0x4))
return expect_data(ser)
def efl_cmd_write_memory(ser, addr, data):
efl_write_cmd(ser, 0x50, struct.pack('<I', len(data)) + data)
expect_ok(ser)
def efl_cmd_read_jid(ser):
efl_write_cmd(ser, 0x36)
return expect_data(ser)
def efl_cmd_flash_erase(ser, addr, len):
end_addr = addr + len - 1
efl_write_cmd(ser, 0x30, struct.pack('<II', addr, end_addr))
timeout = ser.timeout
ser.timeout = 10.0
expect_ok(ser)
ser.timeout = timeout
print(f'Erased {len} bytes @ {hex(addr)}')
def efl_cmd_flash_write(ser, addr, data):
efl_write_cmd(ser, 0x31, struct.pack('<I', addr) + data)
expect_ok(ser)
def efl_cmd_flash_write_check(ser):
efl_write_cmd(ser, 0x3a)
expect_ok(ser)
def efl_cmd_flash_xip_read_start(ser):
efl_write_cmd(ser, 0x60)
expect_ok(ser)
def efl_cmd_flash_xip_read_sha(ser, addr, len):
efl_write_cmd(ser, 0x3e, struct.pack('<II', addr, len))
return expect_data(ser)
def efl_cmd_flash_xip_read_finish(ser):
efl_write_cmd(ser, 0x61)
expect_ok(ser)
def efl_cmd_reset(ser):
efl_write_cmd(ser, 0x21)
expect_ok(ser)
def efl_program_img(ser, addr, data):
data_len = len(data)
efl_cmd_flash_erase(ser, addr, data_len)
print(f'Programming {data_len} bytes @ {hex(addr)}')
sent = 0
with tqdm(total=data_len, unit='byte', unit_scale=True) as pbar:
while sent != data_len:
buf_len = min(2048, data_len - sent)
buf = data[sent:sent + buf_len]
efl_cmd_flash_write(ser, addr + sent, buf)
sent = sent + buf_len
pbar.update(buf_len)
efl_cmd_flash_write_check(ser)
sha256sum = hashlib.sha256(data).digest()
efl_cmd_flash_xip_read_start(ser)
device_sum = efl_cmd_flash_xip_read_sha(ser, addr, data_len)
efl_cmd_flash_xip_read_finish(ser)
if device_sum != sha256sum:
print('Verification failed')
print('Host SHA256:', binascii.hexlify(sha256sum))
print('BL SHA256:', binascii.hexlify(device_sum))
return False
print('Verified by XIP SHA256 hash')
return True
def prepend_fw_header(img, header_file):
if img[0:4] == b'BFNP':
print('Image already has FW header')
return img
with open(header_file, 'rb') as f:
header = f.read()
img = header + (b'\xFF' * (4096-len(header))) + img
return img
def get_contrib_path(name):
sep = os.path.sep
return os.path.dirname(os.path.realpath(__file__)) + sep + 'contrib' + sep + name
def main():
if len(sys.argv) < 3:
print(f'Usage: {sys.argv[0]} <serial port> <firmware bin>')
sys.exit(1)
ser = Serial(sys.argv[1], baudrate=500000, timeout=2)
handshake(ser)
reset(ser)
send_sync(ser)
time.sleep(0.1)
print('Loading helper binary')
load_image(ser, get_contrib_path('eflash_loader_40m.bin'))
time.sleep(0.2)
print()
# at this point, the eflash loader binary is running with efl_ commands
# (which seems to work with a higher baudrate)
ser.baudrate = 2000000
send_sync(ser)
with open(sys.argv[2], 'rb') as f:
data = f.read()
data = prepend_fw_header(data, get_contrib_path('bootheader.bin'))
efl_program_img(ser, 0x10000, data)
efl_cmd_reset(ser)
if __name__ == "__main__":
main()