Trying to Boot Arch Linux RISC-V on QEMU

Attempt to run the RISC-V port of Arch Linux on QEMU using the rootfs provided by https://archriscv.felixc.at/

Arch Linux RISC-V port link

Note: This is a personal experiment log by the author. It may contain errors or omissions and does not represent the best practice. Please refer with caution.

Preparation

Create a working directory and enter it:

mkdir -p archriscv && cd archriscv

Install QEMU RISC-V emulator and cross-compilation toolchain:

sudo pacman -S qemu-system-riscv riscv64-linux-gnu-gcc

Get the Arch Linux RISC-V image file and Linux source code:

wget https://mirrors.pku.edu.cn/archriscv/images/archriscv-latest.tar.zst
git clone https://github.com/torvalds/linux.git --depth=1

Begin Installation

Create Empty Disk Image

qemu-img create -f raw archriscv.img -o nocow=on 10G

Enable nocow for btrfs file system.

Compile the Kernel

The rootfs of Arch Linux RISC-V does not include a kernel, so we use the generic riscv config to compile the Linux kernel and boot with it, then install the kernel via pacman.

cd linux
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig
make -j$(nproc) ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- \
              Image

The compiled file will be located at arch/riscv/boot/Image.

Create GPT Partition Table and Root Partition

cd ..
parted --script archriscv.img mklabel gpt \
    mkpart primary ext4 1MiB 100% \
    set 1 boot on

Create Loop Device, Format and Mount

LOOP=$(sudo losetup -fP --show archriscv.img)
sudo mkfs.ext4 ${LOOP}p1 -L rootfs
sudo mkdir -p /mnt/riscv-root
sudo mount ${LOOP}p1 /mnt/riscv-root

Extract Rootfs to Mount Point

sudo bsdtar -xpf archriscv-latest.tar.zst -C /mnt/riscv-root
umount -R /mnt/riscv-root

Configure Arch Linux

Boot QEMU

Create start.sh script to boot with the previously compiled kernel and connect via NAT:

#!/bin/bash

KERNEL_IMAGE="linux/arch/riscv/boot/Image"
ROOTFS_IMAGE="archriscv.img"

sudo ip tuntap add dev tap0 mode tap
sudo ip link set tap0 up
sudo ip link set tap0 master virbr0

sudo qemu-system-riscv64 \
  -nographic \
  -machine virt \
  -m 12g \
  -smp 8 \
  -kernel "$KERNEL_IMAGE" \
  -append "root=/dev/vda1  rw console=ttyS0 init=/lib/systemd/systemd" \
  -drive file="$ROOTFS_IMAGE",format=raw,if=none,id=hd0 \
  -device virtio-blk-device,drive=hd0 \
  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
  -device virtio-net-device,netdev=net0

Then:

chmod +x start.sh
./start.sh

Configure Network

After entering the system, configure the network as follows:

mkdir -p /etc/systemd/network

cat > /etc/systemd/network/20-eth0.network <<EOF
[Match]
Name=eth0

[Network]
DHCP=yes
EOF

systemctl enable systemd-networkd --now
systemctl enable systemd-resolved --now

ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

systemctl restart systemd-networkd

Install Linux Package

pacman -Syu linux mkinitcpio
mkinitcpio -P

Exit QEMU, remount the loop device and copy files from /mnt/riscv-root/boot:

sudo mount ${LOOP}p1 /mnt/riscv-root
cp /mnt/riscv-root/boot/* ./

Decompress vmlinuz-linux:

zcat vmlinuz-linux > vmlinuz-linux.uncompressed
mv vmlinuz-linux.uncompressed vmlinuz-linux

Modify the startup script as follows:

diff start.sh.old start.sh
3c3
< KERNEL_IMAGE="linux/arch/riscv/boot/Image"
---
> KERNEL_IMAGE="vmlinuz-linux"
15a16
>   -initrd ./initramfs-linux.img \

Now you can boot using the distribution kernel.