Skip to content

sysupgrade: NAND user-data preservation + SquashFS-on-UBI for HiSilicon#2022

Open
widgetii wants to merge 5 commits intomasterfrom
nand-sysupgrade-preserve
Open

sysupgrade: NAND user-data preservation + SquashFS-on-UBI for HiSilicon#2022
widgetii wants to merge 5 commits intomasterfrom
nand-sysupgrade-preserve

Conversation

@widgetii
Copy link
Copy Markdown
Member

Supersedes #2012, which is being closed in favour of this comprehensive rework.

Why

#2012 added basic NAND support to sysupgrade but every -r wiped the entire UBI MTD and the user's overlay with it. The maintainer's review captured the dilemma:

«оно там ломает все. или надо переходить на пивот везде и отказываться от gluebi или использовать как есть»

This PR takes the "do it properly" path. The changes here let a NAND camera survive sysupgrade -r with config intact, the same way NOR cameras already do — and they bring the HiSilicon NAND firmware in line with the architecture sigmastar/rockchip already use.

Two coupled root causes need to be fixed together; neither stands on its own:

  1. Wrong upgrade primitive. flash_eraseall + nandwrite on a UBI MTD leaves UBI's autoresize unable to reclaim tail PEBs cleanly. On reboot the kernel panics with bad image sequence number in PEB N. That brick is reproducible on this branch — see the recovery write-up below.
  2. Wrong rootfs layout. Default HiSilicon ubinize.cfg shipped a single read-write UBIFS rootfs volume, so even with a correct primitive there was no clean place to land preserved user data: any upgrade rewrote the same filesystem the user was editing.

What this PR achieves

  • sysupgrade -r on NAND preserves user config across upgrades by default. -n opts in to wiping. (Same semantics as NOR.)
  • HiSilicon NAND ships SquashFS-on-UBI rootfs + UBIFS rootfs_data for the overlay — matching sigmastar (ubinize_sigmastar.cfg) and rockchip (ubinize_rockchip.cfg).
  • sysupgrade uses ubiformat -y -f (the OpenWrt nand.sh pattern), which handles erase counters and writes one fresh image_seq across every PEB. No more image_seq panics.
  • Existing UBIFS-rootfs cameras get a one-shot bootargs migration via fw_setenv — runs after the flash succeeds so a flash failure can never leave the env pointing at a non-existent rootfs.
  • /etc/sysupgrade.conf is a user-extensible keep-list (one path per line), mirroring OpenWrt.

How it works

[old camera] sysupgrade -r
    │
    ├─ tar /etc/passwd /etc/dropbear /etc/majestic.yaml /root … → /tmp/sysupgrade.tgz
    ├─ compute new bootargs (idempotent; empty if no change)
    ├─ pivot_root → tmpfs (busybox + ubiformat + fw_setenv + fw_env.config + rootfs.ubi + sysupgrade.tgz)
    │
    ├─ ubidetach
    ├─ ubiformat -y -f rootfs.ubi /dev/mtdN          # consistent image_seq across all PEBs
    ├─ fw_setenv bootargs "<migrated>"               # only after flash success
    ├─ ubiattach + wait for autoresize to mat rootfs_data
    ├─ mount ubi0:rootfs_data /tmp/data
    ├─ cp sysupgrade.tgz /tmp/data/
    └─ reboot

[new firmware /init]
    ├─ mount ubi0:rootfs_data /overlay
    ├─ if /overlay/sysupgrade.tgz: tar -xzf … && rm -f      # one-shot, then no-op forever
    ├─ overlayfs lower=/ (squashfs ro) upper=/overlay (UBIFS rw)
    └─ pivot_root → /sbin/init

Reference: OpenWrt package/system/procd/files/nand.sh.

Upgrade steps for users

Online upgrade from a previous OpenIPC NAND camera

sysupgrade -k -r --force_all

That's it. The script:

  • backs up the keep-list paths,
  • replaces the rootfs UBI MTD via ubiformat,
  • migrates U-Boot bootargs automatically (root=ubi0:rootfs rootfstype=ubifsroot=/dev/ubiblock0_0 rootfstype=squashfs ubi.block=0,0),
  • stages the user-data tarball into rootfs_data,
  • reboots.

After reboot, the new firmware's /init extracts the tarball into the overlay and the camera comes up with config intact.

To wipe overlay on upgrade (factory-reset semantics)

sysupgrade -k -r -n --force_all

-n skips the keep-list step; rootfs_data is empty after the flash.

Custom keep-list

Drop additional paths to preserve into /etc/sysupgrade.conf (one path per line, files or directories):

/opt/myscripts/
/etc/cron.d/extra

Migration from the legacy UBIFS-rootfs layout

Existing deployed cameras don't ship ubiformat (it's introduced by this PR via BR2_PACKAGE_MTD=y). The first hop from old → new firmware therefore runs the legacy flash_eraseall + nandwrite path. This single upgrade is destructive to user data (the script logs a clear warning). Every subsequent upgrade on the new firmware preserves correctly because ubiformat is now installed.

The bootargs migration is idempotent (a re-run on already-migrated env is a no-op), so it's safe to invoke from automation.

Recovery if something goes wrong

If the camera fails to boot after upgrade, U-Boot TFTP recovery still works:

=> setenv serverip <your-tftp-host>
=> setenv ipaddr <camera-ip>
=> run urnand

urnand re-flashes the same rootfs.ubi.<soc> image; combined with this PR's ubiformat semantics it lands cleanly. The flash MTD partition is never touched until after ubiformat validates the input image, so a network/checksum failure during download cannot brick the camera.

Scope

Affected Layout sysupgrade flow
HiSilicon NAND ultimate (av100/av200, cv300, dv100, ev200/ev300, hi3518 ev200/ev300) switched to SquashFS-on-UBI preserving by default
Sigmastar / Rockchip NAND already SquashFS-on-UBI; layout untouched preserving by default (uses same code path)
All NOR boards unchanged unchanged

Out of scope (follow-ups)

  • Dropping CONFIG_MTD_UBI_GLUEBI from kernel configs — this PR removes sysupgrade's reliance on gluebi-exposed MTDs; unsetting the kconfig globally touches every NAND-capable kernel config and risks breaking unrelated consumers (notably the JFFS2-on-UBI fallback in /init).
  • Lite NAND variants — same recipe applies once defconfigs exist.

Files modified

File Change
general/scripts/ubifs/ubinize_hisilicon.cfg new — SquashFS rootfs + autoresize rootfs_data
br-ext-chip-hisilicon/configs/{hi3516av100,hi3516av200,hi3516cv300,hi3516dv100,hi3516ev200,hi3516ev300,hi3518ev200,hi3518ev300}_ultimate_defconfig repoint at new ubinize cfg + BR2_PACKAGE_MTD=y (ships ubiformat)
general/overlay/usr/sbin/sysupgrade build_keep_list + compute_new_bootargs helpers; do_update_rootfs_nand rewritten to ubiformat + tarball pattern
general/overlay/init regex broadened to enter UBI overlay branch on squashfs+ubi.block layouts; one-shot /overlay/sysupgrade.tgz extractor
general/overlay/etc/sysupgrade.conf new empty file with format docs
general/package/hisilicon-osdrv-hi3516ev200/files/script/set_allocator additive form — preserves all bootargs keys except mem/mmz_allocator/mmz so ubi.mtd=/ubi.block= survive load_hisilicon invocations

Test plan

  • make BOARD=hi3516av200_ultimate produces a rootfs.ubi.hi3516av200 whose first volume is SquashFS (verify with ubinize -v or unubi).
  • On a hi3516av200 NAND camera at known IP: customise (set root password, add /root/canary.txt, edit /etc/majestic.yaml, append /opt to /etc/sysupgrade.conf, drop /opt/marker.txt).
  • SCP the new sysupgrade to the camera; run sysupgrade -k -r --force_all; capture UART.
  • Verify on reboot: no bad image sequence number panic, root password persists, /root/canary.txt exists, /etc/majestic.yaml retains edits, /opt/marker.txt exists. ubinfo -a shows two volumes (rootfs SquashFS, rootfs_data autoresized). /proc/cmdline shows root=/dev/ubiblock0_0 rootfstype=squashfs. mount shows overlayfs with lowerdir=/ and upperdir=/overlay.
  • Wipe path: sysupgrade -k -r -n --force_all then verify root password reset and /root/canary.txt gone.
  • Idempotent re-run from new firmware: sysupgrade -k -r --force_allfw_setenv is a no-op, bootargs unchanged.
  • Brick recovery via U-Boot TFTP run urnand works (kept compatible).

Vixand and others added 3 commits April 26, 2026 14:54
The HiSilicon NAND build shipped a single read-write UBIFS rootfs volume,
which gives sysupgrade nothing to preserve and forces a destructive flash
of the whole UBI MTD. Other NAND vendors (sigmastar, rockchip) already
ship the upstream-recommended layout: a read-only SquashFS rootfs in one
UBI volume and a writable UBIFS rootfs_data volume that holds the
overlay and is left intact across upgrades.

This commit aligns HiSilicon with that layout:

- New ubinize_hisilicon.cfg: rootfs vol pointing at rootfs.squashfs and
  rootfs_data with vol_flags=autoresize. The default ubinize.cfg is left
  alone so rockchip rv1109/rv1126 (which still consume it) keep working.
- Eight ultimate-NAND HiSilicon defconfigs (av100/av200/cv300/dv100,
  ev200/ev300, hi3518 ev200/ev300) repointed at the new cfg.
- BR2_PACKAGE_MTD=y added to the same defconfigs so the firmware ships
  ubiformat — the only safe primitive for rewriting a UBI MTD without
  hitting the autoresize/image_seq trap that nandwrite triggers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous reconstruction kept only a hardcoded list of keys
(mem, console, panic, rootfstype, root, init, mtdparts, mmz_*) and
silently stripped anything else. ubi.mtd= and ubi.block= — required
for SquashFS-on-UBI boot — were among the casualties, so on the first
post-upgrade boot of an ev200/ev300/hi3518ev300 camera load_hisilicon
would call set_allocator and erase the migrated bootargs.

Switch to additive form: filter out only mem=/mmz_allocator=/mmz= (the
keys this script owns) and pass everything else through unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Existing NAND sysupgrade was destructive: every -r wiped the entire UBI
MTD and the overlay with it. Adopt OpenWrt's nand.sh pattern and let
HiSilicon NAND survive upgrades the same way NOR does.

Flow on a NAND camera with ubiformat available (= post-this-PR firmware):

  1. Build a tarball of /etc/passwd, /etc/dropbear, /etc/network,
     /etc/majestic.yaml, /root, ... — defaults plus anything in
     /etc/sysupgrade.conf or /lib/upgrade/keep.d/*.
  2. Compute migrated bootargs (root=/dev/ubiblock0_0 rootfstype=squashfs
     ubi.block=0,0 ubi.mtd=N,2048). Idempotent — empty result if already
     migrated.
  3. Pivot to a tmpfs new-root carrying busybox, ubiformat, fw_setenv,
     /etc/fw_env.config, the new rootfs.ubi, and the user-data tarball.
  4. ubidetach + 'ubiformat -y -f rootfs.ubi /dev/mtdN'. ubiformat is
     the only primitive that handles erase-counter preservation and
     writes a single fresh image_seq across every PEB, including the
     ones autoresize will later claim. nandwrite fails this on reboot
     with 'bad image sequence number' panics.
  5. fw_setenv bootargs (post-flash, so a flash failure cannot brick
     the env).
  6. ubiattach, wait for autoresize to materialise rootfs_data, mount
     it, drop /sysupgrade.tgz, ubidetach, reboot.

On first boot of the new firmware /init mounts rootfs_data, finds the
tarball, extracts it onto the overlay upper, removes the file, and
proceeds normally.

Existing UBIFS-rootfs cameras have no ubiformat. For that one-time
hop we fall back to flash_eraseall+nandwrite (the path PR #2012
introduced) and warn that user data is erased; subsequent upgrades on
the new firmware preserve it.

The previous PR-#2012 'overlay erased' warning is dropped — overlay is
preserved by default now. -n / --wipe_overlay opts in to wiping (it
skips the tarball step so rootfs_data is left empty after flash).

Other changes:

- general/overlay/init: regex broadened to also enter the UBI overlay
  branch on squashfs+ubi.block= cmdlines, and a one-shot
  /overlay/sysupgrade.tgz extractor added. The tarball is rm -f'd
  after extraction so subsequent boots are no-ops.
- general/overlay/etc/sysupgrade.conf: new empty file so users can
  drop additional paths to preserve without modifying the upgrader.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real-world hi3516av200 NAND upgrade exposed several issues in the
preserve-user-data path that the prior commits did not catch:

- Bootargs migration must also add init=/init. Without it, the kernel
  boots /sbin/init from squashfs directly and skips the overlayfs
  pivot — leaving / read-only and breaking everything that needs to
  write to /etc.
- busybox tar has no -z support; piping through gzip explicitly fixes
  both the keep-list creation (sysupgrade) and the legacy first-boot
  extraction (init).
- Run flash_eraseall before ubiformat so blocks UBI previously marked
  bad get force-erased and ubiformat does not skip them, which would
  otherwise leave stale EC headers with old image_seq and panic the
  kernel on next attach.
- Mirror the post-pivot script's stdout to /dev/console so the UART
  log captures it; otherwise the entire flash phase is invisible.
- Stage user-data directly into rootfs_data:/root (the overlayfs upper
  layer) via tar -xf instead of writing a single tarball file the
  busybox shell can not create on a freshly-mounted UBIFS volume.
  This makes /init's extraction hook redundant, so drop it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@widgetii
Copy link
Copy Markdown
Member Author

End-to-end test results on hi3516av200 NAND

Tested on a real hi3516av200 NAND camera via UART + SSH. The fix-up commit (ecdf7f90) lands several bugs that surfaced during testing.

Working ✅

  • make BOARD=hi3516av200_ultimate builds with the new SquashFS-on-UBI layout (rootfs.ubi 6.7 MB; rootfs.squashfs 6.4 MB; uImage 1.9 MB).
  • Legacy first-hop upgrade (camera without ubiformat) flashes new firmware via flash_eraseall + nandwrite. Camera reboots into the new layout.
  • compute_new_bootargs migrates root=ubi0:rootfs rootfstype=ubifsroot=/dev/ubiblock0_0 rootfstype=squashfs ubi.block=0,0 ubi.mtd=N,2048 init=/init. Idempotent on already-migrated cameras (verified — no-op re-run).
  • fw_setenv writes the migrated bootargs after a successful flash (binary + /etc/fw_env.config staged into tmpfs pre-pivot).
  • /init mounts ubi0:rootfs_data and overlays squashfs → camera comes up with overlay on / type overlay (rw, lowerdir=/, upperdir=/overlay/root). Confirmed via mount.
  • ubiformat -y -f (with leading flash_eraseall) writes consistent image_seq across all PEBs. No more bad image sequence number kernel panics on subsequent boots.
  • set_allocator's additive form preserves ubi.mtd=/ubi.block= across load_hisilicon runs.
  • BR2_PACKAGE_MTD=y ships /usr/sbin/ubiformat in the new firmware.

Known limitation ⚠️

The post-pivot UBIFS write that stages /tmp/sysupgrade.tgz into rootfs_data:/root returns EINVAL from the busybox shell on this kernel (3.18.20)/firmware combination. The /init overlayfs path itself works perfectly in normal runtime — only the post-pivot tmpfs context hits this. Investigation pointed at filesystem-level issues that would need significant kernel-side debugging to resolve cleanly.

The script logs Warning: rootfs_data mount failed; user data not preserved. when this happens; the rest of the upgrade still completes and the camera boots into the new firmware. No data destruction beyond the rootfs replacement itselfrootfs_data survives, just isn't pre-populated with preserved files.

Recommended follow-up

Drop the post-pivot UBIFS write entirely and switch to ubiupdatevol /dev/ubi0_0 < rootfs.squashfs to update only the rootfs volume, leaving rootfs_data untouched throughout. Requires shipping rootfs.squashfs.${soc} alongside rootfs.ubi.${soc} in the release tarball (small Makefile change to REPACK_FIRMWARE). With that pattern there is no UBIFS write during sysupgrade at all and user data is preserved by construction.

Net effect of this PR

Even with the known limitation, this PR is a strict improvement over PR #2012:

  1. Layout is correct (squashfs+overlay, matching sigmastar/rockchip).
  2. Flash mechanism is correct (ubiformat, no more image_seq panics).
  3. First-boot init pivots overlayfs cleanly.
  4. Bootargs migration is automatic and idempotent.
  5. The infrastructure for user-data preservation (/etc/sysupgrade.conf, keep-list, tarball) is in place — the follow-up just swaps the staging mechanism.

Brick recovery via U-Boot TFTP run urnand works (tested), with the caveat that the urnand env on existing deployed cameras targets 0x400000 while the current mtdparts has UBI at 0xA00000. Operators recovering an existing camera need to use explicit offsets:

=> tftpboot ${baseaddr} rootfs.ubi.${soc}
=> nand erase 0xa00000 0x7600000
=> nand write ${baseaddr} 0xa00000 ${filesize}
=> reset

The previous tarball-into-rootfs_data approach hit EINVAL writing files
through the busybox shell on the post-pivot tmpfs UBIFS mount. Switch
to the cleaner pattern: update only the rootfs UBI volume in place via
ubiupdatevol, leaving rootfs_data (and the overlay it backs) entirely
untouched.

End-to-end verified on hi3516av200 NAND:
  - root password set on running camera survives the upgrade,
  - /root/canary.txt and /opt/marker.txt persist,
  - /etc files modified through the overlay persist,
  - kernel rebuilds and reboots into the new build (uname -r shows the
    new SMP build number),
  - rootfs_data UBI volume keeps its 655-LEB autoresized size and its
    image_seq across the upgrade.

Implementation:
  - Makefile: NAND release tarball now also ships rootfs.squashfs.${soc}
    (extra-file slot added to PREPARE_REPACK / REPACK_FIRMWARE).
  - sysupgrade picks one of three modes for do_update_rootfs_nand:
      updatevol — when rootfs.squashfs is present in /tmp AND rootfs_data
                  already exists. ubidetach + ubiattach + ubiupdatevol on
                  /dev/ubi0_0. Fastest, preserves rootfs_data by
                  construction.
      ubiformat — first-hop migration from legacy UBIFS-rootfs firmware
                  to the new layout. Full UBI MTD rewrite (destructive
                  for rootfs_data, which does not yet exist on legacy
                  cameras anyway).
      legacy    — pre-this-PR firmware that does not ship mtd-utils;
                  flash_eraseall + nandwrite. Same one-time destruction
                  as ubiformat path; subsequent upgrades hit updatevol
                  and preserve.
  - The build_keep_list helper and /etc/sysupgrade.conf overlay file are
    dropped — the whole rootfs_data volume survives, so an explicit
    keep-list is not needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@widgetii
Copy link
Copy Markdown
Member Author

Update: user data preservation now works end-to-end ✅

Following maintainer review and the prior comment's recommended follow-up, switched to the single-volume update pattern: ubiupdatevol on /dev/ubi0_0 to rewrite only the rootfs squashfs, leaving the entire rootfs_data UBI volume — and the overlayfs upper layer it backs — untouched.

This sidesteps every UBIFS write quirk hit on the previous attempt and preserves user data by construction rather than by tarball stash.

What changed in commit 1353b24a

  • Makefile: NAND release tarball now also ships rootfs.squashfs.${soc} alongside rootfs.ubi.${soc} (extra-file slot added to PREPARE_REPACK / REPACK_FIRMWARE).
  • sysupgrade picks one of three upgrade modes for do_update_rootfs_nand:
    • updatevol — when rootfs.squashfs is in the release tarball AND rootfs_data already exists. ubidetach + ubiattach + ubiupdatevol /dev/ubi0_0. Fastest, preserves rootfs_data by construction. This is the steady-state path.
    • ubiformat — first-hop migration from legacy UBIFS-rootfs firmware to the new layout. Full UBI MTD rewrite. Destructive to rootfs_data (which doesn't exist on legacy cameras anyway, so nothing to preserve).
    • legacy — pre-this-PR firmware that doesn't ship mtd-utils; flash_eraseall + nandwrite. Same one-time destruction as ubiformat; subsequent upgrades hit updatevol.
  • The build_keep_list helper and /etc/sysupgrade.conf overlay file are dropped — with rootfs_data preserved wholesale, an explicit keep-list is unnecessary.

Verification on hi3516av200 NAND

Staged before upgrade:

  • root password changed to a non-default value
  • /root/canary.txt (custom file)
  • /opt/marker.txt (file in user-only directory)
  • /etc/persistent_test (overlay-modified system file)

After sysupgrade --archive=…:

  • ✅ SSH login with the custom password works
  • ✅ All three files present with original content and timestamps
  • uname -r shows the new kernel build (Up #10, was Dev #8)
  • ubinfo confirms rootfs_data is still 655 LEBs (its previously-autoresized size — the volume itself was never touched)
  • ✅ No image_seq panic on reboot
  • /proc/cmdline unchanged (camera was already migrated; compute_new_bootargs correctly returned empty)

Sysupgrade output excerpt:

RootFS
UBI partition: /dev/mtd3 (mtd3)
Update rootfs (NAND, updatevol) from /tmp/rootfs.squashfs.hi3516av200
Preparing tmpfs root for NAND upgrade...
Performing pivot_root to tmpfs and flashing NAND...
[post-pivot, on UART]
Detaching UBI from mtd3...
Re-attaching UBI to update rootfs volume in place...
ubiupdatevol /dev/ubi0_0 with rootfs.squashfs...
Rootfs volume updated; rootfs_data left intact.
NAND upgrade complete. Rebooting...

The "known limitation" note in the prior comment is now resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants