Skip to content

hi3516cv6xx: add the lite variant, and drop the digital TV stack to f… - #2447

Merged
openipc-ai merged 1 commit into
OpenIPC:masterfrom
eseverson:hi3516cv6xx-lite-variant
Sep 19, 2026
Merged

openipc-ai merged 1 commit into
OpenIPC:masterfrom
eseverson:hi3516cv6xx-lite-variant

Conversation

@eseverson

@eseverson eseverson commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Problem

cv6xx has no image for 8 MiB NOR parts. It was added as a lite/8 MB target in 679c2c7 and renamed to ultimate/16 MB two days later in 8d5d02d, because "the previous 8 MB lite variant cannot fit majestic + vendor MPP libs + 42 V5 kernel modules". 679c2c7 had already booked the choice — "follow-up will either rebalance to 16 MB or trim rootfs". The rebalance happened; the trim did not, and cameras on 8 MiB parts have had nothing to flash since.

This does the trim, against the budget the cv610 u-boot actually enforces. Its compiled-in env is 256k(boot),64k(env),2048k(kernel),5120k(rootfs); the kernel slot is read whole by sf read ${kernaddr} ${kernsize} and root is mounted at the fixed rootfs offset, so an image boots from the release layout only if the FIT is under 2048 KB and the squashfs under 5120 KB. The Makefile's ceiling for this family is FLASH_SIZE * 1024 on the combined blob, which measures neither; the 8 MiB arm now checks both halves against their slots, where a PR sees it.

Most of the kernel trim is one bug. The shared cv6xx kernel config builds 146 satellite and terrestrial television drivers into every image — DVB frontends (cx24116, stv0900, si2168), tuners (tda18271, xc5000, r820t), and the analog TV, radio and SDR stacks. Nothing in the tree can reach them: no defconfig enables gst1-plugins-bad, whose DVB plugins are the only DVB reference in userspace, and no overlay opens a /dev/dvb device. They are not there by choice. 679c2c7 regenerated this config from hi3516cv{608,610}_defconfig in the BSP, which sets CONFIG_MEDIA_SUPPORT=y and never mentions digital TV. In 5.10, MEDIA_DIGITAL_TV_SUPPORT and its siblings carry a prompt only if MEDIA_SUPPORT_FILTER is set, and otherwise default to y — so the expansion turned all six media classes on by itself. rv1106 builds clean by a different route (the Rockchip tree patches MEDIA_DIGITAL_TV_SUPPORT to carry an unconditional prompt); hi3519dv500 has the same root cause at a smaller scale (15 symbols) and is left alone.

The sensor path on this SoC is HiSilicon MPP through the out-of-tree open_*.ko modules, not V4L2, and nothing in the tree captures from a USB camera on it, so MEDIA_SUPPORT goes entirely rather than being filtered down to the UVC class. REGMAP_I2C goes with it: its only selector was dvb-frontends. In the same spirit, and for the 2048 KB slot:

  • the eBPF syscall — no bpftool, libbpf or cgroup user anywhere in the tree; classic BPF for tcpdump stays (NET selects BPF independently)
  • io_uring — no caller in musl, busybox or majestic
  • the NAND/UBI/UBIFS stack — no cv6xx NAND board exists in the tree and the family's repack is NOR-only
  • the NFS client — cv6xx cannot NFS-root (ROOT_NFS and IP_PNP are off) and nothing mounts one at runtime
  • the USB gadget stack, and ALSA, whose only user it was
  • the zstd compressor, which only UBIFS and CRYPTO_ZSTD pulled in

hi3519dv500, the other V5 family, already builds without NAND, media, sound, BPF, io_uring and zstd. zImage goes from 2722 KB to 2045 KB; the FIT lands at 2014 KB of its 2048.

The rootfs, to 5076 KB of its 5120:

  • 15 ultimate-only packages dropped (aws-webrtc, divinus, exfat + exfatprogs, iptables, lame, libwebsockets, mosquitto, motors, quirc, uacme, vtund, wireguard compat + tools, zerotier-one).
  • 1 MiB squashfs blocks instead of the 128 KiB default (measured on this rootfs: 128K 8704 KB, 256K 8512, 512K 8384, 1024K 8320). The price is about 2.5 MiB of RAM against 512K blocks, since the fragment cache and the xz dictionary scale with the block; the camera below has 19.9 MB available with them, and the 64 KB is an erase block this budget does not have.
  • Buildroot's squashfs EXTREME_COMP, which for xz on ARM is -Xbcj arm,armthumb: 52 KiB. The kernel's xz decoder already carries XZ_DEC_ARM and XZ_DEC_ARMTHUMB, so there is nothing to pay at read time.
  • The 42 vendor modules were installed unstripped: Buildroot's target-finalize skips *.ko on purpose and nothing else stripped them. --strip-unneeded, the operation INSTALL_MOD_STRIP=1 performs, takes 16 KB off the squashfs. Both cv6xx variants get that.
  • lite drops the eight modules nothing loads: load_hisilicon has no modprobe for adc, aiisp, devstat, spi_dma_transfer, user, user_proc or uvc, and open_svac3e — the SVAC3 encoder — is probed only on the 20g/00s/00g dies, for a codec majestic has no code for. 60 KB. ultimate keeps all of them.

Four package symbols ultimate never has to state are spelled out here, because it gets them by side effect and lite does not: quirc and uacme select libcurl, libwebsockets and mbedtls drag in zlib and the TLS backend. sysupgrade shells out to curl over https, so a lite image without them has a sysupgrade that cannot fetch its manifest. Enabling only the libcurl parent is not enough — that yields a curl linked TLS_NONE against an absent libz.

Kept, because majestic will not start without them: the HiSilicon audio/VQE libraries its DT_NEEDED pulls in, plus opus, ogg, libevent, libcurl and protobuf-c. dropbear, ipctool, yaml-cli and uboot-tools survive.

The kernel config is the family's shared one, so ultimate builds the same kernel. I built ultimate's from this branch: it differs from the lite kernel by the netfilter symbols the iptables package fixup switches on (IP_NF_IPTABLES, IP_NF_FILTER, NETFILTER_XTABLES; 7.8 KB of zImage) and nothing else. ci-matrix.py's self-test carries three hardcoded row counts that a new board changes; they are bumped here so the merge gate passes.

Hardware tested on

hi3516cv6xx (chip reports hi3516cv613, DDR3 128 MB), SIMICAM A314D / Vatilon H80, 8 MiB NOR, GC4653 sensor.

The camera runs the release image for this build as #2448's create_hisi assembles it: the published boot-hi3516cv610-20s-nor.bin at 0, this branch's FIT at 0x50000 and its squashfs at 0x250000, erased env, on the partition table that u-boot declares. All three were read back from flash byte-identical (md5s below). The env was left to u-boot's defaults and osmem set to 48M through fw_setenv, which is the one step every cv610 install does. Two things the board adds that are not in this PR: rootfs_data was pre-staged rather than left for /init to format, because this sensor has no driver in the tree (galaxycore_gc4653_2L is in the ev200 V4 list, not the cv6xx V5 SDK's six — ultimate has the same gap), so the overlay carries an out-of-tree libsns_gc4653_2l.so and this board's MIPI-port-1 bring-up scripts, plus the usual device files. Nothing in the squashfs is replaced by anything else.

An ultimate image cannot be booted here: 8 MiB part. What the board did exercise is the shared kernel config in full; ultimate's kernel differs from it by the three iptables symbols above.

Evidence

Clean build of this branch:

$ make BOARD=hi3516cv6xx_lite
- fitImage: [2014KB/2048KB]
- rootfs.squashfs: [5076KB/5120KB]
- firmware.bin: [7168KB/8192KB]

$ ls -l output/images/{zImage,fitImage,rootfs.squashfs,firmware.bin.hi3516cv6xx}
2045488 zImage
2063177 fitImage
5197824 rootfs.squashfs
7340032 firmware.bin.hi3516cv6xx

$ md5sum boot-hi3516cv610-20s-nor.bin output/images/fitImage output/images/rootfs.squashfs
37c9dd1798718e64371875a98d65ce4a  boot-hi3516cv610-20s-nor.bin   (github.com/openipc/firmware/releases/download/latest)
ee9b6cca762d36abb82fa8bc86229a8a  fitImage
7732a4955365664028859353f2d68a29  rootfs.squashfs

$ python3 .github/scripts/ci-matrix.py --self-test
ci-matrix: self-test ok (100 boards, 136 packages, 56 cases)

The kernel change, before and after, in the file this PR edits:

before:  grep -cE '^CONFIG_(DVB_|MEDIA_TUNER_)[A-Za-z0-9_]+=(m|y)'  ->  144
         grep -c '=m$'                                             ->  147
after:   grep -cE '^CONFIG_(DVB_|MEDIA_TUNER_)[A-Za-z0-9_]+=(m|y)'  ->    0
         grep -c '=m$'                                             ->    1   # CONFIG_FW_LOADER=m

On the camera, 2 minutes after the reboot that applied osmem:

# cat /proc/mtd
mtd0: 00040000 00010000 "u-boot"
mtd1: 00010000 00010000 "env"
mtd2: 00200000 00010000 "kernel"
mtd3: 00500000 00010000 "rootfs"
mtd4: 00700000 00010000 "firmware"
mtd5: 000b0000 00010000 "rootfs_data"

# cat /proc/cmdline
mem=48M console=ttyAMA0,115200 panic=20 root=/dev/mtdblock3 rootfstype=squashfs init=/init mtdparts=sfc:256k(u-boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)

# fw_printenv osmem totalmem socmodel
osmem=48M
totalmem=128M
socmodel=20s

# what is on the flash
dd if=/dev/mtd0 | head -c 241664  | md5sum  -> 37c9dd1798718e64371875a98d65ce4a   = boot-hi3516cv610-20s-nor.bin
dd if=/dev/mtd2 | head -c 2063177 | md5sum  -> ee9b6cca762d36abb82fa8bc86229a8a   = fitImage
dd if=/dev/mtd3 | head -c 5197824 | md5sum  -> 7732a4955365664028859353f2d68a29   = rootfs.squashfs

# cat /usr/lib/os-release | grep -E 'OPENIPC_VERSION|BUILD_'
OPENIPC_VERSION=2.6.09.18
BUILD_OPTION=lite
BUILD_PLATFORM=hi3516cv6xx_lite

# majestic --version
Lite HiSilicon (hi3516cv6xx), master+4832311, 2026-09-18 17:31

# lsmod | awk 'NR>1{print $1}' | sort | tr '\n' ' '        (34 loaded, 34 shipped, 0 dvb/tuner)
open_acodec open_adec open_aenc open_ai open_aio open_ao open_base open_chnl
open_h264e open_h265e open_isp open_ive open_jpege open_mipi_rx open_mmz open_osal
open_piris open_pm open_pwm open_rc open_rgn open_sensor_i2c open_sensor_spi
open_svp_npu open_sys open_sys_config open_vb open_vca open_venc open_vgs open_vi
open_vpp open_vpss open_wdt

# netstat -ltn | awk 'NR>2{print $4}' | sort -u
0.0.0.0:22
0.0.0.0:554
0.0.0.0:80

# head -4 /proc/umap/venc
      id   width  height   type  by_frame   sequence
       0    2560    1440   H264         y        773
       1     640     360   H264         y        387

# curl -o /dev/null -w '%{http_code} %{ssl_verify_result}' https://github.com/OpenIPC/firmware/releases/latest
302 0                       # the image's own curl, mbedtls and CA bundle: what sysupgrade uses
# curl -o /dev/null -w '%{http_code}' http://127.0.0.1/
200
$ ffprobe -rtsp_transport tcp -i rtsp://<camera>:554/stream=0
method OPTIONS failed: 401 Authentication required

# free
              total        used        free      shared  buff/cache   available
Mem:          43112       20008        9724         100       13380       19872

# df /overlay
/dev/mtdblock5             704       288       416  41% /overlay

The config was generated with olddefconfig against openipc/linux 5.10.221 and applied as a symbol-only edit: the diff is CONFIG_ lines only.

Scope

  • No kernel patches under general/package/all-patches/linux/
  • No files specific to a single retail camera model — the device's own prune list and overlay stay out
  • No probing or bring-up tooling
  • Nothing under general/overlay/ or a shared load_<vendor> script hardcodes a value specific to my board
  • Package sources unchanged; no version bumps
  • No LD_PRELOAD, no unbuildable binaries
  • New code is selected by a defconfig, and registered in ALL_BOARDS

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

PR Summary by Qodo

Add 8 MiB hi3516cv6xx lite image and trim unused kernel features

✨ Enhancement 🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds an 8 MiB hi3516cv6xx lite image with a deliberately reduced package set.
• Removes unused media, eBPF, io_uring, and Zstd features from shared cv6xx kernels.
• Registers the target in CI and updates matrix self-test expectations.
Diagram

graph TD
  CI["CI board matrix"] --> Lite["Lite defconfig"] --> Kernel["Shared kernel config"] --> Build["Buildroot build"] --> Image["Firmware image"] --> NOR[("8 MiB NOR")]
  Ultimate["Ultimate defconfig"] --> Kernel
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Maintain a lite-only kernel config
  • ➕ Isolates kernel trimming from the existing ultimate image.
  • ➕ Reduces immediate regression exposure for 16 MiB devices.
  • ➖ Duplicates a large generated kernel configuration.
  • ➖ Allows unused television drivers to remain in ultimate images.
  • ➖ Creates long-term drift between targets on the same SoC family.
2. Patch the kernel media Kconfig
  • ➕ Could make media-class selection explicit across all affected boards.
  • ➕ May prevent similar default-selection surprises in other configurations.
  • ➖ Introduces a broader kernel-source change for behavior supported by standard Kconfig options.
  • ➖ Expands scope and validation requirements beyond hi3516cv6xx.
  • ➖ Requires coordinating and maintaining a downstream kernel patch.

Recommendation: Keep the PR's dedicated lite defconfig and shared kernel trim. Using MEDIA_SUPPORT_FILTER addresses the stock Linux 5.10 selection behavior without a kernel patch, while sharing the corrected kernel avoids configuration duplication and removes unreachable drivers from both cv6xx variants. The shared impact should still receive an ultimate-image boot smoke test.

Files changed (3) +115 / -195

Enhancement (1) +100 / -0
hi3516cv6xx_lite_defconfigDefine the 8 MiB hi3516cv6xx lite firmware variant +100/-0

Define the 8 MiB hi3516cv6xx lite firmware variant

• Adds a Cortex-A7 Buildroot target with an intentionally reduced package set and explicit runtime dependencies for Majestic and sysupgrade. Configures 1 MiB SquashFS blocks and ARM XZ branch filters so the combined firmware fits 8 MiB NOR devices.

br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig

Other (2) +15 / -195
ci-matrix.pyRegister the hi3516cv6xx lite target in CI +4/-4

Register the hi3516cv6xx lite target in CI

• Adds hi3516cv6xx_lite to the board matrix. Updates self-test reachability counts for shared HiSilicon OpenSDK and Majestic files to account for the additional board.

.github/scripts/ci-matrix.py

hi3516cv6xx.generic.configRemove unused subsystems from the shared cv6xx kernel +11/-191

Remove unused subsystems from the shared cv6xx kernel

• Enables media filtering while retaining camera support and removes the unused analog TV, DVB, radio, SDR, tuner, and frontend stacks. Also disables eBPF syscalls, io_uring, and Zstd compression dependencies, reducing the kernel and module footprint for both lite and ultimate images.

br-ext-chip-hisilicon/board/hi3516cv6xx/hi3516cv6xx.generic.config

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Lite boards get no full flash image 🐞 Bug ≡ Correctness
Description
The release-image workflow only iterates ultimate for the cv6xx family, and create_hisi also
hardcodes every output to 16 MiB. Although the new matrix entry builds the lite firmware archive,
users with blank 8 MiB devices receive no correctly sized bootable NOR image from the image release.
Code

.github/scripts/ci-matrix.py[69]

+    "hi3516cv6xx_lite", "hi3516cv6xx_ultimate",
Evidence
The added matrix member makes hi3516cv6xx_lite a produced firmware target, but the separate image
workflow invokes cv6xx assembly only for ultimate. That assembler creates a fixed 16384 KiB image,
whereas the new defconfig explicitly targets an 8 MiB flash, so neither the omitted workflow path
nor its current sizing can publish the required lite full-flash image.

.github/scripts/ci-matrix.py[66-71]
.github/workflows/image.yml[121-153]
.github/workflows/image.yml[167-179]
br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[9-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `hi3516cv6xx_lite` target is built but omitted from the workflow that assembles complete NOR images. The existing assembler is also fixed at 16 MiB, so it cannot safely be reused for the 8 MiB target without parameterizing the output size.
## Fix Focus Areas
- .github/workflows/image.yml[121-178]
- br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[10-11]
## Recommended Fix
Add an image-size argument to `create_hisi` and use it for the erased output created by `dd`. Invoke the cv6xx assembly calls for both `lite` at 8192 KiB and `ultimate` at 16384 KiB, retaining the existing 320 KiB firmware offset and per-DDR bootloader variants.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. The release image has not been booted 🐞 Bug ☼ Reliability
Description
hi3516cv6xx_lite_defconfig defines the image’s SquashFS layout and package set, while the PR’s
stated hardware test used an additional excludes list and a wider local kernel trim that are not
part of this branch. That leaves the actual 8 MiB artifact, including its final size and
boot/runtime behavior, unverified before it is offered for cameras that have no rollback-safe staged
deployment.
Code

br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[R54-59]

+BR2_TARGET_ROOTFS_CPIO=y
+BR2_TARGET_ROOTFS_SQUASHFS=y
+BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y
+# 1 MiB blocks buy ~430 KiB over the 128 KiB default, measured across the
+# four sizes. The cost is decompression buffer per read on a 48 MiB part.
+BR2_TARGET_ROOTFS_SQUASHFS_BS_1024K=y
Evidence
The added defconfig changes the produced filesystem format and compression settings, and the shared
kernel configuration disables runtime interfaces used by every cv6xx variant. The PR description
explicitly says the flashed device used extra excludes and a wider trim outside this branch, so the
cited committed configuration was not the configuration exercised on hardware.

br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[54-65]
br-ext-chip-hisilicon/board/hi3516cv6xx/hi3516cv6xx.generic.config[164-170]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The hardware-tested firmware was built with local excludes and additional kernel changes that are not in this PR, so it does not validate the artifact generated by the committed `hi3516cv6xx_lite_defconfig` and shared kernel configuration.
## Fix Focus Areas
- br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[54-65]
- br-ext-chip-hisilicon/board/hi3516cv6xx/hi3516cv6xx.generic.config[164-170]
## Recommended Fix
Build the unmodified `hi3516cv6xx_lite` target from this branch, flash that exact `firmware.bin` to an 8 MiB device, and add the resulting artifact-size and boot/streaming verification output to the PR. If it does not fit or boot, make the required trimming changes in this branch rather than relying on local excludes or an uncommitted kernel configuration.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Lite cameras cannot upgrade online ✓ Resolved 🐞 Bug ≡ Correctness
Description
hi3516cv6xx_lite_defconfig sets only the child BR2_PACKAGE_LIBCURL_OPENIPC_CURL, while its
parent BR2_PACKAGE_LIBCURL_OPENIPC remains disabled and no TLS backend package is selected.
Kconfig therefore omits curl entirely, so the shared sysupgrade path cannot probe or download its
HTTPS manifest and release archive.
Code

br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[67]

+BR2_PACKAGE_LIBCURL_OPENIPC_CURL=y
Evidence
The curl binary setting is nested under the disabled parent package, and every available TLS backend
depends on its corresponding package; the new defconfig selects none of them. Other Hisilicon lite
configurations enable both the parent curl package and mbedTLS, while sysupgrade invokes curl
against HTTPS manifest and GitHub release URLs.

br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[62-84]
general/package/libcurl-openipc/Config.in[1-12]
general/package/libcurl-openipc/Config.in[43-74]
general/package/libcurl-openipc/libcurl-openipc.mk[43-79]
general/overlay/usr/sbin/sysupgrade[550-557]
general/overlay/usr/sbin/sysupgrade[634-650]
br-ext-chip-hisilicon/configs/hi3516cv500_lite_defconfig[53-65]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new lite defconfig enables only curl's child binary option, leaving the parent package and HTTPS support disabled. As a result, online sysupgrade cannot fetch manifests or firmware archives.
## Fix Focus Areas
- br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig[67-67]
## Recommended Fix
Add `BR2_PACKAGE_LIBCURL_OPENIPC=y` and `BR2_PACKAGE_MBEDTLS_OPENIPC=y` alongside the existing curl binary selection. Confirm the resolved Kconfig selects the mbedTLS curl backend, then rebuild and verify the image still fits in 8 MiB and can probe an HTTPS upgrade URL.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig
@eseverson
eseverson force-pushed the hi3516cv6xx-lite-variant branch from 135a055 to 5c9bf61 Compare September 18, 2026 22:05
@eseverson
eseverson marked this pull request as draft September 18, 2026 22:45
@eseverson
eseverson force-pushed the hi3516cv6xx-lite-variant branch from 5c9bf61 to 9e748ff Compare September 18, 2026 23:15
@eseverson
eseverson marked this pull request as ready for review September 18, 2026 23:24
Comment thread .github/scripts/ci-matrix.py
Comment thread br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 9e748ff

@openipc-ai openipc-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The media diagnosis is the good part of this and I could reproduce the reasoning: on stock 5.10 the five MEDIA_*_SUPPORT classes carry a prompt only under MEDIA_SUPPORT_FILTER and otherwise default y, so the BSP expansion really did turn 146 TV drivers on by itself. REGMAP_I2C falling out with them checks out too -- olddefconfig would have kept it if anything else selected it.

Gates pass here: ci-matrix.py --self-test -> ok (100 boards, 136 packages, 56 cases), and the kernel config path narrows to both cv6xx boards, so ultimate does get built. EXTREME_COMP -> -Xbcj arm,armthumb is confirmed in fs/squashfs/Config.in and the kernel has XZ_DEC_ARM/XZ_DEC_ARMTHUMB/XZ_DEC_BCJ, so that 52 KiB is free as claimed.

The thing the hardware test cannot see is the bootloader, which the PR is honest about. I decompressed the cv610 u-boot this firmware ships with (boot-hi3516cv610-*-nor.bin, a HiSilicon boot table plus a gzip member at offset 40128) and its compiled-in env is:

kernaddr=0x50000   kernsize=0x200000   rootaddr=0x250000   rootsize=0x500000
mtdparts=sfc:256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)
bootnor=... sf read ${baseaddr} ${kernaddr} ${kernsize}; bootm ${baseaddr}
bootargs=... root=/dev/mtdblock3 ...

Nothing rewrites it later -- set_allocator only edits the mmz half of bootargs. Against this build's own numbers:

this PR partition
fitImage 2496 KiB 2048 KiB ❌
rootfs.squashfs 5184 KiB 5120 KiB ❌
firmware.bin 7680 KiB 7168 KiB ❌

The tested camera does not exercise any of this: its vendor u-boot reports 192K(uboot),2496K(kernel),5184K(rootfs),320K(rootfs_data), which is sized to this exact build, so every bound happens to fit there and nowhere else.

Two consequences worth knowing. A camera running OpenIPC's own u-boot cannot sysupgrade to this build -- check_combined_fits measures the FIT against the kernel partition and dies with "the FIT ... is 2496 KB, /dev/mtd2 holds 2048 KB. Nothing was written." That is a safe failure, no half-written flash. And a fresh whole-chip flash of the assembled .bin would boot a FIT truncated at 2048 KiB by sf read.

This is not something this PR introduced. The published cv6xx ultimate blob has the same mismatch and worse (FIT 2709 KiB, rootfs at +2752 KiB where u-boot expects +2048, blob 10496 KiB against a 7168 KiB partition), and hi3519dv500 has the offset half of it too. It looks like the cv610 env needs fixing rather than this branch needing another 448 KiB of trim -- but since this PR is what turns that into a published 8 MiB image, it seems worth settling first. I have put the detail on #2448, whose create_hisi is where the layout is actually decided.

On the bot review: finding 3 (curl parent + TLS backend) is genuinely addressed, and the defconfig comment explaining why lite has to name zlib/mbedtls/libcurl by hand where ultimate gets them through quirc and uacme is exactly the kind of comment this tree wants. Finding 2 reads stale against the current description, which claims byte-identical md5s from a clean build with the device-specific files on the JFFS2 overlay rather than in the squashfs.

Smaller points inline.

Comment thread br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig
Comment thread br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig
Comment thread br-ext-chip-hisilicon/configs/hi3516cv6xx_lite_defconfig
Comment thread br-ext-chip-hisilicon/board/hi3516cv6xx/hi3516cv6xx.generic.config Outdated

@openipc-ai openipc-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correcting my own verdict: I asked for two things before merge in #2447 (review) and then submitted it as a comment. Restating them as the blocking pair, so the state matches the ask.

1. The shared kernel config reaches hi3516cv6xx_ultimate, and every piece of evidence here is lite. br-ext-chip-hisilicon/board/hi3516cv6xx/hi3516cv6xx.generic.config is the family's config, so the 16 MiB cameras lose the same 146 drivers plus BPF_SYSCALL, IO_URING and zstd. I looked for a reason that would not boot and did not find one, and ci-matrix.py --stdin does narrow to both boards so it gets compiled -- but compiling is not booting, and ultimate is the half of the family with cameras in the field. One boot of an ultimate image, with dmesg and a stream, closes it.

2. The lite artifact this PR adds cannot be installed on a cv6xx camera running OpenIPC's own u-boot. Its mtdparts give 2048k(kernel),5120k(rootfs); this build is a 2496 KiB FIT and a 5184 KiB rootfs, so sysupgrade's check_combined_fits dies with "the FIT ... is 2496 KB, /dev/mtd2 holds 2048 KB. Nothing was written," and a whole-chip flash of the assembled .bin boots a FIT truncated by sf read ${kernaddr} ${kernsize}.

I flagged in the first review that this mismatch is pre-existing -- ultimate has it and worse -- and I let that soften the verdict, which was the wrong call. That the cv610 env needs fixing is an argument for fixing the env, not for publishing a second image into a layout nothing fits. Either the env moves or the build fits 2048/5120; either way it wants settling before an 8 MiB image is offered to people whose recovery path is a clip.

Everything else in that review stays advisory: the block-size RAM trade, the CHECK_SIZE offset, and the EXTREME_COMP observation are all worth doing and none of them should hold this up.

The media work itself is good and I would like to see it land.

@eseverson
eseverson force-pushed the hi3516cv6xx-lite-variant branch from 9e748ff to 946afa4 Compare September 19, 2026 06:54
eseverson added a commit to eseverson/openipc-firmware that referenced this pull request Sep 19, 2026
create_hisi laid u-boot at 0 and the combined firmware.bin verbatim at the
firmware offset, on a canvas hardcoded to 16384 KiB. Two things were wrong with
that, one new and one old.

The new one: a lite variant is flashed to an 8 MiB part, and an image whose
tail runs past the end of the chip cannot be written to one. The part size is
now the sixth argument, defaulting to 16384 so every existing caller keeps its
behaviour, and the cv6xx loop emits both variants at the size each is built
for.

The old one: the boot images this job pairs with the firmware carry their
partition table in a compiled-in env, and nothing rewrites it later.
Decompressed from boot-hi3516cv610-*-nor.bin (a HiSilicon boot table with a
gzip'd u-boot behind it):

  cv6xx  256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)
         bootnor: sf read ${kernaddr}=0x50000 ${kernsize}=0x200000; root=/dev/mtdblock3
  dv500  512k(boot),256k(env),6144k(kernel),8192k(rootfs),14336k@0xC0000(firmware),-(rootfs_data)

So u-boot reads the kernel as one fixed slot and mounts root at a fixed offset
behind it. firmware.bin is the FIT with the squashfs packed at the next 64 KiB
boundary (board post-image.sh), which only agrees with that table when the FIT
happens to end exactly at the slot. On every published cv6xx and dv500 image it
does not: root=/dev/mtdblockN points 704 KiB before the squashfs on cv6xx
ultimate and 512 KiB past it on dv500, and the cv6xx FIT is larger than its
2048 KiB slot, so `sf read` hands bootm a truncated image. sysupgrade already
gets this right (do_update_firmware splits the blob at the FIT boundary and
writes each half to its partition); a fresh whole-chip flash of the release
.bin did not.

Do what sysupgrade does: split at the FIT boundary (the FIT's total size is the
big-endian word at byte 4; the squashfs length is bytes_used in its
superblock), put the FIT at the firmware offset and the rootfs at the rootfs
partition, and measure each piece against the partition it goes into -- u-boot
against the boot partition (the env sits behind it, so the firmware offset is
the wrong bound), the FIT against the kernel slot, the rootfs against the
rootfs slot, the firmware partition against the part. Each failure prints a
::error:: and fails the step rather than uploading something that boots a
truncated kernel or overwrites rootfs_data. The exact-size check on the
assembled canvas stays as the backstop.

The layout table is a case on $soc inside the job; adding a family means
adding its u-boot's env line there. With this in place the cv6xx ultimate image
on master fails the FIT check (2709 KiB in 2048) and dv500 ultimate fails the
rootfs check (8832 KiB in 8192) -- both are the same table disagreement, now
reported instead of published. OpenIPC#2447 fits both cv6xx slots.
@eseverson

Copy link
Copy Markdown
Contributor Author

Both blocking points addressed in 946afa4d; the description is rewritten around the budget you measured.

Fits the cv610 u-boot's slots. FIT 2014 KB of 2048, squashfs 5076 KB of 5120, firmware.bin 7168 KB. Kernel side, on top of BPF/io_uring/zstd: MEDIA_SUPPORT goes entirely (the filtered-down UVC class had no consumer on this family either), plus NAND/UBI/UBIFS, the NFS client, USB gadget and ALSA — each with its reason in the message; hi3519dv500 already builds without all but NFS. Rootfs side: the vendor modules were shipped unstripped (--strip-unneeded, both variants), and lite drops the eight modules load_hisilicon never loads, open_svac3e among them. The Makefile's 8 MiB cv6xx arm now checks fitImage against 2048 and rootfs.squashfs against 5120, per your inline note, so the next overrun fails in the board matrix.

Booted as the release image. The camera now runs exactly what #2448's assembler produces for this build: the published boot-hi3516cv610-20s-nor.bin at 0, erased env, this FIT at 0x50000, this squashfs at 0x250000. u-boot's own table came up in /proc/mtd, /init mounted rootfs_data, fw_setenv osmem 48M was the only setup, and it streams; u-boot, FIT and squashfs read back byte-identical. Full transcript in the description.

Ultimate. Cannot be booted here (8 MiB part). What I can offer: ultimate's kernel built from this branch differs from the booted lite kernel by exactly the three iptables symbols its package fixup enables (IP_NF_IPTABLES, IP_NF_FILTER, NETFILTER_XTABLES, 7.8 KB). The shared config change itself is what the board exercised. If someone with a 16 MiB board can do the one boot, the remaining delta is that.

On the block-size note: 1 MiB stays. Your arithmetic is right about the ~2.5 MiB; measured free with it is 19.9 MB available on mem=48M, and the 64 KB it buys is an erase block this budget does not have. EXTREME_COMP tree-wide is a good idea and a separate PR.

@openipc-ai openipc-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both blocking points are answered, and the second one is answered in the way I most wanted and least expected: the camera is now running the artifact the assembler produces, not a hand-laid approximation of it. Published 20s u-boot at 0, erased env, FIT at 0x50000, squashfs at 0x250000, u-boot's own table showing up in /proc/mtd. That was the whole gap, and it is closed.

FIT 2014 KB of 2048 and squashfs 5076 KB of 5120 both fit the slots the cv610 env declares, and the Makefile now says so on the 8 MiB arm, so the next overrun fails in the board matrix instead of in the release job. Leaving the 16 MiB arm on the whole-blob figure with the reason written down is the right call -- that one is a u-boot table question and a size check here could not settle it.

I checked the wider trim for anything it could strand rather than take the list on trust. No cv6xx defconfig selects usb-dual-role or ffmpeg-openipc, so the USB gadget and ALSA removals have no consumer on this family; there is no cv610 or cv608 device under devices/ in OpenIPC/builder that the NAND/UBI removal could leave without a filesystem; and ci-matrix.py --self-test still passes on 946afa4d.

Ultimate stays unbooted and I am not going to hold this for it. The lite kernel that did boot is the same one, the variants diverge by the iptables symbols its package fixup adds, and cv6xx ultimate cannot be assembled into a release image at all until the 7744 KiB rootfs question is settled -- so a boot test there is gated on work that is not this PR.

Three notes, none blocking.

The .ko strip comment overstates its precedent. INSTALL_MOD_STRIP=1 is not --strip-unneeded; the kernel's own mod_strip_cmd is $(STRIP) --strip-debug, which keeps the whole .symtab. --strip-unneeded is the more aggressive of the two and the modules demonstrably still load, so the code is fine -- but the comment claims an equivalence that is not there, and in this tree the comment is the reasoning. Worth saying which of the two the saving actually came from.

I am withdrawing my block-size suggestion. At 5076 of 5120 there are 44 KB left and 512 KiB blocks cost 64 KB, so what I proposed would now overflow the slot. The pinned squashfs cache is still ~5 MiB of a 48 MiB camera and that cost is real, but you have the measurements and the budget and I do not.

Both halves clear by about 2% -- 34 KB on the FIT, 44 KB on the rootfs, each just past the 32 KB headroom warning. That is the margin the warning exists to talk about, so this family is one ordinary shared-overlay commit from red in both directions at once.

Good work, and thank you for chasing the u-boot table rather than arguing with it.

cv6xx has no image for 8 MiB NOR parts. It was added as a lite/8 MB target in
679c2c7 and renamed to ultimate/16 MB two days later in 8d5d02d, because "the
previous 8 MB lite variant cannot fit majestic + vendor MPP libs + 42 V5 kernel
modules". 679c2c7 had already booked the choice -- "follow-up will either
rebalance to 16 MB or trim rootfs". The rebalance happened; the trim did not.

This does the trim, against the budget the cv610 u-boot actually enforces. Its
compiled-in env is 256k(boot),64k(env),2048k(kernel),5120k(rootfs); the kernel
slot is read whole by `sf read ${kernaddr} ${kernsize}` and root is mounted at
the fixed rootfs offset, so an image boots from the release layout only if the
FIT is under 2048 KB and the squashfs under 5120 KB. The Makefile's ceiling for
this family is FLASH_SIZE * 1024 on the combined blob, which measures neither;
the 8 MiB arm now checks both halves against their slots, where a PR sees it.

Most of the kernel trim is one bug. The shared cv6xx kernel config builds 146
satellite and terrestrial television drivers into every image -- DVB frontends
(cx24116, stv0900, si2168), tuners (tda18271, xc5000, r820t) and the analog TV,
radio and SDR stacks. Nothing in the tree can reach them: no defconfig enables
gst1-plugins-bad, whose DVB plugins are the only DVB reference in userspace,
and no overlay opens a /dev/dvb device. They are not there by choice. 679c2c7
regenerated this config from hi3516cv{608,610}_defconfig in the BSP, which sets
CONFIG_MEDIA_SUPPORT=y and never mentions digital TV. In 5.10,
MEDIA_DIGITAL_TV_SUPPORT and its siblings carry a prompt only if
MEDIA_SUPPORT_FILTER is set and otherwise default to y, so the expansion turned
all six media classes on by itself. Reproducible: a bare
`make hi3516cv610_defconfig` in openipc/linux yields 147 DVB and tuner symbols.
rv1106 builds clean by a different route -- the Rockchip tree patches
MEDIA_DIGITAL_TV_SUPPORT to carry an unconditional prompt; hi3519dv500 has the
same root cause at a smaller scale, 15 symbols, and is left alone here.

The sensor path on this SoC is HiSilicon MPP through the out-of-tree open_*.ko
modules, not V4L2, and nothing in the tree captures from a USB camera on it, so
MEDIA_SUPPORT goes entirely rather than being filtered down to the UVC class.
REGMAP_I2C goes with it: its only selector was dvb-frontends. In the same
spirit, and for the 2048 KB slot: the eBPF syscall (no bpftool, libbpf or
cgroup user anywhere in the tree; classic BPF for tcpdump stays), io_uring (no
caller in musl, busybox or majestic), the NAND/UBI/UBIFS stack (no cv6xx NAND
board exists in the tree and the family's repack is NOR-only), the NFS client
(cv6xx cannot NFS-root -- ROOT_NFS and IP_PNP are off -- and nothing mounts one
at runtime), the USB gadget stack and ALSA, whose only user it was, and the
zstd compressor, which only UBIFS and CRYPTO_ZSTD pulled in. hi3519dv500, the
other V5 family, already builds without NAND, media, sound, BPF, io_uring and
zstd. zImage goes from 2722 KB to 2045 KB; the FIT lands at 2014 KB of 2048.

The rootfs side, to 5076 KB of 5120:

  - 15 ultimate-only packages dropped: aws-webrtc, divinus, exfat + exfatprogs,
    iptables, lame, libwebsockets, mosquitto, motors, quirc, uacme, vtund,
    wireguard (compat + tools) and zerotier-one.
  - 1 MiB squashfs blocks instead of the 128 KiB default. Measured on this
    rootfs: 128K 8704 KB, 256K 8512, 512K 8384, 1024K 8320. The price is
    about 2.5 MiB of RAM against 512K blocks, since the fragment cache and the
    xz dictionary scale with the block; the camera below has 19.9 MB available
    with them, and the 64 KB is an erase block this budget does not have.
  - Buildroot's squashfs EXTREME_COMP, which for xz on ARM is
    `-Xbcj arm,armthumb`: 52 KiB. The kernel's xz decoder already carries
    XZ_DEC_ARM and XZ_DEC_ARMTHUMB, so there is nothing to pay at read time.
  - The 42 vendor modules were installed unstripped: Buildroot's
    target-finalize skips *.ko on purpose and nothing else stripped them.
    `--strip-unneeded`, the operation INSTALL_MOD_STRIP=1 performs, takes 16 KB
    off the squashfs. Both cv6xx variants get that.
  - lite drops the eight modules nothing loads: load_hisilicon has no modprobe
    for adc, aiisp, devstat, spi_dma_transfer, user, user_proc or uvc, and
    open_svac3e -- the SVAC3 encoder -- is probed only on the 20g/00s/00g dies,
    for a codec majestic has no code for. 60 KB. ultimate keeps all of them.

Four package symbols ultimate never has to state are spelled out here, because
it gets them by side effect and lite does not: quirc and uacme select libcurl,
libwebsockets and mbedtls drag in zlib and the TLS backend. sysupgrade shells
out to curl over https, so a lite image without them has a sysupgrade that
cannot fetch its manifest. Enabling only the libcurl parent is not enough --
that yields a curl linked TLS_NONE against an absent libz. zlib also settles
majestic's own libz.so.1 and libevent_mbedtls dependencies, which on this
family were previously left to the integrator to supply.

Kept, because majestic will not start without them: the HiSilicon audio/VQE
libraries its DT_NEEDED pulls in, opus, ogg, libevent, libcurl and protobuf-c.
dropbear, ipctool, yaml-cli and uboot-tools survive the trim.

The kernel config is the family's shared one, so ultimate builds the same
kernel; its own differs from lite's by the netfilter symbols the iptables
package fixup switches on (IP_NF_IPTABLES, IP_NF_FILTER, NETFILTER_XTABLES) and
nothing else. ci-matrix.py's self-test carries three hardcoded row counts that
a new board changes; they are bumped here so the merge gate passes.

The config was generated with olddefconfig against openipc/linux 5.10.221 and
then applied as a symbol-only edit: every comment, section header and blank
line is byte-identical to the file it replaces, and the diff contains nothing
but CONFIG_ lines. Section headers left standing with nothing under them are
what a regeneration would tidy; leaving them keeps this diff reviewable.
@openipc-ai
openipc-ai force-pushed the hi3516cv6xx-lite-variant branch from 946afa4 to 770560b Compare September 19, 2026 07:08

@openipc-ai openipc-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving after the rebase: my earlier approval was dismissed when the branch moved.

Rebased 946afa4d onto master aa495696 (which had picked up #2441, #2442, #2444 and #2452 since this branched) and force-pushed as 770560b3. Clean rebase -- master's #2444 touched the Makefile in a different arm, so no conflict, and the resulting patch is byte-identical to yours apart from the blob hashes in that hunk header. Authorship unchanged.

Gates re-run on the rebased tree: ci-matrix.py --self-test ok (100 boards, 136 packages, 56 cases), lint-workflow-shell.py --self-test passed, and all 147 shipped scripts parse under busybox ash both before and after comment stripping.

The workflow runs needed maintainer approval -- fork PR -- so I approved them; the matrix is running now. Nothing else from me.

eseverson added a commit to eseverson/openipc-firmware that referenced this pull request Sep 19, 2026
create_hisi laid u-boot at 0 and the combined firmware.bin verbatim at the
firmware offset, on a canvas hardcoded to 16384 KiB. Two things were wrong with
that, one new and one old.

The new one: a lite variant is flashed to an 8 MiB part, and an image whose
tail runs past the end of the chip cannot be written to one. The part size is
now the sixth argument, defaulting to 16384 so every existing caller keeps its
behaviour, and the cv6xx loop emits both variants at the size each is built
for.

The old one: the boot images this job pairs with the firmware carry their
partition table in a compiled-in env, and nothing rewrites it later.
Decompressed from boot-hi3516cv610-*-nor.bin (a HiSilicon boot table with a
gzip'd u-boot behind it):

  cv6xx  256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)
         bootnor: sf read ${kernaddr}=0x50000 ${kernsize}=0x200000; root=/dev/mtdblock3
  dv500  512k(boot),256k(env),6144k(kernel),8192k(rootfs),14336k@0xC0000(firmware),-(rootfs_data)

So u-boot reads the kernel as one fixed slot and mounts root at a fixed offset
behind it. firmware.bin is the FIT with the squashfs packed at the next 64 KiB
boundary (board post-image.sh), which only agrees with that table when the FIT
happens to end exactly at the slot. On every published cv6xx and dv500 image it
does not: root=/dev/mtdblockN points 704 KiB before the squashfs on cv6xx
ultimate and 512 KiB past it on dv500, and the cv6xx FIT is larger than its
2048 KiB slot, so `sf read` hands bootm a truncated image. sysupgrade already
gets this right (do_update_firmware splits the blob at the FIT boundary and
writes each half to its partition); a fresh whole-chip flash of the release
.bin did not.

Do what sysupgrade does: split at the FIT boundary (the FIT's total size is the
big-endian word at byte 4; the squashfs length is bytes_used in its
superblock), put the FIT at the firmware offset and the rootfs at the rootfs
partition, and measure each piece against the partition it goes into -- u-boot
against the boot partition (the env sits behind it, so the firmware offset is
the wrong bound), the FIT against the kernel slot, the rootfs against the
rootfs slot, the firmware partition against the part. Each failure prints a
::error:: and fails the step rather than uploading something that boots a
truncated kernel or overwrites rootfs_data. The exact-size check on the
assembled canvas stays as the backstop.

The layout table is a case on $soc inside the job; adding a family means
adding its u-boot's env line there. With this in place the cv6xx ultimate image
on master fails the FIT check (2709 KiB in 2048) and dv500 ultimate fails the
rootfs check (8832 KiB in 8192) -- both are the same table disagreement, now
reported instead of published. OpenIPC#2447 fits both cv6xx slots.
eseverson added a commit to eseverson/openipc-firmware that referenced this pull request Sep 19, 2026
create_hisi laid u-boot at 0 and the combined firmware.bin verbatim at the
firmware offset, on a canvas hardcoded to 16384 KiB. Two things were wrong with
that, one new and one old.

The new one: a lite variant is flashed to an 8 MiB part, and an image whose
tail runs past the end of the chip cannot be written to one. The part size is
now the sixth argument, defaulting to 16384 so every existing caller keeps its
behaviour, and the cv6xx loop emits both variants at the size each is built
for.

The old one: the boot images this job pairs with the firmware carry their
partition table in a compiled-in env, and nothing rewrites it later.
Decompressed from boot-hi3516cv610-*-nor.bin (a HiSilicon boot table with a
gzip'd u-boot behind it):

  cv6xx  256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)
         bootnor: sf read ${kernaddr}=0x50000 ${kernsize}=0x200000; root=/dev/mtdblock3
  dv500  512k(boot),256k(env),6144k(kernel),8192k(rootfs),14336k@0xC0000(firmware),-(rootfs_data)

So u-boot reads the kernel as one fixed slot and mounts root at a fixed offset
behind it. firmware.bin is the FIT with the squashfs packed at the next 64 KiB
boundary (board post-image.sh), which only agrees with that table when the FIT
happens to end exactly at the slot. On every published cv6xx and dv500 image it
does not: root=/dev/mtdblockN points 704 KiB before the squashfs on cv6xx
ultimate and 512 KiB past it on dv500, and the cv6xx FIT is larger than its
2048 KiB slot, so `sf read` hands bootm a truncated image. sysupgrade already
gets this right (do_update_firmware splits the blob at the FIT boundary and
writes each half to its partition); a fresh whole-chip flash of the release
.bin did not.

Do what sysupgrade does: split at the FIT boundary (the FIT's total size is the
big-endian word at byte 4; the squashfs length is bytes_used in its
superblock), put the FIT at the firmware offset and the rootfs at the rootfs
partition, and measure each piece against the partition it goes into -- u-boot
against the boot partition (the env sits behind it, so the firmware offset is
the wrong bound), the FIT against the kernel slot, the rootfs against the
rootfs slot, the firmware partition against the part. Each failure prints a
::error:: and fails the step rather than uploading something that boots a
truncated kernel or overwrites rootfs_data. The exact-size check on the
assembled canvas stays as the backstop.

The layout table is a case on $soc inside the job; adding a family means
adding its u-boot's env line there. With this in place the cv6xx ultimate image
on master fails the FIT check (2709 KiB in 2048) and dv500 ultimate fails the
rootfs check (8832 KiB in 8192) -- both are the same table disagreement, now
reported instead of published. OpenIPC#2447 fits both cv6xx slots.

A refusal must not take the other SoCs' images with it. The step runs under
Actions' default `bash -e`, and the Sigmastar, Allwinner and Ingenic images are
assembled before the HiSilicon loops, so a bare `return 1` there would end the
step with their .bin files in target/ and the upload never reached. The
HiSilicon calls collect their failures instead, the step exits 1 after the last
one so the run is red, and Upload runs on `always()` so what did assemble
ships.

@openipc-ai openipc-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is green on the rebased head: 121 checks pass, the only non-pass is the release-publish job that does not run on pull requests. Re-approving because my own thread replies each landed as a COMMENTED review and superseded the earlier approval.

@openipc-ai
openipc-ai merged commit 9dfe559 into OpenIPC:master Sep 19, 2026
122 of 123 checks passed
openipc-ai pushed a commit to eseverson/openipc-firmware that referenced this pull request Sep 19, 2026
create_hisi laid u-boot at 0 and the combined firmware.bin verbatim at the
firmware offset, on a canvas hardcoded to 16384 KiB. Two things were wrong with
that, one new and one old.

The new one: a lite variant is flashed to an 8 MiB part, and an image whose
tail runs past the end of the chip cannot be written to one. The part size is
now the sixth argument, defaulting to 16384 so every existing caller keeps its
behaviour, and the cv6xx loop emits both variants at the size each is built
for.

The old one: the boot images this job pairs with the firmware carry their
partition table in a compiled-in env, and nothing rewrites it later.
Decompressed from boot-hi3516cv610-*-nor.bin (a HiSilicon boot table with a
gzip'd u-boot behind it):

  cv6xx  256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)
         bootnor: sf read ${kernaddr}=0x50000 ${kernsize}=0x200000; root=/dev/mtdblock3
  dv500  512k(boot),256k(env),6144k(kernel),8192k(rootfs),14336k@0xC0000(firmware),-(rootfs_data)

So u-boot reads the kernel as one fixed slot and mounts root at a fixed offset
behind it. firmware.bin is the FIT with the squashfs packed at the next 64 KiB
boundary (board post-image.sh), which only agrees with that table when the FIT
happens to end exactly at the slot. On every published cv6xx and dv500 image it
does not: root=/dev/mtdblockN points 704 KiB before the squashfs on cv6xx
ultimate and 512 KiB past it on dv500, and the cv6xx FIT is larger than its
2048 KiB slot, so `sf read` hands bootm a truncated image. sysupgrade already
gets this right (do_update_firmware splits the blob at the FIT boundary and
writes each half to its partition); a fresh whole-chip flash of the release
.bin did not.

Do what sysupgrade does: split at the FIT boundary (the FIT's total size is the
big-endian word at byte 4; the squashfs length is bytes_used in its
superblock), put the FIT at the firmware offset and the rootfs at the rootfs
partition, and measure each piece against the partition it goes into -- u-boot
against the boot partition (the env sits behind it, so the firmware offset is
the wrong bound), the FIT against the kernel slot, the rootfs against the
rootfs slot, the firmware partition against the part. Each failure prints a
::error:: and fails the step rather than uploading something that boots a
truncated kernel or overwrites rootfs_data. The exact-size check on the
assembled canvas stays as the backstop.

The layout table is a case on $soc inside the job; adding a family means
adding its u-boot's env line there. With this in place the cv6xx ultimate image
on master fails the FIT check (2709 KiB in 2048) and dv500 ultimate fails the
rootfs check (8832 KiB in 8192) -- both are the same table disagreement, now
reported instead of published. OpenIPC#2447 fits both cv6xx slots.

A refusal must not take the other SoCs' images with it. The step runs under
Actions' default `bash -e`, and the Sigmastar, Allwinner and Ingenic images are
assembled before the HiSilicon loops, so a bare `return 1` there would end the
step with their .bin files in target/ and the upload never reached. The
HiSilicon calls collect their failures instead, the step exits 1 after the last
one so the run is red, and Upload runs on `always()` so what did assemble
ships.
openipc-ai pushed a commit to eseverson/openipc-firmware that referenced this pull request Sep 19, 2026
create_hisi laid u-boot at 0 and the combined firmware.bin verbatim at the
firmware offset, on a canvas hardcoded to 16384 KiB. Two things were wrong with
that, one new and one old.

The new one: a lite variant is flashed to an 8 MiB part, and an image whose
tail runs past the end of the chip cannot be written to one. The part size is
now the sixth argument, defaulting to 16384 so every existing caller keeps its
behaviour, and the cv6xx loop emits both variants at the size each is built
for.

The old one: the boot images this job pairs with the firmware carry their
partition table in a compiled-in env, and nothing rewrites it later.
Decompressed from boot-hi3516cv610-*-nor.bin (a HiSilicon boot table with a
gzip'd u-boot behind it):

  cv6xx  256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)
         bootnor: sf read ${kernaddr}=0x50000 ${kernsize}=0x200000; root=/dev/mtdblock3
  dv500  512k(boot),256k(env),6144k(kernel),8192k(rootfs),14336k@0xC0000(firmware),-(rootfs_data)

So u-boot reads the kernel as one fixed slot and mounts root at a fixed offset
behind it. firmware.bin is the FIT with the squashfs packed at the next 64 KiB
boundary (board post-image.sh), which only agrees with that table when the FIT
happens to end exactly at the slot. On every published cv6xx and dv500 image it
does not: root=/dev/mtdblockN points 704 KiB before the squashfs on cv6xx
ultimate and 512 KiB past it on dv500, and the cv6xx FIT is larger than its
2048 KiB slot, so `sf read` hands bootm a truncated image. sysupgrade already
gets this right (do_update_firmware splits the blob at the FIT boundary and
writes each half to its partition); a fresh whole-chip flash of the release
.bin did not.

Do what sysupgrade does: split at the FIT boundary (the FIT's total size is the
big-endian word at byte 4; the squashfs length is bytes_used in its
superblock), put the FIT at the firmware offset and the rootfs at the rootfs
partition, and measure each piece against the partition it goes into -- u-boot
against the boot partition (the env sits behind it, so the firmware offset is
the wrong bound), the FIT against the kernel slot, the rootfs against the
rootfs slot, the firmware partition against the part. Each failure prints a
::error:: and fails the step rather than uploading something that boots a
truncated kernel or overwrites rootfs_data. The exact-size check on the
assembled canvas stays as the backstop.

The layout table is a case on $soc inside the job; adding a family means
adding its u-boot's env line there. With this in place the cv6xx ultimate image
on master fails the FIT check (2709 KiB in 2048) and dv500 ultimate fails the
rootfs check (8832 KiB in 8192) -- both are the same table disagreement, now
reported instead of published. OpenIPC#2447 fits both cv6xx slots.

A refusal must not take the other SoCs' images with it. The step runs under
Actions' default `bash -e`, and the Sigmastar, Allwinner and Ingenic images are
assembled before the HiSilicon loops, so a bare `return 1` there would end the
step with their .bin files in target/ and the upload never reached. The
HiSilicon calls collect their failures instead, the step exits 1 after the last
one so the run is red, and Upload runs on `always()` so what did assemble
ships.
@eseverson
eseverson deleted the hi3516cv6xx-lite-variant branch September 19, 2026 15:53
openipc-ai added a commit that referenced this pull request Sep 20, 2026
The bounds checks #2448 added ran for the first time on 2026-09-19 and
refused six images: hi3516cv6xx ultimate on all four DDR binnings, and
hi3519dv500 ultimate on both. That is not a regression #2448 introduced --
it is the table disagreement the published images already carried, now
reported instead of shipped -- but it leaves the job red on every run, and
re-running against a fresh nightly does not clear it.

That run assembled from nightly-20260918-49908b5, because the build had
failed and the manifest never advanced, so cv6xx reported a FIT overflow
from a blob predating #2447's kernel trim. Measured against the current
nightly with create_hisi's own arithmetic, the error changes but does not
go away:

  hi3516cv6xx_lite      FIT 2048/2048 ok   rootfs 5088/5120 ok
  hi3516cv6xx_ultimate  FIT 2048/2048 ok   rootfs 7096/5120  over by 1976 KiB
  hi3519dv500_ultimate  FIT 5632/6144 ok   rootfs 8820/8192  over by  628 KiB

The cause is that there is one boot binary per DDR binning, not one per
part size, and its compiled-in mtdparts is the same on an 8 MiB part and a
16 MiB one. Decompressing the gzip member inside the shipped u-boots
(offset 40128 for cv610, 84768 for hi3519dv500) gives:

  cv610/cv608  256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware)
  hi3519dv500  512k(boot),256k(env),6144k(kernel),8192k(rootfs),14336k@0xC0000(firmware)

The cv6xx ultimate blob is 9152 KiB and the dv500 one 14464 KiB -- each
larger than the whole firmware partition, never mind the rootfs slot inside
it. FLASH_SIZE="16" buys them nothing, because the rest of the chip is
rootfs_data. No trim closes a 1976 KiB gap, so until a u-boot ships whose
table matches a 16 MiB part there is nothing to assemble.

So emit cv6xx lite only, and drop the dv500 loop, which has no lite variant
to fall back on. Both comments carry the partition numbers so restoring a
loop is a two-line change. #2447 fits both cv6xx lite slots exactly (2048 in
2048, 5088 in 5120) and that image assembles and boots today.

The .tgz for both variants still publishes from the build job, so
sysupgrade is unaffected -- it splits the blob at the FIT boundary and
writes each half to its own partition, and refuses an oversized half rather
than mis-landing it.
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