# 2019q1 Homework4 (riscv) contributed by < `jeffcarl67` > ## 環境 * Linux 4.15.0-45-generic #48~16.04.1-Ubuntu SMP * gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609 * 相關程式碼引用自 [linux v5.0](https://elixir.bootlin.com/linux/v5.0/source) ## 作業要求 * 回答上述「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼,以第一手材料 (包含自己設計的實驗) 為佳 * 調整 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) ## 自我檢查清單 * [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) 一文後,對照原始程式碼,你發現什麼? Virtualization 大致可分為 Full virtualization 與 paravirtualization 兩種, 在 Full virtualization 中 guest os 並不知道自己執行於 hosted hypervisor 中而認為自身執行於實際硬體上, 在此種模式下 guest os 並不需要經過任何修改即可執行於 hosted hypervisor 之上, 當 guest os 對設備進行操作時, kvm 會攔截設備操作相關指令並將相關結果回報給 hosted hypervisor, hosted hypervisor 會模擬相關設備的執行, 由於程式碼的執行路徑較長, 因此 Full virtualization 通常有較差的 IO 效能 在 paravirtualization 中 guest os 知曉自身執行於 hypervisor 中, 因此可以藉由 `virtio` 的機制使得 guest os 直接與 hosted hypervisor 溝通, 有較短的程式執行路徑, 因此通常有較好的 IO 效能 * 透過 `$ temu root-riscv64.cfg`, 我們在 RISCV/Linux 模擬環境中,可執行 `gcc` 並輸出對應的執行檔,而之後我們則執行 `riscv64-buildroot-linux-gnu-gcc`,這兩者有何不同? (提示: cross-compiler, 複習 [你所不知道的 C 語言: 編譯器和最佳化原理篇](https://hackmd.io/s/Hy72937Me) 使用以下指令觀察模擬環境與實際電腦中的 gcc 相關資訊後 ```shell= $ temu https://bellard.org/jslinux/buildroot-riscv64.cfg $ gcc -v ``` ```shell= $ output/host/usr/bin/riscv64-buildroot-linux-gnu-gcc -v ``` 會發現皆有 `Target: riscv64-buildroot-linux-gnu` , 亦即此二 gcc 皆會產生以 riscv 指令集架構為目標的機器碼, 但是再詳細觀察模擬環境中的 gcc 相關資訊, 會發現有以下配置 ```shell= Configured with: ./configure --target=riscv64-buildroot-linux-gnu --host=riscv64-buildroot-linux-gnu --build=x86_64-pc-linux-gnu ... ``` 意思是在模擬環境下的 gcc 在 x86_64 還境下構建, 執行於 riscv64 環境中, 並產生 riscv64 機器碼, 而在實際機器上編譯的 `riscv64-buildroot-linux-gnu-gcc` 則構建於 x86_64 環境也執行於 x86_64 環境並輸出 riscv64 機器碼, 因此這兩支 gcc 最大的不同在於執行環境, 模擬環境中的 gcc 只能執行於 riscv64 架構機器中, 而實際電腦上的 gcc 執行於 x86_64 架構機器中 * 在 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 依據 `netinet.sh` 中的註解, 先用 `ifconfig` 命令查看連接到網路的 network interface, 在我的電腦上為 `wlp4s0`, 將 `netinet,sh` 中 `internet_ifname="enp0s20f0u1"`換為`internet_ifname="wlp4s0"`, 之後執行 `netinet.sh` 在 `test64.cfg` 中, 將 `eth0: { driver: "user" }` 換為 `eth0: { driver: "tap", ifname: "tap0" }` 後啟動 `temu`, `temu` 輸出錯誤訊息 `Error: could not configure /dev/net/tun` , 以 `sudo` 命令提升權限解決, 最後依照說明輸入以下指令 ```shell= $ ifconfig eth0 192.168.3.2 $ route add -net 0.0.0.0 gw 192.168.3.1 eth0 ``` 即可成功 `ping 8.8.8.8` * 最初實驗輸入 `$ 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) `bbl` 為 UC, Berkeley 開發的 `Berkeley Boot Loader` , 提供類似於其他架構下 bios 或 uefi 的功能, 在機器啟動的早期階段初始化各設備並將探測到的系統信息傳遞給 linux, 在 `bbl` 執行完後即會開始執行 linux * 能否用 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)) ## 編譯 kilo 根據作業說明以 `buildroot` 構建相關編譯工具鏈, 取得 `kilo` 程式碼後執行以下命令 ```shell= $ CC=/tmp/buildroot-riscv-2018-10-20/output/host/usr/bin/riscv64-buildroot-linux-gnu-gcc make ``` cross-compiler 即會生成能執行於 riscv64 環境下的 `kilo` 執行檔 接著在模擬環境下執行 `kilo` ```shell= $ temu test64.cfg $ mount -t 9p /dev/root /mnt $ cd /mnt/kilo $ ./kilo kilo.c ``` 出現以下畫面, 成功執行 `kilo` ![](https://i.imgur.com/hiGWXxC.png) 以此種方式編譯的 `kilo` 使用 dynamic linking, 因此只能執行於支持 `glibc` 的環境 ## 將 kilo 加入 buildroot 依據 [buildroot 17.2. Config files](https://buildroot.org/downloads/manual/manual.html#adding-packages) 在 package 目錄下新建目錄命名為 `kilo`, 在其中新增兩檔案 * `Config.in` `Config.in` 用於構建時配置 `buildroot` ``` config BR2_PACKAGE_KILO bool "kilo" help kilo ``` * `kilo.mk` `kilo.mk` 指示如何編譯及安裝 `kilo` ``` KILO_VERSION = af3919d68cb2e70a3d9a2309596cf290cf6bc1ac KILO_SITE = $(call github,sysprog21,kilo,$(KILO_VERSION)) KILO_SITE_METHOD = git define KILO_BUILD_CMDS $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \ CC="$(TARGET_CC)" \ CFLAGS+="$(TARGET_CFLAGS) " endef define KILO_INSTALL_TARGET_CMDS $(INSTALL) -m 0755 -D $(@D)/kilo $(TARGET_DIR)/usr/bin/kilo endef $(eval $(generic-package)) ``` 此檔案指示 `buildroot` 從 `https://github.com/sysprog21/kilo.git` 取得 `commit af3919d68cb2e70a3d9a2309596cf290cf6bc1ac` 版本的檔案, 編譯後安裝到 `/usr/bin` 最後編輯 `package/Config.in` 在 `menu "Text editors and viewers"` 條目下加入 `source "package/kilo/Config.in"` , 執行 `make menuconfig` 並選擇 ` Target packages` -> `Text editors and viewers` 即可見: ![](https://i.imgur.com/Bpf78Zl.png)