From ac6c607cc43fe01661cd0dabdc9de99be5c50f31 Mon Sep 17 00:00:00 2001 From: foxxcn Date: Sun, 18 Oct 2026 01:24:47 +0800 Subject: [PATCH 1/4] ci/firmware: add xxd for camera-engine-rkaiq The rkaiq build embeds its IQ binary with xxd, which the Ubuntu base image does not have. Co-Authored-By: Claude Opus 5.5 --- ci/firmware/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/firmware/Dockerfile b/ci/firmware/Dockerfile index ea7791e..6e4456b 100644 --- a/ci/firmware/Dockerfile +++ b/ci/firmware/Dockerfile @@ -17,7 +17,7 @@ RUN apt-get update \ libnl-3-dev libssl-dev llvm-dev lz4 openssl parted patch \ pkg-config python-is-python3 python3 python3-dev python3-pip python3-pyelftools \ rsync sudo texinfo \ - u-boot-tools udev unzip uuid-dev wget xz-utils zstd \ + u-boot-tools udev unzip uuid-dev wget xxd xz-utils zstd \ && rm -rf /var/lib/apt/lists/* # Google's `repo` launcher (v2.69), pinned by content hash. From d288c06af780c0eed523881363541a4cd299d18b Mon Sep 17 00:00:00 2001 From: foxxcn Date: Sun, 18 Oct 2026 01:29:47 +0800 Subject: [PATCH 2/4] os/rk3588: clone mpp with 30 commits of history mpp's CMakeLists embeds the last few commit subjects in its version string and fails to configure on a depth-1 clone. Co-Authored-By: Claude Opus 5.5 --- os/rk3588/manifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/os/rk3588/manifest.xml b/os/rk3588/manifest.xml index 1c8bf3c..a771c3b 100644 --- a/os/rk3588/manifest.xml +++ b/os/rk3588/manifest.xml @@ -11,7 +11,8 @@ - + + From a2a0c7229711c0474bb64d795c1c1833fdd0046d Mon Sep 17 00:00:00 2001 From: foxxcn Date: Sun, 18 Oct 2026 03:32:37 +0800 Subject: [PATCH 3/4] firmware: ship the app in the rootfs, fix image packing in containers - /userdata is an empty partition mounted over the rootfs directory, so a binary placed there by the build was hidden and a freshly flashed board had no rustkvm_app. The image now carries /usr/lib/rustkvm/rustkvm_app and S99rustkvm installs it to /userdata on first boot or after a wipe. - build step: select the board and build u-boot, kernel and Buildroot as the build user; `./build.sh .mk` repeats the whole build as root and leaves root-owned objects that break the next run. - devkit.sh firmware mounts the host /dev so loop partition nodes exist. - collect names the images after the version baked into the image. Co-Authored-By: Claude Opus 5.5 --- docs/firmware-release.md | 1 + os/rk3588/README.md | 8 +++-- .../rootfs-overlay/etc/init.d/S99rustkvm | 7 ++++ scripts/build-firmware.sh | 35 ++++++++++++++----- scripts/devkit.sh | 5 +-- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/docs/firmware-release.md b/docs/firmware-release.md index 5ac166a..57523ed 100644 --- a/docs/firmware-release.md +++ b/docs/firmware-release.md @@ -80,6 +80,7 @@ What a freshly flashed unit looks like, and what "factory reset" must restore: | Item | Factory state | | --- | --- | +| `/userdata` | empty partition; first boot copies the factory binary from `/usr/lib/rustkvm/` | | `/userdata/rustkvm/config.toml` | absent → setup wizard on first login (device password or no-password mode) | | TLS certificate | self-signed, generated at start | | SSH (dropbear) | off; enabled only by `/userdata/rustkvm/devmode.enable` | diff --git a/os/rk3588/README.md b/os/rk3588/README.md index 7c4f728..43d1fb5 100644 --- a/os/rk3588/README.md +++ b/os/rk3588/README.md @@ -10,9 +10,11 @@ checks it out at the commits pinned here and applies the rest. | `patches//NNNN-*.patch` | Changes to SDK projects, applied in order with `git apply` | | `rootfs-overlay/` | Files copied into the root filesystem (init script, network config) | -The build also adds two things that are not in the tree: the `rustkvm_app` binary -(installed to `/userdata/rustkvm/bin/`) and `/etc/rustkvm-release` with the firmware -version. +The build also adds two things that are not in the tree: the `rustkvm_app` binary and +`/etc/rustkvm-release` with the firmware version. The binary ships in the rootfs as +`/usr/lib/rustkvm/rustkvm_app`, because `/userdata` is a separate partition that starts +empty and is mounted over the rootfs directory; `S99rustkvm` copies it to +`/userdata/rustkvm/bin/` on first boot and whenever userdata has been wiped. ## What the patches do diff --git a/os/rk3588/rootfs-overlay/etc/init.d/S99rustkvm b/os/rk3588/rootfs-overlay/etc/init.d/S99rustkvm index 207dfba..7f90409 100755 --- a/os/rk3588/rootfs-overlay/etc/init.d/S99rustkvm +++ b/os/rk3588/rootfs-overlay/etc/init.d/S99rustkvm @@ -4,6 +4,7 @@ # exit, so it is always stopped with SIGTERM. SIGKILL reboots the device. APP=/userdata/rustkvm/bin/rustkvm_app +FACTORY_APP=/usr/lib/rustkvm/rustkvm_app LOG_DIR=/userdata/rustkvm/log stop_app() { @@ -43,6 +44,12 @@ start) mkdir -p $LOG_DIR + # First boot or wiped userdata: install the binary shipped in the image. + if [ ! -f "$APP" ] && [ -f "$FACTORY_APP" ]; then + mkdir -p "$(dirname "$APP")" + cp "$FACTORY_APP" "$APP.tmp" && mv "$APP.tmp" "$APP" + fi + if [ ! -f "$APP" ]; then echo "rustkvm_app not found!" >>$LOG_DIR/rustkvm_app.log exit 1 diff --git a/scripts/build-firmware.sh b/scripts/build-firmware.sh index 32aea16..7edacef 100755 --- a/scripts/build-firmware.sh +++ b/scripts/build-firmware.sh @@ -119,17 +119,32 @@ BOARD=${BOARD_MK%.mk} EOF [ -f "$APP" ] || die "application binary not found: $APP (run scripts/build-app.sh first)" + # /userdata is its own (initially empty) partition mounted over the rootfs + # directory, so the factory binary lives in the rootfs and S99rustkvm copies + # it to /userdata/rustkvm/bin on first boot (and after a userdata wipe). log "install application $(basename "$APP")" - local bin=$SDK/device/rockchip/common/images/userdata/normal/rustkvm/bin - mkdir -p "$bin" - install -m 0755 "$APP" "$bin/rustkvm_app" + rm -rf "$SDK/device/rockchip/common/images/userdata/normal/rustkvm" + install -D -m 0755 "$APP" "$files/usr/lib/rustkvm/rustkvm_app" } step_build() { cd "$SDK" - log "build kernel, u-boot, Buildroot and SD image ($BOARD_MK)" - ./build.sh "$BOARD_MK" - log "build eMMC flasher image" + # `./build.sh .mk` builds everything twice, the second time as root, + # which leaves root-owned objects that break the next unprivileged build. + # Select the board ourselves and build each part as the current user; only + # image packing needs root (build.sh sudoes for those targets itself). + if [ -n "$(find u-boot kernel buildroot/output -maxdepth 3 -user root -print -quit 2>/dev/null)" ]; then + log "reclaim root-owned build files" + sudo chown -R "$(id -u):$(id -g)" u-boot kernel buildroot/output scripts/sd-fuse/out + fi + ln -sfn "$(readlink -f .current)/$BOARD_MK" .current_config.mk + for part in uboot kernel buildroot; do + log "build $part ($BOARD_MK)" + ./build.sh "$part" + done + log "pack SD image" + ./build.sh sd-img + log "pack eMMC flasher image" ./build.sh emmc-img } @@ -140,10 +155,14 @@ step_collect() { sd=$(ls -t "$SDK"/out/*_sd.img 2>/dev/null | head -n1) || true eflasher=$(ls -t "$SDK"/out/*_eflasher.img 2>/dev/null | head -n1) || true [ -n "$sd" ] || die "no SD image under $SDK/out" - local name=rustkvm-rk3588-$FW_VERSION + # Name the files after the version baked into the image, not the current HEAD. + local release=$SDK/device/friendlyelec/rustkvm-files/etc/rustkvm-release + local built + built=$(sed -n 's/^FIRMWARE_VERSION=//p' "$release") + local name=rustkvm-rk3588-$built gzip -c "$sd" >"$OUT/$name-sd.img.gz" [ -n "$eflasher" ] && gzip -c "$eflasher" >"$OUT/$name-eflasher.img.gz" - cp "$SDK/device/friendlyelec/rustkvm-files/etc/rustkvm-release" "$OUT/$name.release" + cp "$release" "$OUT/$name.release" (cd "$OUT" && sha256sum "$name"-* "$name.release" >"$name.SHA256SUMS") ls -lh "$OUT"/"$name"* } diff --git a/scripts/devkit.sh b/scripts/devkit.sh index 62248b4..c77a599 100755 --- a/scripts/devkit.sh +++ b/scripts/devkit.sh @@ -97,8 +97,9 @@ cmd_firmware_image() { cmd_firmware() { have_image "$FIRMWARE_IMAGE" || cmd_firmware_image mkdir -p "$SDK" "$CACHE/dl" "$CACHE/ccache" - # --privileged: the SDK packs SD/eMMC images through loop devices. - docker run --rm "${TTY[@]}" --privileged \ + # --privileged and the host /dev: the SDK packs SD/eMMC images through loop + # devices, and only the host creates the loopNpM partition nodes. + docker run --rm "${TTY[@]}" --privileged -v /dev:/dev \ -e RUSTKVM_SDK_DIR=/sdk -e BR2_DL_DIR=/cache/dl \ -e RUSTKVM_ROOT_PASSWORD -e FW_VERSION -e BOARD_MK -e REPO_DEPTH \ -v "$ROOT:/work" -v "$SDK:/sdk" -v "$CACHE:/cache" \ From 37f43259eb0b4c24b398bf88853660bb9e12d51c Mon Sep 17 00:00:00 2001 From: foxxcn Date: Sun, 18 Oct 2026 04:07:52 +0800 Subject: [PATCH 4/4] firmware: make Buildroot legal-info complete rkupdate declared a NOTICE license file that does not exist, which aborted `make legal-info`. Point it at LICENSE, and leave host-tool sources out of the legal-info bundle (632 MB for v0.1.0). Co-Authored-By: Claude Opus 5.5 --- os/rk3588/README.md | 1 + ...point-license-at-the-existing-LICENSE-file.patch | 13 +++++++++++++ scripts/gpl-source-bundle.sh | 4 +++- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 os/rk3588/patches/buildroot/0003-rkupdate-point-license-at-the-existing-LICENSE-file.patch diff --git a/os/rk3588/README.md b/os/rk3588/README.md index 43d1fb5..6a4c055 100644 --- a/os/rk3588/README.md +++ b/os/rk3588/README.md @@ -22,6 +22,7 @@ empty and is mounted over the rootfs directory; `S99rustkvm` copies it to | --- | --- | | `buildroot` | dhcpcd privsep seccomp allows `dup3` (SIGSYS at boot); chrony drift file ownership | | `buildroot` | RustKVM defconfig (GStreamer Opus, RGA, uvc-gadget, dropbear client, update engine); uvc-gadget builds with `-Dwerror=false` and accepts CONTINUOUS frame intervals | +| `buildroot` | rkupdate's license points at its real `LICENSE` file, so `make legal-info` completes | | `kernel` | `nanopi6_linux_defconfig`: NBD built in (virtual media), USB UAC2 gadget | | `scripts/sd-fuse` | builds the `v4l2loopback` module (operator-camera passthrough) | | `external` | rockit links `librkAlgoDis.so` | diff --git a/os/rk3588/patches/buildroot/0003-rkupdate-point-license-at-the-existing-LICENSE-file.patch b/os/rk3588/patches/buildroot/0003-rkupdate-point-license-at-the-existing-LICENSE-file.patch new file mode 100644 index 0000000..15cf07d --- /dev/null +++ b/os/rk3588/patches/buildroot/0003-rkupdate-point-license-at-the-existing-LICENSE-file.patch @@ -0,0 +1,13 @@ +diff --git a/package/rockchip/rkupdate/rkupdate.mk b/package/rockchip/rkupdate/rkupdate.mk +index 2ac99eb6..dfb1d658 100644 +--- a/package/rockchip/rkupdate/rkupdate.mk ++++ b/package/rockchip/rkupdate/rkupdate.mk +@@ -9,7 +9,7 @@ RKUPDATE_SITE = $(TOPDIR)/../external/rkupdate + RKUPDATE_SITE_METHOD = local + + RKUPDATE_LICENSE = Apache V2.0 +-RKUPDATE_LICENSE_FILES = NOTICE ++RKUPDATE_LICENSE_FILES = LICENSE + + RKUPDATE_DEPENDENCIES = \ + libpthread-stubs util-linux diff --git a/scripts/gpl-source-bundle.sh b/scripts/gpl-source-bundle.sh index a35c51f..f0df4bb 100755 --- a/scripts/gpl-source-bundle.sh +++ b/scripts/gpl-source-bundle.sh @@ -50,6 +50,8 @@ if [ "${LEGAL_INFO:-0}" = 1 ]; then out_dir=$SDK/buildroot/output/rockchip_rk3588 [ -f "$out_dir/.config" ] || { echo "error: no built Buildroot tree in $out_dir" >&2; exit 1; } make -C "$SDK/buildroot" O="$out_dir" legal-info - tar -C "$out_dir" -czf "$OUT/rustkvm-$version-legal-info.tar.gz" legal-info + # Host tools are not distributed, so their sources stay out of the bundle. + tar -C "$out_dir" --exclude=legal-info/host-sources \ + -czf "$OUT/rustkvm-$version-legal-info.tar.gz" legal-info echo "==> $OUT/rustkvm-$version-legal-info.tar.gz" fi