Skip to main content

KVM Demo

RockOS supports KVM virtualization based on H Extension (RISC-V Hypervisor Extension).

Currently the following are verified as working:

  • Ubuntu 24.04.1 LTS & 24.10
  • openEuler 24.03 LTS & 24.09
  • FreeBSD 14.1-RELEASE
  • Debian testing netinst CD

Other versions are not tested, but they should also work with the same steps below.

If not, feel free to open an issue at rockos-riscv/rockos.

Boards currently verified as working:

  • Milk-V Megrez
  • SiFive HiFive Premier P550
  • PINE64 StarPro64

Environment

You can try other mirrors if the official server is too slow for you.

Steps

Currently QEMU does not support loading M Mode firmware via -bios while KVM is enabled. See comments here. So here are some of the options we have now:

  • Use u-boot.elf provided by u-boot-qemu package
    • You can boot Ubuntu and FreeBSD with this method.
  • Use other firmware
    • e.g. openEuler RISC-V + EDK II (distributed along side with system image)
    • EDK II comes with qemu-efi-riscv64 package
  • Use -initrd -kernel -append flags
    • You can boot Ubuntu with this method.
    • The BusyBox KVM Demo is also using this method.
    • You can also boot FreeBSD this way according to FreeBSD Wiki.
      • Not tested & not demonstrated in this article.

By default RockOS does not load KVM module on boot, so before we start, manually load it:

sudo modprobe kvm

Install required packages:

sudo apt update; sudo apt install -y wget u-boot-qemu qemu-efi-riscv64

Method A: Acquire U-Boot from u-boot-qemu package

For this method, we have Ubuntu preinstalled server image, FreeBSD and Debian netinst CD as examples.

wget https://cdimage.ubuntu.com/releases/24.10/release/ubuntu-24.10-preinstalled-server-riscv64.img.xz
xz -dkv -T0 ubuntu-24.10-preinstalled-server-riscv64.img.xz
sudo qemu-system-riscv64 --enable-kvm -M virt -cpu host -m 2048 -smp 2 -nographic \
-device virtio-net-device,netdev=eth0 -netdev user,id=eth0 \
-device virtio-rng-pci \
-kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
-drive file=ubuntu-24.10-preinstalled-server-riscv64.img,format=raw,if=virtio

The default username and password are both ubuntu.

For Ubuntu preinstalled image, you'll be prompted to change your password on first boot. Follow the steps and you're good to go.

Method B: Use other firmware (e.g. TianoCore EDK II)

Currently, openEuler, Ubuntu and Debian are verified.

For openEuler use the EDK II firmware distributed with the main system image; for Ubuntu and Debian, install qemu-efi-riscv64 and use the firmware provided by this package.

Current version of EDK II might get stuck at Press ESCAPE within 5 seconds for boot options (~50s). Press Enter to skip.

Obtain and decompress the image:

wget https://repo.openeuler.org/openEuler-24.09/virtual_machine_img/riscv64/RISCV_VIRT_CODE.fd \
https://repo.openeuler.org/openEuler-24.09/virtual_machine_img/riscv64/RISCV_VIRT_VARS.fd \
https://repo.openeuler.org/openEuler-24.09/virtual_machine_img/riscv64/openEuler-24.09-riscv64.qcow2.xz \
https://repo.openeuler.org/openEuler-24.09/virtual_machine_img/riscv64/start_vm.sh
xz -dkv -T0 openEuler-24.09-riscv64.qcow2.xz

Modify the VM start script:

nano start_vm.sh

The following parts need to be changed:

  • Add --enable-kvm flag
  • Change RAM allocate size: memory=2 for 2G
    • ram1 and ram2 also need to be changed:object memory-backend-ram,size=1G,id=ram1 object memory-backend-ram,size=1G,id=ram2
  • Add if=none flag to boot disk

The edited script should look like this:

#!/usr/bin/env bash

# The script is created for starting a riscv64 qemu virtual machine with specific parameters.

RESTORE=$(echo -en '\001\033[0m\002')
YELLOW=$(echo -en '\001\033[00;33m\002')

## Configuration
vcpu=2
memory=2
drive="$(ls *.qcow2)"
fw1="RISCV_VIRT_CODE.fd"
fw2="RISCV_VIRT_VARS.fd"
ssh_port=12055

cmd="qemu-system-riscv64 \
-nographic -machine virt,pflash0=pflash0,pflash1=pflash1,acpi=off \
--enable-kvm \
-smp "$vcpu" -m "$memory"G \
-object memory-backend-ram,size=1G,id=ram1 \
-numa node,memdev=ram1 \
-object memory-backend-ram,size=1G,id=ram2 \
-numa node,memdev=ram2 \
-blockdev node-name=pflash0,driver=file,read-only=on,filename="$fw1" \
-blockdev node-name=pflash1,driver=file,filename="$fw2" \
-drive file="$drive",format=qcow2,id=hd0,if=none \
-object rng-random,filename=/dev/urandom,id=rng0 \
-device virtio-vga \
-device virtio-rng-device,rng=rng0 \
-device virtio-blk-device,drive=hd0 \
-device virtio-net-device,netdev=usernet \
-netdev user,id=usernet,hostfwd=tcp::"$ssh_port"-:22 \
-device qemu-xhci -usb -device usb-kbd -device usb-tablet"

echo ${YELLOW}:: Starting VM...${RESTORE}
echo ${YELLOW}:: Using following configuration${RESTORE}
echo ""
echo ${YELLOW}vCPU Cores: "$vcpu"${RESTORE}
echo ${YELLOW}Memory: "$memory"G${RESTORE}
echo ${YELLOW}Disk: "$drive"${RESTORE}
echo ${YELLOW}SSH Port: "$ssh_port"${RESTORE}
echo ""
echo ${YELLOW}:: NOTE: Make sure ONLY ONE .qcow2 file is${RESTORE}
echo ${YELLOW}in the current directory${RESTORE}
echo ""
echo ${YELLOW}:: Tip: Try setting DNS manually if QEMU user network doesn\'t work well. ${RESTORE}
echo ${YELLOW}:: HOWTO -\> https://serverfault.com/a/810639 ${RESTORE}
echo ""
echo ${YELLOW}:: Tip: If \'ping\' reports permission error, try reinstalling \'iputils\'. ${RESTORE}
echo ${YELLOW}:: HOWTO -\> \'sudo dnf reinstall iputils\' ${RESTORE}
echo ""

sleep 2

eval $cmd

Start the KVM:

sudo bash start_vm.sh

Or manually execute:

sudo qemu-system-riscv64 \
-nographic -machine virt,pflash0=pflash0,pflash1=pflash1,acpi=off \
--enable-kvm \
-smp 2 -m 2G \
-object memory-backend-ram,size=1G,id=ram1 \
-numa node,memdev=ram1 \
-object memory-backend-ram,size=1G,id=ram2 \
-numa node,memdev=ram2 \
-blockdev node-name=pflash0,driver=file,read-only=on,filename=RISCV_VIRT_CODE.fd \
-blockdev node-name=pflash1,driver=file,filename=RISCV_VIRT_VARS.fd \
-drive file=openEuler-24.09-riscv64.qcow2,format=qcow2,id=hd0,if=none \
-object rng-random,filename=/dev/urandom,id=rng0 \
-device virtio-vga \
-device virtio-rng-device,rng=rng0 \
-device virtio-blk-device,drive=hd0 \
-device virtio-net-device,netdev=usernet \
-netdev user,id=usernet,hostfwd=tcp::12055-:22 \
-device qemu-xhci -usb -device usb-kbd -device usb-tablet

Default username: root or openeuler

Default password: openEuler12#$

If needed, you can press ESC to interrupt EDK II autoboot while promting Press ESCAPE within 5 seconds for boot options, and enter EDK II menu to edit settings.

Method C: Directly load vmlinuz and initrd

We have a busybox based KVM demo available at: kvm_demo

Download and start it up:

wget https://fast-mirror.isrc.ac.cn/revyos/extra/kvm_demo/rootfs_kvm_guest.img
wget https://fast-mirror.isrc.ac.cn/revyos/extra/kvm_demo/start_vm.sh
chmod +x start_vm.sh
./start_vm.sh

Screen Records

Here are some asciinema screen records during the boot process for your reference.

Ubuntu 24.10 + U-Boot:

Ubuntu 24.10 + U-Boot

Ubuntu 24.10, initrd + vmlinuz:

Ubuntu 24.10, initrd + vmlinuz

openEuler 24.09 + TianoCore EDK II:

openEuler 24.09 + TianoCore EDK II

FreeBSD 14.1-RELEASE + U-Boot:

FreeBSD 14.1-RELEASE + U-Boot

Debian testing netinst CD + U-Boot:

Debian testing netinst CD + U-Boot

Notes

  • Make sure you've added acpi=off to QEMU's cmdline while using EDK II.
  • If you get stuck at EDK II and can't boot into the system, try manually selecting boot device inside EDK II menu, or choose EFI file from the boot partition.