同步更新升级文件

Signed-off-by: h60047265 <honglulu3@h-partners.com>
This commit is contained in:
hongll
2026-02-02 10:31:07 +08:00
parent 55d2ca08f3
commit d3264407dd
13 changed files with 1010 additions and 0 deletions
+206
View File
@@ -0,0 +1,206 @@
mkroot - simple linux system builder
Compiles a toybox-based root filesystem and kernel that can boot under qemu.
Prebuilt binaries available from http://landley.net/bin/mkroot/latest
launched via ./run-qemu.sh (which assumes you have QEMU installed, KVM
works in a pinch), and then run "exit" to shut down the emulated system.
This project is a successor to https://landley.net/aboriginal/about.html
and shares most of the same goals, with a much simpler implementation.
--- Quick Start
To install the build prerequisites: download toybox source, linux kernel source,
and one or more musl cross compiler toolchain(s) in the "ccc" directory:
$ cd toybox
$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
$ wget https://landley.net/bin/toolchains/latest/i686-linux-musl-cross.tar.xz
$ mkdir ccc
$ tar xvJCf ccc i686-linux-musl-cross.tar.xz
Then invoke mkroot like:
$ mkroot/mkroot.sh CROSS=i686 LINUX=linux
$ root/i686/run-qemu.sh
This project is a successor to https://landley.net/aboriginal/about.html
and shares most of the same goals, with a much simpler implementation.
--- Building without a cross compiler (warning: glibc sucks)
Running ./mkroot.sh with no arguments and no $CROSS_COMPILE environment
variable builds a statically linked root filesystem with the host's compiler.
$ mkroot/mkroot.sh
You can then chroot into it like this:
$ sudo chroot output/host/root /init
$ ls -l
$ exit
Unfortunately, glibc doesn't properly support static linking, so if your host
Linux uses glibc the build will spit out a bunch of warnings indicating
all sorts of glibc features won't work (DNS lookups always fail, ls -l can't
read names out of /etc/password, etc). This is a known problem with glibc,
because ex-maintainer Ulrich Drepper had a strong personal dislike of static
linking and actively sabotaged it.
If building on a non-glibc system, such as Alpine Linux, you're fine.
Otherwise, you'll probably want to cross compile with a musl-libc toolchain
to avoid glibc's very long list of static linking bugs. (The resulting root
filesystem is also significantly smaller: a stripped statically linked
"hello world" binary for x86-64 is 5420 bytes with musl-libc, and 682,696 bytes
with glibc.)</p>
--- Building with a cross compiler.
The variable $CROSS_COMPILE indicates the toolchain prefix to apply to
commands such as "cc" and "ld". Since prefixed cross compiler names tend
to look like "armv5l-cc" this prefix tends to end with a dash.
$ mkroot/mkroot.sh CROSS_COMPILE=armv5l-
If you haven't added the cross compiler to your $PATH, you can specify
a path as part of the prefix:
$ mkroot/mkroot.sh CROSS_COMPILE=~/x86_64-linux-musl-cross/bin/x86_64-linux-musl-cross-
Don't forget the trailing dash.
Alternately, the variable $CROSS (as used in the Quick Start above) tells
mkroot to look in the "ccc" directory for a cross compiler starting with
a short name:
$ mkroot/mkroot.sh CROSS=s390x
That would look (using wildcards) for ccc/s390x-*cross/bin/s390x*-cc and
if found, work out the appropriate $CROSS_COMPILER prefix to use for the
corresponding other tools. Use "CROSS=help" to see the list of cross compilers
currently available in the ccc directory.
You only need to set one of $CROSS or $CROSS_COMPILE, the other gets derived
from the one you provided.
The downloadable toolchains were built with toybox's scripts/mcm-buildall.sh
running in a fresh checkout of https://github.com/richfelker/musl-cross-make
and are available as prebuilt binaries from https://landley.net/bin/toolchains
(The "native" compilers run _on_ the target system, as well as producing
binaries for them. Those are packaged as squashfs filesystems, to be loopback
mounted within qemu.)
--- Adding a kernel
On the mkroot command line add LINUX= pointing to a kernel source directory:
$ mkroot/mkroot.sh CROSS=sh4 LINUX=~/linux
This will build a kernel for the appropriate target, package the filesystem
as cpio.gz for use by initramfs, and create a run-qemu.sh script to invoke
qemu. This results in the following files under root/$CROSS:
initramfs.cpio.gz - the "fs" dir packaged for initramfs, plus any $MODULES
linux-kernel - the compiled kernel
linux.dtb - The device tree binary (if this target requires one)
run-qemu.sh - the qemu invocation to run it all
And also:
fs/ - the generated root filesystem (you can chroot here)
docs/ - Additional information not needed to run qemu.
The run-qemu.sh script will connect together the appropriate -kernel, -initrd,
and -dtb arguments to consume the provided files, as well as -m board and
-append "kernel command line arguments". The KARGS environment variable is
added to the kernel command line arguments, and any additional arguments
provided to the script are passed through to qemu, so you can do:
$ KARGS="rdinit=/bin/sh" ./run-qemu.sh -hda blah.img
Running the script should boot the kernel to a command prompt, with the
serial console connected to stdin and stdout of the qemu process so you can
just type into it and see the output. The generated kernel config should
provide basic NAT network support (as if behind a router) and block device
support.
--- Environment variables
Any "name=value" argument provided on the mkroot.sh command line will set
an environment variable, and any string without an = indicates a package
script to run before building toybox (explained below). This is why CROSS=
CROSS_COMPILE= and LINUX= were all set on the command line above.
For portability reasons, mkroot.sh clears all environment variables at the
start of its run, with the following exceptions:
LINUX - Linux kernel source directory.
CROSS_COMPILE - Cross compiler prefix (sets $CROSS from prefix before first -)
CROSS - Short target name (sets $CROSS_COMPILE from ccc)
HOME - Absolute path to user's home directory.
PATH - Executable path to find binaries.
NOCLEAR - Don't clear environment variables. (Can't set on command line.)
Other interesting variables to set on the command line include:
NOAIRLOCK - don't do a hermetic build, just use the $PATH's tools.
NOLOGPATH - don't use the command line recording wrapper
NOLOG - don't record build output to root/build/log/$CROSS.[yn]
NOTOYBOX - don't build toybox
PENDING - extra commands to enable out of toys/pending
KEXTRA - Additional kernel symbols to enable (in short CSV format)
MODULES - Kernel modules to build (in short CSV format)
--- Adding build modules
You can run additional build scripts from the mkroot/packages directory by
listing them on the command line:
$ mkroot/mkroot.sh dropbear overlay OVERLAY=~/blah
Any "name=value" argument provided on the command line will set an environment
variable in mkroot (explained above), and any string that without an =
indicates a package script to run before building toybox.
The provided build scripts mostly download source tarballs, cross compile them,
and install them into the root filesystem. Additional package build instructions
are available from the "Linux From Scratch" (http://linuxfromscratch.org/lfs)
and "Beyond Linux From Scratch" (http://linuxfromscratch.org/blfs) projects.
If you specify any packages, the "plumbing" package is automatically read first
to provide the download, setupfor, and cleanup shell functions to fetch and
manage source tarballs, and set the $DOWNLOAD variable (defaulting to store
downloaded tarballs in "./root_download").
The "overlay" script copies the $OVERLAY directory (default "./overlay")
into the root filesystem, so you can add arbitrary additional files.
The "dynamic" script attempts to copy dynamic libraries out of the
toolchain, to allow a dynamically linked root filesystem. It's a work in
progress. (A debian host toolchain can have multiple gigabytes of shared
libraries.)
The "tests" script copies the toybox test suite into the new filesystem,
downloads some test files, and adds some test modules to the kernel build.
--- Creating new build modules
Build scripts run after creating the directory layout and writing
the the init script and etc files (resolv.conf/passwd/group), but before
building toybox.
These scripts are sourced, not run, so environment variables you set remain
in force. The following variables can affect the remaining mkroot.sh build:
NOTOYBOX - if set, toybox will not be installed into the new root filesystem
KEXTRA - additional kernel symbols to enable (in same CSV format as $KCONF)
QEMU_MORE - Additional qemu command line arguments added to run-qemu.sh
To append instead of replacing (in case they're already set), you can use
QEMU_MORE="$QEMU_MORE --blah" and KEXTRA="${KEXTRA+$KEXTRA,}"BLAH,BLAH,BLAH
If you check your own build scripts into mkroot/packages without touching
any existing files, you should be able to "git pull --ff" to update your tree
without conflicts. Alternately, you can add your script directory to the start
of the $PATH and bash's "source" command will fall back to looking there next.
+18
View File
@@ -0,0 +1,18 @@
Use ./run-qemu.sh to boot system image to a shell prompt, "exit" when done.
Additional arguments to run-qemu.sh are QEMU arguments,
$KARGS contains additional linux kernel arguments. For example:
KARGS=quiet ./run-qemu.sh -hda docs/linux-fullconfig
# cat /dev/?da
# exit
To extract the root filesystem from cpio.gz and chroot into it
( mkdir fs && cd fs && zcat ../initramfs.cpio.gz | cpio -i -d -H newc )
chroot fs /init
To recreate the initramfs.cpio.gz from fs directory
( cd fs && find . -printf '%P\n' | cpio -o -H newc -R +0:+0 | gzip ) \
> initramfs.cpio.gz
+389
View File
@@ -0,0 +1,389 @@
#!/bin/bash
# ------------------------------ Part 1: Setup -------------------------------
# Clear environment variables by restarting script w/bare minimum passed through
[ -z "$NOCLEAR" ] && exec env -i NOCLEAR=1 HOME="$HOME" PATH="$PATH" \
LINUX="$LINUX" CROSS="$CROSS" CROSS_COMPILE="$CROSS_COMPILE" "$0" "$@"
! [ -d mkroot ] && echo "Run mkroot/mkroot.sh from toybox source dir." && exit 1
# assign command line NAME=VALUE args to env vars, the rest are packages
for i in "$@"; do
[ "${i/=/}" != "$i" ] && export "$i" || { [ "$i" != -- ] && PKG="$PKG $i"; }
done
# Set default directory locations (overrideable from command line)
: ${TOP:=$PWD/root} ${BUILD:=$TOP/build} ${LOG:=$BUILD/log}
: ${AIRLOCK:=$BUILD/airlock} ${CCC:=$PWD/ccc} ${PKGDIR:=$PWD/mkroot/packages}
announce() { printf "\033]2;$CROSS $*\007" 2>/dev/null >/dev/tty; printf "\n=== $*\n";}
die() { echo "$@" >&2; exit 1; }
# ----- Are we cross compiling (via CROSS_COMPILE= or CROSS=)
if [ -n "$CROSS_COMPILE" ]; then
# airlock needs absolute path
[ -z "${X:=$(command -v "$CROSS_COMPILE"cc)}" ] && die "no ${CROSS_COMPILE}cc"
CROSS_COMPILE="$(realpath -s "${X%cc}")"
[ -z "$CROSS" ] && CROSS=${CROSS_COMPILE/*\//} CROSS=${CROSS/-*/}
elif [ -n "$CROSS" ]; then # CROSS=all/allnonstop/$ARCH else list known $ARCHes
[ ! -d "$CCC" ] && die "No ccc symlink to compiler directory."
TARGETS="$(ls "$CCC" | sed -n 's/-.*//p' | sort -u)"
if [ "${CROSS::3}" == all ]; then # loop calling ourselves for each target
for i in $TARGETS; do
"$0" "$@" CROSS=$i || [ "$CROSS" == allnonstop ] || exit 1
done; exit
else # Find matching cross compiler under ccc/ else list available targets
CROSS_COMPILE="$(echo "$CCC/$CROSS"-*cross/bin/"$CROSS"*-cc)" # wildcard
[ ! -e "$CROSS_COMPILE" ] && echo $TARGETS && exit # list available targets
CROSS_COMPILE="${CROSS_COMPILE%cc}" # trim to prefix for cc/ld/as/nm/strip
fi
fi
# Set per-target output directory (using "host" if not cross-compiling)
: ${CROSS:=host} ${OUTPUT:=$TOP/$CROSS} ${OUTDOC:=$OUTPUT/docs}
# Verify selected compiler works
${CROSS_COMPILE}cc --static -xc - -o /dev/null <<< "int main(void){return 0;}"||
die "${CROSS_COMPILE}cc can't create static binaries"
# ----- Create hermetic build environment
rm -rf generated
if [ -z "$NOAIRLOCK"] && [ -n "$CROSS_COMPILE" ]; then
# When cross compiling set host $PATH to binaries with known behavior by
# - building a host toybox later builds use as their command line
# - cherry-picking specific commands from old path via symlink
if [ ! -e "$AIRLOCK/toybox" ]; then
announce "airlock" &&
PREFIX="$AIRLOCK" KCONFIG_CONFIG=.singleconfig_airlock CROSS_COMPILE= \
make clean defconfig toybox install_airlock && # see scripts/install.sh
rm .singleconfig_airlock || exit 1
fi
export PATH="$AIRLOCK"
fi
# Create per-target work directories
TEMP="$BUILD/${CROSS}-tmp" && rm -rf "$TEMP" &&
mkdir -p "$TEMP" "$OUTPUT" "$LOG" || exit 1
[ -z "$ROOT" ] && ROOT="$OUTPUT/fs" && rm -rf "$ROOT"
LOG="$LOG/$CROSS"
# ----- log build output
# Install command line recording wrapper, logs all commands run from $PATH
if [ -z "$NOLOGPATH" ]; then
# Move cross compiler into $PATH so calls to it get logged
[ -n "$CROSS_COMPILE" ] && PATH="${CROSS_COMPILE%/*}:$PATH" &&
CROSS_COMPILE=${CROSS_COMPILE##*/}
export WRAPDIR="$BUILD/record-commands" LOGPATH="$LOG"-commands.txt
rm -rf "$WRAPDIR" "$LOGPATH" generated/obj &&
eval "$(WRAPDIR="$WRAPDIR" CROSS_COMPILE= NOSTRIP=1 mkroot/record-commands)"||
exit 1
fi
# Start logging stdout/stderr
rm -f "$LOG".{n,y} || exit 1
[ -z "$NOLOG" ] && exec > >(tee "$LOG".n) 2>&1
echo "Building for $CROSS"
# ---------------------- Part 2: Create root filesystem -----------------------
# ----- Create new root filesystem's directory layout.
# FHS wants boot media opt srv usr/{local,share}, stuff under /var...
mkdir -p "$ROOT"/{dev,etc/rc,home,mnt,proc,root,sys,tmp/run,usr/{bin,sbin,lib},var} &&
chmod a+rwxt "$ROOT"/tmp && ln -s usr/{bin,sbin,lib} tmp/run "$ROOT" || exit 1
# Write init script. Runs as pid 1 from initramfs to set up and hand off system.
cat > "$ROOT"/init << 'EOF' &&
#!/bin/sh
export HOME=/home PATH=/bin:/sbin
if ! mountpoint -q dev; then
mount -t devtmpfs dev dev
[ $$ -eq 1 ] && ! 2>/dev/null <0 && exec 0<>/dev/console 1>&0 2>&1
for i in ,fd /0,stdin /1,stdout /2,stderr
do ln -sf /proc/self/fd${i/,*/} dev/${i/*,/}; done
mkdir -p dev/shm
chmod +t /dev/shm
fi
mountpoint -q dev/pts || { mkdir -p dev/pts && mount -t devpts dev/pts dev/pts;}
mountpoint -q proc || mount -t proc proc proc
mountpoint -q sys || mount -t sysfs sys sys
echo 0 99999 > /proc/sys/net/ipv4/ping_group_range
if [ $$ -eq 1 ]; then
mountpoint -q mnt || [ -e /dev/?da ] && mount /dev/?da /mnt
# Setup networking for QEMU (needs /proc)
ifconfig lo 127.0.0.1
ifconfig eth0 10.0.2.15
route add default gw 10.0.2.2
[ "$(date +%s)" -lt 1000 ] && timeout 2 sntp -sq 10.0.2.2 # Ask host
[ "$(date +%s)" -lt 10000000 ] && sntp -sq time.google.com
# Run package scripts (if any)
for i in $(ls -1 /etc/rc 2>/dev/null | sort); do . /etc/rc/"$i"; done
echo 3 > /proc/sys/kernel/printk
[ -z "$HANDOFF" ] && [ -e /mnt/init ] && HANDOFF=/mnt/init
[ -z "$HANDOFF" ] && HANDOFF=/bin/sh && echo -e '\e[?7hType exit when done.'
setsid -c <>/dev/$(sed '$s@.*[ /]@@' /sys/class/tty/console/active) >&0 2>&1 \
$HANDOFF
reboot -f &
sleep 5
else # for chroot
/bin/sh
umount /dev/pts /dev /sys /proc
fi
EOF
chmod +x "$ROOT"/init &&
# Google's nameserver, passwd+group with special (root/nobody) accounts + guest
echo "nameserver 8.8.8.8" > "$ROOT"/etc/resolv.conf &&
cat > "$ROOT"/etc/passwd << 'EOF' &&
root:x:0:0:root:/root:/bin/sh
guest:x:500:500:guest:/home/guest:/bin/sh
nobody:x:65534:65534:nobody:/proc/self:/dev/null
EOF
echo -e 'root:x:0:\nguest:x:500:\nnobody:x:65534:' > "$ROOT"/etc/group &&
# Grab toybox version git or toys.h
: ${VERSION:=$(git describe --tags --abbrev=12 2>/dev/null)} &&
: ${VERSION:=$(sed -n 's/.*TOYBOX_VERSION "\([^"]*\)".*/\1/p' toys.h)} &&
# Optional file, basically a comment
echo $'NAME="mkroot"\nVERSION="'${VERSION#* }$'"\nHOME_URL="https://landley.net/toybox"' > "$ROOT"/etc/os-release || exit 1
# Build any packages listed on command line
for i in ${PKG:+plumbing $PKG}; do
pushd .
announce "$i"; PATH="$PKGDIR:$PATH" source $i || die $i
popd
done
# Build static toybox with existing .config if there is one, else defconfig+sh
if [ -z "$NOTOYBOX" ]; then
announce toybox
[ -n "$PENDING" ] && rm -f .config
grep -q CONFIG_SH=y .config 2>/dev/null && CONF=silentoldconfig || unset CONF
for i in $PENDING sh route; do XX="$XX"$'\n'CONFIG_${i^^?}=y; done
[ -e "$ROOT"/lib/libc.so ] || export LDFLAGS=--static
PREFIX="$ROOT" make clean \
${CONF:-defconfig KCONFIG_ALLCONFIG=<(echo "$XX")} toybox install || exit 1
unset LDFLAGS
fi
# ------------------ Part 3: Build + package bootable system ------------------
# Convert comma separated values in $1 to CONFIG=$2 lines
csv2cfg() { sed -E '/^$/d;s/([^,]*)($|,)/CONFIG_\1\n/g' <<< "$1" | sed '/^$/!{/=/!s/.*/&='"$2/}";}
be2csv() { eval "echo $*" | tr ' ' ,; } # brace expansion to csv
# Set variables from $CROSS, die on unrecognized target:
# BUILTIN - if set, statically link initramfs into kernel image
# DTB - device tree binary file in build dir (qemu -dtb $DTB)
# KARCH - linux ARCH= build argument (selects arch/$ARCH directory in source)
# KARGS - linux kernel command line arguments (qemu -append "console=$KARGS")
# KCONF - kernel config options for target (expanded by csv2cfg above)
# VMLINUX - linux bootable kernel file in build dir (qemu -kernel $VMLINUX)
# QEMU - emulator name (qemu-system-$QEMU) and arguments
get_target_config()
{
# Target-specific info in an (alphabetical order) if/else staircase
# Each target needs board config, serial console, RTC, ethernet, block device.
KARGS=ttyS0 VMLINUX=vmlinux
if [ "$CROSS" == armv5l ] || [ "$CROSS" == armv4l ]; then
# This could use the same VIRT board as armv7, but let's demonstrate a
# different one requiring a separate device tree binary.
KARCH=arm KARGS=ttyAMA0 VMLINUX=zImage
QEMU="arm -M versatilepb"
KCONF="$(be2csv CPU_ARM926T MMU VFP ARM_THUMB AEABI ARCH_VERSATILE ATAGS \
DEPRECATED_PARAM_STRUCT BLK_DEV_SD GPIOLIB NET_VENDOR_SMSC SMC91X \
ARM_ATAG_DTB_COMPAT{,_CMDLINE_EXTEND} PCI{,_VERSATILE} \
SERIAL_AMBA_PL011{,_CONSOLE} RTC_{CLASS,DRV_PL031,HCTOSYS} \
SCSI{,_LOWLEVEL,_SYM53C8XX_{2,MMIO,DMA_ADDRESSING_MODE=0}})"
DTB=versatile-pb.dtb
elif [ "$CROSS" == armv7l ] || [ "$CROSS" == aarch64 ]; then
if [ "$CROSS" == aarch64 ]; then
QEMU="aarch64 -M virt -cpu cortex-a57" KARCH=arm64 VMLINUX=Image
else
QEMU="arm -M virt" KARCH=arm VMLINUX=zImage
fi
KARGS=ttyAMA0
KCONF="$(be2csv MMU SOC_DRA7XX VDSO CPU_IDLE KERNEL_MODE_NEON \
ARCH_{MULTI_V7,VIRT,OMAP2PLUS_TYPICAL,ALPINE} ARM_{THUMB,CPUIDLE,LPAE} \
ATA{,_SFF,_BMDMA,_PIIX,_GENERIC} VIRTIO_{MENU,NET,BLK,PCI,MMIO} \
SERIAL_AMBA_PL011{,_CONSOLE} RTC_{CLASS,HCTOSYS,DRV_PL031} \
PATA_{,OF_}PLATFORM PCI{,_HOST_GENERIC})"
elif [ "$CROSS" == hexagon ]; then
QEMU_M=comet KARCH="hexagon LLVM_IAS=1" KCONF=SPI,SPI_BITBANG,IOMMU_SUPPORT
elif [ "$CROSS" == i486 ] || [ "$CROSS" == i686 ] ||
[ "$CROSS" == x86_64 ] || [ "$CROSS" == x32 ]; then
if [ "$CROSS" == i486 ]; then
QEMU="i386 -cpu 486 -global fw_cfg.dma_enabled=false" KCONF=M486
elif [ "$CROSS" == i686 ]; then
QEMU="i386 -cpu pentium3" KCONF=MPENTIUMII
else
QEMU=x86_64 KCONF=64BIT
[ "$CROSS" == x32 ] && KCONF=X86_X32
fi
KARCH=x86 VMLINUX=bzImage
KCONF+=,"$(be2csv UNWINDER_FRAME_POINTER PCI BLK_DEV_SD NET_VENDOR_INTEL \
E1000 RTC_CLASS ATA{,_SFF,_BMDMA,_PIIX} SERIAL_8250{,_CONSOLE})"
elif [ "$CROSS" == m68k ]; then
QEMU_M=q800 KARCH=m68k
KCONF="$(be2csv MMU M68040 M68KFPU_EMU MAC BLK_DEV_SD MACINTOSH_DRIVERS \
NET_VENDOR_NATSEMI MACSONIC SCSI{,_LOWLEVEL,_MAC_ESP} \
SERIAL_PMACZILOG{,_TTYS,_CONSOLE})"
elif [ "$CROSS" == microblaze ]; then
QEMU_M=petalogix-s3adsp1800 KARCH=microblaze KARGS=ttyUL0
KCONF="$(be2csv MMU CPU_BIG_ENDIAN SERIAL_UARTLITE{,_CONSOLE} \
XILINX_{EMACLITE,MICROBLAZE0_{FAMILY="spartan3adsp",USE_{{MSR,PCMP}_INSTR,BARREL,HW_MUL}=1}} \
NET_VENDOR_XILINX)"
elif [ "${CROSS#mips}" != "$CROSS" ]; then # mips mipsel mips64 mips64el
QEMU_M=malta KARCH=mips
KCONF="$(be2csv MIPS_MALTA CPU_MIPS32_R2 BLK_DEV_SD NET_VENDOR_AMD PCNET32 \
PCI SERIAL_8250{,_CONSOLE} ATA{,_SFF,_BMDMA,_PIIX} POWER_RESET{,_SYSCON})"
[ "${CROSS/64/}" == "$CROSS" ] && KCONF+=,CPU_MIPS32_R2 ||
KCONF+=,64BIT,CPU_MIPS64_R1,MIPS32_O32
[ "${CROSS%el}" != "$CROSS" ] && KCONF+=,CPU_LITTLE_ENDIAN
elif [ "$CROSS" == or1k ]; then
KARCH=openrisc QEMU_M=virt KARGS=ttyS0
KCONF="$(be2csv ETHOC SERIO SERIAL_OF_PLATFORM SERIAL_8250{,_CONSOLE} \
VIRTIO_{MENU,NET,BLK,PCI,MMIO} POWER_RESET{,_SYSCON{,_POWEROFF}} SYSCON_REBOOT_MODE)"
elif [ "$CROSS" == powerpc ]; then
KARCH=powerpc QEMU="ppc -M g3beige"
KCONF="$(be2csv ALTIVEC PATA_MACIO BLK_DEV_SD MACINTOSH_DRIVERS SERIO \
NET_VENDOR_{8390,NATSEMI} NE2K_PCI SERIAL_PMACZILOG{,_TTYS,_CONSOLE} \
ATA{,_SFF,_BMDMA} ADB{,_CUDA} BOOTX_TEXT PPC_{PMAC,OF_BOOT_TRAMPOLINE})"
elif [ "$CROSS" == powerpc64 ] || [ "$CROSS" == powerpc64le ]; then
KARCH=powerpc QEMU="ppc64 -M pseries -vga none" KARGS=hvc0
KCONF="$(be2csv PPC64 BLK_DEV_SD ATA NET_VENDOR_IBM IBMVETH HVC_CONSOLE \
PPC_{PSERIES,OF_BOOT_TRAMPOLINE,TRANSACTIONAL_MEM,DISABLE_WERROR} \
SCSI_{LOWLEVEL,IBMVSCSI})"
[ "$CROSS" == powerpc64le ] && KCONF=$KCONF,CPU_LITTLE_ENDIAN
elif [ "$CROSS" = riscv32 ] || [ "$CROSS" = riscv64 ]; then
# Note: -hda file.img doesn't work, but this insane overcomplicated pile:
# -drive file=file.img,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
QEMU_M="virt -netdev user,id=net0 -device virtio-net-device,netdev=net0"
KARCH=riscv VMLINUX=Image
# Probably only about half of these kernel symbols are actually needed?
KCONF="$(be2csv MMU SOC_VIRT NONPORTABLE CMODEL_MEDANY \
RISCV_ISA_{ZICBO{M,Z},FALLBACK} FPU PCI{,_HOST_GENERIC} BLK_DEV_SD \
SCSI_{PROC_FS,LOWLEVEL,VIRTIO} VIRTIO_{MENU,NET,BLK,PCI} SERIO_SERPORT \
SERIAL_{EARLYCON,8250{,_CONSOLE,_PCI},OF_PLATFORM} HW_RANDOM{,_VIRTIO} \
RTC_{CLASS,HCTOSYS} DMADEVICES VIRTIO_{MENU,PCI{,_LEGACY},INPUT,MMIO})"
[ "$CROSS" = riscv32 ] && KCONF+=,ARCH_RV32I
elif [ "$CROSS" = s390x ]; then
KARCH=s390 VMLINUX=bzImage
KCONF="$(be2csv MARCH_Z900 PACK_STACK S390_GUEST VIRTIO_{NET,BLK} \
SCLP_VT220_{TTY,CONSOLE})"
elif [ "$CROSS" == sh2eb ]; then
BUILTIN=1 KARCH=sh
KCONF="$(be2csv CPU_{SUBTYPE_J2,BIG_ENDIAN} SH_JCORE_SOC SMP JCORE_EMAC \
FLATMEM_MANUAL MEMORY_START=0x10000000 CMDLINE_OVERWRITE DNOTIFY FUSE_FS \
INOTIFY_USER SPI{,_JCORE} SERIAL_UARTLITE{,_CONSOLE} PWRSEQ_SIMPLE \
MMC{,_BLOCK,_SPI} UIO{,_PDRV_GENIRQ} MTD{,_SPI_NOR,_SST25L,_OF_PARTS} \
BINFMT_{ELF_FDPIC,MISC} I2C{,_HELPER_AUTO})"
KCONF+=,CMDLINE=\"console=ttyUL0\ earlycon\"
elif [ "$CROSS" == sh4 ] || [ "$CROSS" == sh4eb ]; then
QEMU_M="r2d -serial null -serial mon:stdio" KARCH=sh
KARGS="ttySC1 noiotrap" VMLINUX=zImage
KCONF="$(be2csv CPU_SUBTYPE_SH7751R MMU VSYSCALL SH_{FPU,RTS7751R2D} PCI \
RTS7751R2D_PLUS SERIAL_SH_SCI{,_CONSOLE} NET_VENDOR_REALTEK 8139CP \
BLK_DEV_SD ATA{,_SFF,_BMDMA} PATA_PLATFORM BINFMT_ELF_FDPIC \
CMDLINE_FROM_BOOTLOADER MEMORY_START=0x0c000000)"
#see also SPI{,_SH_SCI} MFD_SM501 RTC_{CLASS,DRV_{R9701,SH},HCTOSYS}
[ "$CROSS" == sh4eb ] && KCONF+=,CPU_BIG_ENDIAN
else die "Unknown \$CROSS=$CROSS"
fi
: ${QEMU:=$CROSS ${QEMU_M:+-M $QEMU_M}}
}
# Linux kernel .config symbols common to all architectures
: ${GENERIC_KCONF:=$(be2csv PANIC_TIMEOUT=1 NO_HZ_IDLE HIGH_RES_TIMERS RD_GZIP \
BINFMT_{ELF,SCRIPT} BLK_DEV{,_INITRD,_LOOP} EXT4_{FS,USE_FOR_EXT2} \
VFAT_FS FAT_DEFAULT_UTF8 MISC_FILESYSTEMS NLS_{CODEPAGE_437,ISO8859_1} \
SQUASHFS{,_XATTR,_ZLIB} TMPFS{,_POSIX_ACL} DEVTMPFS{,_MOUNT} \
NET{,DEVICES,_CORE,CONSOLE} PACKET UNIX INET IPV6 ETHERNET \
COMPAT_32BIT_TIME EARLY_PRINTK IKCONFIG{,_PROC})}
# ----- Build kernel for target
INITRAMFS=initramfs.cpio.gz
if [ -z "$LINUX" ] || [ ! -d "$LINUX/kernel" ]; then
echo 'No $LINUX directory, kernel build skipped.'
else
# Which architecture are we building a kernel for?
LINUX="$(realpath "$LINUX")"
[ "$CROSS" == host ] && CROSS="$(uname -m)"
get_target_config
# Write the qemu launch script
if [ -n "$QEMU" ]; then
[ -z "$BUILTIN" ] && INITRD='-initrd "$DIR"/'"$INITRAMFS"
{ echo DIR='"$(dirname $0)";' qemu-system-"$QEMU" -m 256 '"$@"' $QEMU_MORE \
-nographic -no-reboot -kernel '"$DIR"'/linux-kernel $INITRD \
${DTB:+-dtb '"$DIR"'/linux.dtb} \
"-append \"HOST=$CROSS console=$KARGS \$KARGS\"" &&
echo "echo -e '\\e[?7h'"
} > "$OUTPUT"/run-qemu.sh &&
chmod +x "$OUTPUT"/run-qemu.sh || exit 1
fi
announce "linux-$KARCH"
pushd "$LINUX" && make distclean && popd &&
cp -sfR "$LINUX" "$TEMP/linux" && pushd "$TEMP/linux" &&
# Write microconfig (minimal symbol name/value list in CSV format)
mkdir -p "$OUTDOC" &&
for i in "$GENERIC_KCONF" "$KCONF" ${MODULES+MODULES,MODULE_UNLOAD} "$KEXTRA"
do echo "$i"; done > "$OUTDOC"/linux-microconfig &&
# expand to miniconfig (symbol list to switch on after running "allnoconfig")
{
echo "# make ARCH=$KARCH allnoconfig KCONFIG_ALLCONFIG=linux-miniconfig"
echo "# make ARCH=$KARCH -j \$(nproc)"
echo "# boot $VMLINUX${DTB:+ dtb $DTB} console=$KARGS"
echo
while read i; do
echo "# architecture ${X:-independent}"
csv2cfg "$i" y
X=${X:+extra} X=${X:-specific}
done < "$OUTDOC"/linux-microconfig
[ -n "$BUILTIN" ] && echo -e CONFIG_INITRAMFS_SOURCE="\"$OUTPUT/fs\""
for i in $MODULES; do csv2cfg "$i" m; done
} > "$OUTDOC/linux-miniconfig" &&
# Expand miniconfig to full .config
make ARCH=$KARCH allnoconfig KCONFIG_ALLCONFIG="$OUTDOC/linux-miniconfig" &&
cp .config "$OUTDOC/linux-fullconfig" &&
# Build kernel. Copy config, device tree binary, and kernel binary to output
make ARCH=$KARCH CROSS_COMPILE="$CROSS_COMPILE" -j $(nproc) all || exit 1
[ -n "$DTB" ] && { cp "$(find -name $DTB)" "$OUTPUT/linux.dtb" || exit 1 ;}
if [ -n "$MODULES" ]; then
make ARCH=$KARCH INSTALL_MOD_PATH=modz modules_install &&
(cd modz && find lib/modules | cpio -o -H newc -R +0:+0 ) | gzip \
> "$OUTDOC/modules.cpio.gz" || exit 1
fi
[ ! -e "$VMLINUX" ] && VMLINUX=arch/$KARCH/boot/$VMLINUX
cp "$VMLINUX" "$OUTPUT"/linux-kernel && cd .. && rm -rf linux && popd ||exit 1
fi
# clean up and package root filesystem for initramfs.
announce initramfs
[ -z "$BUILTIN" ] && DIR="$OUTPUT" || DIR="$OUTDOC"
{ (cd "$ROOT" && find . -printf '%P\n' | cpio -o -H newc -R +0:+0 ) || exit 1
! test -e "$OUTDOC/modules.cpio.gz" || zcat $_;} | gzip \
> "$DIR/$INITRAMFS" || exit 1
mv "$LOG".{n,y} && echo "Output is in $OUTPUT"
rmdir "$TEMP" 2>/dev/null || exit 0 # remove if empty, not an error
+48
View File
@@ -0,0 +1,48 @@
#!/bin/echo Try "scripts/mkroot.sh busybox"
download a5d40ca0201b20909f7a8a561adf57adccc8a877 \
http://www.busybox.net/downloads/busybox-1.36.1.tar.bz2
# 4 commands: ash, route, udhcpc, stty
setupfor busybox
make defconfig &&
# Busybox checks for host bzip2, which toybox does not provide.
sed -i 's/^bzip2/true bzip2/' scripts/{mkconfigs,embedded_scripts} &&
# zap script that wants diff
ln -sf /bin/true scripts/generate_BUFSIZ.sh &&
echo '#define COMMON_BUFSIZE (4096)
extern char bb_common_bufsiz1[];
#define setup_common_bufsiz()' > include/common_bufsiz.h &&
LDFLAGS=--static make SKIP_STRIP=y -j $(nproc) &&
cp busybox "$ROOT/bin" &&
make busybox.links &&
mkdir -p "$ROOT/busybox" || exit 1
while read i; do ln -sf /bin/busybox "$ROOT/busybox/$(basename "$i")" || exit 1
done < busybox.links
cp .config "$ROOT/../busybox-config"
cleanup
# busybox ash doesn't support $(<file)
#ln -sf busybox "$ROOT/bin/sh" &&
#mkdir -p "$ROOT/etc/rc" &&
#echo '[ -z "$CONSOLE" ] && CONSOLE="$(cat /sys/class/tty/console/active)"' > \
# "$ROOT/etc/rc/busybox.sh" &&
cat > "$ROOT"/etc/dhcp.sh << 'EOF' &&
#!/bin/sh
[ "$1" = bound ] || exit
[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
[ -n "$subnet" ] && NETMASK="netmask $subnet"
ifconfig $interface $ip $BROADCAST $NETMASK
[ -n "$router" ] && exit
echo "deleting routers"
while route del default gw 0.0.0.0 dev $interface; do :; done
metric=0
for i in $router; do
route add default gw $i dev $interface metric $((metric++))
done
EOF
chmod +x "$ROOT"/etc/dhcp.sh || exit 1
+59
View File
@@ -0,0 +1,59 @@
#!/bin/echo Try "mkroot/mkroot.sh dropbear"
# Example overlay file, adding dropbear (which requires zlib)
echo === download source
download f535367b1a11e2f9ac3bec723fb007fbc0d189e5 \
https://www.zlib.net/fossils/zlib-1.3.1.tar.gz
download 216ae176572dc008e128042eae82b6aacfdc8a51 \
https://matt.ucc.asn.au/dropbear/releases/dropbear-2024.86.tar.bz2
echo === Native build static zlib
setupfor zlib
# They keep checking in broken generated files.
rm -f Makefile zconf.h &&
CC=${CROSS_COMPILE}cc LD=${CROSS_COMPILE}ld AS=${CROSS_COMPILE}as ./configure &&
make -j $(nproc) || exit 1
# do _not_ cleanup zlib, we need the files we just built for dropbear
echo === $HOST Native build static dropbear
setupfor dropbear
# Repeat after me: "autoconf is useless"
echo 'echo "$@"' > config.sub &&
ZLIB="$(echo ../zlib*)" &&
CC="$CROSS_COMPILE"cc CFLAGS="-I $ZLIB -O2" LDFLAGS="-L $ZLIB" ./configure --enable-static \
--disable-wtmp --host="$(basename "$CROSS_COMPILE" | sed 's/-$//')" &&
sed -i 's@/usr/bin/dbclient@ssh@;s@\(#define NON_INETD_MODE\) 1@\1 0@' \
src/default_options.h &&
make -j $(nproc) PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" MULTI=1 SCPPROGRESS=1 &&
${CROSS_COMPILE}strip dropbearmulti &&
mkdir -p "$ROOT"/{bin,etc/{rc,dropbear},var/log} &&
touch "$ROOT"/var/log/lastlog &&
cp dropbearmulti "$ROOT"/bin || exit 1
for i in "$ROOT"/bin/{ssh,dropbear,scp,dropbearkey}
do
ln -s dropbearmulti $i || exit 1
done
# We didn't cleanup zlib
unset ZLIB
rm -rf ../zlib-*
# cleanup dropbear
cleanup
# user root password root, user guest no password
echo -e 'root:$1$939UTPzb$/PfVYAsF2Hqi/AQ3UBjbK/:::::::\nguest::::::::' > "$ROOT"/etc/shadow &&
chmod 600 "$ROOT"/etc/shadow &&
echo 'netcat -p 22 -L dropbear -iRB &' > "$ROOT"/etc/rc/dropbear &&
# file to run on host to ssh into guest
echo 'ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" ${1:+$1@}127.0.0.1 -p 2222' > "$OUTPUT"/ssh2dropbear.sh &&
chmod +x "$OUTPUT"/ssh2dropbear.sh
# Forward 127.0.0.1:2222 into qemu instance
QEMU_MORE+=" -nic user,hostfwd=tcp:127.0.0.1:2222-:22"
+15
View File
@@ -0,0 +1,15 @@
#!/bin/echo Try "mkroot/mkroot.sh dynamic"
# Copy dynamic libraries from cross compiler
"${CROSS_COMPILE}cc" -xc - <<< 'void main(void) {;}' ||
die "${CROSS_COMPILE}cc can't create dynamic binaries"
LDSO="$("${CROSS_COMPILE}readelf" -a a.out | sed -n 's/.*interpreter: \([^]]*\)[]]$/\1/p')"
mkdir -p "$ROOT"/"$(dirname "$LDSO")" &&
cp "$LDSO" "$ROOT"/"$LDSO" || die "Couldn't copy ldso"
unset LDSO
"${CROSS_COMPILE}cc" -print-search-dirs | sed -n 's/libraries: =//p' | \
tr : '\n' | while read i; do
[ -e "$i" ] && find "$i" -maxdepth 1 -name '*.so' -o -name '*.so*[0-9]'
done | while read i; do cp -a "$i" "$ROOT"/lib/; done
+35
View File
@@ -0,0 +1,35 @@
#!/bin/echo Try "mkroot/mkroot.sh lfs"
[ -z "$(which mksquashfs)" ] && echo "no squashfs" && exit 1
# Download osuosl's rollup tarball of all the LFS packages.
download 45a27da2ee443a8e35a7e29db8a0c6877bbb98bb \
http://ftp.osuosl.org/pub/lfs/lfs-packages/lfs-packages-12.1.tar
# This one's a little weird, we're creating a target-agonstic squashfs image
# not part of the initramfs.
setupfor lfs-packages
LFS="$OUTPUT/lfs" LFSRC="$LFS/src"
rm -rf "$LFS" && mkdir -p "$LFSRC/tzdata" &&
# Fixup names
tar xfC tzdata*.tar.gz "$LFSRC/tzdata" && # Horrible package, no subdirectory!
rm tzdata*.tar.gz &&
mv {expect*,expect-0}.tar.gz && # broken name (no - before version)
rm -f tcl*-html.tar.gz && # Broken _and_ duplicate name
mv {tcl*,tcl-0}.tar.gz &&
mkdir sub || exit 1
# extract tarballs to package name in output and apply patches (if any)
for i in *.tar*; do
PKG="${i/-[0-9]*/}"
echo process $PKG
tar xfC $i sub && mv sub/* "$LFSRC/$PKG" || exit 1
for j in $PKG*.patch; do
[ -e "$j" ] && { ( cd "$LFSRC/$PKG" && patch -p1) < "$j" || exit 1 ; }
done
done
# Archive the sources
mksquashfs "$LFSRC" "$TOP"/lfs.sqf -noappend -all-root >/dev/null
+3
View File
@@ -0,0 +1,3 @@
#!/bin/echo Try "mkroot/mkroot.sh overlay"
cp -a "${OVERLAY:=overlay}"/. "$ROOT"/.
+47
View File
@@ -0,0 +1,47 @@
#!/bin/echo run this from "make root"
# Plumbing to download files
[ -z "$ROOT" ] && echo "no" && exit 1
mkdir -p "${DOWNLOAD:=$PWD/root_download}" || exit 1
### Functions to download, extract, and clean up after source packages.
# Usage: download HASH URL
# Grabs source from URL confirming SHA1 hash (Basically "wget $2")
# If extracted source is in $DOWNLOAD (no version) build will use that instead
download() {
local FILE="$(basename "$2")" WGET=wget
[ -d "$DOWNLOAD/${FILE/-*/}" ] && echo "$FILE" local && return 0
X=0; while true; do
[ "$(sha1sum < "$DOWNLOAD/$FILE" 2>/dev/null)" == "$1 -" ] &&
echo "$FILE" confirmed && break
rm -f $DOWNLOAD/${FILE/-[0-9]*/}-[0-9]* || exit 1
[ $X -eq 0 ] && X=1 || [ $X -eq 1 ] && X=2 WGET=/usr/bin/wget || exit 1
$WGET "$2" -O "$DOWNLOAD/$FILE"
done
}
# Usage: setupfor PACKAGE
# Extracts source tarball (or snapshot a repo) to create disposable build dir.
# Basically "tar -xvz -C $TEMP -f $DOWNLOAD/$1.tar.gz && cd $NEWDIR"
setupfor() {
PACKAGE="$(basename "$1")"
announce "$PACKAGE" && cd "$TEMP" && rm -rf "$PACKAGE" || exit 1
if [ -d "$DOWNLOAD/$PACKAGE" ]; then
cp -la "$DOWNLOAD/$PACKAGE/." "$PACKAGE" && cd "$PACKAGE" || exit 1
else
local DIR=$(mktemp -dp.)
tar xvafC "$DOWNLOAD/$PACKAGE"-*.t* "$DIR" &&
mv "$DIR"/* "$PACKAGE" && rmdir "$DIR" && cd "$PACKAGE" || exit 1
fi
}
# Usage: cleanup
# Delete setupfor's dir, exiting if build failed (basically "rm -rf $PACKAGE")
cleanup() {
[ $? -ne 0 ] && exit 1
[ -z "$PACKAGE" ] && exit 1
[ ! -z "$NO_CLEANUP" ] && return
cd .. && rm -rf "$PACKAGE"* || exit 1
}
+14
View File
@@ -0,0 +1,14 @@
#!/bin/echo Try "mkroot/mkroot.sh $0"
# Alas http://www.linux-usb.org/usb.ids is not versioned, so...
download 36d4e16755502fbc684be75e56841e1014e4a94a \
https://github.com/usbids/usbids/raw/a5edeafb6099/usb.ids
# Nor is https://pci-ids.ucw.cz/v2.2/pci.ids (tool version, not file version)
download 6694284723e034f0c564e81a30879939d5ef8b7e \
https://github.com/pciutils/pciids/raw/c7929c0f9480/pci.ids
cp "$DOWNLOAD"/{usb,pci}.ids "$ROOT/etc/" || exit 1
# add a couple test modules
MODULES+=FSCACHE,CACHEFILES
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
# Set up command recording wrapper
[ -z "$WRAPDIR" ] && WRAPDIR="$PWD"/record-commands && RM=$(which rm)
[ -z "$LOGPATH" ] && export LOGPATH="$PWD"/log.txt
if [ ! -x "$WRAPDIR/logpath" ]
then
LOG="$(which logpath)"
mkdir -p "$WRAPDIR" || exit 1
[ -e "$LOG" ] && cp -H "$LOG" "$WRAPDIR/logpath" || { cd "$(dirname $0)/.." &&
PREFIX="$WRAPDIR/" scripts/single.sh logpath >/dev/null &&
LOG="$PWD/logpath" || exit 1; }
tr : '\n' <<< "$PATH" | while read i; do
find "$i" \( -type f -o -type l \) -maxdepth 1 -executable -exec basename {} \; | \
while read FILE; do ln -s logpath "$WRAPDIR/$FILE" 2>/dev/null; done
done
fi
# Delete old log (if any)
rm -f "$LOGPATH"
# When sourced, set up wrapper for current context.
if [ $# -gt 0 ]
then
PATH="$WRAPDIR:$PATH" "$@"
X=$?
[ -n "$RM" ] && "$RM" -rf "$WRAPDIR"
exit $X
else
echo export LOGPATH=${LOGPATH@Q} PATH=${WRAPDIR@Q}:${PATH@Q}
fi
+41
View File
@@ -0,0 +1,41 @@
#!/bin/bash
# tar up completed system images to send to website, with READMEs
rm -f root/toybox-* root/*.tgz
for i in root/*/fs/bin/toybox
do
cp $i root/toybox-$(echo $i | sed 's@root/\([^/]*\)/.*@\1@') || exit 1
done
for i in root/*/run-qemu.sh
do
i=${i%/run-qemu.sh} j=${i#root/}
[ ! -e "$i" ] && continue
# Add README, don't include "fs" dir (you can extract it from cpio.gz)
cp mkroot/README.root $i/docs/README &&
tar cvzfC $i.tgz root --exclude=fs $j || exit 1
done
# Generate top level README
KVERS=$(toybox sed -n '3s@# Linux/[^ ]* \(.*\) Kernel Configuration@\1@p' root/*/docs/linux-fullconfig)
cat > root/README << EOF
Bootable system images created by:
mkroot/mkroot.sh LINUX=~/linux CROSS=allnonstop
Each system image is built from two packages: toybox and linux.
The run-qemu.sh script in each tarball should boot the system
to a shell prompt under qemu, exit from that shell to shut down the
virtual system and stop the emulator.
See https://landley.net/toybox/faq.html#mkroot for details.
Built from mkroot $(git describe --tags), and Linux $KVERS with patches in linux-patches/
EOF
if [ $# -eq 2 ]
then
scp root/toybox-* "$1/$2/" &&
scp root/*.tgz root/README "$1/mkroot/$2/"
fi
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
# usage: mkroot/testroot.sh [TARGET...]
#
# Test system image(s) (by booting qemu with -hda test.img providing /mnt/init)
# and check that:
#
# A) it boots and runs our code (which means /dev/hda works)
# B) the clock is set sanely ("make" is unhappy when source newer than output)
# C) it can talk to the virtual network
#
# Each successful test prints a === line, and all 3 means it passed.
# Writes result into root/build/test/$TARGET-test.txt
#
# With arguments, tests those targets (verbosely). With no arguments, tests
# each target with a linux-kernel (in parallel) and prints pass/fail summary.
die() { echo "$@"; exit 1; }
[ -n "$(which toybox)" -a -n "$(which mksquashfs)" ] ||
die "Need toybox and mksquashfs in $PATH"
mkdir -p "${TEST:=$PWD/root/build/test}" &&
# Setup test filesystem and package it into a squashfs.
cat > "$TEST"/init << 'EOF' &&
#!/bin/sh
echo
echo === init $HOST
[ "$(date +%s)" -gt 1500000000 ] && echo === date ok $HOST
wget http://10.0.2.2:65432 -O -
# TODO: cd /mnt && scripts/test.sh
EOF
chmod +x "$TEST"/init &&
mksquashfs "$TEST"/init configure scripts/ tests/ "$TEST"/init.sqf -noappend -all-root >/dev/null &&
# Setup server on host's loopback for network smoke test
echo === net ok > "$TEST"/index.html || die "smoketest setup"
toybox netcat -p 65432 -s 127.0.0.1 -L toybox httpd "$TEST" &
trap "kill $!" EXIT
sleep .25
[ -n "$(toybox wget http://127.0.0.1:65432/ -O - | grep ===)" ] || die "wget"
do_test()
{
X=$(dirname "$1") Y=$(basename $X)
[ ! -e "$1" ] && { echo skip "$Y"; return 0; }
# Alas KARGS=quiet doesn't silence qemu's bios, so filter output ourselves.
# QEMU broke -hda because too many people know how to use it, this is
# the new edgier version they added to be -hda without gratuitous breakage.
{
cd $X || continue
echo === $X
# Can't point two QEMU instances at same sqf because gratuitous file locking
cp "$TEST"/init.{sqf,$BASHPID} &&
# When stdin is a tty QEMU will SIGTTOU itself here, so </dev/null.
toybox timeout -i 10 bash -c "./run-qemu.sh -drive format=raw,file='$TEST'/init.$BASHPID < /dev/null 2>&1"
rm -f "$TEST/init.$BASHPID"
cd ../..
} | tee root/build/log/$Y-test.txt | { [ -z "$V" ] && cat >/dev/null || { [ "$V" -gt 1 ] && cat || grep '^=== '; } }
}
# Just test targets on command line?
if [ $# -gt 0 ]; then
((V++))
for I in "$@"; do do_test root/"$I"/linux-kernel; done
exit
fi
COUNT=0 CPUS=$(($(nproc)+0))
for I in root/*/linux-kernel
do
do_test "$I" | { [ -z "$V" ] && cat >/dev/null || { [ "$V" -gt 1 ] && cat || grep '^=== '; } } &
[ $((++COUNT)) -ge $CPUS ] &&
{ wait -n; ((--COUNT)); [ -z "$V" ] && echo -n .; }
done
while [ $COUNT -gt 0 ]; do
wait -n; ((--COUNT)); [ -z "$V" ] && echo -n .
done
echo
PASS= NOPASS=
for I in root/*/linux-kernel
do
[ ! -e "$I" ] && continue
X=$(dirname $I) Y=$(basename $X)
[ "$(grep '^=== ' root/build/log/$Y-test.txt | wc -l)" -eq 4 ] &&
PASS+="$Y " || NOPASS+="$Y "
done
[ -n "$PASS" ] && echo PASS=$PASS
[ -n "$NOPASS" ] && echo NOPASS=$NOPASS
bd() { sed 's@.*/\([^/]*\)/[^/]*@\1@'; }
NO="$(ls -d root/*/fs | bd | egrep -xv "$(ls root/*/linux-kernel | bd | tr '\n' '|')build" | xargs)"
[ -n "$NO" ] && echo No kernel: $NO