install Alpine Linux riscv64 under qemu with u-boot loader

2021-09-02

updated: 2023-07-26

usual disclaimer: no one is responsible for any damage/loss by using this

script bellow will create VM disk image of Alpine Linux riscv64 to run in qemu

script is created with big help of Alpine Linux developer Carlo

size of VM disk image should be adjusted according needs and parameters for sgdisk to create partitions of the needed sizes

prerequisite on alpine linux:

qemu-openrc
util-linux or util-linux-misc
e2fsprogs
qemu-riscv64
qemu-img
losetup
blkid
sgdisk

start this before running install script
/etc/init.d/qemu-binfmt start

for other linux distros apk-tool-static could be downloaded from
https://dl-cdn.alpinelinux.org/alpine/edge/main/$ARCH/apk-tools-static-$version.apk
and unpacked with tar

$ARCH is linux host OS architecture and $version is current version
for example https://dl-cdn.alpinelinux.org/alpine/edge/main/aarch64/apk-tools-static-2.14.0-r5.apk

image file name and size could be set at wish on top of the script

#!/bin/sh

# apk add qemu-img qemu-openrc losetup blkid sgdisk e2fsprogs wget tar
set -eu


: "${DISK:=rv64-vm-mmc.img}"
: "${DISKSIZE:=500M}"

readonly SCRIPT="${0##*/}"
readonly TMPDIR="$(mktemp -dt "${SCRIPT%.*}".XXXXXX)"
readonly APK_KEY="alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub"
readonly MIRROR="http://dl-cdn.alpinelinux.org/alpine"
readonly LOOPDEV="$(losetup --find)"
readonly BOOTDEV="$LOOPDEV"p1
readonly ROOTDEV="$LOOPDEV"p2

cleanup() {
	set +e
	mountpoint -q "$TMPDIR"/boot && umount "$TMPDIR"/boot
	mountpoint -q "$TMPDIR" && umount "$TMPDIR"
	losetup --detach-all
	rm -rf "$TMPDIR"
}
trap cleanup EXIT INT

rm -f $DISK
truncate -s $DISKSIZE $DISK

echo "Paritioning disk"
sgdisk --clear --set-alignment=1 \
 	-n 1:2048:+128MiB: -c 1:boot -A 1:set:2 \
 	-n 2::: -c 2:root \
"${DISK}"

echo "Creating dirs and mounting"
losetup -P "$LOOPDEV" "$DISK"
mkfs.ext4 -qL boot "$BOOTDEV"
mkfs.ext4 -qL root "$ROOTDEV"
mount -t ext4 "$ROOTDEV" "$TMPDIR"
mkdir -p "$TMPDIR"/boot
mkdir -p "$TMPDIR"/etc/apk/keys
mount -t ext4 "$BOOTDEV" "$TMPDIR"/boot


echo "Setup apk keys and repositories"
wget -qP "$TMPDIR"/etc/apk/keys https://alpinelinux.org/keys/"$APK_KEY"
printf "$MIRROR/edge/%s\n" main community testing > "$TMPDIR"/etc/apk/repositories

echo "installing base packages"
apk --allow-untrusted --root "$TMPDIR" --arch riscv64 --initdb add \
	alpine-base alpine-baselayout alpine-conf kmod openrc \
  dbus util-linux blkid chrony \
	sysfsutils ssl_client ca-certificates-bundle alpine-keys \
	ethtool e2fsprogs e2fsprogs-extra libudev-zero libudev-zero-helper \
	linux-firmware-none installkernel mkinitfs \
	agetty tar tzdata openssh wget sgdisk linux-edge

echo "Setting up services and inittab"
for rc in boot/bootmisc boot/hostname boot/modules boot/sysctl boot/urandom boot/networking \
	sysinit/devfs sysinit/hwdrivers sysinit/mdev sysinit/modules \
	shutdown/mount-ro shutdown/killprocs \
	default/dbus default/chronyd default/local; do
	ln -s /etc/init.d/"${rc##*/}" "$TMPDIR"/etc/runlevels/"$rc"
done

echo 'SUBSYSTEM=drm;.*   root:video 660 */usr/libexec/libudev-zero-helper' >>  $TMPDIR/etc/mdev.conf
echo 'SUBSYSTEM=input;.* root:input 660 */usr/libexec/libudev-zero-helper' >>  $TMPDIR/etc/mdev.conf

echo "Settting boot loader"
bootuuid=$(blkid -s UUID -o value $BOOTDEV)
echo "LABEL=boot	/boot	ext4	auto" >> $TMPDIR/etc/fstab

rootuuid=$(blkid -s UUID -o value $ROOTDEV)
echo "creating extlinux.conf, u-boot setup"

mkdir -p "$TMPDIR"/boot/extlinux
cat > $TMPDIR/boot/extlinux/extlinux.conf<<'EOF'
TIMEOUT 50
PROMPT 1
DEFAULT edge

MENU TITLE riscv64

LABEL edge
MENU LABEL alpine linux-edge kernel
KERNEL /vmlinuz-edge
INITRD /initramfs-edge
APPEND "modules=sd-mod,usb-storage,ext4,f2fs root=/dev/vda2 rw rootwait console=ttyS0 earlycon=sbi loglevel=15"
EOF



sed -i 's/^tty1.*/tty1::respawn:\/sbin\/agetty -L 115200 tty1 linux --login-pause --autologin root --noclear/' $TMPDIR/etc/inittab
sed -i 's/^#ttyS0/ttyS0/' $TMPDIR/etc/inittab
sed -i 's/^ttyS0.*/ttyS0::respawn:\/sbin\/agetty -L 115200 ttyS0 linux --login-pause --autologin root --noclear/' $TMPDIR/etc/inittab

echo "Finished, cleaning up"

to run this image file fw_payload.elf from opensbi is needed.

it can be easily built following instructions in opensbi or u-boot

here is one I have built: https://dev.alpinelinux.org/~mps/riscv64/fw_payload.elf

run this VM with next command. Memory size, number of CPUs and other parameters could be set as needed


#!/bin/sh
: "${DISK:=rv64-vm-mmc.img}"
qemu-system-riscv64 -M virt -accel tcg \
	-cpu rv64 -smp cores=2 \
	-bios fw_payload.elf \
	-m 512M \
	-drive file=${DISK},id=hd0 -device virtio-blk,drive=hd0 \
	-rtc base=utc,clock=host \
	-object rng-random,filename=/dev/urandom,id=rng0 \
	-device virtio-rng,rng=rng0 \
	-netdev user,id=n1 -device virtio-net-pci,netdev=n1 \
	-nographic