Build a bootable image, and boot with a network
Two gaps between "this SDK builds a kernel" and "this SDK can replace the
vendor SDK for the device".
BOOT IMAGE. build-kernel.sh emitted a zImage and a dtb and stopped, so
producing something the board's U-Boot would actually boot still meant going
through the vendor tree. mk-bootimg.sh packages the pair the way M2 bring-up
established (kernel/docs/m2-boot-on-c8a3.md): an EXTERNAL-DATA FIT
(mkimage -E -p 0x800), a mandatory `resource` multi sub-image carrying
rk-kernel.dtb plus any logos, and the sysmem sentinel load addresses. Each of
those was learned from a specific failure -- an embedded-data FIT is "No fit
blob", a missing resource image is "Failed to load DTB, ret=-19", real load
addresses collide -- so the script also ASSERTS the metadata stayed small,
because an embedded-data FIT looks perfectly fine until a panel will not come
back.
NETWORKING. Diffing this defconfig's expansion against the kernel actually
shipping on the panel found three whole subsystems missing, none of which fail
at build time and none of which are visible until the unit is in the field:
- WIREGUARD + NET_UDP_TUNNEL: flared's mesh to FLARE. Without it wg0 never
comes up.
- VLAN_8021Q: the MikroTik app configures tagged ports the panel terminates.
- NETFILTER and legacy iptables: every rule in S35iptables, and NAT for
router mode.
The netfilter half carried a trap worth naming. 6.18 split the legacy tables
out behind NETFILTER_XTABLES_LEGACY and IP_NF_IPTABLES_LEGACY, symbols that do
not exist in 5.10 -- so copying the vendor kernel's symbol list verbatim gives
a kernel where IP_NF_FILTER and IP_NF_NAT silently stay off and `iptables` has
no filter or nat table at all. Our userspace drives legacy iptables, not nft.
The result now shows ZERO regressions against the shipping 5.10 kernel across
mesh, gadget, firewall, VLAN, storage, net core, display/input, RGA/NPU,
wifi/BT and watchdog.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T2D2KtdgwbhbF6Mo64eUrn
This commit is contained in:
Executable
+148
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
# Package a bootable boot.img from a kernel this SDK built.
|
||||
#
|
||||
# build-kernel.sh produces a zImage and a dtb; this turns them into the image
|
||||
# the board's U-Boot will actually boot, which was the last thing keeping the
|
||||
# device build tied to the vendor SDK. The format is not negotiable and was
|
||||
# established the hard way during M2 bring-up (kernel/docs/m2-boot-on-c8a3.md):
|
||||
#
|
||||
# - EXTERNAL-DATA FIT (`mkimage -E -p 0x800`). The FDT metadata stays tiny and
|
||||
# the kernel/fdt/resource payloads are appended after it. An ordinary
|
||||
# embedded-data FIT is rejected outright: "FIT: No fit blob".
|
||||
# - A `resource` MULTI SUB-IMAGE IS MANDATORY. Rockchip's U-Boot reads the DTB
|
||||
# over its RESC: path from a resource image holding `rk-kernel.dtb` (plus
|
||||
# any boot logos). Without one: "Failed to load DTB, ret=-19".
|
||||
# - SYSMEM SENTINEL LOAD ADDRESSES: fdt load 0xffffff00, kernel load/entry
|
||||
# 0xffffff01. These tell U-Boot's sysmem to place the images itself. Real
|
||||
# low addresses collide and come back as "No fit blob".
|
||||
#
|
||||
# The board's U-Boot has CONFIG_FIT_SIGNATURE off, so the image is not signed.
|
||||
#
|
||||
# Usage:
|
||||
# mk-bootimg.sh --kernel zImage --dtb rv1106-warden.dtb --out boot.img
|
||||
# [--logo FILE] [--logo-kernel FILE] [--logo-recovery FILE]
|
||||
# [--resource-tool PATH]
|
||||
set -euo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KERNEL=""; DTB=""; OUT=""; RTOOL=""
|
||||
LOGO=""; LOGO_KERNEL=""; LOGO_RECOVERY=""
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--kernel) KERNEL="${2:?}"; shift 2 ;;
|
||||
--dtb) DTB="${2:?}"; shift 2 ;;
|
||||
--out) OUT="${2:?}"; shift 2 ;;
|
||||
--logo) LOGO="${2:?}"; shift 2 ;;
|
||||
--logo-kernel) LOGO_KERNEL="${2:?}"; shift 2 ;;
|
||||
--logo-recovery) LOGO_RECOVERY="${2:?}"; shift 2 ;;
|
||||
--resource-tool) RTOOL="${2:?}"; shift 2 ;;
|
||||
-h|--help) sed -n '2,25p' "$0"; exit 0 ;;
|
||||
*) echo "FATAL: unknown argument '$1'" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$KERNEL" ] && [ -f "$KERNEL" ] || { echo "FATAL: --kernel: no such file" >&2; exit 1; }
|
||||
[ -n "$DTB" ] && [ -f "$DTB" ] || { echo "FATAL: --dtb: no such file" >&2; exit 1; }
|
||||
[ -n "$OUT" ] || { echo "FATAL: --out is required" >&2; exit 1; }
|
||||
command -v mkimage >/dev/null || {
|
||||
echo "FATAL: mkimage not on PATH (Debian/Ubuntu: u-boot-tools)" >&2; exit 1; }
|
||||
|
||||
# resource_tool is a Rockchip host tool. It has no free-standing source here, so
|
||||
# it is taken from the vendor SDK when one is present rather than vendored as a
|
||||
# binary; --resource-tool overrides. FAILS CLOSED, because a boot.img built
|
||||
# without a resource image looks fine and does not boot.
|
||||
if [ -z "$RTOOL" ]; then
|
||||
for c in \
|
||||
"$HERE/../tools/resource_tool" \
|
||||
"${WARDEN_VENDOR_SDK:-$HERE/../../../sdk}/sysdrv/tools/pc/toolkits/resource_tool" \
|
||||
"$HOME/projects/scada/flare-edge/sdk/sysdrv/tools/pc/toolkits/resource_tool"
|
||||
do
|
||||
[ -x "$c" ] && { RTOOL="$c"; break; }
|
||||
done
|
||||
fi
|
||||
[ -n "$RTOOL" ] && [ -x "$RTOOL" ] || {
|
||||
echo "FATAL: resource_tool not found; pass --resource-tool PATH" >&2; exit 1; }
|
||||
|
||||
WORKDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# resource_tool packs files under a root dir by their names, and U-Boot looks
|
||||
# for the DTB under the fixed name rk-kernel.dtb.
|
||||
cp "$DTB" "$WORKDIR/rk-kernel.dtb"
|
||||
RES_FILES=(rk-kernel.dtb)
|
||||
for pair in "$LOGO:logo.bmp" "$LOGO_KERNEL:logo_kernel.bmp" "$LOGO_RECOVERY:logo_recovery.bmp"; do
|
||||
src="${pair%%:*}"; dst="${pair##*:}"
|
||||
[ -n "$src" ] || continue
|
||||
[ -f "$src" ] || { echo "FATAL: logo '$src' not found" >&2; exit 1; }
|
||||
cp "$src" "$WORKDIR/$dst"
|
||||
RES_FILES+=("$dst")
|
||||
done
|
||||
|
||||
echo "== resource.img (${RES_FILES[*]})"
|
||||
( cd "$WORKDIR" && "$RTOOL" --pack --root=. --image=resource.img "${RES_FILES[@]}" >/dev/null )
|
||||
[ -s "$WORKDIR/resource.img" ] || { echo "FATAL: resource_tool produced nothing" >&2; exit 1; }
|
||||
|
||||
cp "$KERNEL" "$WORKDIR/kernel"
|
||||
cp "$WORKDIR/resource.img" "$WORKDIR/resource"
|
||||
cp "$DTB" "$WORKDIR/fdt"
|
||||
|
||||
cat > "$WORKDIR/boot.its" <<'ITS'
|
||||
/dts-v1/;
|
||||
/ {
|
||||
description = "FIT image with Linux kernel, FDT blob and resource";
|
||||
images {
|
||||
fdt {
|
||||
data = /incbin/("fdt");
|
||||
type = "flat_dt";
|
||||
arch = "arm";
|
||||
compression = "none";
|
||||
load = <0xffffff00>;
|
||||
hash { algo = "sha256"; };
|
||||
};
|
||||
kernel {
|
||||
data = /incbin/("kernel");
|
||||
type = "kernel";
|
||||
arch = "arm";
|
||||
os = "linux";
|
||||
compression = "none";
|
||||
entry = <0xffffff01>;
|
||||
load = <0xffffff01>;
|
||||
hash { algo = "sha256"; };
|
||||
};
|
||||
resource {
|
||||
data = /incbin/("resource");
|
||||
type = "multi";
|
||||
arch = "arm";
|
||||
compression = "none";
|
||||
hash { algo = "sha256"; };
|
||||
};
|
||||
};
|
||||
configurations {
|
||||
default = "conf";
|
||||
conf {
|
||||
rollback-index = <0x0>;
|
||||
kernel = "kernel";
|
||||
fdt = "fdt";
|
||||
multi = "resource";
|
||||
};
|
||||
};
|
||||
};
|
||||
ITS
|
||||
|
||||
echo "== FIT (external data, -E -p 0x800)"
|
||||
( cd "$WORKDIR" && mkimage -f boot.its -E -p 0x800 boot.img >/dev/null )
|
||||
|
||||
# A FIT whose metadata swelled to the size of the whole image is an
|
||||
# embedded-data build, which this U-Boot rejects. Catch it here rather than on
|
||||
# a panel that will not come back.
|
||||
meta="$(od -An -tu4 -j4 -N4 --endian=big "$WORKDIR/boot.img" | tr -d ' ')"
|
||||
total="$(stat -c %s "$WORKDIR/boot.img")"
|
||||
if [ "${meta:-0}" -gt 65536 ] || [ "${meta:-0}" -ge "$total" ]; then
|
||||
echo "FATAL: FIT metadata is ${meta} bytes of a ${total}-byte image: that is an" >&2
|
||||
echo " embedded-data FIT and U-Boot will report 'No fit blob'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -m 0644 "$WORKDIR/boot.img" "$OUT"
|
||||
echo "boot.img: $OUT ($(stat -c %s "$OUT") bytes, FIT metadata ${meta} bytes)"
|
||||
@@ -1308,3 +1308,48 @@ CONFIG_DEBUG_UART_PHYS=0xff4c0000
|
||||
CONFIG_DEBUG_UART_VIRT=0xff4c0000
|
||||
CONFIG_DEBUG_UART_8250_WORD=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
|
||||
#
|
||||
# WardenOS platform networking.
|
||||
#
|
||||
# These were absent, and their absence is not visible until the panel is in
|
||||
# the field: the kernel boots, the UI runs, and then the mesh never comes up,
|
||||
# every iptables rule fails to load, and a tagged port carries nothing. The
|
||||
# shipping 5.10 kernel has all three, so a 6.18 image without them is a
|
||||
# REGRESSION against what is on the panel today -- caught by diffing the two
|
||||
# device configs, not by anything that fails at build time.
|
||||
#
|
||||
# WireGuard: flared's mesh to FLARE (wg0). Needs the UDP tunnel helper.
|
||||
CONFIG_WIREGUARD=y
|
||||
CONFIG_NET_UDP_TUNNEL=y
|
||||
# VLAN: the MikroTik app configures tagged ports and the panel terminates them.
|
||||
CONFIG_VLAN_8021Q=y
|
||||
# Netfilter: S35iptables and router mode. Scoped to what the scripts actually
|
||||
# use -- filter + nat tables, MASQUERADE, and the state/limit matches -- rather
|
||||
# than the vendor kernel's full 124-symbol suite.
|
||||
CONFIG_NETFILTER=y
|
||||
CONFIG_NETFILTER_ADVANCED=y
|
||||
CONFIG_NF_CONNTRACK=y
|
||||
CONFIG_NF_NAT=y
|
||||
CONFIG_NF_NAT_MASQUERADE=y
|
||||
CONFIG_NETFILTER_XTABLES=y
|
||||
# ...which in turn gates on the legacy xtables core, another 6.18 split.
|
||||
CONFIG_NETFILTER_XTABLES_LEGACY=y
|
||||
CONFIG_NETFILTER_XT_MATCH_STATE=y
|
||||
CONFIG_NETFILTER_XT_MATCH_LIMIT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
|
||||
CONFIG_NETFILTER_XT_NAT=y
|
||||
CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
|
||||
CONFIG_NF_DEFRAG_IPV4=y
|
||||
CONFIG_NF_CONNTRACK_IPV4=y
|
||||
CONFIG_IP_NF_IPTABLES=y
|
||||
# 6.18 SPLIT THE LEGACY TABLES OUT. IP_NF_FILTER and IP_NF_NAT depend on
|
||||
# IP_NF_IPTABLES_LEGACY, which did not exist in 5.10 -- so copying the vendor
|
||||
# kernel's symbol list verbatim yields a kernel where `iptables` has no filter
|
||||
# or nat table and every rule in S35iptables fails. Our userspace drives legacy
|
||||
# iptables, not nft, so this is the table set to enable.
|
||||
CONFIG_IP_NF_IPTABLES_LEGACY=y
|
||||
CONFIG_IP_NF_FILTER=y
|
||||
CONFIG_IP_NF_TARGET_REJECT=y
|
||||
CONFIG_IP_NF_NAT=y
|
||||
CONFIG_IP_NF_MANGLE=y
|
||||
|
||||
Reference in New Issue
Block a user