From 793e3c02cd7ce6392913031b1f546923d4277f50 Mon Sep 17 00:00:00 2001 From: Piotr Dymacz Date: Sun, 6 Nov 2022 10:22:42 +0100 Subject: [PATCH] base-files: minimal support for QCA runtime failsafe upgrade During research, when working on support for YunCore AX840 board it was found that this vendor decided to use Qualcomm-made, so called 'runtime failsafe' based upgrades from QSDK (see e.g. [1] and [2]). It turned out that vendor firmware makes use of dual-image feature controlled by SBL (not only by U-Boot as usual). This mode allows to switch between two copies of firmware in case of a failed upgrade and works not only with kernel/rootfs but apparently, also with other parts of the firmware, like APPS (U-Boot), Wi-Fi (ath11k) firmware, etc. Here, we will focus only on the firmware (in our case, UBI on NAND). Two, equal in size, UBI partitions are used: the 'rootfs' is always the active one (for booting) and 'rootfs_1' is used for storing new firmware during the regular 'sysupgrade'. Partition names are swapped, probably by the 'SBL' (closed-source, second level bootloader), based on layout in 'MIBIB' and content of 'BOOTCONFIG'/'BOOTCONFIG1' partitions (the second one seems to be only a backup, as mentioned in related kernel driver) and then provided by 'SMEM' to U-Boot and kernel. For example: - when primary 'rootfs' partition is #0: 0x000000000000-0x000003c00000 : "rootfs" (used for booting) 0x000003c00000-0x000007800000 : "rootfs_1" - when primary 'rootfs' partition is #1: 0x000000000000-0x000003c00000 : "rootfs_1" 0x000003c00000-0x000007800000 : "rootfs" (used for booting) The whole solution requires support in several other places, including kernel (detecting crash) and U-Boot (partition swap) but this requires more research and apparently, YunCore doesn't even use all of that in a proper way, resulting only in a rotation of firmware partitions during upgrade (without the part responsible for reverting in case of fail during the upgrade). This patch adds only a small part of the solution, making 'dual-image' feature kind of working, resulting in rotation between 'rootfs' and 'rootfs_1' partitions (areas on NAND) between subsequent updates. This requires also 'CONFIG_BOOTCONFIG_PARTITION' support in the kernel and in simple terms, works as follows: 1. Get name of target 'rootfs' partition for upgrade from: '/proc/boot_info/rootfs/upgradepartition' 2. Perform upgrade and in case of success change 'primaryboot' flag (1 -> 0, 0 -> 1) for 'rootfs' partition in 'BOOTCONFIG' 3. Write back updated 'BOOTCONFIG' content to flash: from '/proc/boot_info/getbinary_bootconfig' to mtd partitions ('0:BOOTCONFIG' and '0:BOOTCONFIG1') This topic definitely requires additional research if a proper solution (kernel crash and/or failed upgrade detection) is needed so more changes might come in future. [1] https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/67225a559ca8e61e87c36a1c9940a085b3950c97 [2] https://git.codelinaro.org/clo/qsdk/oss/system/openwrt_repo/-/blob/NHSS.QSDK.12.1.5.r2/target/linux/ipq60xx/base-files/lib/upgrade/platform.sh#L175 Signed-off-by: Piotr Dymacz --- package/base-files/files/lib/upgrade/nand.sh | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh index ab3db4cdf2..349d287841 100644 --- a/package/base-files/files/lib/upgrade/nand.sh +++ b/package/base-files/files/lib/upgrade/nand.sh @@ -15,6 +15,9 @@ CI_ROOTPART="${CI_ROOTPART:-rootfs}" # ipq807x qsdk kernel misbehaves CI_IPQ807X=0 +# update BOOTCONFIG partitions (rotate rootfs/rootfs_1) +CI_BOOTCFG=0 + ubi_mknod() { local dir="$1" local dev="/dev/$(basename $dir)" @@ -216,9 +219,31 @@ nand_upgrade_prepare_ubi() { return 0 } +nand_qca_update_bootconfig() { + local primary="0" + local mtdnum + local part + + [ -f /proc/boot_info/rootfs/primaryboot ] || return + [ -f /proc/boot_info/getbinary_bootconfig ] || return + + [ "$(cat /proc/boot_info/rootfs/primaryboot)" = "0" ] && primary="1" + echo "$primary" > /proc/boot_info/rootfs/primaryboot 2>/dev/null + + for part in "0:BOOTCONFIG" "0:BOOTCONFIG1"; do + mtdnum="$(find_mtd_index "$part")" + [ -c "/dev/mtd${mtdnum}" ] && { + mtd -qq write /proc/boot_info/getbinary_bootconfig \ + "/dev/mtd${mtdnum}" 2>/dev/null &&\ + echo "partition '$part' updated" + } + done +} + nand_do_upgrade_success() { local conf_tar="/tmp/sysupgrade.tgz" sync + [ "$CI_BOOTCFG" = 1 ] && nand_qca_update_bootconfig [ -f "$conf_tar" ] && nand_restore_config "$conf_tar" echo "sysupgrade successful" umount -a -- 2.38.1