├── da-dump.py └── mtk-bootloader-tool.py /da-dump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # This utility dumps information about supported chips from combined 4 | # download agent (DA) binary. A typical name of such binary is 5 | # MTK_AllInOne_DA.bin, though names can be used too. 6 | # 7 | import sys 8 | import struct 9 | 10 | 11 | def decode(f, sz, fmt): 12 | s = f.read(sz) 13 | return struct.unpack("<" + fmt, s) 14 | 15 | 16 | f = open(sys.argv[1], "rb") 17 | 18 | sig = f.read(0x20) 19 | assert sig.startswith(b"MTK_DOWNLOAD_AGENT") 20 | 21 | da_id = f.read(0x40) 22 | da_id = da_id.rstrip(b"\0") 23 | print("# Mediatek Download Agent information dump") 24 | print("id: %s" % str(da_id, "ascii")) 25 | assert decode(f, 4, "I")[0] == 4 26 | assert f.read(4) == b"\x99\x88\x66\x22" 27 | 28 | num_socs = decode(f, 4, "I")[0] 29 | print("num_chips:", num_socs) 30 | 31 | PARTS = [ 32 | 5, 33 | 9, 34 | 14, 35 | 19, 36 | ] 37 | 38 | for i in range(num_socs): 39 | rec = f.read(0xdc) 40 | fields = struct.unpack("<2sHIIIIIIIQIIIIIIIIIIIII128s", rec) 41 | assert fields[0] == b'\xda\xda' 42 | # assert fields[-1] == b"\0" * 128, fields[-1] 43 | fields = fields[1:-1] 44 | flds = ["0x%08x" % x if isinstance(x, int) else x for x in fields] 45 | 46 | print("- chip_id: %#x" % fields[0]) 47 | print(" chip_ver: %#x" % fields[1]) 48 | print(" fw_ver: %#x" % fields[2]) 49 | print(" extra_ver: %#x" % fields[3]) 50 | print(" all_fields:", flds) 51 | 52 | # assert fields[10] == fields[12] + fields[13], (fields[10], fields[12], fields[13]) 53 | 54 | print(" # file_offset, size, load_addr") 55 | for i, no in enumerate(PARTS): 56 | print(" da_part%d: [%#x, 0x%05x, 0x%08x] # %#x-%#x" % (i, fields[no], fields[no + 1], fields[no + 2], fields[no], fields[no] + fields[no + 1])) 57 | -------------------------------------------------------------------------------- /mtk-bootloader-tool.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os.path 3 | import time 4 | import struct 5 | from binascii import hexlify 6 | #from serial import Serial 7 | from serial_ import Serial 8 | #import serial 9 | 10 | DEVICE = sys.argv[1] 11 | 12 | CMD_GET_VERSION = b"\xff" # this returns echo if security is off 13 | CMD_GET_BL_VER = b"\xfe" 14 | CMD_GET_HW_VER = b"\xfc" 15 | CMD_GET_HW_CODE = b"\xfd" 16 | CMD_SEND_DA = b"\xd7" 17 | CMD_JUMP_DA = b"\xd5" 18 | CMD_GET_TARGE_CONFIG = b"\xd8" 19 | CMD_READ16 = b"\xa2" 20 | CMD_WRITE16 = b"\xd2" 21 | CMD_READ32 = b"\xd1" 22 | CMD_WRITE32 = b"\xd4" 23 | CMD_PWR_INIT = b"\xc4" 24 | CMD_PWR_DEINIT = b"\xc5" 25 | CMD_PWR_READ16 = b"\xc6" 26 | CMD_PWR_WRITE16 = b"\xc7" 27 | 28 | 29 | def hexs(s): 30 | return hexlify(s).decode("ascii") 31 | 32 | while True: 33 | try: 34 | # s = Serial(DEVICE, 19200) 35 | s = Serial(DEVICE, 115200) 36 | sys.stdout.write("\n") 37 | break 38 | except OSError as e: 39 | sys.stdout.write("."); sys.stdout.flush() 40 | time.sleep(0.1) 41 | 42 | 43 | def connect(): 44 | while True: 45 | s.write(b"\xa0\x0a\x50\x05") 46 | resp = s.read(4) 47 | print(resp) 48 | if resp == b"\x5f\xf5\xaf\xfa": 49 | break 50 | if resp == b"READ": 51 | print(s.read(1)) 52 | return 53 | 54 | s.write(b"\xa0\x0a\x50\x05\xa0\x0a\x50\x05\xa0\x0a\x50\x05") 55 | print(s.read(5)) 56 | print(s.read(5)) 57 | resp = s.read(4) 58 | print(resp) 59 | assert resp == b"\x5f\xf5\xaf\xfa" 60 | print("Connected") 61 | 62 | 63 | def cmd_echo(cmd, resp_sz): 64 | print(">", hexs(cmd)) 65 | s.write(cmd) 66 | echo = s.read(len(cmd)) 67 | assert echo == cmd, echo 68 | resp = s.read(resp_sz) 69 | print("<", hexs(resp)) 70 | return resp 71 | 72 | def cmd_noecho(cmd, resp_sz, show=True): 73 | if show: 74 | print(">", hexs(cmd)) 75 | else: 76 | print("> ...") 77 | s.write(cmd) 78 | resp = s.read(resp_sz) 79 | print("<", hexs(resp)) 80 | return resp 81 | 82 | def write32(addr, cnt, vals): 83 | resp = cmd_echo(CMD_WRITE32 + struct.pack(">II", addr, cnt), 2) 84 | assert resp == b"\0\0" 85 | for v in vals: 86 | cmd_echo(struct.pack(">I", v), 0) 87 | resp = cmd_echo(b"", 2) 88 | assert resp == b"\0\0" 89 | 90 | def get_da_part1_params(): 91 | # addr, size, size_of_xxx? 92 | params = (0x00200000, 0x00011518, 0x00000100) 93 | with open("MTK_AllInOne_DA.bin", "rb") as f: 94 | f.seek(0x3b5310) 95 | data = f.read(params[1]) 96 | return (params, data) 97 | 98 | def get_da_part2_params(): 99 | # addr, size, block_size 100 | params = (0x80000000, 0x00033ea0, 0x00001000) 101 | with open("MTK_AllInOne_DA.bin", "rb") as f: 102 | f.seek(0x3c6828) 103 | data = f.read(params[1]) 104 | return (params, data) 105 | 106 | 107 | def boot_da2(): 108 | connect() 109 | 110 | resp = cmd_echo(CMD_GET_HW_CODE, 4) 111 | soc_id, soc_step = struct.unpack(">HH", resp) 112 | print("Chip: %x, stepping?: %x" % (soc_id, soc_step)) 113 | 114 | resp = cmd_echo(CMD_GET_HW_VER, 8) 115 | subver, ver, extra = struct.unpack(">HHI", resp) 116 | print("Hardware version: %#x, subversion: %#x, extra: %#x" % (ver, subver, extra)) 117 | 118 | write32(0x10007000, 1, [0x22000064]) 119 | 120 | cmd_noecho(CMD_GET_BL_VER, 1) 121 | assert cmd_noecho(CMD_GET_VERSION, 1) == b"\xff" 122 | cmd_noecho(CMD_GET_BL_VER, 1) 123 | 124 | print("Downloading DA part 1") 125 | params, data = get_da_part1_params() 126 | resp = cmd_echo(CMD_SEND_DA + struct.pack(">III", *params), 2) 127 | assert resp == b"\0\0" 128 | while data: 129 | s.write(data[:1024]) 130 | #print("Wrote %d bytes: %s" % (len(data[:1024]), data[:16])) 131 | data = data[1024:] 132 | 133 | resp = cmd_echo(b"", 2) 134 | resp = cmd_echo(b"", 2) 135 | assert resp == b"\0\0" 136 | 137 | print("Starting DA part 1...") 138 | resp = cmd_echo(CMD_JUMP_DA + struct.pack(">I", params[0]), 2) 139 | 140 | resp = cmd_echo(b"", 41) 141 | print("DA part 1 startup response:", resp) 142 | 143 | resp = cmd_noecho(b"Z", 3) 144 | assert resp == b"\x04\x02\x94" 145 | 146 | resp = cmd_noecho(\ 147 | b"\xff" 148 | b"\x01" 149 | b"\x00\x08" 150 | b"\x00" 151 | b"\x70\x07\xff\xff" 152 | b"\x01" 153 | b"\x00\x00\x00\x00" 154 | b"\x02" 155 | b"\x00" 156 | b"\x02" 157 | b"\x00" 158 | b"\x00\x02\x00\x00", 4) 159 | 160 | assert resp == b"\0\0\0\0" 161 | 162 | print("Downloading DA part 2") 163 | 164 | params, data = get_da_part2_params() 165 | 166 | resp = cmd_noecho(struct.pack(">III", *params), 1) 167 | assert resp == b"Z" 168 | 169 | BLK_SZ = 4096 170 | 171 | while data: 172 | s.write(data[:BLK_SZ]) 173 | assert s.read(1) == b"Z" 174 | #print("Wrote %d bytes" % len(data[:BLK_SZ])) 175 | data = data[BLK_SZ:] 176 | 177 | resp = cmd_noecho(b"", 1) 178 | assert resp == b"Z" 179 | 180 | resp = cmd_noecho(b"Z", 236) 181 | print(resp) 182 | # In this response: EMMC partition sizes, etc. 183 | assert resp[-1] == 0xc1 184 | 185 | resp = cmd_noecho(b"r", 2) 186 | assert resp == b"Z\x01" 187 | resp = cmd_noecho(b"r", 2) 188 | assert resp == b"Z\x01" 189 | 190 | resp = cmd_noecho(b"\x60", 1) 191 | assert resp == b"Z" 192 | resp = cmd_noecho(b"\x08", 1) 193 | assert resp == b"Z" 194 | 195 | 196 | def read_flash(start, size, outf): 197 | sth = 2 # ?? 198 | resp = cmd_noecho(b"\xd6\x0c" + struct.pack(">BQQ", sth, start, size), 1) 199 | assert resp == b"Z" 200 | 201 | # After so many transferred bytes, there will be 2-byte checksum 202 | chksum_blk_size = 0x10000 203 | cmd_noecho(struct.pack(">I", chksum_blk_size), 0) 204 | 205 | while size > 0: 206 | chunk = s.read(min(size, chksum_blk_size)) 207 | #data += chunk 208 | size -= len(chunk) 209 | chksum = struct.unpack(">H", s.read(2))[0] 210 | chksum_my = 0 211 | for b in chunk: 212 | chksum_my += b 213 | assert chksum_my & 0xffff == chksum 214 | #print(hex(ck_my), hexs(chksum), chunk) 215 | outf.write(chunk) 216 | sys.stdout.write("."); sys.stdout.flush() 217 | s.write(b"Z") 218 | print() 219 | 220 | 221 | boot_da2() 222 | 223 | print("Reading flash...") 224 | 225 | f = open("rom.bin", "wb") 226 | 227 | start = 0 228 | size = 4096 #1024 * 1024 * 1024 229 | read_flash(start, size, f) 230 | 231 | f.close() 232 | --------------------------------------------------------------------------------