# Installing Ubuntu Server on the StarFive VisionFive 2

In the evolving landscape of computer architectures, bringing familiar operating systems to new platforms presents a unique set of challenges. A recent endeavour of mine involved installing Ubuntu Server on the StarFive VisionFive 2 RISC-V board.

Unlike the straightforward installation process we're accustomed to with more mainstream architectures, this task required delving into the nuances of UEFI bootloader configurations and intricately managing the board's boot process.

This blog post is a deep dive into that journey, aiming to demystify the steps needed to achieve a stable and functional Ubuntu setup on this emerging architecture.

### 📋 Prerequisites

**⚠️ Before you begin, your VisionFive 2 board will need to be running the latest firmware. For a guide on how to do this, check out** [**my previous blog post**](https://blog.bytesguy.com/updating-u-boot-and-spl-on-the-starfive-visionfive-2) **⚠️**

Hardware you will need:

* A MicroSD card (8GB+)
    
* NVME SSD (installed on the underside of the VisionFive 2 board)
    
* USB to UART dongle
    

Files:

* [Ubuntu Server Installer](https://ubuntu.com/download/risc-v) (choose the StarFive VisionFive 2 option, then download the "live installer"
    

### ✅ Board Setup

Flash the installer to the MicroSD card using [**Etcher**](http://etcher.balena.io/) and insert it into the MicroSD slot of the board.

Set the DIP switches to set the Boot Mode to `Flash`

* `RGPIO_1` -&gt; 0
    
* `RGPIO_0` -&gt; 0
    

Finally, set up the UART to USB connection. I have been using the [Raspberry Pi Debug Probe](https://www.raspberrypi.com/products/debug-probe/) which works great. Connect the UART leads to the board as follows:

* Pin 6 -&gt; `GND`
    
* Pin 8 -&gt; `RX`
    
* Pin 10 -&gt; `TX`
    

![StarFive VisionFive2 Board With UART Connected](https://cdn.hashnode.com/res/hashnode/image/upload/v1700567801091/022a1d58-ca59-4723-b0a3-b0309f800fc2.jpeg align="left")

Next, you need to connect to the serial terminal. If you are using macOS for example, the serial device will show with a prefix of `tty.usbmodem`, so you can search for it with the command:

```plaintext
ls /dev | grep tty.usb
```

Then initialise a connection to it using `picocom`:

```bash
sudo picocom -b 115200 /dev/tty.usbmodem2112402
```

Now we are ready to install Ubuntu!

### 👢Booting the Ubuntu Installer

To successfully boot the installer on the StarFive VisionFive 2 board, we must first identify the appropriate MicroSD Card and manually load essential files using U-Boot commands. Unlike more conventional systems where this process might be automated, the Ubuntu Installer for this specific RISC-V architecture requires direct intervention through the serial console. This step is crucial for initiating the installation process.

Firstly, power on the VisionFive board and interrupt the boot process when we see the following:

```plaintext
Hit any key to stop autoboot:  2
```

We start by listing all available memory cards with `mmc list` to see what options we have:

```plaintext
StarFive # mmc list
sdio0@16010000: 0
sdio1016020000: 1
```

In this case, we can see two devices listed; however, only one will be the MicroSD card with the installer on it. We can use the `part list` command to probe the device for partitions, and from there, determine which device and partition we need.

Let's examine the first device:

```plaintext
StarFive # part list mmc 0
Card did not respond to voltage select! : -110
```

Any time you see `Card did not respond to voltage select! : -110`, this generally means the device does not exist, or is not valid. So in this case we need to inspect the device with ID `1`:

```plaintext
StarFive # part list mmc 1

Partition Map for MMC device 1  --   Partition Type: EFI

Part	Start LBA	End LBA		Name
	Attributes
	Type GUID
	Partition GUID
  1	0x0000a000	0x0003efff	"EFI System"
	attrs:	0x0000000000000000
	type:	c12a7328-f81f-11d2-ba4b-00a0c93ec93b
	type:	system
	guid:	df2030ee-9281-40ba-8e38-027c9f37917e
  2	0x0003f000	0x0063efff	"Live Media"
	attrs:	0x0000000000000004
	type:	0fc63daf-8483-4772-8e79-3d69d8477de4
	type:	linux
	guid:	4b987a45-c7ad-4804-9048-717d7d37d6b0
  3	0x00000022	0x00000821	"loader1"
	attrs:	0x0000000000000000
	type:	5b193300-fc78-40cd-8002-e86c45580b47
	guid:	ad54bbfb-4527-4681-8f6e-fa7ef471109f
  4	0x00000822	0x00002821	"loader2"
	attrs:	0x0000000000000000
	type:	2e54b353-1271-4842-806f-e436d6af6985
	guid:	3a71d22e-655d-46a6-99db-66c67ea264a1
  5	0x0063f000	0x0e81f7ff	""
	attrs:	0x0000000000000000
	type:	0fc63daf-8483-4772-8e79-3d69d8477de4
	type:	linux
	guid:	84bc9b76-b57e-4fa4-9367-cef404541690
```

We are looking for the `EFI System` partition, which in this case is partition number `1` of device `1`

Next, we load a crucial hardware configuration file called the Device Tree Blob (DTB) from the the `EFI Partition` of the MicroSD Card (`mmc 1:1`) into the system's memory using the `load` command as below:

```plaintext
load mmc 1:1 $fdt_addr_r dtb/starfive/jh7110-starfive-visionfive-2-v1.3b.dtb
```

Following that, we also load the RISC-V Linux kernel, located in an EFI file, into memory using the `load` command:

```plaintext
load mmc 1:1 $kernel_addr_r EFI/boot/bootriscv64.efi
```

You will know the `load` command has run successfully if you see a message in the format of `12345 bytes read in 10 ms`.

Finally, we initiate the boot process with the `bootefi` command:

```plaintext
bootefi $kernel_addr_r $fdt_addr_r
```

This uses the loaded kernel and hardware configuration file to start up the installer. This sequence of commands essentially prepares the system and directs it to boot up from the specified files located on the memory card.

If successful, after a few seconds you should be greeted with the GRUB bootloader screen:

![GRUB Bootloader](https://cdn.hashnode.com/res/hashnode/image/upload/v1700952223671/61a7d2df-1b91-42ab-80ff-f4b587110a8d.png align="center")

Hit enter, or wait 30s, to proceed to the installer.

### ⚙️ Running Through the Installer

The installer can take a few minutes to run through the boot process and can, at times, appear to hang, but just let it run and it will eventually show you the following screen:

![Ubuntu Installer via Serial](https://cdn.hashnode.com/res/hashnode/image/upload/v1700952354089/90d568b3-66c5-4686-bc31-cfebffc0f851.png align="center")

From this menu, you can continue working with the installer via the serial console in either `rich` or `basic` mode. However, personally I found it easier, and more reliable, to use the SSH method. If you choose this method, you will be provided instructions on how to access the installer over SSH:

![Ubuntu Installer SSH Config](https://cdn.hashnode.com/res/hashnode/image/upload/v1700952454094/16c99943-763d-46b1-ab13-a848c0633e4e.png align="center")

Once you SSH in, you will see the standard Ubuntu Server installer program:

![Ubuntu Installer](https://cdn.hashnode.com/res/hashnode/image/upload/v1700952480394/0113e863-ea5d-4d36-8411-b1813fd79d70.png align="center")

The rest of the installer is the same as any other system, so work through it and configure the installation to your needs. Be sure to install Ubuntu on the NVME drive when prompted for the disk configuration - this will likely be the default option.

Once installed, let the device reboot automatically and move on to the final stage of configuring Ubuntu on the StarFive 2 board.

### 🥾Booting Ubuntu on NVME

Once the board reboots, you will likely be greeted with error messages in the serial console and Ubuntu will not boot automatically from the NVME drive:

```plaintext
Retrieving file: /extlinux/extlinux.conf
Can't set block device
Error reading config file
```

Don't panic, this is normal! In most systems, the Ubuntu installer will automatically configure UEFI variables to tell the bootloader how to boot into GRUB, however, U-Boot does not allow operating systems to configure these directly. So, we will have to do this manually.

Once the board has hit the error, it should drop you into the U-Boot prompt. From here you can run commands to boot Ubuntu manually:

```plaintext
pci enum
nvme scan
efidebug boot add -b 0001 'Ubuntu' nvme 0:1 /EFI/ubuntu/grubriscv64.efi
efidebug boot order 0001
bootefi bootmgr
```

We first enumerate the PCI devices on the board with `pci enum`, then we use `nvme scan` to identify and prepare any connected NVMe drives for use, allowing the system to recognize and interact with them.

Next, we need to add a UEFI boot entry with `efidebug boot add`. In this case, we are setting boot entry `1` and pointing it to the NVME drive with `nvme 0:1` and finally pointing it to the EFI executable `/EFI/ubuntu/grubriscv64.efi`.

We then set the boot order with `efidebug boot order` and then use `bootefi bootmgr` to boot the system using the UEFI boot manager. This will attempt to boot from the boot entries in the order they are set in the UEFI configuration, starting with the entry you specified (`0001` in this case).

### 🔄 Getting Ubuntu to Boot Automatically from NVME

This above commands will boot Ubuntu fine every time, but this will not "stick" and the next time you boot the board you will face the same error and the OS will not load automatically. So just how do we achieve automatic boots?

Once again, we need to drop into the U-Boot console. This time we can set some UEFI environment variables to store the commands and run them automatically each time the board is booted:

```plaintext
env default -f -a
saveenv
setenv bootcmd_nvme0 "pci enum; nvme scan; efidebug boot add -b 0001 'Ubuntu' nvme 0:1 /EFI/ubuntu/grubriscv64.efi; efidebug boot order 0001; bootefi bootmgr"
setenv bootcmd 'run bootcmd_nvme0'
saveenv
```

First, we clear any existing variables to ensure a clean slate. Afterwards, we bundle the chain of commands into the `bootcmd_nvme0` variable for use later.

Then we point the `bootcmd` variable to run our custom boot command. The `bootcmd` variable defines the series of commands that U-Boot will automatically execute during the boot process. Essentially, it's the default action that U-Boot will take unless interrupted or directed otherwise.

Finally, save the variables and you are good to go!

![Ubuntu Console on StarFive VisionFive 2 RISC-V Board](https://cdn.hashnode.com/res/hashnode/image/upload/v1700954910088/bd810b8d-d737-4213-a13c-1b339a569fd7.png align="center")

The support for this board and RISC-V is still early for Ubuntu and certain things, such as HDMI output, may not be functional at the time of writing this. I suggest accessing the device through the serial console first, enabling SSH and going from there.

### 🤔 Conclusion

In summary, installing and running Ubuntu on the StarFive VisionFive 2 board is a journey through the intricacies of U-Boot/UEFI, highlighting the adaptability required when working with emerging architectures.

By manually configuring the UEFI boot process, we can successfully navigate these hurdles and get our favourite distros up and running. This process not only underscores the importance of a solid foundational understanding of system boot mechanisms but also opens the door to the vast potential of RISC-V boards in the world of Linux computing.

Have you ventured into installing operating systems on unconventional architectures like RISC-V? Do you have experiences, insights, or questions about running Ubuntu on the StarFive VisionFive 2 or similar boards? I’d love to hear about your journey and any challenges or triumphs you’ve encountered along the way. Feel free to share in the comments below or reach out to me on [Mastodon](https://mastodon.social/@bytesguy)!
