diff --git a/.github/scripts/test_sysupgrade.sh b/.github/scripts/test_sysupgrade.sh index e9ad6e9548..64bec3cf62 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,21 @@ 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 "*) [ "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" ;; + esac + exit 0 ;; +esac case "${STUB_MOUNT:-ok}" in ok) mkdir -p "$target/etc" @@ -373,15 +389,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,19 +2077,32 @@ 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 - set_platform gk7205v500_ultimate ultimate - export STUB_VENDOR=goke STUB_SOC=gk7205v500 STUB_IMG_SOC=gk7205v500 + case "$1" in + ubifs|ubifs-kvol) set_cmdline "$CMDLINE_UBIFS" ;; + *) set_cmdline "$CMDLINE_UBIBLOCK" ;; + esac + # 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"; } @@ -2095,7 +2127,7 @@ 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_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 @@ -2104,13 +2136,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 -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" +make_fit_soc "$UFIT" gk7205v500 +STUB_PIVOT_RC=0 +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 + 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 +2212,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 +2231,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 +2319,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,10 +2356,134 @@ 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 -default_url_is "$F/openipc.gk7205v500-nor-ultimate.tgz" ubi_setup ubiblock +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/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..51dfc7d0c2 100755 --- a/general/overlay/usr/sbin/sysupgrade +++ b/general/overlay/usr/sbin/sysupgrade @@ -167,6 +167,100 @@ 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)) + # $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)) +} + +# 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 + # 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" + 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." + 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 +364,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 +691,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 +1852,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 @@ -2061,7 +2161,7 @@ get_system_info() { # 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 && + if [ "$ubi_layout" = "ubiblock" ] && ! nand_layout_moved && ! inittab_hands_off && [ "/dev/" != "$(get_device "rootfs")" ]; then ubi_layout= fi @@ -2071,6 +2171,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 +2202,70 @@ 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 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 @@ -2358,11 +2502,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 +2707,32 @@ 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/$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 +# 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." +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