把 Linux 6.12 塞進一顆微控制器:ESP32-S31 上的 Sv32 移植實驗
GitHub · GrieferPig/esp32-s31-linux · 2026-08-02
開發者 GrieferPig 在 esp32-s31-linux 專案中把 Linux 6.12 核心直接移植到 ESP32-S31 微控制器上執行,而不是靠模擬器跑一個虛擬機。這顆 RISC-V 晶片原本設計來跑 FreeRTOS 搭配 Espressif 的無線韌體,README 開頭就直接聲明「這是一個實驗性的硬體 bring-up 專案,絕對不要用在正式產品上」。專案已在 ESP32-S31-WROOM-3 E1H16R16V(Core Board/Korvo)模組上實測開機成功。
原本的問題
一般 Linux 移植到 MCU 等級硬體,最大障礙是沒有完整的記憶體管理單元與標準特權模式。ESP32-S31 的 Supervisor 模式並非標準 RISC-V 實作,作者寫到「S31 的 S 模式沒有任何標準用法,很多 CSR 用法都是從 ESP32-P4 的 TRM 猜測或探測得到的」——例如它用了 sclicbase 暫存器,卻沒有標準的 sie。中斷控制器也不是 Linux 預期的 PLIC,而是類似 P4 的 CLIC/CLINT 組合,因此需要自製 CLIC 驅動(參考 litex-hub 的一個 patch)才能讓中斷正常運作。晶片只有 16MB PSRAM,兩顆核心還共享僅 32KiB 的 D-cache。
採用的方法
為了不把寶貴的 PSRAM 塞爆,這個移植採用了執行時直接讀取 Flash(XIP,execute-in-place)的做法,程式碼不必先載入 RAM 才能跑。README 提到主線 Linux 已經移除了 RISC-V 上的 XIP 支援,所以作者選擇還留有完整 XIP 支援的 6.12 版本。開機所需的 OpenSBI(RISC-V 的特權層韌體介面)也被修改成同時支援 Flash 與內部 SRAM 兩種 XIP 映射,因為兩塊記憶體的位址空間並不連續——這也是為什麼開機畫面上 OpenSBI 會顯示一個奇怪的韌體大小 3915901 KB。
- 虛擬記憶體:Sv32(RISC-V 的兩層式 32-bit 分頁表方案)
- UART0 主控台、Buildroot 使用者空間、reboot/poweroff:標記為 Stable
- USB、時脈樹、安全加速器:仍在 WIP
- I2C、I2S、SPI、RMT、LP 子系統、PMP/APM:完全未實作
- 雙核心 SMP:明確不會做——第二顆核心保留給 FreeRTOS 跑 Espressif 的閉源無線韌體
實際效果
燒錄方式使用標準 esptool,把 bootloader、分割表、OpenSBI payload、XIP 核心映像與 SquashFS rootfs 依序寫入指定的 Flash 位址。
esptool -p /dev/ttyUSB0 -b 2000000 write-flash \
--flash-mode dio --flash-freq 80m --flash-size 16MB \
0x2000 bootloader.bin \
0x20000 hello_world.bin \
0x2A0000 xipImage \
0xA20000 rootfs.sqfs作者在 FAQ 中把這顆晶片比喻為「重生的 Bouffalo BL808」,不做 SMP 除了無線韌體閉源、兩核心異構外,也單純是想留一個 RTOS 跑其他工作。
追了一週的 ripgrep musl segfault,最後查到是 Linux 核心的 TLB flush bug
GitHub Issue · BurntSushi/ripgrep #3494 · 2026-08-02
使用者 dfoxfranke 在 2026-07-26 回報:官方發布的 ripgrep-15.2.0-x86_64-unknown-linux-musl 二進位檔在對超大目錄樹做高並行搜尋時會偶發 SIGSEGV。他在 24 核心機器上對約 20GB、180 萬檔案的目錄樹跑 rg 搜尋一個不存在的字串,通常一分鐘內就會當機。當時看起來像是 musl 配置器本身壞掉了。
原本的問題
崩潰的 coredump 顯示錯誤發生在 musl 的 mallocng 配置器內部,呼叫鏈是 opendir() 觸發的 calloc,一路進到 meta.h 裡的一個堆積中繼資料完整性斷言:
#0 get_meta () at ../src_musl/src/malloc/mallocng/meta.h:141
#1 __malloc_allzerop ()
#2 calloc () at ../src_musl/src/malloc/calloc.c:41
#3 opendir () at ../src_musl/src/dirent/opendir.c:15
#4 std::sys::fs::unix::readdir
...
#11 ignore::walk::Work::read_dir
#12 ignore::walk::Worker::run_one倉庫作者 BurntSushi 追問是否只有 musl 版本才會炸、glibc 版本行不行,dfoxfranke 確認只有 musl 連結的二進位檔會崩潰,而且退回 ripgrep 15.1.0 一樣會炸,只是崩潰的第一層堆疊換成了 a_crash() 這個 musl 內部的原子操作斷言。這讓大家一度懷疑問題出在 musl 的分配器邏輯本身。
技術細節
但 dfoxfranke 07-28 貼出後續分析,稱「這個坑比 musl 內部出錯還深,我認為其實是核心 bug」,並附分析倉庫 dfoxfranke/ripgrep-3494-analysis。08-01 使用者 bspengler-oss 根據 Andy Lutomirski 在 LKML 的發現,指出禍首在 mm/memory.c 的 zap_pte_range():
- 提交
4c640eb4181cf8("mm: move pte table reclaim code to memory.c")把原本用start的程式碼誤改成addr - 函式裡先前的重試邏輯讓
addr到這裡時恆等於end,跑出了原本該處理的範圍 - x86 釋放頁表時會忽略傳入位址,但 TLB flush 用的是同一個錯誤位址,部分頁表項目未被正確清除
- 使用者空間程式讀到失效的分頁映射,表現成看似「heap 中繼資料被破壞」的假象
bspengler-oss 提供一行修正 diff,把 pte_free_tlb(tlb, pmd_pgtable(pmdval), addr) 改回 start。
後續影響
dfoxfranke 在自己的 SUSE 7.0.12 核心上加了 sysctl 開關來 A/B 測試這個 patch,但重開機後原本的重現腳本竟然兩種設定下都不再觸發崩潰了——他推測是頁表配置在前一次開機的長時間執行後才會「演化」到容易觸發此 bug 的排列方式,重開機後page table 重新初始化,問題暫時消失。他表示會保持該核心開機、等崩潰重新出現後再做乾淨的 A/B 對照。另一位使用者 DavidBuchanan314 則建議檢查記憶體是否有 ECC 錯誤以排除硬體因素,dfoxfranke 回覆系統有 ECC 且未記錄任何錯誤。截至 08-01,此 issue 仍在 open 狀態,等待可重現的驗證結果。
十年 AUR 維護者請辭:Morten Linderud 為何淡出 Arch Linux
Morten Linderud 個人部落格 · 2026-08-01
Arch Linux 開發者 Morten Linderud 在部落格上宣布辭去他在 Arch 專案裡的所有角色。他在文中提到自己的封裝維護者(Package Maintainer)申請書是九年前寫的,而他實際參與 Arch 社群的時間則將近十年,期間陸續擔任過 AUR 維護者、安全團隊成員、封裝維護者,最終成為擁有完整提交權限的 Developer。
背景
在這段任期內,Linderud 列出了幾項具體貢獻:他實作了 Arch 的 debug package 支援,讓使用者能額外安裝除錯符號套件而不必自行重編;他也做了 Arch 套件庫從 SVN 遷移到 Git 的概念驗證(PoC),促成後來整個版本控制系統的遷移;另外他也在 2020 年疫情期間籌辦過一場線上研討會。
離職原因
對於離開的原因,他寫得相當節制,只表示「情況正在改變,現在是放手的好時機」("Things are changing and it's a good time to let go."),沒有點名任何具體衝突、內部爭執或不滿事件。整篇文章的語氣偏向平和的告別,而不是帶著情緒離場的公開信,他也同步把辭職信發到了 arch-dev-public 郵件論壇上作為正式紀錄。
後續影響
Linderud 表示不會離開技術社群,而是把心力轉向自己感興趣的方向,包括 TPM(可信賴平台模組)、Secure Boot 與平台安全性相關的開發工作。這代表他從「維護一個發行版的套件生態系」轉向更聚焦在系統安全底層的個人專案,而不是完全退出開源開發。