浏览代码

Allow Greaseweazle bootloader to be update (use with caution!)

Keir Fraser 5 年之前
父节点
当前提交
fd2a04188e
共有 8 个文件被更改,包括 142 次插入32 次删除
  1. 2 1
      Makefile
  2. 3 0
      inc/cdc_acm_protocol.h
  3. 28 13
      scripts/greaseweazle/tools/update.py
  4. 1 1
      scripts/greaseweazle/tools/util.py
  5. 10 0
      scripts/greaseweazle/usb.py
  6. 22 17
      scripts/mk_update.py
  7. 2 0
      src/Makefile
  8. 74 0
      src/floppy.c

+ 2 - 1
Makefile

@@ -25,7 +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 src/$(PROJ).bin $(PROJ)-$(VER).upd $(stm32)
+	$(PYTHON) ./scripts/mk_update.py bootloader/Bootloader.bin \
+		src/$(PROJ).bin $(PROJ)-$(VER).upd $(stm32)
 
 blinky:
 	$(MAKE) debug=y stm32=f1 -C blinky_test -f $(ROOT)/Rules.mk \

+ 3 - 0
inc/cdc_acm_protocol.h

@@ -19,6 +19,9 @@
 /* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>. 
  * Host follows with <update_len> bytes.
  * Bootloader finally returns a status byte, 0 on success. */
+/* [MAIN FIRMWARE] CMD_UPDATE, length=10, <update_len>, 0xdeafbee3.
+ * Host follows with <update_len> bytes.
+ * Main firmware finally returns a status byte, 0 on success. */
 #define CMD_UPDATE          1
 /* CMD_SEEK, length=3, cyl#. Seek to cyl# on selected drive. */
 #define CMD_SEEK            2

+ 28 - 13
scripts/greaseweazle/tools/update.py

@@ -18,6 +18,8 @@ from greaseweazle import usb as USB
 # Updates the Greaseweazle firmware using the specified Update File.
 def update_firmware(usb, args):
 
+    req_type = b'BL' if args.bootloader else b'GW'
+
     filename = args.file
     if filename == "auto":
         # Get the absolute path to the root Greaseweazle folder.
@@ -35,7 +37,8 @@ def update_firmware(usb, args):
     # Search the catalogue for a match on our Weazle's hardware type.
     while dat:
         upd_len, hw_type = struct.unpack("<2H", dat[:4])
-        if hw_type == usb.hw_type:
+        upd_type, = struct.unpack("2s", dat[upd_len-4:upd_len-2])
+        if hw_type == usb.hw_type and upd_type == req_type:
             # Match: Pull out the embedded update file.
             dat = dat[4:upd_len+4]
             break
@@ -48,7 +51,7 @@ def update_firmware(usb, args):
 
     # Check the matching update file's footer.
     sig, maj, min, hw_type = struct.unpack("<2s2BH", dat[-8:-2])
-    if len(dat) & 3 != 0 or sig != b'GW' or hw_type != usb.hw_type:
+    if len(dat) & 3 != 0 or sig != req_type or hw_type != usb.hw_type:
         print("%s: Bad update file" % (filename))
         return
     crc16 = crcmod.predefined.Crc('crc-ccitt-false')
@@ -58,17 +61,27 @@ def update_firmware(usb, args):
         return
 
     # Perform the update.
-    print("Updating to v%u.%u..." % (maj, min))
-    ack = usb.update_firmware(dat)
-    if ack != 0:
-        print("** UPDATE FAILED: Please retry!")
-        return
-    print("Done.")
-    
-    if usb.hw_type == 7:
-        util.usb_reopen(usb, is_update=False)
+    print("Updating %s to v%u.%u..."
+          % ("Bootloader" if args.bootloader else "Main Firmware", maj, min))
+    if args.bootloader:
+        ack = usb.update_bootloader(dat)
+        if ack != 0:
+            print("""\
+** UPDATE FAILED: Please retry immediately or your Weazle may need
+        full reflashing via a suitable programming adapter!""")
+            return
+        print("Done.")
     else:
-        print("** Disconnect Greaseweazle and remove the Programming Jumper.")
+        ack = usb.update_firmware(dat)
+        if ack != 0:
+            print("** UPDATE FAILED: Please retry!")
+            return
+        print("Done.")
+    
+        if usb.hw_type == 7:
+            util.usb_reopen(usb, is_update=False)
+        else:
+            print("** Disconnect Greaseweazle and remove the Programming Jumper.")
 
 
 def main(argv):
@@ -79,10 +92,12 @@ def main(argv):
                         help="update filename")
     parser.add_argument("device", nargs="?", default="auto",
                         help="serial device")
+    parser.add_argument("--bootloader", action="store_true",
+                        help="update the bootloader (WARNING: Use with caution!)")
     parser.prog += ' ' + argv[1]
     args = parser.parse_args(argv[2:])
 
-    usb = util.usb_open(args.device, is_update=True)
+    usb = util.usb_open(args.device, is_update=not args.bootloader)
 
     try:
         update_firmware(usb, args)

+ 1 - 1
scripts/greaseweazle/tools/util.py

@@ -140,7 +140,7 @@ def usb_open(devicename, is_update=False):
             if not usb.update_mode:
                 return usb
         print("Greaseweazle is in Firmware Update Mode:")
-        print(" The only available action is \"update <update_file>\"")
+        print(" The only available action is \"update\" of main firmware")
         if usb.update_jumpered:
             print(" Remove the Update Jumper for normal operation")
         else:

+ 10 - 0
scripts/greaseweazle/usb.py

@@ -250,6 +250,16 @@ class Unit:
         return ack
 
 
+    ## update_bootloader:
+    ## Update Greaseweazle with the given new bootloader.
+    def update_bootloader(self, dat):
+        self._send_cmd(struct.pack("<2B2I", Cmd.Update, 10,
+                                   len(dat), 0xdeafbee3))
+        self.ser.write(dat)
+        (ack,) = struct.unpack("B", self.ser.read(1))
+        return ack
+
+
     ## _decode_flux:
     ## Decode the Greaseweazle data stream into a list of flux samples.
     def _decode_flux(self, dat):

+ 22 - 17
scripts/mk_update.py

@@ -1,4 +1,4 @@
-# mk_update.py
+# mk_update.py <bootloader> <main_firmware> <output> <stm_type>
 #
 # Convert a raw firmware binary into an update file for our bootloader.
 #
@@ -9,7 +9,7 @@
 #   Payload:
 #     N bytes: <raw binary data>
 #   Footer:
-#     2 bytes: 'GW'
+#     2 bytes: 'GW' or 'BL'
 #     2 bytes: major, minor
 #     2 bytes: <hw_type>
 #     2 bytes: CRC16-CCITT, seed 0xFFFF (big endian, excludes Catalogue Header)
@@ -24,22 +24,27 @@ import re, struct, sys
 
 from greaseweazle import version
 
-def main(argv):
-    in_f = open(argv[1], "rb")
-    out_f = open(argv[2], "wb")
-    hw_type = int(re.match("f(\d)", argv[3]).group(1))
-    in_dat = in_f.read()
-    in_len = len(in_dat)
-    assert (in_len & 3) == 0, "input is not longword padded"
+def mk_cat_entry(dat, hw_type, sig):
+    max_kb = { 1: { b'BL':  8, b'GW': 56 },
+               7: { b'BL': 16, b'GW': 48 } }
+    dlen = len(dat)
+    assert (dlen & 3) == 0, "input is not longword padded"
+    assert dlen <= max_kb[hw_type][sig]*1024, "input is too long"
+    header = struct.pack("<2H", dlen + 8, hw_type)
+    footer = struct.pack("<2s2BH", sig, version.major, version.minor, hw_type)
     crc16 = crcmod.predefined.Crc('crc-ccitt-false')
-    out_f.write(struct.pack("<2H", in_len + 8, hw_type))
-    out_f.write(in_dat)
-    crc16.update(in_dat)
-    in_dat = struct.pack("<2s2BH", b'GW', version.major, version.minor, hw_type)
-    out_f.write(in_dat)
-    crc16.update(in_dat)
-    in_dat = struct.pack(">H", crc16.crcValue)
-    out_f.write(in_dat)
+    crc16.update(dat)
+    crc16.update(footer)
+    footer += struct.pack(">H", crc16.crcValue)
+    return header + dat + footer
+
+def main(argv):
+    out_f = open(argv[3], "wb")
+    hw_type = 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_type, b'GW'))
+    with open(argv[1], "rb") as bl_f:
+        out_f.write(mk_cat_entry(bl_f.read(), hw_type, b'BL'))
 
 if __name__ == "__main__":
     main(sys.argv)

+ 2 - 0
src/Makefile

@@ -1,5 +1,6 @@
 OBJS += board.o
 OBJS += build_info.o
+OBJS += crc.o
 OBJS += vectors.o
 OBJS += main.o
 OBJS += string.o
@@ -9,6 +10,7 @@ OBJS += time.o
 OBJS += timer.o
 OBJS += util.o
 OBJS += floppy.o
+OBJS += fpec_$(stm32).o
 
 OBJS-$(debug) += console.o
 

+ 74 - 0
src/floppy.c

@@ -86,6 +86,7 @@ static enum {
     ST_erase_flux,
     ST_source_bytes,
     ST_sink_bytes,
+    ST_update_bootloader,
 } floppy_state = ST_inactive;
 
 static uint32_t u_cons, u_prod;
@@ -859,6 +860,65 @@ static void sink_bytes(void)
 }
 
 
+/*
+ * BOOTLOADER UPDATE
+ */
+
+#define BL_START 0x08000000
+#define BL_END   ((uint32_t)_stext)
+#define BL_SIZE  (BL_END - BL_START)
+
+static struct {
+    uint32_t len;
+    uint32_t cur;
+} update;
+
+static void erase_old_bootloader(void)
+{
+    uint32_t p;
+    for (p = BL_START; p < BL_END; p += FLASH_PAGE_SIZE)
+        fpec_page_erase(p);
+}
+
+static void update_prep(uint32_t len)
+{
+    fpec_init();
+    erase_old_bootloader();
+
+    floppy_state = ST_update_bootloader;
+    update.cur = 0;
+    update.len = len;
+
+    printk("Update Bootloader: %u bytes\n", len);
+}
+
+static void update_continue(void)
+{
+    int len;
+
+    if ((len = ep_rx_ready(EP_RX)) >= 0) {
+        usb_read(EP_RX, &u_buf[u_prod], len);
+        u_prod += len;
+    }
+
+    if ((len = u_prod) >= 2) {
+        int nr = len & ~1;
+        fpec_write(u_buf, nr, BL_START + update.cur);
+        update.cur += nr;
+        u_prod -= nr;
+        memcpy(u_buf, &u_buf[nr], u_prod);
+    }
+
+    if ((update.cur >= update.len) && ep_tx_ready(EP_TX)) {
+        uint16_t crc = crc16_ccitt((void *)BL_START, update.len, 0xffff);
+        printk("Final CRC: %04x (%s)\n", crc, crc ? "FAIL" : "OK");
+        u_buf[0] = !!crc;
+        floppy_state = ST_command_wait;
+        floppy_end_command(u_buf, 1);
+    }
+}
+
+
 static void process_command(void)
 {
     uint8_t cmd = u_buf[0];
@@ -895,6 +955,16 @@ static void process_command(void)
         resp_sz += 32;
         break;
     }
+    case CMD_UPDATE: {
+        uint32_t u_len = *(uint32_t *)&u_buf[2];
+        uint32_t signature = *(uint32_t *)&u_buf[6];
+        if (len != 10) goto bad_command;
+        if (u_len & 3) goto bad_command;
+        if (u_len > BL_SIZE) goto bad_command;
+        if (signature != 0xdeafbee3) goto bad_command;
+        update_prep(u_len);
+        break;
+    }
     case CMD_SEEK: {
         uint8_t cyl = u_buf[2];
         if ((len != 3) || (cyl > 85))
@@ -1131,6 +1201,10 @@ void floppy_process(void)
         sink_bytes();
         break;
 
+    case ST_update_bootloader:
+        update_continue();
+        break;
+
     default:
         break;