diff --git a/target/d211/common/Recovery.gz b/target/d211/common/Recovery.gz index 83c49a4f6..e6ce1ff7a 100644 Binary files a/target/d211/common/Recovery.gz and b/target/d211/common/Recovery.gz differ diff --git a/target/d211/demo128_nand/rootfs_overlay/etc/ota_build_id b/target/d211/demo128_nand/rootfs_overlay/etc/ota_build_id index 693850970..0e8962666 100644 --- a/target/d211/demo128_nand/rootfs_overlay/etc/ota_build_id +++ b/target/d211/demo128_nand/rootfs_overlay/etc/ota_build_id @@ -1 +1 @@ -demo128_nand-ota-marker=2026-04-27_v9999 +demo128_nand-ota-marker=2026-04-27_v1001 diff --git a/target/d211/demo128_nand/swupdate/sw-description b/target/d211/demo128_nand/swupdate/sw-description index b99a30777..9af5e1a0a 100644 --- a/target/d211/demo128_nand/swupdate/sw-description +++ b/target/d211/demo128_nand/swupdate/sw-description @@ -66,6 +66,16 @@ software = * Step1: Download and upgrade partitons * Step2: Setup ENV variables, and mark OTA "finish" * Step3: Reboot to main system + * + * data UBIFS (ubi1:data, ubisystem): + * Optional factory/user data image built from + * target/.../data/ overlay (see image_cfg.json "data"). + * Listed last so earlier images are processed first. + * + * IMPORTANT: If the .swu file itself is stored on /data (ubi1:data), + * updating this volume can corrupt the stream while it is being read. + * Prefer placing the package in RAM (/tmp), on the network, or on + * external media for OTA that includes "data". */ upgrade_kernel = { images: ( @@ -86,6 +96,12 @@ software = volume = "rootfs"; installed-directly = true; sha256 = "@rootfs"; + }, + { + filename = "data"; + volume = "data"; + installed-directly = true; + sha256 = "@data"; } ); diff --git a/target/d211/demo128_nand/swupdate/sw-images.cfg b/target/d211/demo128_nand/swupdate/sw-images.cfg index 94c97a479..7ae15d125 100644 --- a/target/d211/demo128_nand/swupdate/sw-images.cfg +++ b/target/d211/demo128_nand/swupdate/sw-images.cfg @@ -4,5 +4,7 @@ ${BINARIES_DIR}/recovery.itb:recovery ${BINARIES_DIR}/kernel.itb:kernel ${BINARIES_DIR}/logo.itb:logo ${BINARIES_DIR}/rootfs_page_2k_block_128k.ubifs:rootfs +${BINARIES_DIR}/data_page_2k_block_128k.ubifs:data #${BINARIES_DIR}/rootfs_page_4k_block_256k.ubifs:rootfs +#${BINARIES_DIR}/data_page_4k_block_256k.ubifs:data ) diff --git a/target/d211/ota/rootfs_overlay/etc/init.d/S91swupdate_ota b/target/d211/ota/rootfs_overlay/etc/init.d/S91swupdate_ota index ffe426a77..c967d9101 100755 --- a/target/d211/ota/rootfs_overlay/etc/init.d/S91swupdate_ota +++ b/target/d211/ota/rootfs_overlay/etc/init.d/S91swupdate_ota @@ -24,6 +24,89 @@ attach_ubi_by_name() ubiattach /dev/ubi_ctrl -m "$mtd_num" 2>&1 } +# If the .swu path in swu_param is under /data, swupdate cannot rewrite the +# ubi "data" volume while UBIFS holds it mounted (kernel: ubi_open_volume -EBUSY). +# Copy the archive to a dedicated tmpfs and umount /data before swupdate_cmd runs. +# Note: the .swu must fit in RAM (tmpfs quota below). +# +# Initramfs root is often ramfs/tmpfs where "remount,size=" does not give swupdate's +# statvfs() enough reported space; it then fails extracting kernel with ~8 MiB free. +# A fresh tmpfs on /mnt/swutmp with an explicit size= fixes that; TMPDIR must point +# there before swupdate runs (see start). +relocate_swu_off_data() +{ + swu_param=$(fw_printenv -n swu_param 2>/dev/null) || return 0 + [ -z "$swu_param" ] && return 0 + + set -- $swu_param + swu_in="" + while [ $# -gt 0 ]; do + if [ "$1" = "-i" ] && [ -n "$2" ]; then + swu_in="$2" + break + fi + shift + done + [ -z "$swu_in" ] && return 0 + [ ! -f "$swu_in" ] && return 0 + case "$swu_in" in + /data/*) ;; + *) return 0 ;; + esac + + SWU_STAGED=/mnt/swutmp/ota.swu + mkdir -p /mnt/swutmp + umount /mnt/swutmp 2>/dev/null + echo "S91: mount dedicated tmpfs on /mnt/swutmp for .swu + swupdate extract" + if ! mount -t tmpfs -o size=110m,nr_inodes=16k tmpfs /mnt/swutmp; then + echo "S91: ERROR: tmpfs mount /mnt/swutmp failed" + return 1 + fi + + echo "S91: OTA package on $swu_in — copy to $SWU_STAGED and umount /data (unlock ubi data volume)" + if ! cp "$swu_in" "$SWU_STAGED"; then + echo "S91: ERROR: copy to $SWU_STAGED failed (increase tmpfs size= ?)" + umount /mnt/swutmp 2>/dev/null + return 1 + fi + sync + if ! umount /data; then + echo "S91: ERROR: umount /data failed" + umount /mnt/swutmp 2>/dev/null + return 1 + fi + + set -- $swu_param + new_param="" + first=1 + while [ $# -gt 0 ]; do + if [ "$1" = "-i" ] && [ -n "$2" ]; then + if [ $first -eq 1 ]; then + new_param="-i $SWU_STAGED" + first=0 + else + new_param="$new_param -i $SWU_STAGED" + fi + shift 2 + continue + fi + if [ $first -eq 1 ]; then + new_param="$1" + first=0 + else + new_param="$new_param $1" + fi + shift + done + # Values starting with "-i" are parsed as fw_setenv options; use import file. + printf 'swu_param=%s\n' "$new_param" > /tmp/swu_param.relocsave + fw_setenv -s /tmp/swu_param.relocsave + sync + echo "S91: swu_param is now $(fw_printenv -n swu_param 2>/dev/null)" + touch /mnt/swutmp/.ota_use_tmpdir + return 0 +} + case "$1" in start) echo "S91: starting OTA second stage" @@ -50,9 +133,20 @@ case "$1" in mountpoint -q /data || echo "S91: WARNING /data not mounted" fi + if ! relocate_swu_off_data; then + echo "S91: ERROR: could not move OTA package off /data; aborting swupdate" + exit 1 + fi + + if [ -f /mnt/swutmp/.ota_use_tmpdir ]; then + export TMPDIR=/mnt/swutmp + echo "S91: TMPDIR=$TMPDIR (swupdate extract uses same tmpfs as .swu)" + fi + exec /usr/lib/swupdate/swupdate_cmd.sh ;; stop) umount /data 2>/dev/null + umount /mnt/swutmp 2>/dev/null ;; esac