Skip to content

Support mounting rootfs from virtio-blk via initrd #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,56 @@ You can exit the emulator using: \<Ctrl-a x\>. (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.
Expand Down
120 changes: 112 additions & 8 deletions scripts/build-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
26 changes: 26 additions & 0 deletions scripts/rootfs_ext4.sh
Original file line number Diff line number Diff line change
@@ -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}
40 changes: 40 additions & 0 deletions target/init
Original file line number Diff line number Diff line change
@@ -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/console) 2>/dev/null; then
exec 0</dev/console
exec 1>/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