Communitygithub.com

angelwzr/linux-phone-porting

一個編碼代理技能,協助你將主線 Linux 移植到 Android 手機上。

linux-phone-porting 是什麼?

這個 AI 代理技能專為主線 Linux 移植到 Android 手機的開發者設計,提供一套嚴謹的流程來解決移植過程中的常見問題。它支援多種平台,包括 postmarketOS、Mobian、NixOS 等,並強調在首次刷機前備份所有分割區(除了使用者資料),並記錄雜湊值以供後續驗證。技能要求開發者依序執行:設定環境、收集現場證據、研究來源、實施修復,最後驗證修復結果。它特別重視與裝置互動時的注意事項,例如確認快閃內容、避免除錯介面造成的偽陽性反應,以及正確記錄經驗法則。透過這套結構化方法,技能能有效減少盲目的修復嘗試,將每次除錯迭代成本控制在一個開機週期內。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add angelwzr/linux-phone-porting

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Linux 手機移植

在一個正在進行主線啟動的手機上,一次除錯迭代的成本包括:建構、刷機以及開機(約 5-10 分鐘),而盲目修復失敗的機率遠高於成功。請嚴格遵循此順序。

0. 設定移植環境

僅需在首次刷機前執行一次。之所以將此步驟列為第 0 階段而非文書工作,有兩個原因:原廠系統是你針對此開發板所能取得權威性最高的研究來源,而且一旦你開始覆寫分割區,其部分內容將無法再讀取。

首先確立目標,因為後續所有答案都取決於此:

  • 確切的裝置識別資訊 — 行銷名稱、代號、SoC,以及地區或儲存容量變體。同一行銷名稱的不同變體,其面板、觸控控制器或數據機通常也不同,針對錯誤變體研究的修復方法看起來合理,但最終會失敗。
  • 目標發行版與 init 系統 — postmarketOS、Mobian、NixOS、純 Debian 或其他。這決定了套件封裝、映像佈局,以及第 2 階段中哪個來源最接近。
  • 開機拓撲 — A/B 或非 A/B、分割區佈局,以及 bootloader 鎖定狀態。僅限已解鎖的 bootloader。 如果 bootloader 已鎖定,請停止:此技能不解鎖 bootloader,也不提供解鎖步驟 — 請引導使用者參考其 OEM 的說明文件,並在裝置解鎖後再繼續。
  • Recovery — 是否安裝了第三方 recovery。非必要條件:僅為了備份與還原的便利性。

接著對除了 userdata 之外的每個分割區進行完整備份:

  • 跳過 userdata:它佔據大部分空間,是使用者的資料,也是唯一可以承受遺失風險的分割區。
  • 備份裝置獨有的分割區,且絕不對外發布。 Modem NV/EFS、persist 及其校準相關分割區包含此特定手機的 IMEI、無線電校準和感測器調整。它們無法從網路上的任何映像重新產生,且一個損壞它們的移植會導致手機無法註冊網路。請將其視為既關鍵又私密的資料。
  • 對每個映像進行雜湊計算,並將雜湊值與分割區表格一同記錄。之後你將需要回答「裝置上的內容是否仍與我備份的一致」,而只有雜湊值能回答這個問題。
  • 在第一次有風險的刷機之前(而不是之後),驗證備份確實可以還原。

備份在研究方面(而非僅用於復原)的價值:

  • 原廠 DTB — 此開發板裝置樹的「地面真相」,也是當同系列 SoC 的 dtsi 與其不一致時的最終裁決者。
  • 韌體 blob 及其載入順序 — 廠商堆疊實際提供的內容以及它期望在哪裡找到它們。
  • 廠商核心開機參數與開機映像佈局 — 偏移量、標頭版本,以及原廠核心所收到的引數。
  • 確切的核心版本字串 — 來自原廠 ROM 的 uname -a 或 /proc/version(在其仍可讀取時;之後也可從備份的開機映像中解壓縮)。這是第 2 階段中用於選取正確 GPL 發布的 OEM 原始碼版本的指紋;廠商會發布多個建構版本,且這些版本各不相同。
  • 廠商設定 — 描述主線驅動程式必須滿足的介面之感測器、數據機和 HAL 設定。

最後,設定本階段剩餘工作所需的運作慣例:主機端目錄(用於為每次開機積累一個子目錄以存放日誌和 pstore),以及記錄哪個 recovery 路徑已知可用。如果已有來自先前嘗試的備份,請確認它涵蓋了這些分割區,且其雜湊值仍然正確,而不是假設它沒問題。

1. 首先從運作中的裝置收集證據

在進行任何更改之前,擷取失敗執行的完整 dmesg。在重現問題之前啟用相關的除錯開關(例如 echo 0xffffffff > /sys/module/<driver>/parameters/debug_mask),因為第二次重現需要另一次開機。

證據通道(大致按可靠性排序):

  • 透過網路或 USB 的遠端 Shell。 成本最低的通道;用於任何裝置尚能存活的狀況。
  • pstore。 一旦 systemd 啟動,從 /var/lib/systemd/pstore/(而非 /sys/fs/pstore/)收集 — systemd-pstore.service 在開機初期就將所有記錄移走,因此空的 /sys/fs/pstore 並不表示沒有記錄到崩潰。主控台區域通常是一個單一插槽:在執行任何其他操作之前先將其複製到主機,否則下一次崩潰也會在存檔中將其覆蓋。
  • ramoops。 視為有損。DRAM 電荷保持能力會在斷電期間衰減 — 在一個裝置上測量到大約 6.5% 的位元錯誤,足以使區域標頭的 ECC 失敗並完全遺失一條記錄。請模糊地比對記錄。導致區域內容損毀的是斷電時間,而不是在斷電前大量寫入日誌的時間,因此如果裝置當機,請讓它在主控台區域容量允許範圍內盡可能長時間記錄,然後立即斷電並重新開機。在假設你必須匆忙之前,請先檢查區域大小:一個 4 MiB 的主控台區域可以容納數小時的看門狗訊息,直到被覆寫。
  • 核心層級的 USB ACM 主控台(CONFIG_U_SERIAL_CONSOLE=y 加上 cmdline 中的 console=ttyGS0)。這是唯一能在裝置當機後存活的通道,並且值得在移植初期花費一次性刷機成本來啟用。一個使用者空間的 ACM getty 無法在當機後存活 — 它需要一個可排程的 CPU,並且在 USB 仍可正常枚舉時不會產生任何位元組。兩個部分都是必需的:僅有設定符號會使此功能處於無效狀態。

五個反覆將推論轉變為測量的法則:

  • 透過讀回分割區來確認實際刷入的內容,切勿依賴筆記或建構日誌。使用 iflag=direct 讀取,這樣讀取本身就不會干擾你正在除錯的內容。請注意僅比較大小的陷阱 — 開機映像會補齊到固定大小,因此兩個不同映像的長度相等並不能證明什麼。對內容進行雜湊計算。
  • 讀取除錯介面可能會製造出問題的症狀。 DRM/GPU 崩潰狀態節點是經典案例:在 GPU 活躍提交工作時取樣一次,會合成從未發生過的故障和恢復行(已測量:五次讀取,五次故障/恢復配對,而未被觀察時則為零)。在相信日誌行之前,請檢查是否是你自己的觀察行為產生了它。
  • 在解釋映射的 errno 之前,先擷取拒絕回應的原始內容。 一個韌體呼叫的映射錯誤可能是模糊的 — EINVAL 可能代表編碼錯誤或策略拒絕 — 直到呼叫包裝函式中的動態除錯暴露了原始回應字詞:一個格式良好的呼叫帶有明確的拒絕。這是策略問題,而非編碼問題 — 相反的結論,帶來相反的下一步行動。在對其採取行動之前,先將每次韌體拒絕分類為:編碼錯誤、策略拒絕或找不到。鏡像法則:一個乾淨、格式良好的負面回應是正面證據 — 一個來自格式正確查詢的「找不到」結果驗證了編碼方式,並消除了計劃中的一個分支,而無需觸及載入器。
  • 一個與失控條件競爭的測量會回傳一個看似合理但無效的數字。 一個基準測試在兩秒內得到了 55 分:合成器持有 DRM master,每個場景都失敗了,並且數字看起來很真實,直到檢查了條件。有效的比較 — 132 → 485 — 需要兩次執行都停止合成器,並且僅在該世代內進行比較。在相信基準測試之前,請找出可能使其無效的條件,並驗證它在每次執行時都成立。
  • 記錄你撤回的法則。 啟動過程會快速累積經驗法則,而一個在一個硬體版本或緩衝區大小下正確的法則,在條件改變後會變得具有誤導性。將被推翻的版本與當前版本以及變更的內容放在一起,這樣下一次工作階段就知道哪些建議已經過壓力測試。

值得預先注意的主機端陷阱:

  • timeout N ssh <device> '<cmd>' 只會殺死本機客戶端;遠端程序會繼續執行。請隨後發出明確的 kill。
  • pgrep -f 和 pkill -f 透過相同的 ssh 路徑執行時,會自我匹配到它們自己的遠端 shell。請過濾掉匹配項,或按 PID 匹配。
  • 當起始路徑是符號連結時,find 需要 -L 參數,否則會回傳無聲的空結果。
  • 任何改寫命令輸出的包裝函式或掛鉤 — 記號過濾器、分頁器、格式化工具 — 都會破壞證據,有時是反轉測試結果而不是明顯地破壞它。對於你將記錄為發現結果的任何內容,請使用原始命令。

2. 在實作之前進行研究 — 所有這些來源

研究始終先於實作。消耗什麼取決於你在做什麼:

  • 調查一個失敗 — 使用第 1 階段的證據。先擷取它;研究一個你實際上沒有讀取到的症狀,只是在引用猜測。
  • 新增一個功能 — 一個從未出現過的驅動程式、周邊或子系統 — 使用第 0 階段的人工製品作為裝置資料。原廠 DTB、廠商設定和韌體佈局描述了此開發板預期如何驅動該功能,它們在權威性上與崩潰日誌相當。

無論哪種情況,在撰寫任何程式碼之前:

  1. 推理子系統 — 識別哪個驅動程式、綁定或韌體介面擁有這個失敗,或將擁有這個新功能。你的會話上下文中列出了你可用的技能:現在掃描該列表,如果其中有核心開發或崩潰分析技能,請在繼續之前閱讀它 — 其暫存器層級和綁定的細節正是此步驟所需的。僅在列表中没有列出時才直接推理。
  2. 查詢涉及的使用者空間函式庫和工具 — 查閱當前文件,而不是回憶 API。如果同一個會話列表中有文件檢索技能,請執行它,而不是從記憶中回憶簽名。
  3. 跨所有六個來源家族進行網路研究(在此處使用平行子代理效果很好;如果列表中有網路研究技能,請優先使用它進行快取,因為會話會重新讀取相同的頁面):
    • 主線 — lore.kernel.org、驅動程式原始碼、DT 綁定,以及最接近的同系列 SoC(相同家族或相同 RPM 世代)的 dtsi。在確定任何「移植下游驅動程式」的計畫之前,先掃描主線針對該介面已有的內容:其匯出的 API、它是僅限核心內部還是可達使用者空間,以及其閘道 — 機器允許列表、「由 bootloader 預載」語意、內建而非模組化(已測量:一次這樣的掃描將一個 9.4k 行的下游移植縮減為一個約 300 行的探測模組,因為主線已經在核心內部透過一個允許列表提供了該介面 — 計畫變成了驅動現有 API,而不是移植它)。
    • 下游廠商核心 — GPL 發布的 OEM 原始碼。 GPLv2 要求 Android OEM 發布他們出貨的核心原始碼,因此此裝置的精確版本通常存在:OEM 的開源入口網站(Samsung、Sony、Xiaomi、Fairphone、OnePlus/OPPO 和 Motorola 都有運作)、他們的 GitHub/GitLab 鏡像,以及當入口網站連結失效時的社群存檔(如 XDA)。選擇版本字串與第 0 階段的 /proc/version 相符的壓縮包 — 廠商會發布多個,且它們各不相同。合規性品質參差不齊,因此請將此原始碼樹視為一個可以挖掘的 oracle — 此確切裝置的開發板 dts 和 defconfig,以及廠商樹外的驅動程式(觸控、面板、充電器、數據機粘合層)及其暫存器序列和韌體握手協定 — 而不是假設它可以建構。當沒有精確版本時,兄弟裝置的版本通常仍然包含 SoC dtsi。更好的是:從裝置本身提取的原廠 DTB(dd 未觸及的開機槽,搜尋 d00dfeed,dtc -I dtb -O dts)。這是此開發板的「地面真相」,並且已經多次糾正錯誤的同系列 SoC 猜測 — 包括一個同系列 SoC 弄錯而原廠 DTB 正確的 SMMU 串流 ID。
    • postmarketOS — pmaports 裝置套件和 APKBUILD、合併請求、Wiki 裝置頁面,以及 SoC-mainline 樹。
    • Halium / UBports — halium/android_device_* 樹、hybris-boot 設定,以及 UBports 裝置移植。Halium 執行的是 Android 廠商堆疊而不是主線,這使其成為了解廠商韌體期望如何被驅動的最佳來源:韌體 blob 路徑和載入順序、daemon 期望的屬性和 socket 介面、感測器和數據機 HAL 設定。當主線驅動程式探測成功但韌體握手失敗時 — 或者當你需要知道一個尚未被支援的周邊裝置期望什麼握手時 — 通常可以在這裡找到缺失的資訊。
    • Mobian — Debian 裝置儲存庫。
    • NixOS — mobile-nixos 模式和 nixpkgs 套件化。

結果稀少是常態,而非障礙。 移植所需的大部分內容從未在此開發板上完成過,甚至經常不在這個 SoC 上。六個來源都沒有回報結果,意味著沒有人發布過這項工作 — 這絕不代表工作應該停止或交還。當沒有先例時,答案仍然存在於主要人工製品中:已經驅動周邊裝置的樹外廠商驅動程式(暫存器序列、韌體握手、GPIO 和穩壓器接線)、原廠 DTB 中自己的節點、廠商 HAL 設定命名使用者空間所期望的內容,以及最接近的、用於相同裝置類別的主線驅動程式作為結構模板。閱讀這些內容,推導出設計,並撰寫缺失的部分 — 一個 DT 節點、一個 quirks、一個小型驅動程式。撰寫該部分是啟動過程的正常終止狀態,而非需要證明的例外;第 0 階段的備份和「每次刷機只改變一個變數」的規則正是為了讓一個錯誤的推導只花費一次開機和一行記錄的成本,與任何其他反駁相同。

空白的搜尋結果和空白的掃描是不同的發現。 對來源進行空白搜尋意味著沒有人發布過這項工作。而空白的掃描 — 一個回傳空結果的提取 — 是無效的,除非已經用已知的正確樣本對相同的方法進行了測試,並且其覆蓋範圍已驗證:檔案計數、區段列表、glob 實際匹配了什麼。已測量的接近失誤:一個 glob 匹配了 27 個韌體區段中的 9 個,並差點記錄了一個錯誤的「韌體省略了它」;一個函式庫掃描只有在它找到一個已知的晶片 ID 作為其正控制組後才變得可靠。

下游識別符是下游的 p…

[原文截斷,無法提供完整翻譯]

相關技能