Przeglądaj źródła

fwupdate: support fw v2 format (multiversion) for OTA updates

This implements support for the v2 firmware format (supports multiple
board versions in one file) for OTA and SD card updates.
H. Peter Anvin 2 lat temu
rodzic
commit
634b03c355

+ 36 - 2
common/fwimg.h

@@ -10,14 +10,26 @@
 /*
  * Firmware chunk header.
  */
-#define FW_MAGIC		0x7a07fbd6
+#define FW_MAGIC_V1		0x7a07fbd6
+#define FW_MAGIC_V2		0xa924ed0b
+
+#define FW_HDR_LEN_V1		16
+#define FW_HDR_LEN_V2		32
 
 struct fw_header {
+    /* All versions */
     uint32_t magic;		/* Magic number */
     uint16_t type;		/* Content type */
     uint16_t flags;		/* Content flags */
     uint32_t len;		/* Content length (excluding header) */
     uint32_t addr;		/* Address or similar */
+
+    /* v2 only */
+    uint32_t vmatch;		/* Flags to match */
+    uint32_t vmask;		/* Mask of flags to match */
+    uint16_t vmin;		/* Minimum version match */
+    uint16_t vmax;		/* Maximum version match */
+    uint32_t resv;		/* For future use */
 };
 
 enum fw_data_type {
@@ -33,7 +45,29 @@ enum fw_data_type {
     FDT_BOARDINFO		/* Board information flash address */
 };
 enum fw_data_flags {
-    FDF_OPTIONAL     = 0x0001	/* Ignore if chunk data type unknown */
+    FDF_OPTIONAL     = 0x0001,	/* Ignore if chunk data type unknown */
+    FDF_PRETARGET    = 0x0002	/* Matching FDT_TARGET not required (yet) */
 };
 
+/*
+ * Additional error codes beyond those defined in zlib
+ */
+#define FWUPDATE_ERR_IN_PROGRESS	(-10)
+#define FWUPDATE_ERR_BAD_CHUNK		(-11)
+#define FWUPDATE_ERR_ERASE_FAILED	(-12)
+#define FWUPDATE_ERR_PROGRAM_FAILED	(-13)
+#define FWUPDATE_ERR_WRITE_PROTECT	(-14)
+#define FWUPDATE_ERR_NOT_READY		(-15)
+#define FWUPDATE_ERR_FPGA_JTAG          (-16)
+#define FWUPDATE_ERR_FPGA_MISMATCH	(-17)
+#define FWUPDATE_ERR_FPGA_FAILED	(-18)
+#define FWUPDATE_ERR_UNKNOWN            (-19)
+#define FWUPDATE_ERR_ESP_NO_PARTITION	(-20)
+#define FWUPDATE_ERR_ESP_BAD_OTA	(-21)
+#define FWUPDATE_ERR_ESP_FLASH_FAILED	(-22)
+#define FWUPDATE_ERR_ESP_BAD_DATA	(-23)
+#define FWUPDATE_ERR_CONFIG_READ	(-24)
+#define FWUPDATE_ERR_CONFIG_SAVE	(-25)
+#define FWUPDATE_ERR_NOT_MINE		(-26)
+
 #endif /* FW_H */

+ 0 - 20
esp32/max80/fw.h

@@ -5,26 +5,6 @@
 
 #include <zlib.h>
 
-/*
- * Additional error codes
- */
-#define FWUPDATE_ERR_IN_PROGRESS	(-10)
-#define FWUPDATE_ERR_BAD_CHUNK		(-11)
-#define FWUPDATE_ERR_ERASE_FAILED	(-12)
-#define FWUPDATE_ERR_PROGRAM_FAILED	(-13)
-#define FWUPDATE_ERR_WRITE_PROTECT	(-14)
-#define FWUPDATE_ERR_NOT_READY		(-15)
-#define FWUPDATE_ERR_FPGA_JTAG          (-16)
-#define FWUPDATE_ERR_FPGA_MISMATCH	(-17)
-#define FWUPDATE_ERR_FPGA_FAILED	(-18)
-#define FWUPDATE_ERR_UNKNOWN            (-19)
-#define FWUPDATE_ERR_ESP_NO_PARTITION	(-20)
-#define FWUPDATE_ERR_ESP_BAD_OTA	(-21)
-#define FWUPDATE_ERR_ESP_FLASH_FAILED	(-22)
-#define FWUPDATE_ERR_ESP_BAD_DATA	(-23)
-#define FWUPDATE_ERR_CONFIG_READ	(-24)
-#define FWUPDATE_ERR_CONFIG_SAVE	(-25)
-
 extern_c int firmware_update_start(read_func_t read_data, token_t token, bool autoreboot);
 extern_c int firmware_update_wait(TickType_t delay);
 extern_c int esp_update(read_func_t read_func, token_t token, size_t size);

+ 86 - 18
esp32/max80/fwupdate.c

@@ -10,6 +10,7 @@
 #include "httpd.h"
 #include "fw.h"
 #include "boardinfo_esp.h"
+#include "matchver.h"
 
 #include <unzipLIB.h>
 #include <zlib.h>
@@ -284,23 +285,89 @@ static int fwupdate_boardinfo(spz_stream *spz)
     return spz->err;
 }
 
-/* Process a data chunk; return a nonzero value if done */
-static int fwupdate_process_chunk(spz_stream *spz)
+/* Get a piece of the chunk header */
+static int fwupdate_get_header_data(spz_stream *spz, void *buf, size_t len)
 {
     int rv;
-    char *str;
 
-    rv = spz_read_data(spz, &spz->header, sizeof spz->header);
+    rv = spz_read_data(spz, buf, len);
     if (spz->err)
 	return spz->err;
     else if (!rv)
 	return Z_STREAM_END;
-    else if (rv != sizeof spz->header)
+    else if (rv != len)
 	return spz->err = Z_STREAM_ERROR;
+    else
+	return Z_OK;
+}
+
+/* Get and validate a chunk header */
+static int fwupdate_get_header(spz_stream *spz)
+{
+    struct fw_header * const hdr = &spz->header;
+    uint8_t *hptr = (uint8_t *)hdr;
+    int rv;
+    unsigned int hlen;
+
+    memset(hdr, 0, sizeof *hdr);
+    hdr->vmax = -1;
+
+    rv = fwupdate_get_header_data(spz, hptr, FW_HDR_LEN_V1);
+    if (rv)
+	return rv;
+
+    switch (hdr->magic) {
+    case FW_MAGIC_V1:
+	hlen = FW_HDR_LEN_V1;
+	break;
+
+    case FW_MAGIC_V2:
+	hlen = FW_HDR_LEN_V2;
+	break;
+
+    default:
+	MSG("bad chunk header magic 0x%08x\n", hdr->magic);
+	hlen = 0;
+	rv = Z_DATA_ERROR;
+	break;
+    }
+
+    if (hlen > FW_HDR_LEN_V1) {
+	rv = fwupdate_get_header_data(spz, hptr + FW_HDR_LEN_V1,
+				      hlen - FW_HDR_LEN_V1);
+
+	if (rv == Z_STREAM_END)	/* Only valid for the first chunk */
+	    rv = Z_STREAM_ERROR;
+    }
 
-    if (spz->header.magic != FW_MAGIC) {
-	MSG("bad chunk header magic 0x%08x\n", spz->header.magic);
-	return spz->err = Z_DATA_ERROR;
+    return spz->err = rv;
+}
+
+/* Process a data chunk; return a nonzero value if done */
+static int fwupdate_process_chunk(spz_stream *spz)
+{
+    int rv;
+    char *str;
+
+    rv = fwupdate_get_header(spz);
+    if (rv)
+	return rv;
+
+    if (spz->header.type != FDT_NOTE &&
+	spz->header.type != FDT_TARGET &&
+	spz->header.type != FDT_END &&
+	!(spz->header.flags & FDF_PRETARGET)) {
+	if (!spz->vmatch.magic) {
+	    /* No matching firmware target support */
+	    return spz->err = FWUPDATE_ERR_NOT_MINE;
+	}
+
+	if (spz->header.vmin > spz->vmatch.vmax ||
+	    spz->header.vmax < spz->vmatch.vmin ||
+	    ((spz->header.vmatch ^ spz->header.vmatch) & spz->header.vmask)) {
+	    /* Chunk not applicable to this target */
+	    return fwupdate_skip_chunk(spz);
+	}
     }
 
     switch (spz->header.type) {
@@ -312,17 +379,17 @@ static int fwupdate_process_chunk(spz_stream *spz)
     case FDT_BOARDINFO:
 	return fwupdate_boardinfo(spz);
     case FDT_TARGET:
+    {
+	bool match;
 	str = fwupdate_read_chunk_str(spz);
-#if 0
-	if (!str || strcmp(str, spz->flash->target)) {
-	    MSG("this firmware file targets \"%s\", need \"%s\"\n",
-		str, spz->flash->target);
-	    return spz->err = Z_DATA_ERROR;
-	}
-#else
-	MSG("firmware target: \"%s\"\n", str);
-#endif
+	match = match_version(board_info.version_str, str);
+	if (match || spz->header.magic == FW_MAGIC_V1)
+	    spz->vmatch = spz->header;
+
+	MSG("firmware file supports: %s%s\n",
+	    str, match ? " (match)" : "");
 	return Z_OK;
+    }
     case FDT_NOTE:
 	str = fwupdate_read_chunk_str(spz);
 	MSG("%s\n", str);
@@ -381,7 +448,8 @@ const char *firmware_errstr(int err)
 	[-FWUPDATE_ERR_ESP_FLASH_FAILED] = "ESP flash program failed",
 	[-FWUPDATE_ERR_ESP_BAD_DATA]     = "ESP firmware image corrupt",
 	[-FWUPDATE_ERR_CONFIG_READ]      = "Configuration upload failure",
-	[-FWUPDATE_ERR_CONFIG_SAVE]      = "Error saving configuration"
+	[-FWUPDATE_ERR_CONFIG_SAVE]      = "Error saving configuration",
+	[-FWUPDATE_ERR_NOT_MINE]         = "Firmware file is not compatible"
     };
 
     switch (err) {

+ 1 - 0
esp32/max80/matchver_esp.c

@@ -0,0 +1 @@
+#include "matchver.c"

+ 1 - 0
esp32/max80/spz.h

@@ -25,6 +25,7 @@ struct spz_stream {
 	};
     };
     struct fw_header header;	    /* Header of currently processed chunk */
+    struct fw_header vmatch;	    /* Matched firmware version string */
     int err;			    /* Error code to return */
     bool eoi;			    /* Reached end of input */
     bool cleanup;		    /* Call inflateEnd() */

BIN
esp32/output/max80.ino.bin


+ 2 - 2
fpga/max80.qpf

@@ -19,12 +19,12 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 17:39:42  August 18, 2022
+# Date created = 00:29:54  September 02, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "17:39:42  August 18, 2022"
+DATE = "00:29:54  September 02, 2022"
 
 # Revisions
 

BIN
fpga/output/bypass.jic


BIN
fpga/output/bypass.rpd.gz


BIN
fpga/output/max80.fw


BIN
fpga/output/v1.fw


BIN
fpga/output/v1.jic


BIN
fpga/output/v1.rbf.gz


BIN
fpga/output/v1.rpd.gz


BIN
fpga/output/v1.sof


BIN
fpga/output/v1.svf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.fw


BIN
fpga/output/v2.jic


BIN
fpga/output/v2.rbf.gz


BIN
fpga/output/v2.rpd.gz


BIN
fpga/output/v2.sof


BIN
fpga/output/v2.svf.gz


BIN
fpga/output/v2.xsvf.gz


+ 1 - 1
rv32/checksum.h

@@ -1,4 +1,4 @@
 #ifndef CHECKSUM_H
 #define CHECKSUM_H
-#define SDRAM_SUM 0x5cde6c34
+#define SDRAM_SUM 0x66b2eb69
 #endif

+ 88 - 17
rv32/spiflash.c

@@ -3,6 +3,7 @@
 #include "spiflash.h"
 #include "esp.h"
 #include "matchver.h"
+#include "boardinfo_fpga.h"
 
 #if 1
 # include "console.h"
@@ -30,6 +31,7 @@ struct spz_stream {
 	};
     };
     struct fw_header header;	    /* Header of currently processed chunk */
+    struct fw_header vmatch;	    /* Matched firmware version string */
     int err;			    /* Error code to return */
     bool eoi;			    /* Reached end of input */
     bool cleanup;		    /* Call inflateEnd() */
@@ -451,7 +453,7 @@ static int spiflash_flash_chunk(spz_stream *spz)
 
 	    spiflash_check_block(spz, addr, &erase_mask, prog_mask);
 	    if (erase_mask) {
-		spz->err = SPIFLASH_ERR_ERASE_FAILED;
+		spz->err = FWUPDATE_ERR_ERASE_FAILED;
 		MSG("%04x left, ", erase_mask);
 		goto err;
 	    }
@@ -491,7 +493,7 @@ static int spiflash_flash_chunk(spz_stream *spz)
 	    if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,
 		       SPIFLASH_PAGE_SIZE)) {
 		MSG("verify ");
-		spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
+		spz->err = FWUPDATE_ERR_PROGRAM_FAILED;
 		goto err;
 	    }
 	    MSG("ok\n");
@@ -600,23 +602,89 @@ static int esp_ota_chunk(spz_stream *spz)
     return spz->err;
 }
 
-/* Process a data chunk; return a nonzero value if done */
-static int spiflash_process_chunk(spz_stream *spz)
+/* Get a piece of the chunk header */
+static int fwupdate_get_header_data(spz_stream *spz, void *buf, int len)
 {
     int rv;
-    char *str;
 
-    rv = spiflash_read_data(spz, &spz->header, sizeof spz->header);
+    rv = spiflash_read_data(spz, buf, len);
     if (spz->err)
 	return spz->err;
     else if (!rv)
 	return Z_STREAM_END;
-    else if (rv != sizeof spz->header)
+    else if (rv != len)
 	return spz->err = Z_STREAM_ERROR;
+    else
+	return Z_OK;
+}
 
-    if (spz->header.magic != FW_MAGIC) {
-	MSG("update: bad chunk header magic 0x%08x\n", spz->header.magic);
-	return spz->err = Z_DATA_ERROR;
+/* Get and validate a chunk header */
+static int fwupdate_get_header(spz_stream *spz)
+{
+    struct fw_header * const hdr = &spz->header;
+    uint8_t *hptr = (uint8_t *)hdr;
+    int rv;
+    unsigned int hlen;
+
+    memset(hdr, 0, sizeof *hdr);
+    hdr->vmax = -1;
+
+    rv = fwupdate_get_header_data(spz, hptr, FW_HDR_LEN_V1);
+    if (rv)
+	return rv;
+
+    switch (hdr->magic) {
+    case FW_MAGIC_V1:
+	hlen = FW_HDR_LEN_V1;
+	break;
+
+    case FW_MAGIC_V2:
+	hlen = FW_HDR_LEN_V2;
+	break;
+
+    default:
+	MSG("update: bad chunk header magic 0x%08x\n", hdr->magic);
+	hlen = 0;
+	rv = Z_DATA_ERROR;
+	break;
+    }
+
+    if (hlen > FW_HDR_LEN_V1) {
+	rv = fwupdate_get_header_data(spz, hptr + FW_HDR_LEN_V1,
+				      hlen - FW_HDR_LEN_V1);
+
+	if (rv == Z_STREAM_END)	/* Only valid for the first chunk */
+	    rv = Z_STREAM_ERROR;
+    }
+
+    return spz->err = rv;
+}
+
+/* Process a data chunk; return a nonzero value if done */
+static int spiflash_process_chunk(spz_stream *spz)
+{
+    int rv;
+    char *str;
+
+    rv = fwupdate_get_header(spz);
+    if (rv)
+	return rv;
+
+    if (spz->header.type != FDT_NOTE &&
+	spz->header.type != FDT_TARGET &&
+	spz->header.type != FDT_END &&
+	!(spz->header.flags & FDF_PRETARGET)) {
+	if (!spz->vmatch.magic) {
+	    /* No matching firmware target support */
+	    return spz->err = FWUPDATE_ERR_NOT_MINE;
+	}
+
+	if (spz->header.vmin > spz->vmatch.vmax ||
+	    spz->header.vmax < spz->vmatch.vmin ||
+	    ((spz->header.vmatch ^ spz->header.vmatch) & spz->header.vmask)) {
+	    /* Chunk not applicable to this target */
+	    goto skip;
+	}
     }
 
     con_printf("update: chunk type %u size %u addr 0x%08x\n",
@@ -630,14 +698,17 @@ static int spiflash_process_chunk(spz_stream *spz)
 	    goto skip;
 	return spiflash_flash_chunk(spz);
     case FDT_TARGET:
+    {
+	bool match;
 	str = spiflash_read_chunk_str(spz);
-	/* XXX: replace with proper matching algorithm */
-	if (!str || !match_version(spz->flash->target, str)) {
-	    MSG("update: this firmware file targets \"%s\", need \"%s\"\n",
-		str, spz->flash->target);
-	    return spz->err = Z_DATA_ERROR;
-	}
-	break;
+	match = match_version(board_info.version_str, str);
+	if (match || spz->header.magic == FW_MAGIC_V1)
+	    spz->vmatch = spz->header;
+
+	MSG("update: firmware file supports: %s%s\n",
+	    str, match ? " (match)" : "");
+	return Z_OK;
+    }
     case FDT_NOTE:
 	str = spiflash_read_chunk_str(spz);
 	MSG("update: %s\n", str);

+ 0 - 9
rv32/spiflash.h

@@ -247,15 +247,6 @@ struct spiflash {
     const char *target;		/* What are we programming? */
 };
 
-/*
- * Additional error codes
- */
-#define SPIFLASH_ERR_ERASE_FAILED	(-7)
-#define SPIFLASH_ERR_PROGRAM_FAILED	(-8)
-#define SPIFLASH_ERR_WRITE_PROTECT	(-9)
-#define SPIFLASH_ERR_NOT_READY		(-10)
-#define SPIFLASH_ERR_DETECT		(-11)
-
 /*
  * Top-level operations. These may return an error value from the ops
  * functions, any of the negative error values defined in zlib.h,