1*4882a593Smuzhiyun#!/bin/bash 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Copyright (c) 2021 Rockchip Electronics Co., Ltd 4*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# The script to generate splited image and update.hdr for TFTP image upgrade. 7*4882a593Smuzhiyun# 1. U-Boot have limited tftp download buffer, we have to split images into pieces for download 8*4882a593Smuzhiyun# 2. Use FIT mechanism to record image pieces information in update.hdr: order, hash, signature, etc. 9*4882a593Smuzhiyun# 3. The TFTP client download update.hdr and verify it (if need) 10*4882a593Smuzhiyun# 4. The TFTP client download => verify => flash image pieces accorrding to update.hdr. 11*4882a593Smuzhiyun# 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunset -e 14*4882a593Smuzhiyun 15*4882a593SmuzhiyunHDR_SIZE=0x10000 # 64KB 16*4882a593SmuzhiyunARG_VERSION=0 17*4882a593SmuzhiyunARG_ROLLBACK_IDX=0 18*4882a593SmuzhiyunARG_FORCE_UPDATE=0 19*4882a593SmuzhiyunSUFFIX=".part.img" 20*4882a593SmuzhiyunGPT="gpt_env.txt" 21*4882a593Smuzhiyun 22*4882a593Smuzhiyunfunction help() 23*4882a593Smuzhiyun{ 24*4882a593Smuzhiyun echo 25*4882a593Smuzhiyun echo "Usage:" 26*4882a593Smuzhiyun echo " $0 --image <dir> --size-MB <size> [optional args]" 27*4882a593Smuzhiyun echo 28*4882a593Smuzhiyun echo " Mandory args:" 29*4882a593Smuzhiyun echo " --image <dir>: directory of image to upgrade" 30*4882a593Smuzhiyun echo " --size-MB <size>: MB size unit for image to split into pieces. In Decimal integer: 1, 2, 3 ..." 31*4882a593Smuzhiyun echo 32*4882a593Smuzhiyun echo " Optional args:" 33*4882a593Smuzhiyun echo " --sign: sign firmware with RSA key-pair in \"./keys\" directory" 34*4882a593Smuzhiyun echo " --rollback-index <index>: rollback index in Decimal integer. It depends on \"--sign\"" 35*4882a593Smuzhiyun echo " --force-update: enable force upgrade" 36*4882a593Smuzhiyun echo " --config <file>: config file" 37*4882a593Smuzhiyun echo " --version <ver>: firmware version in Decimal integer" 38*4882a593Smuzhiyun echo " --clean: clean generated files" 39*4882a593Smuzhiyun echo 40*4882a593Smuzhiyun} 41*4882a593Smuzhiyun 42*4882a593Smuzhiyunfunction check_decimal() 43*4882a593Smuzhiyun{ 44*4882a593Smuzhiyun if [ -z $1 ]; then 45*4882a593Smuzhiyun help 46*4882a593Smuzhiyun exit 1 47*4882a593Smuzhiyun fi 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun DECIMAL=`echo $1 |sed 's/[0-9]//g'` 50*4882a593Smuzhiyun if [ ! -z ${DECIMAL} ]; then 51*4882a593Smuzhiyun echo "ERROR: $1 is not decimal integer" 52*4882a593Smuzhiyun help 53*4882a593Smuzhiyun exit 1 54*4882a593Smuzhiyun fi 55*4882a593Smuzhiyun} 56*4882a593Smuzhiyun 57*4882a593Smuzhiyunfunction process_args() 58*4882a593Smuzhiyun{ 59*4882a593Smuzhiyun while [ $# -gt 0 ]; do 60*4882a593Smuzhiyun case $1 in 61*4882a593Smuzhiyun --help|-help|help|--h|-h) 62*4882a593Smuzhiyun help 63*4882a593Smuzhiyun exit 64*4882a593Smuzhiyun ;; 65*4882a593Smuzhiyun --force-update) 66*4882a593Smuzhiyun ARG_FORCE_UPDATE="1" 67*4882a593Smuzhiyun shift 1 68*4882a593Smuzhiyun ;; 69*4882a593Smuzhiyun --image) 70*4882a593Smuzhiyun ARG_IMAGE_DIR=$2 71*4882a593Smuzhiyun OUTPUT_DIR=${ARG_IMAGE_DIR}/output 72*4882a593Smuzhiyun shift 2 73*4882a593Smuzhiyun ;; 74*4882a593Smuzhiyun --config) 75*4882a593Smuzhiyun ARG_CONFIG_FILE=$2 76*4882a593Smuzhiyun if [ ! -f ${ARG_CONFIG_FILE} ]; then 77*4882a593Smuzhiyun echo "ERROR: No ${ARG_CONFIG_FILE}" 78*4882a593Smuzhiyun exit 1 79*4882a593Smuzhiyun fi 80*4882a593Smuzhiyun shift 2 81*4882a593Smuzhiyun ;; 82*4882a593Smuzhiyun --rollback-index) 83*4882a593Smuzhiyun ARG_ROLLBACK_IDX=$2 84*4882a593Smuzhiyun check_decimal $2 85*4882a593Smuzhiyun shift 2 86*4882a593Smuzhiyun ;; 87*4882a593Smuzhiyun --sign) 88*4882a593Smuzhiyun ARG_SIGN="y" 89*4882a593Smuzhiyun shift 1 90*4882a593Smuzhiyun ;; 91*4882a593Smuzhiyun --size-MB) 92*4882a593Smuzhiyun ARG_IMG_MB=$2 93*4882a593Smuzhiyun check_decimal $2 94*4882a593Smuzhiyun shift 2 95*4882a593Smuzhiyun ;; 96*4882a593Smuzhiyun --version) 97*4882a593Smuzhiyun ARG_VERSION=$2 98*4882a593Smuzhiyun check_decimal $2 99*4882a593Smuzhiyun shift 2 100*4882a593Smuzhiyun ;; 101*4882a593Smuzhiyun --clean) 102*4882a593Smuzhiyun rm -f *${SUFFIX} *.itb *.its *.dtb *.dts data2sign* *.hdr *.hash orderlist.txt 103*4882a593Smuzhiyun exit 0 104*4882a593Smuzhiyun ;; 105*4882a593Smuzhiyun *) 106*4882a593Smuzhiyun echo "Invalid arg: $1" 107*4882a593Smuzhiyun help 108*4882a593Smuzhiyun exit 1 109*4882a593Smuzhiyun ;; 110*4882a593Smuzhiyun esac 111*4882a593Smuzhiyun done 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun if [ -z ${ARG_IMG_MB} ]; then 114*4882a593Smuzhiyun help 115*4882a593Smuzhiyun exit 1 116*4882a593Smuzhiyun elif [ -z ${ARG_IMAGE_DIR} ]; then 117*4882a593Smuzhiyun help 118*4882a593Smuzhiyun exit 1 119*4882a593Smuzhiyun elif [ ! -d ${ARG_IMAGE_DIR} ]; then 120*4882a593Smuzhiyun echo "ERROR: No directory ${ARG_IMAGE_DIR}" 121*4882a593Smuzhiyun exit 1 122*4882a593Smuzhiyun fi 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun if [ "${ARG_SIGN}" == "y" ]; then 125*4882a593Smuzhiyun if [ ! -f keys/dev.key ]; then 126*4882a593Smuzhiyun echo "ERROR: No keys/dev.key" 127*4882a593Smuzhiyun exit 1 128*4882a593Smuzhiyun elif [ ! -f keys/dev.crt ]; then 129*4882a593Smuzhiyun echo "ERROR: No keys/dev.crt" 130*4882a593Smuzhiyun exit 1 131*4882a593Smuzhiyun fi 132*4882a593Smuzhiyun else 133*4882a593Smuzhiyun ARG_ROLLBACK_IDX=0 134*4882a593Smuzhiyun fi 135*4882a593Smuzhiyun} 136*4882a593Smuzhiyun 137*4882a593Smuzhiyunfunction split_image() 138*4882a593Smuzhiyun{ 139*4882a593Smuzhiyun rm -rf ${OUTPUT_DIR} 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun if [ "${ARG_CONFIG_FILE}" ]; then 142*4882a593Smuzhiyun cp ${ARG_CONFIG_FILE} orderlist.txt 143*4882a593Smuzhiyun else 144*4882a593Smuzhiyun ls ${ARG_IMAGE_DIR} > orderlist.txt 145*4882a593Smuzhiyun fi 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun SEQ=0 148*4882a593Smuzhiyun cat orderlist.txt | while read IMAGE 149*4882a593Smuzhiyun do 150*4882a593Smuzhiyun if [ "${IMAGE}" == "${GPT}" ]; then 151*4882a593Smuzhiyun continue; 152*4882a593Smuzhiyun fi 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun NAME=`echo ${IMAGE} | awk -F "." '{ print $1 }'` 155*4882a593Smuzhiyun echo "=> split: ${IMAGE}" 156*4882a593Smuzhiyun if [ ! -f ${ARG_IMAGE_DIR}/${IMAGE} ]; then 157*4882a593Smuzhiyun echo "ERROR: No ${ARG_IMAGE_DIR}/${IMAGE}" 158*4882a593Smuzhiyun exit 1 159*4882a593Smuzhiyun fi 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun if [ ${SEQ} -lt 10 ]; then 162*4882a593Smuzhiyun SEQ_NAME="seq0${SEQ}-${NAME}" 163*4882a593Smuzhiyun else 164*4882a593Smuzhiyun SEQ_NAME="seq${SEQ}-${NAME}" 165*4882a593Smuzhiyun fi 166*4882a593Smuzhiyun 167*4882a593Smuzhiyun split -b ${ARG_IMG_MB}M ${ARG_IMAGE_DIR}/${IMAGE} ${SEQ_NAME}- -d --additional-suffix=${SUFFIX} 168*4882a593Smuzhiyun SEQ=`expr ${SEQ} + 1` 169*4882a593Smuzhiyun ls ${SEQ_NAME}-* 170*4882a593Smuzhiyun done 171*4882a593Smuzhiyun echo 172*4882a593Smuzhiyun} 173*4882a593Smuzhiyun 174*4882a593Smuzhiyunfunction gen_its() 175*4882a593Smuzhiyun{ 176*4882a593Smuzhiyuncat > update.its << EOF 177*4882a593Smuzhiyun/* 178*4882a593Smuzhiyun * Copyright (C) 2021 Rockchip Electronic Co.,Ltd 179*4882a593Smuzhiyun * 180*4882a593Smuzhiyun */ 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun/dts-v1/; 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun/ { 185*4882a593Smuzhiyun description = "FIT Image with ATF/OP-TEE/U-Boot"; 186*4882a593Smuzhiyun #address-cells = <1>; 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun images { 189*4882a593SmuzhiyunEOF 190*4882a593Smuzhiyun if [ -f ${ARG_IMAGE_DIR}/${GPT} ]; then 191*4882a593Smuzhiyun cp ${ARG_IMAGE_DIR}/${GPT} ./ 192*4882a593Smuzhiyun echo " ${GPT} { 193*4882a593Smuzhiyun data = /incbin/(\"./${GPT}\"); 194*4882a593Smuzhiyun hash { 195*4882a593Smuzhiyun algo = \"sha256\"; 196*4882a593Smuzhiyun }; 197*4882a593Smuzhiyun };" >> update.its 198*4882a593Smuzhiyun fi 199*4882a593Smuzhiyun 200*4882a593Smuzhiyun NUM=0 201*4882a593Smuzhiyun MAX=`ls -l *part* | wc -l` 202*4882a593Smuzhiyun for IMG in `ls -l *part* | awk '{ print $9 }'` 203*4882a593Smuzhiyun do 204*4882a593Smuzhiyun NAME=`echo ${IMG} | awk -F "." '{ print $1 }'` 205*4882a593Smuzhiyun echo " ${NAME} { 206*4882a593Smuzhiyun data = /incbin/(\"./${NAME}${SUFFIX}\"); 207*4882a593Smuzhiyun hash { 208*4882a593Smuzhiyun algo = \"sha256\"; 209*4882a593Smuzhiyun }; 210*4882a593Smuzhiyun };" >> update.its 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun NUM=`expr ${NUM} + 1` 213*4882a593Smuzhiyun if [ ${NUM} -lt ${MAX} ]; then 214*4882a593Smuzhiyun FIRMWARES=${FIRMWARES}"\"${NAME}\", " 215*4882a593Smuzhiyun else 216*4882a593Smuzhiyun FIRMWARES=${FIRMWARES}"\"${NAME}\"" 217*4882a593Smuzhiyun LAST_IMG=${NAME} 218*4882a593Smuzhiyun fi 219*4882a593Smuzhiyun done 220*4882a593Smuzhiyuncat >> update.its << EOF 221*4882a593Smuzhiyun }; 222*4882a593Smuzhiyun 223*4882a593Smuzhiyun configurations { 224*4882a593Smuzhiyun default = "conf"; 225*4882a593Smuzhiyun conf { 226*4882a593Smuzhiyun rollback-index = <${ARG_ROLLBACK_IDX}>; 227*4882a593Smuzhiyun force-update = <${ARG_FORCE_UPDATE}>; 228*4882a593Smuzhiyun image-size-MB = <${ARG_IMG_MB}>; 229*4882a593Smuzhiyun firmware = ${FIRMWARES}; 230*4882a593Smuzhiyun signature { 231*4882a593Smuzhiyun algo = "sha256,rsa2048"; 232*4882a593Smuzhiyun padding = "pss"; 233*4882a593Smuzhiyun key-name-hint = "dev"; 234*4882a593Smuzhiyun sign-images = "firmware"; 235*4882a593Smuzhiyun }; 236*4882a593Smuzhiyun }; 237*4882a593Smuzhiyun }; 238*4882a593Smuzhiyun}; 239*4882a593SmuzhiyunEOF 240*4882a593Smuzhiyun} 241*4882a593Smuzhiyun 242*4882a593Smuzhiyunfunction gen_update_hdr() 243*4882a593Smuzhiyun{ 244*4882a593Smuzhiyun # generate itb 245*4882a593Smuzhiyun if [ "${ARG_SIGN}" == "y" ]; then 246*4882a593Smuzhiyun # create a temporary dtb to store public-key, just for mkimage work normal. 247*4882a593Smuzhiyun SIGNED="signed" 248*4882a593Smuzhiyun cp update.its key.its 249*4882a593Smuzhiyun sed -i "/data =/d" key.its 250*4882a593Smuzhiyun dtc -I dts -O dtb key.its -o key.dtb 251*4882a593Smuzhiyun 252*4882a593Smuzhiyun ./mkimage -f update.its -k keys/ -K key.dtb -E -p ${HDR_SIZE} -r update.itb -v ${ARG_VERSION} 253*4882a593Smuzhiyun ./fit_check_sign -f update.itb -k key.dtb 254*4882a593Smuzhiyun else 255*4882a593Smuzhiyun SIGNED="no-signed" 256*4882a593Smuzhiyun ./mkimage -f update.its -E -p ${HDR_SIZE} update.itb -v ${ARG_VERSION} 257*4882a593Smuzhiyun fi 258*4882a593Smuzhiyun 259*4882a593Smuzhiyun fdtdump update.itb > update.hdr.dts 260*4882a593Smuzhiyun 261*4882a593Smuzhiyun # validate update.hdr 262*4882a593Smuzhiyun if [ "${ARG_SIGN}" == "y" ]; then 263*4882a593Smuzhiyun NUM=`grep "${LAST_IMG}" update.hdr.dts | wc -l` 264*4882a593Smuzhiyun if [ ${NUM} -lt 3 ]; then 265*4882a593Smuzhiyun echo "ERROR: mkimage can't makeup full \"hashed-nodes\" property list" 266*4882a593Smuzhiyun exit 1 267*4882a593Smuzhiyun fi 268*4882a593Smuzhiyun fi 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun # Remove binary from update.itb 271*4882a593Smuzhiyun BYTE=`sed -n "/totalsize:/p" update.hdr.dts | awk '{ print $4 }' | tr -d '(' | tr -d ')'` 272*4882a593Smuzhiyun dd if=update.itb of=update.hdr bs=${BYTE} count=1 273*4882a593Smuzhiyun 274*4882a593Smuzhiyun # Append checksum 275*4882a593Smuzhiyun openssl dgst -sha256 -binary -out update.hash update.hdr 276*4882a593Smuzhiyun cat update.hash >> update.hdr 277*4882a593Smuzhiyun 278*4882a593Smuzhiyun rm -f *.itb *.its *.dtb *.dts data2sign* *.hash orderlist.txt 279*4882a593Smuzhiyun mkdir -p ${OUTPUT_DIR} 280*4882a593Smuzhiyun ls ${GPT} >/dev/null 2>&1 && mv ${GPT} ${OUTPUT_DIR} 281*4882a593Smuzhiyun mv -f *${SUFFIX} update.hdr ${OUTPUT_DIR} 282*4882a593Smuzhiyun 283*4882a593Smuzhiyun echo 284*4882a593Smuzhiyun echo "Success: [${SIGNED}] TFTP upgrade images(unit: ${ARG_IMG_MB}MB) are ready in directory: ${OUTPUT_DIR}." 285*4882a593Smuzhiyun echo 286*4882a593Smuzhiyun} 287*4882a593Smuzhiyun 288*4882a593Smuzhiyun############################################################### 289*4882a593Smuzhiyunprocess_args $* 290*4882a593Smuzhiyunsplit_image 291*4882a593Smuzhiyungen_its 292*4882a593Smuzhiyungen_update_hdr 293