From af81f00e2cadb67f16284362a89db74ccf08f0ab Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:15:19 +0300 Subject: [PATCH] XM: take the split firmware.bin when a board publishes no fixed-slot package Since OpenIPC/firmware#2666 a board whose rootfs outgrows its 5 MiB slot ships only openipc.-nor-lite-split.tgz. gk7201v200 is about to (OpenIPC/firmware#2729: the G3-WQ-G's Wi-Fi driver takes it to 5340 KB), and the fetch step's plain wget of the fixed-slot package would then fail the whole release run. The fetch step now tries both packages for every SoC, and _xm2oipc.sh takes the fixed-slot one when it is there, else the split one. The split firmware.bin -- the uImage padded to 64 KiB, then the squashfs -- is cut at the squashfs and burned as the same uImage.img and rootfs.img, so InstallDesc and the env are unchanged and the flash holds firmware.bin byte for byte from the kernel's address. rootfs_data is blanked from its end. The kernel recognises the layout and ignores the env's mtdparts and root= (OpenIPC/linux#140). Checked with the 2026-10-28 nightly and the stock G3-WQ-G image (000929QB, V5.03.C01), each package's images applied to an 8 MB flash the way "Burn" writes them: - split only: firmware.bin at 0x50000 byte-identical, 0x700000 to the end blank, U-Boot untouched. In qemu-hisilicon's gk7201v200 the stock XM U-Boot boots it: "sfc: split NOR layout, device tree partitions", "root=/dev/mtdblock3 ignored", root on 31:4, login. The overlay gets its jffs2 at 0x700000, and a second boot comes up clean. - both packages: the fixed-slot one is used, and the flash is byte-identical to what main's script produces. Not run on a camera, and the stock upgrader itself is not emulated. --- .github/workflows/_xm2oipc.sh | 41 +++++++++++++++++++++++++--- .github/workflows/xm.yml | 51 ++++++++++++++++++++--------------- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/.github/workflows/_xm2oipc.sh b/.github/workflows/_xm2oipc.sh index 3eece92..9c1404a 100755 --- a/.github/workflows/_xm2oipc.sh +++ b/.github/workflows/_xm2oipc.sh @@ -106,7 +106,6 @@ SOC="${SOC:-hi3516ev200}" [[ ${SENSOR} ]] && SENSOR="sensor=${SENSOR}" FLASH_SIZE="${FLASH_SIZE:-0x800000}" -ROOTFS_DATA="${ROOTFS_DATA:-$(($FLASH_SIZE - $ROOTFS_E ))}" WORKDIR="workdir" OUTPUTDIR="${OUTPUTDIR:-..}" @@ -118,10 +117,44 @@ IFS=" " mkdir -p ${WORKDIR} mkdir -p ${OUTPUTDIR} -tar -xvz -f openipc.${SOC}-nor-${RELEASE:-lite}.tgz -C ${WORKDIR}/ --exclude "*.md5sum" || exit 1 +PACKAGE=openipc.${SOC}-nor-${RELEASE:-lite} +if [ -f ${PACKAGE}.tgz ]; then + tar -xvz -f ${PACKAGE}.tgz -C ${WORKDIR}/ --exclude "*.md5sum" || exit 1 + + # Check if give files exceed partition boundaries + [[ $(stat --printf="%s" ${WORKDIR}/rootfs*) -gt $(($ROOTFS_E - $ROOTFS_A)) ]] || [[ $(stat --printf="%s" ${WORKDIR}/uImage*) -gt $(($KERNEL_E - $KERNEL_A)) ]] && echo "Filesize exceeds boundaries" && exit 1 +elif [ -f ${PACKAGE}-split.tgz ]; then + # The split NOR layout (OpenIPC/firmware#2668): firmware.bin is the uImage, + # padded to 64 KiB, then the squashfs, written whole at the kernel's address. + # The kernel finds the squashfs after itself and takes rootfs_data from the + # rest of the chip, whatever mtdparts and root= the env below passes, so a + # board ships this once its rootfs outgrows the fixed slot. It is burned as + # the same two images as the fixed slots, cut at the squashfs, which lands + # firmware.bin on the flash byte for byte; only the boundaries move. + tar -xvz -f ${PACKAGE}-split.tgz -C ${WORKDIR}/ --exclude "*.md5sum" || exit 1 + FIRMWARE=$(ls ${WORKDIR}/firmware.bin*) + FIRMWARE_SIZE=$(stat --printf="%s" ${FIRMWARE}) + # The uImage header's data size (bytes 12..15, big-endian), plus the 64-byte + # header, in whole 64 KiB blocks: where the squashfs starts. + [ "$(od -An -tx1 -N4 ${FIRMWARE} | tr -d ' \n')" = "27051956" ] || { echo "${FIRMWARE} does not start with a uImage"; exit 1; } + KERNEL_BLOCKS=$(( (64 + 0x$(od -An -tx1 -j12 -N4 ${FIRMWARE} | tr -d ' \n') + 0xFFFF) / 0x10000 )) + # The bootcmd reads 2 MiB from the kernel's address; the firmware build + # checks the uImage against that, and so does this. + [ $((KERNEL_BLOCKS * 0x10000)) -le $(($KERNEL_E - $KERNEL_A)) ] || { echo "Filesize exceeds boundaries"; exit 1; } + ROOTFS_A=$(printf "0x%x" $(($KERNEL_A + KERNEL_BLOCKS * 0x10000))) + KERNEL_E=${ROOTFS_A} + ROOTFS_E=$(printf "0x%x" $(( ($KERNEL_A + FIRMWARE_SIZE + 0xFFFF) / 0x10000 * 0x10000 ))) + # The firmware build leaves at least 256 KiB of rootfs_data; so does this. + [ $(($ROOTFS_E + 0x40000)) -le $(($FLASH_SIZE)) ] || { echo "Filesize exceeds boundaries"; exit 1; } + head -c $((KERNEL_BLOCKS * 0x10000)) ${FIRMWARE} > ${WORKDIR}/uImage + tail -c +$((KERNEL_BLOCKS * 0x10000 + 1)) ${FIRMWARE} > ${WORKDIR}/rootfs.squashfs + rm -f ${FIRMWARE} +else + echo "Neither ${PACKAGE}.tgz nor ${PACKAGE}-split.tgz is here" + exit 1 +fi -# Check if give files exceed partition boundaries -[[ $(stat --printf="%s" ${WORKDIR}/rootfs*) -gt $(($ROOTFS_E - $ROOTFS_A)) ]] || [[ $(stat --printf="%s" ${WORKDIR}/uImage*) -gt $(($KERNEL_E - $KERNEL_A)) ]] && echo "Filesize exceeds boundaries" && exit 1 +ROOTFS_DATA="${ROOTFS_DATA:-$(($FLASH_SIZE - $ROOTFS_E ))}" # Generate Readme README=$(cat <<-EOF diff --git a/.github/workflows/xm.yml b/.github/workflows/xm.yml index c9cdc22..fc93078 100644 --- a/.github/workflows/xm.yml +++ b/.github/workflows/xm.yml @@ -29,30 +29,37 @@ jobs: - name: Checkout uses: actions/checkout@v2.3.4 - name: Fetch OpenIPC release + # A board whose rootfs outgrows its fixed slot publishes only the + # -split package (OpenIPC/firmware#2666); _xm2oipc.sh takes whichever + # of the two is here, the fixed-slot one first. run: | - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516cv100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3518cv100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3518ev100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516cv200-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516dv100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3518ev200-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516cv300-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516ev100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516ev300-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3518ev300-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3516ev200-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3536cv100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.hi3536dv100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.gk7201v200-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.gk7205v200-nor-lite.tgz + for soc in hi3516cv100 \ + hi3518cv100 \ + hi3518ev100 \ + hi3516cv200 \ + hi3516dv100 \ + hi3518ev200 \ + hi3516cv300 \ + hi3516ev100 \ + hi3516ev300 \ + hi3518ev300 \ + hi3516ev200 \ + hi3536cv100 \ + hi3536dv100 \ + gk7201v200 \ + gk7205v200 \ + gk7205v300 \ + gk7605v100 \ + gk7205v500 \ + xm530 \ + xm510 \ + nt98562 \ + nt98566; do + wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.$soc-nor-lite.tgz || + echo "openipc.$soc-nor-lite.tgz is not published" + wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.$soc-nor-lite-split.tgz || true + done # wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.gk7205v200-nor-fpv.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.gk7205v300-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.gk7605v100-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.gk7205v500-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.xm530-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.xm510-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.nt98562-nor-lite.tgz - wget -q https://github.com/OpenIPC/firmware/releases/download/latest/openipc.nt98566-nor-lite.tgz - name: Create firmwares run: | export RUN="$GITHUB_WORKSPACE/.github/workflows/_xm2oipc.sh"