Alpine Linux on riscv64 Starfive VisionFive V1 and V2 SBC

2022-06-02

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

note: Alpine repository for VisionFive is here https://gitlab.alpinelinux.org/nmeum/alpine-visionfive

kernel APKBUILD and config is here https://gitlab.alpinelinux.org/nmeum/alpine-visionfive/-/tree/main/starfive/linux-starfive

Also there is IRC channel #alpine-riscv64 on OFTC for discussion about development and help


there is ready made disk images here for V1 https://dev.alpinelinux.org/~mps/riscv64/visionfive-v1-mmc.img.xz

and for V2 https://dev.alpinelinux.org/~mps/riscv64/visionfive-v2-mmc.img.xz

download appropriate image, uncompress with

xz -d visionfive-v1-mmc.img.xz
or for V2
xz -d visionfive-v2-mmc.img.xz

and

dd if=visionfive-v1-mmc.img of=/dev/sdX
or for V2
dd if=visionfive-v2-mmc.img of=/dev/sdX

where X is mmc device.

resizing root filesystem (example):
localhost:~# lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk0     179:0    0   29G  0 disk
├─mmcblk0p3 179:3    0   64M  0 part /boot
└─mmcblk0p4 179:4    0  200M  0 part /

and then
sgdisk -e /dev/mmcblk0
sgdisk -d 4 -n 4:0:0 -c 4:root /dev/mmcblk0
partx -u /dev/mmcblk0
resize2fs /dev/mmcblk0p4

if the device is not mmcblk0 change it to appropriate one

reboot

script bellow will create same disk image for visionfive V1 but could be tweaked to create different image as needed for particular use cases

script is created with big help of Alpine Linux developer Carlo

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

prerequisite on alpine linux:

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

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

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

when script finish use dd to write disk inage to mmc card and boot SBC

Script used to create visionfive V2 disk img:

#!/bin/sh

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

: "${DISK:=visionfive-v2-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="https://dl-cdn.alpinelinux.org/alpine"
readonly LOOPDEV="$(losetup --find)"
readonly BOOTDEV="$LOOPDEV"p3
readonly ROOTDEV="$LOOPDEV"p4

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=2 \
  --new=1:4096:+2MiB: --change-name=1:spl --typecode=1:2E54B353-1271-4842-806F-E436D6AF6985\
  --new=2::+4MiB: --change-name=2:uboot --typecode=2:BC13C2FF-59E6-4262-A352-B275FD6F7172  \
  --new=3::+128MiB: --change-name=3:boot --typecode=3:EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 \
  --new 4::: -c 4:root \
"${DISK}"
sgdisk --attributes=3:set:2 ${DISK}
#-n 4::: -c 4:root -A 4:set:2 \

echo "Creating dirs and mounting"
losetup -P "$LOOPDEV" "$DISK"
mkfs.ext4 -qL mmc1.bfs "$BOOTDEV"
mkfs.ext4 -qL mmc1.rfs "$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 \
	iwd linux-firmware-brcm linux-firmware-cypress installkernel mkinitfs \
	agetty openresolv tar tzdata openssh wget sgdisk u-boot-starfive linux-starfive

dd if="$TMPDIR"/usr/share/u-boot/starfive_visionfive2/u-boot-spl.bin.normal.out of=${LOOPDEV}p1
dd if="$TMPDIR"/usr/share/u-boot/starfive_visionfive2/u-boot.itb of=${LOOPDEV}p2

echo "Setting up services"
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 'blacklist jh7110_crypto' >>  $TMPDIR/etc/modprobe.d/blacklist-local.conf
echo "LABEL=mmc1.bfs	/boot	ext4	auto" >> $TMPDIR/etc/fstab

echo "Settting boot loader"
bootuuid=$(blkid -s UUID -o value $BOOTDEV)
rootuuid=$(blkid -s UUID -o value $ROOTDEV)

mkdir -p "$TMPDIR"/boot/extlinux
cat <<EOF > "$TMPDIR"/boot/extlinux/extlinux.conf
menu title StarFive VisionFive
timeout 50
default starfive

label starfive
	menu label Alpine visionfive 
	kernel /vmlinuz-starfive
	initrd /initramfs-starfive
	fdtdir /dtbs-starfive/
	append earlycon=sbi rw root=UUID=$rootuuid rootfstype=ext4 rootwait console=ttyS0,115200 console=tty0

EOF

echo "Setting up inittab"
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"


Script used to create visionfive V1 disk img:

#!/bin/sh

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

: "${DISK:=visionfive-v1-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="https://dl-cdn.alpinelinux.org/alpine"
readonly LOOPDEV="$(losetup --find)"
readonly BOOTDEV="$LOOPDEV"p3
readonly ROOTDEV="$LOOPDEV"p4

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

  kernel="linux-vf1-6.8_rc2-r0.apk"
  dlurl="https://dev.alpinelinux.org/~mps/riscv64"

if [ ! -f $kernel ]; then
  wget "$dlurl"/${kernel}
fi

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

echo "Paritioning disk"
sgdisk --clear \
  --set-alignment=2 \
  --new=1:4096:+2MiB: --change-name=1:spl --typecode=1:2E54B353-1271-4842-806F-E436D6AF6985\
  --new=2::+4MiB: --change-name=2:uboot --typecode=2:BC13C2FF-59E6-4262-A352-B275FD6F7172  \
  --new=3::+128MiB: --change-name=3:boot --typecode=3:EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 \
  --new 4::: -c 4:root \
"${DISK}"
sgdisk --attributes=3:set:2 ${DISK}
#
#-n 4::: -c 4:root -A 4:set:2 \

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

cat <<EOF > "$TMPDIR"/boot/uEnv.txt
fdt_high=0xffffffffffffffff
initrd_high=0xffffffffffffffff

scriptaddr=0x88100000
script_offset_f=0x1fff000
script_size_f=0x1000

kernel_addr_r=0x84000000
kernel_comp_addr_r=0x90000000
kernel_comp_size=0x10000000

fdt_addr_r=0x88000000
ramdisk_addr_r=0x88300000

bootcmd=devnum=0; run mmc_boot
bootcmd_mmc0=devnum=0; run mmc_boot
distro_bootcmd=run bootcmd_mmc0
EOF

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 f2fs-tools e2fsprogs e2fsprogs-extra libudev-zero libudev-zero-helper \
	iwd linux-firmware-brcm linux-firmware-cypress installkernel mkinitfs \
	agetty openresolv tar tzdata openssh wget sgdisk
apk --allow-untrusted --root "$TMPDIR" --arch riscv64 add $kernel

echo "Setting up services"
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 'blacklist jh7110_crypto' >>  $TMPDIR/etc/modprobe.d/blacklist-local.conf
echo "LABEL=mmc1.bfs	/boot	ext4	auto" >> $TMPDIR/etc/fstab

echo "Settting boot loader"
bootuuid=$(blkid -s UUID -o value $BOOTDEV)
rootuuid=$(blkid -s UUID -o value $ROOTDEV)

mkdir -p "$TMPDIR"/boot/extlinux
cat <<EOF > "$TMPDIR"/boot/extlinux/extlinux.conf
menu title StarFive VisionFive
timeout 50
default vf1

label vf1
	menu label Alpine visionfive V1
	kernel /vmlinuz-vf1
	initrd /initramfs-vf1
	fdt /dtbs-vf1/starfive/jh7100-starfive-visionfive-v1.dtb
	append earlycon=sbi rw root=UUID=$rootuuid rootfstype=ext4 rootwait console=ttyS0,115200 console=tty0 stmmaceth=chain_mode:1

EOF

echo "Setting up inittab"
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"