[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