Differences between revisions 2 and 20 (spanning 18 versions)
Revision 2 as of 2022-05-25 17:25:07
Size: 8930
Editor: ?AlanBeadle
Comment: Some minor fixes
Revision 20 as of 2022-05-31 21:23:24
Size: 17931
Editor: ?BaptisteJonglez
Comment: Simplify EFI copy
Deletions are marked like this. Additions are marked like this.
Line 45: Line 45:
= Boot process overview =

The full boot process is documented in the [[https://rvspace.org/en/Product/VisionFive/Technical_Documents/VisionFive_Single_Board_Computer_Quick_Start_Guide#appendix-a-visionfive-boot-flow|official documentation]]. This guide is focused on getting u-boot to boot the Linux kernel.

The board comes with the u-boot bootloader flashed on the internal SPI flash. This u-boot first tries to load the file '''/boot/uEnv.txt''' from the
third partition of the SD card, allowing to customize the boot process.
It then implements the [[https://u-boot.readthedocs.io/en/latest/develop/distro.html|"generic distro" concept of u-boot]].

Unfortunately, as of May 2022 (U-Boot 2022.04-rc2-VisionFive built on Mar
07 2022 - 21:12:22 +0800), the bundled u-boot on the board doesn't do
anything except loading the uEnv.txt file, because it's missing a built-in
definition of "bootcmd". That is, the uEnv.txt file is currently
mandatory and needs to specify how to boot. It also needs to define load
addresses for the kernel, FDT, and/or EFI image. See https://github.com/starfive-tech/u-boot/pull/31 and https://github.com/starfive-tech/u-boot/pull/32 for patches fixing these issues.

In any case, u-boot can boot Linux using two main methods:

 1. either it can load an extlinux.conf file describing which kernel/FDT/initrd images to boot, load all three images, and boot the kernel directly. This fits the [[https://u-boot.readthedocs.io/en/latest/develop/distro.html|"generic distro" model]].
 2. or it can load an EFI image, typically grub, and delegate the boot process to this second bootloader. Like in the previous case, u-boot will scan partitions to find an EFI image at a specific location (/efi/boot/bootriscv64.efi).

In the long term, using EFI is the preferred and more standard way.
However, Debian currently lacks the tooling to make this a straightforward
option. The other option, the "legacy" extlinux.conf method, is well
integrated in Debian, so it is currently easier to setup. Both options
are documented below.

Line 47: Line 74:
As described above, two boot options are presented in this guide: either using u-boot directly or using a Grub EFI image.

Most of the steps are common for the two options.
Line 55: Line 85:
wget https://github.com/starfive-tech/linux/archive/refs/heads/visionfive.zip
unzip visionfive.zip
wget https://github.com/starfive-tech/linux/archive/refs/heads/visionfive.tar.gz
tar xf visionfive.tar.gz
Line 61: Line 91:
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- menuconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -jX bindeb-pkg LOCALVERSION=-starfive-5.18

# While you wait for the kernel to compile, you can begin setting up the roots in the next section. The following step will not work until you do this.
# Once the kernel is built and the microSD partitions are mounted (see next section), copy the .deb files to the microSD card
sudo cp ../*.deb /mnt/jh7100/root/

# Also copy the device tree
sudo cp arch/riscv/boot/dts/starfive/jh7100-starfive-visionfive-v1.dtb /mnt/jh7100/boot/
       
}}}


== Prepare filesystems and rootfs ==
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- olddefconfig
nice make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc) bindeb-pkg LOCALVERSION=-starfive
}}}

While the kernel is compiling, you can proceed with the next steps.

== Bootstrap the root filesystem ==

This step will bootstrap a minimal RISC-V root filesystem in /tmp/riscv-chroot, which can later be copied to the SD card. This is much faster than bootstrapping directly on the SD card.

{{{
# Install prerequisites on host:
sudo apt install qemu-user-static binfmt-support debootstrap debian-ports-archive-keyring systemd-container rsync wget

# The command below will bootstrap debian
sudo debootstrap --arch=riscv64 --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring unstable /tmp/riscv-chroot https://deb.debian.org/debian-ports/

# If you get an error "Unable to execute target architecture", try the following process instead:
sudo debootstrap --foreign --arch=riscv64 --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring unstable /tmp/riscv-chroot http://deb.debian.org/debian-ports
sudo mkdir -p /tmp/riscv-chroot/usr/bin/
sudo cp "$(which qemu-riscv64-static)" /tmp/riscv-chroot/usr/bin/
sudo chroot /tmp/riscv-chroot/ /debootstrap/debootstrap --second-stage
}}}

While debootstrap is working, you can proceed with the next step.

== Create partitions and filesystems ==

We need to create several partitions on the SD card:

 * partition 1 is unused
 * partition 2 is the EFI System Partition (used for boot option 2)
 * partition 3 is the root filesystem, which also includes the kernel, initramfs, and boot configuration

If you want to use a separate boot partition, be aware that it's possible but not straightforward. See "Tips to handle a separate /boot partition" at the end of this page.

We use a GPT partition table to be future-proof, but it could work with a DOS partition table.
Line 78: Line 131:
# First, prepare the microSD card partitions
sudo cfdisk -z /dev/mmcblk0
# Pick "dos" when prompted
# Create the following four partitions:
#
# 16M: 83 Linux (will be deleted anyway)
# 16M: C W95 FAT 32
# 512M: 83 Linux BOOTABLE
# Rest of card: 83 Linux

# Then, delete the first partition (leaving 16M free space there), write the partitions, and exit
# The following command will automatically setup all partitions.
# Be careful, it doesn't ask any confirmation!
sudo sgdisk -g --clear --new=1:0:+16M: --new=2:0:+100M: -t 2:EF00 --new=3:0:-1M: --attributes 3:set:2 -d 1 /dev/mmcblk0

# If you choose to create the partitions manually with gdisk, don't
# forget to set the "legacy BIOS bootable" flag on partition 3
# (go to advanced menu, then "set attributes", select partition, and set flag 2)
Line 94: Line 144:
sudo mkfs.ext4 -L _/boot /dev/mmcblk0p3
sudo mkfs.ext4 -L _/ /dev/mmcblk0p4

sudo mkdir /mnt/jh7100
sudo mount /dev/mmcblk0p4 /mnt/jh7100

sudo mkdir -p /mnt/jh7100/boot
sudo mount /dev/mmcblk0p3 /mnt/jh7100/boot/

sudo mkdir -p /mnt/jh7100/boot/efi
sudo mount /dev/mmcblk0p2 /mnt/jh7100/boot/efi/

# You can check your work with lsblk

}}}

== Bootstrapping ==
{{{
# Install prerequisites on host:
sudo apt install qemu-user-static binfmt-support

# The command below will bootstrap debian on the SD card.
sudo debootstrap --arch=riscv64 --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring unstable /mnt/jh7100 https://deb.debian.org/debian-ports/

# If you get an error "Unable to execute target architecture", try the following process instead:
sudo debootstrap --foreign --arch=riscv64 --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring unstable /mnt/jh7100/ http://deb.debian.org/debian-ports
sudo mkdir -p /mnt/jh7100/usr/bin/
sudo cp "$(which qemu-riscv64-static)" /mnt/jh7100/usr/bin/
sudo chroot /mnt/jh7100/ /debootstrap/debootstrap --second-stage
sudo mkfs.ext4 -m 0 -L root /dev/mmcblk0p3

# Prepare mountpoint for EFI filesystem in the temporary debian rootfs
sudo mkdir -p /tmp/riscv-chroot/boot/efi
}}}

== Copy kernel packages ==

Once the kernel has finished compiling, you need to copy the resulting packages in the chroot:

{{{
# Run this from the directory where you compiled the kernel
sudo cp ../*.deb /tmp/riscv-chroot/root/
}}}

== Configure Debian ==

At this point, you must wait for debootstrap to finish bootstrapping the Debian root filesystem. Then you can enter the chroot and configure the system:

{{{
Line 125: Line 165:
sudo systemd-nspawn -D /mnt/jh7100/ -M debian --bind-ro=/etc/resolv.conf sudo systemd-nspawn -D /tmp/riscv-chroot/ -M debian --bind-ro=/etc/resolv.conf
Line 127: Line 167:
apt dist-upgrade

}}}

== Installing kernel and bootloader config ==

{{{
# Now that you are in the chroot, you will install the .deb files from the kernel compilation
dpkg -i *.deb

# Next, prepare the initramfs
cd /boot
apt install initramfs-tools
update-initramfs -ck all

# You will need some files from the official StarFive Fedora image
apt upgrade

# Install initramfs tools, SSH server, NTP client, and various utils
apt install initramfs-tools openssh-server systemd-timesyncd rsync bash-completion

# Clean downloaded packages
apt clean

# Install kernel packages
apt install ./linux-*.deb

# Configure u-boot
# This should eventually become unnecessary, see:
# https://github.com/starfive-tech/u-boot/pull/31
# https://github.com/starfive-tech/u-boot/pull/32
cat <<EOF > /boot/uEnv.txt
fdt_high=0xffffffffffffffff
initrd_high=0xffffffffffffffff
kernel_addr_r=0x84000000
kernel_comp_addr_r=0x90000000
kernel_comp_size=0x10000000
fdt_addr_r=0x88000000
ramdisk_addr_r=0x88300000
# Move DHCP after MMC to speed up booting
boot_targets=mmc0 dhcp
# Fix wrong fdtfile name
fdtfile=starfive/jh7100-starfive-visionfive-v1.dtb
# Fix missing bootcmd
bootcmd=run distro_bootcmd
EOF

# Configure fstab
cat <<EOF > /etc/fstab
/dev/mmcblk0p2 /boot/efi vfat umask=0077 0 1
EOF

# Configure network
cat <<EOF >> /etc/network/interfaces
allow-hotplug eth0
iface eth0 inet dhcp
EOF

# Set root password
passwd

# Change hostname
echo visionfive > /etc/hostname

# sync disks
sync

# exit chroot
exit
}}}

== Option 1: legacy u-boot through extlinux.conf ==

With this method, we need to create an extlinux configuration file that will be parsed by u-boot.

During normal operation, the configuration is generated automatically by [[https://packages.debian.org/bookworm/u-boot-menu|u-boot-menu]]. But it doesn't work correctly in a chroot, so we need to fix it manually.

{{{
# Enter chroot again
sudo systemd-nspawn -D /tmp/riscv-chroot/ -M debian --bind-ro=/etc/resolv.conf

# Install u-boot-menu
apt install u-boot-menu

# Uncomment U_BOOT_PARAMETERS in /etc/default/u-boot and set it to the following:
# U_BOOT_PARAMETERS="rw console=tty0 console=ttyS0,115200 earlycon rootwait stmmaceth=chain_mode:1 selinux=0"
vi /etc/default/u-boot

# Re-run kernel postinstall hooks
dpkg-reconfigure linux-image-5.18.0-starfive

# Verify extlinux configuration was generated
cat /boot/extlinux/extlinux.conf

# Fix root parameter in extlinux configuration
sed -i -e 's|root=[^ ]*|root=/dev/mmcblk0p3|' /boot/extlinux/extlinux.conf

# exit chroot
exit
}}}

Finishing steps on the host:

{{{
# Copy the content of the chroot to the SD card
sudo mkdir -p /mnt/sdcard
sudo mount /dev/mmcblk0p3 /mnt/sdcard
sudo cp -a /tmp/riscv-chroot/* /mnt/sdcard/

# Make sure everything is really written (can take a long time)
sync

umount /mnt/sdcard
}}}

The SD card should now boot correctly on the VisionFive.

== Option 2: EFI with grub ==

With this option, we setup a grub EFI image on the EFI System Partition at the standard path "EFI/boot/bootriscv64.efi". This image will be detected and booted by u-boot.

Unfortunately, Debian is not yet capable of generating a RISC-V grub image. For now, we have to copy it from the Fedora image provided by StarFive.

First, configure grub:

{{{
# Add a symlink from /grub.cfg to /boot/grub.cfg
# This is a workaround because the Fedora grub image only looks at /grub.cfg
ln -s boot/grub.cfg /tmp/riscv-chroot/grub.cfg

# Write a basic grub.cfg configuration
# You should adapt the paths depending on kernel version
sudo tee /tmp/riscv-chroot/boot/grub.cfg <<EOF
set default=0
set timeout_style=menu
set timeout=2

set debug="linux,loader,mm"
set term="vt100"

menuentry 'Debian kernel 5.18 for visionfive' {
    linux /boot/vmlinuz-5.18.0-starfive root=/dev/mmcblk0p3 rw console=tty0 console=ttyS0,115200 earlycon rootwait stmmaceth=chain_mode:1 selinux=0
    devicetree /usr/lib/linux-image-5.18.0-starfive/starfive/jh7100-starfive-visionfive-v1.dtb
    initrd /boot/initrd.img-5.18.0-starfive
}
EOF
}}}

Then, if you already have a SD card with a Fedora image on it, you can just copy the grub image from it:

{{{
# Insert Fedora SD card

# Mount EFI partition of Fedora SD card
sudo mkdir -p /mnt/fedora-efi
sudo mount /dev/mmcblk0p2 /mnt/fedora-efi

# Copy EFI grub image somewhere, then unmount partition
sudo cp /mnt/fedora-efi/EFI/fedora/grubriscv64.efi /tmp/
sudo umount /mnt/fedora-efi

# Insert Debian SD card, then mount EFI partition
sudo mount /dev/mmcblk0p2 /tmp/riscv-chroot/boot/efi

# Copy EFI grub image to final destination, then umount partition
sudo mkdir -p /tmp/riscv-chroot/boot/efi/EFI/boot
sudo cp /tmp/grubriscv64.efi /tmp/riscv-chroot/boot/efi/EFI/boot/bootriscv64.efi
sudo umount /tmp/riscv-chroot/boot/efi
}}}

'''Alternatively''', if you don't have a Fedora SD card, you need to download the StarFive Fedora image and extract the file from it:

{{{
# You will need some files from the official StarFive Fedora image.
Line 153: Line 335:
# Mount the second partition of the image as follows. # Mount the partition numbered 2 of the image as follows.
Line 155: Line 337:
# For example, if the start is 319488 and units is 512 bytes, then (319488 * 512) = 163577856 # For example, if the start is 69632 and units is 512 bytes, then (69632 * 512) = 35651584
Line 157: Line 339:
sudo mkdir /mnt/fedora/
sudo mount -o loop,offset=163577856 Fedora-riscv64-jh7100-developer-xfce-Rawhide-20211226-214100.n.0-sda.raw /mnt/fedora

# Now you should be able to copy some files from the Fedora image to the SD card
sudo cp -R /mnt/fedora/boot/ /mnt/jh7100/boot
sudo cp -R /mnt/fedora/extlinux/ /mnt/jh7100/boot/
sudo cp /mnt/fedora/grub.cfg /mnt/jh7100/boot/

# Unmount the second Fedora partition
sudo umount /mnt/fedora

# And mount the first Fedora partition
sudo mount -o loop,offset=35651584 Fedora-riscv64-jh7100-developer-xfce-Rawhide-20211226-214100.n.0-sda.raw /mnt/fedora
sudo cp -R /mnt/fedora/EFI /mnt/jh7100/boot/efi

# Back in the chroot, you need to edit some of the new files

# Edit /boot/boot/uEnv.txt by commenting out ipaddr and netmask, and change "fedora" to "debian"

# Rename a directory:
cd /boot/efi/EFI
mv fedora/ debian/

# Edit /boot/extlinux/extlinux.conf to match your files:

timeout 20
menu title Kernel Boot Options.

label kernel
 kernel /vmlinuz-5.18.0-starfive-5.18
 fdt /jh7100-starfive-visionfive-v1.dtb
 append rw root=UUID=245a4691-f80c-4d26-8c59-9c4fce3f044d rhgb console=tty0 console=ttyS0,115200 earlycon=sbi rootwait stmmaceth=chain_mode:1 selinux=0 LANG=en_US.UTF-8
 initrd /initrd.img-5.18.0-starfive-5.18


# Edit /boot/grub.cfg to match your files:

set default=0
set timeout_style=menu
set timeout=2

set debug="linux,loader,mm"
set term="vt100"

menuentry 'Debian vmlinuz-5.18.0-starfive-5.18 visionfive' {
 linux /vmlinuz-5.18.0-starfive-5.18 rw root=UUID=245a4691-f80c-4d26-8c59-9c4fce3f044d rhgb console=tty0 console=ttyS0,115200 earlycon rootwait stmmaceth=chain_mode:1 selinux=0 LANG=en_US.UTF-8
 devicetree /jh7100-starfive-visionfive-v1.dtb
 initrd /initrd.img-5.18.0-starfive-5.18
}

# Set root password
passwd

# sync disks
sudo mkdir -p /mnt/fedora-efi
sudo mount -o loop,offset=35651584 Fedora-riscv64-jh7100-developer-xfce-Rawhide-20211226-214100.n.0-sda.raw /mnt/fedora-efi

# Mount EFI partition of Debian SD card
sudo mount /dev/mmcblk0p2 /tmp/riscv-chroot/boot/efi

# Now we can copy the grub image from Fedora
sudo mkdir -p /tmp/riscv-chroot/boot/efi/EFI/boot
sudo cp /mnt/fedora-efi/EFI/fedora/grubriscv64.efi /tmp/riscv-chroot/boot/efi/EFI/boot/bootriscv64.efi

# Unmount Debian EFI partition
sudo umount /tmp/riscv-chroot/boot/efi

# Unmount the Fedora partition
sudo umount /mnt/fedora-efi
}}}

Run final steps:

{{{
# Copy the content of the chroot to the SD card
sudo mkdir -p /mnt/sdcard
sudo mount /dev/mmcblk0p3 /mnt/sdcard
sudo cp -a /tmp/riscv-chroot/* /mnt/sdcard/

# Make sure everything is really written (can take a long time)
Line 213: Line 367:
# Change hostname
echo visionfive > /etc/hostname

# Install ssh server and ntp
apt install openssh-server ntp

# exit chroot
exit

# Unmount everything from the host
sudo umount /mnt/fedora
sudo umount /mnt/jh7100/boot/efi
sudo umount /mnt/jh7100/boot/
sudo umount /mnt/jh7100/

# Now the microSD card should be bootable by the VisionFive V1
}}}
umount /mnt/sdcard
}}}

The SD card should now boot correctly on the VisionFive. To verify if Debian is correctly booted through EFI, you can check if the path "/sys/firmware/efi/" exists on the running system.

== Tips to handle a separate /boot partition ==

In case you want to use a separate boot partition, here are some tips to make it work:

 * Use partition number 3 for /boot, so that uEnv.txt gets loaded from here
 * Put the uEnv.txt configuration file at "/boot/boot/uEnv.txt" (assuming that your boot partition is mounted at /boot)
 * Make sure your extlinux.conf or grub.cfg configuration is correct (no "/boot" prefix for initrd and kernel)
 * Make sure that /etc/fstab has an entry for /boot
 * You need to copy DTBs to the boot partition. This can be done automatically, see below.

=== Synchronizing DTBs automatically ===

There is a small script that automatically synchronizes DTBs to the boot partition each time you install or update a kernel.

First, make sure '''rsync''' is installed in the rootfs on the SD card. Then, assuming your rootfs is at "/mnt/sdcard" and the boot partition is mounted:

{{{
# If you used option 1 (extlinux.conf generated by u-boot-menu)
cp /mnt/sdcard/usr/share/doc/u-boot-menu/examples/zz-sync-dtb /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb
chmod +x /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb

# If you used option 2 (grub EFI image)
wget -O /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb https://sources.debian.org/data/main/u/u-boot-menu/4.0.4/zz-sync-dtb
chmod +x /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb

# In all cases, perform a manual initial sync (adapt kernel version)
mkdir -p /mnt/sdcard/boot/usr/lib/linux-image-5.18.0-starfive
rsync -a /mnt/sdcard/usr/lib/linux-image-5.18.0-starfive/. /mnt/sdcard/boot/usr/lib/linux-image-5.18.0-starfive/.
}}}

For option 1, make sure that "/boot/extlinux/extlinux.conf" has a line such as "fdtdir /usr/lib/linux-image-5.18.0-starfive/". If this is not the case, chroot and run "u-boot-update".

For option 2, make sure your "/boot/grub.cfg" has something like "devicetree /usr/lib/linux-image-5.18.0-starfive/starfive/jh7100-starfive-visionfive-v1.dtb"

= Updating the kernel =

To udpate the kernel, you need to:

 * build a new kernel using exactly the same steps as during installation (cross-compilation)
 * copy and install the resulting .deb on the device
 * if using the grub EFI method, manually update /boot/grub.cfg to point to the new kernel

The rest should work automatically (DTB synchronisation, extlinux.conf generation for the legacy boot method)

DebianOn is an effort to document how to install, configure and use Debian on some specific hardware. Therefore potential buyers would know if that hardware is supported and owners would know how get the best out of that hardware.

The purpose is not to duplicate the Debian Official Documentation, but to document how to install Debian on some specific hardware.

If you need help to get Debian running on your hardware, please have a look at our user support channels where you may find specific channels (mailing list, IRC channel) dedicated to certain types of hardware.

Translation(s): none

Models covered
?StarFive ?VisionFiveV1

  • ?StarFive VisionFive V1, ?StarFive JH7100 (Dual-core 64-bit ?SiFive U74) + Embedded 32-bit ?SiFive E24 Core, 8 GB 64-bit LPDDR4, Gigabit Ethernet, microSD Card.

Overall Status

Core Components

[ATTACH]

Boot Standard Kernel:

{?}

LAN network card ():

{OK}

Detect hard drives (microSD/NVMe):

{OK}

Xorg

[?]

Extra Features

CPU Frequency Scaling

[?]

Hibernation

[?]

Sleep / Suspend

[?]

Power Off / Reboot

{OK}

Legend :
{OK} = OK ; {X} Unsupported(No Driver) ; /!\ = Error (Couldn't get it working); [?] Unknown, Not Test ; [-] Not-applicable
{i} = Configuration Required; X-( = Only works with a non-free driver and or firmware

Important Note

There are no official Debian images available yet.

Boot process overview

The full boot process is documented in the official documentation. This guide is focused on getting u-boot to boot the Linux kernel.

The board comes with the u-boot bootloader flashed on the internal SPI flash. This u-boot first tries to load the file /boot/uEnv.txt from the third partition of the SD card, allowing to customize the boot process. It then implements the "generic distro" concept of u-boot.

Unfortunately, as of May 2022 (U-Boot 2022.04-rc2-?VisionFive built on Mar 07 2022 - 21:12:22 +0800), the bundled u-boot on the board doesn't do anything except loading the uEnv.txt file, because it's missing a built-in definition of "bootcmd". That is, the uEnv.txt file is currently mandatory and needs to specify how to boot. It also needs to define load addresses for the kernel, FDT, and/or EFI image. See https://github.com/starfive-tech/u-boot/pull/31 and https://github.com/starfive-tech/u-boot/pull/32 for patches fixing these issues.

In any case, u-boot can boot Linux using two main methods:

  1. either it can load an extlinux.conf file describing which kernel/FDT/initrd images to boot, load all three images, and boot the kernel directly. This fits the "generic distro" model.

  2. or it can load an EFI image, typically grub, and delegate the boot process to this second bootloader. Like in the previous case, u-boot will scan partitions to find an EFI image at a specific location (/efi/boot/bootriscv64.efi).

In the long term, using EFI is the preferred and more standard way. However, Debian currently lacks the tooling to make this a straightforward option. The other option, the "legacy" extlinux.conf method, is well integrated in Debian, so it is currently easier to setup. Both options are documented below.

Installing Debian on VisionFive V1

As described above, two boot options are presented in this guide: either using u-boot directly or using a Grub EFI image.

Most of the steps are common for the two options.

Build the kernel

# Install prerequisites on the host machine
sudo apt install libncurses-dev libssl-dev bc flex bison make gcc gcc-riscv64-linux-gnu

# Download and extract latest StarFive JH7100 sources
wget https://github.com/starfive-tech/linux/archive/refs/heads/visionfive.tar.gz
tar xf visionfive.tar.gz

# build the kernel
cd linux-visionfive/
cp arch/riscv/configs/starfive_jh7100_fedora_defconfig .config
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- olddefconfig
nice make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc) bindeb-pkg LOCALVERSION=-starfive

While the kernel is compiling, you can proceed with the next steps.

Bootstrap the root filesystem

This step will bootstrap a minimal RISC-V root filesystem in /tmp/riscv-chroot, which can later be copied to the SD card. This is much faster than bootstrapping directly on the SD card.

# Install prerequisites on host:
sudo apt install qemu-user-static binfmt-support debootstrap debian-ports-archive-keyring systemd-container rsync wget

# The command below will bootstrap debian
sudo debootstrap --arch=riscv64 --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring unstable /tmp/riscv-chroot https://deb.debian.org/debian-ports/

# If you get an error "Unable to execute target architecture", try the following process instead:
sudo debootstrap --foreign --arch=riscv64 --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring unstable /tmp/riscv-chroot http://deb.debian.org/debian-ports
sudo mkdir -p /tmp/riscv-chroot/usr/bin/
sudo cp "$(which qemu-riscv64-static)" /tmp/riscv-chroot/usr/bin/
sudo chroot /tmp/riscv-chroot/ /debootstrap/debootstrap --second-stage

While debootstrap is working, you can proceed with the next step.

Create partitions and filesystems

We need to create several partitions on the SD card:

  • partition 1 is unused
  • partition 2 is the EFI System Partition (used for boot option 2)
  • partition 3 is the root filesystem, which also includes the kernel, initramfs, and boot configuration

If you want to use a separate boot partition, be aware that it's possible but not straightforward. See "Tips to handle a separate /boot partition" at the end of this page.

We use a GPT partition table to be future-proof, but it could work with a DOS partition table.

# These instructions assume that your microSD card is at /dev/mmcblk0
# The following command will automatically setup all partitions.
# Be careful, it doesn't ask any confirmation!
sudo sgdisk -g --clear --new=1:0:+16M: --new=2:0:+100M: -t 2:EF00 --new=3:0:-1M: --attributes 3:set:2 -d 1 /dev/mmcblk0

# If you choose to create the partitions manually with gdisk, don't
# forget to set the "legacy BIOS bootable" flag on partition 3
# (go to advanced menu, then "set attributes", select partition, and set flag 2)                                                                                                                                                                                      

# You can double check your work with:
fdisk -l /dev/mmcblk0

# Next, prepare the filesystems
sudo mkfs.vfat /dev/mmcblk0p2
sudo mkfs.ext4 -m 0 -L root /dev/mmcblk0p3

# Prepare mountpoint for EFI filesystem in the temporary debian rootfs
sudo mkdir -p /tmp/riscv-chroot/boot/efi

Copy kernel packages

Once the kernel has finished compiling, you need to copy the resulting packages in the chroot:

# Run this from the directory where you compiled the kernel
sudo cp ../*.deb /tmp/riscv-chroot/root/

Configure Debian

At this point, you must wait for debootstrap to finish bootstrapping the Debian root filesystem. Then you can enter the chroot and configure the system:

# Now enter the chroot and ensure that the package db is up to date
sudo systemd-nspawn -D /tmp/riscv-chroot/ -M debian --bind-ro=/etc/resolv.conf
apt update
apt upgrade

# Install initramfs tools, SSH server, NTP client, and various utils
apt install initramfs-tools openssh-server systemd-timesyncd rsync bash-completion

# Clean downloaded packages
apt clean

# Install kernel packages
apt install ./linux-*.deb

# Configure u-boot
# This should eventually become unnecessary, see:
# https://github.com/starfive-tech/u-boot/pull/31
# https://github.com/starfive-tech/u-boot/pull/32
cat <<EOF > /boot/uEnv.txt
fdt_high=0xffffffffffffffff
initrd_high=0xffffffffffffffff
kernel_addr_r=0x84000000
kernel_comp_addr_r=0x90000000
kernel_comp_size=0x10000000
fdt_addr_r=0x88000000
ramdisk_addr_r=0x88300000
# Move DHCP after MMC to speed up booting
boot_targets=mmc0 dhcp
# Fix wrong fdtfile name
fdtfile=starfive/jh7100-starfive-visionfive-v1.dtb
# Fix missing bootcmd
bootcmd=run distro_bootcmd
EOF

# Configure fstab
cat <<EOF > /etc/fstab
/dev/mmcblk0p2 /boot/efi vfat umask=0077 0 1
EOF

# Configure network
cat <<EOF >> /etc/network/interfaces
allow-hotplug eth0
iface eth0 inet dhcp
EOF

# Set root password
passwd

# Change hostname
echo visionfive > /etc/hostname

# sync disks
sync

# exit chroot
exit

Option 1: legacy u-boot through extlinux.conf

With this method, we need to create an extlinux configuration file that will be parsed by u-boot.

During normal operation, the configuration is generated automatically by u-boot-menu. But it doesn't work correctly in a chroot, so we need to fix it manually.

# Enter chroot again
sudo systemd-nspawn -D /tmp/riscv-chroot/ -M debian --bind-ro=/etc/resolv.conf

# Install u-boot-menu
apt install u-boot-menu

# Uncomment U_BOOT_PARAMETERS in /etc/default/u-boot and set it to the following:
# U_BOOT_PARAMETERS="rw console=tty0 console=ttyS0,115200 earlycon rootwait stmmaceth=chain_mode:1 selinux=0"
vi /etc/default/u-boot

# Re-run kernel postinstall hooks
dpkg-reconfigure linux-image-5.18.0-starfive

# Verify extlinux configuration was generated
cat /boot/extlinux/extlinux.conf

# Fix root parameter in extlinux configuration
sed -i -e 's|root=[^ ]*|root=/dev/mmcblk0p3|' /boot/extlinux/extlinux.conf

# exit chroot
exit

Finishing steps on the host:

# Copy the content of the chroot to the SD card
sudo mkdir -p /mnt/sdcard
sudo mount /dev/mmcblk0p3 /mnt/sdcard
sudo cp -a /tmp/riscv-chroot/* /mnt/sdcard/

# Make sure everything is really written (can take a long time)
sync

umount /mnt/sdcard

The SD card should now boot correctly on the ?VisionFive.

Option 2: EFI with grub

With this option, we setup a grub EFI image on the EFI System Partition at the standard path "EFI/boot/bootriscv64.efi". This image will be detected and booted by u-boot.

Unfortunately, Debian is not yet capable of generating a RISC-V grub image. For now, we have to copy it from the Fedora image provided by ?StarFive.

First, configure grub:

# Add a symlink from /grub.cfg to /boot/grub.cfg
# This is a workaround because the Fedora grub image only looks at /grub.cfg
ln -s boot/grub.cfg /tmp/riscv-chroot/grub.cfg

# Write a basic grub.cfg configuration
# You should adapt the paths depending on kernel version
sudo tee /tmp/riscv-chroot/boot/grub.cfg <<EOF
set default=0
set timeout_style=menu
set timeout=2

set debug="linux,loader,mm"
set term="vt100"

menuentry 'Debian kernel 5.18 for visionfive' {
    linux /boot/vmlinuz-5.18.0-starfive root=/dev/mmcblk0p3 rw console=tty0 console=ttyS0,115200 earlycon rootwait stmmaceth=chain_mode:1 selinux=0
    devicetree /usr/lib/linux-image-5.18.0-starfive/starfive/jh7100-starfive-visionfive-v1.dtb
    initrd /boot/initrd.img-5.18.0-starfive
}
EOF

Then, if you already have a SD card with a Fedora image on it, you can just copy the grub image from it:

# Insert Fedora SD card

# Mount EFI partition of Fedora SD card
sudo mkdir -p /mnt/fedora-efi
sudo mount /dev/mmcblk0p2 /mnt/fedora-efi

# Copy EFI grub image somewhere, then unmount partition
sudo cp /mnt/fedora-efi/EFI/fedora/grubriscv64.efi /tmp/
sudo umount /mnt/fedora-efi

# Insert Debian SD card, then mount EFI partition
sudo mount /dev/mmcblk0p2 /tmp/riscv-chroot/boot/efi

# Copy EFI grub image to final destination, then umount partition
sudo mkdir -p /tmp/riscv-chroot/boot/efi/EFI/boot
sudo cp /tmp/grubriscv64.efi /tmp/riscv-chroot/boot/efi/EFI/boot/bootriscv64.efi
sudo umount /tmp/riscv-chroot/boot/efi

Alternatively, if you don't have a Fedora SD card, you need to download the ?StarFive Fedora image and extract the file from it:

# You will need some files from the official StarFive Fedora image.
# Open a terminal on the host machine and download the image from here:
# https://github.com/starfive-tech/Fedora_on_StarFive

# Extract the Fedora image
sudo apt install zstd
zstd -d  Fedora-riscv64-jh7100-developer-xfce-Rawhide-20211226-214100.n.0-sda.raw.zst

# Examine the partitions. Note the partition start locations and units
fdisk -u -l Fedora-riscv64-jh7100-developer-xfce-Rawhide-20211226-214100.n.0-sda.raw

# Mount the partition numbered 2 of the image as follows.
# Compute the offset by multiplying (units * start)
# For example, if the start is 69632 and units is 512 bytes, then (69632 * 512) = 35651584
# Use this offset value to mount the second partition:
sudo mkdir -p /mnt/fedora-efi
sudo mount -o loop,offset=35651584 Fedora-riscv64-jh7100-developer-xfce-Rawhide-20211226-214100.n.0-sda.raw /mnt/fedora-efi

# Mount EFI partition of Debian SD card
sudo mount /dev/mmcblk0p2 /tmp/riscv-chroot/boot/efi

# Now we can copy the grub image from Fedora
sudo mkdir -p /tmp/riscv-chroot/boot/efi/EFI/boot
sudo cp /mnt/fedora-efi/EFI/fedora/grubriscv64.efi /tmp/riscv-chroot/boot/efi/EFI/boot/bootriscv64.efi

# Unmount Debian EFI partition
sudo umount /tmp/riscv-chroot/boot/efi

# Unmount the Fedora partition
sudo umount /mnt/fedora-efi

Run final steps:

# Copy the content of the chroot to the SD card
sudo mkdir -p /mnt/sdcard
sudo mount /dev/mmcblk0p3 /mnt/sdcard
sudo cp -a /tmp/riscv-chroot/* /mnt/sdcard/

# Make sure everything is really written (can take a long time)
sync

umount /mnt/sdcard

The SD card should now boot correctly on the ?VisionFive. To verify if Debian is correctly booted through EFI, you can check if the path "/sys/firmware/efi/" exists on the running system.

Tips to handle a separate /boot partition

In case you want to use a separate boot partition, here are some tips to make it work:

  • Use partition number 3 for /boot, so that uEnv.txt gets loaded from here
  • Put the uEnv.txt configuration file at "/boot/boot/uEnv.txt" (assuming that your boot partition is mounted at /boot)
  • Make sure your extlinux.conf or grub.cfg configuration is correct (no "/boot" prefix for initrd and kernel)
  • Make sure that /etc/fstab has an entry for /boot
  • You need to copy DTBs to the boot partition. This can be done automatically, see below.

Synchronizing DTBs automatically

There is a small script that automatically synchronizes DTBs to the boot partition each time you install or update a kernel.

First, make sure rsync is installed in the rootfs on the SD card. Then, assuming your rootfs is at "/mnt/sdcard" and the boot partition is mounted:

# If you used option 1 (extlinux.conf generated by u-boot-menu)
cp /mnt/sdcard/usr/share/doc/u-boot-menu/examples/zz-sync-dtb /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb
chmod +x /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb

# If you used option 2 (grub EFI image)
wget -O /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb https://sources.debian.org/data/main/u/u-boot-menu/4.0.4/zz-sync-dtb
chmod +x /mnt/sdcard/etc/kernel/postinst.d/zz-sync-dtb

# In all cases, perform a manual initial sync (adapt kernel version)
mkdir -p /mnt/sdcard/boot/usr/lib/linux-image-5.18.0-starfive
rsync -a /mnt/sdcard/usr/lib/linux-image-5.18.0-starfive/. /mnt/sdcard/boot/usr/lib/linux-image-5.18.0-starfive/.

For option 1, make sure that "/boot/extlinux/extlinux.conf" has a line such as "fdtdir /usr/lib/linux-image-5.18.0-starfive/". If this is not the case, chroot and run "u-boot-update".

For option 2, make sure your "/boot/grub.cfg" has something like "devicetree /usr/lib/linux-image-5.18.0-starfive/starfive/jh7100-starfive-visionfive-v1.dtb"

Updating the kernel

To udpate the kernel, you need to:

  • build a new kernel using exactly the same steps as during installation (cross-compilation)
  • copy and install the resulting .deb on the device
  • if using the grub EFI method, manually update /boot/grub.cfg to point to the new kernel

The rest should work automatically (DTB synchronisation, extlinux.conf generation for the legacy boot method)