From a424c62f6a116fef4ce4c38f93a32adf8ab93551 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Sun, 4 Oct 2026 16:31:53 +0300 Subject: [PATCH 1/3] nand: kernel inside the UBIFS rootfs, volumes sized by their images NAND images no longer set aside flash for a kernel. The UBI device now holds two volumes: - rootfs: UBIFS, with the kernel in it as /boot/fitImage (zImage + DTB, each hashed); - rootfs_data: the overlay. external.mk adds the kernel to the UBIFS image's own copy of the target tree, so the NOR squashfs is unchanged. It is one file, never two: the FIT where the board builds one, otherwise the uImage. u-boot-xmedia boots either. ubinize-nand.cfg gives rootfs no vol_size, so the volume is exactly as big as its image, and rootfs_data takes the rest. The hi3516ev200 family (hi3516ev200, hi3516ev300, hi3518ev300) moves to this layout from the retired split one (a uImage in a raw `kernel` partition, UBIFS root beside it). Its nand-fit.its names the DTB as @DTB@, since each model builds its own -demb.dtb; rootfs_script.sh stamps it alongside @SOC@. hi3516av100, av200, dv100, cv300 and hi3518ev200 stop building NAND images: none has a bootloader that can install or boot this layout. Their NOR images are unchanged. The NAND package carries rootfs.ubifs, rootfs.ubi for a fresh install, and fitImage as the SoC witness. The only size gate is rootfs.ubi at 24M, the RAM a fresh install stages it in; no volume bounds either image. sysupgrade: - ubifs layout (rootfs volume, no kernel volume, no kernel partition): an upgrade writes the one image and resizes the volumes to it. As PID 1 in the RAM root it copies the settings out of rootfs_data, removes that volume, resizes rootfs to the image, writes it, recreates rootfs_data on what is left and puts the settings back. -k means writing the rootfs, since the kernel is in it. -r -n skips the copy. The room checked is both volumes less the settings, not the old rootfs volume. - The split layout is refused before anything is touched, kernel-only runs included. Without that refusal the MTD path flashcp'd a NOR squashfs through gluebi over the mounted UBIFS volume. - The earlier layout with a separate kernel volume is refused the same way. Both refusals point at the reinstall instructions on openipc.org. - ubiblock cameras are unchanged. The offline harness covers all of it: 253 checks pass. Verified on a hi3516ev300 (W25N01GV NAND, 5 factory bad blocks): - Installed from u-boot-xmedia with the openipc.org commands: tftpboot rootfs.ubi, nand erase 0x100000 0x7f00000, nand write.trimffs. - U-Boot loads /boot/fitImage from UBIFS, the hashes pass, and the kernel mounts root=ubi0:rootfs read-only, the UBIFS overlay read-write. - sysupgrade -r from a local package, with a marker in the overlay: - the first run rewrote rootfs at the same 155 LEBs; the second, with an image 2 MB smaller, shrank it from 155 to 138 LEBs. Growing a volume is covered by the harness only; - rootfs_data was recreated each time on the rest (837 and 854 LEBs); - the marker and the SSH key survived both runs; - the camera rebooted each time and came back booting the FIT. - The same camera, still on the split layout, refused an upgrade: "retired split NAND layout ... nothing was written", exit 1, /proc/mtd unchanged. --- .github/scripts/test_sysupgrade.sh | 177 ++++++++++++++--- Makefile | 17 +- .../board/gk7205v500/nand-fit.its | 5 +- .../board/gk7205v500/ubinize-nand.cfg | 25 +-- .../board/hi3516ev200/nand-fit.its | 55 ++++++ .../board/hi3516ev200/ubinize-nand.cfg | 23 +++ .../configs/hi3516av100_ultimate_defconfig | 5 - .../configs/hi3516av200_ultimate_defconfig | 5 - .../configs/hi3516cv300_ultimate_defconfig | 5 - .../configs/hi3516dv100_ultimate_defconfig | 5 - .../configs/hi3516ev200_ultimate_defconfig | 2 +- .../configs/hi3516ev300_ultimate_defconfig | 2 +- .../configs/hi3518ev200_ultimate_defconfig | 5 - .../configs/hi3518ev300_ultimate_defconfig | 2 +- general/external.mk | 26 +++ general/overlay/usr/sbin/sysupgrade | 178 +++++++++++++++--- general/scripts/rootfs_script.sh | 11 +- 17 files changed, 448 insertions(+), 100 deletions(-) create mode 100644 br-ext-chip-hisilicon/board/hi3516ev200/nand-fit.its create mode 100644 br-ext-chip-hisilicon/board/hi3516ev200/ubinize-nand.cfg diff --git a/.github/scripts/test_sysupgrade.sh b/.github/scripts/test_sysupgrade.sh index e9ad6e9548..193caba0bc 100755 --- a/.github/scripts/test_sysupgrade.sh +++ b/.github/scripts/test_sysupgrade.sh @@ -283,7 +283,8 @@ case "$applet" in # like flashcp, so a test can assert what reached which volume, in order. ubiupdatevol) echo "ubiupdatevol $*" >> "$FLASH_LOG" [ "1" = "$STUB_FLASHCP_FAIL" ] && exit 1 ;; - kill) echo "$applet $*" >> "$FLASH_LOG" ;; + kill|ubirmvol|ubirsvol|ubimkvol) + echo "$applet $*" >> "$FLASH_LOG" ;; umount) echo "$applet $*" >> "$FLASH_LOG" exit "${STUB_BB_UMOUNT_RC:-0}" ;; esac @@ -310,6 +311,20 @@ for a in "$@"; do move|tmpfs|--move|-M) exit 0 ;; esac done +# rewrite_ubifs mounts rootfs_data read-only to copy the settings out, then +# the new, empty volume read-write to put them back. The copy finds a marker; +# the new volume starts empty, so the marker is there afterwards only if the +# settings really went round through the backup. +case " $* " in + *" -t ubifs "*) + case " $* " in + *" ro "*) echo "mount ubifs ro $target" >> "$FLASH_LOG" + mkdir -p "$target/etc"; echo settings > "$target/etc/marker" ;; + *) echo "mount ubifs rw $target" >> "$FLASH_LOG" + rm -rf "$target"; mkdir -p "$target" ;; + esac + exit 0 ;; +esac case "${STUB_MOUNT:-ok}" in ok) mkdir -p "$target/etc" @@ -373,15 +388,18 @@ make_fit_soc() { # volumes, as /sys/class/ubi shows them: 126976-byte LEBs (2 KiB pages, # 128 KiB blocks). The rootfs volume is 2 LEBs by default -- room for the # fixtures below and not much more, so a size test has an edge to cross. +# $2 is the volume list, $3 the overlay's LEBs (the ubifs layout's rootfs can +# grow into them, so its size tests need them to be real). set_ubi() { local u="$SB/sys/class/ubi" i name ebs rm -rf "$u" i=0 - for name in kernel rootfs rootfs_data; do + for name in ${2:-kernel rootfs rootfs_data}; do mkdir -p "$u/ubi0_$i" echo "$name" > "$u/ubi0_$i/name" echo 126976 > "$u/ubi0_$i/usable_eb_size" ebs=2; [ "$name" = rootfs ] && ebs=${1:-2} + [ "$name" = rootfs_data ] && ebs=${3:-2} echo "$ebs" > "$u/ubi0_$i/reserved_ebs" i=$((i + 1)) done @@ -2058,14 +2076,20 @@ US="$SB/tmp/rootfs.squashfs.gk7205v500" # ubi_setup [rootfs_reserved_ebs]: a gk7205v500 NAND camera # whose MTD table has no kernel/rootfs partitions -- they are UBI volumes. ubi_setup() { - set_ubi "${2:-2}" + case "$1" in + ubifs) set_ubi "${2:-2}" "rootfs rootfs_data" 600 ;; + *) set_ubi "${2:-2}" ;; + esac set_mtd <<'EOF2' dev: size erasesize name mtd0: 000c0000 00020000 "boot" mtd1: 00040000 00020000 "env" mtd2: 07f00000 00020000 "ubi" EOF2 - if [ "$1" = ubifs ]; then set_cmdline "$CMDLINE_UBIFS"; else set_cmdline "$CMDLINE_UBIBLOCK"; fi + case "$1" in + ubifs|ubifs-kvol) set_cmdline "$CMDLINE_UBIFS" ;; + *) set_cmdline "$CMDLINE_UBIBLOCK" ;; + esac set_platform gk7205v500_ultimate ultimate export STUB_VENDOR=goke STUB_SOC=gk7205v500 STUB_IMG_SOC=gk7205v500 make_fit "$UFIT" @@ -2104,13 +2128,26 @@ else bad "stage 2 ubiblock order ${u:-none}/${k:-none}/${r:-none} log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi -# --- ubifs: a kernel-only write touches no mounted volume +# --- ubifs: the kernel is a file in the rootfs, so -k writes the rootfs reset_env; ubi_setup ubifs +make_fit_soc "$UFIT" gk7205v500 +STUB_PIVOT_RC=0 run -z --kernel="$UFIT" -if [ "$RC" -eq 0 ] && ubi_wrote "/dev/ubi0_0 $UFIT" && ! handed_off; then - ok "ubifs: a FIT kernel is written into the kernel volume in place" +if printf '%s' "$OUT" | grep -q "kernel lives inside the rootfs" && handed_off && nothing_ubi; then + ok "ubifs: -k means writing the rootfs, through the hand-off" +else + bad "ubifs: -k should become a rootfs write, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi + +# --- the retired layout with a kernel volume of its own is reinstalled, not upgraded +reset_env; ubi_setup ubifs-kvol +STUB_PIVOT_RC=0 +run -z --kernel="$UFIT" --rootfs="$UFS" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout with a separate kernel volume" && + nothing_ubi && ! handed_off && ! rebooted; then + ok "ubifs-kvol: refused before anything is touched, reinstall link given" else - bad "ubifs: kernel-only should write ubi0_0 without a hand-off, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "ubifs-kvol must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi # --- ubifs: a local UBIFS rootfs has no SoC witness @@ -2167,8 +2204,9 @@ else bad "ubifs: LEB mismatch must be refused, rc=$RC out='$OUT'" fi -# --- bigger than the volume +# --- bigger than the UBI device leaves room for, beside the settings reset_env; ubi_setup ubifs 1 +echo 24 > "$SB/sys/class/ubi/ubi0_1/reserved_ebs" dd if=/dev/zero bs=1k count=200 >> "$UFS" 2>/dev/null run -z -f --kernel="$UFIT" --rootfs="$UFS" if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "does not fit" && nothing_ubi; then @@ -2185,30 +2223,48 @@ envf="$SB/ram/sysupgrade.env" if handed_off && nothing_ubi && [ -x "$SB/ram/sbin/init" ] && [ ! -e "$SB/ram/bin/umount" ] && [ -L "$SB/ram/sbin/umount" ] && grep -q "_handoff=1" "$envf" 2>/dev/null && grep -q "_ramfs_phase=.1" "$envf" && - grep -q "ubi_rootfs_dev=./dev/ubi0_1" "$envf" && grep -q "sysupgrade.env" "$SB/ram/sbin/init"; then + grep -q "ubi_rootfs_dev=./dev/ubi0_0" "$envf" && grep -q "sysupgrade.env" "$SB/ram/sbin/init"; then ok "ubifs: the RAM root is staged for PID 1 and SIGQUIT sent before any write" else bad "ubifs: hand-off staging, log='$(cat "$SB/tmp/flash.log")' init='$(cat "$SB/ram/sbin/init" 2>&1)' out='$OUT'" fi -# --- stage 2, as PID 1: release the old root, then write, then reboot +# --- stage 2, as PID 1: release the old root, copy the settings out, rebuild +# the volumes around the new image, put the settings back, reboot +S2="_ramfs_phase=1 _handoff=1 ubi_layout=ubifs kernel_device=/rom/boot/uImage ubi_rootfs_dev=/dev/ubi0_0 ubi_data_dev=/dev/ubi0_1 model=gk7205v500 skip_soc=1 skip_ver=1 root_on_flash=1 ram_root_shipped=1 overlay_kb=64" +reset_env; ubi_setup ubifs +RUN_ENV="$S2 update_rootfs=1 rootfs_file=$UFS" +run +u=$(logged_at "umount -l /mnt"); bk=$(logged_at "mount ubifs ro"); rm=$(logged_at "ubirmvol /dev/ubi0 -N rootfs_data") +rs=$(logged_at "ubirsvol /dev/ubi0 -n 0 -s $(stat -c %s "$UFS")"); w=$(logged_at "ubiupdatevol /dev/ubi0_0 $UFS") +mk=$(logged_at "ubimkvol /dev/ubi0 -N rootfs_data -m"); rs2=$(logged_at "mount ubifs rw"); b=$(logged_at "^reboot") +if [ -n "$u" ] && [ -n "$bk" ] && [ -n "$rm" ] && [ -n "$rs" ] && [ -n "$w" ] && [ -n "$mk" ] && [ -n "$rs2" ] && [ -n "$b" ] && + [ "$u" -lt "$bk" ] && [ "$bk" -lt "$rm" ] && [ "$rm" -lt "$rs" ] && [ "$rs" -lt "$w" ] && [ "$w" -lt "$mk" ] && + [ "$mk" -lt "$rs2" ] && [ "$rs2" -lt "$b" ] && [ -f "$SB/tmp/overlay.old/etc/marker" ] && + printf '%s' "$OUT" | grep -q "Settings carried over"; then + ok "stage 2: settings copied, volumes resized to the image, settings restored, then reboot" +else + bad "stage 2 order release/copy/rmvol/rsvol/write/mkvol/restore/reboot = ${u:-none}/${bk:-none}/${rm:-none}/${rs:-none}/${w:-none}/${mk:-none}/${rs2:-none}/${b:-none} log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi + +# --- -r with -n: the new overlay is the wipe; nothing is copied or restored reset_env; ubi_setup ubifs -RUN_ENV="_ramfs_phase=1 _handoff=1 ubi_layout=ubifs kernel_device=/dev/ubi0_0 ubi_rootfs_dev=/dev/ubi0_1 ubi_data_dev=/dev/ubi0_2 update_kernel=1 update_rootfs=1 kernel_file=$UFIT rootfs_file=$UFS model=gk7205v500 skip_soc=1 skip_ver=1 root_on_flash=1 ram_root_shipped=1" +RUN_ENV="$S2 update_rootfs=1 rootfs_file=$UFS clear_overlay=1" run -u=$(logged_at "umount -l /mnt"); k=$(logged_at "ubiupdatevol /dev/ubi0_0"); r=$(logged_at "ubiupdatevol /dev/ubi0_1"); b=$(logged_at "^reboot") -if [ -n "$u" ] && [ -n "$k" ] && [ -n "$r" ] && [ -n "$b" ] && [ "$u" -lt "$k" ] && [ "$k" -lt "$r" ] && [ "$r" -lt "$b" ]; then - ok "stage 2: old root released, kernel then rootfs written, then reboot" +if ubi_wrote "/dev/ubi0_0 $UFS" && grep -q "ubimkvol /dev/ubi0 -N rootfs_data -m" "$SB/tmp/flash.log" && + ! grep -q "mount ubifs" "$SB/tmp/flash.log" && ! grep -q "ubiupdatevol -t" "$SB/tmp/flash.log" && rebooted; then + ok "stage 2: -r -n rebuilds an empty settings volume and copies nothing" else - bad "stage 2 order umount/kernel/rootfs/reboot = ${u:-none}/${k:-none}/${r:-none}/${b:-none} log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "stage 2 -r -n, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi reset_env; ubi_setup ubifs -RUN_ENV="_ramfs_phase=1 _handoff=1 ubi_layout=ubifs kernel_device=/dev/ubi0_0 ubi_rootfs_dev=/dev/ubi0_1 ubi_data_dev=/dev/ubi0_2 clear_overlay=1 model=gk7205v500 root_on_flash=1 ram_root_shipped=1" +RUN_ENV="$S2 clear_overlay=1" run -if grep -q "ubiupdatevol -t /dev/ubi0_2" "$SB/tmp/flash.log" && rebooted; then - ok "stage 2: the UBIFS overlay volume is truncated" +if grep -q "ubiupdatevol -t /dev/ubi0_1" "$SB/tmp/flash.log" && ! grep -q "ubirmvol" "$SB/tmp/flash.log" && rebooted; then + ok "stage 2: -n alone truncates the UBIFS overlay volume" else - bad "stage 2 wipe: expected ubiupdatevol -t /dev/ubi0_2, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "stage 2 wipe: expected ubiupdatevol -t /dev/ubi0_1, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi # --- -n on a mounted UBIFS overlay needs the hand-off too @@ -2255,9 +2311,10 @@ fi # --- stage 2 writes nothing when the old root will not let go reset_env; ubi_setup ubifs -RUN_ENV="STUB_BB_UMOUNT_RC=1 _ramfs_phase=1 _handoff=1 ubi_layout=ubifs kernel_device=/dev/ubi0_0 ubi_rootfs_dev=/dev/ubi0_1 ubi_data_dev=/dev/ubi0_2 update_kernel=1 update_rootfs=1 kernel_file=$UFIT rootfs_file=$UFS model=gk7205v500 skip_soc=1 skip_ver=1 root_on_flash=1 ram_root_shipped=1" +RUN_ENV="STUB_BB_UMOUNT_RC=1 $S2 update_rootfs=1 rootfs_file=$UFS" run -if printf '%s' "$OUT" | grep -q "Could not let go of the old root" && nothing_ubi && rebooted; then +if printf '%s' "$OUT" | grep -q "Could not let go of the old root" && nothing_ubi && + ! grep -q "ubirmvol" "$SB/tmp/flash.log" && rebooted; then ok "stage 2: a failed release of the old root reboots with nothing written" else bad "stage 2: umount failure must stop before the first write, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" @@ -2291,6 +2348,82 @@ else bad "ubiblock old inittab, no gluebi, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi +# --- the retired HiSilicon split NAND layout: uImage in a raw `kernel` +# partition, UBIFS root in a UBI device beside it, no `kernel` volume. gluebi +# names the UBIFS volume "rootfs" in /proc/mtd, which is what the MTD path +# would have flashed a NOR squashfs over. +split_setup() { + local u="$SB/sys/class/ubi" i=0 name + rm -rf "$u" + for name in rootfs rootfs_data; do + mkdir -p "$u/ubi0_$i" + echo "$name" > "$u/ubi0_$i/name" + echo 126976 > "$u/ubi0_$i/usable_eb_size" + echo 260 > "$u/ubi0_$i/reserved_ebs" + i=$((i + 1)) + done + set_mtd <<'EOF2' +dev: size erasesize name +mtd0: 00100000 00020000 "boot" +mtd1: 00100000 00020000 "env" +mtd2: 00800000 00020000 "kernel" +mtd3: 07600000 00020000 "ubi" +mtd4: 02017000 0001f000 "rootfs" +mtd5: 04f51000 0001f000 "rootfs_data" +EOF2 + set_cmdline 'mem=128M console=ttyAMA0,115200 panic=20 rootfstype=ubifs root=ubi0:rootfs ubi.mtd=3,2048 mtdparts=hinand:1024k(boot),1024k(env),8192k(kernel),-(ubi)' + set_platform hi3516ev300_ultimate ultimate + export STUB_VENDOR=hisilicon STUB_SOC=hi3516ev300 STUB_IMG_SOC=hi3516ev300 + SK="$SB/tmp/uImage.hi3516ev300"; SS="$SB/tmp/rootfs.squashfs.hi3516ev300" + make_uimage "$SK" hi3516ev300 + make_squashfs "$SS" 8192 +} +reset_env; split_setup +STUB_PIVOT_RC=0 +run -z --kernel="$SK" --rootfs="$SS" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired split NAND layout" && + printf '%s' "$OUT" | grep -q "openipc.org/cameras/vendors/hisilicon/socs/hi3516ev300" && + ! grep -qE "flashcp|ubiupdatevol|flash_eraseall|pivot_root" "$SB/tmp/flash.log" && ! rebooted; then + ok "split NAND: kernel+rootfs refused before anything is touched, reinstall link given" +else + bad "split NAND -k -r, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; split_setup +run -z --kernel="$SK" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired split NAND layout" && nothing_wrote && ! rebooted; then + ok "split NAND: a kernel-only run is refused too" +else + bad "split NAND -k, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; split_setup +run -z -n +if ! printf '%s' "$OUT" | grep -q "retired split NAND layout"; then + ok "split NAND: -n alone is not refused" +else + bad "split NAND -n should not be refused, rc=$RC out='$OUT'" +fi + +# --- an hi3516ev300 on the UBI-only layout is an ordinary ubifs camera +ubi_setup_hisi() { + ubi_setup ubifs + set_cmdline 'mem=32M console=ttyAMA0,115200 panic=20 init=/init root=ubi0:rootfs rootfstype=ubifs ubi.mtd=2,2048 mtdparts=hinand:768k(boot),256k(env),-(ubi)' + set_platform hi3516ev300_ultimate ultimate + export STUB_VENDOR=hisilicon STUB_SOC=hi3516ev300 STUB_IMG_SOC=hi3516ev300 +} +reset_env; ubi_setup_hisi +HFIT="$SB/tmp/fitImage.hi3516ev300"; make_fit_soc "$HFIT" hi3516ev300 +HFS="$SB/tmp/rootfs.ubifs.hi3516ev300"; make_ubifs "$HFS" +STUB_PIVOT_RC=0 +run -z --kernel="$HFIT" --rootfs="$HFS" +if printf '%s' "$OUT" | grep -q "SoC from the FIT kernel beside it: hi3516ev300" && handed_off && nothing_ubi && + ! printf '%s' "$OUT" | grep -qE "retired (split )?NAND layout"; then + ok "hi3516ev300 UBI-only: an ordinary ubifs camera, rootfs write handed off" +else + bad "hi3516ev300 UBI-only, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +default_url_is "https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516ev300-nand-ultimate.tgz" \ + ubi_setup_hisi + # --- the download: -nand- for ubifs, -nor- for ubiblock F=https://github.com/OpenIPC/firmware/releases/download/latest default_url_is "$F/openipc.gk7205v500-nand-ultimate.tgz" ubi_setup ubifs diff --git a/Makefile b/Makefile index 981075b125..09fea5308e 100644 --- a/Makefile +++ b/Makefile @@ -194,13 +194,16 @@ ifeq ($(BR2_TARGET_ROOTFS_UBI),y) ifneq ($(filter $(BR2_OPENIPC_SOC_VENDOR),"rockchip" "sigmastar"),) @$(call PREPARE_REPACK,,,rootfs.ubi,16384,nand) else ifneq ($(wildcard $(PWD)/br-ext-chip-$(subst ",,$(BR2_OPENIPC_SOC_VENDOR))/board/$(subst ",,$(BR2_OPENIPC_SOC_FAMILY))/nand-fit.its),) -# FIT NAND (board//nand-fit.its): the kernel lives in the `kernel` UBI -# volume, so the package carries what sysupgrade writes into each volume -- -# fitImage and rootfs.ubifs -- and rootfs.ubi for a fresh install. Measured -# against the volume sizes in the board's ubinize-nand.cfg. - @$(call CHECK_SIZE,fitImage,4096) - @$(call CHECK_SIZE,rootfs.ubifs,32768) - @$(call CHECK_SIZE,rootfs.ubi,16384) +# FIT NAND (board//nand-fit.its): the kernel lives inside the UBIFS +# rootfs (/boot, see external.mk), so the package carries what sysupgrade +# writes -- rootfs.ubifs -- plus rootfs.ubi for a fresh install, and fitImage +# as the SoC witness sysupgrade reads beside a UBIFS rootfs. No volume bounds +# either image: sysupgrade sizes the volumes to them. rootfs.ubi is the whole +# UBI image a fresh install loads into RAM at 0x42000000 and writes from there, +# so it is held to the 24M the installer stages (openipc.org's 0x1800000), +# which still clears the relocated U-Boot at the top of a 64M part +# (hi3516ev200). + @$(call CHECK_SIZE,rootfs.ubi,24576) @$(call REPACK_NAND_FIT) else @$(call PREPARE_REPACK,uImage,4096,rootfs.ubi,16384,nand) diff --git a/br-ext-chip-goke/board/gk7205v500/nand-fit.its b/br-ext-chip-goke/board/gk7205v500/nand-fit.its index d1410dca34..5ff8bee0e0 100644 --- a/br-ext-chip-goke/board/gk7205v500/nand-fit.its +++ b/br-ext-chip-goke/board/gk7205v500/nand-fit.its @@ -1,10 +1,11 @@ /dts-v1/; /* - * Kernel volume of the gk7205v500-family NAND image: the zImage and its DTB, + * Kernel of the gk7205v500-family NAND image: the zImage and its DTB, * each hashed so U-Boot refuses a kernel that NAND has corrupted instead of * booting it. Built by general/scripts/rootfs_script.sh (the "NAND FIT" step) - * from the kernel tree, before ubinize packs it into the `kernel` volume. + * from the kernel tree; external.mk puts it in the UBIFS rootfs as + * /boot/fitImage. * * No fdt load address: u-boot-xmedia relocates the DTB under bootm_size, * inside the kernel's lowmem. diff --git a/br-ext-chip-goke/board/gk7205v500/ubinize-nand.cfg b/br-ext-chip-goke/board/gk7205v500/ubinize-nand.cfg index c98bb4289c..49680d9780 100644 --- a/br-ext-chip-goke/board/gk7205v500/ubinize-nand.cfg +++ b/br-ext-chip-goke/board/gk7205v500/ubinize-nand.cfg @@ -1,28 +1,21 @@ -# gk7205v500-family NAND: kernel (FIT) + rootfs (UBIFS) + rootfs_data. -# Boots with u-boot-xmedia's FIT NAND env: `ubi read ${baseaddr} kernel; bootm` -# and root=ubi0:rootfs. sysupgrade rewrites `kernel` and `rootfs` in place -# with ubiupdatevol, so their sizes are the ceiling for future images. -[kernel] -mode=ubi -vol_id=0 -vol_type=dynamic -vol_name=kernel -vol_alignment=1 -vol_size=4MiB -image=BINARIES_DIR/fitImage - +# gk7205v500-family NAND: rootfs (UBIFS, the kernel inside as +# /boot/fitImage) + rootfs_data. u-boot-xmedia boots it with +# `ubifsmount ubi0:rootfs; ubifsload /boot/fitImage; bootm` and root=ubi0:rootfs. +# +# No vol_size: the rootfs volume is exactly as big as its image, and the +# overlay takes everything else. sysupgrade resizes both when it writes a new +# rootfs, so neither size is a limit a future image has to fit. [rootfs] mode=ubi -vol_id=1 +vol_id=0 vol_type=dynamic vol_name=rootfs vol_alignment=1 -vol_size=32MiB image=BR2_ROOTFS_UBIFS_PATH [rootfs_data] mode=ubi -vol_id=2 +vol_id=1 vol_type=dynamic vol_name=rootfs_data vol_alignment=1 diff --git a/br-ext-chip-hisilicon/board/hi3516ev200/nand-fit.its b/br-ext-chip-hisilicon/board/hi3516ev200/nand-fit.its new file mode 100644 index 0000000000..bdd614fa5d --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3516ev200/nand-fit.its @@ -0,0 +1,55 @@ +/dts-v1/; + +/* + * Kernel of the hi3516ev200-family NAND image (hi3516ev200, hi3516ev300, + * hi3518ev300): the zImage and its DTB, each hashed so U-Boot refuses a kernel + * that NAND has corrupted instead of booting it. Built by + * general/scripts/rootfs_script.sh (the "NAND FIT" step) from the kernel + * tree; external.mk puts it in the UBIFS rootfs as /boot/fitImage. + * + * Each model's kernel builds only its own -demb.dtb, so the DTB name is + * a placeholder filled in per build, like the SoC name. + * + * Load and entry are CONFIG_HI_ZRELADDR. No fdt load address: u-boot-xmedia + * relocates the DTB under bootm_size, inside the kernel's lowmem. + */ +/ { + /* "OpenIPC ": sysupgrade's SoC check reads it (fit_soc). @SOC@ is + filled in from OPENIPC_SOC_MODEL when the FIT is built. */ + description = "OpenIPC @SOC@"; + #address-cells = <1>; + + images { + kernel { + description = "Linux"; + data = /incbin/("zImage"); + type = "kernel"; + arch = "arm"; + os = "linux"; + compression = "none"; + load = <0x40008000>; + entry = <0x40008000>; + hash-1 { algo = "crc32"; }; + hash-2 { algo = "sha1"; }; + }; + + fdt { + description = "@DTB@"; + data = /incbin/("@DTB@"); + type = "flat_dt"; + arch = "arm"; + compression = "none"; + hash-1 { algo = "crc32"; }; + hash-2 { algo = "sha1"; }; + }; + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "Linux + @DTB@"; + kernel = "kernel"; + fdt = "fdt"; + }; + }; +}; diff --git a/br-ext-chip-hisilicon/board/hi3516ev200/ubinize-nand.cfg b/br-ext-chip-hisilicon/board/hi3516ev200/ubinize-nand.cfg new file mode 100644 index 0000000000..0bd59c190a --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3516ev200/ubinize-nand.cfg @@ -0,0 +1,23 @@ +# hi3516ev200-family NAND (hi3516ev200, hi3516ev300, hi3518ev300): rootfs (UBIFS, the kernel inside as +# /boot/fitImage) + rootfs_data. u-boot-xmedia boots it with +# `ubifsmount ubi0:rootfs; ubifsload /boot/fitImage; bootm` and root=ubi0:rootfs. +# +# No vol_size: the rootfs volume is exactly as big as its image, and the +# overlay takes everything else. sysupgrade resizes both when it writes a new +# rootfs, so neither size is a limit a future image has to fit. +[rootfs] +mode=ubi +vol_id=0 +vol_type=dynamic +vol_name=rootfs +vol_alignment=1 +image=BR2_ROOTFS_UBIFS_PATH + +[rootfs_data] +mode=ubi +vol_id=1 +vol_type=dynamic +vol_name=rootfs_data +vol_alignment=1 +vol_size=2MiB +vol_flags=autoresize diff --git a/br-ext-chip-hisilicon/configs/hi3516av100_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516av100_ultimate_defconfig index e24c0aaabb..6c5ae6d48b 100644 --- a/br-ext-chip-hisilicon/configs/hi3516av100_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516av100_ultimate_defconfig @@ -39,11 +39,6 @@ BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_SQUASHFS=y BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y -BR2_TARGET_ROOTFS_UBI=y -BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 -BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" -BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC BR2_OPENIPC_SOC_VENDOR="hisilicon" diff --git a/br-ext-chip-hisilicon/configs/hi3516av200_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516av200_ultimate_defconfig index 94e6f23cfb..7c9663f3de 100644 --- a/br-ext-chip-hisilicon/configs/hi3516av200_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516av200_ultimate_defconfig @@ -39,11 +39,6 @@ BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_SQUASHFS=y BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y -BR2_TARGET_ROOTFS_UBI=y -BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 -BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" -BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC BR2_OPENIPC_SOC_VENDOR="hisilicon" diff --git a/br-ext-chip-hisilicon/configs/hi3516cv300_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516cv300_ultimate_defconfig index 6599565f01..4e4dabd532 100644 --- a/br-ext-chip-hisilicon/configs/hi3516cv300_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516cv300_ultimate_defconfig @@ -36,11 +36,6 @@ BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_SQUASHFS=y BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y -BR2_TARGET_ROOTFS_UBI=y -BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 -BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" -BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC BR2_OPENIPC_SOC_VENDOR="hisilicon" diff --git a/br-ext-chip-hisilicon/configs/hi3516dv100_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516dv100_ultimate_defconfig index 5633363441..76427949d6 100644 --- a/br-ext-chip-hisilicon/configs/hi3516dv100_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516dv100_ultimate_defconfig @@ -39,11 +39,6 @@ BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_SQUASHFS=y BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y -BR2_TARGET_ROOTFS_UBI=y -BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 -BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" -BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC BR2_OPENIPC_SOC_VENDOR="hisilicon" diff --git a/br-ext-chip-hisilicon/configs/hi3516ev200_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516ev200_ultimate_defconfig index c661d3cbdd..5f5cdcb1a6 100644 --- a/br-ext-chip-hisilicon/configs/hi3516ev200_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516ev200_ultimate_defconfig @@ -42,7 +42,7 @@ BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y BR2_TARGET_ROOTFS_UBI=y BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" +BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(EXTERNAL_VENDOR)/board/$(OPENIPC_SOC_FAMILY)/ubinize-nand.cfg" BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC diff --git a/br-ext-chip-hisilicon/configs/hi3516ev300_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516ev300_ultimate_defconfig index 22e5351d88..ce9e936b0f 100644 --- a/br-ext-chip-hisilicon/configs/hi3516ev300_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516ev300_ultimate_defconfig @@ -43,7 +43,7 @@ BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y BR2_TARGET_ROOTFS_UBI=y BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" +BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(EXTERNAL_VENDOR)/board/$(OPENIPC_SOC_FAMILY)/ubinize-nand.cfg" BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC diff --git a/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_defconfig index cbae5465ca..21150accfa 100644 --- a/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_defconfig @@ -36,11 +36,6 @@ BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_SQUASHFS=y BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y -BR2_TARGET_ROOTFS_UBI=y -BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 -BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" -BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC BR2_OPENIPC_SOC_VENDOR="hisilicon" diff --git a/br-ext-chip-hisilicon/configs/hi3518ev300_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3518ev300_ultimate_defconfig index e80cd1f9de..79d3f5646a 100644 --- a/br-ext-chip-hisilicon/configs/hi3518ev300_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3518ev300_ultimate_defconfig @@ -42,7 +42,7 @@ BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y BR2_TARGET_ROOTFS_UBI=y BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y -BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" +BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(EXTERNAL_VENDOR)/board/$(OPENIPC_SOC_FAMILY)/ubinize-nand.cfg" BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 # OpenIPC diff --git a/general/external.mk b/general/external.mk index 6dce6ca836..78bb334b1a 100644 --- a/general/external.mk +++ b/general/external.mk @@ -28,6 +28,32 @@ OPENIPC_TOOLCHAIN := toolchain/toolchain.$(OPENIPC_KERNEL) # by ~45KB in the size report rather than anything failing. WPA_SUPPLICANT_CONFIG_DISABLE += CONFIG_TDLS CONFIG_IEEE80211R +# NAND boards with a FIT (board//nand-fit.its) carry their kernel inside +# the UBIFS rootfs rather than in a volume of its own: no flash is set aside +# for a kernel, and the rootfs volume is as big as its image. Exactly one +# kernel goes in, never two: /boot/fitImage (zImage + DTB, hashed) when the +# board builds a FIT, which these do, otherwise /boot/uImage (the NOR kernel, +# DTB appended). u-boot-xmedia boots either, trying the FIT first, so a board +# without a FIT -- or a U-Boot with UBIFS but no FIT, given a uImage build -- +# needs nothing else. +# +# Copied into the UBIFS image's own copy of the target tree only, so the +# squashfs the NOR package ships stays as it was. The kernel exists by then: +# the uImage from the kernel build, the FIT from rootfs_script.sh, which runs +# before any filesystem is generated. Same include-order dependency as above: +# the hook list is expanded when the rule runs. +ifneq ($(wildcard $(EXTERNAL_VENDOR)/board/$(OPENIPC_SOC_FAMILY)/nand-fit.its),) +define OPENIPC_UBIFS_BOOT + mkdir -p $(TARGET_DIR)/boot + if [ -f $(BINARIES_DIR)/fitImage ]; then \ + cp $(BINARIES_DIR)/fitImage $(TARGET_DIR)/boot/; \ + else \ + cp $(BINARIES_DIR)/uImage $(TARGET_DIR)/boot/; \ + fi +endef +ROOTFS_UBIFS_PRE_GEN_HOOKS += OPENIPC_UBIFS_BOOT +endif + # linux.mk passes INSTALL_MOD_STRIP=1, which the kernel turns into # `strip --strip-debug`: every module still ships its full .symtab/.strtab. # Any other value is handed to strip as its options, and --strip-unneeded keeps diff --git a/general/overlay/usr/sbin/sysupgrade b/general/overlay/usr/sbin/sysupgrade index 8fa31ef056..4b562318aa 100755 --- a/general/overlay/usr/sbin/sysupgrade +++ b/general/overlay/usr/sbin/sysupgrade @@ -167,6 +167,84 @@ rootfs_device() { get_device "rootfs" } +# The ubifs layout sizes its volumes to the image on every upgrade, so what a +# rootfs has to fit is not its volume but the UBI space it shares with the +# overlay: both volumes' blocks, less what the overlay needs afterwards -- a +# floor UBIFS cannot mount below, plus the settings that are carried across +# (none under -n). Bytes, or nothing when sysfs cannot say. +UBIFS_OVERLAY_MIN_LEBS=24 +ubifs_rootfs_room() { + local r="$UBI_SYS/${ubi_rootfs_dev#/dev/}" d="$UBI_SYS/${ubi_data_dev#/dev/}" usable ebs debs=0 keep=0 + usable=$(cat "$r/usable_eb_size" 2>&3) && ebs=$(cat "$r/reserved_ebs" 2>&3) || return 0 + [ -n "$ubi_data_dev" ] && debs=$(cat "$d/reserved_ebs" 2>&3 || echo 0) + [ "1" = "$clear_overlay" ] || keep=$((${overlay_kb:-0} * 1024)) + echo $(((ebs + debs - UBIFS_OVERLAY_MIN_LEBS) * usable - keep)) +} + +# Refuse a rootfs that cannot fit where it is going, before anything is written. +check_rootfs_fits() { + local x=$1 room + if [ "$ubi_layout" = "ubifs" ]; then + [ -f "$x" ] || return 0 + room=$(ubifs_rootfs_room) + [ -n "$room" ] || return 0 + [ "$(stat -c %s "$x" 2>/dev/null || echo 0)" -le "$room" ] && return 0 + die "The rootfs image does not fit: $x is $(($(stat -c %s "$x") / 1024)) KB, the UBI device has $((room / 1024)) KB for it beside the settings. Nothing was written." + fi + check_image_fits "$x" "$(rootfs_device)" rootfs +} + +# Write a ubifs-layout rootfs: the kernel is inside it, and the volumes are +# sized to it. As PID 1 in the RAM root, with the old root released, so +# nothing holds either volume: +# 1. copy the overlay (the camera's settings) into RAM, unless -n +# 2. remove rootfs_data, which frees every block it held +# 3. resize rootfs to the image -- bigger or smaller, the UBI device decides +# 4. write the image +# 5. recreate rootfs_data on everything that is left +# 6. put the settings back; UBIFS formats the empty volume as it mounts +# A power cut between 2 and 6 costs the settings, not the camera: init mounts a +# tmpfs overlay when rootfs_data is missing or empty. One between 3 and 4 +# leaves no kernel to boot, as any rootfs write would. +rewrite_ubifs() { + local x=$1 ubi=${ubi_rootfs_dev%_*} rid=${ubi_rootfs_dev##*_} + local name=${ubi#/dev/} bak=/tmp/overlay.tar mnt=/tmp/overlay.old + local size=$(stat -c %s "$x" 2>/dev/null) + [ -n "$size" ] || die "Cannot size $x." + mark_live_flash_dirty + rm -f "$bak" + if [ -n "$ubi_data_dev" ]; then + if [ "1" != "$clear_overlay" ]; then + mkdir -p "$mnt" + if mount -t ubifs -o ro "$name:rootfs_data" "$mnt" 2>&3; then + tar -C "$mnt" -cf "$bak" . 2>&3 || + { echo_c 33 "Warning: could not copy the settings; they will be reset."; rm -f "$bak"; } + busybox umount "$mnt" 2>&3 + fi + fi + set_progress ubirmvol "$ubi" -N rootfs_data || + die "Removing the settings volume failed." + fi + set_progress ubirsvol "$ubi" -n "$rid" -s "$size" || + die "Resizing the rootfs volume to $size bytes failed." + set_progress ubiupdatevol "$ubi_rootfs_dev" "$x" || + die "Writing $x to $ubi_rootfs_dev failed." + set_progress ubimkvol "$ubi" -N rootfs_data -m || + die "Recreating the settings volume failed; the camera boots with its settings in RAM until it is reset." + overlay_recreated=1 + if [ -f "$bak" ]; then + mkdir -p "$mnt" + if mount -t ubifs "$name:rootfs_data" "$mnt" 2>&3 && tar -C "$mnt" -xf "$bak" 2>&3; then + sync + echo "Settings carried over" + else + echo_c 33 "Warning: could not restore the settings; the camera starts from defaults." + fi + busybox umount "$mnt" 2>&3 + rm -f "$bak" + fi +} + # Refuse an image that cannot fit the partition it is bound for, before the # write rather than during it. # @@ -270,7 +348,7 @@ preflight_image_sizes() { [ "1" = "$update_rootfs" ] && [ ! -f "$(rootfs_image)" ] && die "No rootfs image for this camera ($model): $(rootfs_image) not found. Is the firmware built for $model?" [ "1" = "$update_kernel" ] && check_image_fits "$(kernel_image)" "$kernel_device" kernel - [ "1" = "$update_rootfs" ] && check_image_fits "$(rootfs_image)" "$(rootfs_device)" rootfs + [ "1" = "$update_rootfs" ] && check_rootfs_fits "$(rootfs_image)" [ "1" = "$update_rootfs" ] && check_rootfs_complete "$(rootfs_image)" # The UBIFS checks read the target volume out of sysfs, which the flash # phase may not have; the camera is also still whole here. @@ -597,6 +675,12 @@ do_update_rootfs() { [ ! -f "$x" ] && die "File $x not found" [ "1" = "$exit_update" ] && return 0 local dev=$(rootfs_device) + if [ "$ubi_layout" = "ubifs" ]; then + check_rootfs_fits "$x" + rewrite_ubifs "$x" + echo_c 32 "RootFS updated to ${rootfs_version:-unknown}" + return 0 + fi check_image_fits "$x" "$dev" rootfs mark_live_flash_dirty # ${rootfs_version} is what verify_rootfs read out of the CANDIDATE FILE, so @@ -1752,7 +1836,7 @@ enter_ramfs() { export system_platform device_pinned # The UBI layout and its volumes, resolved on the whole system: the flash # phase may have no /sys to resolve them from. - export ubi_layout ubi_rootfs_dev ubi_data_dev UBI_SYS INITTAB + export ubi_layout ubi_rootfs_dev ubi_data_dev UBI_SYS INITTAB overlay_kb # archive: verify_ubifs_rootfs does not count a local archive as pinned. export archive @@ -2071,6 +2155,12 @@ get_system_info() { kernel_device=$(ubi_volume kernel) ubi_rootfs_dev=$(ubi_volume rootfs) ubi_data_dev=$(ubi_volume rootfs_data) + # ubifs: no kernel device; the running kernel is the uImage in the + # rootfs, and the settings are what an upgrade carries across. + if [ "$ubi_layout" = "ubifs" ]; then + kernel_device=/rom/boot/uImage + overlay_kb=$(df -k /overlay 2>&3 | awk 'NR == 2 { print $3 }') + fi else kernel_device=$(get_device "kernel") [ "$kernel_device" = "/dev/" ] && kernel_device=$(get_device "firmware") @@ -2096,32 +2186,59 @@ get_system_info() { && root_on_flash=0 } -# Which NAND layout this camera boots, when its kernel and rootfs are UBI -# volumes rather than MTD partitions. Empty for everything else -- NOR, a raw -# NAND kernel partition, and a squashfs reached through gluebi's mtdblock, which -# the MTD path below already writes. -# -# ubifs kernel volume = FIT, rootfs volume = UBIFS (root=ubi0:rootfs). -# Fetches the -nand- package: fitImage. + rootfs.ubifs.. -# ubiblock kernel volume = uImage, rootfs volume = squashfs mounted through -# ubiblock (root=/dev/ubiblockX_Y). Its volumes hold exactly the -# NOR artifacts, so it fetches the NOR package. -# -# Either way the rootfs volume is in use for as long as the camera runs: UBIFS -# or ubiblock holds it open under init's overlay root, and ubiupdatevol needs -# the volume to itself (see need_handoff). So a rootfs write on either layout -# has PID 1 leave the flash first. See enter_ramfs. +# Which NAND layout this camera boots, when its rootfs is a UBI volume. Empty +# for everything else -- NOR, and the retired split layout (a raw `kernel` +# partition beside the UBI device, see is_split_nand), which no upgrade writes. +# +# ubifs one UBIFS `rootfs` volume holding the root filesystem AND the +# kernel (/boot/fitImage, /boot/uImage), plus `rootfs_data`; no +# `kernel` volume and no `kernel` partition (root=ubi0:rootfs). +# Fetches the -nand- package: rootfs.ubifs., with +# fitImage. beside it as the SoC witness. An upgrade writes +# the one image and resizes the volumes to it (rewrite_ubifs). +# ubifs-kvol the earlier UBIFS layout with the kernel in a `kernel` volume of +# its own. Retired: a run that would write it is refused, and +# the camera is reinstalled from U-Boot. +# ubiblock kernel volume = uImage, rootfs volume = squashfs mounted through +# ubiblock (root=/dev/ubiblockX_Y). Its volumes hold exactly the +# NOR artifacts, so it fetches the NOR package. +# +# The rootfs volume is in use for as long as the camera runs: UBIFS or +# ubiblock holds it open under init's overlay root, and ubiupdatevol needs the +# volume to itself (see need_handoff). So a rootfs write on any of them has +# PID 1 leave the flash first. See enter_ramfs. detect_ubi_layout() { ubi_layout= - [ -n "$(ubi_volume kernel)" ] && [ -n "$(ubi_volume rootfs)" ] || return 0 + [ -n "$(ubi_volume rootfs)" ] || return 0 if grep -qE '(^|[[:space:]])root=ubi[0-9]*:rootfs([[:space:]]|$)' /proc/cmdline 2>&3; then - ubi_layout=ubifs - elif grep -qE '(^|[[:space:]])root=/dev/ubiblock' /proc/cmdline 2>&3; then + if [ -n "$(ubi_volume kernel)" ]; then + ubi_layout=ubifs-kvol + elif ! grep -q '"kernel"$' /proc/mtd 2>&3; then + ubi_layout=ubifs + fi + elif grep -qE '(^|[[:space:]])root=/dev/ubiblock' /proc/cmdline 2>&3 && + [ -n "$(ubi_volume kernel)" ]; then ubi_layout=ubiblock fi return 0 } +# The retired split NAND layout: the kernel a uImage in a raw `kernel` MTD +# partition beside the UBI device (hinand:1024k(boot),1024k(env),8192k(kernel), +# -(ubi)), and a UBIFS root in it with no `kernel` volume. detect_ubi_layout +# does not recognise it, so it would fall to the MTD path, which reaches the +# rootfs through gluebi's "rootfs" mtd and writes a NOR squashfs over the +# mounted UBIFS volume -- a camera that no longer boots. Its bootloader is +# retired too: the current one boots the kernel from the UBI `kernel` volume +# and never reads the raw partition, so no part of an upgrade is safe here. +# Reinstalling from U-Boot moves the camera to the UBI-only layout. +is_split_nand() { + [ -z "$ubi_layout" ] || return 1 + [ -n "$(ubi_volume rootfs)" ] && [ -z "$(ubi_volume kernel)" ] || return 1 + grep -qE '(^|[[:space:]])root=ubi[0-9]*:rootfs([[:space:]]|$)' /proc/cmdline 2>&3 || return 1 + grep -q '"kernel"$' /proc/mtd 2>&3 +} + # The package flavour this camera installs from, as the manifest spells it. pkg_flash() { [ "$ubi_layout" = "ubifs" ] && echo nand || echo nor @@ -2358,11 +2475,11 @@ else # committed. exit_update means the rootfs is not being written at all, so # whether it would have fitted is not this run's business. [ "1" = "$update_rootfs" ] && [ "1" != "$exit_update" ] && - check_image_fits "$(rootfs_image)" "$(rootfs_device)" rootfs + check_rootfs_fits "$(rootfs_image)" [ "1" = "$update_kernel" ] && do_update_kernel "$kernel_file" [ "1" = "$update_rootfs" ] && do_update_rootfs "$rootfs_file" fi -[ "1" = "$clear_overlay" ] && do_wipe_overlay +[ "1" = "$clear_overlay" ] && [ "1" != "$overlay_recreated" ] && do_wipe_overlay reboot_system @@ -2563,6 +2680,23 @@ fi [ "1" != "$skip_selfupdate" ] && self_update +# Before anything is locked, downloaded or written. -n alone stays allowed: it +# only empties rootfs_data, which this layout keeps in a UBI volume like any +# other. +if { [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; } && is_split_nand; then + die "This camera uses the retired split NAND layout (kernel in its own partition, UBIFS root beside it), which this upgrade cannot write safely. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/hisilicon/socs/$model -- nothing was written." +fi +if { [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; } && [ "$ubi_layout" = "ubifs-kvol" ]; then + die "This camera uses the retired NAND layout with a separate kernel volume; current images carry the kernel inside the rootfs. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/$vendor/socs/$model -- nothing was written." +fi +# On the ubifs layout the kernel is a file in the rootfs: there is no kernel +# to write on its own, and either flag means writing the one image. +if [ "$ubi_layout" = "ubifs" ] && [ "1" = "$update_kernel" ]; then + update_kernel=0 + update_rootfs=1 + echo_c 37 "\nThe kernel lives inside the rootfs on this camera: writing the rootfs." +fi + create_lock free_resources diff --git a/general/scripts/rootfs_script.sh b/general/scripts/rootfs_script.sh index 8625a8c6f3..15843887ed 100755 --- a/general/scripts/rootfs_script.sh +++ b/general/scripts/rootfs_script.sh @@ -122,10 +122,15 @@ if [ -f "${NAND_FIT_ITS}" ] && grep -q "^BR2_TARGET_ROOTFS_UBI=y" "${BR2_CONFIG} KBOOT=$(ls -d "${BUILD_DIR}"/linux-*/arch/arm/boot 2>/dev/null | grep -v headers | head -1) FIT_DIR="${BINARIES_DIR}/nand-fit" rm -rf "${FIT_DIR}" && mkdir -p "${FIT_DIR}" || exit 1 - sed "s/@SOC@/${OPENIPC_SOC_MODEL}/" "${NAND_FIT_ITS}" > "${FIT_DIR}/nand-fit.its" || exit 1 + # @SOC@ is the SoC the FIT is stamped with (sysupgrade's fit_soc reads it); + # @DTB@ is for a family .its whose models each build their own + # -demb.dtb (hi3516ev200 family). An .its naming its DTB outright + # has no @DTB@ and is copied as it is. + sed -e "s/@SOC@/${OPENIPC_SOC_MODEL}/" -e "s/@DTB@/${OPENIPC_SOC_MODEL}-demb.dtb/g" \ + "${NAND_FIT_ITS}" > "${FIT_DIR}/nand-fit.its" || exit 1 cp "${KBOOT}/zImage" "${FIT_DIR}/" || { echo "NAND FIT: no zImage in ${KBOOT}" >&2; exit 1; } - # Every DTB the .its names, from the kernel's dts output. - for dtb in $(grep -o '/incbin/("[^"]*\.dtb")' "${NAND_FIT_ITS}" | sed 's/.*("\(.*\)")/\1/'); do + # Every DTB the stamped .its names, from the kernel's dts output. + for dtb in $(grep -o '/incbin/("[^"]*\.dtb")' "${FIT_DIR}/nand-fit.its" | sed 's/.*("\(.*\)")/\1/'); do cp "${KBOOT}/dts/${dtb}" "${FIT_DIR}/" || { echo "NAND FIT: no ${dtb} in ${KBOOT}/dts" >&2; exit 1; } done "${HOST_DIR}/bin/mkimage" -f "${FIT_DIR}/nand-fit.its" "${BINARIES_DIR}/fitImage" || exit 1 From ea056619619d8375e43e9ce5f9822c6b8ed0f0d5 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Sun, 4 Oct 2026 16:59:29 +0300 Subject: [PATCH 2/3] sysupgrade: retire the ubiblock NAND layout The squashfs-over-ubiblock layout keeps its kernel in a sized volume of its own, as the first FIT layout does. It is retired the same way: a run that would write either half is refused before anything is touched, with the reinstall link. -n alone still empties the settings volume. Also gone: the gluebi path an old ubiblock camera took when its inittab had no ::restart: entry, and check_rootfs_format's ubiblock branch, which nothing reaches now. Images on the retired layout still boot; init keeps mounting their overlay. Only upgrades are refused. A NAND camera upgrades from a -nand- package only, which the ultimate builds of the hi3516ev200 and gk7205v500 families ship. Harness: 244 checks pass. --- .github/scripts/test_sysupgrade.sh | 141 ++++------------------------ general/overlay/usr/sbin/sysupgrade | 40 +++----- 2 files changed, 35 insertions(+), 146 deletions(-) diff --git a/.github/scripts/test_sysupgrade.sh b/.github/scripts/test_sysupgrade.sh index 193caba0bc..6ad922af17 100755 --- a/.github/scripts/test_sysupgrade.sh +++ b/.github/scripts/test_sysupgrade.sh @@ -2060,12 +2060,13 @@ rm -f "$SB/bin/stat" echo echo "=== Part 1b: UBI NAND layouts ===" -# Two layouts keep kernel and rootfs in UBI volumes, told apart by root=: -# ubifs kernel = FIT, rootfs = UBIFS, root=ubi0:rootfs -# ubiblock kernel = uImage, rootfs = squashfs through ubiblock -# ubiupdatevol takes its volume exclusively, and the rootfs volume is held -# open on both (UBIFS, or ubiblock's reader), so a rootfs write on either -# goes through the PID 1 hand-off; the kernel volume is written in place. +# The NAND layout is one UBIFS rootfs volume with the kernel inside it +# (/boot/fitImage) plus rootfs_data, root=ubi0:rootfs. An upgrade writes the +# one image and resizes both volumes to it, from the handed-off PID 1: the +# rootfs volume is held open for as long as the camera runs, and +# ubiupdatevol takes its volume exclusively. The layouts with a kernel volume +# of their own -- ubiblock (uImage + squashfs) and ubifs-kvol (FIT + UBIFS) -- +# and the split one (raw kernel partition) are retired: refused, reinstalled. CMDLINE_UBIFS='mem=32M console=ttyAMA0,115200 panic=20 init=/init root=ubi0:rootfs rootfstype=ubifs ubi.mtd=2,2048 mtdparts=nand:768k(boot),256k(env),-(ubi)' CMDLINE_UBIBLOCK='mem=32M console=ttyAMA0,115200 panic=20 init=/init root=/dev/ubiblock0_1 ubi.mtd=2,2048 ubi.block=0,1 mtdparts=nand:768k(boot),256k(env),-(ubi)' UFIT="$SB/tmp/fitImage.gk7205v500" @@ -2101,31 +2102,22 @@ ubi_wrote() { grep -q "ubiupdatevol .*$1" "$SB/tmp/flash.log"; } nothing_ubi() { ! grep -qE "ubiupdatevol|flashcp|flash_eraseall" "$SB/tmp/flash.log"; } handed_off() { grep -q "^kill -QUIT 1" "$SB/tmp/flash.log"; } -# --- ubiblock: NOR artifacts; the rootfs goes through the hand-off too -# (measured: ubiupdatevol on a volume ubiblock has open is EBUSY) +# --- ubiblock is retired: a write of either half is refused, nothing touched reset_env; ubi_setup ubiblock STUB_PIVOT_RC=0 run -z --kernel="$UK" --rootfs="$US" -if handed_off && nothing_ubi && grep -q "ubi_layout=.ubiblock" "$SB/ram/sysupgrade.env"; then - ok "ubiblock: a rootfs write hands PID 1 off before anything is written" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout with a separate kernel volume" && + nothing_ubi && ! handed_off && ! rebooted; then + ok "ubiblock: a kernel+rootfs run is refused before anything is touched" else - bad "ubiblock: expected the hand-off, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "ubiblock must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi reset_env; ubi_setup ubiblock run -z --kernel="$UK" -if [ "$RC" -eq 0 ] && ubi_wrote "/dev/ubi0_0 $UK" && ! handed_off && ! grep -q flashcp "$SB/tmp/flash.log"; then - ok "ubiblock: a uImage is written into the kernel volume in place" -else - bad "ubiblock: kernel-only should write ubi0_0 in place, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" -fi -reset_env; ubi_setup ubiblock -RUN_ENV="_ramfs_phase=1 _handoff=1 ubi_layout=ubiblock kernel_device=/dev/ubi0_0 ubi_rootfs_dev=/dev/ubi0_1 ubi_data_dev=/dev/ubi0_2 update_kernel=1 update_rootfs=1 kernel_file=$UK rootfs_file=$US model=gk7205v500 skip_soc=1 skip_ver=1 root_on_flash=1 ram_root_shipped=1" -run -u=$(logged_at "umount -l /mnt"); k=$(logged_at "ubiupdatevol /dev/ubi0_0 $UK"); r=$(logged_at "ubiupdatevol /dev/ubi0_1 $US") -if [ -n "$u" ] && [ -n "$k" ] && [ -n "$r" ] && [ "$u" -lt "$k" ] && [ "$k" -lt "$r" ] && rebooted; then - ok "stage 2 (ubiblock): old root released, uImage then squashfs written" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout" && nothing_ubi && ! rebooted; then + ok "ubiblock: a kernel-only run is refused too" else - bad "stage 2 ubiblock order ${u:-none}/${k:-none}/${r:-none} log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "ubiblock -k must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi # --- ubifs: the kernel is a file in the rootfs, so -k writes the rootfs @@ -2186,13 +2178,6 @@ if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "boots a UBIFS rootfs" && not else bad "ubifs: a squashfs must be refused, rc=$RC out='$OUT'" fi -reset_env; ubi_setup ubiblock -run -z -f --rootfs="$UFS" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "is a UBIFS image" && nothing_ubi; then - ok "ubiblock: a UBIFS rootfs is refused" -else - bad "ubiblock: a UBIFS image must be refused, rc=$RC out='$OUT'" -fi # --- a UBIFS made for other LEBs would never mount reset_env; ubi_setup ubifs @@ -2320,7 +2305,7 @@ else bad "stage 2: umount failure must stop before the first write, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi -# --- a ubiblock camera from before the hand-off keeps its gluebi path +# --- a ubiblock camera from before the hand-off no longer writes through gluebi reset_env; ubi_setup ubiblock printf '::sysinit:/etc/init.d/rcS\n' > "$SB/etc/inittab" set_mtd <<'EOF2' @@ -2333,101 +2318,15 @@ mtd4: 01f00000 0001f000 "rootfs" mtd5: 00200000 0001f000 "rootfs_data" EOF2 run -z --kernel="$UK" --rootfs="$US" -if [ "$RC" -eq 0 ] && flashed /dev/mtd3 && flashed /dev/mtd4 && ! handed_off && - ! grep -q ubiupdatevol "$SB/tmp/flash.log"; then - ok "ubiblock without ::restart: writes through gluebi as it always did" -else - bad "ubiblock old-inittab fallback, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" -fi -reset_env; ubi_setup ubiblock -printf '::sysinit:/etc/init.d/rcS\n' > "$SB/etc/inittab" -run -z --kernel="$UK" --rootfs="$US" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "::restart:/sbin/init" && nothing_ubi && ! rebooted; then - ok "ubiblock without ::restart: and without gluebi is refused, nothing written" -else - bad "ubiblock old inittab, no gluebi, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" -fi - -# --- the retired HiSilicon split NAND layout: uImage in a raw `kernel` -# partition, UBIFS root in a UBI device beside it, no `kernel` volume. gluebi -# names the UBIFS volume "rootfs" in /proc/mtd, which is what the MTD path -# would have flashed a NOR squashfs over. -split_setup() { - local u="$SB/sys/class/ubi" i=0 name - rm -rf "$u" - for name in rootfs rootfs_data; do - mkdir -p "$u/ubi0_$i" - echo "$name" > "$u/ubi0_$i/name" - echo 126976 > "$u/ubi0_$i/usable_eb_size" - echo 260 > "$u/ubi0_$i/reserved_ebs" - i=$((i + 1)) - done - set_mtd <<'EOF2' -dev: size erasesize name -mtd0: 00100000 00020000 "boot" -mtd1: 00100000 00020000 "env" -mtd2: 00800000 00020000 "kernel" -mtd3: 07600000 00020000 "ubi" -mtd4: 02017000 0001f000 "rootfs" -mtd5: 04f51000 0001f000 "rootfs_data" -EOF2 - set_cmdline 'mem=128M console=ttyAMA0,115200 panic=20 rootfstype=ubifs root=ubi0:rootfs ubi.mtd=3,2048 mtdparts=hinand:1024k(boot),1024k(env),8192k(kernel),-(ubi)' - set_platform hi3516ev300_ultimate ultimate - export STUB_VENDOR=hisilicon STUB_SOC=hi3516ev300 STUB_IMG_SOC=hi3516ev300 - SK="$SB/tmp/uImage.hi3516ev300"; SS="$SB/tmp/rootfs.squashfs.hi3516ev300" - make_uimage "$SK" hi3516ev300 - make_squashfs "$SS" 8192 -} -reset_env; split_setup -STUB_PIVOT_RC=0 -run -z --kernel="$SK" --rootfs="$SS" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired split NAND layout" && - printf '%s' "$OUT" | grep -q "openipc.org/cameras/vendors/hisilicon/socs/hi3516ev300" && - ! grep -qE "flashcp|ubiupdatevol|flash_eraseall|pivot_root" "$SB/tmp/flash.log" && ! rebooted; then - ok "split NAND: kernel+rootfs refused before anything is touched, reinstall link given" -else - bad "split NAND -k -r, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" -fi -reset_env; split_setup -run -z --kernel="$SK" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired split NAND layout" && nothing_wrote && ! rebooted; then - ok "split NAND: a kernel-only run is refused too" -else - bad "split NAND -k, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" -fi -reset_env; split_setup -run -z -n -if ! printf '%s' "$OUT" | grep -q "retired split NAND layout"; then - ok "split NAND: -n alone is not refused" -else - bad "split NAND -n should not be refused, rc=$RC out='$OUT'" -fi - -# --- an hi3516ev300 on the UBI-only layout is an ordinary ubifs camera -ubi_setup_hisi() { - ubi_setup ubifs - set_cmdline 'mem=32M console=ttyAMA0,115200 panic=20 init=/init root=ubi0:rootfs rootfstype=ubifs ubi.mtd=2,2048 mtdparts=hinand:768k(boot),256k(env),-(ubi)' - set_platform hi3516ev300_ultimate ultimate - export STUB_VENDOR=hisilicon STUB_SOC=hi3516ev300 STUB_IMG_SOC=hi3516ev300 -} -reset_env; ubi_setup_hisi -HFIT="$SB/tmp/fitImage.hi3516ev300"; make_fit_soc "$HFIT" hi3516ev300 -HFS="$SB/tmp/rootfs.ubifs.hi3516ev300"; make_ubifs "$HFS" -STUB_PIVOT_RC=0 -run -z --kernel="$HFIT" --rootfs="$HFS" -if printf '%s' "$OUT" | grep -q "SoC from the FIT kernel beside it: hi3516ev300" && handed_off && nothing_ubi && - ! printf '%s' "$OUT" | grep -qE "retired (split )?NAND layout"; then - ok "hi3516ev300 UBI-only: an ordinary ubifs camera, rootfs write handed off" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout" && nothing_ubi && ! rebooted; then + ok "ubiblock without ::restart: is refused too, gluebi or not" else - bad "hi3516ev300 UBI-only, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "ubiblock old inittab must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi -default_url_is "https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516ev300-nand-ultimate.tgz" \ - ubi_setup_hisi -# --- the download: -nand- for ubifs, -nor- for ubiblock +# --- the download: -nand- for ubifs F=https://github.com/OpenIPC/firmware/releases/download/latest default_url_is "$F/openipc.gk7205v500-nand-ultimate.tgz" ubi_setup ubifs -default_url_is "$F/openipc.gk7205v500-nor-ultimate.tgz" ubi_setup ubiblock # --- the unpack leaves rootfs.ubi (fresh-install image) in the archive reset_env; ubi_setup ubifs diff --git a/general/overlay/usr/sbin/sysupgrade b/general/overlay/usr/sbin/sysupgrade index 4b562318aa..7a24ee3966 100755 --- a/general/overlay/usr/sbin/sysupgrade +++ b/general/overlay/usr/sbin/sysupgrade @@ -526,17 +526,13 @@ is_ubifs() { } # The rootfs format has to be the one this camera's kernel command line mounts: -# a squashfs written into the volume of a root=ubi0:rootfs camera, or a UBIFS -# into a ubiblock one, flashes cleanly and never mounts again. The kernel's -# rootfstype is fixed by the bootloader environment, which no upgrade rewrites. +# a squashfs written into the volume of a root=ubi0:rootfs camera flashes +# cleanly and never mounts again. The kernel's rootfstype is fixed by the +# bootloader environment, which no upgrade rewrites. check_rootfs_format() { - [ -n "$ubi_layout" ] || return 0 if [ "$ubi_layout" = "ubifs" ] && ! is_ubifs "$1"; then die "This camera boots a UBIFS rootfs, and $1 is not one. Nothing was written." fi - if [ "$ubi_layout" = "ubiblock" ] && is_ubifs "$1"; then - die "This camera boots a squashfs rootfs through ubiblock, and $1 is a UBIFS image. Nothing was written." - fi return 0 } @@ -1164,9 +1160,9 @@ remote_length_kb() { download_firmware() { - # A UBIFS NAND layout installs from the -nand- package; every other camera, - # NAND ones included (a ubiblock rootfs holds the NOR artifacts verbatim), - # from the NOR package. + # A UBIFS NAND layout installs from the -nand- package, every other camera + # from the NOR package. A NAND camera on a retired layout has been refused + # before this. [ "$flash_type" = "nand" ] && [ "$(pkg_flash)" = "nor" ] && echo_c 31 "\nNote: the updater uses the NOR package for updating NAND" echo_c 33 "\nFirmware" @@ -2139,16 +2135,6 @@ get_system_info() { [ -z "$model" ] && die "Cannot determine SoC: no BUILD_PLATFORM and no U-Boot 'soc'" resolve_model_alias detect_ubi_layout - # A ubiblock camera running an image from before the hand-off has no - # ::restart: entry, and only the upgrade being run would ship one. Its - # rootfs was written through gluebi's mtd until now, which works with the - # volume mounted (gluebi writes beside ubiblock's reader), so keep doing - # that rather than refusing every upgrade. The next image brings the - # entry, and the UBI path, with it. - if [ "$ubi_layout" = "ubiblock" ] && ! inittab_hands_off && - [ "/dev/" != "$(get_device "rootfs")" ]; then - ubi_layout= - fi if [ -n "$ubi_layout" ]; then # Resolved here, on the whole system: the flash phase may run in a ramfs # where /sys came across best-effort, or not at all. @@ -2200,8 +2186,7 @@ get_system_info() { # its own. Retired: a run that would write it is refused, and # the camera is reinstalled from U-Boot. # ubiblock kernel volume = uImage, rootfs volume = squashfs mounted through -# ubiblock (root=/dev/ubiblockX_Y). Its volumes hold exactly the -# NOR artifacts, so it fetches the NOR package. +# ubiblock (root=/dev/ubiblockX_Y). Retired like ubifs-kvol. # # The rootfs volume is in use for as long as the camera runs: UBIFS or # ubiblock holds it open under init's overlay root, and ubiupdatevol needs the @@ -2686,9 +2671,14 @@ fi if { [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; } && is_split_nand; then die "This camera uses the retired split NAND layout (kernel in its own partition, UBIFS root beside it), which this upgrade cannot write safely. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/hisilicon/socs/$model -- nothing was written." fi -if { [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; } && [ "$ubi_layout" = "ubifs-kvol" ]; then - die "This camera uses the retired NAND layout with a separate kernel volume; current images carry the kernel inside the rootfs. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/$vendor/socs/$model -- nothing was written." -fi +# Both NAND layouts with a kernel volume of their own are retired the same way: +# current images carry the kernel inside the UBIFS rootfs and size the volumes +# to it, which neither can take without its volumes being made anew. +case "$ubi_layout" in ubifs-kvol|ubiblock) + if [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; then + die "This camera uses the retired NAND layout with a separate kernel volume; current images carry the kernel inside the rootfs. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/$vendor/socs/$model -- nothing was written." + fi ;; +esac # On the ubifs layout the kernel is a file in the rootfs: there is no kernel # to write on its own, and either flag means writing the one image. if [ "$ubi_layout" = "ubifs" ] && [ "1" = "$update_kernel" ]; then From 182ea0e9e45bff6fc9bb618134ac824d8abc3b06 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Sun, 4 Oct 2026 17:37:39 +0300 Subject: [PATCH 3/3] sysupgrade: scope the ubiblock retirement, and keep settings or stop Review on #2537. - ubiblock is retired only on the SoCs whose NAND images moved to the kernel-in-rootfs layout: gk7205v500, v510, v530, hi3516ev200, hi3516ev300, hi3518ev300 and hi3516dv200 (nand_layout_moved). SigmaStar and Rockchip NAND images are still ubiblock-shaped, so everywhere else the ubiblock write path stays: the kernel volume is written in place, the rootfs goes through the hand-off, and old-inittab cameras keep the gluebi fallback. - Keeping the settings on an upgrade now has to work before anything changes. If the read-only mount or the copy into RAM fails, the run stops with nothing written, and -n remains the way to upgrade without the settings. The archive's real size, which counts sparse files at full length, is then checked against the room beside the new image before the first volume operation. - --kernel=FILE alone on the ubifs layout is refused: the kernel lives inside rootfs.ubifs and has to come with --rootfs=. Previously the run wrote whatever rootfs was at the default path. A remote -k still becomes a rootfs write. - The split-layout refusal links to the camera's own vendor page, not always hisilicon's. Harness: 257 checks pass. New cases: ubiblock writes on ssc338q, ubiblock refused on gk7205v500 with and without ::restart:, --kernel alone refused, and unreadable settings stopping stage 2 before any volume is touched. --- .github/scripts/test_sysupgrade.sh | 207 ++++++++++++++++++++++++---- general/overlay/usr/sbin/sysupgrade | 84 ++++++++--- 2 files changed, 247 insertions(+), 44 deletions(-) diff --git a/.github/scripts/test_sysupgrade.sh b/.github/scripts/test_sysupgrade.sh index 6ad922af17..64bec3cf62 100755 --- a/.github/scripts/test_sysupgrade.sh +++ b/.github/scripts/test_sysupgrade.sh @@ -318,7 +318,8 @@ done case " $* " in *" -t ubifs "*) case " $* " in - *" ro "*) echo "mount ubifs ro $target" >> "$FLASH_LOG" + *" ro "*) [ "1" = "$STUB_UBIFS_RO_FAIL" ] && exit 255 + echo "mount ubifs ro $target" >> "$FLASH_LOG" mkdir -p "$target/etc"; echo settings > "$target/etc/marker" ;; *) echo "mount ubifs rw $target" >> "$FLASH_LOG" rm -rf "$target"; mkdir -p "$target" ;; @@ -2060,13 +2061,12 @@ rm -f "$SB/bin/stat" echo echo "=== Part 1b: UBI NAND layouts ===" -# The NAND layout is one UBIFS rootfs volume with the kernel inside it -# (/boot/fitImage) plus rootfs_data, root=ubi0:rootfs. An upgrade writes the -# one image and resizes both volumes to it, from the handed-off PID 1: the -# rootfs volume is held open for as long as the camera runs, and -# ubiupdatevol takes its volume exclusively. The layouts with a kernel volume -# of their own -- ubiblock (uImage + squashfs) and ubifs-kvol (FIT + UBIFS) -- -# and the split one (raw kernel partition) are retired: refused, reinstalled. +# Two layouts keep kernel and rootfs in UBI volumes, told apart by root=: +# ubifs kernel = FIT, rootfs = UBIFS, root=ubi0:rootfs +# ubiblock kernel = uImage, rootfs = squashfs through ubiblock +# ubiupdatevol takes its volume exclusively, and the rootfs volume is held +# open on both (UBIFS, or ubiblock's reader), so a rootfs write on either +# goes through the PID 1 hand-off; the kernel volume is written in place. CMDLINE_UBIFS='mem=32M console=ttyAMA0,115200 panic=20 init=/init root=ubi0:rootfs rootfstype=ubifs ubi.mtd=2,2048 mtdparts=nand:768k(boot),256k(env),-(ubi)' CMDLINE_UBIBLOCK='mem=32M console=ttyAMA0,115200 panic=20 init=/init root=/dev/ubiblock0_1 ubi.mtd=2,2048 ubi.block=0,1 mtdparts=nand:768k(boot),256k(env),-(ubi)' UFIT="$SB/tmp/fitImage.gk7205v500" @@ -2091,40 +2091,56 @@ EOF2 ubifs|ubifs-kvol) set_cmdline "$CMDLINE_UBIFS" ;; *) set_cmdline "$CMDLINE_UBIBLOCK" ;; esac - set_platform gk7205v500_ultimate ultimate - export STUB_VENDOR=goke STUB_SOC=gk7205v500 STUB_IMG_SOC=gk7205v500 + # ubiblock is what SigmaStar and Rockchip NAND images still are; on the + # SoCs that moved to the kernel-in-rootfs layout it is retired, so its + # write path is exercised on one that did not (ssc338q). UBLOCK_SOC picks + # a moved one instead, for the refusal. + local soc=gk7205v500 vendor=goke + [ "$1" = ubiblock ] && { soc=${UBLOCK_SOC:-ssc338q}; [ "$soc" = ssc338q ] && vendor=sigmastar; } + UK="$SB/tmp/uImage.$soc"; US="$SB/tmp/rootfs.squashfs.$soc" + set_platform ${soc}_ultimate ultimate + export STUB_VENDOR=$vendor STUB_SOC=$soc STUB_IMG_SOC=$soc make_fit "$UFIT" make_ubifs "$UFS" - make_uimage "$UK" gk7205v500 + make_uimage "$UK" $soc make_squashfs "$US" 8192 } ubi_wrote() { grep -q "ubiupdatevol .*$1" "$SB/tmp/flash.log"; } nothing_ubi() { ! grep -qE "ubiupdatevol|flashcp|flash_eraseall" "$SB/tmp/flash.log"; } handed_off() { grep -q "^kill -QUIT 1" "$SB/tmp/flash.log"; } -# --- ubiblock is retired: a write of either half is refused, nothing touched +# --- ubiblock: NOR artifacts; the rootfs goes through the hand-off too +# (measured: ubiupdatevol on a volume ubiblock has open is EBUSY) reset_env; ubi_setup ubiblock STUB_PIVOT_RC=0 run -z --kernel="$UK" --rootfs="$US" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout with a separate kernel volume" && - nothing_ubi && ! handed_off && ! rebooted; then - ok "ubiblock: a kernel+rootfs run is refused before anything is touched" +if handed_off && nothing_ubi && grep -q "ubi_layout=.ubiblock" "$SB/ram/sysupgrade.env"; then + ok "ubiblock: a rootfs write hands PID 1 off before anything is written" else - bad "ubiblock must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "ubiblock: expected the hand-off, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi reset_env; ubi_setup ubiblock run -z --kernel="$UK" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout" && nothing_ubi && ! rebooted; then - ok "ubiblock: a kernel-only run is refused too" +if [ "$RC" -eq 0 ] && ubi_wrote "/dev/ubi0_0 $UK" && ! handed_off && ! grep -q flashcp "$SB/tmp/flash.log"; then + ok "ubiblock: a uImage is written into the kernel volume in place" +else + bad "ubiblock: kernel-only should write ubi0_0 in place, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; ubi_setup ubiblock +RUN_ENV="_ramfs_phase=1 _handoff=1 ubi_layout=ubiblock kernel_device=/dev/ubi0_0 ubi_rootfs_dev=/dev/ubi0_1 ubi_data_dev=/dev/ubi0_2 update_kernel=1 update_rootfs=1 kernel_file=$UK rootfs_file=$US model=ssc338q skip_soc=1 skip_ver=1 root_on_flash=1 ram_root_shipped=1" +run +u=$(logged_at "umount -l /mnt"); k=$(logged_at "ubiupdatevol /dev/ubi0_0 $UK"); r=$(logged_at "ubiupdatevol /dev/ubi0_1 $US") +if [ -n "$u" ] && [ -n "$k" ] && [ -n "$r" ] && [ "$u" -lt "$k" ] && [ "$k" -lt "$r" ] && rebooted; then + ok "stage 2 (ubiblock): old root released, uImage then squashfs written" else - bad "ubiblock -k must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "stage 2 ubiblock order ${u:-none}/${k:-none}/${r:-none} log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi # --- ubifs: the kernel is a file in the rootfs, so -k writes the rootfs reset_env; ubi_setup ubifs make_fit_soc "$UFIT" gk7205v500 STUB_PIVOT_RC=0 -run -z --kernel="$UFIT" +run -z --kernel="$UFIT" --rootfs="$UFS" if printf '%s' "$OUT" | grep -q "kernel lives inside the rootfs" && handed_off && nothing_ubi; then ok "ubifs: -k means writing the rootfs, through the hand-off" else @@ -2178,6 +2194,13 @@ if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "boots a UBIFS rootfs" && not else bad "ubifs: a squashfs must be refused, rc=$RC out='$OUT'" fi +reset_env; ubi_setup ubiblock +run -z -f --rootfs="$UFS" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "is a UBIFS image" && nothing_ubi; then + ok "ubiblock: a UBIFS rootfs is refused" +else + bad "ubiblock: a UBIFS image must be refused, rc=$RC out='$OUT'" +fi # --- a UBIFS made for other LEBs would never mount reset_env; ubi_setup ubifs @@ -2305,7 +2328,7 @@ else bad "stage 2: umount failure must stop before the first write, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" fi -# --- a ubiblock camera from before the hand-off no longer writes through gluebi +# --- a ubiblock camera from before the hand-off keeps its gluebi path reset_env; ubi_setup ubiblock printf '::sysinit:/etc/init.d/rcS\n' > "$SB/etc/inittab" set_mtd <<'EOF2' @@ -2318,15 +2341,149 @@ mtd4: 01f00000 0001f000 "rootfs" mtd5: 00200000 0001f000 "rootfs_data" EOF2 run -z --kernel="$UK" --rootfs="$US" -if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout" && nothing_ubi && ! rebooted; then - ok "ubiblock without ::restart: is refused too, gluebi or not" +if [ "$RC" -eq 0 ] && flashed /dev/mtd3 && flashed /dev/mtd4 && ! handed_off && + ! grep -q ubiupdatevol "$SB/tmp/flash.log"; then + ok "ubiblock without ::restart: writes through gluebi as it always did" +else + bad "ubiblock old-inittab fallback, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; ubi_setup ubiblock +printf '::sysinit:/etc/init.d/rcS\n' > "$SB/etc/inittab" +run -z --kernel="$UK" --rootfs="$US" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "::restart:/sbin/init" && nothing_ubi && ! rebooted; then + ok "ubiblock without ::restart: and without gluebi is refused, nothing written" +else + bad "ubiblock old inittab, no gluebi, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi + +# --- the retired HiSilicon split NAND layout: uImage in a raw `kernel` +# partition, UBIFS root in a UBI device beside it, no `kernel` volume. gluebi +# names the UBIFS volume "rootfs" in /proc/mtd, which is what the MTD path +# would have flashed a NOR squashfs over. +split_setup() { + local u="$SB/sys/class/ubi" i=0 name + rm -rf "$u" + for name in rootfs rootfs_data; do + mkdir -p "$u/ubi0_$i" + echo "$name" > "$u/ubi0_$i/name" + echo 126976 > "$u/ubi0_$i/usable_eb_size" + echo 260 > "$u/ubi0_$i/reserved_ebs" + i=$((i + 1)) + done + set_mtd <<'EOF2' +dev: size erasesize name +mtd0: 00100000 00020000 "boot" +mtd1: 00100000 00020000 "env" +mtd2: 00800000 00020000 "kernel" +mtd3: 07600000 00020000 "ubi" +mtd4: 02017000 0001f000 "rootfs" +mtd5: 04f51000 0001f000 "rootfs_data" +EOF2 + set_cmdline 'mem=128M console=ttyAMA0,115200 panic=20 rootfstype=ubifs root=ubi0:rootfs ubi.mtd=3,2048 mtdparts=hinand:1024k(boot),1024k(env),8192k(kernel),-(ubi)' + set_platform hi3516ev300_ultimate ultimate + export STUB_VENDOR=hisilicon STUB_SOC=hi3516ev300 STUB_IMG_SOC=hi3516ev300 + SK="$SB/tmp/uImage.hi3516ev300"; SS="$SB/tmp/rootfs.squashfs.hi3516ev300" + make_uimage "$SK" hi3516ev300 + make_squashfs "$SS" 8192 +} +reset_env; split_setup +STUB_PIVOT_RC=0 +run -z --kernel="$SK" --rootfs="$SS" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired split NAND layout" && + printf '%s' "$OUT" | grep -q "openipc.org/cameras/vendors/hisilicon/socs/hi3516ev300" && + ! grep -qE "flashcp|ubiupdatevol|flash_eraseall|pivot_root" "$SB/tmp/flash.log" && ! rebooted; then + ok "split NAND: kernel+rootfs refused before anything is touched, reinstall link given" +else + bad "split NAND -k -r, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; split_setup +run -z --kernel="$SK" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired split NAND layout" && nothing_wrote && ! rebooted; then + ok "split NAND: a kernel-only run is refused too" +else + bad "split NAND -k, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; split_setup +run -z -n +if ! printf '%s' "$OUT" | grep -q "retired split NAND layout"; then + ok "split NAND: -n alone is not refused" else - bad "ubiblock old inittab must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" + bad "split NAND -n should not be refused, rc=$RC out='$OUT'" fi -# --- the download: -nand- for ubifs +# --- an hi3516ev300 on the UBI-only layout is an ordinary ubifs camera +ubi_setup_hisi() { + ubi_setup ubifs + set_cmdline 'mem=32M console=ttyAMA0,115200 panic=20 init=/init root=ubi0:rootfs rootfstype=ubifs ubi.mtd=2,2048 mtdparts=hinand:768k(boot),256k(env),-(ubi)' + set_platform hi3516ev300_ultimate ultimate + export STUB_VENDOR=hisilicon STUB_SOC=hi3516ev300 STUB_IMG_SOC=hi3516ev300 +} +reset_env; ubi_setup_hisi +HFIT="$SB/tmp/fitImage.hi3516ev300"; make_fit_soc "$HFIT" hi3516ev300 +HFS="$SB/tmp/rootfs.ubifs.hi3516ev300"; make_ubifs "$HFS" +STUB_PIVOT_RC=0 +run -z --kernel="$HFIT" --rootfs="$HFS" +if printf '%s' "$OUT" | grep -q "SoC from the FIT kernel beside it: hi3516ev300" && handed_off && nothing_ubi && + ! printf '%s' "$OUT" | grep -qE "retired (split )?NAND layout"; then + ok "hi3516ev300 UBI-only: an ordinary ubifs camera, rootfs write handed off" +else + bad "hi3516ev300 UBI-only, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +default_url_is "https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516ev300-nand-ultimate.tgz" \ + ubi_setup_hisi + +# --- the download: -nand- for ubifs, -nor- for ubiblock F=https://github.com/OpenIPC/firmware/releases/download/latest default_url_is "$F/openipc.gk7205v500-nand-ultimate.tgz" ubi_setup ubifs +default_url_is "$F/openipc.ssc338q-nor-ultimate.tgz" ubi_setup ubiblock + +# --- on a SoC that moved to the kernel-in-rootfs layout, ubiblock is retired +reset_env; UBLOCK_SOC=gk7205v500 ubi_setup ubiblock +STUB_PIVOT_RC=0 +run -z --kernel="$UK" --rootfs="$US" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout with a separate kernel volume" && + nothing_ubi && ! handed_off && ! rebooted; then + ok "ubiblock on gk7205v500: refused before anything is touched, reinstall link given" +else + bad "ubiblock on a moved SoC must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi +reset_env; UBLOCK_SOC=gk7205v500 ubi_setup ubiblock +printf '::sysinit:/etc/init.d/rcS\n' > "$SB/etc/inittab" +set_mtd <<'EOF2' +dev: size erasesize name +mtd0: 000c0000 00020000 "boot" +mtd1: 00040000 00020000 "env" +mtd2: 07f00000 00020000 "ubi" +mtd3: 003e0000 0001f000 "kernel" +mtd4: 01f00000 0001f000 "rootfs" +mtd5: 00200000 0001f000 "rootfs_data" +EOF2 +run -z --kernel="$UK" --rootfs="$US" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "retired NAND layout" && nothing_ubi && ! rebooted; then + ok "ubiblock on gk7205v500 without ::restart: is refused too, not written through gluebi" +else + bad "old-inittab ubiblock on a moved SoC must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi + +# --- ubifs: a local kernel file cannot be written on its own +reset_env; ubi_setup ubifs +make_fit_soc "$UFIT" gk7205v500 +run -z --kernel="$UFIT" +if [ "$RC" -ne 0 ] && printf '%s' "$OUT" | grep -q "pass the rootfs.ubifs that carries it" && nothing_ubi && ! handed_off; then + ok "ubifs: --kernel=FILE alone is refused, not swapped for some other rootfs" +else + bad "ubifs --kernel=FILE alone must be refused, rc=$RC log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi + +# --- stage 2: settings that cannot be copied stop the run before any volume changes +reset_env; ubi_setup ubifs +RUN_ENV="$S2 update_rootfs=1 rootfs_file=$UFS STUB_UBIFS_RO_FAIL=1" +run +if printf '%s' "$OUT" | grep -q "Could not read the settings" && ! grep -qE "ubirmvol|ubirsvol|ubiupdatevol|ubimkvol" "$SB/tmp/flash.log" && rebooted; then + ok "stage 2: unreadable settings stop the run before any volume is touched" +else + bad "stage 2 settings read failure, log='$(cat "$SB/tmp/flash.log")' out='$OUT'" +fi # --- the unpack leaves rootfs.ubi (fresh-install image) in the archive reset_env; ubi_setup ubifs diff --git a/general/overlay/usr/sbin/sysupgrade b/general/overlay/usr/sbin/sysupgrade index 7a24ee3966..51dfc7d0c2 100755 --- a/general/overlay/usr/sbin/sysupgrade +++ b/general/overlay/usr/sbin/sysupgrade @@ -178,6 +178,8 @@ ubifs_rootfs_room() { usable=$(cat "$r/usable_eb_size" 2>&3) && ebs=$(cat "$r/reserved_ebs" 2>&3) || return 0 [ -n "$ubi_data_dev" ] && debs=$(cat "$d/reserved_ebs" 2>&3 || echo 0) [ "1" = "$clear_overlay" ] || keep=$((${overlay_kb:-0} * 1024)) + # $1 replaces the settings estimate, for the caller that has measured them. + [ -n "$1" ] && keep=$1 echo $(((ebs + debs - UBIFS_OVERLAY_MIN_LEBS) * usable - keep)) } @@ -214,13 +216,27 @@ rewrite_ubifs() { mark_live_flash_dirty rm -f "$bak" if [ -n "$ubi_data_dev" ]; then + # Keeping the settings means having them in RAM before the volume + # goes. Any failure here stops the run while nothing is written -- + # die reboots into the system as it was; -n is the way to upgrade + # without them. The archive, not df, is what has to fit back: it + # counts sparse files at their full length, as extracting them will. if [ "1" != "$clear_overlay" ]; then mkdir -p "$mnt" - if mount -t ubifs -o ro "$name:rootfs_data" "$mnt" 2>&3; then - tar -C "$mnt" -cf "$bak" . 2>&3 || - { echo_c 33 "Warning: could not copy the settings; they will be reset."; rm -f "$bak"; } + mount -t ubifs -o ro "$name:rootfs_data" "$mnt" 2>&3 || + die "Could not read the settings to carry them over. Nothing was written; -n upgrades without them." + if ! tar -C "$mnt" -cf "$bak" . 2>&3; then busybox umount "$mnt" 2>&3 + rm -f "$bak" + die "Could not copy the settings into RAM. Nothing was written; -n upgrades without them." fi + busybox umount "$mnt" 2>&3 + local room=$(ubifs_rootfs_room 0) + local need=$(( size + $(stat -c %s "$bak" 2>/dev/null || echo 0) )) + [ -z "$room" ] || [ "$need" -le "$room" ] || { + rm -f "$bak" + die "The new rootfs and the settings ($(( need / 1024 )) KB together) do not fit the UBI device ($(( room / 1024 )) KB). Nothing was written; -n upgrades without the settings." + } fi set_progress ubirmvol "$ubi" -N rootfs_data || die "Removing the settings volume failed." @@ -526,13 +542,17 @@ is_ubifs() { } # The rootfs format has to be the one this camera's kernel command line mounts: -# a squashfs written into the volume of a root=ubi0:rootfs camera flashes -# cleanly and never mounts again. The kernel's rootfstype is fixed by the -# bootloader environment, which no upgrade rewrites. +# a squashfs written into the volume of a root=ubi0:rootfs camera, or a UBIFS +# into a ubiblock one, flashes cleanly and never mounts again. The kernel's +# rootfstype is fixed by the bootloader environment, which no upgrade rewrites. check_rootfs_format() { + [ -n "$ubi_layout" ] || return 0 if [ "$ubi_layout" = "ubifs" ] && ! is_ubifs "$1"; then die "This camera boots a UBIFS rootfs, and $1 is not one. Nothing was written." fi + if [ "$ubi_layout" = "ubiblock" ] && is_ubifs "$1"; then + die "This camera boots a squashfs rootfs through ubiblock, and $1 is a UBIFS image. Nothing was written." + fi return 0 } @@ -1160,9 +1180,9 @@ remote_length_kb() { download_firmware() { - # A UBIFS NAND layout installs from the -nand- package, every other camera - # from the NOR package. A NAND camera on a retired layout has been refused - # before this. + # A UBIFS NAND layout installs from the -nand- package; every other camera, + # NAND ones included (a ubiblock rootfs holds the NOR artifacts verbatim), + # from the NOR package. [ "$flash_type" = "nand" ] && [ "$(pkg_flash)" = "nor" ] && echo_c 31 "\nNote: the updater uses the NOR package for updating NAND" echo_c 33 "\nFirmware" @@ -2135,6 +2155,16 @@ get_system_info() { [ -z "$model" ] && die "Cannot determine SoC: no BUILD_PLATFORM and no U-Boot 'soc'" resolve_model_alias detect_ubi_layout + # A ubiblock camera running an image from before the hand-off has no + # ::restart: entry, and only the upgrade being run would ship one. Its + # rootfs was written through gluebi's mtd until now, which works with the + # volume mounted (gluebi writes beside ubiblock's reader), so keep doing + # that rather than refusing every upgrade. The next image brings the + # entry, and the UBI path, with it. + if [ "$ubi_layout" = "ubiblock" ] && ! nand_layout_moved && ! inittab_hands_off && + [ "/dev/" != "$(get_device "rootfs")" ]; then + ubi_layout= + fi if [ -n "$ubi_layout" ]; then # Resolved here, on the whole system: the flash phase may run in a ramfs # where /sys came across best-effort, or not at all. @@ -2186,7 +2216,8 @@ get_system_info() { # its own. Retired: a run that would write it is refused, and # the camera is reinstalled from U-Boot. # ubiblock kernel volume = uImage, rootfs volume = squashfs mounted through -# ubiblock (root=/dev/ubiblockX_Y). Retired like ubifs-kvol. +# ubiblock (root=/dev/ubiblockX_Y). Its volumes hold exactly the +# NOR artifacts, so it fetches the NOR package. # # The rootfs volume is in use for as long as the camera runs: UBIFS or # ubiblock holds it open under init's overlay root, and ubiupdatevol needs the @@ -2224,6 +2255,17 @@ is_split_nand() { grep -q '"kernel"$' /proc/mtd 2>&3 } +# The SoCs whose NAND images moved to the kernel-in-rootfs layout (ubifs). +# On these the older NAND layouts with a kernel volume of their own are +# retired and reinstalled; every other SoC keeps its ubiblock layout, which is +# still what its NAND images are (SigmaStar, Rockchip). +nand_layout_moved() { + case "$model" in + gk7205v500|gk7205v510|gk7205v530|hi3516ev200|hi3516ev300|hi3518ev300|hi3516dv200) return 0 ;; + esac + return 1 +} + # The package flavour this camera installs from, as the manifest spells it. pkg_flash() { [ "$ubi_layout" = "ubifs" ] && echo nand || echo nor @@ -2669,19 +2711,23 @@ fi # only empties rootfs_data, which this layout keeps in a UBI volume like any # other. if { [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; } && is_split_nand; then - die "This camera uses the retired split NAND layout (kernel in its own partition, UBIFS root beside it), which this upgrade cannot write safely. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/hisilicon/socs/$model -- nothing was written." + die "This camera uses the retired split NAND layout (kernel in its own partition, UBIFS root beside it), which this upgrade cannot write safely. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/$vendor/socs/$model -- nothing was written." +fi +# The NAND layouts with a kernel volume of their own are retired on the SoCs +# that moved to the kernel-in-rootfs one: current images for them carry the +# kernel inside the UBIFS rootfs and size the volumes to it. ubiblock stays +# for the rest (nand_layout_moved). +if { [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; } && + { [ "$ubi_layout" = "ubifs-kvol" ] || { [ "$ubi_layout" = "ubiblock" ] && nand_layout_moved; }; }; then + die "This camera uses the retired NAND layout with a separate kernel volume; current images carry the kernel inside the rootfs. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/$vendor/socs/$model -- nothing was written." fi -# Both NAND layouts with a kernel volume of their own are retired the same way: -# current images carry the kernel inside the UBIFS rootfs and size the volumes -# to it, which neither can take without its volumes being made anew. -case "$ubi_layout" in ubifs-kvol|ubiblock) - if [ "1" = "$update_kernel" ] || [ "1" = "$update_rootfs" ]; then - die "This camera uses the retired NAND layout with a separate kernel volume; current images carry the kernel inside the rootfs. Reinstall it from U-Boot with the current NAND instructions: https://openipc.org/cameras/vendors/$vendor/socs/$model -- nothing was written." - fi ;; -esac # On the ubifs layout the kernel is a file in the rootfs: there is no kernel # to write on its own, and either flag means writing the one image. if [ "$ubi_layout" = "ubifs" ] && [ "1" = "$update_kernel" ]; then + # A local kernel file cannot be written on its own: it would have to be + # inside the rootfs image. Say so rather than write some other rootfs. + [ -n "$kernel_file" ] && [ -z "$rootfs_file" ] && [ "1" != "$remote_update" ] && + die "On this camera the kernel lives inside the rootfs: pass the rootfs.ubifs that carries it with --rootfs=. Nothing was written." update_kernel=0 update_rootfs=1 echo_c 37 "\nThe kernel lives inside the rootfs on this camera: writing the rootfs."