diff options
Diffstat (limited to 'scripts/make-ras.sh')
-rwxr-xr-x | scripts/make-ras.sh | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/scripts/make-ras.sh b/scripts/make-ras.sh new file mode 100755 index 0000000000..ccddaa0016 --- /dev/null +++ b/scripts/make-ras.sh @@ -0,0 +1,196 @@ +#!/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" |