From: Ludwig Thomeczek 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 firmware-utils/mksercommfw: fix musl build * add missing for musl Signed-off-by: Andy Walsh 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 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 Signed-off-by: Koen Vandeputte 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 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 Signed-off-by: Kevin Darbyshire-Bryant 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 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 +#include +#include +#include +#include +#include +#include +#include + +#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 use hardware id specified with (ASCII)\n" + " -r use hardware revision specified with (ASCII)\n" + " -v set image version to (decimal, hex or octal notation)\n" + " -i 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; +}