OpenWrt – Rev 4

Subversion Repositories:
Rev:
#!/usr/bin/env bash
#
# Licensed under the terms of the GNU GPL License version 2 or later.
#
# Author: Jason Wu <jason.hy.wu@gmail.com>
# with modifications for multi-DTB-same-image by:
# Mathew McBride <matt@traverse.com.au>
#
# U-Boot firmware supports the booting of images in the Flattened Image
# Tree (FIT) format.  The FIT format uses a device tree structure to
# describe a kernel image, device tree blob, ramdisk, etc.  This script
# creates an Image Tree Source (.its file) which can be passed to the
# 'mkimage' utility to generate an Image Tree Blob (.itb file).  The .itb
# file can then be booted by U-Boot (or other bootloaders which support
# FIT images).  See doc/uImage.FIT/howto.txt in U-Boot source code for
# additional information on FIT images.
#
# This tools supports:
#   - multi-configuration
#   - multi-image support - multiple kernel/fdt/ramdsik
#   - per image configuration:
#     - hash algorithm and generated required subnodes
#     - compression
#     - signature and generated required subnodes
#
set -e

# image config limit
MAX_IMG=50
# conf config limit
MAX_CONF=10

# declare main data array
declare -a img_array
declare -a conf_array

# initialize array with empty values
for (( index=1; index<=$MAX_IMG; index++ )); do
        declare -a img$index
        for i in {0..13}; do
                eval img${index}[$i]=""
        done
done

for (( index=1; index<=$MAX_CONF; index++ )); do
        declare -a conf$index
        for i in {0..9}; do
                eval conf${index}[$i]=""
        done
done

# imgX array index information
#       0: type of image - kernel, fdt, ramdsik
#       1: image location
#       2: image index
#       3: loadaddr of image
#       4: entrypoint of image
#       5: compression
#       6: hash algorithm
#       7: part of the configuration
#       8: Human friend name for the image
#       9: key file name
#       10: signature
#       11: conf friendly name

# confX array index information
#       0: conf number
#       1: kernel conf
#       2: fdt conf
#       3: rootfs conf
#       4: kernel key file
#       5: fdt key file
#       6: rootfs key file
#       7: kernel sign_algorithm
#       8: fdt sign_algorithm
#       9: rootfs sign_algorithm
#       10: conf friendly name

usage() {
        echo "Usage: `basename $0` -A arch -v version -o its_file" \
                "-k kernel -a addr -e entry [-C none] [-h sha1] [-c conf]"
        echo -e "Example1:\n\tkernel image ker_img1 with no compression +"
        echo -e "\tsha1 hash + fdt dtb1 with sha1 and crc32 hash for conf 1"
        echo -e "\t $ `basename $0` -A arm -v 4.4 \ "
        echo -e "\t      -k ker_img1 -C none -h sha1 -e 0x8000 -a 0x8000 -c 1 \ "
        echo -e "\t      -d dtb1 -h sha1 -h crc32 -c 1\n"
        echo "General settings:"
        echo -e "\t-A ==> set architecture to 'arch'"
        echo -e "\t-v ==> set kernel version to 'version'"
        echo -e "\t-o ==> create output file 'its_file' [optional]"
        echo "Input image type:"
        echo -e "\t-k ==> kernel image 'kernel'"
        echo -e "\t-d ==> Device Tree Blob 'dtb'"
        echo -e "\t-r ==> ramdisk image 'ramdisk"
        echo "Per image configurations:"
        echo -e "\t-C ==> set compression type 'comp'"
        echo -e "\t-c ==> set image config (multiple -c allowed)"
        echo -e "\t-a ==> set load address to 'addr' (hex)"
        echo -e "\t-e ==> set entry point to 'entry' (hex)"
        echo -e "\t-D ==> human friendly 'name' (one word only)"
        echo -e "\t-h ==> set hash algorithm (multiple -h allowed)"
        echo -e "\t-s ==> set signature for given config image"
        echo -e "\t-K ==> set key file for given config image"
        exit 1
}

array_check()
{
        local a=999
        local max_a=0
        local max_i=0

        if echo $1 | grep -q img; then
                max_a=$MAX_IMG
                max_i=13
                let a=$(echo $1 | awk -F "img" '{print $2}')
        elif echo $1 | grep -q conf; then
                max_a=$MAX_CONF
                max_i=10
                let a=$(echo $1 | awk -F "conf" '{print $2}')
        fi
        if [ ${a} -lt 0 -o ${a} -gt ${max_a} -o \
                ${2} -lt 0 -o ${2} -gt ${max_i} ]; then
                echo "WARNING: Invalid array name, skipping!!!"
                return 255
        fi
}

#
# $1:   array name
# $2:   index
# $3:   value
# $4:   append operation
#
array_put()
{
        # check if array is declared
        array_check $1 $2 || return 0
        if [ -z "$4" ]; then
                eval $1[$2]=$3
        else
                eval $1[$2]=\"\${$1[$2]} $3\"
        fi
}

#
# $1:   array name
# $2:   index
#
array_get()
{
        local val
        eval val=\${$1[$2]}
        echo $val
}

parse_args() {
        local i=-1 k=-1 d=-1 r=-1
        while getopts ":A:a:C:c:D:d:e:h:k:K:o:v:r:s:n:" OPTION; do
                case $OPTION in
                        A ) ARCH=$OPTARG;;
                        a ) array_put img$i 3 $OPTARG;;
                        C ) value_sanity_chk compression $OPTARG;
                                array_put img$i 5 $OPTARG;;
                        c ) array_put img$i 7 $OPTARG append;;
                        D ) array_put img$i 8 $OPTARG;;
                        d ) i=$(($i + 1));
                                d=$(($d + 1));
                                img_array[$i]=img$i;
                                array_put img$i 0 fdt;
                                array_put img$i 1 $OPTARG;
                                array_put img$i 2 $d;
                                ;;
                        e ) array_put img$i 4 $OPTARG;;
                        h ) value_sanity_chk hash $OPTARG;
                                array_put img$i 6 $OPTARG append;;
                        k ) i=$(($i + 1));
                                k=$(($k + 1));
                                img_array[$i]=img$i;
                                array_put img$i 0 "kernel";
                                array_put img$i 1 $OPTARG;
                                array_put img$i 2 $k;
                                ;;
                        K ) array_put img$i 9 $OPTARG;;
                        n ) array_put img$i 11 $OPTARG;;
                        o ) OUTPUT=$OPTARG;;
                        v ) VERSION=$OPTARG;;
                        r ) i=$(($i + 1));
                                r=$(($r + 1));
                                img_array[$i]=img$i;
                                array_put img$i 0 "ramdisk";
                                array_put img$i 1 $OPTARG;
                                array_put img$i 2 $r;
                                ;;
                        s ) value_sanity_chk signature $OPTARG;
                                array_put img$i 10 $OPTARG;
                                ;;
                        * ) echo "Invalid option passed to '$0' (options:$@)"
                        usage;;
                esac
        done
        [ -n "${OUTPUT}" ] || OUTPUT=fitimage.its
        [ -n "${VERSION}" ] || VERSION="Unknown"
        [ -n "${ARCH}" ] || ARCH=arm
}

#
# sanity check for signature, compression and hash
#
value_sanity_chk()
{
        local valid=""
        case $1 in
                signature) valid="sha-1,rsa-2048 sha-256,rsa-2048 sha-256,rsa-4096";;
                compression) valid="gzip bzip2 none";;
                hash) valid="sha1 md5 crc32";;
        esac
        if ! echo $valid | grep -q "$2"; then
                echo "Error: Invalid $1 provided '$2'"
                echo "Valid options are: $valid"
                exit 255
        fi
}

#
# Emit the fitImage section bits
#
# $1: Section bit type: fitstart   - its header
#                       imagestart - image section start
#                       confstart  - configuration section start
#                       sectend    - section end
#                       fitend     - fitimage end
# $2: optional variable for confstart section
#
emit_its() {
        case $1 in
        fitstart)
                cat << EOF > ${OUTPUT}
/dts-v1/;

/ {
        description = "U-Boot fitImage for ${VERSION} kernel";
        #address-cells = <1>;
EOF
        ;;
        imagestart)
                echo -e "\n\timages {" >> ${OUTPUT};;
        confstart)
#               echo -e "\tconfigurations {\n\t\tdefault = \"conf@${2:-0}\";" \
        echo -e "\tconfigurations {\n" \
                        >> ${OUTPUT};;
        sectend)
                echo -e "\t};" >> ${OUTPUT};;
        fitend)
                echo -e "};" >> ${OUTPUT};;
        esac
}

#
# Emit kernel image node
#
emit_kernel() {
        local image=${1}
        local count=${2:-${MAX_IMG}}
        local loaddaddr=${3:-0x8000}
        local entrypoint=${4:-0x8000}
        local compresson=${5:-none}
        local checksum=${6:-sha1}
        local name=${7}

        [ -z "${name}" ] || name=" ${name}"
        cat << EOF >> ${OUTPUT}
                kernel@${count} {
                        description = "Linux Kernel${name}";
                        data = /incbin/("${image}");
                        type = "kernel";
                        arch = "${ARCH}";
                        os = "linux";
                        compression = "${compresson}";
                        load = <${loaddaddr}>;
                        entry = <${entrypoint}>;
EOF
        emit_cksum ${checksum}

        if [ -z "$SIGN_IN_CONF" ] ; then
                emit_signature "$9" "" "" "$8" "" ""
        fi

        echo "          };" >> ${OUTPUT}
}

#
# Emit fdt node
#
emit_fdt() {
        local image=${1}
        local count=${2:-${MAX_IMG}}
        local compresson=${3:-none}
        local checksum=${4:-sha1}
        local name=${5}
        local loadaddr=${6}

        [ -z "${name}" ] || name=" ${name}"
        cat << EOF >> ${OUTPUT}
                fdt@${count} {
                        description = "Flattened Device Tree blob${name}";
                        data = /incbin/("${image}");
                        type = "flat_dt";
                        arch = "${ARCH}";
                        load = <${loadaddr}>;
                        compression = "none";
EOF
        emit_cksum ${checksum}
        if [ -z "$SIGN_IN_CONF" ] ; then
                emit_signature "" "$7" "" "" "$6" ""
        fi
        echo "          };" >> ${OUTPUT}
}

#
# Emit ramdisk node
#
emit_ramdisk() {
        local image=${1}
        local count=${2:-${MAX_IMG}}
        local compresson=${3:-none}
        local checksum=${4:-sha1}
        local name=${5}

        [ -z "${name}" ] || name=" ${name}"
        cat << EOF >> ${OUTPUT}
                ramdisk@${count} {
                        description = "ramdisk${name}";
                        data = /incbin/("${image}");
                        type = "ramdisk";
                        arch = "${ARCH}";
                        os = "linux";
                        compression = "${compresson}";
EOF
        emit_cksum ${checksum}
        if [ -z "$SIGN_IN_CONF" ] ; then
                emit_signature "" "" "$7" "" "" "$6"
        fi
        echo "          };" >> ${OUTPUT}
}

#
# Emit check sum sub node
#
emit_cksum() {
        csum_list=$@
        count=1
        for csum in ${csum_list}; do
                cat << EOF >> ${OUTPUT}
                        hash@${count} {
                                algo = "${csum}";
                        };
EOF
                count=`expr ${count} + 1`
        done
}

#
# Emit signature sub node
#
emit_signature() {
        local kernel=$1
        local fdt=$2
        local rootfs=$3
        local kernel_key=$4
        local fdt_key=$5
        local rootfs_key=$6
        local imgs=""
        local count=0
        local chk_list="" algo="" algos="" i=""

        for i in kernel fdt rootfs; do
                eval algo=\$$i
                eval key=\$${i}_key
                [ -n "$algo" ] || continue
                if ! echo "$algos" | grep -q $algo; then
                        if [ -z "$algos" ]; then
                                algos=$algo
                        else
                                algos="${algos} $algo"
                        fi
                fi
                if ! echo "$keys" | grep -q $key; then
                        if [ -z "$keys" ]; then
                                keys=$key
                        else
                                keys="${keys} $key"
                        fi
                fi
        done

        for algo in $algos; do
                for key in $keys; do
                        img=""
                        for i in kernel fdt rootfs; do
                                eval tmp_algo=\$$i
                                eval tmp_key=\$${i}_key
                                [ "$tmp_algo" == "$algo" ] || continue
                                [ "$tmp_key" == "$key" ] || continue
                                if [ -z "$img" ]; then
                                        img=$i
                                else
                                        img=${img},$i
                                fi
                        done

                        [ -n "$img" ] || continue
                        cat << EOF >> ${OUTPUT}
                        signature@${count} {
                                algo = "${algo}";
                                key-name-hint = "${key}";
EOF
                        if [ -n "$SIGN_IN_CONF" ] ; then
                                echo "                  sign-images = \"$img\";" >> ${OUTPUT}
                        fi
                        echo "                  };" >> ${OUTPUT}

                        count=`expr ${count} + 1`
                done
        done
}

#
# Emit config sub nodes
#
emit_config() {
        local conf_csum="sha1"

        config_name="conf@${1}"
        if [ ! -z "${11}" ]; then
                config_name="${11}"
        fi 
        if [ -z "${2}" ]; then
                echo "Error: config has no kernel img, skipping conf node!"
                return 0
        fi

        # Test if we have any DTBs at all
        if [ -z "${3}" ] ; then
                conf_desc="Boot Linux kernel"
                fdt_line=""
        else
                conf_desc="Boot Linux kernel with FDT blob"
                fdt_line="
                        fdt = \"fdt@${3}\";"
        fi

        # Test if we have any ROOTFS at all
        if [ -n "${4}" ] ; then
                conf_desc="$conf_desc + ramdisk"
                fdt_line="${fdt_line}
                        ramdisk = \"ramdisk@${4}\";"
        fi

        kernel_line="kernel = \"kernel@${2}\";"

        cat << EOF >> ${OUTPUT}
                ${config_name} {
                        description = "${conf_desc}";
                        ${kernel_line}${fdt_line}
                        hash@1 {
                                algo = "${conf_csum}";
                        };
EOF
        if [ -n "$SIGN_IN_CONF" ] ; then
                emit_signature "$5" "$6" "$7" "$8" "$9" "${10}"
        fi

        echo "          };" >> ${OUTPUT}
}

#
# remove prefix space
#
remove_prefix_space()
{
        echo "$@" | sed "s:^ ::g"
}

#
# generate image nodes and its subnodes
#
emit_image_nodes()
{
        local t img_c img_i img_index chk
        local img_type img_path img_count img_loadadr img_entrypoint \
                img_compression img_hash img_conf img_name img_key img_sign \
                img_index

        emit_its imagestart
        for t in "kernel" "fdt" "ramdisk"; do
                img_index=0
                for a in ${img_array[@]}; do
                        img_type=$(array_get $a 0)
                        img_path=$(array_get $a 1)
                        img_count=$(array_get $a 2)
                        img_loadadr=$(array_get $a 3)
                        img_entrypoint=$(array_get $a 4)
                        img_compression=$(array_get $a 5)
                        img_hash=$(array_get $a 6)
                        img_conf=$(array_get $a 7)
                        img_name=$(array_get $a 8)
                        img_key=$(array_get $a 9)
                        img_sign=$(array_get $a 10)
                        img_cname=$(array_get $a 11)
                        
                        img_conf=$(remove_prefix_space $img_conf)
                        img_hash=$(remove_prefix_space $img_hash)

                        [ "${img_type}" == $t ] || continue
                        # generate sub nodes
                        eval chk=\$DEF_$t
                        [ -n "${chk}" ] || eval DEF_$t=$img_count
                        case $t in
                                kernel) emit_kernel "$img_path" "$img_count" \
                                        "$img_loadadr" "$img_entrypoint" \
                                        "$img_compression" "$img_hash" \
                                        "$img_name" "$img_key" "$img_sign";;
                                fdt) emit_fdt "$img_path" "$img_count" \
                                        "$img_compression" "$img_hash" \
                                        "$img_name" "$img_loadadr" "$img_key" "$img_sign"  ;;

                                ramdisk) emit_ramdisk "$img_path" "$img_count" \
                                        "$img_compression" "$img_hash" \
                                        "$img_name" "$img_key" "$img_sign";;
                        esac

                        # set up configuration data
                        for img_c in $img_conf; do
                                img_i=""
                                #set up default configuration if its not set
                                [ -n "$DEF_CONFIG" ] || DEF_CONFIG=$img_c
                                [ -z "${img_c}" ] || conf_array[$img_c]=conf$img_c
                                array_put conf$img_c 0 ${img_c}
                                case $t in
                                        kernel) img_i=1;;
                                        fdt) img_i=2;;
                                        ramdisk) img_i=3;;
                                esac
                                array_put conf$img_c $img_i $img_index
                                array_put conf$img_c $(($img_i + 3)) ${img_sign}
                                array_put conf$img_c $(($img_i + 6)) ${img_key}
                                array_put conf$img_c 10 $img_cname
                        done
                        img_index=$((img_index + 1))
                done
        done
        emit_its sectend
}

#
# generate configuration node and its subnodes
#
emit_configuration_nodes ()
{
        local count kernel fdt ramdisk ker_file fdt_file rfs_file ker_sign \
                fdt_sign rfs_sign
        emit_its confstart $DEF_CONFIG
        for a in ${conf_array[@]}; do
                count=$(array_get $a 0)
                kernel=$(array_get $a 1)
                fdt=$(array_get $a 2)
                ramdisk=$(array_get $a 3)
                er_file=$(array_get $a 4)
                fdt_file=$(array_get $a 5)
                rfs_file=$(array_get $a 6)
                ker_sign=$(array_get $a 7)
                fdt_sign=$(array_get $a 8)
                rfs_sign=$(array_get $a 9)
                cname=$(array_get $a 10)
                emit_config "$count" "$kernel" "$fdt" "$ramdisk" "$ker_file" \
                        "$fdt_file" "$rfs_file" "$ker_sign" "$fdt_sign" \
                        "$rfs_sign" "${cname}"
        done
        if [ -z "${DEF_CONFIG}" ]; then
                emit_config "0" "$DEF_kernel" "$DEF_fdt" "$DEF_ramdisk"
        fi
        emit_its sectend
}

# Set to none empty to create signature sub node under images node
SIGN_IN_CONF=${SIGN_IN_CONF:-""}
# Set to default config used
DEF_CONFIG=${DEF_CONFIG:-""}

parse_args $@

emit_its fitstart
emit_image_nodes
emit_configuration_nodes
emit_its fitend