| #!/usr/bin/env python |
| # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import print_function |
| import argparse |
| import ctypes |
| import os |
| |
| |
| class Header(ctypes.Structure): |
| _pack_ = 1 |
| _fields_ = [ |
| ('signature', ctypes.c_uint32), |
| ('ftb_ver', ctypes.c_uint32), |
| ('chip_id', ctypes.c_uint32), |
| ('svn_ver', ctypes.c_uint32), |
| ('fw_ver', ctypes.c_uint32), |
| ('config_id', ctypes.c_uint32), |
| ('config_ver', ctypes.c_uint32), |
| ('reserved', ctypes.c_uint8 * 8), |
| ('release_info', ctypes.c_ulonglong), |
| ('sec_size', ctypes.c_uint32 * 4), |
| ('crc', ctypes.c_uint32), |
| ] |
| |
| FW_HEADER_SIZE = 64 |
| FW_HEADER_SIGNATURE = 0xAA55AA55 |
| FW_FTB_VER = 0x00000001 |
| FW_CHIP_ID = 0x3936 |
| FW_BYTES_ALIGN = 4 |
| FW_BIN_VER_OFFSET = 16 |
| FW_BIN_CONFIG_ID_OFFSET = 20 |
| |
| # Starting address in flash for each section |
| FLASH_SEC_ADDR = [ |
| 0x0000 * 4, # CODE |
| 0x7C00 * 4, # CONFIG |
| 0x7000 * 4, # CX |
| None # This section shouldn't exist |
| ] |
| |
| UPDATE_PDU_SIZE = 4096 |
| |
| # Bin file format: |
| # FTB header (padded to `UPDATE_PDU_SIZE`) |
| # Flash sections |
| # CODE |
| # CX |
| # CONFIG |
| OUTPUT_FILE_SIZE = UPDATE_PDU_SIZE + 128 * 1024 |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--input', '-i', required=True) |
| parser.add_argument('--output', '-o', required=True) |
| args = parser.parse_args() |
| |
| with open(args.input) as f: |
| bs = f.read() |
| |
| size = len(bs) |
| if size < FW_HEADER_SIZE + FW_BYTES_ALIGN: |
| raise Exception('FW size too small') |
| |
| print('FTB file size:', size) |
| |
| header = Header() |
| assert ctypes.sizeof(header) == FW_HEADER_SIZE |
| |
| ctypes.memmove(ctypes.addressof(header), bs, ctypes.sizeof(header)) |
| if (header.signature != FW_HEADER_SIGNATURE or |
| header.ftb_ver != FW_FTB_VER or |
| header.chip_id != FW_CHIP_ID): |
| raise Exception('Invalid header') |
| |
| for key, _ in header._fields_: |
| v = getattr(header, key) |
| if isinstance(v, ctypes.Array): |
| print(key, map(hex, v)) |
| else: |
| print(key, hex(v)) |
| |
| dimension = sum(header.sec_size) |
| |
| assert dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN == size |
| data = bs[FW_HEADER_SIZE:FW_HEADER_SIZE + dimension] |
| |
| with open(args.output, 'wb') as f: |
| # ensure the file size |
| f.seek(OUTPUT_FILE_SIZE - 1, os.SEEK_SET) |
| f.write('\x00') |
| |
| f.seek(0, os.SEEK_SET) |
| f.write(bs[0 : ctypes.sizeof(header)]) |
| |
| offset = 0 |
| # write each sections |
| for i, addr in enumerate(FLASH_SEC_ADDR): |
| size = header.sec_size[i] |
| assert addr is not None or size == 0 |
| |
| if size == 0: |
| continue |
| |
| f.seek(UPDATE_PDU_SIZE + addr, os.SEEK_SET) |
| f.write(data[offset : offset + size]) |
| offset += size |
| |
| f.flush() |
| |
| |
| if __name__ == '__main__': |
| main() |