# 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 (含) 之前