[OpenWrt-Devel] [PATCH v2] build: add mkrasimage

Karl Palsson karlp at tweak.net.au
Mon Aug 20 05:42:44 EDT 2018


David Bauer <mail at david-bauer.net> wrote:
> 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 old script is still kept but can be deleted in the future.

You need to update your commit message, this is no longer the
case.

> 
> The new mkrasimage is also compatible with other ZyXEL devices
> using the ras image-format. This is not tested with a OpenWRT
> build but it correctly builds the header for ZyXEL factory
> images for all devices it is added to.
> 
> Signed-off-by: David Bauer <mail at david-bauer.net>
> ---
> 
> v2 changes:
>  - Rework image-generation code
>  - Add factory image for NBG6616
>  - Add factory image for NBG6817
> 
>  include/image-commands.mk             |  18 +-
>  scripts/make-ras.sh                   | 196 -----------
>  target/linux/ar71xx/image/generic.mk  |   6 +-
>  target/linux/ipq40xx/image/Makefile   |   2 +-
>  target/linux/ipq806x/image/Makefile   |   6 +-
>  tools/firmware-utils/Makefile         |   1 +
>  tools/firmware-utils/src/mkrasimage.c | 474 ++++++++++++++++++++++++++
>  7 files changed, 495 insertions(+), 208 deletions(-)
>  delete mode 100755 scripts/make-ras.sh
>  create mode 100644 tools/firmware-utils/src/mkrasimage.c
> 
> diff --git a/include/image-commands.mk
> b/include/image-commands.mk index 3cc5dc21e1..61ba49de51 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 seperate-kernel,$(word 1,$(1))),-k $(IMAGE_KERNEL)) \
> +		&& mv $@.new $@
>  endef
>  
>  define Build/mkbuffaloimg
> diff --git a/scripts/make-ras.sh b/scripts/make-ras.sh deleted
> file mode 100755 index ccddaa0016..0000000000
> --- a/scripts/make-ras.sh
> +++ /dev/null
> @@ -1,196 +0,0 @@
> -#!/usr/bin/env bash
> -#
> -# --- ZyXEL header format ---
> -# Original Version by Benjamin Berg <benjamin at 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
> 3b320814c5..55963c5768 100644
> --- a/target/linux/ar71xx/image/generic.mk
> +++ b/target/linux/ar71xx/image/generic.mk
> @@ -1063,8 +1063,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 at 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 index
> d1ee1004fd..5a08312d2c 100644
> --- 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 seperate-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
> 2902af3231..cbb03272fb 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 seperate-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 := "$(VERSION_DIST) $(REVISION)"
>  	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 436a43794c..00917c3417
> 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 0000000000..f0f8a54371
> --- /dev/null
> +++ b/tools/firmware-utils/src/mkrasimage.c
> @@ -0,0 +1,474 @@
> +/*
> + * --- ZyXEL header format ---
> + * Original Version by Benjamin Berg <benjamin at sipsolutions.net>
> + * C implementation based on generation-script by Christian Lamparter <chunkeey at 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>
> +
> +#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 unsigned int get_big_endian(const unsigned int in)
> +{
> +    int x = 1;
> +    unsigned int out;
> +
> +    /*
> +     * checksum is stored as big-endian in header, so we need to convert it
> +     * in case we are on a litte-endian system.
> +     */
> +    if (*((char *) &x) == 1) {
> +        /* Little endian, need to convert */
> +        out = ((in << 8) & 0xFF00FF00 ) | ((in >> 8) & 0x00FF00FF );
> +        out = (out << 16) | (out >> 16);
> +    }
> +    return out;
> +}
> +
> +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 get_big_endian(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 = get_big_endian(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 = get_big_endian(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();
> +}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.html
Type: application/pgp-signature
Size: 1161 bytes
Desc: OpenPGP Digital Signature
URL: <http://lists.infradead.org/pipermail/openwrt-devel/attachments/20180820/b5cf6d37/attachment.sig>
-------------- next part --------------
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list