# F07: riscv
:::info
主講人: [jserv](http://wiki.csie.ncku.edu.tw/User/jserv) / 課程討論區: [2019 年系統軟體課程](https://www.facebook.com/groups/system.software2019/)
:mega: 返回「[Linux 核心設計](http://wiki.csie.ncku.edu.tw/linux/schedule)」課程進度表
:::
## 預期目標
* 熟悉 GNU Toolchain 相關開發工具
* 接觸 [RISC-V](https://riscv.org/) 處理器架構
* 客製化 [Buildroot](https://buildroot.org/)
* 學習系統模擬器的內部運作機制
## RISC-V 處理器架構
* 為何要學 RISC-V 呢?跟著「台灣之光」衝
![](https://i.imgur.com/Yvl1SgR.png)
* [Andes Technology](http://www.andestech.com/) (晶心科技) 是亞洲地區唯一的 CPU 矽智財供應商,自主開發並提供處理器與周邊平台矽智財以及相關開發工具與軟體。根據 Linley 的調查顯示,晶心市佔率排名全球第 5
* [Embedded Linux on RISC-V](https://events.linuxfoundation.org/wp-content/uploads/2017/12/ELC_IoT_EU18_Embedded_Linux-Riscv_Khem-Raj.pdf)
* Linux 核心和 gcc 在內的 GNU Toolchain 已正式支援 RISC-V
* BBL: Berkeley Boot Loader
* [RISC-V: Open Hardware for Your Open Source Software](https://content.riscv.org/wp-content/uploads/2017/02/riscv_fosdem17.pdf) (2017)
* [RISC-V Tutorial + PULP](https://pulp-platform.org/docs/hipeac/pulp_intro_kgf.pdf)
## [Fabrice Bellard](http://bellard.org/)
* 引自 [Fabrice Bellard 是個什麼水平的程序員?](https://www.zhihu.com/question/28388113) 和 [計算的威力,智慧的傳奇 —— Fabrice Bellard](https://blog.csdn.net/gatieme/article/details/44671623)
* 上大學前重寫 LZSS 壓縮演算法,解壓軟體速度快體積小
* 1997 年 1 月 20 日,發佈最快速的計算圓周率的計算程式,是 [Bailey-Borwein-Plouffe 公式](https://en.wikipedia.org/wiki/Bailey%E2%80%93Borwein%E2%80%93Plouffe_formula) 的變形,前者的時間複雜度是 O(n^3^),他改善為 O(n^2^),使得計算速度提高 43%,這是他在數學領域的成就。此時他僅 25 歲
* 2000 年他化名 Gérard Lantau,建立 [FFmpeg 專案](https://ffmpeg.org/),做過多媒體影音處理的人應該都知道這個專案到底多強大
* YouTube, VLC, MPlayer 等知名服務和軟體採用 FFmpeg 的編解碼函式庫
* FFmpeg 易擴展、功能強、速度快、佔資源少,支持的影音格式極其廣泛,超越其他所有同類軟體,這是他在多媒體處理領域的巨大成就
* 2000-2001 年,贏得兩次國際混淆 C 程式碼大賽 ([IOCCC](https://www.ioccc.org/)) 獎項
* 第一個作品是 4 KB 大小的 C 語言編譯器子集 OTCC,這可以算作是 TinyCC 的前身
* 第二個作品以 475B 這麼小的程式碼實作高效能的找尋質數程式,用傅立葉轉換實現
* 2002 年他發佈 [TinyGL](https://bellard.org/TinyGL/),這是 OpenGL 的一個子集實作,體積小速度快,佔資源還少,這是他在圖像處理領域的成就
* 2003 年開發 Emacs 的一個變種 QEmacs
* 2004 年 8 月在之前 OTCC 的基礎上繼續開發,使之具備能夠編譯 Linux 核心的能力,這就是 TinyCC 的正式版,簡稱 [TCC](https://bellard.org/tcc/)
* 為了證明 TCC 的威力,他又寫了一個只有 138KB 的 boot loader,稱為 [TCCBOOT](https://bellard.org/tcc/tccboot.html),可在 15 秒內從原始程式碼編譯並啟動 Linux 核心
* 2005 年發佈 QEMU,這是個爆炸性的項目,現在眾多底層開發人員已經離不開它了,這需要非常廣泛的底層硬體和作業系統等等知識
* QEMU 的技術已經被應用於 KVM, Xen, VirtualBox 等多個虛擬化專案
* 2005 年用普通個人電腦和 VGA 顯示卡設計一個數位電視系統
* 2009 年 12 月 31 日,聲稱打破圓周率計算的世界紀錄,僅用一台普通個人電腦,耗時 116 天,算出圓周率小數點後 2.7 兆位,比 2009年 8 月 17 日由超級計算機算出的世界紀錄多 1200 億位
* 憑藉這個突出的數學貢獻,登上《科學美國人》法文版
* 2011 年,用 JavaScript 開發出模擬 Intel x86 為基礎的個人電腦的模擬器
* 包含一個 32-bit 的 x86 相容處理器、8259 可程式化中斷控制器、8254 可程式化中斷計時器,和 16450 UART
* 繼續看 Wikipedia 的 [詞目](https://en.wikipedia.org/wiki/Fabrice_Bellard)
* [TinyEMU](https://bellard.org/tinyemu/) 是 Fabrice Bellard 近期作品,支援 RISC-V 和 Intel x86 架構的系統模擬
## riscv-emu
:::warning
以下操作均在 GNU/Linux 環境中進行,在模擬器內執行 RISC-V/Linux,反應時間可能會大幅增加,請保持耐心
命令標示方式:
* ==`$ `== 開頭表示在 Host 端 GNU/Linux 輸入的命令,也就是你平常的作業環境
* ==`~# `== 開頭表示在 [riscv-emu](https://github.com/sysprog21/riscv-emu) 模擬環境中的 RISCV/Linux 命令環境中輸入的命令。這樣的模擬環境簡稱 Guest 端
:::
* [riscv-emu](https://github.com/sysprog21/riscv-emu) 衍生自 [TinyEMU](https://bellard.org/tinyemu/)
* 編譯 [riscv-emu](https://github.com/sysprog21/riscv-emu) 之前需要安裝開發套件
```shell
$ sudo apt install libsdl2-dev libssl-dev libcurl4-openssl-dev
```
* 編譯和執行
```shell
$ make
$ ./temu
temu version 2019-02-10, Copyright (c) 2016-2018 Fabrice Bellard
usage: riscvemu [options] config_file
options are:
-m ram_size set the RAM size in MB
-rw allow write access to the disk image (default=snapshot)
-ctrlc the C-c key stops the emulator instead of being sent to the
emulated software
-append cmdline append cmdline to the kernel command line
Console keys:
Press C-a x to exit the emulator, C-a h to get some help.
```
* 透過 [riscv-emu](https://github.com/sysprog21/riscv-emu) 載入預先準備好的 RISC-V/Linux 核心映像檔案和 root file system,確保你的網路連線無誤
```shell
$ ./temu https://bellard.org/jslinux/buildroot-riscv64.cfg
```
請耐心等待,過程中程式會自上述網址下載對應的檔案。
預期會看到以下輸出:
```
Welcome to JS/Linux (riscv64)
Use 'vflogin username' to connect to your account.
You can create a new account at https://vfsync.org/signup .
Use 'export_file filename' to export a file to your computer.
Imported files are written to the home directory.
[root@localhost ~]#
```
當看到上方 `[root@localhost ~]# ` 命令提示訊息時,就可以輸入 GNU/Linux 常見命令,如 `uname -a` 和 `ps`
用 `ls` 觀察,可發現 `/root` 目錄下已有 `readme.txt` 檔案,請用 `cat readme.txt` 觀察並作配合的實驗。
例如計算圓周率:
```shell
~# tinypi 1000 pi.txt
~# cat pi.txt
```
甚至可在 risc64 模擬環境中模擬 riscv128:
```shell
~# riscvemu128 -m 16 rv128test.bin
```
預期輸出結果:
```
RISCV dynamic base ISA change:
RV128I: max register value=340282366920938463463374607431768211455
FP128: sqrt(2)=1.414213562373095048801688724209698
RV64I: max register value=18446744073709551615
FP64: sqrt(2)=1.414213562373095
RV32I: max register value=4294967295
FP32: sqrt(2)=1.414213
Power off.
```
當要離開 [riscv-emu](https://github.com/sysprog21/riscv-emu) 時,先按下 `Ctrl-A` 組合鍵,「放開後再按」 `x` 鍵
假設目前目錄為 `$HOME/riscv-emu`,我們設定 `$PATH` 環境變數,讓之後方便使用:
```shell
$ export PATH=`pwd`:$PATH
```
接著取得預先準備好的 diskimage:
```shell
$ cd /tmp
$ wget https://bellard.org/tinyemu/diskimage-linux-riscv-2018-09-23.tar.gz
$ tar zxvf diskimage-linux-riscv-2018-09-23.tar.gz
```
切換到解開的 diskimage 目錄並啟動模擬器:
```shell
$ cd diskimage-linux-riscv-2018-09-23
$ temu root-riscv64.cfg
```
預期將看到以下輸出:
```shell
[ 0.406021] NET: Registered protocol family 17
[ 0.406693] 9pnet: Installing 9P2000 support
[ 0.411081] EXT4-fs (vda): couldn't mount as ext3 due to feature incompatibilities
[ 0.412022] EXT4-fs (vda): mounting ext2 file system using the ext4 subsystem
[ 0.420029] EXT4-fs (vda): mounted filesystem without journal. Opts: (null)
[ 0.420326] VFS: Mounted root (ext2 filesystem) on device 254:0.
[ 0.421026] devtmpfs: mounted
[ 0.421493] Freeing unused kernel memory: 80K
[ 0.421664] This architecture does not have kernel memory protection.
~ #
```
檢查處理器架構和核心資訊:
```shell
~# uname -a
```
預期輸出為:
```
Linux localhost 4.15.0-00049-ga3b1e7a-dirty #10 Thu Sep 13 20:03:10 CEST 2018 riscv64 GNU/Linux
```
繼續輸入以下命令,觀察輸出結果:
* `cat /proc/cpuinfo`
* `cat /proc/meminfo`
* `cat /proc/cmdline`
* `cat /proc/devices`
* `cat /proc/interrupts`
* `cat /proc/loadavg`
先離開模擬器 (按下 `Ctrl-A` 組合鍵,放開後再按 `x` 鍵),編輯檔案 `root_9p-riscv64.cfg`,確保內容如下:
```
{
version: 1,
machine: "riscv64",
memory_size: 128,
bios: "bbl64.bin",
kernel: "kernel-riscv64.bin",
cmdline: "console=hvc0 root=/dev/vda rw",
drive0: { file: "root-riscv64.bin" },
fs0: { tag: "/dev/root", file: "/tmp" },
eth0: { driver: "user" },
}
```
也就是新增 `kernel: "kernel-riscv64.bin"` 這行。
啟動模擬器:
```shell
$ temu root_9p-riscv64.cfg
```
當看到 `~ # ` 命令提示訊息時,輸入以下命令:
```shell
~# mount -t 9p /dev/root /mnt
```
上述命令是透過 [VirtFS](https://wiki.qemu.org/Documentation/9psetup) 掛載 Host 端檔案系統到 Guest 端,驗證方式:
```shell
~# ls /mnt
```
觀察輸出的內容,不難發現存在 `diskimage-linux-riscv-2018-09-23` 目錄,也就是之前我們自壓縮檔案解開的目錄,我們的模擬器也從中載入映像檔案。有這樣的檔案分享機制,你就可以很方便地在模擬器中實驗。
詳細閱讀 [TinyEMU System Emulator by Fabrice Bellard](https://bellard.org/tinyemu/readme.txt)
## Buildroot
* 參照 [TinyEMU RISC-V Buildroot](https://bellard.org/tinyemu/buildroot.html) 指示
* 取得客製化的 buildroot 並設定:
```shell
$ wget https://bellard.org/tinyemu/buildroot-riscv-2018-10-20.tar.gz
$ tar zxvf buildroot-riscv-2018-10-20.tar.gz
$ cd buildroot-riscv-2018-10-20
$ cp configs/riscv64_defconfig .config
```
注意檔案 `.config` 是我們要透過 buildroot 工具去建構 kernel image + root file system 的設定檔案,我們可透過 `$ make menuconfig` 去調整。在這之前,確保已經安裝必要的開發套件:
```shell
$ sudo apt install libncurses5-dev
```
接著變更設定:
```shelll
$ make menuconfig
```
預期會看到下方輸出:
![](https://i.imgur.com/HnrXQyW.png)
可嘗試用方向鍵瀏覽,觀看個別設定內容。記錄你發現有趣的選項 (乍看不懂沒關係,保持好奇心)。
由於 buildroot 裡頭 [e2fsprogs](http://e2fsprogs.sourceforge.net/) 套件較舊,可能在新版 GNU/Linux 發行套件彙編譯失敗,於是我們著手更新:
* 編輯檔案 `package/e2fsprogs/e2fsprogs.mk`,更改 `E2FSPROGS_VERSION =` 後面的字串從 `1.43.1` 到 `1.44.5`
* 編輯檔案 `package/e2fsprogs/e2fsprogs.hash`, 新增以下
```
sha256 ba5eb3069d69160d96818bb9700de9ab5a8458d9add1fd85d427c0000d34c5b9 e2fsprogs-1.44.5.tar.xz
```
* 刪去檔案 `package/e2fsprogs/0002-fuse2fs-might-need-librt.patch`
接著終於要使用 buildroot 來建構 root file system,過程要等上一陣子。
```shell
$ make
```
如果沒有遭遇到困難 (就算遇到也別急著 Google 搜尋,嘗試自己排除),應該可在 `output` 目錄發現 buildroot 幫我們建構的檔案,其中 `output/host` 是必要的開發工具,如:
```shell
$ output/host/usr/bin/riscv64-buildroot-linux-gnu-gcc -v
```
檢查上述輸出中,是否包含以下字串:
* `--target=riscv64-buildroot-linux-gnu`
* `Thread model: posix`
* `gcc version 7.3.0`
透過 buildroot 產生的 GNU Toolchain 也置放於 `output/host` 目錄。
建立一個小程式,檔名為 `hello.c`:
```cpp
#include <stdio.h>
int main() { puts("Hello!"); return 0; }
```
先在 Host 端進行 [cross-compile](https://en.wikipedia.org/wiki/Cross_compiler):
```shell
$ ./output/host/usr/bin/riscv64-buildroot-linux-gnu-gcc -o hello hello.c -static
```
之後啟動 [riscv-emu](https://github.com/sysprog21/riscv-emu),需要透過 [VirtFS](https://wiki.qemu.org/Documentation/9psetup) 掛載 Host 端檔案系統到 Guest 端的方式: (自行透過 `cd` 切換到對應的目錄)
```shell
$ temu root_9p-riscv64.cfg
```
在 Guest 中執行以下命令:
```shell
~# mount -t 9p /dev/root /mnt
~# cd /mnt/buildroot-riscv-2018-10-20
~# ./hello
```
應該可見 `Hello!` 字串輸出。
透過 buildroot 建立的 root filesystem 位於 `output/images/rootfs.ext2`,用 file 檢查:
```shell
$ file output/images/rootfs.ext2
```
預期會看到 `output/images/rootfs.ext2: Linux rev 1.0 ext2 filesystem data` 的輸出,注意到 `ext2` 字串。
回到 `diskimage-linux-riscv-2018-09-23` 目錄,建立以下檔案:
```
{
version: 1,
machine: "riscv64",
memory_size: 128,
bios: "bbl64.bin",
kernel: "kernel-riscv64.bin",
cmdline: "console=hvc0 root=/dev/vda rw",
drive0: { file: "/tmp/buildroot-riscv-2018-10-20/output/images/rootfs.ext2" },
fs0: { tag: "/dev/root", file: "/tmp" },
eth0: { driver: "user" },
}
```
相較之前的設定檔案,變更了 `drive0` 這項,接著啟動模擬器:
```shell
$ temu test64.cfg
```
預期會看到以下訊息
```
Welcome to Buildroot
localhost login:
```
輸入 `root`,不用密碼即可登入系統。
在 Guest 中輸入以下命令:
```shell
~# busybox --help | head
```
注意看 `BusyBox v1.24.2` 旁邊的字串,應該會有 `2019-03-26` 或更晚的時間,表示你透過 buildroot 編譯的 [Busybox](https://busybox.net/) 工具。
做完實驗記得在 ==Guest 端==關機: (不要傻到在 Host 端輸入)
```shell
~# poweroff
```
## Kilo
[kilo](https://github.com/sysprog21/kilo) 是個極小的程式碼編輯器,支援語法高亮度提示和常見的編輯功能,原始程式碼約 1000 行。
預期執行畫面:
![](https://i.imgur.com/QbN3FMk.png)
功能按鍵:
* `Ctrl-Q`: 離開編輯器
* `Ctrl-F`: 尋找特定字串
* `Ctrl-E`: 刪去目前所在列
* `Ctrl-J`: 移動游標到列首
* `Ctrk-K`: 移動游標到列尾
## 自我檢查清單
* [riscv-emu](https://github.com/sysprog21/riscv-emu) 原始程式碼中多次出現 [virtio](https://www.linux-kvm.org/page/Virtio),這樣的機制對於 host 和 guest 兩端有何作用?在閱讀 [Virtio: An I/O virtualization framework for Linux](https://www.ibm.com/developerworks/library/l-virtio/index.html) 一文後,對照原始程式碼,你發現什麼?
* 透過 `$ temu root-riscv64.cfg`, 我們在 RISCV/Linux 模擬環境中,可執行 `gcc` 並輸出對應的執行檔,而之後我們則執行 `riscv64-buildroot-linux-gnu-gcc`,這兩者有何不同? (提示: cross-compiler, 複習 [你所不知道的 C 語言: 編譯器和最佳化原理篇](https://hackmd.io/s/Hy72937Me)
* 在 Guest 端透過 `$ dmesg | grep 9pnet` 命令,我們可發現 `9P2000` 字樣,這和上述 [VirtFS](https://wiki.qemu.org/Documentation/9psetup) 有何關聯?請解釋運作原理並設計實驗
* 在 [TinyEMU System Emulator by Fabrice Bellard](https://bellard.org/tinyemu/readme.txt) 提到 "Network block device",你能否依據說明,嘗試讓 guest 端透過 host 存取到網際網路呢?
* tap, bridge, NAT, iptables
* 最初實驗輸入 `$ temu https://bellard.org/jslinux/buildroot-riscv64.cfg`,然後就能載入 RISC-V/Linux 系統,背後的原理是什麼呢?請以 VirtIO 9P 檔案系統和 [riscv-emu](https://github.com/sysprog21/riscv-emu) 對應的原始程式碼來解說
> TinyEMU supports the VirtIO 9P filesystem to access local or remote filesystems. For remote filesystems, it does HTTP requests to download the files.
> The protocol is compatible with the vfsync utility. In the "mount" command, "/dev/rootN" must be used as device name where N is the index of the filesystem. When N=0 it is omitted.
* [riscv-emu](https://github.com/sysprog21/riscv-emu) 內建浮點運算模擬器,使用到 [SoftFP Library](https://bellard.org/softfp/),請以 `sqrt` 為例,解說 `sqrt_sf32`, `sqrt_sf64`, `sqrt_sf128` 的運作機制,以及如何對應到 RISC-V CPU 模擬器中
* 在 `root-riscv64.cfg` 設定檔中,有 `bios: "bbl64.bin"` 描述,這用意為何?提示:參閱 [Booting a RISC-V Linux Kernel](https://www.sifive.com/blog/all-aboard-part-6-booting-a-risc-v-linux-kernel)
* 能否用 buildroot 編譯 Linux 核心呢?請務必參閱 [Buildroot Manual](https://buildroot.org/downloads/manual/manual.html)
* `BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="/tmp/diskimage-linux-riscv-2018-09-23/patches/config_linux_riscv64"`
* 核心啟動的參數 `console=hvc0 root=/dev/vda rw` 代表什麼意思呢?這對應到模擬器內部設計的哪些部分?
* `$ cat /proc/loadavg` 的輸出意義為何?能否對應到 Linux 核心原始碼去解釋呢? (提示: 熟悉的 fixed-point 操作)
* 為何需要在 host 端準備 e2fsprogs 工具呢?具體作用為何呢?
* root file system 在 Linux 核心的存在意義為何?而 [initramfs](http://blog.linux.org.tw/~jserv/archives/001954.html) 的存在的考量為何?
* busybox 這樣的工具有何作用?請搭配原始程式碼解說 (提示: 參見 [取得 GNU/Linux 行程的執行檔路徑](http://blog.linux.org.tw/~jserv/archives/002041.html))
## 作業要求
* 回答上述「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼,以第一手材料 (包含自己設計的實驗) 為佳
* 調整 buildroot 設定,讓原本輸出的 ext2 image 佔用約 14 MB 空間,變得更精簡
* 移除套件,但確保仍可開機並提供必要的服務及工具
* Busybox 裡頭的 vi 也不需要存在,改用 kilo
* 編譯 [kilo](https://github.com/sysprog21/kilo) 編輯器,允許在 RISC-V/Linux 模擬環境中運作
* 客製化 buildroot,將 [kilo](https://github.com/sysprog21/kilo) 編輯器加入到 build image,建立 Git repository 來追蹤你對 buildroot 做的變更,並依據 [Buildroot Manual](https://buildroot.org/downloads/manual/manual.html) 更新套件描述和建構方式
* 重新編譯 Linux 核心,確保能在 [riscv-emu](https://github.com/sysprog21/riscv-emu) 運作,並思考縮減 image size 的方法
* 先從 `$ make menuconfig` 調整核心選項開始,當然要能開機才有效益
* 參照 [Linux Kernel Tinification](https://tiny.wiki.kernel.org)
## 繳交方式
編輯 [Homework4 作業區共筆](https://hackmd.io/s/r1bJyiauV),將你的觀察、上述要求的解說和改善過程,紀錄於新建立的共筆
## 截止日期
Apr 12, 2019 (含) 之前