Getting started

QEMU options

Every environment variable the boot and test recipes read, and every disk and device they give QEMU.

Every boot and test recipe starts QEMU through scripts/qemu_run.sh; the justfile sets some of its variables first. Both files are authoritative. Set a variable in the environment, before just:

QEMU_DISPLAY=sdl ROULETTE=0 just boot

Options for the kernel itself (its command line) are listed in Boot options.

Modes

The wrapper runs in one of three modes, chosen by the recipe.

ModeRecipesWindowSerial output
interactiveboot, boot-fast, boot-debug, boot-liveYes, unless VIDEO=0The terminal
loggedboot-logNoLOG_FILE
testtest and its variants, test-persist, test-install and the other checks that bootNoThe test runner

Recipe variables

Read by the justfile and turned into kernel command-line options or recipe behaviour.

VariableDefaultEffect
ROULETTE10, false, off, no or skip adds roulette=skip: no Wheel of Fate
DEBUG01, true, on or yes adds boot.debug=on
VIDEO1 for boot and boot-live0 opens no window; the console is the terminal
KERNEL_RELEASE1 for boot, otherwise unset1 builds and boots the optimised kernel, 0 the dev kernel
BOOT_CMDLINEtests=off root=initramfsThe live ISO's whole kernel command line (iso, boot-live, boot-log). boot ignores it
BOOT_LOG_TIMEOUT15Seconds boot-log runs before QEMU is stopped
LOG_FILEtest_output.logWhere boot-log writes the serial log, with ANSI escapes removed
DEV_QEMU_MEM4GMemory for boot when QEMU_MEM is unset
PERSIST_IMAGE_SIZE512MMinimum size of the persistent root; a larger value grows the existing image in place
ROOT_FREE_FLOOR6GFree space the persistent root keeps for builds; a root with less is grown
CAPACITY_IMAGE_SIZE16GSize of the test-capacity volume
CAPACITY_INODE_RATIO16384Bytes per inode for the test-capacity volume
CAPACITY_QEMU_MEM2GMemory for test-capacity when QEMU_MEM is unset
BUILD_DIRbuilddirBuild output directory

ports is a justfile variable, not an environment variable, and goes between just and the recipe: just ports=7777,8080 boot. See Networking.

Machine

-machine q35,accel=<QEMU_ACCEL>
-cpu <QEMU_CPU>
-smp <QEMU_SMP>
-m <QEMU_MEM>
VariableDefaultEffect
QEMU_BINqemu-system-x86_64QEMU binary
QEMU_ACCELkvm:tcg on Linux, hvf:tcg on macOSAccelerators, tried in order
QEMU_CPUhostCPU model
QEMU_SMP4CPU count; must be a power of two
QEMU_MEM4G for boot, 1G in test mode, 512M otherwiseGuest memory

On Linux, KVM is used when /dev/kvm exists and is readable and writable. When no hypervisor is usable (also on macOS when QEMU lists no HVF), the wrapper prints No hardware acceleration and runs TCG with -cpu max, because -cpu host needs a hypervisor.

Firmware

QEMU boots UEFI firmware (OVMF), fetched on first use and checked against pinned SHA-256 sums. Each run boots from a fresh copy of the firmware's variable store.

Boot mediumRecipesFirmware
Live ISO on an AHCI CD-ROMboot-live, boot-log, testPinned edk2 nightly in third_party/ovmf
UEFI boot disk on NVMeboot, boot-fast, boot-debug, the install checksPinned Arch edk2-ovmf build in third_party/ovmf-nv (OVMF_NV_DIR overrides the directory)

The boot disk needs the second build because it chooses its next boot through a UEFI variable (LoaderEntryOneShot), which the nightly does not keep across a reset. BOOT_DISK_IMG names the boot disk and selects this firmware; the boot recipes set it. In interactive mode a guest reboot resets the machine inside the same QEMU process, keeping the variable store; in test mode QEMU exits on a reboot unless QEMU_ALLOW_REBOOT=1, which the install checks set.

Download overrides (OVMF_BASE_URL, OVMF_NV_PKG_URL, LIMINE_URL and others) are listed in Troubleshooting.

Disks

Every NVMe controller is probed before any virtio one, in command-line order, so a root disk, when one is attached, is nvme0n1.

Guest nameWhenContents
nvme0n1bootfs/assets/ext2-persist.img, the persistent root
nvme0n1test mode, except test-installfs/assets/ext2-tests.img, rebuilt every run
nvme0n2test modeBlank 8 MiB scratch for destructive block tests
nvme0n3CAPACITY_IMG names an existing file (test-capacity)The capacity volume, kept between runs
nvme1n1test modeext4 volume labelled slopos-media, 4096-byte logical blocks, mounted at /media
nvme1n2test modeBlank scratch with 4096-byte logical blocks
nvme2n1test modeScratch on a controller that a test shuts down
nvme1n1bootbuilddir/boot-disk.img, the boot disk (see below)
nvme3n1install checksThe boot disk, after the test controllers
vdatest modefs/assets/ext2.img, the shipped verified image, copy-on-write
vdbtest modeBlank virtio scratch

QEMU_NO_ROOT_DISK=1 omits the root disk. boot-live and boot-log set it, so they attach no disk at all; test-install sets it because its root is a partition on the boot disk. Test-mode scratch files go in SCRATCH_DIR (default builddir). Disks explains device naming.

The boot disk (scripts/build_bootdisk.sh) is a GPT disk in the layout SlopOS uses on a real machine:

PartitionContents
EFI system partitionLimine and its limine.conf under \EFI\SlopOS\, plus a copy at the removable-media path \EFI\BOOT\
SlopOS bootFAT32; one directory per slot (/boot/a, /boot/b), each holding kernel.elf and base.img
SlopOS rootOnly when BOOTDISK_ROOT_IMAGE names an ext4 image (test-install); the slots boot with it as root
SlopOS crashRaw, zeroed

BOOTDISK_PANIC_ENTRY=1 adds a slot bad whose kernel panics and resets. Installing a system explains slots.

Display

VariableDefaultEffect
QEMU_DISPLAYcocoa on macOS, auto elsewherecocoa, gtk, sdl or auto. auto picks Cocoa when QEMU has it, SDL on a Wayland session that has SDL, and GTK otherwise
GPUvirtio-vgavirtio-vga, virtio-gpu-pci or vga
QEMU_FB_AUTO1Detect the host screen size for the boot framebuffer
QEMU_FB_WIDTH1920Width when detection is off or fails
QEMU_FB_HEIGHT1080Height when detection is off or fails
QEMU_FB_AUTO_POLICYprimarySize from the primary screen or the max one
QEMU_FB_AUTO_OUTPUTemptyDetect from this output name only
QEMU_GTK_ZOOM_TO_FIToffGTK zoom-to-fit
GPU valueBehaviour
virtio-vgaFirmware framebuffer from power-on, then the kernel's virtio-gpu driver takes over
virtio-gpu-pciNothing on screen until the driver probes
vgaPlain framebuffer; pair with video=framebuffer in BOOT_CMDLINE

When QEMU cannot create the virtio device, the wrapper warns and uses vga. The framebuffer size is written into the bootloader configuration when the ISO or boot disk is built, which the boot recipes do on every run. just show-qemu-resolution prints the size detection would pick.

Networking

QEMU user networking (SLIRP) backs a virtio-net-pci card in every mode.

SettingDefaultEffect
ports (justfile)emptyComma-separated ports to forward from host to guest; host:guest maps different numbers. Sets NET=1 and NET_PORTS
NET01 forwards NET_PORTS
NET_PORTS7777,8080,8081Ports forwarded when NET=1

ports does not reach the inner just boot that boot-fast and boot-debug start; use ROULETTE=0 just ports=7777 boot.

These services run inside the SLIRP network, started by QEMU per connection:

AddressServiceVariables
10.0.2.100:9999TCP echo service the network tests dialECHO_PEER_ADDR, ECHO_PEER_PORT, ECHO_PEER_CMD (default /bin/cat)
10.0.2.4:9418git daemon serving this checkout, read-only
10.0.2.4:9419git daemon serving a bare repository that accepts pushesGIT_PUSH_REPO, an absolute path, created if absent

boot sets GIT_PUSH_REPO to fs/assets/guest-push.git. Development machine shows how the guest uses both.

Debugging

VariableEffect
QEMU_DEBUG=1GDB stub on TCP port 1234 and the QEMU monitor on /tmp/slopos-monitor.sock. boot-debug sets it
QEMU_ENABLE_ISA_EXIT=1Add the isa-debug-exit device (port 0xf4) outside test mode, so the guest can end QEMU with a status
QEMU_PCI_DEVICESExtra QEMU arguments, space-separated, appended to the command line

just debug-gdb, just debug-bt and just debug-monitor attach to a running boot-debug. See Debugging with GDB.

On this page