#!/usr/bin/env bash # # Licensed under the terms of the GNU GPL License version 2 or later. # # Author: Jason Wu # with modifications for multi-DTB-same-image by: # Mathew McBride # # 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