2018-11-14 22:02:25 +00:00
|
|
|
From: David Bauer <mail@david-bauer.net>
|
|
|
|
Date: Wed, 22 Aug 2018 17:30:44 +0200
|
|
|
|
Subject: build: add mkrasimage
|
|
|
|
|
|
|
|
The current make-ras.sh image generation script for the ZyXEL NBG6617
|
|
|
|
has portability issues with bash. Because of this, factory images are
|
|
|
|
currently not built correctly by the OpenWRT buildbots.
|
|
|
|
|
|
|
|
This commit replaces the make-ras.sh by C-written mkrasimage.
|
|
|
|
|
|
|
|
The new mkrasimage is also compatible with other ZyXEL devices using
|
|
|
|
the ras image-format.
|
|
|
|
This is not tested with the NBG6616 but it correctly builds the
|
|
|
|
header for ZyXEL factory image.
|
|
|
|
|
|
|
|
Signed-off-by: David Bauer <mail@david-bauer.net>
|
|
|
|
|
|
|
|
diff --git a/include/image-commands.mk b/include/image-commands.mk
|
|
|
|
index 28b39c310e499caea4fe8a6d9ad6dceb5d95363b..552d8db1cbacf533c12d0d8e2e5cffbe5591adb4 100644
|
|
|
|
--- a/include/image-commands.mk
|
|
|
|
+++ b/include/image-commands.mk
|
|
|
|
@@ -49,17 +49,17 @@ define Build/eva-image
|
|
|
|
mv $@.new $@
|
|
|
|
endef
|
|
|
|
|
|
|
|
-define Build/make-ras
|
|
|
|
+define Build/zyxel-ras-image
|
|
|
|
let \
|
|
|
|
newsize="$(subst k,* 1024,$(RAS_ROOTFS_SIZE))"; \
|
|
|
|
- $(TOPDIR)/scripts/make-ras.sh \
|
|
|
|
- --board $(RAS_BOARD) \
|
|
|
|
- --version $(RAS_VERSION) \
|
|
|
|
- --kernel $(call param_get_default,kernel,$(1),$(IMAGE_KERNEL)) \
|
|
|
|
- --rootfs $@ \
|
|
|
|
- --rootfssize $$newsize \
|
|
|
|
- $@.new
|
|
|
|
- @mv $@.new $@
|
|
|
|
+ $(STAGING_DIR_HOST)/bin/mkrasimage \
|
|
|
|
+ -b $(RAS_BOARD) \
|
|
|
|
+ -v $(RAS_VERSION) \
|
|
|
|
+ -r $@ \
|
|
|
|
+ -s $$newsize \
|
|
|
|
+ -o $@.new \
|
|
|
|
+ $(if $(findstring separate-kernel,$(word 1,$(1))),-k $(IMAGE_KERNEL)) \
|
|
|
|
+ && mv $@.new $@
|
|
|
|
endef
|
|
|
|
|
|
|
|
define Build/netgear-chk
|
|
|
|
diff --git a/scripts/make-ras.sh b/scripts/make-ras.sh
|
|
|
|
deleted file mode 100755
|
|
|
|
index ccddaa0016b0c926d4737abb5757e7212b0a1157..0000000000000000000000000000000000000000
|
|
|
|
--- a/scripts/make-ras.sh
|
|
|
|
+++ /dev/null
|
|
|
|
@@ -1,196 +0,0 @@
|
|
|
|
-#!/usr/bin/env bash
|
|
|
|
-#
|
|
|
|
-# --- ZyXEL header format ---
|
|
|
|
-# Original Version by Benjamin Berg <benjamin@sipsolutions.net>
|
|
|
|
-#
|
|
|
|
-# The firmware image prefixed with a header (which is written into the MTD device).
|
|
|
|
-# The header is one erase block (~64KiB) in size, but the checksum only convers the
|
|
|
|
-# first 2KiB. Padding is 0xff. All integers are in big-endian.
|
|
|
|
-#
|
|
|
|
-# The checksum is always a 16-Bit System V checksum (sum -s) stored in a 32-Bit integer.
|
|
|
|
-#
|
|
|
|
-# 4 bytes: checksum of the rootfs image
|
|
|
|
-# 4 bytes: length of the contained rootfs image file (big endian)
|
|
|
|
-# 32 bytes: Firmware Version string (NUL terminated, 0xff padded)
|
|
|
|
-# 4 bytes: checksum over the header partition (big endian - see below)
|
|
|
|
-# 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded)
|
|
|
|
-# 4 bytes: checksum of the kernel partition
|
|
|
|
-# 4 bytes: length of the contained kernel image file (big endian)
|
|
|
|
-# rest: 0xff padding
|
|
|
|
-#
|
|
|
|
-# The checksums are calculated by adding up all bytes and if a 16bit
|
|
|
|
-# overflow occurs, one is added and the sum is masked to 16 bit:
|
|
|
|
-# csum = csum + databyte; if (csum > 0xffff) { csum += 1; csum &= 0xffff };
|
|
|
|
-# Should the file have an odd number of bytes then the byte len-0x800 is
|
|
|
|
-# used additionally.
|
|
|
|
-#
|
|
|
|
-# The checksum for the header is calculated over the first 2048 bytes with
|
|
|
|
-# the rootfs image checksum as the placeholder during calculation.
|
|
|
|
-#
|
|
|
|
-# The header is padded with 0xff to the erase block size of the device.
|
|
|
|
-#
|
|
|
|
-board=""
|
|
|
|
-version=""
|
|
|
|
-kernel=""
|
|
|
|
-rootfs=""
|
|
|
|
-outfile=""
|
|
|
|
-err=""
|
|
|
|
-
|
|
|
|
-while [ "$1" ]; do
|
|
|
|
- case "$1" in
|
|
|
|
- "--board")
|
|
|
|
- board="$2"
|
|
|
|
- shift
|
|
|
|
- shift
|
|
|
|
- continue
|
|
|
|
- ;;
|
|
|
|
- "--version")
|
|
|
|
- version="$2"
|
|
|
|
- shift
|
|
|
|
- shift
|
|
|
|
- continue
|
|
|
|
- ;;
|
|
|
|
- "--kernel")
|
|
|
|
- kernel="$2"
|
|
|
|
- shift
|
|
|
|
- shift
|
|
|
|
- continue
|
|
|
|
- ;;
|
|
|
|
- "--rootfs")
|
|
|
|
- rootfs="$2"
|
|
|
|
- shift
|
|
|
|
- shift
|
|
|
|
- continue
|
|
|
|
- ;;
|
|
|
|
- "--rootfssize")
|
|
|
|
- rootfssize="$2"
|
|
|
|
- shift
|
|
|
|
- shift
|
|
|
|
- continue
|
|
|
|
- ;;
|
|
|
|
- *)
|
|
|
|
- if [ ! "$outfile" ]; then
|
|
|
|
- outfile=$1
|
|
|
|
- shift
|
|
|
|
- continue
|
|
|
|
- fi
|
|
|
|
- ;;
|
|
|
|
- esac
|
|
|
|
-done
|
|
|
|
-
|
|
|
|
-if [ ! -n "$board" -o ! -n "$version" -o ! -r "$kernel" -o ! -r "$rootfs" -o ! "$rootfssize" -o ! "$outfile" ]; then
|
|
|
|
- echo "syntax: $0 [--board ras-boardname] [--version ras-version] [--kernel kernelimage] [--rootfs rootfs] out"
|
|
|
|
- exit 1
|
|
|
|
-fi
|
|
|
|
-
|
|
|
|
-rootfs_len=$(wc -c < "$rootfs")
|
|
|
|
-
|
|
|
|
-if [ "$rootfs_len" -lt "$rootfssize" ]; then
|
|
|
|
- dd if=$rootfs of=$rootfs.new bs=$rootfssize conv=sync
|
|
|
|
- mv $rootfs.new $rootfs
|
|
|
|
-fi
|
|
|
|
-
|
|
|
|
-if [ ${#version} -ge 28 ]; then
|
|
|
|
- echo "version: '$version' is too long"
|
|
|
|
- exit 1
|
|
|
|
-fi
|
|
|
|
-
|
|
|
|
-tmpdir="$( mktemp -d 2> /dev/null )"
|
|
|
|
-if [ -z "$tmpdir" ]; then
|
|
|
|
- # try OSX signature
|
|
|
|
- tmpdir="$( mktemp -t 'ubitmp' -d )"
|
|
|
|
-fi
|
|
|
|
-
|
|
|
|
-if [ -z "$tmpdir" ]; then
|
|
|
|
- exit 1
|
|
|
|
-fi
|
|
|
|
-
|
|
|
|
-to_be() {
|
|
|
|
- local val="$1"
|
|
|
|
- local size="$2"
|
|
|
|
-
|
|
|
|
- case "$size" in
|
|
|
|
- 4)
|
|
|
|
- echo $(( "$val" >> 24 | ("$val" & 0xff0000) >> 8 | ("$val" & 0xff00) << 8 | ("$val" & 0xff) << 24 ))
|
|
|
|
- ;;
|
|
|
|
- 2)
|
|
|
|
- echo $(( "$val" >> 8 | ("$val" & 0xff) << 8))
|
|
|
|
- ;;
|
|
|
|
- esac
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-checksum_file() {
|
|
|
|
- local file=$1
|
|
|
|
-
|
|
|
|
- # ZyXEL seems to use System V sum mode... Now this is classy, who would have thought?!
|
|
|
|
- echo $(sum -s ${file} | cut -f1 -d" ")
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-append_bin() {
|
|
|
|
- local val=$1
|
|
|
|
- local size=$2
|
|
|
|
- local file=$3
|
|
|
|
-
|
|
|
|
- while [ "$size" -ne 0 ]; do
|
|
|
|
- printf \\$(printf %o $(("$val" & 0xff))) >> "$file"
|
|
|
|
- val=$(($val >> 8))
|
|
|
|
- let size-=1
|
|
|
|
- done
|
|
|
|
- return
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-tf=${tmpdir}/out
|
|
|
|
-pad=$(printf '%0.1s' $(printf "\xff"){1..64})
|
|
|
|
-
|
|
|
|
-rootfs_header_file="$tmpdir/rootfs_header"
|
|
|
|
-rootfs_chksum=$(to_be $(checksum_file ${rootfs}) 4)
|
|
|
|
-rootfs_len=$(to_be $(wc -c < "$rootfs") 4)
|
|
|
|
-
|
|
|
|
-versionpadlen=$(( 32 - ( ${#version} + 1) ))
|
|
|
|
-
|
|
|
|
-# 4 bytes: checksum of the rootfs image
|
|
|
|
-append_bin "$rootfs_chksum" 4 "$rootfs_header_file"
|
|
|
|
-# 4 bytes: length of the contained rootfs image file (big endian)
|
|
|
|
-append_bin "$rootfs_len" 4 "$rootfs_header_file"
|
|
|
|
-# 32 bytes: Firmware Version string (NUL terminated, 0xff padded)
|
|
|
|
-printf "%s\x00%.*s" "$version" "$versionpadlen" "$pad" >> "$rootfs_header_file"
|
|
|
|
-
|
|
|
|
-kernel_header_file="$tmpdir/kernel_header"
|
|
|
|
-kernel_chksum=$(to_be $(checksum_file ${kernel}) 4)
|
|
|
|
-kernel_len=$(to_be $(wc -c < "$kernel") 4)
|
|
|
|
-
|
|
|
|
-# 4 bytes: checksum of the kernel image
|
|
|
|
-append_bin "$kernel_chksum" 4 "$kernel_header_file"
|
|
|
|
-# 4 bytes: length of the contained kernel image file (big endian)
|
|
|
|
-append_bin "$kernel_len" 4 "$kernel_header_file"
|
|
|
|
-
|
|
|
|
-board_header_file="$tmpdir/board_header"
|
|
|
|
-board_file="$tmpdir/board"
|
|
|
|
-boardpadlen=$(( 64 - ( ${#board} + 1) ))
|
|
|
|
-# 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded)
|
|
|
|
-printf "%s\x00%.*s" "$board" "$boardpadlen" "$pad" > "$board_file"
|
|
|
|
-cat "$kernel_header_file" >> "$board_file"
|
|
|
|
-printf "%.12s" "$pad" >> "$board_file"
|
|
|
|
-# rest: 0xff padding
|
|
|
|
-for i in {1..511}; do
|
|
|
|
- printf "%s%s" "$pad" "$pad" >> "$board_file"
|
|
|
|
-done
|
|
|
|
-
|
|
|
|
-tmp_board_file="$tmpdir/tmp_board_file"
|
|
|
|
-cat "$rootfs_header_file" > "$tmp_board_file"
|
|
|
|
-
|
|
|
|
-# The checksum for the header is calculated over the first 2048 bytes with
|
|
|
|
-# the rootfs image checksum as the placeholder during calculation.
|
|
|
|
-append_bin "$rootfs_chksum" 4 "$tmp_board_file"
|
|
|
|
-cat "$board_file" >> "$tmp_board_file"
|
|
|
|
-
|
|
|
|
-truncate -s 2048 $tmp_board_file
|
|
|
|
-board_chksum=$(to_be $(checksum_file ${tmp_board_file}) 4)
|
|
|
|
-
|
|
|
|
-# 4 bytes: checksum over the header partition (big endian)
|
|
|
|
-append_bin "$board_chksum" 4 "$board_header_file"
|
|
|
|
-cat "$board_file" >> "$board_header_file"
|
|
|
|
-
|
|
|
|
-cat "$rootfs_header_file" "$board_header_file" "$rootfs" "$kernel" > "$outfile"
|
|
|
|
-
|
|
|
|
-rm -rf "$tmpdir"
|
|
|
|
diff --git a/target/linux/ar71xx/image/generic.mk b/target/linux/ar71xx/image/generic.mk
|
|
|
|
index 640557532c8a02f37bc6f84ade8cb34e7172162d..4568b656219419e9ca1156c6716bd2124074cb32 100644
|
|
|
|
--- a/target/linux/ar71xx/image/generic.mk
|
|
|
|
+++ b/target/linux/ar71xx/image/generic.mk
|
|
|
|
@@ -1086,8 +1086,12 @@ define Device/NBG6616
|
|
|
|
IMAGE_SIZE := 15323k
|
|
|
|
MTDPARTS := spi0.0:192k(u-boot)ro,64k(env)ro,64k(RFdata)ro,384k(zyxel_rfsd),384k(romd),64k(header),2048k(kernel),13184k(rootfs),15232k@0x120000(firmware)
|
|
|
|
CMDLINE += mem=128M
|
|
|
|
- IMAGES := sysupgrade.bin
|
|
|
|
+ RAS_BOARD := NBG6616
|
|
|
|
+ RAS_ROOTFS_SIZE := 14464k
|
|
|
|
+ RAS_VERSION := "$(VERSION_DIST) $(REVISION)"
|
|
|
|
+ IMAGES := factory.bin sysupgrade.bin
|
|
|
|
KERNEL := kernel-bin | patch-cmdline | lzma | uImage lzma | jffs2 boot/vmlinux.lzma.uImage
|
|
|
|
+ IMAGE/factory.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-rootfs | pad-rootfs | pad-to 64k | check-size $$$$(IMAGE_SIZE) | zyxel-ras-image
|
|
|
|
IMAGE/sysupgrade.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE)
|
|
|
|
# We cannot currently build a factory image. It is the sysupgrade image
|
|
|
|
# prefixed with a header (which is actually written into the MTD device).
|
|
|
|
diff --git a/target/linux/ipq40xx/image/Makefile b/target/linux/ipq40xx/image/Makefile
|
2019-04-21 17:39:27 +00:00
|
|
|
index cb79baccd21b3fa7f35df543bfca1a7d6ba8f83f..a0f81f7d631b6c53a5612dee172e752a9fecd06d 100644
|
2018-11-14 22:02:25 +00:00
|
|
|
--- a/target/linux/ipq40xx/image/Makefile
|
|
|
|
+++ b/target/linux/ipq40xx/image/Makefile
|
|
|
|
@@ -221,7 +221,7 @@ define Device/zyxel_nbg6617
|
|
|
|
# at least as large as the one of the initial firmware image (not the current
|
|
|
|
# one on the device). This only applies to the Web-UI, the bootlaoder ignores
|
|
|
|
# this minimum-size. However, the larger image can be flashed both ways.
|
|
|
|
- IMAGE/factory.bin := append-rootfs | pad-rootfs | check-size $$$$(ROOTFS_SIZE) | make-ras
|
|
|
|
+ IMAGE/factory.bin := append-rootfs | pad-rootfs | pad-to 64k | check-size $$$$(ROOTFS_SIZE) | zyxel-ras-image separate-kernel
|
|
|
|
IMAGE/sysupgrade.bin/squashfs := append-rootfs | pad-rootfs | check-size $$$$(ROOTFS_SIZE) | sysupgrade-tar rootfs=$$$$@ | append-metadata
|
|
|
|
DEVICE_PACKAGES := ipq-wifi-zyxel_nbg6617 uboot-envtools
|
|
|
|
endef
|
|
|
|
diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile
|
|
|
|
index 2902af3231a87592e9c040e7e7e1e4e1958a01a5..a7f740ff62e05c7f6f24caa6da538302abee8d8f 100644
|
|
|
|
--- a/target/linux/ipq806x/image/Makefile
|
|
|
|
+++ b/target/linux/ipq806x/image/Makefile
|
|
|
|
@@ -67,7 +67,8 @@ define Device/ZyXELImage
|
|
|
|
KERNEL_SUFFIX := -uImage
|
|
|
|
KERNEL = kernel-bin | append-dtb | uImage none | pad-to $${KERNEL_SIZE}
|
|
|
|
KERNEL_NAME := zImage
|
|
|
|
- IMAGES := sysupgrade.bin mmcblk0p5-rootfs.bin mmcblk0p4-kernel.bin
|
|
|
|
+ IMAGES := factory.bin sysupgrade.bin mmcblk0p5-rootfs.bin mmcblk0p4-kernel.bin
|
|
|
|
+ IMAGE/factory.bin := append-rootfs | pad-rootfs | pad-to $$$$(BLOCKSIZE) | zyxel-ras-image separate-kernel
|
|
|
|
IMAGE/sysupgrade.bin/squashfs := append-rootfs | pad-to $$$${BLOCKSIZE} | sysupgrade-tar rootfs=$$$$@ | append-metadata
|
|
|
|
IMAGE/mmcblk0p5-rootfs.bin := append-rootfs | pad-rootfs | pad-to $$$${BLOCKSIZE}
|
|
|
|
IMAGE/mmcblk0p4-kernel.bin := append-kernel
|
|
|
|
@@ -245,6 +246,9 @@ define Device/zyxel_nbg6817
|
|
|
|
KERNEL_SIZE := 4096k
|
|
|
|
BLOCKSIZE := 64k
|
|
|
|
BOARD_NAME := nbg6817
|
|
|
|
+ RAS_BOARD := NBG6817
|
|
|
|
+ RAS_ROOTFS_SIZE := 20934k
|
|
|
|
+ RAS_VERSION := "V1.99(OWRT.9999)C0"
|
|
|
|
SUPPORTED_DEVICES += nbg6817
|
|
|
|
DEVICE_TITLE := ZyXEL NBG6817
|
|
|
|
DEVICE_PACKAGES := ath10k-firmware-qca9984 e2fsprogs kmod-fs-ext4 losetup
|
|
|
|
diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
|
|
|
|
index 4b4af999088455e3d9231b227fecfa71c8284b2f..a6379e35eb01f1cbbe2b1ece3fc9eb20bcd68d90 100644
|
|
|
|
--- a/tools/firmware-utils/Makefile
|
|
|
|
+++ b/tools/firmware-utils/Makefile
|
|
|
|
@@ -70,6 +70,7 @@ define Host/Compile
|
|
|
|
$(call cc,fix-u-media-header cyg_crc32,-Wall)
|
|
|
|
$(call cc,hcsmakeimage bcmalgo)
|
|
|
|
$(call cc,mkporayfw, -Wall)
|
|
|
|
+ $(call cc,mkrasimage, --std=gnu99)
|
|
|
|
$(call cc,mkhilinkfw, -lcrypto)
|
|
|
|
$(call cc,mkdcs932, -Wall)
|
|
|
|
$(call cc,mkheader_gemtek,-lz)
|
|
|
|
diff --git a/tools/firmware-utils/src/mkrasimage.c b/tools/firmware-utils/src/mkrasimage.c
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000000000000000000000000000000000000..8eee29cc086a04104b51e263aeac0e73340056ca
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/tools/firmware-utils/src/mkrasimage.c
|
|
|
|
@@ -0,0 +1,459 @@
|
|
|
|
+/*
|
|
|
|
+ * --- ZyXEL header format ---
|
|
|
|
+ * Original Version by Benjamin Berg <benjamin@sipsolutions.net>
|
|
|
|
+ * C implementation based on generation-script by Christian Lamparter <chunkeey@gmail.com>
|
|
|
|
+ *
|
|
|
|
+ * The firmware image prefixed with a header (which is written into the MTD device).
|
|
|
|
+ * The header is one erase block (~64KiB) in size, but the checksum only convers the
|
|
|
|
+ * first 2KiB. Padding is 0xff. All integers are in big-endian.
|
|
|
|
+ *
|
|
|
|
+ * The checksum is always a 16-Bit System V checksum (sum -s) stored in a 32-Bit integer.
|
|
|
|
+ *
|
|
|
|
+ * 4 bytes: checksum of the rootfs image
|
|
|
|
+ * 4 bytes: length of the contained rootfs image file (big endian)
|
|
|
|
+ * 32 bytes: Firmware Version string (NUL terminated, 0xff padded)
|
|
|
|
+ * 4 bytes: checksum over the header partition (big endian - see below)
|
|
|
|
+ * 64 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded)
|
|
|
|
+ * 4 bytes: checksum of the kernel partition
|
|
|
|
+ * 4 bytes: length of the contained kernel image file (big endian)
|
|
|
|
+ * rest: 0xff padding (To erase block size)
|
|
|
|
+ *
|
|
|
|
+ * The kernel partition checksum and length is not used for every device.
|
|
|
|
+ * If it's notused, pad those 8 bytes with 0xFF.
|
|
|
|
+ *
|
|
|
|
+ * The checksums are calculated by adding up all bytes and if a 16bit
|
|
|
|
+ * overflow occurs, one is added and the sum is masked to 16 bit:
|
|
|
|
+ * csum = csum + databyte; if (csum > 0xffff) { csum += 1; csum &= 0xffff };
|
|
|
|
+ * Should the file have an odd number of bytes then the byte len-0x800 is
|
|
|
|
+ * used additionally.
|
|
|
|
+ *
|
|
|
|
+ * The checksum for the header is calculated over the first 2048 bytes with
|
|
|
|
+ * the rootfs image checksum as the placeholder during calculation.
|
|
|
|
+ *
|
|
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
|
|
+ * under the terms of the GNU General Public License version 2 as published
|
|
|
|
+ * by the Free Software Foundation.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+#include <fcntl.h>
|
|
|
|
+#include <getopt.h>
|
|
|
|
+#include <libgen.h>
|
|
|
|
+#include <stdio.h>
|
|
|
|
+#include <string.h>
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+#include <unistd.h>
|
|
|
|
+
|
|
|
|
+#include <sys/mman.h>
|
|
|
|
+#include <sys/stat.h>
|
|
|
|
+
|
|
|
|
+#include <arpa/inet.h>
|
|
|
|
+
|
|
|
|
+#define VERSION_STRING_LEN 31
|
|
|
|
+#define ROOTFS_HEADER_LEN 40
|
|
|
|
+
|
|
|
|
+#define KERNEL_HEADER_LEN 8
|
|
|
|
+
|
|
|
|
+#define BOARD_NAME_LEN 64
|
|
|
|
+#define BOARD_HEADER_LEN 68
|
|
|
|
+
|
|
|
|
+#define HEADER_PARTITION_CALC_LENGTH 2048
|
|
|
|
+#define HEADER_PARTITION_LENGTH 0x10000
|
|
|
|
+
|
|
|
|
+struct file_info {
|
|
|
|
+ char *name; /* name of the file */
|
|
|
|
+ char *data; /* file content */
|
|
|
|
+ size_t size; /* length of the file */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static char *progname;
|
|
|
|
+
|
|
|
|
+static char *board_name = 0;
|
|
|
|
+static char *version_name = 0;
|
|
|
|
+static unsigned int rootfs_size = 0;
|
|
|
|
+
|
|
|
|
+static struct file_info kernel = { NULL, NULL, 0 };
|
|
|
|
+static struct file_info rootfs = { NULL, NULL, 0 };
|
|
|
|
+static struct file_info rootfs_out = { NULL, NULL, 0 };
|
|
|
|
+static struct file_info out = { NULL, NULL, 0 };
|
|
|
|
+
|
|
|
|
+#define ERR(fmt, ...) do { \
|
|
|
|
+ fprintf(stderr, "[%s] *** error: " fmt "\n", \
|
|
|
|
+ progname, ## __VA_ARGS__ ); \
|
|
|
|
+} while (0)
|
|
|
|
+
|
|
|
|
+void map_file(struct file_info *finfo)
|
|
|
|
+{
|
|
|
|
+ struct stat file_stat = {0};
|
|
|
|
+ int fd;
|
|
|
|
+
|
|
|
|
+ fd = open(finfo->name, O_RDONLY, (mode_t)0600);
|
|
|
|
+ if (fd == -1) {
|
|
|
|
+ ERR("Error while opening file %s.", finfo->name);
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (fstat(fd, &file_stat) == -1) {
|
|
|
|
+ ERR("Error getting file size for %s.", finfo->name);
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ finfo->size = file_stat.st_size;
|
|
|
|
+ finfo->data = mmap(0, finfo->size, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
+
|
|
|
|
+ if (finfo->data == MAP_FAILED) {
|
|
|
|
+ ERR("Error mapping file %s.", finfo->name);
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ close(fd);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void unmap_file(struct file_info *finfo)
|
|
|
|
+{
|
|
|
|
+ if(munmap(finfo->data, finfo->size) == -1) {
|
|
|
|
+ ERR("Error unmapping file %s.", finfo->name);
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void write_file(struct file_info *finfo)
|
|
|
|
+{
|
|
|
|
+ FILE *fout = fopen(finfo->name, "w");
|
|
|
|
+
|
|
|
|
+ fwrite(finfo->data, finfo->size, 1, fout);
|
|
|
|
+
|
|
|
|
+ if (ferror(fout)) {
|
|
|
|
+ ERR("Wanted to write, but something went wrong.");
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fclose(fout);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void usage(int status)
|
|
|
|
+{
|
|
|
|
+ FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
|
|
|
|
+
|
|
|
|
+ fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
|
|
|
|
+ fprintf(stream,
|
|
|
|
+ "\n"
|
|
|
|
+ "Options:\n"
|
|
|
|
+ " -k <kernel> path for kernel image\n"
|
|
|
|
+ " -r <rootfs> path for rootfs image\n"
|
|
|
|
+ " -s <rfssize> size of output rootfs\n"
|
|
|
|
+ " -v <version> version string\n"
|
|
|
|
+ " -b <boardname> name of board to generate image for\n"
|
|
|
|
+ " -o <out_name> name of output image\n"
|
|
|
|
+ " -h show this screen\n"
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ exit(status);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int sysv_chksm(const unsigned char *data, int size)
|
|
|
|
+{
|
|
|
|
+ int r;
|
|
|
|
+ int checksum;
|
|
|
|
+ unsigned int s = 0; /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < size; i++) {
|
|
|
|
+ s += data[i];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
|
|
|
|
+ checksum = (r & 0xffff) + (r >> 16);
|
|
|
|
+
|
|
|
|
+ return checksum;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int zyxel_chksm(const unsigned char *data, int size)
|
|
|
|
+{
|
|
|
|
+ return htonl(sysv_chksm(data, size));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+char *generate_rootfs_header(struct file_info filesystem, char *version)
|
|
|
|
+{
|
|
|
|
+ size_t version_string_length;
|
|
|
|
+ unsigned int chksm, size;
|
|
|
|
+ char *rootfs_header;
|
|
|
|
+ size_t ptr = 0;
|
|
|
|
+
|
|
|
|
+ rootfs_header = malloc(ROOTFS_HEADER_LEN);
|
|
|
|
+ if (!rootfs_header) {
|
|
|
|
+ ERR("Couldn't allocate memory for rootfs header!");
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Prepare padding for firmware-version string here */
|
|
|
|
+ memset(rootfs_header, 0xff, ROOTFS_HEADER_LEN);
|
|
|
|
+
|
|
|
|
+ chksm = zyxel_chksm((const unsigned char *)filesystem.data, filesystem.size);
|
|
|
|
+ size = htonl(filesystem.size);
|
|
|
|
+
|
|
|
|
+ /* 4 bytes: checksum of the rootfs image */
|
|
|
|
+ memcpy(rootfs_header + ptr, &chksm, 4);
|
|
|
|
+ ptr += 4;
|
|
|
|
+
|
|
|
|
+ /* 4 bytes: length of the contained rootfs image file (big endian) */
|
|
|
|
+ memcpy(rootfs_header + ptr, &size, 4);
|
|
|
|
+ ptr += 4;
|
|
|
|
+
|
|
|
|
+ /* 32 bytes: Firmware Version string (NUL terminated, 0xff padded) */
|
|
|
|
+ version_string_length = strlen(version) <= VERSION_STRING_LEN ? strlen(version) : VERSION_STRING_LEN;
|
|
|
|
+ memcpy(rootfs_header + ptr, version, version_string_length);
|
|
|
|
+ ptr += version_string_length;
|
|
|
|
+ /* Add null-terminator */
|
|
|
|
+ rootfs_header[ptr] = 0x0;
|
|
|
|
+
|
|
|
|
+ return rootfs_header;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+char *generate_kernel_header(struct file_info kernel)
|
|
|
|
+{
|
|
|
|
+ unsigned int chksm, size;
|
|
|
|
+ char *kernel_header;
|
|
|
|
+ size_t ptr = 0;
|
|
|
|
+
|
|
|
|
+ kernel_header = malloc(KERNEL_HEADER_LEN);
|
|
|
|
+ if (!kernel_header) {
|
|
|
|
+ ERR("Couldn't allocate memory for kernel header!");
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ chksm = zyxel_chksm((const unsigned char *)kernel.data, kernel.size);
|
|
|
|
+ size = htonl(kernel.size);
|
|
|
|
+
|
|
|
|
+ /* 4 bytes: checksum of the kernel image */
|
|
|
|
+ memcpy(kernel_header + ptr, &chksm, 4);
|
|
|
|
+ ptr += 4;
|
|
|
|
+
|
|
|
|
+ /* 4 bytes: length of the contained kernel image file (big endian) */
|
|
|
|
+ memcpy(kernel_header + ptr, &size, 4);
|
|
|
|
+
|
|
|
|
+ return kernel_header;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+unsigned int generate_board_header_checksum(char *kernel_hdr, char *rootfs_hdr, char *boardname)
|
|
|
|
+{
|
|
|
|
+ char *board_hdr_tmp;
|
|
|
|
+ unsigned int sum;
|
|
|
|
+ size_t ptr = 0;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The checksum of the board header is calculated over the first 2048 bytes of
|
|
|
|
+ * the header partition with the rootfs checksum used as a placeholder for then
|
|
|
|
+ * board checksum we calculate in this step. The checksum gained from this step
|
|
|
|
+ * is then used for the final board header partition.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ board_hdr_tmp = malloc(HEADER_PARTITION_CALC_LENGTH);
|
|
|
|
+ if (!board_hdr_tmp) {
|
|
|
|
+ ERR("Couldn't allocate memory for temporary board header!");
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+ memset(board_hdr_tmp, 0xff, HEADER_PARTITION_CALC_LENGTH);
|
|
|
|
+
|
|
|
|
+ /* 40 bytes: RootFS header */
|
|
|
|
+ memcpy(board_hdr_tmp, rootfs_hdr, ROOTFS_HEADER_LEN);
|
|
|
|
+ ptr += ROOTFS_HEADER_LEN;
|
|
|
|
+
|
|
|
|
+ /* 4 bytes: RootFS checksum (BE) as placeholder for board-header checksum */
|
|
|
|
+ memcpy(board_hdr_tmp + ptr, rootfs_hdr, 4);
|
|
|
|
+ ptr += 4;
|
|
|
|
+
|
|
|
|
+ /* 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded) */
|
|
|
|
+ memcpy(board_hdr_tmp + ptr, boardname, strlen(boardname));
|
|
|
|
+ ptr += strlen(boardname);
|
|
|
|
+ /* Add null-terminator */
|
|
|
|
+ board_hdr_tmp[ptr] = 0x0;
|
|
|
|
+ ptr = ROOTFS_HEADER_LEN + 4 + BOARD_NAME_LEN;
|
|
|
|
+
|
|
|
|
+ /* 8 bytes: Kernel header */
|
|
|
|
+ if (kernel_hdr)
|
|
|
|
+ memcpy(board_hdr_tmp + ptr, kernel_hdr, 8);
|
|
|
|
+
|
|
|
|
+ /* Calculate the checksum over the first 2048 bytes */
|
|
|
|
+ sum = zyxel_chksm((const unsigned char *)board_hdr_tmp, HEADER_PARTITION_CALC_LENGTH);
|
|
|
|
+ free(board_hdr_tmp);
|
|
|
|
+ return sum;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+char *generate_board_header(char *kernel_hdr, char *rootfs_hdr, char *boardname)
|
|
|
|
+{
|
|
|
|
+ unsigned int board_checksum;
|
|
|
|
+ char *board_hdr;
|
|
|
|
+
|
|
|
|
+ board_hdr = malloc(BOARD_HEADER_LEN);
|
|
|
|
+ if (!board_hdr) {
|
|
|
|
+ ERR("Couldn't allocate memory for board header!");
|
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
|
+ }
|
|
|
|
+ memset(board_hdr, 0xff, BOARD_HEADER_LEN);
|
|
|
|
+
|
|
|
|
+ /* 4 bytes: checksum over the header partition (big endian) */
|
|
|
|
+ board_checksum = generate_board_header_checksum(kernel_hdr, rootfs_hdr, boardname);
|
|
|
|
+ memcpy(board_hdr, &board_checksum, 4);
|
|
|
|
+
|
|
|
|
+ /* 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded) */
|
|
|
|
+ memcpy(board_hdr + 4, boardname, strlen(boardname));
|
|
|
|
+ board_hdr[4 + strlen(boardname)] = 0x0;
|
|
|
|
+
|
|
|
|
+ return board_hdr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int build_image()
|
|
|
|
+{
|
|
|
|
+ char *rootfs_header = NULL;
|
|
|
|
+ char *kernel_header = NULL;
|
|
|
|
+ char *board_header = NULL;
|
|
|
|
+
|
|
|
|
+ size_t ptr;
|
|
|
|
+
|
|
|
|
+ /* Load files */
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ map_file(&kernel);
|
|
|
|
+ map_file(&rootfs);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Allocate memory and copy input rootfs for temporary output rootfs.
|
|
|
|
+ * This is important as we have to generate the rootfs checksum over the
|
|
|
|
+ * entire rootfs partition. As we might have to pad the partition to allow
|
|
|
|
+ * for flashing via ZyXEL's Web-GUI, we prepare the rootfs partition for the
|
|
|
|
+ * output image here (and also use it for calculating the rootfs checksum).
|
|
|
|
+ *
|
|
|
|
+ * The roofs padding has to be done with 0x00.
|
|
|
|
+ */
|
|
|
|
+ rootfs_out.data = calloc(rootfs_out.size, sizeof(char));
|
|
|
|
+ memcpy(rootfs_out.data, rootfs.data, rootfs.size);
|
|
|
|
+
|
|
|
|
+ /* Prepare headers */
|
|
|
|
+ rootfs_header = generate_rootfs_header(rootfs_out, version_name);
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ kernel_header = generate_kernel_header(kernel);
|
|
|
|
+ board_header = generate_board_header(kernel_header, rootfs_header, board_name);
|
|
|
|
+
|
|
|
|
+ /* Prepare output file */
|
|
|
|
+ out.size = HEADER_PARTITION_LENGTH + rootfs_out.size;
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ out.size += kernel.size;
|
|
|
|
+ out.data = malloc(out.size);
|
|
|
|
+ memset(out.data, 0xFF, out.size);
|
|
|
|
+
|
|
|
|
+ /* Build output image */
|
|
|
|
+ memcpy(out.data, rootfs_header, ROOTFS_HEADER_LEN);
|
|
|
|
+ memcpy(out.data + ROOTFS_HEADER_LEN, board_header, BOARD_HEADER_LEN);
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ memcpy(out.data + ROOTFS_HEADER_LEN + BOARD_HEADER_LEN, kernel_header, KERNEL_HEADER_LEN);
|
|
|
|
+ ptr = HEADER_PARTITION_LENGTH;
|
|
|
|
+ memcpy(out.data + ptr, rootfs_out.data, rootfs_out.size);
|
|
|
|
+ ptr += rootfs_out.size;
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ memcpy(out.data + ptr, kernel.data, kernel.size);
|
|
|
|
+
|
|
|
|
+ /* Write back output image */
|
|
|
|
+ write_file(&out);
|
|
|
|
+
|
|
|
|
+ /* Free allocated memory */
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ unmap_file(&kernel);
|
|
|
|
+ unmap_file(&rootfs);
|
|
|
|
+ free(out.data);
|
|
|
|
+ free(rootfs_out.data);
|
|
|
|
+
|
|
|
|
+ free(rootfs_header);
|
|
|
|
+ if (kernel.name)
|
|
|
|
+ free(kernel_header);
|
|
|
|
+ free(board_header);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int check_options()
|
|
|
|
+{
|
|
|
|
+ if (!rootfs.name) {
|
|
|
|
+ ERR("No rootfs filename supplied");
|
|
|
|
+ return -2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!out.name) {
|
|
|
|
+ ERR("No output filename supplied");
|
|
|
|
+ return -3;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!board_name) {
|
|
|
|
+ ERR("No board-name supplied");
|
|
|
|
+ return -4;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!version_name) {
|
|
|
|
+ ERR("No version supplied");
|
|
|
|
+ return -5;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rootfs_size <= 0) {
|
|
|
|
+ ERR("Invalid rootfs size supplied");
|
|
|
|
+ return -6;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (strlen(board_name) > 31) {
|
|
|
|
+ ERR("Board name is to long");
|
|
|
|
+ return -7;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int main(int argc, char *argv[])
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ progname = basename(argv[0]);
|
|
|
|
+ while (1) {
|
|
|
|
+ int c;
|
|
|
|
+
|
|
|
|
+ c = getopt(argc, argv, "b:k:o:r:s:v:h");
|
|
|
|
+ if (c == -1)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ switch (c) {
|
|
|
|
+ case 'b':
|
|
|
|
+ board_name = optarg;
|
|
|
|
+ break;
|
|
|
|
+ case 'h':
|
|
|
|
+ usage(EXIT_SUCCESS);
|
|
|
|
+ break;
|
|
|
|
+ case 'k':
|
|
|
|
+ kernel.name = optarg;
|
|
|
|
+ break;
|
|
|
|
+ case 'o':
|
|
|
|
+ out.name = optarg;
|
|
|
|
+ break;
|
|
|
|
+ case 'r':
|
|
|
|
+ rootfs.name = optarg;
|
|
|
|
+ break;
|
|
|
|
+ case 's':
|
|
|
|
+ sscanf(optarg, "%u", &rootfs_size);
|
|
|
|
+ break;
|
|
|
|
+ case 'v':
|
|
|
|
+ version_name = optarg;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ usage(EXIT_FAILURE);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = check_options();
|
|
|
|
+ if (ret)
|
|
|
|
+ usage(EXIT_FAILURE);
|
|
|
|
+
|
|
|
|
+ /* As ZyXEL Web-GUI only accept images with a rootfs equal or larger than the first firmware shipped
|
|
|
|
+ * for the device, we need to pad rootfs partition to this size. To perform further calculations, we
|
|
|
|
+ * decide the size of this part here. In case the rootfs we want to integrate in our image is larger,
|
|
|
|
+ * take it's size, otherwise the supplied size.
|
|
|
|
+ *
|
|
|
|
+ * Be careful! We rely on assertion of correct size to be performed beforehand. It is unknown if images
|
|
|
|
+ * with a to large rootfs are accepted or not.
|
|
|
|
+ */
|
|
|
|
+ rootfs_out.size = rootfs_size < rootfs.size ? rootfs.size : rootfs_size;
|
|
|
|
+ return build_image();
|
|
|
|
+}
|