Tinker-V

先按照官方說明將 bootloader, Kernel image 燒錄到板子上,裡面有用到 Tera term 傳送 Flash Writer 到板子上的操作,我不知道如何在 Linux 上面做,所以都是在 Windows 上面用好,確定透過 UART 看 log、下命令,才到 Ubuntu 上面改用 screen 連線到板子上

注意事項:

  1. Tinker-V 用到的 glibc 版本為 2.28,如果用新版的 toolchain 編譯出來的 box64 會找不到新版 glibc,執行起來就像下面這樣
root@rzfive-tinker-v:~/tests# ./box64_v2 ./x86/puzzle 
./box64_v2: /lib/libc.so.6: version `GLIBC_2.29' not found (required by ./box64_v2)
./box64_v2: /lib/libc.so.6: version `GLIBC_2.33' not found (required by ./box64_v2)
./box64_v2: /lib/libc.so.6: version `GLIBC_2.38' not found (required by ./box64_v2)
./box64_v2: /lib/libc.so.6: version `GLIBC_2.32' not found (required by ./box64_v2)
./box64_v2: /lib/libc.so.6: version `GLIBC_2.34' not found (required by ./box64_v2)
  1. 當 Tinker-V 接好 UART 到電腦上,可以找 /dev 底下有沒有 /dev/ttyUSB{id},確定裝置後用 screen 連線到 Tinker-V 上面,鮑率要設 115200
$ ls /dev | grep USB
/dev/ttyUSB0
$ sudo screen /dev/ttyUSB1 115200

這時候應該能夠看 rzfive-tinker-v login:,輸入 root 即可登入

  1. screen 沒辦法捲動終端機,或許可以試試看 PuTTY

    有時候 UART 會遇到下面這些問題,遇到的話就把 RS232 拔掉重連,或是拔電源重開

    • 打字 lag。偶爾會延遲 1~2 秒才出現之前打的字
    • 打字會出現前一次的字。例如輸入 abc,當輸入到 b 才會出現 a,輸入到 c 才出現 b,最後印出的是 ab
    • 亂碼? 有時候會無法用 backspace 刪除字元,所以會一直出現 log 把螢幕填滿,讓畫面很混亂,甚至打的字都不會印出
  2. GPIO 佔用 kernel
    使用 patching 詳見 : issue

  3. glib2.0 來源遺失
    修改 recipe ,詳見:issue

  4. 修改 linux_libc_headers_5.10.bb 缺少 checksum
    linux_libc_headers_5.10.bb 中添加 :

SRC_URI[sha256sum] = "c1d276741f8387da5aab790954e7486354feb7de2c830f123ea830a1723bf604" 
  1. glibc-2.28-r0 do_fetch: Fetcher failure
    要將 meta-renesas/meta-rz-common/recipes-debian/buster/source 內的 glibc.inc 改為:
# This is generated by debian-source.bbclass
DPV = "2.28-10+deb10u4"
DPV_EPOCH = ""
REPACK_PV = "2.28"
PV = "2.28"

DEBIAN_SRC_URI = " \
    ${DEBIAN_SECURITY_UPDATE_MIRROR}/main/g/glibc/glibc_2.28-10+deb10u4.dsc;name=glibc_2.28-10+deb10u4.dsc \
    ${DEBIAN_SECURITY_UPDATE_MIRROR}/main/g/glibc/glibc_2.28.orig.tar.xz;name=glibc_2.28.orig.tar.xz \
    ${DEBIAN_SECURITY_UPDATE_MIRROR}/main/g/glibc/glibc_2.28-10+deb10u4.debian.tar.xz;name=glibc_2.28-10+deb10u4.debian.tar.xz \
"

SRC_URI[glibc_2.28-10+deb10u4.dsc.md5sum] = "b39dd7f7f54c6b7da3eefcd391470af2"
SRC_URI[glibc_2.28.orig.tar.xz.md5sum] = "2d78d5b080fbe4fefa2e1ccef9c39dbc"
SRC_URI[glibc_2.28-10+deb10u4.debian.tar.xz.md5sum] = "8ebb6ee80ba6a5e39eacc31c95994ef0"

SRC_URI[glibc_2.28-10+deb10u4.dsc.sha256sum] = "55e4ebd9a55755c84d42709b23f9b269f46b6a76f5040a0e05cfd323ba67f8af"
SRC_URI[glibc_2.28.orig.tar.xz.sha256sum] = "53d3c1c7bff0fb25d4c7874bf13435dc44a71fd7dd5ffc9bfdcb513cdfc36854"
SRC_URI[glibc_2.28-10+deb10u4.debian.tar.xz.sha256sum] = "dc287870d4b8cb5d1d175fa9b95e3a97d6b68699680b443ae7b2a1b89f0fe8fc"

Box64

安裝 Toolchain

本部分資訊已過時

要用舊版 RISC-V 編譯器來編譯 box64 才能夠在板子上面執行,需下載 glibc 版本的 toolchain 來用,解壓縮到 /opt 並更新好 PATH 變數後,應能夠在終端機使用到 toolchain 提供的執行檔

$ riscv64-unknown-linux-gnu-gcc --version
riscv64-unknown-linux-gnu-gcc (GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

更新 library

如果要用 QEMU 來執行 box64 的程式,會遇到某些 library 找不到(e.g. ld-linux-riscv64-lp64d.so.1),可以將 {toolchain_path}/sysroot/lib 更新到 LD_LIBRARY_PATH

Note: 新版的 toolchain 在 bin/ 目錄底下會有 QEMU 可以使用

export LD_LIBRARY_PATH="/opt/riscv64-unknown-linux-gnu_1320/lib:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH="/opt/riscv64-unknown-linux-gnu_1320/sysroot/lib:$LD_LIBRARY_PATH"

更新 man page(非必要)

如果要用 man 來閱讀 toolchain 的文件,可以將 toolchain 提供的 man 目錄複製到 PC 的 /usr/local/share/man/ 底下

> sudo cp -r /opt/riscv64-unknown-linux-gnu_1320/share/man/man5 /usr/local/share/man/man5

複製好後就可以使用 man 來閱讀 toolchain 的文件了

> man riscv64-unknown-linux-gnu-gcc

編譯 box64

使用最新的 GNU Toolchain for RISC-V,選 riscv64-glibc-ubuntu-20.04-gcc-nightly

安裝 ninja 以加快編譯:

$ sudo apt-get install ninja-build

編譯 box64 可以設定幾個選項來編譯,詳細可以看官方文件,這裡我主要針對 RISC-V 配置編譯選項,最後 make 時,用 bear 輸出 compile_commands.jsonclangd 來分析程式碼,以便用來 trace code

$ mkdir -m 777 build
$ cd build
$ cmake \
    -G Ninja \
    -D RV64=1 -D RV64_DYNAREC=ON \
    -D CMAKE_BUILD_TYPE=RelWithDebInfo \
    -D CMAKE_C_COMPILER=riscv64-unknown-linux-gnu-gcc \
    ..
$ ninja

預期可見到 box64 執行檔,注意在連結階段可能會等待較長時間,之後可改用 mold。

由於 box64 使用新版 toolchain 內附的 glibc,因此會遇到跟原本 rootfs 裡頭 glibc 符號不匹配的狀況,因此可利用 patchelf 工具,修改動態連結器和函式庫的位置。首先安裝 patchelf:

$ git clone https://github.com/NixOS/patchelf
$ cd patchelf
$ ./bootstrap.sh
$ ./configure --prefix=/usr
$ make
$ sudo make install

利用 patchelf 修改上面產生的 box64 執行檔: (假設切換到 box64/build 目錄)

$ patchelf --set-rpath /lib:/lib/custom box64
$ patchelf --set-interpreter /lib/custom/ld-linux-riscv64-lp64d.so.1 box64

先準備 /lib/custom 目錄和從 GNU Toolchain 複製必要檔案: (__TOOLCHAIN__ 是從 GNU Toolchain for RISC-V 取得並解開的目錄)

$ mkdir -p sysroot/lib/custom
$ cp __TOOLCHAIN__/sysroot/lib/*.so* sysroot/lib/custom

接著利用 QEMU 測試:

$ qemu-riscv64 -L sysroot box64 --help

預期可見到:

Dynarec for RISC-V With extension: I M A F D C Zba Zbb Zbc Zbs Vector (vlen: 128)
...
This is Box64, The Linux x86_64 emulator with a twist
...

接著可用 box64 執行預先編譯好的 x86-64 執行檔,例如 /bin/busybox

將上述 sysroot/lib/custom 目錄的內容,放到 Tinker V 的檔案系統中,確保 /lib/custom 目錄包含 ld-linux-riscv64-lp64d.so.1libc.so.6 在內的動態連結函式庫檔案。

為了在 x86-64 主機測試更多程式,進行以下準備: (記得修改 box64/build 的路徑)

$ sudo su -
$ cd /lib
$ ln -s box64/build/sysroot/lib/custom .
$ exit

執行 xlogo 程式:

$ qemu-riscv64 -L sysroot box64 /usr/bin/xlogo

預期可見 X 字樣的視窗。

使用 mold 來縮減連結時間

Rui Ueyama 是一位高產的開放原始碼先鋒,他常帶給技術社群各式驚喜,代表作品像是 8cc (短小精悍且可 self-hosting) 和 lld (LLVM linker,比 GNU gold 或 Microsoft Visual Studio linker 快)。他從 2020 年 9 月開始發展名為 mold 的新連結器 (linker),並於 2022 年初發布 mold 連結器的 1.0 版,以更快的演算法和高效的內部結構,獲得比 LLVM 的 lld 更快的表現,引起許多開發者的重視,GCC 12 也納入相關的支援。

通常構建時間 (build time) 由編譯時間和連結時間組成。與編譯時間相比,後者可藉由 Ninja 和 ccache 等工具加速,連結時間主要依賴於連結器本身的效率。因此,連結器的速度至關重要,mold 無疑是目前開放原始碼實作效率最高,且有足夠好相容性的專案,例如 LLVM 子專案 lld 儘管也強調平行化的連結,但不支援 GCC LTO,而 GNU gold 一開始要跳脫 GNU ld 的效能限制,但現在 gold 發展停滯。

安裝 mold:

$ curl -L -o mold.tar.gz https://github.com/rui314/mold/releases/download/v2.34.1/mold-2.34.1-x86_64-linux.tar.gz
$ sudo tar -xz --strip-components=1 -C /usr/ -f mold.tar.gz

在上述的 cmake 階段,要增加 -D WITH_MOLD=1 選項,重新執行 cmake 後,執行以下:

$ mold -run ninja

在 Intel Xeon CPU E5-2650 v4 @ 2.20GHz 機器上,原本要花 7 分鐘建構 box64,利用 mold 則縮減到 4 分鐘

clangd

可觀看撿到槍的系統軟體工程師 GDB&VScode ,大致了解 clangd 的配置和用途。

建議在 VSCode 的 user setting 加入關閉自動插入 header 的設定,有時候會引入到不對的 header

"clangd.arguments": [
    "--header-insertion=never",
],

clangd 還有哪些 arguments 可以修改?請在終端機輸入 clangd --help

.clangd 示範

.clangd 為 clangd 的設定檔,一般來說會放在 project 的根目錄,細節可以看文件,這裡示範如何在不同環境下引入 header files。例如使用不同的 ARM 或是 RISC-V toolchain 會有自己的 C library,如果在 x86 的 PC 上面練習 RISC-V 的 C code,對程式碼的 memset 跳躍到定義,會跳到 usr/include/string.h,顯然這不是真正被宣告的路徑。

上述例子看似沒什麼大不了,toolchain 都應該支援標準的 C headers,那麽如果要在 PC 上練習 ARM 的 neon 指令,VSCode 就會找不到 arm_neon.h,還是可以 ARM 的 toolchain 編譯,但就是不方便 trace code,下面那 RISC-V 和 Kernel module 為例。

如果要用 RISC-V 的 toolchain 來開發,可以在 .clangd 的 compiler flags 加上 headers 的路徑,改完後重新啟動 clangd,找一個 standard C 函式 goto defination,例如 printf 就會跳到 toolchain 的 stdio.h,而不是 /usr/include/stdio.h

CompileFlags:
  Add: [-I/opt/riscv64-unknown-linux-gnu_1320/sysroot/usr/include]
  Remove: -W*
  Compiler: riscv64-unknown-linux-gnu-gcc

反之如果要開發 Kernel module 就可以把 -I 改成 usr/src/ 底下的 Linux/include,可用 echo 搭配 uname -r 查看 Kernel version

$ echo /usr/src/linux-headers-`uname -r`/include
/usr/src/linux-headers-6.8.0-40-generic/include

把 include 的路徑貼到 .clangd 即可

CompileFlags:
  Add: [-I/usr/src/linux-headers-6.8.0-40-generic/include]
  Remove: -W*
  Compiler: gcc

這樣就可以跳躍到 Kernel 的 headers,寫程式也會跳出 Kernel 函式的提示字

例如下面兩個是 Kernel module 會用到的 header,一般情況下,VSCode 找不到路徑,按上述修改完路徑,就能跳躍過去

#include <linux/printk.h>
#include <linux/slab.h>

如此一來,就算換成開發板提供的 toolchain,只要加入 headers 的路徑到 .clangd 就能夠讓 VSCode 跳躍到實際宣告的 header 或函式的實作。

編譯 x86 程式

-static 非必要,且為了讓 box64 能善用 wrapper,應儘量採用動態連結 (即預設編譯組態)。

編譯 x86 的程式要加上 -static,因為環境從 PC 換到 Tinker-V,板子不一定會有 PC 的函式庫,避免板子找不到函式庫而無法執行。以 rv32emu 的 puzzle.c 為例

> gcc -static puzzle.c -o puzzle_x86

編譯完之後可以存到 SD 卡或是 USB,

  • SD 卡

可以在板子上用 fdsik -l 來看是 SD 卡的裝置是那一個

掛載 SD card 到 /media

mount -t ext4 /dev/mmcblk1p2 /media
mount -t ext4 /dev/sda /media

卸載

umount /media
  • USB(OTG)

如果程式存到 USB 可以透過 micro USB(OTG) 接到板子上再掛載到指定目錄,載入方式和 SD 卡一樣

Note

或許也可以透過某種方式用 wget 來下載程式到板子上就不用每次都要插拔儲存裝置?之前有用過網路線接到板子後可以使用 wget 下載檔案

遠端連線

WinSCP

  1. 下載並安裝 WinSCP
  2. 使用網路線連接板子與 PC
  3. ifconfig 獲得 inet addr
  4. 用inet addr登入

接著就可以傳輸、刪除檔案,如下圖所示:
image

也可以使用 ssh 來操控 tinker-v 的終端機

$ ssh root@{inet addr}

scp

Linux PC 可以先將自己的 public key 加入到 Tinker-V 的 ~/.ssh/authorized_keys,往後複製檔案和連線都不必輸入密碼

Tinker-V 和 PC 都要 SSH key-gen,如果已經 keygen 過的話可以省略

$ ssh-keygen -t ed25519 -b 4096 -C "your_email@example.com"

將 PC 的公鑰內容複製好

$ cat ~/.ssh/OOO.pub

將 PC 端的 SSH 公鑰內容貼入到 Tinker-V 的 authorized_keys

$ vim ~/.ssh/authorized_keys

如果你有多台電腦或板子要連線到 Tinker-V的話,則要將每個公鑰內容一行一行的貼上

vsode 沒辦法用 SSH 連線到 Tinker-V,會出現下面的錯誤訊息,但還是可以在 vscode 的終端機用 ssh 命令來連線,這樣就不必透過 screen 了

Resolver error: Error: The remote host's architecture is not supported

Tinker-V 傳資料到 PC

先把 Tinker-V 的公鑰內容複製到 PC 端的 ~/.ssh/authorized_keys 再開啟 PC 的 ssh server 的功能

$ sudo apt-get install openssh-server
$ sudo service ssh start

在 Tinker-V 上面一樣使用 scp 傳檔案到 PC 端,PC 的 IP 地址一樣用 ifconfig 查詢,user 就是 whoami 命令輸出的名稱

$ scp ./msg.txt lambert-wu@{PC inet addr}:~/Documents

Tinker-V 傳完檔案後可以在 PC 的 ~/Documents 目錄確認有沒有 msg.txt

如果開啟 SSH server 不放心的話,可以用下面的命令關掉 ssh service

$ sudo service ssh stop
Stopping 'ssh.service', but its triggering units are still active:
ssh.socket
$ sudo service disable --now ssh.socket
$ systemctl -a | grep ssh

systemctl -a | grep ssh 用來確認 ssh 相關的服務是不是 inactive dead 的狀態

執行 box64

當 box64 和 x86 程式都在 Tinker-V 上面,就可以測試了

>./box64 ./puzzle_x86

目前使用幾個 rv32emu 的 test cases 在 box64 上面執行,大部分沒遇到 crash 問題,但是有些程式會執行很久。

executable 空白欄位表示程式可以執行,但執行時間要很久,沒有完全執行完,先終止掉

program executable Note
aes Y
donut Y
hamilton 執行太久,用 time 來測量,到 mapping edges 終止,已達 181m
lena Y
mandelbrot Y
nqueens Y 大約 size = 12 後會變慢
pi Y
qrcode Y
rvsim Y
stream
captcha Y
fcalc Y
jit N segement fault
line Y
mt19937 Y
nyancat Y 中止程式後,畫面會看不到游標,使用 clear 依舊如此,但還是可以打字下命令
puzzle Y
richards
spirograph Y 會影響到終端機顯示,同 nyancat
test-path Y

推薦先讀 box64 官方的 blog 找幾篇相關的文章來了解 box64 的運作,像是 Inner workings

Benchmarking

利用 /usr/bin/time -p 測量 RV64 和 box64 對應的執行時間: (user time)

Benchmark Name RV64 x86-64
aes 3.41 482.13
dhrystone 1.05 353.21
miniz 9.74 1612.67
norx 2.47 214.66
primes 16.01 678.36
qsort 3.52 795.05
sha512 2.01 395.96

使用動態連結之後的數據: (user time)

註: 會遇到 sh: relocation error: /lib/custom/libc.so.6: symbol __nptl_set_robust_list_avail version GLIBC_PRIVATE not defined in file ld-linux-riscv64-lp64d.so.1 with link time reference ,但輸出正確,原因待查明

使用動態編譯後:

Benchmark Name RV64 x86-64
aes 4.06 8.37
dhystone 1.17 33.86
miniz 11.79 181.11
norx 2.8 4.51
primes 18.54 24.82
sha512 2.25 3.97
NBench 206.93 211.38
Coremark 16.10 22.60
whetstonez 4.16 51.87

更新後的數據

Dynarec for RISC-V With extension: I M A F D C PageSize:4096 Running on unknown riscv64 cpu with 1 Cores
Will use Hardware counter measured at 12.0 MHz emulating 3.0 GHz
Params database has 14 entries
Box64 with Dynarec v0.3.1 126cf279 built on Nov  1 2024 18:08:42

Benchmark Name RV64 x86-64
aes 3.01 6.32
dhrystone 1.17 26.39
miniz 9.43 145.21
norx 2.23 3.38
primes 15.84 21.03
qsort 2.97 124.39
sha512 2.00 3.20
NBench 205.80 209.08
Coremark 9.79 9.67
whetstonez 3.38 47.42

Use case

在 Tinker V 上面執行 proprietary x86-64 (closed source) 的程式:
在完全不用 recompile 程式的前提 (事實上,也沒辦法做到,因為沒辦法取得原始程式碼),利用 box64
Tinker V (RV64) 在模擬 x86-64 程式,並利用 X11 forwarding,將視窗操作導向到具備
顯示及輸入的裝置,過程中,proprietary x86-64 的程式沒有任何洩漏的風險,且運行效率尚可,亦即
將 Tinker V 視為 Hardware Security Modules (HSM),換言之,在 Tinker V 上面可執行密碼學
或者涉及特定領域專業之演算法,在最小資訊揭露的前提,進行裝置之間的互動。

TODO: 撰寫 X11 forwarding 原理
TODO: 製作 Hardware Security Modules (HSM) 介紹圖文