mk_update.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. # mk_update.py new <output> <binary> "<stm_model>-<major>.<minor>-<type>"
  2. # mk_update.py cat <output> <update_file>*
  3. # mk_update.py verify <update_file>*
  4. #
  5. # Convert a raw firmware binary into an update file for our bootloader.
  6. #
  7. # Update Format (Little endian, unless otherwise stated):
  8. # File Header:
  9. # 4 bytes: 'GWUP'
  10. # Catalogue Header:
  11. # 2 bytes: <length> (excludes Catalogue Header)
  12. # 2 bytes: <hw_model>
  13. # Payload:
  14. # N bytes: <raw binary data>
  15. # Footer:
  16. # 2 bytes: 'GW' or 'BL'
  17. # 2 bytes: major, minor
  18. # 2 bytes: <hw_model>
  19. # 2 bytes: CRC16-CCITT, seed 0xFFFF (big endian, excludes Catalogue Header)
  20. # File Footer:
  21. # 4 bytes: CRC32 (MPEG-2, big endian)
  22. #
  23. # Written & released by Keir Fraser <keir.xen@gmail.com>
  24. #
  25. # This is free and unencumbered software released into the public domain.
  26. # See the file COPYING for more details, or visit <http://unlicense.org>.
  27. import crcmod.predefined
  28. import re, struct, sys
  29. name_to_hw_model = { 'stm32f1': 1,
  30. 'stm32f7': 7,
  31. 'at32f4': 4 }
  32. hw_model_to_name = { 1: 'STM32F1',
  33. 7: 'STM32F7',
  34. 4: 'AT32F4' }
  35. def mk_cat_entry(dat, hw_model, major, minor, sig):
  36. max_kb = { 1: { b'BL': 8, b'GW': 56 },
  37. 7: { b'BL': 16, b'GW': 48 },
  38. 4: { b'BL': 16, b'GW': 48 } }
  39. dlen = len(dat)
  40. assert (dlen & 3) == 0, "input is not longword padded"
  41. assert dlen <= max_kb[hw_model][sig]*1024, "input is too long"
  42. header = struct.pack("<2H", dlen + 8, hw_model)
  43. footer = struct.pack("<2s2BH", sig, major, minor, hw_model)
  44. crc16 = crcmod.predefined.Crc('crc-ccitt-false')
  45. crc16.update(dat)
  46. crc16.update(footer)
  47. footer += struct.pack(">H", crc16.crcValue)
  48. return header + dat + footer
  49. def new_upd(argv):
  50. dat = b'GWUP'
  51. m = re.match('([^-]+)-(\d+)\.(\d+)-(y?)', argv[1])
  52. hw_model = name_to_hw_model[m.group(1)]
  53. major, minor = int(m.group(2)), int(m.group(3))
  54. is_bootloader = (m.group(4) == 'y')
  55. sig = b'BL' if is_bootloader else b'GW'
  56. with open(argv[0], "rb") as f:
  57. dat += mk_cat_entry(f.read(), hw_model, major, minor, sig)
  58. return dat
  59. def cat_upd(argv):
  60. dat = b'GWUP'
  61. for fname in argv:
  62. with open(fname, "rb") as f:
  63. d = f.read()
  64. assert struct.unpack('4s', d[:4])[0] == b'GWUP'
  65. crc32 = crcmod.predefined.Crc('crc-32-mpeg')
  66. crc32.update(d)
  67. assert crc32.crcValue == 0
  68. dat += d[4:-4]
  69. return dat
  70. def _verify_upd(d):
  71. assert struct.unpack('4s', d[:4])[0] == b'GWUP'
  72. crc32 = crcmod.predefined.Crc('crc-32-mpeg')
  73. crc32.update(d)
  74. assert crc32.crcValue == 0
  75. d = d[4:-4]
  76. while d:
  77. upd_len, hw_model = struct.unpack("<2H", d[:4])
  78. upd_type, major, minor = struct.unpack("2s2B", d[upd_len-4:upd_len])
  79. crc16 = crcmod.predefined.Crc('crc-ccitt-false')
  80. crc16.update(d[4:upd_len+4])
  81. assert crc16.crcValue == 0
  82. print('%s %s v%u.%u: %u bytes'
  83. % (hw_model_to_name[hw_model],
  84. {b'BL': 'Boot', b'GW': 'Main'}[upd_type],
  85. major, minor, upd_len))
  86. d = d[upd_len+4:]
  87. def verify_upd(argv):
  88. for fname in argv:
  89. with open(fname, "rb") as f:
  90. d = f.read()
  91. _verify_upd(d)
  92. def main(argv):
  93. if argv[1] == 'new':
  94. dat = new_upd(argv[3:])
  95. elif argv[1] == 'cat':
  96. dat = cat_upd(argv[2:])
  97. elif argv[1] == 'verify':
  98. verify_upd(argv[2:])
  99. return
  100. else:
  101. assert False
  102. crc32 = crcmod.predefined.Crc('crc-32-mpeg')
  103. crc32.update(dat)
  104. dat += struct.pack(">I", crc32.crcValue)
  105. with open(argv[2], "wb") as out_f:
  106. out_f.write(dat)
  107. if __name__ == "__main__":
  108. main(sys.argv)
  109. # Local variables:
  110. # python-indent: 4
  111. # End: