# 2019q1 Homework4 (riscv)
contributed by < `rebvivi` >
###### tags: `linux2019`
* [F07: riscv](https://hackmd.io/s/ryHaBkrOE#F07-riscv)
## 作業要求
- 回答「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼,以第一手材料 (包含自己設計的實驗) 為佳
- 調整 buildroot 設定,讓原本輸出的 ext2 image 佔用約 14 MB 空間,變得更精簡
* 移除套件,但確保仍可開機並提供必要的服務及工具
* Busybox 裡頭的 vi 也不需要存在,改用 kilo
- 編譯 kilo 編輯器,允許在 RISC-V/Linux 模擬環境中運作
- 客製化 buildroot,將 kilo 編輯器加入到 build image,建立 Git repository 來追蹤你對 buildroot 做的變更,並依據 Buildroot Manual 更新套件描述和建構方式
- 重新編譯 Linux 核心,確保能在 riscv-emu 運作,並思考縮減 image size 的方法
* 先從 `$ make menuconfig` 調整核心選項開始,當然要能開機才有效益
* 參照 Linux Kernel Tinification
## 自我檢查清單
1. riscv-emu 原始程式碼中多次出現 virtio,這樣的機制對於 host 和 guest 兩端有何作用?在閱讀 Virtio: An I/O virtualization framework for Linux 一文後,對照原始程式碼,你發現什麼?
2. 透過 `$ temu root-riscv64.cfg`, 我們在 RISCV/Linux 模擬環境中,可執行 `gcc` 並輸出對應的執行檔,而之後我們則執行 `riscv64-buildroot-linux-gnu-gcc`,這兩者有何不同? (提示: cross-compiler, 複習 你所不知道的 C 語言: 編譯器和最佳化原理篇
3. 在 Guest 端透過 `$ dmesg | grep 9pnet` 命令,我們可發現 `9P2000` 字樣,這和上述 VirtFS 有何關聯?請解釋運作原理並設計實驗
4. 在 TinyEMU System Emulator by Fabrice Bellard 提到 “Network block device”,你能否依據說明,嘗試讓 guest 端透過 host 存取到網際網路呢?
* tap, bridge, NAT, iptables
5. 最初實驗輸入 `$ temu https://bellard.org/jslinux/buildroot-riscv64.cfg`,然後就能載入 RISC-V/Linux 系統,背後的原理是什麼呢?請以 VirtIO 9P 檔案系統和 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.
6. riscv-emu 內建浮點運算模擬器,使用到 SoftFP Library,請以 `sqrt` 為例,解說 `sqrt_sf32`, `sqrt_sf64`, `sqrt_sf128` 的運作機制,以及如何對應到 RISC-V CPU 模擬器中
7. 在 `root-riscv64.cfg` 設定檔中,有 `bios: "bbl64.bin"` 描述,這用意為何?提示:參閱 Booting a RISC-V Linux Kernel
8. 能否用 buildroot 編譯 Linux 核心呢?請務必參閱 Buildroot Manual
* `BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="/tmp/diskimage-linux-riscv-2018-09-23/patches/config_linux_riscv64"`
9. 核心啟動的參數 `console=hvc0 root=/dev/vda rw` 代表什麼意思呢?這對應到模擬器內部設計的哪些部分?
10. `$ cat /proc/loadavg` 的輸出意義為何?能否對應到 Linux 核心原始碼去解釋呢? (提示: 熟悉的 fixed-point 操作)
11. 為何需要在 host 端準備 e2fsprogs 工具呢?具體作用為何呢?
12. root file system 在 Linux 核心的存在意義為何?而 initramfs 的存在的考量為何?
13. busybox 這樣的工具有何作用?請搭配原始程式碼解說 (提示: 參見 取得 GNU/Linux 行程的執行檔路徑)
### 1. riscv-emu 原始程式碼中多次出現 virtio,這樣的機制對於 host 和 guest 兩端有何作用?在閱讀 Virtio: An I/O virtualization framework for Linux 一文後,對照原始程式碼,你發現什麼?
- `virtio`:是一些常見的虛擬裝置在`paravirtualized` `hypervisor`裡的具象化,一種 I/O `paravirtualized` 解決方案,是一套通用 I/O 設備虛擬化的程式
- 在`Full-Virtualization`,`guest` 要使用底層 `host` 的資源,需要 `Hypervisor` 來截獲所有的請求指令,然後模擬出這些指令的行為
- `Para-Virtualization`通過底層`H/W`輔助的方式,將部分`Non-Virtualizable OS 指令`通過`H/W`來完成,`Hypervisor` 只負責完成部分指令的虛擬化,但要做到這點需要 `guest` 來配合 :
- `guest` 完成不同設備的前端驅動程式,`Hypervisor` 配合 `guest` 完成相應的後端驅動程式,這樣兩者之間通過某種交互機制就可以實現高效的虛擬化過程
- 由於不同 `guest` 設備其工作邏輯大同小異,單獨為每個設備定義是很沒有必要的,而且還要考慮擴平台的兼容性問題,不同`Hypervisor` 的實現方式也大同小異,這個時候就需要一套通用框架和標準接口來完成兩者之間的交互過程,`virtio` 就是這樣一套標準,解決了這些不通用的問題

- 在`virtio`有分為`guest`中的前端程式和`hypervisor`中的後端程式,而前端程式有 5 種,每個前端驅動程式在 `hypervisor` 中有一個對應的後端驅動程式 :
- `virtio_blk`:`virtio_blk` 提供了 `virtual machine` 可以透過`virtio API` 進行 `block device I/O` 的相關驅動程式,藉以提升存取 `block device` 的效能
- `virtio_net`:使用 `virtio network device`,有提高 throughput & 降低 latency 的兩項優點,可以達到接近原生網卡的效能,因此通常是配置網路時的優先選擇
- `virtio_balloon`:在 `virtual machine` 運行時動態調整記憶體的配置,而不需要 `virtual machine` 關機
- 當 `host machine` 記憶體空間不足時,在 `virtual machine` 中的記憶體 balloon 會膨脹,讓 `virtual machine` 實際上無法使用到太多的記憶體,進而讓記憶體空間可以讓 `host machine` 暫時利用
- 當 `virtual machine` 記憶體空間不足時,在 `virtual machine` 中的記憶體 balloon 則會壓縮,讓 `host machine` 可以分配閒置的記憶體給 `virtual machine`

- `virtio_driver`:使用 `virtio_device` 建立的 `guest`端 driver,通過`register_virtio_driver` 進行註冊
- 在 `virtio_driver`的 struct 中,定義了upper-level device driver、 driver 支援的 device ID 列表、一個特性表單( 取決於 device type )和一個 callback function 列表
- `hypervisor` 識別到與 device 列表中 device ID 相符的新 device 時,將調用 `probe()`來傳入 `virtio_device`
- 調用 `virtio_config_ops` 來 get/set 特定於 device 的選項,例如:為各種 block device driver 設定 block size 或 get/set `virtio_blk` device 的 disk 的 Read/Write status
- 要 identify 與該 `virtio_device` 相關聯的`virtqueue`,需要結合使用 `virtio_config_ops` 的 `find_vq()`,它 return 與這個 `virtio_device`相關聯的 `virtual queue`
- `virtqueue` 是一個簡單的 structure ,它 identify:
- `void (*callback)(struct virqueue *vq)`
- `struct virtio_device *vdev`
- `struct virtqueue_ops *vq_ops`
- `void *priv`:存放著底層的 implementation
- `virtqueue_ops`:定義在`guest`和`hypervisor`之間 command 和 data 的傳輸方式

### 2. 透過 `$ temu root-riscv64.cfg`, 我們在 RISCV/Linux 模擬環境中,可執行 `gcc` 並輸出對應的執行檔,而之後我們則執行 `riscv64-buildroot-linux-gnu-gcc`,這兩者有何不同? (提示: cross-compiler, 複習 你所不知道的 C 語言:
在 RISCV/Linux 模擬環境中,執行 `gcc`,只是把平常執行 `gcc` 的環境換成 RISCV/Linux 模擬環境
- `cross compile`:在一種平台上進行 program 的 compile ,而這個 compile 出來的 program 放到另一個平台上執行
我們執行 `riscv64-buildroot-linux-gnu-gcc`就是進行`cross compile`,我們在 x86 的平台進行 program 的 compile ,compile 出來產生 RISCV 的執行檔,放到 RISCV 的平台上面執行
### 3. 在 Guest 端透過 `$ dmesg | grep 9pnet` 命令,我們可發現 `9P2000` 字樣,這和上述 VirtFS 有何關聯?請解釋運作原理並設計實驗
- 當我們在輸入以下指令:
```cpp
[root@localhost ~]# dmesg | grep 9pnet
```
- 會出現以下輸出:
```cpp
[ 0.297743] 9pnet: Installing 9P2000 support
```
### 7. 在 `root-riscv64.cfg` 設定檔中,有 `bios: "bbl64.bin"` 描述,這用意為何?提示:參閱 Booting a RISC-V Linux Kernel
`bbl`(`Berkeley Boot Loader`):`bbl` 會初始化機器狀態,然後將裝置的資訊傳遞給作業系統知道
## 編譯 kilo 編輯器,允許在 RISC-V/Linux 模擬環境中運作
首先,我們要把 `kilo` 加入 buildroot
- `/home/peiwen/buildroot-riscv-2018-10-20/package`在這個路徑底下建一個叫做 `kilo`的目錄
- 在`kilo`的目錄中,建立檔案`Config.in`,`Config.in`的內容如下:
```cpp
config KILO
bool "kilo><"
string "add kilo success~~"
help
https://github.com/rebvivi/kilo
```
這些內容之後會顯示在 `<Help>` 中

- 修改`/home/peiwen/buildroot-riscv-2018-10-20/package`這個路徑底下`Config.in`的內容

增加`source "package/kilo/Config.in"`這個路徑,因為這樣之後在執行`$ make menuconfig`的時候,我們能在`Target packages --->`中的`Text editors and viewers --->`找到我們剛剛設定的`"add kilo success~~"`

- 之後在 `/home/peiwen/buildroot-riscv-2018-10-20/package/kilo` 這個路徑下新增 `kilo.mk` 這個檔案,檔案內容如下:
```cpp
KILO_VERSION = 1.0
KILO_SITE = https://github.com/rebvivi/kilo.git
define KILO_BUILD_CMDS
$(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) all
endef
define KILO_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(@D)/kilo $(TARGET_DIR)/usr/bin
endef
$(eval $(generic-package))
```
- 接著就能在`buildroot-riscv-2018-10-20` 目錄下執行 `$ make`
- 之後到`riscv-emu`目錄底下設定 `$PATH` 環境變數:
```cpp
$ export PATH=`pwd`:$PATH
```
- 切換到 `tmp` 目錄中的 `diskimage-linux-riscv-2018-09-23` 目錄
```cpp
$ cd /tmp
$ cd diskimage-linux-riscv-2018-09-23
```
- 接著就可以啟動模擬器:
```cpp
$ temu test64.cfg
```
- 看到以下訊息之後,輸入 `root` 登入
```cpp
Welcome to Buildroot
localhost login:
```
- 因為剛剛在 `kilo.mk` 設定將 `kilo` 安裝在 `/usr/bin`底下,所以我們打開到 `/usr/bin` 目錄底下的 `kilo` 之後,開始執行 `kilo`,編輯 `hello.c`
```cpp
[root@localhost ~]# cd /usr/bin
[root@localhost bin]# ./kilo hello.c
```

- 這時就可以在`kilo`編輯程式碼,我們將`hello.c`的內容設定如下:
```cpp
#include <stdio.h>
int main() { puts("Hello!"); return 0; }
```
- 按 `Ctrl-S` 存檔,按`Ctrl-Q` 離開
之後我們想要嘗試用 `gcc` compile 這個程式碼,但是此時 buildroot 還沒有 `gcc`
>這裡參考`cjwind` 同學將 compiler 裝進 target 的方法
- 我們到 `buildroot-riscv-2018-10-20` 目錄底下,執行 `$ make menuconfig`
- 找到 `Target packages --->` 中的 ` Development tools --->`,選取 `gcc_target2`、`binutils`、`binutils binaries`
- 找到 `Target packages --->` 中的 `Libraries --->` 中的 `Other --->`,選取 `gmp`、`mpc`

- 接著就能在 `buildroot-riscv-2018-10-20` 目錄下執行 `$ make`
- 重新啟動模擬器
```cpp
$ temu test64.cfg
```
- 當我輸入 `[root@localhost bin]# gcc -o hello hello.c` 嘗試 compile `hello.c` 時,出現以下錯誤訊息:
```cpp
/usr/libexec/gcc/riscv64-buildroot-linux-gnu/7.3.0/cc1: error while loading shared libraries: libmpc.so.3: cannot open shared object file: No such file or directory
```
- 所以我們需要修改環境變數 `LD_LIBRARY_PATH`,增加新的路徑
```cpp
[root@localhost bin]# LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH gcc -o hello hello.c
```
- 之後執行 `hello`
```cpp
[root@localhost bin]# LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH ./hello
```
就會產生輸出結果:
```cpp
Hello!
```
## 調整 buildroot 設定,讓原本輸出的 ext2 image 佔用約 14 MB 空間,變得更精簡
- 用`$ make graph-size`的指令畫出 filesystem size 的配置圖
>以下是還沒有縮減空間的圖
>

- 試著移除 `Busybox` 裡頭的 `vi`:
- 輸入`$ make busybox-source`
- 接著輸入`$ make busybox-menuconfig`
- 找到 `Editors --->`,點進去就可以找到 `vi`,取消勾選它

- 但 total filesystem size 卻反而增加了QQ
