firmware-utils: mksercommfw: overhaul image creation
authorMathias Kresin <dev@kresin.me>
Sat, 12 Jan 2019 19:18:52 +0000 (20:18 +0100)
committerMathias Kresin <dev@kresin.me>
Tue, 15 Jan 2019 18:11:54 +0000 (19:11 +0100)
Move the zip compression into a build recipe. Pad the image using the
existing build recipes as well to remove duplicate functionality

Change the code to append header and footer in two steps. Allow to use a
fixed filename as the netgear update image does.

Use a fixed timestamp within the zip archive to make the images
reproducible.

Due to the changes we are now compatible to the gnu89 c standard used by
default on the buildbots and we don't need to force a more recent
standard anymore.

Beside all changes, the footer still looks wrong in compare to the
netgear update image.

Signed-off-by: Mathias Kresin <dev@kresin.me>
include/image-commands.mk
target/linux/ramips/image/mt76x8.mk
tools/firmware-utils/Makefile
tools/firmware-utils/src/mksercommfw.c

index 169c191..70c09ad 100644 (file)
@@ -168,6 +168,16 @@ define Build/gzip
        @mv $@.new $@
 endef
 
+define Build/zip
+       mkdir $@.tmp
+       mv $@ $@.tmp/$(1)
+
+       zip -j -X \
+               $(if $(SOURCE_DATE_EPOCH),--mtime="$(SOURCE_DATE_EPOCH)") \
+               $@ $@.tmp/$(if $(1),$(1),$@)
+       rm -rf $@.tmp
+endef
+
 define Build/jffs2
        rm -rf $(KDIR_TMP)/$(DEVICE_NAME)/jffs2 && \
                mkdir -p $(KDIR_TMP)/$(DEVICE_NAME)/jffs2/$$(dirname $(1)) && \
index 3a2d8e3..be3aeb1 100644 (file)
@@ -2,17 +2,22 @@
 # MT76x8 Profiles
 #
 
-DEVICE_VARS += SERCOMM_KERNEL_OFFSET SERCOMM_HWID SERCOMM_HWVER SERCOMM_SWVER
+DEVICE_VARS += SERCOMM_HWID SERCOMM_HWVER SERCOMM_SWVER
 
-define Build/mksercommfw
+define Build/sercom-seal
        $(STAGING_DIR_HOST)/bin/mksercommfw \
-               $@ \
-               $(SERCOMM_KERNEL_OFFSET) \
-               $(SERCOMM_HWID) \
-               $(SERCOMM_HWVER) \
-               $(SERCOMM_SWVER)
+               -i $@ \
+               -b $(SERCOMM_HWID) \
+               -r $(SERCOMM_HWVER) \
+               -v $(SERCOMM_SWVER) \
+               $(1)
 endef
 
+define Build/sercom-footer
+       $(call Build/sercom-seal,-f)
+endef
+
+
 define Device/tplink
   TPLINK_FLASHLAYOUT :=
   TPLINK_HWID :=
@@ -116,14 +121,14 @@ define Device/netgear_r6120
   IMAGE_SIZE := $(ralink_default_fw_size_16M)
   DEVICE_TITLE := Netgear AC1200 R6120
   DEVICE_PACKAGES := kmod-mt76x2 kmod-usb2 kmod-usb-ohci
-  SERCOMM_KERNEL_OFFSET := 0x90000
   SERCOMM_HWID := CGQ
   SERCOMM_HWVER := A001
   SERCOMM_SWVER := 0x0040
   IMAGES += factory.img
   IMAGE/default := append-kernel | pad-to $$$$(BLOCKSIZE)| append-rootfs | pad-rootfs
   IMAGE/sysupgrade.bin := $$(IMAGE/default) | append-metadata | check-size $$$$(IMAGE_SIZE)
-  IMAGE/factory.img := $$(IMAGE/default) | mksercommfw
+  IMAGE/factory.img := pad-extra 576k | $$(IMAGE/default) | \
+       sercom-footer | pad-to 128 | zip R6120.bin | sercom-seal
 endef
 TARGET_DEVICES += netgear_r6120
 
index 68782f3..864a3df 100644 (file)
@@ -84,7 +84,7 @@ define Host/Compile
        $(call cc,mkdhpimg buffalo-lib, -Wall)
        $(call cc,mkdlinkfw mkdlinkfw-lib, -lz -Wall --std=c99)
        $(call cc,dns313-header, -Wall)
-       $(call cc,mksercommfw, -Wall --std=gnu99)
+       $(call cc,mksercommfw, -Wall)
 endef
 
 define Host/Install
index 23e4dd0..f6f1d93 100644 (file)
@@ -5,6 +5,7 @@
 #include <unistd.h>
 #include <byteswap.h>
 #include <endian.h>
+#include <getopt.h>
 
 #if !defined(__BYTE_ORDER)
 #error "Unknown byte order"
 
 #if __BYTE_ORDER == __BIG_ENDIAN
 #define cpu_to_be32(x)  (x)
-#define be32_to_cpu(x)  (x)
-#define cpu_to_be16(x)  (x)
-#define be16_to_cpu(x)  (x)
 #elif __BYTE_ORDER == __LITTLE_ENDIAN
 #define cpu_to_be32(x)  bswap_32(x)
-#define be32_to_cpu(x)  bswap_32(x)
-#define cpu_to_be16(x)  bswap_16(x)
-#define be16_to_cpu(x)  bswap_16(x)
 #else
 #error "Unsupported endianness"
 #endif
@@ -33,9 +28,6 @@
 #endif
 
 #define ERR(...) {printf(__VA_ARGS__); }
-#define ALIGN(a,b) ((a) + ((b) - ((a) % (b))))
-#define ROOTFS_ALIGN 128
-#define HEADER_SIZE 71
 
 /*
  * Fw Header Layout for Netgear / Sercomm devices (bytes)
  * magic  : 63-69  ASCII
  * ChkSum : 511    Inverse value of the full image checksum while this location is 0x00
  */
-
 static const char* magic = "sErCoMm"; /* 7 */
-
-/* 7-11: version control/download control ? */
 static const unsigned char version[4] = { 0x00, 0x01, 0x00, 0x00 };
+static const int header_sz = 512;
+static const int footer_sz = 71;
 
-
-/* 512 onwards -> ZIP containing rootfs with the same Header */
+static int is_header = 1;
 
 struct file_info {
        char* file_name; /* name of the file */
@@ -68,23 +58,32 @@ struct file_info {
 
 static u_int8_t getCheckSum(char* data, int len) {
        u_int8_t new = 0;
+       int i;
 
        if (!data) {
                ERR("Invalid pointer provided!\n");
                return 0;
        }
 
-       for (int i = 0; i < len; i++) {
+       for (i = 0; i < len; i++) {
                new += data[i];
        }
 
        return new;
 }
 
-static int bufferFile(struct file_info* finfo) {
-       int fs = 0;
+/*
+ * read file into buffer
+ * add space for header/footer
+ */
+static int copyToOutputBuf(struct file_info* finfo) {
        FILE* fp = NULL;
 
+       int file_sz = 0;
+       int extra_sz;
+       int hdr_pos;
+       int img_pos;
+
        if (!finfo || !finfo->file_name) {
                ERR("Invalid pointer provided!\n");
                return -1;
@@ -100,25 +99,39 @@ static int bufferFile(struct file_info* finfo) {
        /* Get filesize */
        rewind(fp);
        fseek(fp, 0L, SEEK_END);
-       fs = ftell(fp);
+       file_sz = ftell(fp);
        rewind(fp);
 
-       if (fs < 0) {
+       if (file_sz < 1) {
                ERR("Error getting filesize: %s\n", finfo->file_name);
                fclose(fp);
                return -1;
        }
 
-       DBG("Filesize: %i\n", fs);
-       finfo->file_size = fs;
+       if (is_header) {
+               extra_sz = header_sz;
+               hdr_pos = 0;
+               img_pos = header_sz;
+       } else {
+               extra_sz = footer_sz;
+               hdr_pos = file_sz;
+               img_pos = 0;
+       }
+
+       DBG("Filesize: %i\n", file_sz);
+       finfo->file_size = file_sz + extra_sz;
 
-       if (!(finfo->file_data = malloc(fs))) {
+       if (!(finfo->file_data = malloc(finfo->file_size))) {
                ERR("Out of memory!\n");
                fclose(fp);
                return -1;
        }
 
-       if (fread(finfo->file_data, 1, fs, fp) != fs) {
+       /* init header/footer bytes */
+       memset(finfo->file_data + hdr_pos, 0, extra_sz);
+
+       /* read file and take care of leading header if exists */
+       if (fread(finfo->file_data + img_pos, 1, file_sz, fp) != file_sz) {
                ERR("Error reading file %s\n", finfo->file_name);
                fclose(fp);
                return -1;
@@ -127,7 +140,7 @@ static int bufferFile(struct file_info* finfo) {
        DBG("File: read successful\n");
        fclose(fp);
 
-       return 0;
+       return hdr_pos;
 }
 
 static int writeFile(struct file_info* finfo) {
@@ -157,266 +170,92 @@ static int writeFile(struct file_info* finfo) {
        return 0;
 }
 
-static void fi_clean(struct file_info* finfo) {
-       if (!finfo)
-               return;
-
-       if (finfo->file_name) {
-               finfo->file_name = NULL;
-       }
-
-       if (finfo->file_data) {
-               free(finfo->file_data);
-               finfo->file_data = NULL;
-       }
-
-       finfo->file_size = 0;
-}
-
 static void usage(char* argv[]) {
-    printf("Usage: %s <sysupgradefile> <kernel_offset> <HWID> <HWVER> <SWID>\n"
-           "All are positional arguments ...   \n"
-           "   sysupgradefile:     File with the kernel uimage at 0\n"
-           "   kernel_offset:      Offset where the kernel is located (decimal, hex or octal notation)\n"
-           "   HWID:               Hardware ID, ASCII\n"
-           "   HWVER:              Hardware Version, ASCII\n"
-           "   SWID:               Software Version (decimal, hex or octal notation)\n"
-           "   \n"
-           , argv[0]);
+       printf("Usage: %s [OPTIONS...]\n"
+              "\n"
+              "Options:\n"
+              "  -f            add sercom footer (if absent, header)\n"
+              "  -b <hwid>     use hardware id specified with <hwid> (ASCII)\n"
+              "  -r <hwrev>    use hardware revision specified with <hwrev> (ASCII)\n"
+              "  -v <version>  set image version to <version> (decimal, hex or octal notation)\n"
+              "  -i <file>     input file\n"
+              , argv[0]);
 }
 
 int main(int argc, char* argv[]) {
-       int ret = 1;
-       int rootfsname_sz;
-       int zipfsname_sz;
-       int zipcmd_sz;
-       u_int32_t kernel_offset = 0x90000; /* offset for the kernel inside the rootfs, default val */
-       u_int32_t swVer = 0;
-       struct file_info sysupgrade = { 0 };
-       struct file_info header = { 0 };
-       struct file_info rootfs = { 0 };
-       struct file_info zippedfs = { 0 };
        struct file_info image = { 0 };
+
        char* hwID = NULL;
        char* hwVer = NULL;
-       char* rootfsname = NULL;
-       char* zipfsname = NULL;
-       char* zipcmd = NULL;
+       u_int32_t swVer = 0;
        u_int8_t chkSum;
-
-       if (argc == 2) {
-               struct file_info myfile = { argv[1], 0, 0 };
-
-               if (bufferFile(&myfile))
-                       return 1;
-
-               chkSum = getCheckSum(myfile.file_data, myfile.file_size);
-               printf("Checksum for File: 0x%hhX\n", chkSum);
-
-               return 0;
+       int hdr_offset;
+
+       while ( 1 ) {
+               int c;
+
+               c = getopt(argc, argv, "b:i:r:v:f");
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'b':
+                       hwID = optarg;
+                       break;
+               case 'f':
+                       is_header = 0;
+                       break;
+               case 'i':
+                       image.file_name = optarg;
+                       break;
+               case 'r':
+                       hwVer = optarg;
+                       break;
+               case 'v':
+                       swVer = (u_int32_t) strtol(optarg, NULL, 0);
+                       swVer = cpu_to_be32(swVer);
+                       break;
+               default:
+                       usage(argv);
+                       return EXIT_FAILURE;
+               }
        }
 
-       if (argc != 6) {
-               usage(argv);
-               return 1;
+       if (!hwID || !hwVer || !image.file_name) {
+                       usage(argv);
+                       return EXIT_FAILURE;
        }
 
-       printf("Building fw image for sercomm devices ..\n");
-
-       /* process args */
-       hwID = argv[3];
-       hwVer = argv[4];
-
-       sysupgrade.file_name = argv[1];
-       image.file_name = argv[1];
-       kernel_offset = (u_int32_t) strtol(argv[2], NULL, 0);
-       swVer = (u_int32_t) strtol(argv[5], NULL, 0);
-       swVer = cpu_to_be32(swVer);
-
-       /* Check if files actually exist */
-       if (access(sysupgrade.file_name, (F_OK | R_OK))) {
-               /* Error */
-               ERR("File not found: %s\n", sysupgrade.file_name);
-               goto cleanup;
+       /*
+        * copy input to buffer, add extra space for header/footer and return
+        * header position
+        */
+       hdr_offset = copyToOutputBuf(&image);
+       if (hdr_offset < 0)
+               return EXIT_FAILURE;
+
+       DBG("Filling header: %s %s %2X %s\n", hwID, hwVer, swVer, magic);
+
+       strncpy(image.file_data + hdr_offset + 0, magic, 7);
+       memcpy(image.file_data + hdr_offset + 7, version, sizeof(version));
+       strncpy(image.file_data + hdr_offset + 11, hwID, 34);
+       strncpy(image.file_data + hdr_offset + 45, hwVer, 10);
+       memcpy(image.file_data + hdr_offset + 55, &swVer, sizeof(swVer));
+       strncpy(image.file_data + hdr_offset + 63, magic, 7);
+
+       /* calculate checksum and invert checksum */
+       if (is_header) {
+               chkSum = getCheckSum(image.file_data, image.file_size);
+               chkSum = (chkSum ^ 0xFF) + 1;
+               DBG("Checksum for Image: %hhX\n", chkSum);
+
+               /* write checksum to header */
+               image.file_data[511] = (char) chkSum;
        }
 
-       /* Calculate amount of required memory (incl. 0-term) */
-       rootfsname_sz = strlen(sysupgrade.file_name) + 7 + 1;
-       zipfsname_sz = strlen(sysupgrade.file_name) + 7 + 4 + 1;
-
-       /* Allocate required memory */
-       if (!(rootfsname = (char*) malloc(rootfsname_sz)) || !(zipfsname =
-                       (char*) malloc(zipfsname_sz))) {
-               /* Error */
-               ERR("Out of memory!\n");
-               goto cleanup;
-       }
-
-       /* Create filenames */
-       if (snprintf(rootfsname, rootfsname_sz, "%s.rootfs", sysupgrade.file_name)
-                       >= rootfsname_sz
-                       || snprintf(zipfsname, zipfsname_sz, "%s.rootfs.zip",
-                                       sysupgrade.file_name) >= zipfsname_sz) {
-               /* Error */
-               ERR("Buffer too small!\n");
-               goto cleanup;
-       }
-
-       /* Buffer all files */
-       if (bufferFile(&sysupgrade)) {
-               /* Error */
-               goto cleanup;
-       }
-
-       DBG("Building header: %s %s %2X %s\n", hwID, hwVer, swVer, magic);
-
-       /* Construct the firmware header/magic */
-       header.file_name = NULL;
-       header.file_size = HEADER_SIZE;
-
-       if (!(header.file_data = (char*) calloc(1, HEADER_SIZE))) {
-               /* Error */
-               ERR("Out of memory!\n");
-               goto cleanup;
-       }
-
-       strncpy(header.file_data + 0, magic, 7);
-       memcpy(header.file_data + 7, version, sizeof(version));
-       strncpy(header.file_data + 11, hwID, 34);
-       strncpy(header.file_data + 45, hwVer, 10);
-       memcpy(header.file_data + 55, &swVer, sizeof(swVer));
-       strncpy(header.file_data + 63, magic, 7);
-
-       DBG("Creating rootfs ..\n");
-
-       /* Construct a rootfs */
-       rootfs.file_name = rootfsname;
-       rootfs.file_size = ALIGN(
-                       sysupgrade.file_size + kernel_offset + header.file_size,
-                       ROOTFS_ALIGN);
-
-       if (!(rootfs.file_data = calloc(1, rootfs.file_size))) {
-               /* Error */
-               ERR("Out of memory!\n");
-               goto cleanup;
-       }
-
-       /* copy Owrt image to kernel location */
-       memcpy(rootfs.file_data + kernel_offset, sysupgrade.file_data,
-                       sysupgrade.file_size);
-
-       /* Append header after the owrt image.  The updater searches for it */
-       memcpy(rootfs.file_data + kernel_offset + sysupgrade.file_size,
-                       header.file_data, header.file_size);
-
-       /* Write to file */
-       if (writeFile(&rootfs)) {
-               /* Error */
-               goto cleanup;
-       }
-
-       /* Construct a zip */
-       DBG("Preparing to zip ..\n");
-
-       /* now that we got the rootfs, repeat the whole thing again(sorta):
-        * 1. zip the rootfs */
-       zipcmd_sz = 3 + 1 + strlen(zipfsname) + 1 + strlen(rootfs.file_name) + 1;
-
-       if (!(zipcmd = malloc(zipcmd_sz))) {
-               /* Error */
-               ERR("Out of memory!\n");
-               goto cleanup;
-       }
-
-       if (snprintf(zipcmd, zipcmd_sz, "%s %s %s", "zip", zipfsname,
-                       rootfs.file_name) >= zipcmd_sz) {
-               /* Error */
-               ERR("Buffer too small!\n");
-               goto cleanup;
-       }
-
-       if (system(zipcmd)) {
-               /* Error */
-               ERR("Error creating a zip file!\n");
-               goto cleanup;
-       }
-
-       /* and load zipped fs */
-       zippedfs.file_name = zipfsname;
-
-       if (bufferFile(&zippedfs)) {
-               /* Error */
-               goto cleanup;
-       }
-
-       DBG("Creating Image.\n");
-
-       /* 2. create new file 512 + rootfs size */
-       image.file_size = zippedfs.file_size + 512;
-       if (!(image.file_data = malloc(zippedfs.file_size + 512))) {
-               /* Error */
-               ERR("Out of memory!\n");
-               goto cleanup;
-       }
-
-       /* 3. add header to file */
-       memcpy(image.file_data, header.file_data, header.file_size);
-
-       /* 4. clear remaining space */
-       if (header.file_size < 512)
-               memset(image.file_data + header.file_size, 0, 512 - header.file_size);
-
-       /* 5. copy zipfile at loc 512 */
-       memcpy(image.file_data + 512, zippedfs.file_data, zippedfs.file_size);
-
-       /* 6. do a checksum run, and compute checksum */
-       chkSum = getCheckSum(image.file_data, image.file_size);
-
-       DBG("Checksum for Image: %hhX\n", chkSum);
-
-       /* 7. write the checksum inverted into byte 511 to bring it to 0 on verification */
-       chkSum = (chkSum ^ 0xFF) + 1;
-       image.file_data[511] = (char) chkSum;
-
-       chkSum = getCheckSum(image.file_data, image.file_size);
-       DBG("Checksum for after fix: %hhX\n", chkSum);
-
-       if (chkSum != 0) {
-               ERR("Invalid checksum!\n")
-               goto cleanup;
-       }
-
-       /* 8. pray that the updater will accept the file */
-       if (writeFile(&image)) {
-               /* Error */
-               goto cleanup;
-       }
-
-       /* All seems OK */
-       ret = 0;
-
-       cleanup:
-
-       if (rootfs.file_name && !access(rootfs.file_name, F_OK | W_OK))
-               remove(rootfs.file_name);
-
-       if (zippedfs.file_name && !access(zippedfs.file_name, F_OK | W_OK))
-               remove(zippedfs.file_name);
-
-       fi_clean(&sysupgrade);
-       fi_clean(&header);
-       fi_clean(&rootfs);
-       fi_clean(&zippedfs);
-       fi_clean(&image);
-
-       if (rootfsname)
-               free(rootfsname);
-
-       if (zipfsname)
-               free(zipfsname);
-
-       if (zipcmd)
-               free(zipcmd);
+       /* overwrite input file */
+       if (writeFile(&image))
+               return EXIT_FAILURE;
 
-       return ret;
+       return EXIT_SUCCESS;
 }