Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 119 additions & 14 deletions .github/workflows/image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,36 @@ jobs:
# (firmware.bin.<soc>) flashed at the SoC's firmware-partition
# offset. So assemble one full NOR image per DDR binning:
# u-boot at 0, firmware.bin at <fw_off_kib>.
# Where the image has to land is decided by the u-boot it is paired
# with: the cv610 and dv500 boot images carry their partition table
# in a compiled-in env (a HiSilicon boot table with a gzip'd u-boot
# behind it), and nothing rewrites that table later. Decompressed:
#
# 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 the kernel is read as one 2048/6144 KiB slot and the rootfs is
# mounted 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 is only the same thing if the FIT happens to
# end exactly at the slot. Split the blob the way sysupgrade's
# do_update_firmware does and put each half where the env looks.
hisi_layout() {
case "$1" in
hi3516cv6xx) boot_kib=256 kern_kib=2048 fw_kib=7168 ;;
hi3519dv500) boot_kib=512 kern_kib=6144 fw_kib=14336 ;;
*) echo "::error::create_hisi: no NOR layout known for $1"; return 1 ;;
esac
}

create_hisi() {
local uboot="$1" soc="$2" variant="$3" tag="$4" off="$5"
local uboot="$1" soc="$2" variant="$3" tag="$4" off="$5" nor="${6:-16384}"
local release="target/openipc-${tag}-nor-${variant}.bin"
local boot_kib kern_kib fw_kib fw fitsz fit_kib rootsz root_kib ub_kib got
hisi_layout "$soc" || return 1

mkdir -p output target
rm -rf output; mkdir -p output target
if ! wget -nv "$UBOOT_URL/$uboot" -O "output/$uboot"; then
echo -e "Download failed: $UBOOT_URL/$uboot\n"
return 0
Expand All @@ -144,14 +169,70 @@ jobs:
fi

tar -xf "output/$soc.tgz" -C output
# 16 MiB NOR, erased (0xff); u-boot at 0, firmware.bin at its
# partition offset (cv6xx MTDPARTS: firmware @ 0x50000 = 320 KiB).
dd if=/dev/zero bs=1K count=16384 status=none | tr '\000' '\377' > "$release"
fw="output/firmware.bin.$soc"

# The FIT is a flattened device tree: its total size is the
# big-endian word at byte 4. The rootfs starts at the next 64 KiB.
set -- $(od -An -tu1 -j4 -N4 "$fw")
fitsz=$(( ($1 << 24) | ($2 << 16) | ($3 << 8) | $4 ))
if [ "$fitsz" -le 0 ] || [ "$fitsz" -gt "$(stat -c%s "$fw")" ]; then
echo "::error::${soc}_${variant}: firmware.bin does not start with a FIT image"
return 1
fi
fit_kib=$(( (fitsz + 65535) / 65536 * 64 ))
# The squashfs superblock carries its own length (bytes_used, a
# little-endian u64 at byte 40); measure that rather than the
# blob's remainder, which post-image.sh pads to an erase block.
rootsz=$(od -An -tu8 -j "$(( fit_kib * 1024 + 40 ))" -N8 "$fw" | tr -d ' ')
if [ "$(od -An -c -j "$(( fit_kib * 1024 ))" -N4 "$fw" | tr -d ' ')" != "hsqs" ] || [ "${rootsz:-0}" -le 0 ]; then
echo "::error::${soc}_${variant}: no squashfs at ${fit_kib} KiB in firmware.bin"
return 1
fi
root_kib=$(( (rootsz + 4095) / 4096 * 4 )) # mksquashfs pads the image to 4 KiB
ub_kib=$(( ( $(stat -c%s "output/$uboot") + 1023 ) / 1024 ))

# Every bound is the partition u-boot declares, not the space left
# on the chip: past the kernel slot the FIT is truncated by sf read,
# past the rootfs slot the squashfs is written over rootfs_data.
if [ "$ub_kib" -gt "$boot_kib" ]; then
echo "::error::${soc}_${variant}: u-boot ${uboot} is ${ub_kib} KiB, the boot partition is ${boot_kib} KiB (the env sits behind it)"
return 1
fi
if [ "$fit_kib" -gt "$kern_kib" ]; then
echo "::error::${soc}_${variant}: the FIT is ${fit_kib} KiB, the kernel partition is ${kern_kib} KiB"
return 1
fi
if [ "$root_kib" -gt "$(( fw_kib - kern_kib ))" ]; then
echo "::error::${soc}_${variant}: the rootfs is ${root_kib} KiB, the rootfs partition is $(( fw_kib - kern_kib )) KiB"
return 1
fi
if [ "$(( off + fw_kib ))" -gt "$nor" ]; then
echo "::error::${soc}_${variant}: firmware partition ends at $(( off + fw_kib )) KiB on a ${nor} KiB part"
return 1
fi

# NOR canvas, erased (0xff): u-boot at 0, the FIT at the firmware
# offset, the rootfs at the rootfs partition; the env and
# rootfs_data stay erased, so u-boot boots its defaults and /init
# formats the overlay. $nor is the part size in KiB -- a lite
# variant is flashed to an 8 MiB part.
dd if=/dev/zero bs=1K count="$nor" status=none | tr '\000' '\377' > "$release"
dd if="output/$uboot" of="$release" bs=1K seek=0 conv=notrunc status=none
dd if="output/firmware.bin.$soc" of="$release" bs=1K seek="$off" conv=notrunc status=none
dd if="$fw" of="$release" bs=1K count="$fit_kib" seek="$off" conv=notrunc status=none
dd if="$fw" of="$release" bs=1K skip="$fit_kib" count="$root_kib" seek="$(( off + kern_kib ))" conv=notrunc status=none

# Backstop for the checks above: if any is ever wrong, the canvas
# has grown past $nor and the file is no longer an image of the
# part. Refuse to upload it rather than ship an unflashable
# release.
got=$(stat -c%s "$release")
if [ "$got" -ne "$(( nor * 1024 ))" ]; then
echo "::error::${soc}_${variant}: assembled ${release} is ${got} bytes, expected $(( nor * 1024 ))"
return 1
fi
rm -rf output

echo -e "Created: $release\n"
echo -e "Created: $release (FIT ${fit_kib} KiB in ${kern_kib}, rootfs ${root_kib} KiB in $(( fw_kib - kern_kib )))\n"
}

for soc in $SIGMASTAR $ALLWINNER; do
Expand All @@ -171,11 +252,22 @@ jobs:
# cv608 is a single DDR2-64M part. firmware partition @ 0x50000.
# The 20s/00s u-boots are picked per DDR (their 20g/00g siblings
# carry the same DDR table, differing only in the socmodel env tag).
for variant in ultimate; do
create_hisi boot-hi3516cv610-10b-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr2-64m 320
create_hisi boot-hi3516cv610-20s-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr3-128m 320
create_hisi boot-hi3516cv610-00s-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr3-512m 320
create_hisi boot-hi3516cv608-nor.bin hi3516cv6xx "$variant" hi3516cv608 320
# lite targets 8 MiB parts, ultimate 16 MiB -- the same kernel and
# rootfs blob, on the flash the variant was built for.
# create_hisi returns 1 for an image it refuses to assemble. This
# step runs under Actions' default `bash -e`, so a bare call would
# end the step at the first refusal -- with the Sigmastar, Allwinner
# and Ingenic images already in target/ and the upload never
# reached, nothing would publish for anyone. Collect instead: every
# target gets its turn, what did assemble ships (Upload runs on
# failure too), and the step still goes red so the refusal is seen.
hisi_failed=
for variant in lite ultimate; do
Comment thread
eseverson marked this conversation as resolved.
Comment thread
openipc-ai marked this conversation as resolved.
Comment thread
openipc-ai marked this conversation as resolved.
case $variant in lite) nor=8192 ;; *) nor=16384 ;; esac
create_hisi boot-hi3516cv610-10b-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr2-64m 320 "$nor" || hisi_failed=1
create_hisi boot-hi3516cv610-20s-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr3-128m 320 "$nor" || hisi_failed=1
create_hisi boot-hi3516cv610-00s-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr3-512m 320 "$nor" || hisi_failed=1
create_hisi boot-hi3516cv608-nor.bin hi3516cv6xx "$variant" hi3516cv608 320 "$nor" || hisi_failed=1
done

# HiSilicon Hi3519DV500 (aarch64 V5). Same scheme: a single shared
Expand All @@ -184,11 +276,24 @@ jobs:
# (6-layer) and dmebpro (4-layer flyby), both DDR4-2666 2 GB. NOR
# layout (16 MiB): u-boot @0, env @0x80000, firmware @0xC0000 = 768 KiB.
for variant in ultimate; do
create_hisi boot-hi3519dv500-dmeb-nor.bin hi3519dv500 "$variant" hi3519dv500-dmeb 768
create_hisi boot-hi3519dv500-dmebpro-nor.bin hi3519dv500 "$variant" hi3519dv500-dmebpro 768
create_hisi boot-hi3519dv500-dmeb-nor.bin hi3519dv500 "$variant" hi3519dv500-dmeb 768 || hisi_failed=1
create_hisi boot-hi3519dv500-dmebpro-nor.bin hi3519dv500 "$variant" hi3519dv500-dmebpro 768 || hisi_failed=1
done

if [ -n "$hisi_failed" ]; then
echo "::error::one or more HiSilicon NOR images were refused (see above); the rest were assembled and will still be uploaded"
exit 1
fi

# A refused HiSilicon image fails the Prepare step on purpose, and the
# images that did assemble must still ship -- so this runs on failure.
# Not always(): that runs on cancellation too, and create_hisi lays down
# the canvas, u-boot, the FIT and the rootfs as four separate writes, so a
# cancelled run leaves a bootloader on an otherwise blank part. Uploading
# that publishes an unflashable image indistinguishable from a finished
# one. The ${{ }} is load-bearing: a bare `!` starts a YAML tag.
- name: Upload
if: ${{ !cancelled() }}
uses: softprops/action-gh-release@v2
with:
tag_name: image
Expand Down
Loading