[][OpenWrt Dev][Add support for dual-boot on UBI devices]

[Description]
Add support for dual-boot on UBI devices
Currently supported on mt7986

[Release-log]
N/A

Change-Id: I32e0f744baaacd9bb094c0764ccfdb2048ad798b
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6105794
diff --git a/openwrt_patches-21.02/140-nand-dual-boot-upgrade.patch b/openwrt_patches-21.02/140-nand-dual-boot-upgrade.patch
new file mode 100644
index 0000000..0ecaed8
--- /dev/null
+++ b/openwrt_patches-21.02/140-nand-dual-boot-upgrade.patch
@@ -0,0 +1,163 @@
+diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh
+index e6f58df..ba8c0fc 100644
+--- a/package/base-files/files/lib/upgrade/nand.sh
++++ b/package/base-files/files/lib/upgrade/nand.sh
+@@ -316,3 +316,158 @@ nand_do_platform_check() {
+ 
+ 	return 0
+ }
++
++dual_boot_upgrade_prepare_ubi() {
++	local kernel_vol_name="$1"
++	local rootfs_vol_name="$2"
++	local kernel_length="$3"
++	local rootfs_length="$4"
++	local reserve_rootfs_data="$5"
++
++	local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
++	if [ ! "$mtdnum" ]; then
++		echo "cannot find ubi mtd partition $CI_UBIPART"
++		return 1
++	fi
++
++	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++	if [ ! "$ubidev" ]; then
++		ubiattach -m "$mtdnum"
++		sync
++		ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++	fi
++
++	if [ ! "$ubidev" ]; then
++		ubiformat /dev/mtd$mtdnum -y
++		ubiattach -m "$mtdnum"
++		sync
++		ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++		ubimkvol /dev/$ubidev -n 0 -N u-boot-env -s 512KiB
++	fi
++
++	local rootfs_data_vol_name=$(cat /sys/module/boot_param/parameters/rootfs_data_part 2>/dev/null)
++
++	local kern_ubivol="$( nand_find_volume $ubidev $kernel_vol_name )"
++	local root_ubivol="$( nand_find_volume $ubidev $rootfs_vol_name )"
++	local data_ubivol="$( nand_find_volume $ubidev $rootfs_data_vol_name )"
++
++	# remove ubiblock device of rootfs
++	local root_ubiblk="ubiblock${root_ubivol:3}"
++	if [ "$root_ubivol" -a -e "/dev/$root_ubiblk" ]; then
++		echo "removing $root_ubiblk"
++		if ! ubiblock -r /dev/$root_ubivol; then
++			echo "cannot remove $root_ubiblk"
++			return 1;
++		fi
++	fi
++
++	# kill volumes
++	[ "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N $kernel_vol_name || true
++	[ "$root_ubivol" ] && ubirmvol /dev/$ubidev -N $rootfs_vol_name || true
++
++	# update kernel
++	if ! ubimkvol /dev/$ubidev -N $kernel_vol_name -s $kernel_length; then
++		echo "cannot create kernel volume"
++		return 1;
++	fi
++
++	# update rootfs
++	if ! ubimkvol /dev/$ubidev -N $rootfs_vol_name -s $rootfs_length; then
++		echo "cannot create rootfs volume"
++		return 1;
++	fi
++
++	if [ x"${reserve_rootfs_data}" = xY ]; then
++		# Do not touch rootfs_data
++		sync
++		return 0
++	fi
++
++	# 'format' rootfs_data volume
++	[ "$data_ubivol" ] && {
++		local rootfs_data_length=$(cat /sys/class/ubi/$data_ubivol/data_bytes)
++
++		# kill rootfs_data volume
++		ubirmvol /dev/$ubidev -N $rootfs_data_vol_name || true
++
++		# update rootfs_data
++		if ! ubimkvol /dev/$ubidev -N $rootfs_data_vol_name -s $rootfs_data_length; then
++			echo "cannot create $rootfs_data_vol_name volume"
++		fi
++	}
++
++	sync
++	return 0
++}
++
++ubi_dual_boot_upgrade_tar() {
++	local tar_file="$1"
++	local board_dir=$(tar tf ${tar_file} | grep -m 1 '^sysupgrade-.*/$')
++	local reserve_rootfs_data=$(cat /sys/module/boot_param/parameters/reserve_rootfs_data 2>/dev/null)
++	board_dir=${board_dir%/}
++
++	kernel_vol_name=$(cat /sys/module/boot_param/parameters/upgrade_kernel_part 2>/dev/null)
++	[ -z "${kernel_vol_name}" -o $? -ne 0 ] && return 1
++
++	rootfs_vol_name=$(cat /sys/module/boot_param/parameters/upgrade_rootfs_part 2>/dev/null)
++	[ -z "${rootfs_vol_name}" -o $? -ne 0 ] && return 1
++
++	local kernel_length=$( (tar xf ${tar_file} ${board_dir}/kernel -O | wc -c) 2> /dev/null)
++	local rootfs_length=$( (tar xf ${tar_file} ${board_dir}/root -O | wc -c) 2> /dev/null)
++
++	dual_boot_upgrade_prepare_ubi "${kernel_vol_name}" "${rootfs_vol_name}" \
++				      "${kernel_length}" "${rootfs_length}" \
++				      "${reserve_rootfs_data}"
++
++	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++
++	[ "${kernel_length}" != 0 ] && {
++		local kern_ubivol="$(nand_find_volume $ubidev ${kernel_vol_name})"
++		tar xf ${tar_file} ${board_dir}/kernel -O | \
++			ubiupdatevol /dev/${kern_ubivol} -s ${kernel_length} -
++	}
++
++	[ "${rootfs_length}" != 0 ] && {
++		local root_ubivol="$(nand_find_volume $ubidev ${rootfs_vol_name})"
++		tar xf ${tar_file} ${board_dir}/root -O | \
++			ubiupdatevol /dev/${root_ubivol} -s ${rootfs_length} -
++	}
++
++	upgrade_image_slot=$(cat /sys/module/boot_param/parameters/upgrade_image_slot 2>/dev/null)
++	[ -n "${upgrade_image_slot}" ] && {
++		v "Set new boot image slot to ${upgrade_image_slot}"
++		# Force the creation of fw_printenv.lock
++		mkdir -p /var/lock
++		touch /var/lock/fw_printenv.lock
++		fw_setenv "dual_boot.current_slot" "${upgrade_image_slot}"
++		fw_setenv "dual_boot.slot_${upgrade_image_slot}_invalid" "0"
++	}
++
++	if [ x"${reserve_rootfs_data}" != xY ]; then
++		# do normal upgrade flow
++		nand_do_upgrade_success
++	fi
++
++	# Do not touch rootfs_data
++	sync
++
++	echo "sysupgrade successful"
++	umount -a
++	reboot -f
++}
++
++ubi_do_upgrade() {
++	local dual_boot=$(cat /sys/module/boot_param/parameters/dual_boot 2>/dev/null)
++	local file_type=$(identify $1)
++
++	if [ x"${dual_boot}" != xY ]; then
++		nand_do_upgrade "$1"
++		return
++	fi
++
++	case "$file_type" in
++		"ubi")		v "Unsupported firmware type: ubinized";;
++		"ubifs")	v "Unsupported firmware type: ubifs";;
++		*)		ubi_dual_boot_upgrade_tar $1;;
++	esac
++}
diff --git a/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh
index 0c66f47..bd38b22 100644
--- a/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh
+++ b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh
@@ -6,7 +6,7 @@
 
 	case "$board" in
 	*snand*)
-		nand_do_upgrade "$1"
+		ubi_do_upgrade "$1"
 		;;
 	*emmc*)
 		mtk_mmc_do_upgrade "$1"