# 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