---
# System prepended metadata

title: Run Linux Kernel on RVVM
tags: [linux]

---

# Run Linux Kernel on RVVM 
鑑於 [RVVM](https://github.com/LekKit/RVVM) 專案對於如何運行 Linux kernel 沒有過多的解釋，猜測作者也許認為這是基本常識，因此以下提供一些指引，讓 Linux kernel 可以順利的在 RVVM 上運行。

感謝這個 RVVM Issue 57 [[2]](https://github.com/LekKit/RVVM/issues/57) 的內容，提供能許多有用的資訊

## 一般開機流程
一般開機流程如下（以下內容的細節可能有些出入，但大方向是對的），但並沒有一定要完全相同，例如如果 OS kernel 已經被載入，我們其實可以直接從 OpenSBI 直接跳到 OS kernel。
1. CPU 進行最簡單的初始化（CPU register, clock, RAM)
2. 這時 LOADER 負責將 OpenSBI 這類 firmware 載入
3. OpenSBI 負責提供 SBI 相關功能，並轉跳至下一個階段的 Bootloader (U-Boot, Grub)
4. 接著 U-boot/Grub 負責載入作業系統（雙系統看見的開機選單就是在這裡）

然而在啟動模擬器時我們可以省略一些步驟，例如在 RVVM 上我們不需要去擔心 ROM 與 LOADER 這部份要怎麼載入 OpenSBI, Linux kernel，RVVM 內部會幫我們處理好；而在 [SEMU](https://github.com/sysprog21/semu) 上，我們甚至不需要 OpenSBI 即可直接啟動 Linux kernel。

![image](https://hackmd.io/_uploads/BkKOsnvoel.png)

## 開機準備

要再 RVVM 上啟動 Linux kernel 至少需要以下三樣東西
* [OpenSBI](https://github.com/riscv-software-src/opensbi) (firmware)
* rootfs (使用 [buildroot](https://buildroot.org/docs.html) 建立)
* [Linux kernel](https://github.com/torvalds/linux)

為了編譯出 RISC-V 指令集的程式，我們還需要 RISC-V 的 toolchain
* [risc-v gnu toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain)

### RISC-V GNU Toolchain
Toolchain 非常容易編譯，照著文件上的 Readme\.md 就可以編譯了，如下：

1. 下載 prerequest
```shell
$ sudo apt-get install autoconf automake autotools-dev curl python3 python3-pip python3-tomli libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev ninja-build git cmake libglib2.0-dev libslirp-dev
```

2. 下載 repository
```shell
$ git clone https://github.com/riscv/riscv-gnu-toolchain
```

3. 決定 Toolchain 要安裝的位置，預設是 64 位元，如果有特殊需求，例如支援的 ISA 與位元，請參考手冊
```shell
$ ./configure --prefix=/opt/riscv
$ make linux
```

4. 最後將 `/opt/riscv/bin` 放到 `PATH` 變數底下

### OpenSBI
接著編譯 OpenSBI，由於我們需要他的 firmware 因此編譯時我們需要指定平台，這邊選定 generic。
```shell
$ make PLATFORM=generic CROSS_COMPILE=riscv64-unknown-linux-gnu-
```

編譯完成後，編譯結果會放在 `build/` 目錄底下。再 `build/platform/generic/firmware/` 會看到編譯出來的 firmware。裡面會有 
* `fw_jump.bin`
* `fw_payload.bin`
* `fw_dynamic.bin`

這些 firmware 的功能有些許的不同。但這邊不展開來講，簡單來說 jump 就是執行完後跳到特定位置 (可以是 OS kernel 也可以是 Bootloader)；如果是 payload 代表將其程式嵌入到 firmware 內。由於我們沒有決定要嵌入的程式，他預設就是一個 while 無窮迴圈。

### Rootfs
使用 buildroot 建立，首先要進到 target option 內修改架構，改成 RISC-V (記得要確定是 64 bit)，否則啟動程式 (e.g. `/sbin/init`) 會沒辦法執行。
```shell
$ make menuconfig
```

![buildroot_menuconfig](https://hackmd.io/_uploads/HyRUkSFslg.png)

接著，在 Filesystem images 內將 ext2/3/4 root filesystem 選項打開。
![image](https://hackmd.io/_uploads/B1ja1SKjxg.png)
![image](https://hackmd.io/_uploads/ByZfgrKill.png)

這樣就可以開始編譯了
```shell
$ make
```

編譯完成後再 `output/images` 內會有 rootfs 的映像檔，之後會用到。

### Linux Kernel
最後最重要的就是編譯 Linux kernel，在 RVVM 的 Main feature 中可以看到 storage 是使用 NVMe 進行溝通，這代表我麼在編譯 Linux kernel 時一定要開啟 NVMe 的驅動程式，否則會沒有辦法將我們的 rootfs mount 上去。接著還有 Frame buffer 與 I2C 需要打開，否則 RVVM 的視窗會沒辦法進行資料的輸入輸出。

```shell
$ make menuconfig ARCH=riscv
```

接著，將這個選項打開。
* `CONFIG_BLK_DEV_NVME`
* `CONFIG_DRM`
* `CONFIG_I2C_OCORES`
* `CONFIG_I2C_HID_OF`
* `CONFIG_FRAMEBUFFER_CONSOLE`
* `CONFIG_FB_SIMPLE`

> 礙於自己對於 Linux Kernel 的理解有限，目前無法正確的解釋為什麼看到 RVVM 支援的 feature 後需要開啟這些功能。雖然經過不斷的試錯後得出最關鍵的幾個設定，但依舊只能推測其原因，不能確定實際的原理，待理解後補上。
> 例如：這個開機流程並沒有使用到 initrd，然而這是否與將 CONFIG_BLK_DEV_NVME 在編譯時放入 Linux kernel 有關？

設定好後就可以編譯了
```shell
$ make vmlinux ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- -j 8
```

## 執行 RVVM
```shell
$ ./rvvm_x86_64 fw_payload.bin -i rootfs_riscv.ext2 -k vmlinux -m 2G -smp 2 -res 1280x720 
```
![RVVM](https://hackmd.io/_uploads/HkPXKwFjxe.png)


## Reference
[1] https://riscv.org/wp-content/uploads/2024/12/13.30-RISCV_OpenSBI_Deep_Dive_v5.pdf
[2] https://github.com/LekKit/RVVM/issues/57
[3] https://github.com/LekKit/patches-misc/blob/master/linux-rvvm-configs/config_tiny_6.2