| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 | # mk_update.py new <output> <binary> "<stm_model>-<major>.<minor>-<type>"# mk_update.py cat <output> <update_file>*# mk_update.py verify <update_file>*## Convert a raw firmware binary into an update file for our bootloader.## Update Format (Little endian, unless otherwise stated):#   File Header:#     4 bytes: 'GWUP'#   Catalogue Header:#     2 bytes: <length> (excludes Catalogue Header)#     2 bytes: <hw_model>#   Payload:#     N bytes: <raw binary data>#   Footer:#     2 bytes: 'GW' or 'BL'#     2 bytes: major, minor#     2 bytes: <hw_model>#     2 bytes: CRC16-CCITT, seed 0xFFFF (big endian, excludes Catalogue Header)#   File Footer:#     4 bytes: CRC32 (MPEG-2, big endian)## Written & released by Keir Fraser <keir.xen@gmail.com>## This is free and unencumbered software released into the public domain.# See the file COPYING for more details, or visit <http://unlicense.org>.import crcmod.predefinedimport re, struct, sysname_to_hw_model = { 'stm32f1': 1,                     'stm32f7': 7,                     'at32f4': 4 }hw_model_to_name = { 1: 'STM32F1',                     7: 'STM32F7',                     4: 'AT32F4' }def mk_cat_entry(dat, hw_model, major, minor, sig):    max_kb = { 1: { b'BL':  8, b'GW': 56 },               7: { b'BL': 16, b'GW': 48 },               4: { b'BL': 16, b'GW': 48 } }    dlen = len(dat)    assert (dlen & 3) == 0, "input is not longword padded"    assert dlen <= max_kb[hw_model][sig]*1024, "input is too long"    header = struct.pack("<2H", dlen + 8, hw_model)    footer = struct.pack("<2s2BH", sig, major, minor, hw_model)    crc16 = crcmod.predefined.Crc('crc-ccitt-false')    crc16.update(dat)    crc16.update(footer)    footer += struct.pack(">H", crc16.crcValue)    return header + dat + footerdef new_upd(argv):    dat = b'GWUP'    m = re.match('([^-]+)-(\d+)\.(\d+)-(y?)', argv[1])    hw_model = name_to_hw_model[m.group(1)]    major, minor = int(m.group(2)), int(m.group(3))    is_bootloader = (m.group(4) == 'y')    sig = b'BL' if is_bootloader else b'GW'    with open(argv[0], "rb") as f:        dat += mk_cat_entry(f.read(), hw_model, major, minor, sig)    return datdef cat_upd(argv):    dat = b'GWUP'    for fname in argv:        with open(fname, "rb") as f:            d = f.read()        assert struct.unpack('4s', d[:4])[0] == b'GWUP'        crc32 = crcmod.predefined.Crc('crc-32-mpeg')        crc32.update(d)        assert crc32.crcValue == 0        dat += d[4:-4]    return datdef _verify_upd(d):    assert struct.unpack('4s', d[:4])[0] == b'GWUP'    crc32 = crcmod.predefined.Crc('crc-32-mpeg')    crc32.update(d)    assert crc32.crcValue == 0    d = d[4:-4]    while d:        upd_len, hw_model = struct.unpack("<2H", d[:4])        upd_type, major, minor = struct.unpack("2s2B", d[upd_len-4:upd_len])        crc16 = crcmod.predefined.Crc('crc-ccitt-false')        crc16.update(d[4:upd_len+4])        assert crc16.crcValue == 0        print('%s %s v%u.%u: %u bytes'              % (hw_model_to_name[hw_model],                 {b'BL': 'Boot', b'GW': 'Main'}[upd_type],                 major, minor, upd_len))        d = d[upd_len+4:]def verify_upd(argv):    for fname in argv:        with open(fname, "rb") as f:            d = f.read()        _verify_upd(d)    def main(argv):    if argv[1] == 'new':        dat = new_upd(argv[3:])    elif argv[1] == 'cat':        dat = cat_upd(argv[2:])    elif argv[1] == 'verify':        verify_upd(argv[2:])        return    else:        assert False    crc32 = crcmod.predefined.Crc('crc-32-mpeg')    crc32.update(dat)    dat += struct.pack(">I", crc32.crcValue)    with open(argv[2], "wb") as out_f:        out_f.write(dat)if __name__ == "__main__":    main(sys.argv)# Local variables:# python-indent: 4# End:
 |