From: Ludwig Thomeczek <ledesrc@wxorx.net>
Date: Tue, 12 Jun 2018 21:16:40 +0200
Subject: firmware-utils: add sercomm/netgear tool

This adds a tool to generate a firmware file accepted
by Netgear or sercomm devices.

They use a zip-packed rootfs with header and a custom
checksum. The generated Image can be flashed via the
nmrpflash tool or the webinterface of the router.

Signed-off-by: Ludwig Thomeczek <ledesrc@wxorx.net>

firmware-utils/mksercommfw: fix musl build

* add missing <sys/types.h> for musl

Signed-off-by: Andy Walsh <andy.walsh44+github@gmail.com>

firmware-utils/mksercommfw: fix build with clang/macOS

fixes error: non-void function 'main' should return a value

Fixes: FS#1770

Signed-off-by: Ryan Mounce <ryan@mounce.com.au>

ramips: fix image generation for mt76x8

Buildbot fails to generate images for targets also generating a
Sercomm binary with following error:

Opening file: /mnt/ramdisk/koen/firmware/builds/owrt_mt76x8/build_dir/target-mipsel_24kc_musl/linux-ramips_mt76x8/tmp/openwrt-ramips-mt76x8-netgear_r6120-squashfs-factory.img.rootfs.zip
Filesize: 3648606 .
mksercommfw: malloc.c:2427: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Makefile:287: recipe for target '/mnt/ramdisk/koen/firmware/builds/owrt_mt76x8/build_dir/target-mipsel_24kc_musl/linux-ramips_mt76x8/tmp/openwrt-ramips-mt76x8-netgear_r6120-squashfs-factory.img' failed

Debugging using valgrind shows stack corruption due to a buffer overflow.
The author of the generator assumes the filename ends with "root",
while it should be "rootfs".

Fix this by accounting for the 2 missing characters which solves the build issues.

More work is required to cleanup this source, which will be done later on.

Reported-by: Hannu Nyman <hannu.nyman@iki.fi>
Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>

ramips: fix netgear r6120 factory image generation

as indicated in commit c5bf408ed6bd "(ramips: fix image generation for mt76x8")
more rework was needed to fix the other issues.

Building on another machine, but using the same arch, showed
the application failing again for different reasons.

Fix this by completely rewriting the application, fixing following found issues:

- buffer overflows, resulting in stack corruption
- flaws in memory requirement calculations (too small, too large)
- memory leaks
- missing bounds checking on string handling
- non-reproducable images, by using unitilized memory in checksum calculation
- missing error handling, resulting in succes on specific image errors
- endianness errors when building on BE machines
- various minor build warnings
- documentation did not match the code actions (header item locations)
- allowing input to be decimal, hex or octal now

Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>

tools: firmware-utils: mksercommfw build on Darwin

asm/byteorder.h & hence __cpu_to_be32() doesn't exist on Darwin

Shamelessly copy some byte swap functions from oseama.c

Acked-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>

firmware-utils: mksercommfw: overhaul image creation

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>

diff --git a/include/image-commands.mk b/include/image-commands.mk
index 552d8db1cbacf533c12d0d8e2e5cffbe5591adb4..aec044294365bf5e964906e022d468d1a1c95fba 100644
--- a/include/image-commands.mk
+++ b/include/image-commands.mk
@@ -115,6 +115,16 @@ define Build/tplink-safeloader
 		$(if $(findstring sysupgrade,$(word 1,$(1))),-S) && mv $@.new $@ || rm -f $@
 endef
 
+define Build/mksercommfw
+	-$(STAGING_DIR_HOST)/bin/mksercommfw \
+		$@ \
+		$(KERNEL_OFFSET) \
+		$(HWID) \
+		$(HWVER) \
+		$(SWVER)
+endef
+
+
 define Build/append-dtb
 	cat $(KDIR)/image-$(firstword $(DEVICE_DTS)).dtb >> $@
 endef
@@ -152,6 +162,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)) && \
diff --git a/target/linux/ramips/image/mt76x8.mk b/target/linux/ramips/image/mt76x8.mk
index bc282666d8c1a4b6ce5beabe2b492331fb48a23a..f1bf78e0713a1bc47d4ac384efdac1d08c1bd8bc 100644
--- a/target/linux/ramips/image/mt76x8.mk
+++ b/target/linux/ramips/image/mt76x8.mk
@@ -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 :=
@@ -107,14 +112,14 @@ define Device/netgear_r6120
   IMAGE_SIZE := $(ralink_default_fw_size_16M)
   DEVICE_TITLE := Netgear R6120
   DEVICE_PACKAGES := kmod-usb2 kmod-usb-ohci
-  SERCOMM_KERNEL_OFFSET := 90000
   SERCOMM_HWID := CGQ
   SERCOMM_HWVER := A001
-  SERCOMM_SWVER := 0040
+  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
 
diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
index a6379e35eb01f1cbbe2b1ece3fc9eb20bcd68d90..ca7722163d68d028b88e4cca2f0457875e633af6 100644
--- a/tools/firmware-utils/Makefile
+++ b/tools/firmware-utils/Makefile
@@ -85,6 +85,7 @@ define Host/Compile
 	$(call cc,mkdhpimg buffalo-lib, -Wall)
 	$(call cc,mkdlinkfw mkdlinkfw-lib, -lz -Wall --std=gnu99)
 	$(call cc,dns313-header, -Wall)
+	$(call cc,mksercommfw, -Wall)
 endef
 
 define Host/Install
diff --git a/tools/firmware-utils/src/mksercommfw.c b/tools/firmware-utils/src/mksercommfw.c
new file mode 100644
index 0000000000000000000000000000000000000000..f6f1d93f37970be0cdf94e71a5e1093b3e92c040
--- /dev/null
+++ b/tools/firmware-utils/src/mksercommfw.c
@@ -0,0 +1,261 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <getopt.h>
+
+#if !defined(__BYTE_ORDER)
+#error "Unknown byte order"
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_be32(x)  (x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_be32(x)  bswap_32(x)
+#else
+#error "Unsupported endianness"
+#endif
+
+/* #define DEBUG 1 */
+
+#ifdef DEBUG
+#define DBG(...) {printf(__VA_ARGS__); }
+#else
+#define DBG(...) {}
+#endif
+
+#define ERR(...) {printf(__VA_ARGS__); }
+
+/*
+ * Fw Header Layout for Netgear / Sercomm devices (bytes)
+ *
+ * Size : 512 bytes + zipped image size
+ *
+ * Locations:
+ * magic  : 0-6    ASCII
+ * version: 7-11   fixed
+ * hwID   : 11-44  ASCII
+ * hwVer  : 45-54  ASCII
+ * swVer  : 55-62  uint32_t in BE
+ * magic  : 63-69  ASCII
+ * ChkSum : 511    Inverse value of the full image checksum while this location is 0x00
+ */
+static const char* magic = "sErCoMm"; /* 7 */
+static const unsigned char version[4] = { 0x00, 0x01, 0x00, 0x00 };
+static const int header_sz = 512;
+static const int footer_sz = 71;
+
+static int is_header = 1;
+
+struct file_info {
+	char* file_name; /* name of the file */
+	char* file_data; /* data of the file in memory */
+	u_int32_t file_size; /* length of the file */
+};
+
+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 (i = 0; i < len; i++) {
+		new += data[i];
+	}
+
+	return new;
+}
+
+/*
+ * 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;
+	}
+
+	DBG("Opening file: %s\n", finfo->file_name);
+
+	if (!(fp = fopen(finfo->file_name, "rb"))) {
+		ERR("Error opening file: %s\n", finfo->file_name);
+		return -1;
+	}
+
+	/* Get filesize */
+	rewind(fp);
+	fseek(fp, 0L, SEEK_END);
+	file_sz = ftell(fp);
+	rewind(fp);
+
+	if (file_sz < 1) {
+		ERR("Error getting filesize: %s\n", finfo->file_name);
+		fclose(fp);
+		return -1;
+	}
+
+	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(finfo->file_size))) {
+		ERR("Out of memory!\n");
+		fclose(fp);
+		return -1;
+	}
+
+	/* 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;
+	}
+
+	DBG("File: read successful\n");
+	fclose(fp);
+
+	return hdr_pos;
+}
+
+static int writeFile(struct file_info* finfo) {
+	FILE* fp;
+
+	if (!finfo || !finfo->file_name) {
+		ERR("Invalid pointer provided!\n");
+		return -1;
+	}
+
+	DBG("Opening file: %s\n", finfo->file_name);
+
+	if (!(fp = fopen(finfo->file_name, "w"))) {
+		ERR("Error opening file: %s\n", finfo->file_name);
+		return -1;
+	}
+
+	DBG("Writing file: %s\n", finfo->file_name);
+
+	if (fwrite(finfo->file_data, 1, finfo->file_size, fp) != finfo->file_size) {
+		ERR("Wanted to write, but something went wrong!\n");
+		fclose(fp);
+		return -1;
+	}
+
+	fclose(fp);
+	return 0;
+}
+
+static void usage(char* argv[]) {
+	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[]) {
+	struct file_info image = { 0 };
+
+	char* hwID = NULL;
+	char* hwVer = NULL;
+	u_int32_t swVer = 0;
+	u_int8_t chkSum;
+	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 (!hwID || !hwVer || !image.file_name) {
+			usage(argv);
+			return EXIT_FAILURE;
+	}
+
+	/*
+	 * 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;
+	}
+
+	/* overwrite input file */
+	if (writeFile(&image))
+		return EXIT_FAILURE;
+
+	return EXIT_SUCCESS;
+}
