penguin/monok8s

k8s image for Mono Gateway Dev Kit

commit 215eb0dc7554b6acd1e606debe845d85736d304bffe1666bef6b869dbe914db1

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-03-26T01:00:28Z
subjectBoots into initramfs then switch_root
commit 215eb0dc7554b6acd1e606debe845d85736d304bffe1666bef6b869dbe914db1
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-03-26T01:00:28Z

    Boots into initramfs then switch_root
---
 .gitignore                                   |   2 +-
 alpine/build-rootfs.sh                       |  12 ++-
 alpine/install-packages.sh                   |   4 +-
 alpine/rootfs-extra/etc/fstab                |  10 ++-
 alpine/rootfs-extra/etc/init.d/root.override |  16 ++++
 board.its                                    |   2 +-
 build.env                                    |   3 +-
 docker/initramfs.Dockerfile                  |  17 ++++
 docs/uboot.md                                |   7 +-
 initramfs/rootfs-extra/init                  | 118 +++++++++++++++++++++++----
 makefile                                     |  14 +++-
 11 files changed, 173 insertions(+), 32 deletions(-)

diff --git a/.gitignore b/.gitignore
index ac6f994..a86299f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
 packages/
 out/
-.swp
+*.swp
diff --git a/alpine/build-rootfs.sh b/alpine/build-rootfs.sh
index da9c1f8..691051a 100755
--- a/alpine/build-rootfs.sh
+++ b/alpine/build-rootfs.sh
@@ -7,6 +7,14 @@ set -euo pipefail
 mkdir -p "$ROOTFS/var/cache/apk"
 mkdir -p "$ROOTFS/opt/monok8s/config"
 mkdir -p "$ROOTFS/build"
+mkdir -p \
+  "$ROOTFS/dev" \
+  "$ROOTFS/proc" \
+  "$ROOTFS/sys" \
+  "$ROOTFS/run" \
+  "$ROOTFS/data" \
+  "$ROOTFS/var" \
+  "$ROOTFS/tmp"
 
 mount --bind /var/cache/apk "$ROOTFS/var/cache/apk"
 mount --bind /dev  "$ROOTFS/dev"
@@ -72,6 +80,8 @@ mount "${TMP_LOOP}p4" /mnt/data
 
 # Put the real /var onto the data partition
 cp -a "$ROOTFS/var" /mnt/data/
+mkdir -p /mnt/data/etc-overlay/work
+mkdir -p /mnt/data/etc-overlay/upper
 
 # Copy rootfs to root partition, but exclude /var
 cp -a "$ROOTFS"/. /mnt/img-root/
@@ -79,9 +89,7 @@ rm -rf /mnt/img-root/var
 mkdir -p /mnt/img-root/var
 
 mkdir -p /mnt/img-root/boot
-cp /build/Image.gz /mnt/img-root/boot/Image.gz
 cp /build/board.itb /mnt/img-root/boot/kernel.itb
-cp /build/${DEVICE_TREE_TARGET}.dtb /mnt/img-root/boot/${DEVICE_TREE_TARGET}.dtb
 
 sync
 umount /mnt/img-root
diff --git a/alpine/install-packages.sh b/alpine/install-packages.sh
index 08c8630..c5a834e 100755
--- a/alpine/install-packages.sh
+++ b/alpine/install-packages.sh
@@ -5,11 +5,11 @@ cd /build
 echo "##################################################### Installing basic packages"
 apk add alpine-base \
 	openrc busybox-openrc bash nftables \
-	lm-sensors lm-sensors-fancontrol lm-sensors-fancontrol-openrc
+	lm-sensors lm-sensors-fancontrol lm-sensors-fancontrol-openrc e2fsprogs
 
 # For diagnotics
 apk add \
-  iproute2 iproute2-ss curl bind-tools procps strace tcpdump lsof jq \
+  iproute2 iproute2-ss curl bind-tools procps strace tcpdump lsof jq binutils \
   openssl nftables conntrack-tools ethtool findmnt kmod coreutils util-linux
 echo '[ -x /bin/bash ] && exec /bin/bash -l' >> "/root/.profile"
 
diff --git a/alpine/rootfs-extra/etc/fstab b/alpine/rootfs-extra/etc/fstab
index f590c2a..055fce5 100644
--- a/alpine/rootfs-extra/etc/fstab
+++ b/alpine/rootfs-extra/etc/fstab
@@ -1,3 +1,7 @@
-PARTLABEL=config               /opt/monok8s/config   vfat    defaults,noatime               0      0
-PARTLABEL=data                 /data                 ext4    defaults,noatime               0      21
-/data/var                      /var                  none    rbind,noatime                  0      0
+PARTLABEL=config  /opt/monok8s/config  vfat  defaults,noatime                          0  0
+PARTLABEL=data    /data                ext4  rw,noatime,nodiratime                     0  0
+
+/data/var         /var                 none  rbind                                     0  0
+
+tmpfs             /run                 tmpfs defaults,nosuid,nodev,mode=0755           0  0
+tmpfs             /tmp                 tmpfs defaults,nosuid,nodev,noexec,mode=1777    0  0
diff --git a/alpine/rootfs-extra/etc/init.d/root.override b/alpine/rootfs-extra/etc/init.d/root.override
new file mode 100755
index 0000000..385661e
--- /dev/null
+++ b/alpine/rootfs-extra/etc/init.d/root.override
@@ -0,0 +1,16 @@
+#!/sbin/openrc-run
+
+description="Keep root filesystem read-only for immutable boot"
+
+depend()
+{
+	after clock
+	need fsck
+	keyword -docker -podman -jail -lxc -openvz -prefix -systemd-nspawn -vserver -wsl
+}
+
+start()
+{
+	ebegin "Keeping root filesystem read-only"
+	eend 0
+}
diff --git a/board.its b/board.its
index 77bb2d3..9da0a4f 100644
--- a/board.its
+++ b/board.its
@@ -1,7 +1,7 @@
 /dts-v1/;
 
 / {
-    description = "LS1046A-RDB FIT Image";
+    description = "LS1046A-RDB-SDK FIT Image";
     #address-cells = <1>;
 
     images {
diff --git a/build.env b/build.env
index 9f5bbb5..ca04aed 100644
--- a/build.env
+++ b/build.env
@@ -18,8 +18,9 @@ ARCH=arm64
 
 CROSS_COMPILE=aarch64-linux-gnu-
 
-# Busybox for initramfs
+# Tools for initramfs
 BUSYBOX_VERSION=1_36_1
+E2FSPROGS_VERSION=1.47.4
 
 ## Alpine Linux
 ALPINE_VER=3.23.3
diff --git a/docker/initramfs.Dockerfile b/docker/initramfs.Dockerfile
index db0cdc3..709519b 100644
--- a/docker/initramfs.Dockerfile
+++ b/docker/initramfs.Dockerfile
@@ -25,6 +25,23 @@ RUN make ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} defconfig \
 RUN make CROSS_COMPILE=${CROSS_COMPILE} -j"$(nproc)"
 RUN make CROSS_COMPILE=${CROSS_COMPILE} CONFIG_PREFIX=/out/initramfs install
 
+ARG E2FSPROGS_VERSION
+
+WORKDIR /build
+COPY packages/e2fsprogs-v${E2FSPROGS_VERSION}.tar.gz ./
+RUN tar -xf e2fsprogs-v${E2FSPROGS_VERSION}.tar.gz && mv "e2fsprogs-${E2FSPROGS_VERSION}" e2fsprogs
+
+WORKDIR /build/e2fsprogs
+
+RUN ./configure \
+    --host=aarch64-linux-gnu \
+    --prefix=/usr
+RUN make -j"$(nproc)"
+RUN make DESTDIR=/out/initramfs install
+RUN mkdir -p /out/initramfs/lib \
+    && cp /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 /out/initramfs/lib/ \
+    && cp /usr/aarch64-linux-gnu/lib/libc.so.6 /out/initramfs/lib/
+
 WORKDIR /build
 
 ARG BUILD_TAG
diff --git a/docs/uboot.md b/docs/uboot.md
index 8ec25f5..cdfcf4d 100644
--- a/docs/uboot.md
+++ b/docs/uboot.md
@@ -3,10 +3,9 @@ Booting release image with bootusb
 ```
 setenv bootusb '
 usb start;
-setenv bootargs "console=ttyS0,115200 root=/dev/sda2 rw rootwait";
-ext4load usb 0:2 ${kernel_addr_r} /boot/Image.gz;
-ext4load usb 0:2 ${fdt_addr_r} /boot/mono-gateway-dk-sdk.dtb;
-booti ${kernel_addr_r} - ${fdt_addr_r}
+setenv kernel_addr_r 0xa0000000;
+ext4load usb 0:2 ${kernel_addr_r} /boot/kernel.itb;
+bootm ${kernel_addr_r};
 '
 
 run bootusb
diff --git a/initramfs/rootfs-extra/init b/initramfs/rootfs-extra/init
index cc1495e..c5027de 100755
--- a/initramfs/rootfs-extra/init
+++ b/initramfs/rootfs-extra/init
@@ -1,24 +1,114 @@
 #!/bin/sh
+set -eu
 
-mount -t devtmpfs devtmpfs /dev
-mount -t proc proc /proc
-mount -t sysfs sysfs /sys
+log() {
+    echo "[init] $*" >&2
+}
 
-# Spin the fan
-echo 100 > /sys/class/hwmon/hwmon0/pwm1
+panic() {
+    echo "initramfs panic: $*" >&2
+    exec sh
+}
+
+mount_or_panic() {
+    mount "$@" || panic "mount failed: $*"
+}
+
+mount_retry() {
+    dev="$1"
+    target="$2"
+    fstype="$3"
+    opts="$4"
+
+    i=0
+    while :; do
+        if mount -o "$opts" -t "$fstype" "$dev" "$target"; then
+            return 0
+        fi
+
+        i=$((i + 1))
+        [ "$i" -ge 50 ] && panic "Timed out mounting $dev on $target"
+        sleep 0.2
+    done
+}
+
+wait_for_path() {
+    path="$1"
+    i=0
+    while [ ! -e "$path" ]; do
+        i=$((i + 1))
+        [ "$i" -ge 50 ] && panic "Timed out waiting for $path"
+        sleep 0.2
+    done
+}
+
+mkdir -p /dev /proc /sys /run
+mount_or_panic -t devtmpfs devtmpfs /dev
+mount_or_panic -t proc proc /proc
+mount_or_panic -t sysfs sysfs /sys
+mount_or_panic -t tmpfs tmpfs /run
+
+echo 1 > /proc/sys/kernel/printk
 
-# Optional but nice
 mkdir -p /dev/pts
-mount -t devpts devpts /dev/pts
+mount_or_panic -t devpts devpts /dev/pts
+
+# Optional early fan kick. Do not fail boot if this path is not ready yet.
+if [ -w /sys/class/hwmon/hwmon0/pwm1 ]; then
+    echo 100 > /sys/class/hwmon/hwmon0/pwm1 || true
+fi
+
+log "Booting kernel took $(cut -d' ' -f1 /proc/uptime) seconds."
 
-echo "Booting kernel took $(cut -d' ' -f1 /proc/uptime) seconds."
-echo "Dropping to shell on ttyS0..."
+. /etc/build-info || panic "failed to source /etc/build-info"
 
-. /etc/build-info
+ROOT_DEV=""
+DATA_DEV=""
 
-while true; do
-    setsid cttyhack /bin/sh
-    echo "Shell exited. Starting another one..."
+for arg in $(cat /proc/cmdline); do
+    case "$arg" in
+        root=*)
+            ROOT_DEV="${arg#root=}"
+            ;;
+        data=*)
+            DATA_DEV="${arg#data=}"
+            ;;
+    esac
 done
 
-exec poweroff -f
+[ -n "$ROOT_DEV" ] || {
+    log "No root= specified in cmdline"
+    exec sh
+}
+
+[ -n "$DATA_DEV" ] || {
+    log "No data= specified in cmdline"
+    exec sh
+}
+
+wait_for_path "$DATA_DEV"
+
+e2fsck -p "$DATA_DEV" || {
+    echo "Auto fsck failed, forcing repair"
+    e2fsck -y "$DATA_DEV" || panic "fsck failed on $DATA_DEV"
+}
+
+mkdir -p /newroot
+mount_retry "$ROOT_DEV" /newroot ext4 ro
+mount_retry "$DATA_DEV" /newroot/data ext4 rw
+
+mount_or_panic --bind /newroot/data/var /newroot/var
+
+mount_or_panic -t overlay overlay \
+    -o lowerdir=/newroot/etc,upperdir=/newroot/data/etc-overlay/upper,workdir=/newroot/data/etc-overlay/work \
+    /newroot/etc
+
+mount_or_panic --move /dev /newroot/dev
+mount_or_panic --move /proc /newroot/proc
+mount_or_panic --move /sys /newroot/sys
+mount_or_panic --move /run /newroot/run
+
+log "Switching root to $ROOT_DEV"
+exec switch_root /newroot /sbin/init
+
+panic "switch_root returned unexpectedly"
diff --git a/makefile b/makefile
index 2fa068c..1928b19 100644
--- a/makefile
+++ b/makefile
@@ -6,10 +6,11 @@ TAG ?= dev
 PACKAGES_DIR := packages
 OUT_DIR      := out
 
-BUSYBOX_TAR  := $(PACKAGES_DIR)/busybox-$(BUSYBOX_VERSION).tar.gz
-ALPINE_TAR   := $(PACKAGES_DIR)/alpine-minirootfs-$(ALPINE_VER)-$(ALPINE_ARCH).tar.gz
-NXP_TAR      := $(PACKAGES_DIR)/$(NXP_VERSION).tar.gz
-CRIO_TAR     := $(PACKAGES_DIR)/$(CRIO_VERSION).tar.gz
+E2FSPROGS_TAR := $(PACKAGES_DIR)/e2fsprogs-$(E2FSPROGS_VERSION).tar.gz
+BUSYBOX_TAR   := $(PACKAGES_DIR)/busybox-$(BUSYBOX_VERSION).tar.gz
+ALPINE_TAR    := $(PACKAGES_DIR)/alpine-minirootfs-$(ALPINE_VER)-$(ALPINE_ARCH).tar.gz
+NXP_TAR       := $(PACKAGES_DIR)/$(NXP_VERSION).tar.gz
+CRIO_TAR      := $(PACKAGES_DIR)/$(CRIO_VERSION).tar.gz
 
 # Kubernetes components
 KUBELET_BIN := $(PACKAGES_DIR)/kubernetes/kubelet
@@ -62,6 +63,7 @@ KERNEL_DEPS := \
 INITRAMFS_DEPS := \
 	$(KERNEL_IMAGE) \
 	$(BUSYBOX_TAR) \
+	$(E2FSPROGS_TAR) \
 	docker/initramfs.Dockerfile \
 	$(INITRAMFS_SRCS) \
 	$(BUILD_INFO_FILE) \
@@ -111,6 +113,9 @@ $(KUBECTL_BIN): | $(PACKAGES_DIR)
 $(BUSYBOX_TAR): | $(PACKAGES_DIR)
 	curl -L -o $@ "https://github.com/mirror/busybox/archive/refs/tags/$(BUSYBOX_VERSION).tar.gz"
 
+$(E2FSPROGS_TAR): | $(PACKAGES_DIR)
+	curl -L -o $@ "https://github.com/tytso/e2fsprogs/archive/refs/tags/v$(E2FSPROGS_VERSION).tar.gz"
+
 $(ALPINE_TAR): | $(PACKAGES_DIR)
 	curl -L -o $@ "https://dl-cdn.alpinelinux.org/alpine/v$(ALPINE_SERIES)/releases/$(ALPINE_ARCH)/alpine-minirootfs-$(ALPINE_VER)-$(ALPINE_ARCH).tar.gz"
 
@@ -158,6 +163,7 @@ $(INITRAMFS): $(INITRAMFS_DEPS) | $(OUT_DIR)
 		--build-arg ARCH=$(ARCH) \
 		--build-arg CROSS_COMPILE=$(CROSS_COMPILE) \
 		--build-arg BUSYBOX_VERSION=$(BUSYBOX_VERSION) \
+		--build-arg E2FSPROGS_VERSION=$(E2FSPROGS_VERSION) \
 		--build-arg BUILD_TAG=$(BUILD_TAG) \
 		--output type=local,dest=./$(OUT_DIR) .
 	test -f $@