#!/bin/bash

function show_help()
{
	echo -e "\n\n***************************************************"
	echo "progstore usage "
	echo "----------------"
	echo "-c option is mandatory. It instructs the script of various conditions being me in the incoming monolith image"
	echo "supported commands are:"
	echo -e "\tMLTH_START: start of monolithic image has been detected. progstore will ready the environment\n"
	echo -e "\tIMG_START: beginning of a software image has been encountered, progstore should configure itself to begin accepting the update of this image\n"
	echo -e "\tIMG_END: end of a software image has been detected. progstore should do appropriate error checking and flash image if necessary\n"
	echo -e "\tMLTH_END: end of monolithic image has been detected. progstore should do appropriate error checking and set boot command to reflect new updates\n"
	echo -e "\tCONFIRM_COMPLETE: system is ready for a reboot, progstore should reboot when ready\n"
	echo " -s option can only accompany IMG_START or IMG_COMPLETE command, and indicates that update should accumulate a CRC and check it against the supplied value"
	echo " -u option indicates that legacy UBIFS images are being programmed"
	echo " -z option indicates that the incoming image is compressed and should be gunzipped prior to flash update"
	echo "examples:"
	echo -e "***************************************************\n"
}

function do_mlth_start()
{
	#be sure to clean up flash/ramfs, in case other failed downloads have occurred, chewing up valuable space
	rm -rf $tmpstorage1
	rm -rf $tmpstorage2
	rm -rf /tmp/*.work

	if [ ! -e "/tmp/update" ]; then
		mknod /tmp/update p
	fi

	if ! lsmod | grep nandsim >/dev/null; then
		echo "1 4 1 7" > /proc/sys/kernel/printk
		if kmodloader 2>/dev/null; then
			insmod nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15 parts=1024,1024
		else
			modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15 parts=1024,1024
		fi
		echo "7 4 1 7" > /proc/sys/kernel/printk
	fi

	#SPRINGEVENT should be unset for normal situation
	boltenv -g SPRINGEVENT >/dev/null
	if [ $? -eq 0 ]; then
		echo "Warning: SPRINGEVENT exists!"
	fi

	#Remove /tmp/SPRINGEVENT_TMP
	if [ -f /tmp/SPRINGEVENT_TMP ]; then
		echo "Last update was incorrect, /tmp/SPRINGEVENT exists!"
		rm /tmp/SPRINGEVENT_TMP
		echo "Remove /tmp/SPRINGEVENT"
	fi

	mkdir -p $tmpstorage1
	mkdir -p $tmpstorage2
}

function set_springevent_tmp() {
	idx_name=$1
	idx=$2

	#Parameters are defined in rcS.util
	if [ -f /tmp/SPRINGEVENT_TMP ]; then
		var_value=`cat /tmp/SPRINGEVENT_TMP`
		if [[ $var_value -eq 0 ]]; then
			var_value=$INIT_SPRINGEVENT
		fi
	else
		var_value=$INIT_SPRINGEVENT
	fi

	case $idx_name in
	"LB")
		mask=$LB_MASK
		shift_bit=$LB_SHIFT
		;;
	"CMNV")
		mask=$CMNV_MASK
		shift_bit=$CMNV_SHIFT
		;;
	"RGNV")
		mask=$RGNV_MASK
		shift_bit=$RGNV_SHIFT
		;;
	"CM")
		mask=$CM_MASK
		shift_bit=$CM_SHIFT
		;;
	"RG")
		mask=$RG_MASK
		shift_bit=$RG_SHIFT
		;;
	"KL")
		mask=$KL_MASK
		shift_bit=$KL_SHIFT
		;;
	"DT")
		mask=$DT_MASK
		shift_bit=$DT_SHIFT
		;;
	"CMMD")
		mask=$CMMD_MASK
		shift_bit=$CMMD_MASK
		;;
	*)
		echo "Invalid name: $name"
		;;
	esac
	var_value=$(($var_value&$mask))
	shift_idx=$(($idx<<$shift_bit))
	var_value=$(($var_value|$shift_idx))
	echo $var_value > /tmp/SPRINGEVENT_TMP
	echo "Set SPRINGEVENT_TMP as $var_value"
}
#Partition values can be the following strings
#    "BOOTL",
#    "DOCSIS",
#    "SVM",
#    "RG",
#    "STB",
#    "APPS",
#    "BOLT",
#    "DEVTREE",
#    "HYP",
#    "KERNEL",
#    "SSBL"

function do_img_start()
{
	#img start for ubifs images means
	#1) determine which mirror to update
	#2) is image zipped
	#3) need CRC
	#4) does pipe exist, if not create it
	#for non ubifs images, nothing
	#echo "do_img_start: partition=$partition command=$command size=$imgsize compressed=$compressed"

	ubiimage=1
	flash_write=0
	file=0
	flash_block_write=0
	if [ ! -e "/tmp/update" ]; then
		mknod /tmp/update p
	fi

	echo 1 > /proc/sys/vm/drop_caches

	case $partition in
		"BOLT")
			ubiimage=0
			d_node=${BLOCK_BOLT}
			d_type=${TYPE_BOLT}
			d_class=${CLASS_BOLT}
			dest=${tmpstorage1}/
			;;

		"BOOTL")
			#BOOTLOADER is a file in a filesystem, so determine space in fs remaining
			ubiimage=0
			space=$(df -k /mnt/flash | tail -1 | awk '{print $4}')
			space=$(($space * 1024))
			echo "free space is $space bytes"
			d_node=${BLOCK_CM0}
			d_type=${TYPE_CM}
			d_class=${CLASS_CM}
			dest=${tmpstorage1}/
			file=1
			;;

		"DEVTREE")
			flash_write=1
			ubiimage=0
			#update DEVTREE mirror to point to partition to be updated
			if [ $dtindex -eq 0 ]; then
				d_node=${BLOCK_DEVTREE1}
				echo -e "\nUpdating DEVTREE [${BLOCK_DEVTREE1}]"
			else
				d_node=${BLOCK_DEVTREE0}
				echo -e "\nUpdating DEVTREE [${BLOCK_DEVTREE0}]"
			fi
			d_type=${TYPE_DEVTREE}
			d_class=${CLASS_DEVTREE}
			#Set opposite SPRINGEVENT, if update fails, do recovery for device tree	.
			aim_idx=`expr 1 - $dtindex`
			set_springevent_var SPRINGEVENT DT $aim_idx
			;;

		"DOCSIS")
			#DOCSIS image is a file in a filesystem, so determine space in fs remaining
			ubiimage=0
			space=$(df -k /mnt/flash | tail -1 | awk '{print $2}')
			space=$(($space * 1024))
			cmbootl_size=$(ls -la /mnt/flash/cmboot.bin | tail -1 | awk '{print $5}')
			space=$((($space - $cmbootl_size) / 2))
			echo "free space is $space bytes"
			d_node=${BLOCK_CM0}
			d_type=${TYPE_CM}
			d_class=${CLASS_CM}
			dest=${tmpstorage2}/
			file=1
			;;

		"KERNEL")
			flash_write=1
			ubiimage=0
			#update KERNEL mirror to point to partition to be updated
			if [ $klindex -eq 0 ]; then
				d_node=${BLOCK_KERNEL1}
				echo -e "\nUpdating KERNEL [${BLOCK_KERNEL1}]"
			else
				d_node=${BLOCK_KERNEL0}
				echo -e "\nUpdating KERNEL [${BLOCK_KERNEL0}]"
			fi
			d_type=${TYPE_KERNEL}
			d_class=${CLASS_KERNEL}
			#Set opposite SPRINGEVENT, if update fails, do recovery for Kernel
			aim_idx=`expr 1 - $klindex`
			set_springevent_var SPRINGEVENT KL $aim_idx
			;;

		"SVM")
			flash_write=1
			d_nandsim=$(cat /proc/mtd | sed -n '/NAND simulator/p' | cut -d ':' -f 1 | sed -n 1p)
			#update CM mirror to point to partition to be updated
			if [ $cmindex -eq 0 ]; then
				d_node=${BLOCK_CM1}
				echo -e "\nUpdating CM [$BLOCK_CM1]"
			else
				d_node=${BLOCK_CM0}
				echo -e "\nUpdating CM [$BLOCK_CM0]"
			fi
			d_type=${TYPE_CM}
			d_class=${CLASS_CM}
			#Set opposite SPRINGEVENT, if update fails, do recovery for CM
			aim_idx=`expr 1 - $cmindex`
			set_springevent_var SPRINGEVENT CM $aim_idx
			;;

		"RG")
			flash_write=1
			d_nandsim=$(cat /proc/mtd | sed -n '/NAND simulator/p' | cut -d ':' -f 1 | sed -n 2p)
			#update RG mirror to point to partition to be updated
			if [ $rgindex -eq 0 ]; then
				d_node=${BLOCK_RG1}
				echo -e "\nUpdating RG [$BLOCK_RG1]"
			else
				d_node=${BLOCK_RG0}
				echo -e "\nUpdating RG [$BLOCK_RG0]"
			fi
			d_type=${TYPE_RG}
			d_class=${CLASS_RG}
			#Set opposite SPRINGEVENT, if update fails, do recovery for RG
			aim_idx=`expr 1 - $rgindex`
			set_springevent_var SPRINGEVENT RG $aim_idx
			;;

		"SSBL")
			#We always copy ssbl0 to ssbl1 and update ssbl0
			echo -e "\nUpdating SSBL [$BLOCK_BOLT]"
			ubiimage=0
			flash_block_write=1
			d_node=${BLOCK_BOLT}
			d_type=${TYPE_BOLT}
			d_class=${CLASS_BOLT}
			dest=${tmpstorage1}/
			;;

		*) echo "Invalid partition: $partition"
			exit 1
			;;
	esac

	if [ $file == 1 ]; then
		if [ ${imgsize} -gt ${space} ]; then
			echo "$partition file image is $imgsize bytes. Will not fit in partition with free space of ${space} bytes"
			exit 1
		fi
	else
		#sanity check: Before proceeding, ensure that the image being received will fit in the destination partition
		partition_size=$(cat ${d_class}/size)
		[ "$d_type" = "disk" ] && let partition_size=partition_size*512

		if [ $flash_block_write == 0 ]; then
			if [ ${imgsize} -gt ${partition_size} ]; then
				echo "$partition image is $imgsize bytes. Will not fit in partition of size $partition_size bytes"
				exit 1
			fi
		fi
	fi

	if [ $flash_write == 1 ]; then
		if [ $ubiimage == 1 ]; then
			if [ "$d_type" = "disk" ]; then
				if [ ! -c /dev/$d_nandsim ]; then
					echo "NAND simulator driver required to flash ubifs image on ext4 file-system"
					exit 1
				fi

				echo "1 4 1 7" > /proc/sys/kernel/printk
				if [ "$compressed" == 1 ]; then
					dd if=/tmp/update | gunzip | ubiformat /dev/${d_nandsim} --yes --flash-image=- --image-size=$imgsize -s 2048 -q &
				else
					dd if=/tmp/update | ubiformat /dev/${d_nandsim} --yes --flash-image=- --image-size=$imgsize -s 2048 -q &
				fi

				if [ $? -ne 0 ]; then
					echo "7 4 1 7" > /proc/sys/kernel/printk
					echo "UBIFS streaming operation failed (code: $?)"
					exit 1
				fi

				echo ${d_node}.${d_nandsim} > /tmp/${partition}.work
			else
				get_mtd_attr ${BLOCK_RG0:3} erasesize flash_erasesize
				modulo=`expr $imgsize % $flash_erasesize`
				if [ $modulo -ne 0 ]; then
					echo "imagesize ($imgsize) is not a multiple of erase size ($flash_erasesize)"
					exit 1
				fi

				if [ "$compressed" == 1 ]; then
					dd if=/tmp/update | gunzip | ubiformat /dev/${d_node} --yes --flash-image=- --image-size=$imgsize -q &
				else
					dd if=/tmp/update | ubiformat /dev/${d_node} --yes --flash-image=- --image-size=$imgsize -q &
				fi

				if [ $? -ne 0 ]; then
					echo "UBIFS streaming operation failed (code: $?)"
					exit 1
				fi

				echo ${d_node} > /tmp/${partition}.work
			fi
		else
			[ "$d_type" != "disk" ] && flash_erase /dev/${d_node} 0 0
			if [ "$d_type" = "nand" ]; then
				if [ "$compressed" == 1 ]; then
					echo "image is compressed"
					dd if=/tmp/update | gunzip | nandwrite -p /dev/${d_node} - &
				else
					dd if=/tmp/update | nandwrite -p /dev/${d_node} - &
				fi

				if [ $? -ne 0 ]; then
					echo "NAND streaming operation failed (code: $?)"
					exit 1
				fi

				echo ${d_node} > /tmp/${partition}.work
			else
				if [ "$compressed" == 1 ]; then
					echo "image is compressed"
					dd if=/tmp/update | gunzip > /dev/${d_node} &
				else
					dd if=/tmp/update > /dev/${d_node} &
				fi

				if [ $? -ne 0 ]; then
					echo "dd streaming operation failed (code: $?)"
					exit 1
				fi

				echo ${d_node} > /tmp/${partition}.work
			fi
		fi
	else
		echo "destpartition=$dest$partition"
		if [ "$compressed" == 1 ]; then
			dd if=/tmp/update | gunzip > $dest"$partition" &
		else
			dd if=/tmp/update > $dest"$partition" &
		fi

		if [ $? -ne 0 ]; then
			echo "dd streaming operation failed (code: $?)"
			exit 1
		fi

		echo ${d_node} > /tmp/${partition}.work
	fi
}

function do_diskimage()
{
	work_file=$1

	dst_src=$(cat ${work_file})
	d_node=${dst_src%.*}
	d_nandsim=${dst_src#*.}
	unum=`expr 10 + ${d_nandsim:(-1)}`
	m_dst=ext4_dst_${unum}
	m_src=ubifs_src_${unum}

	ubiattach -d ${unum} -p /dev/${d_nandsim} -O 2048 > /dev/null
	mkdir -p /mnt/${m_dst}
	mkdir -p /mnt/${m_src}
	ext4_block_name=$(echo ${d_node} | sed 's/[0-9]*$//')
	ext4_block_num=$(echo ${d_node} | sed -n 's/.*\([^0-9]\)//p')
	j=0

	for i in /dev/ubi${unum}_?; do
		if [ "$ubifs" == 1 ]; then
			mount -r -t ubifs $i /mnt/${m_src} > /dev/null
		else
			ubiblock -c $i
			ubi_block=$(echo $i | sed 's/ubi/ubiblock/')
			mount -r -t squashfs ${ubi_block} /mnt/${m_src} > /dev/null
		fi
		if [ $? -ne 0 ]; then
			echo "unable to mount $i"
			ubidetach -d $unum
			exit 1
		fi
		echo "7 4 1 7" > /proc/sys/kernel/printk
		ext4_part_node="${ext4_block_name}`expr $j + $ext4_block_num`"
		mkfs.ext4 -FE stripe-width=32 /dev/$ext4_part_node
		tune2fs -c 0 -i 0 -o journal_data_ordered /dev/$ext4_part_node
		mount -t ext4 /dev/$ext4_part_node /mnt/${m_dst}
		echo "1 4 1 7" > /proc/sys/kernel/printk
		rm -rf /mnt/${m_dst}/lost+found/
		cp -ai /mnt/${m_src}/. /mnt/${m_dst}
		sync
		umount /mnt/${m_dst}
		if [ $? -ne 0 ]; then
			echo "Unable to umount /mnt/${m_dst}"
			exit 1
		fi

		umount /mnt/${m_src} > /dev/null
		if [ $? -ne 0 ]; then
			echo "Unable to umount /mnt/${m_src}"
			exit 1
		fi
		let j=j+2
	done

	if [ "$work_file" == "/tmp/RG.work" -a ! -b /dev/ubi${unum}_1 ]; then
		apps_block="/dev/${ext4_block_name}`expr $j + $ext4_block_num`"
		if node_has_extfs $apps_block; then
			dd if=/dev/zero of=${apps_block} seek=1080 bs=1 count=2 >/dev/null 2>&1
		fi
	fi
	rm -rf /mnt/${m_dst}
	rm -rf /mnt/${m_src}
	ubidetach -d ${unum} > /dev/null
	echo "7 4 1 7" > /proc/sys/kernel/printk
}

function is_dir_unmounted() {
	local dirname="$1"
	local attempts=0
	local maxtries=5

	while [ $attempts -le $maxtries ]; do {
		if ! mount | egrep -q "on ${dirname} type"; then
			return 0
		else
			attempts=$(( $attempts + 1))
			sleep 1s
		fi
	}; done

	return 1
}

function test_mount()
{
	echo "test mount ${d_node}"
	echo 1 > /proc/sys/vm/drop_caches
	ubiattach -p /dev/${d_node} -d 9
	if [ $? -ne 0 ]; then
		echo "unable to ubiattach /dev/${d_node}"
		exit 1
	fi
	for f in /sys/class/ubi/ubi9_?; do
		if [ "$ubifs" == 1 ]; then
			echo "ubifs test mount ${f:15}"
			testdir="/tmp/mnt/${f:15}"
			mkdir -p ${testdir}
			# Justin, 20171209, add squashfs support
			# mount -r -t ubifs ${f:15} ${testdir}
			mount -t squashfs /dev/ubiblk9_${f:20} ${testdir}
		else
			ubiblock -c /dev/${f:15}
			ubi_block=$(echo ${f:15} | sed 's/ubi/ubiblock/')
			echo "squashfs test mount ${ubi_block}"
			testdir="/tmp/mnt/${ubi_block}"
			mkdir -p ${testdir}
			mount -r -t squashfs /dev/${ubi_block} ${testdir}
		fi
		if [ $? -ne 0 ]; then
			echo "unable to mount ${f:15}"
			rmdir ${testdir}
			ubidetach -d 9
			exit 1
		fi
		find ${testdir} > /dev/null
		if [ $? -ne 0 ]; then
			echo "unable to traverse file system ${testdir}"
			umount ${testdir}
			rmdir ${testdir}
			ubidetach -d 9
			exit 1
		fi

		sync

		umount ${testdir}
		rmdir ${testdir}
		if [ "$ubifs" == 1 ]; then
			echo "success ubifs test mount ${f:15}"
		else
			echo "success squashfs test mount ${ubi_block}"
		fi
	done
	echo "before ubidetach"
	# invocation
	if is_dir_unmounted "$1"; then
		echo "success to umount ${testdir}"
		ubidetach -d 9
	else
		echo "failed to umount ${testdir}"
		exit 1
	fi
	echo "success test mount ${d_node}"
	echo 1 > /proc/sys/vm/drop_caches
}

function do_img_end()
{
	#img end for ubifs images means
	#1) check for errors, crc (if necessary)
	#2) Test mount ???
	#3) if all is successfull, update BOLT variable with mirror index
	#for non ubifs images, do actual update from temporary file
	#echo "do_img_end: partition=$partition command=$command size=$imgsize compressed=$compressed"

	case $partition in
		"BOLT" | "BOOTL" | "DOCSIS" | "SSBL")
			#This is a no-op. Update has been piped to a tmp file. Wait for full monolith completion to flash
			rm -rf /tmp/${partition}.work
			;;

		"SVM")
			[ ! -f /tmp/SVM.work ] && return
			ps -C ubiformat &> /dev/null
			while [ $? -eq 0 ]
			do
				echo "waiting for SVM ubiformat to finish"
				sleep 1
				ps -C ubiformat &> /dev/null
			done

			#####################
			##error checking should go here
			if [ $cmindex -eq 0 ]; then
				d_node=${BLOCK_CM1}
			else
				d_node=${BLOCK_CM0}
			fi
			[ "$TYPE_CM" = "disk" ] && do_diskimage /tmp/SVM.work || test_mount
			#####################
			touch $tmpstorage1/CM

			#Set SPRINGEVENT_TMP to backup updated CM partition
			get_env_var "CM_IDX" aim_idx
			set_springevent_tmp CM $aim_idx
			rm -rf /tmp/SVM.work
			;;

		"RG")
			[ ! -f /tmp/RG.work ] && return
			ps -C ubiformat &> /dev/null
			while [ $? -eq 0 ]
			do
				echo "waiting for RG ubiformat to finish"
				sleep 1
				ps -C ubiformat &> /dev/null
			done
			#####################
			##error checking should go here
			if [ $rgindex -eq 0 ]; then
				d_node=${BLOCK_RG1}
			else
				d_node=${BLOCK_RG0}
			fi
			[ "$TYPE_RG" = "disk" ] && do_diskimage /tmp/RG.work || test_mount
			#####################
			touch $tmpstorage1/RG

			#Set SPRINGEVENT_TMP tp backup updated RG partition
			get_env_var "RG_IDX" aim_idx
			set_springevent_tmp RG $aim_idx
			rm -rf /tmp/RG.work
			;;

		"KERNEL")
			[ ! -f /tmp/KERNEL.work ] && return
			#####################
			##error checking should go here
			#####################
			touch $tmpstorage2/KERNEL

			#Set SPRINGEVENT_TMP tp backup updated KL partition
			get_env_var "KL_IDX" aim_idx
			set_springevent_tmp KL $aim_idx
			rm -rf /tmp/KERNEL.work
			;;

		"DEVTREE")
			[ ! -f /tmp/DEVTREE.work ] && return
			#####################
			##error checking should go here
			#####################
			touch $tmpstorage1/DEVTREE

			#Set SPRINGEVENT_TMP tp backup updated DT partition
			get_env_var "DT_IDX" aim_idx
			set_springevent_tmp DT $aim_idx
			rm -rf /tmp/DEVTREE.work
			;;

		*) echo "Invalid partition: $partition"
			exit 1
			;;
	esac
}

function do_mlth_end()
{
	if lsmod | grep nandsim >/dev/null; then
		if kmodloader 2>/dev/null; then
			rmmod nandsim
		else
			modprobe -r nandsim
		fi
	fi

# if we reached this point, the update has been succesfull. So all temp files should now be flashed.
	#identify mtderase/emmc block size
	[ "$TYPE_BOLT" = "disk" ] && bolt_block_size=0x200 || bolt_block_size=0x$(grep "\"flash0\"" /proc/mtd | awk '{print $3}')
	bolt_block_size_b=$((($bolt_block_size) / 1))

	#This handles SSBL upgrades
	main_node=/dev/${BLOCK_BOLT}
	backup_node=/dev/${BLOCK_BOLT}
	if [ -f $tmpstorage1/SSBL -a -e /proc/device-tree/bolt/fsbl_version ]; then
		FSBL_VERSION=$(hexdump -e '2/1 "%02x"' /proc/device-tree/bolt/fsbl_version)
		if [ "$FSBL_VERSION" = "00020000" ]; then
			# Justin, 20180124, modify for NAND alignment
			# SSBL_BANK_TEXT_OFFS=0x00030000
			# SSBL_BANK_2_TEXT_OFFS=0x000B0000
			SSBL_BANK_TEXT_OFFS=0x00040000
			SSBL_BANK_2_TEXT_OFFS=0x000C0000
		elif [ "$FSBL_VERSION" = "00030000" ]; then
			SSBL_BANK_TEXT_OFFS=0x00020000
			SSBL_BANK_2_TEXT_OFFS=0x000A0000
		else
			#For Bolt v4.0 and up
			if [ "$TYPE_BOLT" = "disk" ]; then
				if [ -f "/proc/device-tree/bolt/ssbl0_start" ]; then
					SSBL_BANK_TEXT_OFFS=0x$(hexdump -e '2/1 "%02x"' /proc/device-tree/bolt/ssbl0_start)
					SSBL_BANK_2_TEXT_OFFS=0x$(hexdump -e '2/1 "%02x"' /proc/device-tree/bolt/ssbl1_start)
				else
					SSBL_BANK_TEXT_OFFS=256*1024
					SSBL_BANK_2_TEXT_OFFS=$(($SSBL_BANK_TEXT_OFFS+(512*1024)))
				fi
			else
				SSBL_BANK_TEXT_OFFS=0
				SSBL_BANK_2_TEXT_OFFS=0
				main_node=/dev/$(grep "ssbl0" /proc/mtd | cut -f 1 -d ':')
				backup_node=/dev/$(grep "ssbl1" /proc/mtd | cut -f 1 -d ':')
			fi
		fi
		SSBL_BANK_1=0 # SSBL BANK 1 INDEX
		SSBL_BANK_2=1 # SSBL BANK 2 INDEX

		#Check for upgradability
		boltcrc -u $tmpstorage1/SSBL 2>/dev/null
		is_upgrade_ok=$?
		if [ $is_upgrade_ok -ne 0 ]; then
			echo "SSBL won't be updated($is_upgrade_ok)"
		else
			echo "SSBL Image can be updated($is_upgrade_ok)"
			#identify number of blocks, offsets
			ssbl1_1st_block=$(($SSBL_BANK_TEXT_OFFS/$bolt_block_size))
			ssbl2_1st_block=$(($SSBL_BANK_2_TEXT_OFFS/$bolt_block_size))
			if [ "$FSBL_VERSION" -gt "00030000" ]; then
				ssbl_total_size=0x$(hexdump -e '2/1 "%02x"' /proc/device-tree/bolt/ssbl0_size)
				ssbl_total_block_count=$(($ssbl_total_size/$bolt_block_size))
				#Add an extra block to erase since we alloc'ed an extra block to nand for bad block handling
				if [ "$TYPE_BOLT" = "nand" ]; then
					ssbl_total_block_count=$(($ssbl_total_block_count+1))
				fi
			else
				ssbl_total_block_count=$(($ssbl2_1st_block-$ssbl1_1st_block))
				ssbl_total_size=$(($bolt_block_size*$ssbl_total_block_count))
			fi

			#Check SSBL BANK 1 integrity
			boltcrc -c $SSBL_BANK_1 2>/dev/null
			is_bank_crc_ok=$?
			#If we are using SSBL Bank 2, we assume Bank 1 is bad and should not copy 1 to 2
			current_ssbl_bank=$(boltenv -g SSBL_BANK)
			if [ $is_bank_crc_ok -eq 0 ] && [ "$current_ssbl_bank" == "$SSBL_BANK_1" ]; then
				if [ "$TYPE_BOLT" != "disk" ]; then
					#erase SSBL Bank 2
					echo -e "\nErasing SSBL Bank 2[$backup_node at $SSBL_BANK_2_TEXT_OFFS offset]"
					flash_erase $backup_node $SSBL_BANK_2_TEXT_OFFS $ssbl_total_block_count
				fi

				#Copy SSBL Bank 1 to SSBL Bank 2
				echo -e "\nBacking up SSBL Bank1 to SSBL Bank 2[$backup_node at $SSBL_BANK_2_TEXT_OFFS offset]"
				if [ "$TYPE_BOLT" = "nand" ]; then
					nandwrite -p ${backup_node} --start=$SSBL_BANK_2_TEXT_OFFS --input-skip=$SSBL_BANK_TEXT_OFFS --input-size=$ssbl_total_size ${main_node}
				else
					#NOR and EMMC
					[ "$TYPE_BOLT" = "disk" ] && echo 0 > ${CLASS_BOLT}/force_ro
					dd if=${main_node} skip=${ssbl1_1st_block} of=${backup_node} seek=${ssbl2_1st_block} count=${ssbl_total_block_count} bs=${bolt_block_size_b}
					[ "$TYPE_BOLT" = "disk" ] && echo 1 > ${CLASS_BOLT}/force_ro
				fi

				#Verify SSBL Bank 2 Integrity
				boltcrc -c $SSBL_BANK_2 2>/dev/null
				is_bank_crc_ok=$?
				if [ $is_bank_crc_ok -ne 0 ]; then
					echo "SSBL Bank 2 Integrity Failure($is_bank_crc_ok)"
					rm -f $tmpstorage1/SSBL
					sync
					exit 1
				else
					echo "SSBL Bank 2 Integrity OK($is_bank_crc_ok)"
				fi
			else
				echo "Not backing up SSBL Bank 1 since we are executing from Bank 2 (Bank 1 may be corrupt)"
			fi

			if [ "$TYPE_BOLT" != "disk" ]; then
				#Erase SSBL Bank 1
				echo -e "\nErasing SSBL Bank 1[$main_node at $SSBL_BANK_TEXT_OFFS offset]"
				flash_erase $main_node $SSBL_BANK_TEXT_OFFS $ssbl_total_block_count
			fi

			#Copy Dload SSBL to SSBL Bank 1
			echo -e "\nUpdating SSBL Bank 1[$main_node at $SSBL_BANK_TEXT_OFFS offset]"
			if [ "$TYPE_BOLT" = "nand" ]; then
				nandwrite -p ${main_node} --start=$SSBL_BANK_TEXT_OFFS $tmpstorage1/SSBL
			else
				#NOR and EMMC
				[ "$TYPE_BOLT" = "disk" ] && echo 0 > ${CLASS_BOLT}/force_ro
				dd if=$tmpstorage1/SSBL of=${main_node} seek=${ssbl1_1st_block} count=${ssbl_total_block_count} bs=${bolt_block_size_b}
				[ "$TYPE_BOLT" = "disk" ] && echo 1 > ${CLASS_BOLT}/force_ro
			fi

			#Verify SSBL Bank 1 Integrity
			boltcrc -c $SSBL_BANK_1 2>/dev/null
			is_bank_crc_ok=$?
			if [ $is_bank_crc_ok -ne 0 ]; then
				echo "SSBL Bank 1 Integrity Failure($is_bank_crc_ok)"
				rm -f $tmpstorage1/SSBL
				sync
				exit 1
			else
				echo "SSBL Bank 1 Integrity OK($is_bank_crc_ok)"
			fi

		fi

		rm -f $tmpstorage1/SSBL

		if [ $is_upgrade_ok -ne 0 ]; then
			echo -e "\nSSBL update skipped"
		else
			echo -e "\nSSBL update complete"
		fi
	fi
	#This handles full BOLT upgrades
	if [ -f $tmpstorage1/BOLT ]; then
		node=/dev/${BLOCK_BOLT}
		echo -e "\nUpdating BOLT [$BLOCK_BOLT]"
		if [ "$TYPE_BOLT" != "disk" ]; then
			block_count=0

			#For Bolt v4.0 and up			
			fsbl_partition=$(grep "\"flash0.fsbl\"" /proc/mtd | awk '{print $4}')
			if [ -n "$fsbl_partition" ]; then
				#Get block size from CS0
				bolt_block_size=0x$(grep "\"flash0\"" /proc/mtd | awk '{print $3}')
				#Calculate erase block count from device-tree if exists else get it from MTD
				if [ -f "/proc/device-tree/bolt/ssbl0_start" ]; then
					ssblsz=0x$(hexdump -e '2/1 "%02x"' /proc/device-tree/bolt/ssbl0_size)
					ssblstart=0x$(hexdump -e '2/1 "%02x"' /proc/device-tree/bolt/ssbl0_start)
					block_count=$((($ssblsz+$ssblstart)/$bolt_block_size))
					#Add an extra block to erase since we alloc'ed an extra block to nand for bad block handling
					if [ "$TYPE_BOLT" = "nand" ]; then
						block_count=$(($block_count+1))
					fi
				else
					fsblsz=0x$(grep "fsbl" /proc/mtd | awk '{print $2}')
					ssblsz=0x$(grep "ssbl0" /proc/mtd | awk '{print $2}')
					block_count=$((($fsblsz+($ssblsz*2))/$bolt_block_size))
				fi
			fi

			flash_erase $node 0 $block_count
		fi

		if [ "$TYPE_BOLT" = "nand" ]; then
			nandwrite -p $node $tmpstorage1/BOLT
		else
			[ "$TYPE_BOLT" = "disk" ] && echo 0 > ${CLASS_BOLT}/force_ro
			dd if=$tmpstorage1/BOLT of=${node} bs=${bolt_block_size_b}
			[ "$TYPE_BOLT" = "disk" ] && echo 1 > ${CLASS_BOLT}/force_ro
		fi
		rm -f $tmpstorage1/BOLT
		echo -e "\nBOLT update complete"
	fi
	if [ -f $tmpstorage1/BOOTL ]; then
		echo -e "\nUpdating CM Bootloader "
		cp -f $tmpstorage1/BOOTL /mnt/flash/cmboot.bin
		rm -f $tmpstorage1/BOOTL
		#copy image to mirrored partition as well
		#attach/mount 1-$cmindex partition, then copy
		index=`expr 1 - $cmindex`
		echo -e "\nCM bootloader update complete"

		#Set SPRINGEVENT_TMP to backup CM partition
		get_env_var "CM_IDX" ori_idx
		aim_idx=`expr 1 - $ori_idx`
		set_springevent_tmp CM $aim_idx
	fi
	if [ -f $tmpstorage2/DOCSIS ]; then
		echo -e "\nUpdating DOCSIS image"
		mv -f /mnt/flash/cmrun1.bin /mnt/flash/cmrun0.bin
		cp -f $tmpstorage2/DOCSIS /mnt/flash/cmrun1.bin
		rm -f $tmpstorage2/DOCSIS
		#copy image to mirrored partition as well
		#attach/mount 1-$cmindex partition, then copy
		index=`expr 1 - $cmindex`
		echo -e "\nDOCSIS image update complete"

		#Set SPRINGEVENT_TMP to backup CM partition
		get_env_var "CM_IDX" ori_idx
		aim_idx=`expr 1 - $ori_idx`
		set_springevent_tmp CM $aim_idx
	fi
	if [ -f $tmpstorage2/KERNEL ]; then
		echo -e "\nKERNEL image update complete"
	fi
	if [ -f $tmpstorage1/DEVTREE ]; then
		echo -e "\nDEVTREE image update complete"
		fi
	if [ -f $tmpstorage1/CM ]; then
		echo -e "\nCM image update complete"
	fi
	if [ -f $tmpstorage1/RG ]; then
		echo -e "\nRG image update complete"
	fi

	# Make sure we're all synced to flash since we're running from a script
	sync

	#signal completion by creating file in tmpfs
	touch /tmp/complete
}

function get_default_startup_env_param()
{
	# Determine apps and data
	# EMMC or NOR-EMMC
	if [ "$TYPE_RG" == "disk" ]; then
		if [ $_rgindex ]; then
			eval RG_BLK=\$PARTNUM_RG${_rgindex}
		else
			eval RG_BLK=\$PARTNUM_RG${rgindex}
		fi
		apps_block="/dev/mmcblk0p`expr $RG_BLK + 2`"
	else
		# NAND
		if [ "$TYPE_BOLT" = "nand" ]; then
			RG_BLK=flash0.rg
		# NOR-NAND
		else
			RG_BLK=flash1.rg
		fi

		if [ $_rgindex ]; then
			RG_BLK=${RG_BLK}${_rgindex}
		else
			RG_BLK=${RG_BLK}${rgindex}
		fi
		apps_block=/dev/$(grep "\<${RG_BLK}\>" /proc/mtd | cut -f 1 -d ':')
	fi

	# Check for BOLT RG_BLK variable
	RG_BLK=$(boltenv -g RG_BLK)
	[ $? -ne 0 ] && RG_BLK=

	# NAND
	if [ "$TYPE_BOLT" = "nand" ]; then
		if node_has_ubivol_apps $apps_block; then
			[ "$ubifs" == 1 ] && apps_data="ubifs_apps ubifs_data" || apps_data="squashfs_apps ubifs_data"
		else
			apps_data="ubifs_data"
		fi
		bolt_kernel="flash0.kernel\$KL_IDX"
		if [ "$ubifs" == 1 ]; then
		    bolt_cmdline="\"ubi.mtd=flash0.rg\$RG_IDX rootfstype=ubifs root=ubi0:rootfs platformboot $apps_data coherent_pool=1M \$XARGS\""
		else
		    bolt_cmdline="\"ubi.mtd=flash0.rg\$RG_IDX ubi.block=0,rootfs rootfstype=squashfs root=/dev/ubiblock0_0 ro platformboot $apps_data coherent_pool=1M \$XARGS\""
		fi
	# EMMC
	elif [ "$TYPE_BOLT" = "disk" ]; then
		if node_has_extfs $apps_block; then
			apps_data="ext4_apps ext4_data"
		else
			apps_data="ext4_data"
		fi
		bolt_kernel="flash0.kernel\$KL_IDX"
		if [ -n "$RG_BLK" ]; then
			bolt_cmdline="\"root=/dev/mmcblk0p\$RG_BLK rootfstype=ext4 rootwait mmc_core.removable=0 platformboot $apps_data coherent_pool=1M \$XARGS\""
		elif [ $_rgindex ]; then
			eval RG_BLK=\$PARTNUM_RG${_rgindex}
			bolt_cmdline="\"root=/dev/mmcblk0p$RG_BLK rootfstype=ext4 rootwait mmc_core.removable=0 platformboot $apps_data coherent_pool=1M \$XARGS\""
		else
			eval RG_BLK=\$PARTNUM_RG${rgindex}
			bolt_cmdline="\"root=/dev/mmcblk0p$RG_BLK rootfstype=ext4 rootwait mmc_core.removable=0 platformboot $apps_data coherent_pool=1M \$XARGS\""
		fi
	# NOR-NAND
	elif [ "$TYPE_RG" = "nand" ]; then
		if node_has_ubivol_apps $apps_block; then
			[ "$ubifs" == 1 ] && apps_data="ubifs_apps jffs2_data" || apps_data="squashfs_apps jffs2_data"
		else
			apps_data="jffs2_data"
		fi
		bolt_kernel="flash1.kernel\$KL_IDX"
		if [ "$ubifs" == 1 ]; then
			bolt_cmdline="\"ubi.mtd=flash1.rg\$RG_IDX rootfstype=ubifs root=ubi0:rootfs platformboot $apps_data coherent_pool=1M \$XARGS\""
		else
			bolt_cmdline="\"ubi.mtd=flash1.rg\$RG_IDX ubi.block=0,rootfs rootfstype=squashfs root=/dev/ubiblock0_0 ro platformboot $apps_data coherent_pool=1M \$XARGS\""
		fi
	# NOR-EMMC
	else
		if node_has_extfs $apps_block; then
			apps_data="ext4_apps jffs2_data"
		else
			apps_data="jffs2_data"
		fi
		bolt_kernel="emmcflash0.kernel\$KL_IDX"
		if [ -n "$RG_BLK" ]; then
			bolt_cmdline="\"root=/dev/mmcblk0p\$RG_BLK rootfstype=ext4 rootwait mmc_core.removable=0 platformboot $apps_data coherent_pool=1M \$XARGS\""
		elif [ $_rgindex ]; then
			eval RG_BLK=\$PARTNUM_RG${_rgindex}
			bolt_cmdline="\"root=/dev/mmcblk0p$RG_BLK rootfstype=ext4 rootwait mmc_core.removable=0 platformboot $apps_data coherent_pool=1M \$XARGS\""
		else
			eval RG_BLK=\$PARTNUM_RG${rgindex}
			bolt_cmdline="\"root=/dev/mmcblk0p$RG_BLK rootfstype=ext4 rootwait mmc_core.removable=0 platformboot $apps_data coherent_pool=1M \$XARGS\""
		fi
	fi

	# Justin, 20171211, Add squashfs support
	FLASH_INDEX=`cat /proc/mtd | grep cm0 | awk -F '"' '{print substr($2,6,1)}'`
	D_startup="load -nz -raw -addr=\$DT_ADDRESS -max=0x10000 flash$FLASH_INDEX.kernel\$KL_IDX DT;boot flash$FLASH_INDEX.kernel\$KL_IDX \"ubi.mtd=flash$FLASH_INDEX.rg\$RG_IDX rootfstype=squashfs root=/dev/ubiblk0_0 platformboot ubifs_apps jffs2_data coherent_pool=1M\""
	eval "$1='${D_startup}'"
}

function do_confirm_complete()
{
	echo "Waiting to receive download success or failure notification from CM.."
	count=600
	while [ ! -f /tmp/complete ]; do
			sleep 1
			count=$(($count-1))
			if [ $count == 0 ]; then
				echo "Timeout! Failed to receive download success or failure notification from CM."
				exit 1
			fi
	done

	if [ -f /var/run/wdmd/wdmd.pid ]; then
		# disable watchdog to avoid getting killed while transitioning
		kill -10 `cat /var/run/wdmd/wdmd.pid`
	fi

	if [ -f $tmpstorage2/KERNEL ]; then
		_klindex=`expr 1 - $klindex`
	fi
	if [ -f $tmpstorage1/RG ]; then
		_rgindex=`expr 1 - $rgindex`
	fi
	if [ -f $tmpstorage1/CM ]; then
		_cmindex=`expr 1 - $cmindex`
	fi
	if [ -f $tmpstorage1/DEVTREE ]; then
		_dtindex=`expr 1 - $dtindex`
	fi

	#ensure temp area is empty
	rm -rf $tmpstorage1
	rm -rf $tmpstorage2

	bolt_atomic_set="boltenv "
	#if update index is set and doesn't match existing, then set
	if [ $_dtindex ]; then
		echo "Updating DT_IDX from $dtindex->$_dtindex"
		bolt_atomic_set+="-s DT_IDX "$_dtindex" "
	fi
	if [ $_klindex ]; then
		echo "Updating KL_IDX from $klindex->$_klindex"
		bolt_atomic_set+="-s KL_IDX "$_klindex" "
	fi
	if [ $_rgindex ]; then
		echo "Updating RG_IDX from $rgindex->$_rgindex"
		bolt_atomic_set+="-s RG_IDX "$_rgindex" "
	fi
	if [ $_cmindex ]; then
		echo "Updating CM_IDX from $cmindex->$_cmindex"
		bolt_atomic_set+="-s CM_IDX "$_cmindex" "
	fi

	echo -e "\n\nImage update successful and complete\n\n\n"

	# Check if SPRINGEVENT_TMP is present. SSBL images do not cause eternal
	# spring events. Thus, SPRINGEVENT_TMP is non-existent when only updating
	# SSBL
	if [ -f /tmp/SPRINGEVENT_TMP ]; then
		get_default_startup_env_param new_boot_command

		old_boot_command=`boltenv -g STARTUP`
		if [ $? -ne 0 ]; then
			echo "No startup variable is currently set in BOLT, one will be set accordingly"
			echo -e "\nExample: boot flash0.kernel: 'ubi.mtd=flash1.svm1 rootfstype=ubifs root=ubi0:rootfs platformboot'"
			old_boot_command="dummy"
		fi

		# If new startup is different from old, reprogram
		if [ "$old_boot_command" != "$new_boot_command" ]; then
			echo "Changing boot command to boot from new images"
			echo -e "\nPrevious STARTUP variable: $old_boot_command"
			[ "$old_boot_command" != "dummy" ] && bolt_bakstartup_array=("$old_boot_command")
			echo -e "\nUpdating STARTUP variable to $new_boot_command"
			bolt_startup_array=("$new_boot_command")
		else
			echo -e "\nNot changing boot command for new images"
		fi

		springevent=`cat /tmp/SPRINGEVENT_TMP`
		bolt_atomic_set+="-s SPRINGEVENT "$springevent" "

		echo "Clear the eternal spring reset count"
		bolt_atomic_set+="-s FCNT "0" "

		# Do all of the bolt index and startup writes together
		if [ ${#bolt_bakstartup_array[@]} -ne 0 ]; then
			bolt_atomic_set+="-s BAK_STARTUP '${bolt_bakstartup_array[@]}' "
		fi
		if [ ${#bolt_startup_array[@]} -ne 0 ]; then
			bolt_atomic_set+="-s STARTUP '${bolt_startup_array[@]}'"
		fi
		eval $bolt_atomic_set

		#Set SPRINGEVENT, unset SPRINGEVENT_TMP
		set_springevent_var SPRINGEVENT PROG 1
		rm /tmp/SPRINGEVENT_TMP
	fi
	# Make sure we're all synced to flash since we're running from a script
	sync
	echo "Flash programming complete, CM ready, rebooting..."
	reboot
}

#get details of board, flash, etc
#get_environment
if [ "${CHIP}" = "" ]; then
	. /etc/init.d/rcS.util nodump
fi

####################
#Determine MTD/eMMC locations
BLOCKS="bolt kernel0 kernel1 hyp devtree0 devtree1 cm0 stb0 rg0 cm1 stb1 rg1 rgnonvol0"

for b in $BLOCKS; do
	# Convert lower to upper case
	B=$(echo ${b} | tr 'a-z' 'A-Z')

	# Remove trailing number
	T=$(echo ${B} | sed 's/[0-9]*$//')

	# grep /proc/mtd to find partition name and get corresponding mtdN
	eval BLOCK_${B}=$(grep "\<${b}\>" /proc/mtd | cut -f 1 -d ':')
	eval BLOCK=\$BLOCK_${B}

	if [ -n "${BLOCK}" ]; then
		eval TYPE_${T}=$(cat /sys/class/mtd/${BLOCK}/type | cut -f 2 -d '-')
		eval CLASS_${T}="/sys/class/mtd/${BLOCK}"
	elif [ "$b" = "bolt" ]; then
		# BOLT MTD does not exist. This means the board is either booting from EMMC or has
		# separate fsbl/ssbl partitions
		if [ -e /dev/mtd0 -a -h /sys/class/mtd/mtd0/device ]; then
				BLOCK_BOLT=$(grep "\"flash0\"" /proc/mtd | cut -f 1 -d ':')
				TYPE_BOLT=$(cat /sys/class/mtd/$BLOCK_BOLT/type | cut -f 2 -d '-')
				CLASS_BOLT="/sys/class/mtd/$BLOCK_BOLT"
		elif [ -e /dev/mmcblk0boot0 ]; then
				BLOCK_BOLT="mmcblk0boot0"
				TYPE_BOLT="disk"
				CLASS_BOLT="/sys/class/block/mmcblk0boot0"
		fi
	elif find_part_node_by_name ${b} "mmcblk0" BLOCK_${B}; then
		eval BLOCK=\$BLOCK_${B}
		eval TYPE_${T}="disk"
		eval CLASS_${T}="/sys/class/block/${BLOCK}"
		eval PARTNUM_${B}=$(echo $BLOCK | sed -n 's/.*\([^0-9]\)//p')
	fi
done

####################
tmpstorage1=/tmp/temp/update
tmpstorage2=/tmp/temp
####################

#which mirror did we boot from?
#since we are about to update, let's default them if they don't exist, assuming this is the first IP update
bootstring=`cat /proc/cmdline`
get_env_var "DT_IDX" dtindex
get_env_var "KL_IDX" klindex
get_env_var "RG_IDX" rgindex
get_env_var "CM_IDX" cmindex

compressed=
imgsize=
command=
partition=
ubifs=

#process arguments
# -c to give command (IMG_START, IMG_END, MLTH_START, MLTH_END)
# -p to give partition
# -z for compression on/off
# -s for size
# -S for SquashFS over UBI support

while getopts "h?zs:c:p:u" opt; do
	case "$opt" in
	h|\?)
		show_help
		exit 0
		;;
	z)  compressed=1
		;;
	s)  imgsize=$OPTARG
		;;
	u)  ubifs=1
		;;
	c)  command=$OPTARG
		;;
	p)  partition=$OPTARG
		;;
	esac
done

case $command in
	"IMG_START")
		echo "Received IMG_START command"
		do_img_start
		;;
	"IMG_END")
		echo "Received IMG_END command"
		do_img_end
		;;
	"MLTH_START")
		echo "Received MLTH_START command"
		do_mlth_start
		;;
	"MLTH_END")
		echo "Received MLTH_END command"
		do_mlth_end
		;;
	"CONFIRM_COMPLETE")
		echo "Received CONFIRM_COMPLETE command"
		do_confirm_complete
		;;
	*) echo "Invalid command: $command"
		exit 1
		;;
esac
exit 0
