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