在 RISC-V 初探 一文中我們講到了如何使用 riscv-poky 去產生你的 RISC-V 系統,那如 果我們不想要透過 Yocto 去建立我們的 RISC-V Linux 系統,這樣子要怎樣建立迷你的系 統呢? 答案就是 Busybox 。
Busybox 是一個非常有趣的程式,舉凡我們在 Linux 下最常用的命令如 ls、cd 等到 sed、 vi 他都具有相對應的簡單實現,此外,這些命令實際上都只是一個軟連結 (symlink) 連結 到名為 busybox 的執行檔,也就是說,如果我們將 busybox 進行靜態編譯 (static link), 則製作出來的系統整體大小大約為 2 MB (kernel) + 1.4 MB (busybox),而這個系統卻又 可以具有許多 UN*X 下的常用命令,也因此 busybox 很常用於空間有限的系統。
本文將講述如何編譯 RISC-V 的 Linux toolchain、Linux Kernel 以及 Busybox 來產生一 個迷你的 Linux 系統。
編譯 RISC-V Linux Toolchain
在 RISC-V 初探 一文中我們說到了如何編譯 riscv-tools
套件去產生開發用的
toolchain 與模擬器,在當時所產生的開發環境中其實不包含 RISC-V Linux 用的
toolchain,所以我們需要自己動手去編譯他。
首先進入 riscv-gnu-toolchain
這個目錄,我們需要額外編譯的 toolchain 就在這裡
coldnew@Rosia ~/riscv-tools $ cd riscv-gnu-toolchain
接著執行 configure
,其中 RISCV
環境變數為你想要安裝此 toolchain 的路徑
coldnew@Rosia ~/riscv-tools/riscv-gnu-toolchain $ ./configure --prefix=$RISCV
接下來就是編譯與慢長的等待
coldnew@Rosia ~/riscv-tools/riscv-gnu-toolchain $ make linux
當編譯完成後,你會發現在 $RISCV/bin
下面是不是多了 riscv64-unknown-linux-gnu-*
這一類程式
coldnew@sherry ~/riscv-tools/riscv-gnu-toolchain $ ls $RISCV/bin/
elf2hex riscv64-unknown-elf-ld.bfd riscv64-unknown-linux-gnu-gcc-ranlib
fesvr-eth riscv64-unknown-elf-nm riscv64-unknown-linux-gnu-gcov
fesvr-rs232 riscv64-unknown-elf-objcopy riscv64-unknown-linux-gnu-gcov-tool
fesvr-zedboard riscv64-unknown-elf-objdump riscv64-unknown-linux-gnu-gfortran
riscv64-unknown-elf-addr2line riscv64-unknown-elf-ranlib riscv64-unknown-linux-gnu-gprof
riscv64-unknown-elf-ar riscv64-unknown-elf-readelf riscv64-unknown-linux-gnu-ld
riscv64-unknown-elf-as riscv64-unknown-elf-size riscv64-unknown-linux-gnu-ld.bfd
riscv64-unknown-elf-c++ riscv64-unknown-elf-strings riscv64-unknown-linux-gnu-nm
riscv64-unknown-elf-c++filt riscv64-unknown-elf-strip riscv64-unknown-linux-gnu-objcopy
riscv64-unknown-elf-cpp riscv64-unknown-linux-gnu-addr2line riscv64-unknown-linux-gnu-objdump
riscv64-unknown-elf-elfedit riscv64-unknown-linux-gnu-ar riscv64-unknown-linux-gnu-ranlib
riscv64-unknown-elf-g++ riscv64-unknown-linux-gnu-as riscv64-unknown-linux-gnu-readelf
riscv64-unknown-elf-gcc riscv64-unknown-linux-gnu-c++ riscv64-unknown-linux-gnu-size
riscv64-unknown-elf-gcc-5.2.0 riscv64-unknown-linux-gnu-c++filt riscv64-unknown-linux-gnu-strings
riscv64-unknown-elf-gcc-ar riscv64-unknown-linux-gnu-cpp riscv64-unknown-linux-gnu-strip
riscv64-unknown-elf-gcc-nm riscv64-unknown-linux-gnu-elfedit spike
riscv64-unknown-elf-gcc-ranlib riscv64-unknown-linux-gnu-g++ spike-dasm
riscv64-unknown-elf-gcov riscv64-unknown-linux-gnu-gcc termios-xspike
riscv64-unknown-elf-gcov-tool riscv64-unknown-linux-gnu-gcc-5.2.0 xspike
riscv64-unknown-elf-gprof riscv64-unknown-linux-gnu-gcc-ar
riscv64-unknown-elf-ld riscv64-unknown-linux-gnu-gcc-nm
到此,我們就擁有了 RISC-V 的 Linux toolchain 了
編譯 RISC-V Linux Kernel
RISC-V 官方提供了基於 Linux 3.14 (LTS) 版本的移植,和 RISC-V 相關的移植位於 riscv/riscv-linux 裡面,由於 GitHub 上面只包含的 RISC-V 的移植,因此我們還需要下 載 Linux 3.14.x 的原始碼才行,使用以下命令下載 Linux 3.14.41 並將 RISC-V 的移植 加入進去:
curl -L https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.14.41.tar.xz | tar -xJ cd linux-3.14.41 git init git remote add origin https://github.com/riscv/riscv-linux.git git fetch git checkout -f -t origin/master
程式碼取得後,別忘記將 $RISCV/bin
加入到你的環境變數
coldnew@Rosia ~/linux-3.14.41 $ export PATH=$RISCV/bin:$PATH
接著我們就可以使用預設的設定去編譯 Linux Kernel
coldnew@Rosia ~/linux-3.14.41 $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make defconfig
如果你喜歡客製化,也可以使用 menuconfig 去加/減你的 Linux Kernel 設定
coldnew@Rosia ~/linux-3.14.41 $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make menuconfig
設定都完成後,就是編譯 Linux Kernel 的時候了,這邊我們只需要編譯 vmlinux 就好
coldnew@Rosia ~/linux-3.14.41 $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make vmlinux
編譯完成後,你會在當前目錄下看到 vmlinux
這個檔案
coldnew@Rosia ~/linux-3.14.41 $ file vmlinux vmlinux: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, BuildID[sha1]=3fa9623740f49023785338f0855ccf024f416ab5, not stripped
我們可以使用 spike
去嘗試啟動這個 kernel 看看
coldnew@Rosia ~/linux-3.14.41 $ spike bbl vmlinux ... [ 0.000000] Linux version 3.14.41-ga2f247d (coldnew@sherry) (gcc version 5.2.0 (GCC) ) #1 Tue Sep 15 16:13:41 2015 [ 0.000000] Detected 0x7fc00000 bytes of physical memory [ 0.000000] Initial ramdisk at: 0xffffffff80011a28 (134 bytes) [ 0.000000] Zone ranges: [ 0.000000] Normal [mem 0x00200000-0x7fdfffff] [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x00200000-0x7fdfffff] [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 516110 [ 0.000000] Kernel command line: root=/dev/htifblk0 ... [ 0.150000] CPU: 0 PID: 1 Comm: swapper Not tainted 3.14.41-ga2f247d #1 [ 0.150000] Call Trace: [ 0.150000] [<ffffffff80013f54>] walk_stackframe+0x0/0xc8 [ 0.150000] [<ffffffff801bf70c>] panic+0xb4/0x1c4 [ 0.150000] [<ffffffff80000f64>] mount_block_root+0x270/0x2f8 [ 0.150000] [<ffffffff80001190>] prepare_namespace+0x134/0x180 [ 0.150000] [<ffffffff80000b24>] kernel_init_freeable+0x1a0/0x1d8 [ 0.150000] [<ffffffff801bf120>] rest_init+0x80/0x84 [ 0.150000] [<ffffffff801bf134>] kernel_init+0x10/0xf4 [ 0.150000] [<ffffffff801bf120>] rest_init+0x80/0x84 [ 0.150000] [<ffffffff80012bb8>] ret_from_syscall+0x10/0x14
當然,這個時候我們並未提供 Linux Kernel 可以進入的系統,因此會出現 kernel panic
的訊息。
使用 Hello, World 建立最簡易系統
為了避免開機的時候會出現 kernel panic
的訊息,要稍微了解一下 Linux 的開機流程。
當 kernel 載入完成後會去嘗試掛載 rootfs 並執行 /sbin/init
這一隻程式,這個程式
也就是整個系統的第一隻被執行的 user space 程式 (PID: 1)。(這隻程式也可以透過修
改開機參數 init=/init
來設定其他的位置)
也就是說,在預設的狀況下,我們可以建立一個簡單的程式並將其置放在 rootfs 的
/sbin/init
,這樣開機的時候就會被 Linux Kernel 載入,我們可以使用人見人愛的
Hello, World! 來測試看看,首先建立 hello.c,並將以下內容填入:
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello, RISC-V\n"); while(1); return 0; }
將其編譯為 hello
這隻執行檔,並且為靜態編譯 (static linked)
coldnew@Rosia ~/linux-3.14.41 $ riscv64-unknown-linux-gnu-gcc hello.c -o hello -static
我們向系統索取 10MB 的空間來建立我們的 rootfs 文件
coldnew@Rosia ~/linux-3.14.41 $ dd if=/dev/zero of=root.ext2 bs=1M count=10
將其格式化為 ext2
coldnew@Rosia ~/linux-3.14.41 $ sudo mkfs.ext2 -F root.ext2
我們建立一個用來臨時掛載 root.ext2 用的資料夾,並將剛剛編譯出來的 hello 執行檔複
製為 /sbin/init
,於是 rootfs 就完成了
mkdir -p /tmp/root sudo mount root.ext2 /tmp/root mkdir -p /tmp/root/sbin cp hello /tmp/root/sbin/init sudo umount /tmp/root
完成 rootfs 後,我們可以用 spike
去執行看看,就會發現到 Kernel 執行到了我們的
Hello, World 程式
coldnew@Rosia ~/linux-3.14.41 $ spike +disk=root.ext2 bbl vmlinux ... [ 0.150000] console [htifcon0] enabled [ 0.150000] htifblk htif2: detected disk [ 0.150000] htifblk htif2: added htifblk0 [ 0.150000] TCP: cubic registered [ 0.150000] VFS: Mounted root (ext2 filesystem) readonly on device 254:0. [ 0.150000] devtmpfs: mounted [ 0.150000] Freeing unused kernel memory: 72K (ffffffff80000000 - ffffffff80012000) Hello, RISC-V
使用 Busybox 來建立我們的迷你系統
理解了如何使用 Hello, World 建立最簡易的 rootfs 後,這次我們來使用 busybox 來建 立我們的 rootfs,首先先從官網下載 busybox 程式碼
coldnew@Rosia ~ $ git clone git://git.busybox.net/busybox
切換到穩定版本
coldnew@Rosia ~/busybox $ git checkout -b 1_23_stable origin/1_23_stable
進行我們自己的設定
coldnew@Rosia ~/busybox $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make menuconfig
在進行設定時有以下幾點要確實注意,我們要將 busybox 編譯為靜態連結,並且增加
init
功能,主要設定如下:
Busybox Settings ---> Build Options ---> [*] Build BusyBox as a static binary (no shared libs) Init Utilities ---> [*] init Networking Utilities ---> [ ] inetd Shells ---> [*] ash
設定完成後開始進行編譯
coldnew@Rosia ~/busybox $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make
編譯完成後透過 make install
命令,會將編譯出來的 busybox 與軟連結(symlink)產生
在 _install
資料夾內
coldnew@Rosia ~/busybox $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make install
我們將剛剛測試用的 root.ext2
掛載到 /tmp/root
資料夾下,並將 _install
資料夾內的東西全部複製過去
coldnew@Rosia ~/busybox $ rsync -avr _install/* /tmp/root
建立一些缺少的資料夾 (/dev、/sys …etc)
coldnew@Rosia ~/busybox $ cd /tmp/root && mkdir -p proc sys dev etc/init.d
建立 etc/init.d/rcS
作為啟動腳本,並添加以下內容
coldnew@Rosia ~/busybox $ vim /tmp/root/etc/init.d/rcS #!/bin/sh mount -t proc none /proc mount -t sysfs none /sys /sbin/mdev -s
將 etc/init.d/rcS
加入可執行權限
coldnew@Rosia ~/busybox $ chmod +x /tmp/root/etc/init.d/rcS
解除掛載 root.ext2,這樣我們的 rootfs 就完成了
coldnew@Rosia ~/busybox $ sudo umount /tmp/root
都完成後,我們就可以用 spike
模擬系統並進入 busybox 的 shell 囉~
coldnew@Rosia ~/linux-3.14.41 $ spike +disk=root.ext2 bbl vmlinux ... [ 0.150000] htifcon htif1: detected console [ 0.150000] console [htifcon0] enabled [ 0.150000] htifblk htif2: detected disk [ 0.150000] htifblk htif2: added htifblk0 [ 0.150000] TCP: cubic registered [ 0.150000] VFS: Mounted root (ext2 filesystem) readonly on device 254:0. [ 0.150000] devtmpfs: mounted [ 0.150000] Freeing unused kernel memory: 72K (ffffffff80000000 - ffffffff80012000) mount: mounting none on /sys failed: No such device mdev: /sys/class: No such file or directory can't open /dev/tty4: No such file or directory can't open /dev/tty3: No such file or directory can't open /dev/tty2: No such file or directory Please press Enter to activate this console. / #