Initial Quest Pro ARM64 kexec release

This commit is contained in:
yusuf committed 2026-09-26 21:36:07 +03:00
commit 4537fb948f
26 files changed
+5031

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Pack a static ARM64 BusyBox and init.sh into a gzip newc initramfs."""
import argparse
import gzip
import stat
import struct
from pathlib import Path
def archive(entries):
data = bytearray()
for ino, (name, mode, contents, major, minor) in enumerate(entries, 1):
name = name.encode() + b"\0"
fields = (ino, mode, 0, 0, 1, 0, len(contents), 0, 0, major, minor, len(name), 0)
data += b"070701" + "".join(f"{x:08x}" for x in fields).encode() + name
data += b"\0" * (-len(data) % 4)
data += contents
data += b"\0" * (-len(data) % 4)
data += b"\0" * (-len(data) % 512)
return bytes(data)
def check_static_arm64(path):
b = path.read_bytes()
if b[:6] != b"\x7fELF\x02\x01" or struct.unpack_from("<H", b, 18)[0] != 183:
raise SystemExit(f"{path}: not a little-endian ARM64 ELF")
off = struct.unpack_from("<Q", b, 32)[0]
size, count = struct.unpack_from("<HH", b, 54)
if any(struct.unpack_from("<I", b, off + i * size)[0] == 3 for i in range(count)):
raise SystemExit(f"{path}: must be statically linked")
return b
def main():
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--busybox", type=Path, required=True)
ap.add_argument("--output", type=Path, required=True)
ap.add_argument("--init", type=Path, default=Path(__file__).with_name("init.sh"))
ap.add_argument("--extra", action="append", default=[], metavar="SRC:DEST",
help="add a static ARM64 binary, e.g. modeset:bin/modeset")
a = ap.parse_args()
dirs = ("bin", "sbin", "dev", "dev/pts", "proc", "sys", "config", "etc", "root", "tmp")
entries = [(d, stat.S_IFDIR | 0o755, b"", 0, 0) for d in dirs]
entries += [
("bin/busybox", stat.S_IFREG | 0o755, check_static_arm64(a.busybox), 0, 0),
("init", stat.S_IFREG | 0o755, a.init.read_bytes(), 0, 0),
("dev/console", stat.S_IFCHR | 0o600, b"", 5, 1),
("dev/kmsg", stat.S_IFCHR | 0o600, b"", 1, 11),
("dev/null", stat.S_IFCHR | 0o666, b"", 1, 3),
("etc/passwd", stat.S_IFREG | 0o644, b"root::0:0:root:/root:/bin/sh\n", 0, 0),
("etc/group", stat.S_IFREG | 0o644, b"root:x:0:\n", 0, 0),
]
for spec in a.extra:
src, dest = spec.split(":", 1)
entries.append((dest, stat.S_IFREG | 0o755, check_static_arm64(Path(src)), 0, 0))
entries.append(("TRAILER!!!", 0, b"", 0, 0))
data = gzip.compress(archive(entries), mtime=0)
if len(data) > 0x200000:
raise SystemExit(f"initramfs is {len(data)} bytes; the staging layout allows 2 MiB")
a.output.parent.mkdir(parents=True, exist_ok=True)
a.output.write_bytes(data)
print(f"{a.output}: {len(data)} bytes")
if __name__ == "__main__":
main()
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Download, verify and build a static ARM64 BusyBox.
# Usage: busybox.sh <output-binary>
set -euo pipefail
VER=1.36.1
SHA=b8cc24c9574d809e7279c3be349795c5d5ceb6fdf19ca709f80cde50e47de314
OUT=$(realpath -m "$1")
WORK=$(dirname "$OUT")/busybox-build
CROSS=${CROSS_COMPILE:-aarch64-linux-gnu-}
mkdir -p "$WORK"
cd "$WORK"
[ -f busybox-$VER.tar.bz2 ] ||
curl -fL -o busybox-$VER.tar.bz2 https://busybox.net/downloads/busybox-$VER.tar.bz2
echo "$SHA busybox-$VER.tar.bz2" | sha256sum -c -
rm -rf busybox-$VER
tar xjf busybox-$VER.tar.bz2
cd busybox-$VER
M="make ARCH=arm64 CROSS_COMPILE=$CROSS"
$M defconfig >/dev/null
# Static, and drop tc: it doesn't build against current kernel headers.
sed -i 's/^# CONFIG_STATIC is not set/CONFIG_STATIC=y/; s/^CONFIG_TC=y/# CONFIG_TC is not set/' .config
$M oldconfig </dev/null >/dev/null 2>&1 || true
grep -q '^CONFIG_STATIC=y' .config
$M -j"$(nproc)" >/dev/null
cp busybox "$OUT"
echo "$OUT"
+75
View File
@@ -0,0 +1,75 @@
#!/bin/busybox sh
# RAM-only init: USB Ethernet (ECM) gadget with a BusyBox telnet shell.
# The headset is 10.42.0.2; udhcpd hands the host 10.42.0.1.
# Nothing here mounts a block device.
export PATH=/bin:/sbin
export HOME=/root
export TERM=vt100
/bin/busybox --install -s /bin
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /config /tmp
mount -t devpts devpts /dev/pts
mount -t configfs configfs /config
mount -t tmpfs tmpfs /tmp
exec >/dev/kmsg 2>&1
log() { echo "qkx-init: $*"; }
# /proc/qkx_bootdone comes from the target kernel patches: 'd' disarms the
# boot watchdog, 'k' is a keepalive, 'r' forces a log-preserving warm reset.
bootdone() { [ -w /proc/qkx_bootdone ] && echo "$1" > /proc/qkx_bootdone; }
log "reached $(uname -r)"
g=/config/usb_gadget/qkx
mkdir -p "$g/strings/0x409" "$g/configs/c.1/strings/0x409" "$g/functions/ecm.usb0"
echo 0x1d6b > "$g/idVendor"
echo 0x0105 > "$g/idProduct"
echo QKX-RAM > "$g/strings/0x409/serialnumber"
echo 'quest-pro-kexec' > "$g/strings/0x409/manufacturer"
echo 'RAM-only USB Ethernet shell' > "$g/strings/0x409/product"
echo 'ECM' > "$g/configs/c.1/strings/0x409/configuration"
echo 250 > "$g/configs/c.1/MaxPower"
# Fixed MACs keep the host interface name stable across boots.
echo 02:51:4b:58:00:02 > "$g/functions/ecm.usb0/dev_addr"
echo 02:51:4b:58:00:01 > "$g/functions/ecm.usb0/host_addr"
ln -s "$g/functions/ecm.usb0" "$g/configs/c.1/ecm.usb0"
udc=
for _ in 1 2 3 4 5 6 7 8 9 10; do
for c in /sys/class/udc/*; do
[ -e "$c" ] || continue
echo "${c##*/}" > "$g/UDC" && udc=$c && break 2
done
log "waiting for UDC"
bootdone k
sleep 1
done
if [ -z "$udc" ]; then
log "no UDC; forcing warm reset so the log survives"
bootdone r
while :; do sleep 60; done
fi
log "bound ${udc##*/}"
# Keep the DWC3 glue device out of runtime suspend.
echo on > "$udc/device/../power/control" 2>/dev/null
ifconfig lo 127.0.0.1 up
ifconfig usb0 10.42.0.2 netmask 255.255.255.0 up
cat > /tmp/udhcpd.conf <<EOC
interface usb0
start 10.42.0.1
end 10.42.0.1
max_leases 1
lease_file /tmp/udhcpd.leases
option subnet 255.255.255.0
option lease 86400
EOC
touch /tmp/udhcpd.leases
udhcpd /tmp/udhcpd.conf
telnetd -l /bin/sh -p 23
bootdone d
log "ready: telnet 10.42.0.2"
while :; do sleep 3600; done