diff --git a/README.md b/README.md index 4a4a0db..cf10269 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,56 @@ You can exit the emulator using: \. (press Ctrl+A, leave it, afterwar An automated build script is provided to compile the RISC-V cross-compiler, Busybox, and Linux kernel from source. Please note that it only supports the Linux host environment. +To build everything, simply run: + ```shell $ make build-image ``` +This command invokes the underlying script: `scripts/build-image.sh`, which also offers more flexible usage options. + +### Script Usage + +``` +./scripts/build-image.sh [--buildroot] [--linux] [--all] [--external-root] [--clean-build] [--help] + +Options: + --buildroot Build Buildroot rootfs + --linux Build Linux kernel + --all Build both Buildroot and Linux + --external-root Use external rootfs instead of initramfs + --clean-build Remove entire buildroot/ and/or linux/ directories before build + --help Show this message +``` + +### Examples + +Build the Linux kernel only: + +``` +$ scripts/build-image.sh --linux +``` + +Build Buildroot only: + +``` +$ scripts/build-image.sh --buildroot +``` + +Build Buildroot and generate an external root file system (ext4 image): + +``` +$ scripts/build-image.sh --buildroot --external-root +``` + +Force a clean build: + +``` +$ scripts/build-image.sh --all --clean-build +$ scripts/build-image.sh --linux --clean-build +$ scripts/build-image.sh --buildroot --clean-build +``` + ## License `semu` is released under the MIT License. diff --git a/scripts/build-image.sh b/scripts/build-image.sh index ef65117..8866ebc 100755 --- a/scripts/build-image.sh +++ b/scripts/build-image.sh @@ -19,11 +19,31 @@ function OK PARALLEL="-j$(nproc)" +function safe_copy { + local src="$1" + local dst="$2" + + if [ ! -f "$dst" ]; then + echo "Copying $src -> $dst" + cp -f "$src" "$dst" + else + echo "$dst already exists, skipping copy" + fi +} + function do_buildroot { - ASSERT git clone https://github.com/buildroot/buildroot -b 2024.11.1 --depth=1 - cp -f configs/buildroot.config buildroot/.config - cp -f configs/busybox.config buildroot/busybox.config + if [ ! -d buildroot ]; then + echo "Cloning Buildroot..." + ASSERT git clone https://github.com/buildroot/buildroot -b 2024.11.1 --depth=1 + else + echo "buildroot/ already exists, skipping clone" + fi + + safe_copy configs/buildroot.config buildroot/.config + safe_copy configs/busybox.config buildroot/busybox.config + cp -f target/init buildroot/fs/cpio/init + # Otherwise, the error below raises: # You seem to have the current working directory in your # LD_LIBRARY_PATH environment variable. This doesn't work. @@ -32,13 +52,28 @@ function do_buildroot ASSERT make olddefconfig ASSERT make $PARALLEL popd - cp -f buildroot/output/images/rootfs.cpio ./ + + if [[ $EXTERNAL_ROOT -eq 1 ]]; then + echo "Copying rootfs.cpio to rootfs_full.cpio (external root mode)" + cp -f buildroot/output/images/rootfs.cpio ./rootfs_full.cpio + ASSERT ./scripts/rootfs_ext4.sh + else + echo "Copying rootfs.cpio to rootfs.cpio (initramfs mode)" + cp -f buildroot/output/images/rootfs.cpio ./rootfs.cpio + fi } function do_linux { - ASSERT git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git -b linux-6.12.y --depth=1 - cp -f configs/linux.config linux/.config + if [ ! -d linux ]; then + echo "Cloning Linux kernel..." + ASSERT git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git -b linux-6.12.y --depth=1 + else + echo "linux/ already exists, skipping clone" + fi + + safe_copy configs/linux.config linux/.config + export PATH="$PWD/buildroot/output/host/bin:$PATH" export CROSS_COMPILE=riscv32-buildroot-linux-gnu- export ARCH=riscv @@ -49,5 +84,74 @@ function do_linux popd } -do_buildroot && OK -do_linux && OK +function show_help { + cat << EOF +Usage: $0 [--buildroot] [--linux] [--all] [--external-root] [--clean-build] [--help] + +Options: + --buildroot Build Buildroot rootfs + --linux Build Linux kernel + --all Build both Buildroot and Linux + --external-root Use external rootfs instead of initramfs + --clean-build Remove entire buildroot/ and/or linux/ directories before build + --help Show this message +EOF + exit 1 +} + +BUILD_BUILDROOT=0 +BUILD_LINUX=0 +EXTERNAL_ROOT=0 +CLEAN_BUILD=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --buildroot) + BUILD_BUILDROOT=1 + ;; + --linux) + BUILD_LINUX=1 + ;; + --all) + BUILD_BUILDROOT=1 + BUILD_LINUX=1 + ;; + --external-root) + EXTERNAL_ROOT=1 + ;; + --clean-build) + CLEAN_BUILD=1 + ;; + --help|-h) + show_help + ;; + *) + echo "Unknown option: $1" + show_help + ;; + esac + shift +done + +if [[ $BUILD_BUILDROOT -eq 0 && $BUILD_LINUX -eq 0 ]]; then + echo "Error: No build target specified. Use --buildroot, --linux, or --all." + show_help +fi + +if [[ $CLEAN_BUILD -eq 1 && $BUILD_BUILDROOT -eq 1 && -d buildroot ]]; then + echo "Removing buildroot/ for clean build..." + rm -rf buildroot +fi + +if [[ $CLEAN_BUILD -eq 1 && $BUILD_LINUX -eq 1 && -d linux ]]; then + echo "Removing linux/ for clean build..." + rm -rf linux +fi + +if [[ $BUILD_BUILDROOT -eq 1 ]]; then + do_buildroot && OK +fi + +if [[ $BUILD_LINUX -eq 1 ]]; then + do_linux && OK +fi diff --git a/scripts/rootfs_ext4.sh b/scripts/rootfs_ext4.sh new file mode 100755 index 0000000..0fb91de --- /dev/null +++ b/scripts/rootfs_ext4.sh @@ -0,0 +1,26 @@ +#!/usr/bin/bash + +ROOTFS_CPIO="rootfs_full.cpio" +IMG="ext4.img" +IMG_SIZE=$((1024 * 1024 * 1024)) # 1GB +IMG_SIZE_BLOCKS=$((${IMG_SIZE} / 4096)) # IMG_SIZE / 4k + +DIR=rootfs + +echo "[*] Remove old rootfs directory..." +rm -rf $DIR +mkdir -p $DIR + +echo "[*] Extract CPIO" +pushd $DIR +cpio -idmv < ../$ROOTFS_CPIO +popd + +echo "[*] Create empty image" +dd if=/dev/zero of=${IMG} bs=4k count=${IMG_SIZE_BLOCKS} + +echo "[*] Create ext4 rootfs image" +fakeroot mkfs.ext4 -F ${IMG} -d $DIR + +# Show image size +du -h ${IMG} diff --git a/target/init b/target/init new file mode 100755 index 0000000..052b46e --- /dev/null +++ b/target/init @@ -0,0 +1,40 @@ +#!/bin/sh +export PATH=/bin:/sbin:/usr/bin:/usr/sbin + +ROOTFS_DEV=/dev/vda + +mount -t devtmpfs devtmpfs /dev +mount -t proc none /proc +mount -t sysfs none /sys + +# use the /dev/console device node from devtmpfs if possible to not +# confuse glibc's ttyname_r(). +# This may fail (e.g., booted with console=), and errors from exec will +# terminate the shell, so use a subshell for the test +if (exec 0/dev/null; then + exec 0/dev/console + exec 2>/dev/console +fi + +echo "[*] Attempting to mount $ROOTFS_DEV" +mkdir -p /mnt + +mount -t ext4 $ROOTFS_DEV /mnt +if [ $? -ne 0 ]; then + echo "[!] Failed to mount $ROOTFS_DEV. Using initramfs." + exec /sbin/init "$@" +fi + +echo "[*] $ROOTFS_DEV mounted successfully. Checking for root filesystem..." +if [ -x /mnt/sbin/init ]; then + echo "[*] Valid root filesystem found. Switching root to $ROOTFS_DEV" + mount --move /sys /mnt/sys + mount --move /proc /mnt/proc + mount --move /dev /mnt/dev + exec switch_root -c /dev/console /mnt /sbin/init "$@" +else + echo "[!] No valid root filesystem found on $ROOTFS_DEV. Using initramfs." + umount /mnt + exec /sbin/init "$@" +fi