Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

304 Commits

Repository files navigation

semu

A minimalist RISC-V system emulator capable of running Linux the kernel and corresponding userland. semu implements the following:

  • RISC-V instruction set architecture: RV32IMA
  • Privilege levels: S and U modes
  • Control and status registers (CSR)
  • Virtual memory system: RV32 MMU
  • UART: 8250/16550
  • PLIC (platform-level interrupt controller): 32 interrupts, no priority
  • Standard SBI, with the timer extension
  • I/O support using VirtIO standard:
    • virtio-blk acquires disk image from the host.
    • virtio-net is mapped as TAP interface.
    • virtio-snd uses PortAudio for sound playback on the host with one limitations:
      • As some unknown issues in guest Linux OS (confirmed in v6.7 and v6.12), you need to adjust the buffer size to more than four times of period size, or the program cannot write the PCM frames into guest OS ALSA stack.
        • For instance, the following buffer/period size settings on aplay has been tested with broken and stutter effects yet complete with no any errors: aplay --buffer-size=32768 --period-size=4096 /usr/share/sounds/alsa/Front_Center.wav.
    • virtio-input exposes SDL-backed keyboard and mouse devices to the guest.
    • virtio-gpu exposes a minimal 2D DRM/KMS device to the guest. Linux can bind the virtio_gpu driver and create /dev/dri/card0.
      • Only 2D scanout is currently supported; 3D, virgl, and blob resources are not implemented yet.
    • Press Ctrl+Alt+G to release the mouse cursor from the SDL window.

Prerequisites

Device Tree compiler (dtc) is required. To install it on Debian/Ubuntu Linux, enter the following command:

$ sudo apt install device-tree-compiler

For macOS, use the following command:

$ brew install dtc

For demonstration purposes, ext4 is used for file system mounting. ext4 is a native Linux filesystem, offering stability, high capacity, reliability, and performance while requiring minimal maintenance. The mkfs.ext4 command can create an ext4 file system from disk partitions. This command is a symbolic link of the mke2fs command, and its usage is the same as the mke2fs command.

For most GNU/Linux distributions, mkfs.ext4 command should be installed in advance. For macOS, use the following command:

$ brew install e2fsprogs

Build and Run

Build the emulator:

$ make

Download the prebuilt guest artifacts and run the default check:

$ make check

With the default external-root build, make check uses Image, minimal.dtb, and ext4.img, and boots semu headlessly with an equivalent command line:

$ ./semu -k Image -c 1 -b minimal.dtb -H -d ext4.img

If ENABLE_EXTERNAL_ROOT=0 is used, make check adds the legacy initramfs payload with -i rootfs.cpio. The default disk remains ext4.img; set DISKIMG_FILE= when you want to boot the initramfs path without -d ext4.img.

Please be patient while semu is running.

Reference output:

Starting syslogd: OK
Starting klogd: OK
Running sysctl: OK
Starting network: OK
Welcome to Buildroot
buildroot login:

Enter root to access shell.

You can exit the emulator using: <Ctrl-a x>. (press Ctrl+A, leave it, afterwards press X)

make check accepts runtime flags for common local test variants. Set HEADLESS=0 to show the SDL window, and set DISKIMG_FILE to choose the ext4 image exposed as /dev/vda:

$ make check HEADLESS=0
$ make check HEADLESS=0 DISKIMG_FILE=test-tools.img

The default disk is ext4.img. test-tools.img is an optional replacement disk image for tests that need larger third-party tools, such as DirectFB2. Press Ctrl+Alt+G to release the mouse cursor from the SDL window.

Log in as root, source the test-tools image environment, and run one of the DirectFB2 examples:

# . /root/local-env.sh
# df_drivertest

The installed DirectFB2 examples come from the upstream DirectFB-examples project and can be listed in the guest with:

# ls /usr/local/bin/df_*

Usage

./semu -k linux-image [-b dtb-file] [-d disk-image] [-i initrd-image] [-s shared-directory] [-H]
  • linux-image is the path to the Linux kernel Image.
  • dtb-file is optional, as it specifies the user-specified device tree blob.
  • disk-image is the ext4 image exposed as /dev/vda to the guest. The default boot path mounts this as the root filesystem; make builds it from rootfs.cpio via scripts/rootfs_ext4.sh.
  • shared-directory is optional, as it specifies the path of a directory on the host that will be shared with the guest operating system through virtio-fs, enabling file access from the guest via a virtual filesystem mount.
  • -H (or --headless) skips SDL window creation; useful for CI and make check.
  • initrd-image is optional and only used on the legacy boot path. The default minimal.dtb built with ENABLE_EXTERNAL_ROOT=1 does not advertise initrd placement, so -i there requires either ENABLE_EXTERNAL_ROOT=0 or a custom DTB passed with -b. See Boot mode below.

Boot mode

The default build (make) boots the kernel directly from /dev/vda and runs /sbin/init from the ext4 root, skipping the initramfs unpack step entirely. This is faster, avoids the RCU-stall the kernel hits when unpacking a large cpio, and matches how real systems deploy. The ext4.img is built from rootfs.cpio via scripts/rootfs_ext4.sh, which requires fakeroot, mkfs.ext4, and a fakeroot-compatible rootfs copy/ownership toolchain.

test-tools.img is an optional replacement disk for larger test/user tools that should not inflate rootfs.cpio or the default ext4.img. Use make test-tools.img to download the rolling prebuilt disk, or make build-artifacts ARTIFACTS=test-tools to rebuild it locally from source.

If fakeroot or the selected rootfs toolchain cannot run under fakeroot, the build falls back to the legacy initramfs path (-i rootfs.cpio) automatically and prints a one-line warning. To force the legacy path explicitly:

$ make ENABLE_EXTERNAL_ROOT=0
$ make ENABLE_EXTERNAL_ROOT=0 check

The legacy path uses what the flag is still spelled as: -i initrd-image. That is a runtime choice only when the DTB also carries linux,initrd-{start,end}. semu's default external-root build emits a DTB that always boots from /dev/vda, so -i is rejected there unless you replace the DTB with one that describes the initrd layout. The classical initrd (a filesystem image mounted as /dev/ram0, pivoted into via pivot_root) is effectively obsolete -- it required CONFIG_BLK_DEV_INITRD plus the legacy ramdisk block driver, an in-kernel filesystem driver to mount the image before any userspace code ran, and a /linuxrc handoff. Mainstream distros and embedded builds dropped that path more than a decade ago. Linux 2.6+ kept the flag and the linux,initrd-{start,end} device-tree properties, but the kernel inspects the loaded blob: a cpio archive is unpacked into the in-memory rootfs (initramfs path, runs /init); a filesystem image still falls back to the legacy initrd path if that driver is configured in. semu ships and consumes a cpio (rootfs.cpio), so the legacy build is exercising the initramfs path even though the CLI flag spelling stayed -i initrd-image.

For detailed networking guidance, see docs/networking.md.

Mount and unmount a directory in semu

To mount the directory in semu:

$ mount -t virtiofs myfs [shared-directory]
  • shared-directory is the path of a directory you want to mount in semu.

To unmount the directory in semu:

$ umount [shared-directory]
  • shared-directory is the path of a directory you want to unmount in semu.

Build guest artifacts from source

Fresh local make invocations download missing guest artifacts from the rolling prebuilt release by default. An explicit local source-build path 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.

Use the Make target as the public entry point for local guest artifact source builds:

$ make build-artifacts

ARTIFACTS is passed to the lower-level build script. To build selected artifact classes, set it explicitly:

$ make build-artifacts ARTIFACTS=image
$ make build-artifacts ARTIFACTS=rootfs
$ make build-artifacts ARTIFACTS="rootfs --no-ext4"
$ make build-artifacts ARTIFACTS=test-tools
$ make build-artifacts ARTIFACTS="all --clean-build"
$ make build-artifacts ARTIFACTS="all --full-rebuild"

make build-artifacts is only a Make wrapper around scripts/build-artifacts.sh; pass class targets and class-specific flags through ARTIFACTS so the script remains the single local artifact-build CLI.

Local make treats Image, rootfs.cpio, and test-tools.img as ordinary file targets. If one is missing, Make downloads it from the rolling prebuilt release; if one already exists, Make will use it. These local downloads verify the release archive checksum recorded in prebuilt.sha1, but they are not checked against the current working tree or CI recipe keys.

Run make build-artifacts explicitly after changing guest configs when you want the build system to update those artifacts from source. CI prebuilt cache and recipe-key stamps are handled separately under .ci/prebuilt/ and are not part of the local build interface.

The source builder keeps the default rootfs and optional test-tools rootfs in separate Buildroot output directories, so test-tools packages do not leak back into the default rootfs.cpio or ext4.img. Use --clean-build to remove selected raw artifact outputs and those Buildroot output directories while preserving source checkouts and download cache; use --full-rebuild to remove selected raw artifact outputs and external source/build trees before rebuilding from pinned revisions.

Targets:

image Build the Linux Image. This prepares the Buildroot
toolchain if needed but does not publish rootfs.cpio or
test-tools.img as final outputs.
rootfs Build Buildroot rootfs.cpio and, unless --no-ext4 is
given, derive ext4.img from it.
test-tools Build test-tools.img. By default this uses the canonical
X11 + DirectFB2 test payload recipe.
all Build image, rootfs, and canonical test-tools.

Options:

--x11 Select the X11/C++ runtime recipe entry for test-tools. When
any test-tools recipe flag is given, only selected
entries are included.
--directfb2-test Select the DirectFB2 test payload recipe entry for test-tools.
--no-ext4 With rootfs, skip ext4.img generation and produce only
rootfs.cpio (matches the legacy ENABLE_EXTERNAL_ROOT=0 path).
--clean-build Remove selected raw artifact outputs and Buildroot
output directories while preserving source checkouts and
download cache.
--full-rebuild Remove selected raw artifact outputs and external
source/build trees before building so they are cloned and
built again from pinned revisions.
--help Show this message.

test-tools.img is the shared optional disk for test payloads selected by recipe entries that should not live in the default rootfs.cpio or ext4.img. This keeps the default guest image small while still allowing larger tools to be collected in one place.

The selected test-tools recipe is part of the CI test-tools.img recipe key, so workflow caches and release artifacts distinguish the canonical x11,directfb2 image from x11 or directfb2 local variants.

To add a new test tool, extend the test-tools build path in scripts/prebuilt/artifact-recipes.sh so the tool is staged into extra_packages, then update target/local-env.sh if the tool needs an additional binary or library search path.

The build script copies target/local-env.sh to /root/local-env.sh in the test tools image. After booting the VM, source it once to pick up paths such as /usr/local/bin and /usr/local/lib, instead of running overlaid tools through full paths like /usr/local/bin/df_*.

License

semu is released under the MIT License. Use of this source code is governed by a MIT-style license that can be found in the LICENSE file.

About

A minimalist RISC-V system emulator capable of running Linux kernel with efficient event-driven scheduling

Topics

Resources

Contributing

Stars

309 stars

Watchers

8 watching

Forks

Used by

Contributors

Languages