Linux, 工作, 生活, 家人

Embedded, RiscV, 隨手札記

KianV Risc-V + Linux 7.1 uLinux

最終目標就是要在 Tiny Tapeout (TT) 開一顆 SoC ,不過在這之前,我也想先做一些實驗,驗證這些想法是否可行。

KianRiscV 是一顆完整的 RISC-V CPU,也在 TT 開過 IC,但這顆對我們來說還是太大了(雖然說太大也只大一點)。為了省錢,而且這麼大顆也沒有必要,所以我們還是要縮小 Size。在 kianRiscV/linux_socs/kianv_harris_mcycle_edition 下,看說明是在 ULX3S 85F(FPGA Lattice ECP5 LFE5U-85F-6BG381C,有 Open Source 的 Tool)下也可以執行的 uLinux RISC-V CPU core,不僅符合我的目標,而且對我超級友善啊。

裡面還有預設的 Linux kernel 6.1.14 和一個 busybox initramfs,不過我就想,要就做全套嘛,不如把 Linux kernel 換到最新的 7.1 試試看?換到 Linux kernel 7.1 後,碰到的

第一個問題是 Image 太大,Bootloader 預設的 memcpy 量不夠大

Booloader memcpy 從 4MB 改到 6MB就可以解決

第二個問題:”broken padding”

用 `dd` 從舊 kernel 複製 initramfs,但新 kernel 會出現問題,必須用 `fakeroot` 重新打包一份新的 cpio image。

第三個問題:使用預設的 initramfs 開機時,無法進入 shell

錯誤訊息如下

  [   40.383751]     TERM=linux
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
init started: BusyBox v1.36.0 (2023-05-16 20:02:22 CEST)
starting pid 18, tty '': '/etc/init.d/rcS'
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
sh: waitpid: Function not implemented
mkdir: can't create directory '/': Function not implemented
mkdir: can't create directory '/': Function not implemented
mount: mounting devpts on /dev/pts failed: No such file or directory
mount: mounting tmpfs on /dev/shm failed: No such file or directory

這個問題是,Linux kernel mainline 的 RISC-V 32-bit 是 “time64-only” 架構:它是在 y2038 之後才進入 kernel 的,所以「刻意」不提供那些使用 32-bit 時間的舊 syscall(wait4、gettimeofday……)。而舊的 initramfs 是舊架構的,因此呼叫就會出錯,這需要換成新的 rootfs 來解決。在我處理的時候,buildroot 已經可以產生這個方案,使用 uClibc-ng 函式庫就可以了。

第四個問題:”out of memory”


[ 47.660665] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 ]---
sh: out of memory
out of memory

本來以為是 bFLT 的 heap/stack 不夠,所以把 stack 從 16KB 改為 1MB——結果沒用。最後發現這個問題和第六個問題是一樣的,本質上是 malloc 的問題。

第五個問題:解完以上的問題之後,還是沒進到 shell,但是也沒有錯誤訊息。

因為大部分都是 AI 解的,所以這邊 AI 也寫了一個程式,不過編譯的時候沒加 `-fPIC`,對 nommu 的 flat binary 來說,程式被載到記憶體的任意位置,所有位址都得是「位置無關」的。non-PIC 的程式用絕對位址(`lui aX, 0xNNN`)去抓字串或全域變數。這件事我知道,但是不常碰到,也很難意會到。光這個 Bug 就解了十輪左右,Claude 還想繼續解,但是我覺得應該要換個思路。

最後是自己去找了 mini-rv32ima,請 AI 分析差異在哪邊,AI 分析的同時還用了 qemu 直接執行程式(哎呀,我怎麼沒想到)。Claude 用了 qemu 後速度就快多了,很快就找到問題是在 -fPIC

第六個問題:malloc 在 nommu 上回 NULL

直接 mmap 一塊記憶體 → 成功;但 malloc 任何大小 → 都回傳 NULL。把 uClibc 改成 MALLOC_SIMPLE(純 mmap、為小系統設計的實作)就可以了。

雖然中間 claude 走錯很多次路,都需要人為校正回來,尤其是 -fPIC 這件事,我也沒意會到,只是覺得應該不會這樣複雜,所以試著要走別的路。這種經驗性的問題 Claude 是無法即時察覺的。或許 YOLO mode 可能可以最終也取得成本,但是這中間應該還要多燒更多的 tokens 達到目地,例如也有可能 AI 解不掉就生氣重寫一個 shell 或是大改 Linux kernel 之類的。

不過跟 AI debug 的過程還頗有趣的,想法和解題思路很像我會做的事,可能最終我也會走到這邊,但是花的時間會多非常非常多,畢竟以前就幹過類似的事情,只能說青春不值錢啊。以前和現在最大的差別是,有了 AI agents,我應該沒這個耐心自己解題,而是用 supervisor 的角度去解決問題。其實電腦科技發展一直是節省大家時間的,只是效果不會像 AI agents 這麼明顯,像是 assembly 到 C,或是第一次有了 database,dbase 大行其道,perl/php/甚至是 python 對 C/C++ 的衝擊。不過問題還是在於,會去研究底層的人會愈來愈少,但是未來可能會有更多新的不同的工具。但是這也沒關係,有需求就是有人會去弄的(或是花錢找人弄)。就像現在 assembly 很少人會寫,要用到的時候再寫個一點,但是我小時候,大家都要學上一些 assembly,望向我用 Microsoft ASM 寫 editor 的經歷,雖然曾經寫過,但是現在要不要學做 CPU,我也不太想碰 assembly 了。

發佈留言