blob: 60caa0dd2f58ae5984fe68e0d63a0914cf8ae222 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001#!/usr/bin/env python
2# -*- coding: utf-8 -*
3try:
4 import pyserial as serial
5 import pyserial.tools.list_ports as ser_tools
6except ImportError:
7 import serial
8 import serial.tools.list_ports as ser_tools
9import sys, time, struct, logging
10from optparse import OptionParser
11try:
12 # python2
13 import ConfigParser as configparser
14except ImportError:
15 # python3
16 import configparser
17
18class Fbtool:
19 def __init__(self, config_file, meid=False):
20 self.TGT_CFG_SBC_EN = 0x00000001
21 self.TGT_CFG_SLA_EN = 0x00000002
22 self.TGT_CFG_DAA_EN = 0x00000004
23 self.E_ERROR = 0x1000
24 self.__ser = None
25 self.__connect_type = 'UNKNOWN'
26 self.__meid = meid
27 cfg = configparser.ConfigParser()
28 cfg.read(config_file)
29 self.__da1_path = cfg.get('DA1', 'da1_path')
30 self.__da1_addr = int(cfg.get('DA1', 'da1_addr'), 16)
31 self.__da1_jump_64 = int(cfg.get('DA1', 'da1_jump_64'), 16)
32 self.__da2_path = cfg.get('DA2', 'da2_path')
33 self.__da2_addr = int(cfg.get('DA2', 'da2_addr'), 16)
34 self.__auth_path = cfg.get('Auth', 'auth_path')
35 self.__cert_path = cfg.get('Cert', 'cert_path')
36 self.__fall_thru_to_fb = int(cfg.get('FALL_THRU_TO_FB', 'fall_thru_to_fb'))
37 logging.debug('da1_path: %s' %(self.__da1_path))
38 logging.debug('da1_addr: 0x%x' %(self.__da1_addr))
39 logging.debug('da1_jump_64: 0x%x' %(self.__da1_jump_64))
40 logging.debug('da2_path: %s' %(self.__da2_path))
41 logging.debug('ad2_addr: 0x%x' %(self.__da2_addr))
42 logging.debug('auth_path: %s' %(self.__auth_path))
43 logging.debug('cert_path: %s' %(self.__cert_path))
44
45 def __del__(self):
46 # compatible with pySerial 2.6.
47 # isOpen() is deprecated since version 3.0, 3.0 uses is_open
48 if self.__ser and self.__ser.isOpen():
49 self.__ser.close()
50
51 def __match_usb_br(self, vid, pid):
52 if vid == 0x0e8d and pid == 0x0003:
53 self.__connect_type = 'BROM'
54 return True
55 return False
56
57 def __match_usb_pl(self, vid, pid):
58 if ((vid == 0x0e8d and pid == 0x2000) or (vid == 0x0e8d and pid == 0x3000)):
59 self.__connect_type = 'PRELOADER'
60 return True
61 return False
62
63 def __match_usb_auto(self, vid, pid):
64 if self.__match_usb_br(vid, pid):
65 return True
66 if self.__match_usb_pl(vid, pid):
67 return True
68 return False
69
70 def __open_usb_device(self, match_usb_func, comport, vid, pid):
71 if match_usb_func(vid, pid):
72 time.sleep(0.1)
73 try:
74 self.__ser = serial.Serial(comport, 115200)
75 except serial.SerialException as e:
76 logging.debug('%s, retry...' %(str(e)))
77 else:
78 logging.info('Got %s' %(comport))
79 return True
80 return False
81
82 def __find_usb_device(self, match_usb_func):
83 while True:
84 ports = ser_tools.comports()
85 if serial.VERSION < '3':
86 ports_list = list(ports)
87 for port in ports_list:
88 if 'USB' in port[2]:
89 if sys.platform.startswith('win'):
90 idx = port[2].index('VID_')+4
91 vid = int(port[2][idx : idx + 4], 16)
92 idx = port[2].index('PID_')+4
93 pid = int(port[2][idx : idx + 4], 16)
94 elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
95 idx = port[2].index('VID:PID=') + 8
96 vid = int(port[2][idx : idx + 4], 16)
97 pid = int(port[2][idx + 5 : idx + 13], 16)
98 elif sys.platform.startswith('darwin'):
99 raise EnvironmentError('Unsupport macOS')
100 else:
101 raise EnvironmentError('Unsupported platform')
102 if self.__open_usb_device(match_usb_func, port[0], vid, pid):
103 return
104 else:
105 for port in ports:
106 if self.__open_usb_device(match_usb_func, port.device, port.vid, port.pid):
107 return
108
109 def __read8(self):
110 return struct.unpack('!B', self.__ser.read())[0]
111
112 def __read16(self):
113 return struct.unpack('!H', self.__ser.read(2))[0]
114
115 def __read32(self):
116 return struct.unpack('!I', self.__ser.read(4))[0]
117
118 def __write8(self, data, echo):
119 self.__ser.write(struct.pack('!B', data))
120 if echo:
121 return struct.unpack('!B', self.__ser.read())[0] == data
122 return True
123
124 def __write16(self, data, echo):
125 self.__ser.write(struct.pack('!H', data))
126 if echo:
127 return struct.unpack('!H', self.__ser.read(2))[0] == data
128 return True
129
130 def __write32(self, data, echo):
131 self.__ser.write(struct.pack('!I', data))
132 if echo:
133 return struct.unpack('!I', self.__ser.read(4))[0] == data
134 return True
135
136 def __start_cmd(self):
137 cmd = (0xa0, 0x0a, 0x50, 0x05)
138 echo_cmd = (0x5f, 0xf5, 0xaf, 0xfa)
139
140 i = 0
141 while (i < len(cmd)):
142 self.__write8(cmd[i], False)
143 if self.__read8() != echo_cmd[i]:
144 i = 0
145 self.__ser.flushInput()
146 else:
147 i = i + 1
148 time.sleep(0.1)
149 # self.__ser.flush()
150 self.__ser.flushInput()
151 self.__ser.flushOutput()
152 if self.__connect_type == 'BROM':
153 logging.info('Connect brom')
154 elif self.__connect_type == 'PRELOADER':
155 logging.info('Connect preloader')
156
157 def __load_binary(self, path):
158 logging.info("Loading file: %s" %path)
159 with open(path, 'rb') as f:
160 return f.read()
161
162 def __checksum(self, data, length):
163 checksum = 0
164 for i in range(0, length, 2):
165 checksum ^= struct.unpack('<H', data[i:i+2])[0]
166 checksum &= 0xFFFF
167 return checksum
168
169 def __get_target_config(self):
170 if not self.__write8(0xd8, True):
171 return -1, None
172 cfg = self.__read32()
173 status = self.__read16()
174
175 if status >= self.E_ERROR:
176 return status, None
177 return 0, cfg
178
179 def __send_auth(self, cfg):
180 if self.TGT_CFG_DAA_EN & cfg == 0:
181 return 0
182 else:
183 if self.TGT_CFG_SBC_EN & cfg == 0:
184 logging.error('daa=1, sbc=0')
185 return -2
186 if self.__auth_path == '':
187 logging.error('no auth file')
188 return -3
189 auth = self.__load_binary(self.__auth_path)
190 auth_len = len(auth)
191 logging.debug("auth file size: 0x%x" %(auth_len))
192
193 if not self.__write8(0xe2, True):
194 return -4
195 if not self.__write32(len(auth), True):
196 return -5
197
198 status = self.__read16()
199 if status >= self.E_ERROR:
200 return status
201 self.__ser.write(auth)
202
203 # compare checksum
204 if self.__read16() != self.__checksum(auth, auth_len):
205 return -6
206
207 status = self.__read16()
208 if status >= self.E_ERROR:
209 return status
210 return 0
211
212 def __strip_pl_hdr(self, pl, pl_len):
213 # EMMC_HEADER_V1
214 identifier, ver, dev_rw_unit = struct.unpack('12s2I', pl[:20])
215 # GFH_FILE_INFO_V1
216 gfh = pl[:56]
217 gfh_offset = 0
218
219 if identifier.strip(b'\0') == b'EMMC_BOOT' and ver == 1:
220 logging.debug('emmc_hdr: identifier:%s, ver:0x%08x, dev_rw_unit:0x%08x' %(identifier, ver, dev_rw_unit))
221 # BR_Layout_v1 size: 40
222 if dev_rw_unit + 40 > pl_len:
223 logging.error('EMMC HDR error. dev_rw_unit=0x%x, brlyt_size=0x%x, pl_len=0x%x'
224 %(dev_rw_unit, brlyt_size, pl_len))
225 return False, None, None
226
227 brlyt_identifier, brlyt_ver = struct.unpack('8sI', pl[dev_rw_unit:dev_rw_unit + 12])
228 logging.debug('brlyt_identifier: %s, brlyt_ver=0x%x' %(brlyt_identifier, brlyt_ver))
229 if brlyt_identifier.strip(b'\0') != b'BRLYT' or brlyt_ver != 1:
230 logging.error('BRLYT error. ver=0x%x, identifier=%s' %(brlyt_ver, brlyt_identifier))
231 return False, None, None
232 # BL_Descriptor
233 bl_begin_dev_addr = struct.unpack('I', pl[dev_rw_unit + 28 : dev_rw_unit + 32])[0]
234 if bl_begin_dev_addr + 56 > pl_len:
235 logging.error('BRLYT error. bl_begin_dev_addr=0x%x' %bl_begin_dev_addr)
236 return False, None, None
237 # GFH_FILE_INFO_v1
238 gfh = pl[bl_begin_dev_addr:bl_begin_dev_addr + 56]
239 gfh_offset = bl_begin_dev_addr
240
241 gfh_struct =struct.unpack('I2H12sIH2B7I', gfh)
242
243 gfh_magic_ver = gfh_struct[0]
244 gfh_type = gfh_struct[2]
245 gfh_identifier = gfh_struct[3]
246 gfh_file_len = gfh_struct[9]
247 gfh_jump_offset = gfh_struct[13]
248 gfh_sig_len = gfh_struct[12]
249 if (gfh_magic_ver & 0x00FFFFFF) == 0x004D4D4D and gfh_type == 0 and gfh_identifier.strip(b'\0') == b'FILE_INFO':
250 if gfh_file_len < gfh_jump_offset + gfh_sig_len:
251 logging.error('GFH error. pl_len=0x%x, file_len=0x%x, jump_offset=0x%x, sig_len=0x%x'
252 %(pl_len, gfh_file_len, gfh_jump_offset, gfh_sig_len))
253 return False, None, None
254 logging.debug('gfh: magic_ver: 0x%08x' %gfh_struct[0])
255 logging.debug('gfh: size: 0x%04x' %gfh_struct[1])
256 logging.debug('gfh: type: 0x%04x' %gfh_struct[2])
257 logging.debug('gfh: identifier: %s' %gfh_struct[3])
258 logging.debug('gfh: file_ver: 0x%08x' %gfh_struct[4])
259 logging.debug('gfh: file_type: 0x%04x' %gfh_struct[5])
260 logging.debug('gfh: flash_dev: 0x%02x' %gfh_struct[6])
261 logging.debug('gfh: sig_type: 0x%02x' %gfh_struct[7])
262 logging.debug('gfh: load_addr: 0x%08x' %gfh_struct[8])
263 logging.debug('gfh: file_len: 0x%08x' %gfh_struct[9])
264 logging.debug('gfh: max_size: 0x%08x' %gfh_struct[10])
265 logging.debug('gfh: content_offset: 0x%08x' %gfh_struct[11])
266 logging.debug('gfh: sig_len: 0x%08x' %gfh_struct[12])
267 logging.debug('gfh: jump_offset: 0x%08x' %gfh_struct[13])
268 logging.debug('gfh: attr: 0x%08x' %gfh_struct[14])
269 strip_pl_len = gfh_file_len - gfh_jump_offset - gfh_sig_len
270 start = gfh_offset + gfh_jump_offset
271 strip_pl = pl[start:start + strip_pl_len]
272 return (True, strip_pl, strip_pl_len)
273 else:
274 return (True, pl, pl_len)
275
276 def __send_da(self, addr, da, da_len, sig, sig_len):
277 if not self.__write8(0xd7, True):
278 return -1
279 if not self.__write32(addr, True):
280 return -2
281 logging.debug('len: 0x%x' %(da_len + sig_len))
282 if not self.__write32(da_len + sig_len, True):
283 return -3
284 if not self.__write32(sig_len, True):
285 return -4
286 status = self.__read16()
287 if status >= self.E_ERROR:
288 return status
289
290 if da_len > 0:
291 self.__ser.write(da)
292 if sig_len > 0:
293 self.__ser.write(sig)
294
295 checksum = self.__checksum(da, da_len) ^ self.__checksum(sig, sig_len)
296 data = self.__read16()
297 logging.debug('checksum: 0x%x - 0x%x' %(checksum, data))
298 if data != checksum:
299 return -5
300 status = self.__read16()
301 if status >= self.E_ERROR:
302 return status
303
304 return 0
305
306 def __jump_da(self, addr):
307 if not self.__write8(0xd5, True):
308 return -1
309 if not self.__write32(addr, True):
310 return -2
311 status = self.__read16()
312 if status >= self.E_ERROR:
313 return status
314 return 0
315
316 def __jump_da_ex(self, addr):
317 if not self.__write8(0xde, True):
318 return -1
319 if not self.__write32(addr, True):
320 return -2
321 if not self.__write8(0x1, True):
322 return -3
323 status = self.__read16()
324 if status >= self.E_ERROR:
325 return status
326 if not self.__write8(0x64, True):
327 return -4
328 status = self.__read16()
329 if status >= self.E_ERROR:
330 return status
331 return 0
332
333 def __get_meid(self):
334 if not self.__write8(0xe1, True):
335 return -1
336 len = self.__read32()
337 logging.debug('meid len: 0x%x' %len)
338 data = struct.unpack('!'+str(len)+'B', self.__ser.read(len))
339 meid_str = lambda s: ''.join(map(lambda c: '%02x' %c, s))
340 status = self.__read16()
341 if status >= self.E_ERROR:
342 return status
343 logging.info(meid_str(data));
344 return 0
345
346 def __send_cert(self, data, len):
347 if not self.__write8(0xe0, True):
348 return -1
349 if not self.__write32(len, True):
350 return -2
351 status = self.__read16()
352 if status >= self.E_ERROR:
353 return status
354 self.__ser.write(data)
355 checksum = self.__checksum(data, len)
356 data = self.__read16()
357 if checksum != data:
358 logging.error("checksum: 0x%x - 0x%x" %(checksum, data))
359 return -3
360 status = self.__read16()
361 if status >= self.E_ERROR:
362 return status
363 return 0
364
365 def __reboot_platform(self):
366 if not self.__write8(0xd4, True):
367 return -1
368 if not self.__write32(0x10007000, True):
369 return -2
370 if not self.__write32(0x1, True):
371 return -3
372 status = self.__read16()
373 if status >= self.E_ERROR:
374 return status
375 if not self.__write32(0x22000004, True):
376 return -4
377 status = self.__read16()
378 if status >= self.E_ERROR:
379 return status
380 if not self.__write8(0xd4, True):
381 return -5
382 if not self.__write32(0x10007014, True):
383 return -6
384 if not self.__write32(0x1, True):
385 return -7
386 status = self.__read16()
387 if status >= self.E_ERROR:
388 return status
389 if not self.__write32(0x1209, True):
390 return -8
391 return 0
392
393 def __fall_through_to_fastboot(self):
394 if not self.__write8(0xd9, True):
395 return -1
396 status = self.__read16()
397 if status >= self.E_ERROR:
398 return status
399 return 0
400
401 def start(self):
402 self.__find_usb_device(self.__match_usb_auto)
403 self.__start_cmd()
404
405 # get meid
406 if self.__meid:
407 status = self.__get_meid()
408 if status != 0:
409 logging.error('get meid (%d)' %status)
410 return -1
411 return 0
412
413 # send cert
414 if self.__cert_path != '':
415 cert = self.__load_binary(self.__cert_path)
416 cert_len = len(cert)
417 logging.debug('cert_len: 0x%x' %cert_len)
418 status = self.__send_cert(cert, cert_len)
419 if status != 0:
420 logging.error('send cert (%d)' %status)
421 return -1
422 logging.info('Reboot...')
423 status = self.__reboot_platform()
424 if status != 0:
425 logging.error('reboot platform (%d)' %status)
426 return -1
427 return 0
428
429 if self.__connect_type == 'BROM':
430 status, cfg = self.__get_target_config()
431 if status != 0:
432 logging.error('get target config (%s)' %status)
433 return -1
434 logging.debug('cfg=0x%x' %cfg)
435
436 status = self.__send_auth(cfg)
437 if status != 0:
438 logging.error('send auth (%d)' %status)
439 return -1
440
441 da1 = self.__load_binary(self.__da1_path)
442 da1_len = len(da1)
443 logging.debug('da1 length: 0x%x' %da1_len)
444 status, strip_pl, strip_pl_len = self.__strip_pl_hdr(da1, da1_len)
445 if not status:
446 logging.error('strip pl hdr')
447 return -1
448
449 sig_da1 = None
450 sig_da1_len = 0
451 if self.TGT_CFG_DAA_EN & cfg:
452 sig_da1 = self.__load_binary(self.__da1_path + '.sign')
453 sig_da1_len = len(sig_da1)
454
455 logging.debug('strip_pl_len: 0x%x' %strip_pl_len)
456 logging.info('Send %s' %self.__da1_path)
457 status = self.__send_da(self.__da1_addr, strip_pl, strip_pl_len, sig_da1, sig_da1_len)
458 if status != 0:
459 logging.error('send da1 (%d)' %status)
460 return -1
461 logging.info('Jump da')
462 if self.__da1_jump_64 == 0:
463 status = self.__jump_da(self.__da1_addr)
464 else:
465 status = self.__jump_da_ex(self.__da1_addr)
466 if status != 0:
467 logging.error('jump da1 (%d)' %status)
468 return -1
469
470 self.__ser.close()
471 if self.__da2_path == '':
472 return 0
473
474 # handshake to preloader
475 self.__find_usb_device(self.__match_usb_pl)
476 self.__start_cmd()
477
478 if self.__fall_thru_to_fb == 1:
479 logging.info("fall through to fastboot")
480 status = self.__fall_through_to_fastboot()
481 self.__ser.close()
482 if status != 0:
483 logging.error("fall through to fastboot failed(%d)" %(status))
484 return -1
485 else:
486 return 0
487
488 # load da2 (lk)
489 da2 = self.__load_binary(self.__da2_path)
490 da2_len = len(da2)
491 sig_da2 = self.__load_binary(self.__da2_path + '.sign')
492 sig_da2_len = len(sig_da2)
493 logging.info('Send %s' %self.__da2_path)
494 status = self.__send_da(self.__da2_addr, da2, da2_len, sig_da2, sig_da2_len)
495 if status != 0:
496 logging.error('send da2 (%d)' %status)
497 return -1
498 logging.info('Jump da2')
499 status = self.__jump_da(self.__da2_addr)
500 if status != 0:
501 logging.error('jump da2 (%d)' %status)
502 return -1
503
504 self.__ser.close()
505 return 0
506
507
508if __name__ == '__main__':
509 parser = OptionParser()
510 parser.add_option('-f', '--file', dest='configfile', help='read config file',
511 metavar='FILE', default='dl_addr.ini')
512 parser.add_option('-d', '--debug', action='store_true', dest='debuglog',
513 default=False, help='enable debug log')
514 parser.add_option('-m', '--meid', action='store_true', dest='meid',
515 default=False, help='get meid')
516 options, args = parser.parse_args()
517 config_file = options.configfile
518 meid = options.meid
519 debug = options.debuglog
520 if debug:
521 logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
522 else:
523 logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
524
525 logging.info('pySerial version: (%s)' %serial.VERSION)
526 if serial.VERSION < '2.6':
527 logging.error('pySerial version(%s) is lower than 2.6, please upgrade!' %serial.VERSION)
528 logging.info('Use config file: %s' %(config_file))
529 logging.info('Waiting to connect platform...')
530 fbtool = Fbtool(config_file, meid)
531 fbtool.start()