Browse Source

'gw update': Add a file signature and overall CRC so we can validate
that we are passed a proper valid UPD file.
Fixes #40

Keir Fraser 4 years ago
parent
commit
798a5db854
3 changed files with 77 additions and 12 deletions
  1. 4 3
      Makefile
  2. 11 2
      scripts/greaseweazle/tools/update.py
  3. 62 7
      scripts/mk_update.py

+ 4 - 3
Makefile

@@ -25,8 +25,8 @@ all: scripts/greaseweazle/version.py
 		Bootloader.elf Bootloader.bin Bootloader.hex
 	srec_cat bootloader/Bootloader.hex -Intel src/$(PROJ).hex -Intel \
 	-o $(PROJ)-$(VER).hex -Intel
-	$(PYTHON) ./scripts/mk_update.py bootloader/Bootloader.bin \
-		src/$(PROJ).bin $(PROJ)-$(VER).upd $(stm32)
+	$(PYTHON) ./scripts/mk_update.py new $(PROJ)-$(VER).upd \
+		bootloader/Bootloader.bin src/$(PROJ).bin $(stm32)
 
 blinky:
 	$(MAKE) debug=y stm32=f1 -C blinky_test -f $(ROOT)/Rules.mk \
@@ -61,7 +61,8 @@ dist:
 	$(MAKE) clean
 	$(MAKE) stm32=f7 all
 	cp -a $(PROJ)-$(VER).hex $(PROJ)-$(VER)/$(PROJ)-F7-$(VER).hex
-	cat $(PROJ)-$(VER).upd >>$(PROJ)-$(VER)/$(PROJ)-$(VER).upd
+	$(PYTHON) ./scripts/mk_update.py cat $(PROJ)-$(VER)/$(PROJ)-$(VER).upd \
+		$(PROJ)-$(VER)/$(PROJ)-$(VER).upd $(PROJ)-$(VER).upd
 	$(MAKE) clean
 	$(ZIP) $(PROJ)-$(VER).zip $(PROJ)-$(VER)
 

+ 11 - 2
scripts/greaseweazle/tools/update.py

@@ -32,9 +32,18 @@ def update_firmware(usb, args):
         filename = os.path.join(path, "Greaseweazle-v%d.%d.upd"
                                 % (version.major, version.minor))
     
-    # Read the entire update catalogue.
+    # Read and verify the entire update catalogue.
     with open(filename, "rb") as f:
         dat = f.read()
+    if struct.unpack('4s', dat[:4])[0] != b'GWUP':
+        print('%s: Not an UPD file' % (filename))
+        return
+    crc32 = crcmod.predefined.Crc('crc-32-mpeg')
+    crc32.update(dat)
+    if crc32.crcValue != 0:
+        print('%s: UPD file is corrupt' % (filename))
+        return
+    dat = dat[4:-4]
 
     # Search the catalogue for a match on our Weazle's hardware type.
     while dat:
@@ -48,7 +57,7 @@ def update_firmware(usb, args):
         dat = dat[upd_len+4:]
 
     if not dat:
-        print("%s: No match for hardware type %x" % (filename, usb.hw_model))
+        print("%s: No match for F%u hardware" % (filename, usb.hw_model))
         return
 
     # Check the matching update file's footer.

+ 62 - 7
scripts/mk_update.py

@@ -1,8 +1,12 @@
-# mk_update.py <bootloader> <main_firmware> <output> <stm_model>
+# mk_update.py new <output> <bootloader> <main_firmware> <stm_model>
+# 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>
@@ -13,6 +17,8 @@
 #     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>
 #
@@ -38,13 +44,62 @@ def mk_cat_entry(dat, hw_model, sig):
     footer += struct.pack(">H", crc16.crcValue)
     return header + dat + footer
 
+def new_upd(argv):
+    dat = b'GWUP'
+    hw_model = int(re.match("f(\d)", argv[2]).group(1))
+    with open(argv[1], "rb") as gw_f:
+        dat += mk_cat_entry(gw_f.read(), hw_model, b'GW')
+    with open(argv[0], "rb") as bl_f:
+        dat += mk_cat_entry(bl_f.read(), hw_model, b'BL')
+    return dat
+
+def 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 dat
+    
+def verify_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
+        d = d[4:-4]
+        while d:
+            upd_len, hw_model = struct.unpack("<2H", d[:4])
+            upd_type, = struct.unpack("2s", d[upd_len-4:upd_len-2])
+            crc16 = crcmod.predefined.Crc('crc-ccitt-false')
+            crc16.update(d[4:upd_len+4])
+            assert crc16.crcValue == 0
+            print('F%u %s' % (hw_model, {b'BL': 'Boot',
+                                         b'GW': 'Main'}[upd_type]))
+            d = d[upd_len+4:]
+    
 def main(argv):
-    out_f = open(argv[3], "wb")
-    hw_model = int(re.match("f(\d)", argv[4]).group(1))
-    with open(argv[2], "rb") as gw_f:
-        out_f.write(mk_cat_entry(gw_f.read(), hw_model, b'GW'))
-    with open(argv[1], "rb") as bl_f:
-        out_f.write(mk_cat_entry(bl_f.read(), hw_model, b'BL'))
+    if argv[1] == 'new':
+        dat = new_upd(argv[3:])
+    elif argv[1] == 'cat':
+        dat = cat_upd(argv[3:])
+    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)