Merge "flashtools: Added tools needed to flash images."
diff --git a/flash.sh b/flash.sh
new file mode 100644
index 0000000..98aa7e1
--- /dev/null
+++ b/flash.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+#
+# Copyright 2018 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SCRIPT_DIR=$(dirname $0)
+FLASHTOOLS_DIR=${SCRIPT_DIR}/flashtools
+FBTOOL=${FLASHTOOLS_DIR}/fbtool.py
+EXCELSIOR_DEBUG_RESET_TOOL=${FLASHTOOLS_DIR}/debug_board_reset.py
+DOWNLOAD_AGENT_CONFIG=${FLASHTOOLS_DIR}/dl_addr.ini
+FASTBOOT_TOOL=$(which fastboot)
+
+if [ -z ${FASTBOOT_TOOL} ]; then
+    echo "Fastboot is not found on this machine. Install fastboot before running this script"; exit 1
+fi
+
+if [ -z ${PRODUCT_OUT} ]; then
+    echo "PRODUCT_OUT not set, make sure to source board/setup.sh before running this script"; exit 1
+fi
+
+reset_excelsior () {
+  ${EXCELSIOR_DEBUG_RESET_TOOL} &
+}
+
+reset_excelsior_to_rom() {
+  echo "Reset Excelsior to ROM"
+  ${EXCELSIOR_DEBUG_RESET_TOOL} --rom &
+}
+
+load_lk_to_ram() {
+  echo "Load LK to rom and boot LK"
+  ${FBTOOL} -f ${PRODUCT_OUT}/dl_addr.ini
+}
+
+erase_mmc() {
+  echo "Erase EMMC"
+  ${FASTBOOT_TOOL} erase mmc0
+  ${FASTBOOT_TOOL} erase mmc0boot0
+  ${FASTBOOT_TOOL} erase mmc0boot1
+}
+
+flash_images() {
+    pushd ${PRODUCT_OUT}
+    ${FASTBOOT_TOOL} erase mmc0
+    ${FASTBOOT_TOOL} flash mmc0 GPT_EMMC
+    ${FASTBOOT_TOOL} flash mmc0boot0 lk.img
+    ${FASTBOOT_TOOL} flash TEE1 tz.img
+    ${FASTBOOT_TOOL} flash BOOTIMG1 boot.img
+    ${FASTBOOT_TOOL} flash ROOTFS rootfs.img
+    popd
+}
+
+ln -sf ${SCRIPT_DIR}/dl_addr.ini ${PRODUCT_OUT}/dl_addr.ini
+reset_excelsior_to_rom
+sleep 1
+load_lk_to_ram
+sleep 1
+erase_mmc
+sleep 1
+flash_images
+sleep 1
diff --git a/flashtools/debug_board_reset.py b/flashtools/debug_board_reset.py
new file mode 100755
index 0000000..4d70a3d
--- /dev/null
+++ b/flashtools/debug_board_reset.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python3
+
+import time
+import argparse
+
+import pyftdi
+from pyftdi.ftdi import Ftdi
+from pyftdi.gpio import GpioController
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--rom", help="reboot into rom",
+                    action="store_true", default=False)
+
+
+class DebugBoard(object):
+  """DebugBoard is used to control excelsior reset."""
+  GPIO_MASK = 0xc0
+  RESET_OUTPINS = 0xc0
+  BOOT_NORMAL_OUTPINS = 0x0
+  BOOT_ROM_OUTPINS = 0x40
+
+  def __init__(self, url ='ftdi://0x0403:0x6011/2'):
+    """Create gpio controller and select device."""
+    self._gpio = GpioController()
+    self._url = url
+
+  def _write(self, value):
+    """Set the direction on the GPIOs
+
+    Since the GPIO to control the Excelsior or pullup,
+    simply enabling the GPIO has the result of pulling them low.
+    Setting the GPIO to input allows them to be pulled up.
+    """
+    self._gpio.set_direction(self.GPIO_MASK, value)
+
+  def _reset(self):
+    """Set the GPIO to hold the board in reset."""
+    self._write(self.RESET_OUTPINS)
+
+  def _boot_normal(self):
+    """Set the gpio to boot the board."""
+    self._write(self.BOOT_NORMAL_OUTPINS)
+
+  def _boot_rom(self):
+    """Set the gpio to boot the board to the ROM."""
+    self._write(self.BOOT_ROM_OUTPINS)
+
+  def __enter__(self):
+    """Connect to the FTDI interface."""
+    self._gpio.open_from_url(self._url, self.BOOT_NORMAL_OUTPINS)
+    return self
+
+  def reset(self, boot_rom=False):
+    """Reset the excelsior, and boot normal or to the ROM."""
+    self._reset()
+    time.sleep(0.1)
+    if boot_rom:
+      print("Booting ROM")
+      self._boot_rom()
+      time.sleep(5.0)
+    else:
+      print("Booting LK")
+      self._boot_normal()
+      time.sleep(0.1)
+
+  def __exit__(self, exc_type, value, traceback):
+    """On exit close the connection to the FTDI device."""
+    self._gpio.close()
+
+
+if __name__ == "__main__":
+  args = parser.parse_args()
+  try:
+    with DebugBoard() as board:
+      board.reset(boot_rom=args.rom)
+  except pyftdi.usbtools.UsbToolsError:
+    print("*" * 70)
+    print("Unable to connect to debug board, check connection or manually toggle GPIO.")
+    print("*" * 70)
+    time.sleep(5)
diff --git a/flashtools/dl_addr.ini b/flashtools/dl_addr.ini
new file mode 100644
index 0000000..33b248f
--- /dev/null
+++ b/flashtools/dl_addr.ini
@@ -0,0 +1,27 @@
+[DA1_Path_Addr]

+da1_path=lk.bin

+[DA1_Load_Addr]

+da1_addr=0x201000

+[DA1_JUMP_64]

+da1_jump_64=0x1

+

+[Cert]

+cert_path=

+[MEID]

+meid_ctrl=0

+

+[Auth]

+auth_path=

+

+;[DA1_Path_Addr]

+;da1_path=

+;[DA1_Load_Addr]

+; da1_addr=0x0

+

+[DA2_Path_Addr]

+da2_path=

+[DA2_Wrapper_Addr]

+da2_addr=0x0

+

+; Old usage: fbtool [-a auth] fbtool-da-pl.bin fbtool-da-lk.bin

+; New usage: fbtool mtxxxx_dl_addr.ini

diff --git a/flashtools/fbtool.py b/flashtools/fbtool.py
new file mode 100755
index 0000000..defc061
--- /dev/null
+++ b/flashtools/fbtool.py
@@ -0,0 +1,511 @@
+#!/usr/bin/python3
+
+try:
+    import pyserial as serial
+    import pyserial.tools.list_ports as ser_tools
+except ImportError:
+    import serial
+    import serial.tools.list_ports as ser_tools
+import sys, time, struct, logging
+from optparse import OptionParser
+try:
+    # python2
+    import ConfigParser as configparser
+except ImportError:
+    # python3
+    import configparser
+
+class Fbtool:
+    def __init__(self, config_file, meid=False):
+        self.TGT_CFG_SBC_EN = 0x00000001
+        self.TGT_CFG_SLA_EN = 0x00000002
+        self.TGT_CFG_DAA_EN = 0x00000004
+        self.E_ERROR = 0x1000
+        self.__ser = None
+        self.__connect_type = 'UNKNOWN'
+        self.__meid = meid
+        cfg = configparser.ConfigParser()
+        cfg.read(config_file)
+        self.__da1_path = cfg.get('DA1_Path_Addr', 'da1_path')
+        self.__da1_addr = int(cfg.get('DA1_Load_Addr', 'da1_addr'), 16)
+        self.__da1_jump_64 = int(cfg.get('DA1_JUMP_64', 'da1_jump_64'), 16)
+        self.__da2_path = cfg.get('DA2_Path_Addr', 'da2_path')
+        self.__da2_addr = int(cfg.get('DA2_Wrapper_Addr', 'da2_addr'), 16)
+        self.__auth_path = cfg.get('Auth', 'auth_path')
+        self.__cert_path = cfg.get('Cert', 'cert_path')
+        logging.debug('da1_path: %s' %(self.__da1_path))
+        logging.debug('da1_addr: 0x%x' %(self.__da1_addr))
+        logging.debug('da1_jump_64: 0x%x' %(self.__da1_jump_64))
+        logging.debug('da2_path: %s' %(self.__da2_path))
+        logging.debug('ad2_addr: 0x%x' %(self.__da2_addr))
+        logging.debug('auth_path: %s' %(self.__auth_path))
+        logging.debug('cert_path: %s' %(self.__cert_path))
+
+    def __del__(self):
+        # compatible with pySerial 2.6.
+        # isOpen() is deprecated since version 3.0, 3.0 uses is_open
+        if self.__ser and self.__ser.isOpen():
+            self.__ser.close()
+
+    def __match_usb_br(self, vid, pid):
+        if vid == 0x0e8d and pid == 0x0003:
+            self.__connect_type = 'BROM'
+            return True
+        return False
+
+    def __match_usb_pl(self, vid, pid):
+        if ((vid == 0x0e8d and pid == 0x2000) or (vid == 0x0e8d and pid == 0x3000)):
+            self.__connect_type = 'PRELOADER'
+            return True
+        return False
+
+    def __match_usb_auto(self, vid, pid):
+        if self.__match_usb_br(vid, pid):
+            return True
+        if self.__match_usb_pl(vid, pid):
+            return True
+        return False
+
+    def __open_usb_device(self, match_usb_func, comport, vid, pid):
+        if match_usb_func(vid, pid):
+            time.sleep(0.1)
+            try:
+                self.__ser = serial.Serial(comport, 115200)
+            except serial.SerialException as e:
+                logging.debug('%s, retry...' %(str(e)))
+            else:
+                logging.info('Got %s' %(comport))
+                return True
+        return False
+
+    def __find_usb_device(self, match_usb_func):
+        while True:
+            ports = ser_tools.comports()
+            if serial.VERSION < '3':
+                ports_list = list(ports)
+                for port in ports_list:
+                    if 'USB' in port[2]:
+                        if sys.platform.startswith('win'):
+                            idx = port[2].index('VID_')+4
+                            vid = int(port[2][idx : idx + 4], 16)
+                            idx = port[2].index('PID_')+4
+                            pid = int(port[2][idx : idx + 4], 16)
+                        elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
+                            idx = port[2].index('VID:PID=') + 8
+                            vid = int(port[2][idx : idx + 4], 16)
+                            pid = int(port[2][idx + 5 : idx + 13], 16)
+                        elif sys.platform.startswith('darwin'):
+                            raise EnvironmentError('Unsupport macOS')
+                        else:
+                            raise EnvironmentError('Unsupported platform')
+                        if self.__open_usb_device(match_usb_func, port[0], vid, pid):
+                            return
+            else:
+                for port in ports:
+                    if self.__open_usb_device(match_usb_func, port.device, port.vid, port.pid):
+                        return
+
+    def __read8(self):
+        return struct.unpack('!B', self.__ser.read())[0]
+
+    def __read16(self):
+        return struct.unpack('!H', self.__ser.read(2))[0]
+
+    def __read32(self):
+        return struct.unpack('!I', self.__ser.read(4))[0]
+
+    def __write8(self, data, echo):
+        self.__ser.write(struct.pack('!B', data))
+        if echo:
+            return struct.unpack('!B', self.__ser.read())[0] == data
+        return True
+
+    def __write16(self, data, echo):
+        self.__ser.write(struct.pack('!H', data))
+        if echo:
+            return struct.unpack('!H', self.__ser.read(2))[0] == data
+        return True
+
+    def __write32(self, data, echo):
+        self.__ser.write(struct.pack('!I', data))
+        if echo:
+            return struct.unpack('!I', self.__ser.read(4))[0] == data
+        return True
+
+    def __start_cmd(self):
+        cmd = (0xa0, 0x0a, 0x50, 0x05)
+        echo_cmd = (0x5f, 0xf5, 0xaf, 0xfa)
+
+        i = 0
+        while (i < len(cmd)):
+            self.__write8(cmd[i], False)
+            if self.__read8() != echo_cmd[i]:
+                i = 0
+                self.__ser.flushInput()
+            else:
+                i = i + 1
+        time.sleep(0.1)
+        # self.__ser.flush()
+        self.__ser.flushInput()
+        self.__ser.flushOutput()
+        if self.__connect_type == 'BROM':
+            logging.info('Connect brom')
+        elif self.__connect_type == 'PRELOADER':
+            logging.info('Connect preloader')
+
+    def __load_binary(self, path):
+        logging.info("Loading file: %s" %path)
+        with open(path, 'rb') as f:
+            return f.read()
+
+    def __checksum(self, data, length):
+        checksum = 0
+        for i in range(0, length, 2):
+            checksum ^= struct.unpack('<H', data[i:i+2])[0]
+        checksum &= 0xFFFF
+        return checksum
+
+    def __get_target_config(self):
+        if not self.__write8(0xd8, True):
+            return -1, None
+        cfg = self.__read32()
+        status = self.__read16()
+
+        if status >= self.E_ERROR:
+            return status, None
+        return 0, cfg
+
+    def __send_auth(self, cfg):
+        if self.TGT_CFG_DAA_EN & cfg == 0:
+            return 0
+        else:
+            if self.TGT_CFG_SBC_EN & cfg == 0:
+                logging.error('daa=1, sbc=0')
+                return -2
+            if self.__auth_path == '':
+                logging.error('no auth file')
+                return -3
+            auth = self.__load_binary(self.__auth_path)
+            auth_len = len(auth)
+            logging.debug("auth file size: 0x%x" %(auth_len))
+
+            if not self.__write8(0xe2, True):
+                return -4
+            if not self.__write32(len(auth), True):
+                return -5
+
+            status = self.__read16()
+            if status >= self.E_ERROR:
+                return status
+            self.__ser.write(auth)
+
+            # compare checksum
+            if self.__read16() != self.__checksum(auth, auth_len):
+                return -6
+
+            status = self.__read16()
+            if status >= self.E_ERROR:
+                return status
+            return 0
+
+    def __strip_pl_hdr(self, pl, pl_len):
+        # EMMC_HEADER_V1
+        identifier, ver, dev_rw_unit = struct.unpack('12s2I', pl[:20])
+        # GFH_FILE_INFO_V1
+        gfh = pl[:56]
+        gfh_offset = 0
+
+        if identifier.strip(b'\0') == b'EMMC_BOOT' and ver == 1:
+            logging.debug('emmc_hdr: identifier:%s, ver:0x%08x, dev_rw_unit:0x%08x' %(identifier, ver, dev_rw_unit))
+            # BR_Layout_v1 size: 40
+            if dev_rw_unit + 40 > pl_len:
+                logging.error('EMMC HDR error. dev_rw_unit=0x%x, brlyt_size=0x%x, pl_len=0x%x'
+                    %(dev_rw_unit, brlyt_size, pl_len))
+                return False, None, None
+
+            brlyt_identifier, brlyt_ver = struct.unpack('8sI', pl[dev_rw_unit:dev_rw_unit + 12])
+            logging.debug('brlyt_identifier: %s, brlyt_ver=0x%x' %(brlyt_identifier, brlyt_ver))
+            if brlyt_identifier.strip(b'\0') != b'BRLYT' or brlyt_ver != 1:
+                logging.error('BRLYT error. ver=0x%x, identifier=%s' %(brlyt_ver, brlyt_identifier))
+                return False, None, None
+            # BL_Descriptor
+            bl_begin_dev_addr = struct.unpack('I', pl[dev_rw_unit + 28 : dev_rw_unit + 32])[0]
+            if bl_begin_dev_addr + 56 > pl_len:
+                logging.error('BRLYT error. bl_begin_dev_addr=0x%x' %bl_begin_dev_addr)
+                return False, None, None
+            # GFH_FILE_INFO_v1
+            gfh = pl[bl_begin_dev_addr:bl_begin_dev_addr + 56]
+            gfh_offset = bl_begin_dev_addr
+
+        gfh_struct =struct.unpack('I2H12sIH2B7I', gfh)
+
+        gfh_magic_ver = gfh_struct[0]
+        gfh_type = gfh_struct[2]
+        gfh_identifier = gfh_struct[3]
+        gfh_file_len = gfh_struct[9]
+        gfh_jump_offset = gfh_struct[13]
+        gfh_sig_len = gfh_struct[12]
+        if (gfh_magic_ver & 0x00FFFFFF) == 0x004D4D4D and gfh_type == 0 and gfh_identifier.strip(b'\0') == b'FILE_INFO':
+            if gfh_file_len < gfh_jump_offset + gfh_sig_len:
+                logging.error('GFH error. pl_len=0x%x, file_len=0x%x, jump_offset=0x%x, sig_len=0x%x'
+                                %(pl_len, gfh_file_len, gfh_jump_offset, gfh_sig_len))
+                return False, None, None
+            logging.debug('gfh: magic_ver: 0x%08x' %gfh_struct[0])
+            logging.debug('gfh: size: 0x%04x' %gfh_struct[1])
+            logging.debug('gfh: type: 0x%04x' %gfh_struct[2])
+            logging.debug('gfh: identifier: %s' %gfh_struct[3])
+            logging.debug('gfh: file_ver: 0x%08x' %gfh_struct[4])
+            logging.debug('gfh: file_type: 0x%04x' %gfh_struct[5])
+            logging.debug('gfh: flash_dev: 0x%02x' %gfh_struct[6])
+            logging.debug('gfh: sig_type: 0x%02x' %gfh_struct[7])
+            logging.debug('gfh: load_addr: 0x%08x' %gfh_struct[8])
+            logging.debug('gfh: file_len: 0x%08x' %gfh_struct[9])
+            logging.debug('gfh: max_size: 0x%08x' %gfh_struct[10])
+            logging.debug('gfh: content_offset: 0x%08x' %gfh_struct[11])
+            logging.debug('gfh: sig_len: 0x%08x' %gfh_struct[12])
+            logging.debug('gfh: jump_offset: 0x%08x' %gfh_struct[13])
+            logging.debug('gfh: attr: 0x%08x' %gfh_struct[14])
+            strip_pl = pl[gfh_offset + gfh_jump_offset:]
+            strip_pl_len = gfh_file_len - gfh_jump_offset - gfh_sig_len
+            return (True, strip_pl, strip_pl_len)
+        else:
+            return (True, pl, pl_len)
+
+    def __send_da(self, addr, da, da_len, sig, sig_len):
+        if not self.__write8(0xd7, True):
+            return -1
+        if not self.__write32(addr, True):
+            return -2
+        logging.debug('len: 0x%x' %(da_len + sig_len))
+        if not self.__write32(da_len + sig_len, True):
+            return -3
+        if not self.__write32(sig_len, True):
+            return -4
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+
+        if da_len > 0:
+            self.__ser.write(da)
+        if sig_len > 0:
+            self.__ser.write(sig)
+
+        checksum = self.__checksum(da, da_len) ^ self.__checksum(sig, sig_len)
+        data = self.__read16()
+        logging.debug('checksum: 0x%x - 0x%x' %(checksum, data))
+        if data != checksum:
+            return -5
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+
+        return 0
+
+    def __jump_da(self, addr):
+        if not self.__write8(0xd5, True):
+            return -1
+        if not self.__write32(addr, True):
+            return -2
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        return 0
+
+    def __jump_da_ex(self, addr):
+        if not self.__write8(0xde, True):
+            return -1
+        if not self.__write32(addr, True):
+            return -2
+        if not self.__write8(0x1, True):
+            return -3
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        if not self.__write8(0x64, True):
+            return -4
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        return 0
+
+    def __get_meid(self):
+        if not self.__write8(0xe1, True):
+            return -1
+        len = self.__read32()
+        logging.debug('meid len: 0x%x' %len)
+        data = struct.unpack('!'+str(len)+'B', self.__ser.read(len))
+        meid_str = lambda s: ''.join(map(lambda c: '%02x' %c, s))
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        logging.info(meid_str(data));
+        return 0
+
+    def __send_cert(self, data, len):
+        if not self.__write8(0xe0, True):
+            return -1
+        if not self.__write32(len, True):
+            return -2
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        self.__ser.write(data)
+        checksum = self.__checksum(data, len)
+        data = self.__read16()
+        if checksum != data:
+            logging.error("checksum: 0x%x - 0x%x" %(checksum, data))
+            return -3
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        return 0
+
+    def __reboot_platform(self):
+        if not self.__write8(0xd4, True):
+            return -1
+        if not self.__write32(0x10007000, True):
+            return -2
+        if not self.__write32(0x1, True):
+            return -3
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        if not self.__write32(0x22000004, True):
+            return -4
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        if not self.__write8(0xd4, True):
+            return -5
+        if not self.__write32(0x10007014, True):
+            return -6
+        if not self.__write32(0x1, True):
+            return -7
+        status = self.__read16()
+        if status >= self.E_ERROR:
+            return status
+        if not self.__write32(0x1209, True):
+            return -8
+        return 0
+
+    def start(self):
+        self.__find_usb_device(self.__match_usb_auto)
+        self.__start_cmd()
+
+        # get meid
+        if self.__meid:
+            status = self.__get_meid()
+            if status != 0:
+                logging.error('get meid (%d)' %status)
+                return -1
+            return 0
+
+        # send cert
+        if self.__cert_path != '':
+            cert = self.__load_binary(self.__cert_path)
+            cert_len = len(cert)
+            logging.debug('cert_len: 0x%x' %cert_len)
+            status = self.__send_cert(cert, cert_len)
+            if status != 0:
+                logging.error('send cert (%d)' %status)
+                return -1
+            logging.info('Reboot...')
+            status = self.__reboot_platform()
+            if status != 0:
+                logging.error('reboot platform (%d)' %status)
+                return -1
+            return 0
+
+        if self.__connect_type == 'BROM':
+            status, cfg = self.__get_target_config()
+            if status != 0:
+                logging.error('get target config (%s)' %status)
+                return -1
+            logging.debug('cfg=0x%x' %cfg)
+
+            status = self.__send_auth(cfg)
+            if status != 0:
+                logging.error('send auth (%d)' %status)
+                return -1
+
+            da1 = self.__load_binary(self.__da1_path)
+            da1_len = len(da1)
+            logging.debug('da1 length: 0x%x' %da1_len)
+            status, strip_pl, strip_pl_len = self.__strip_pl_hdr(da1, da1_len)
+            if not status:
+                logging.error('strip pl hdr')
+                return -1
+
+            sig_da1 = None
+            sig_da1_len = 0
+            if self.TGT_CFG_DAA_EN & cfg:
+                sig_da1 = self.__load_binary(self.__da1_path + '.sign')
+                sig_da1_len = len(sig_da1)
+
+            logging.debug('strip_pl_len: 0x%x' %strip_pl_len)
+            logging.info('Send %s' %self.__da1_path)
+            status = self.__send_da(self.__da1_addr, strip_pl, strip_pl_len, sig_da1, sig_da1_len)
+            if status != 0:
+                logging.error('send da1 (%d)' %status)
+                return -1
+            logging.info('Jump da')
+            if self.__da1_jump_64 == 0:
+                status = self.__jump_da(self.__da1_addr)
+            else:
+                status = self.__jump_da_ex(self.__da1_addr)
+            if status != 0:
+                logging.error('jump da1 (%d)' %status)
+                return -1
+
+            self.__ser.close()
+            if self.__da2_path == '':
+                return 0
+
+            # handshake to preloader
+            self.__find_usb_device(self.__match_usb_pl)
+            self.__start_cmd()
+
+        # load da2 (lk)
+        da2 = self.__load_binary(self.__da2_path)
+        da2_len = len(da2)
+        sig_da2 = self.__load_binary(self.__da2_path + '.sign')
+        sig_da2_len = len(sig_da2)
+        logging.info('Send %s' %self.__da2_path)
+        status = self.__send_da(self.__da2_addr, da2, da2_len, sig_da2, sig_da2_len)
+        if status != 0:
+            logging.error('send da2 (%d)' %status)
+            return -1
+        logging.info('Jump da2')
+        status = self.__jump_da(self.__da2_addr)
+        if status != 0:
+            logging.error('jump da2 (%d)' %status)
+            return -1
+
+        self.__ser.close()
+        return 0
+
+
+if __name__ == '__main__':
+    parser = OptionParser()
+    parser.add_option('-f', '--file', dest='configfile', help='read config file',
+                    metavar='FILE', default='dl_addr.ini')
+    parser.add_option('-d', '--debug', action='store_true', dest='debuglog',
+                    default=False, help='enable debug log')
+    parser.add_option('-m', '--meid', action='store_true', dest='meid',
+                    default=False, help='get meid')
+    options, args = parser.parse_args()
+    config_file = options.configfile
+    meid = options.meid
+    debug = options.debuglog
+    if debug:
+        logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
+    else:
+        logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
+
+    logging.info('pySerial version: (%s)' %serial.VERSION)
+    if serial.VERSION < '2.6':
+        logging.error('pySerial version(%s) is lower than 2.6, please upgrade!' %serial.VERSION)
+    logging.info('Use config file: %s' %(config_file))
+    logging.info('Waiting to connect platform...')
+    fbtool = Fbtool(config_file, meid)
+    fbtool.start()