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
+}
