HN 日本語サマリー

← 一覧へ戻る
プログラミング

M4 Mac Mini用Linux GPUドライバーを1ヶ月で構築する

Building a Linux GPU Driver for the M4 Mac Mini in One Month (codyho.dev)

35 pointsby ADevWithAnIdea2 コメント

要約

Niklas氏とCody Ho氏は、わずか1ヶ月でM4 Mac MiniおよびMacBook Neo向けのOpenGL ES 3.0準拠GPUドライバーを開発しました。このプロジェクトでは、AppleのAGX GPUの複雑なファームウェアABIとユーザー空間コンポーネントのリバースエンジニアリングが必要でしたが、クリーンルーム手法を用いて検証可能な形で実施されました。開発されたドライバーは、ChromeやFirefoxでのWebGL表示、さらにはMinecraftを200fpsで実行できる性能を示しています。

全文翻訳

I Came, I Prompted, I Left Part 2: Building a GPU Driver From Scratch in One Month 前のブログ記事: https://codyho.dev/blog/hypervisor-macbook-neo/ 我々がやったこと TL;DR: Niklasと私は、約1ヶ月でM4 Mac MiniとMacBook Neo向けの完全にOpenGL ES 3.0に準拠したGPUドライバーを構築しました。通常なら数年かかるプロセスです。以下は、M4 Mac Mini上で動作するChromeとFirefoxで、コンポジットが機能するWebGLの様子です: 最も重要なのは、このドライバーがMinecraftを200fpsで実行できるほど高速であることです: このドライバーの構築には、AGX(AppleがGPUに付けた名称)の信じられないほど複雑なファームウェアABIとユーザー空間コンポーネントのリバースエンジニアリングが含まれていました。これらはすべて、確立された手法を用いた、透明で検証可能なクリーンルーム方式で行われました。コードはまだエンドユーザー向けではありませんが、できるだけ早く提供したいと考えています。 どのように行ったか 以前、私はmacOSをリバースエンジニアリングするためにハイパーバイザーを構築しました。次に、それを使って実際に何か有用なことをするという目標が生まれ、GPUドライバーを書くこと以上に良いターゲットはありませんでした。GPUは、現代のシステムにとって実質的に必須であり、そうでなければすべてCPUレンダリングになり、桁違いに遅く、電力効率も悪くなります。私たちの目標は、M4 Mac MiniとMacBook Neo向けの準拠したOpenGL(そして間もなくVulkan)ドライバーを実装することでした。 通常、GPUドライバーの構築は数年かかる作業ですが、私たちの目標は数日でそれを達成することでした。結果として、数日は楽観的すぎましたが、数週間でも依然として大きな進歩です。この数週間で、私たちは以下を行いました: ライブプロービングのみを使用して、M4、A18 Pro、および(大部分の)M5のユーザー空間をリバースエンジニアリングし、Appleのドライバーが出力しないハードウェアサポート機能と命令を発見しました。 新しいカスタムIR/シェーダーコンパイラ、コマンドストリームビルダー、その他多くのコンポーネントを含む、完全に動作するユーザー空間ドライバーを構築しました。 以前構築したハイパーバイザーからのトレースを使用して、AGXファームウェアABI全体をゼロからリバースエンジニアリングしました。 このプロセス全体を通して、Appleのバイナリは一切見ておらず、ハードウェアトレース(ハイパーバイザーから取得)と自作のシェーダーのみを使用しました。グラフィックスのリバースエンジニアリングでは、必要なAppleのブロブ(バイナリファイル)を不透明なオブジェクトとして扱うよう注意しました。友人にこれらのブロブに関するドキュメントを書いてもらい(https://github.com/codyho/agx-re/blob/main/docs/agx-firmware-abi.md)、それに基づいてクリーンルーム実装を行いました(これは主に、試行錯誤を繰り返して機能するまで行うという形でした)。私たちの作業の出所を誰でも検証できるように、すべての実験結果を公開しています(Deliverablesの下にあるtwin agx-reリポジトリを参照)。 このブログ記事は、ユーザー空間とカーネル空間の2つのパートに分かれています。これは、すべての現代のGPUドライバーにおける分割を反映しています。カーネルは、ファームウェアとのインターフェース、バッファの割り当て、スケジューリングの管理を担当し、バッファの実際のコンテンツとスケジューリングされているものは不透明です。ユーザー空間は、GPUが実際にどのように機能するかを理解し、それらのバッファをデータで満たす責任があります。 カーネル空間 Apple Siliconでは、カーネルドライバーはハードウェアと直接インターフェースしません。代わりに、カスタムRTOSであるRTKitを実行しているGPUファームウェアと通信します。つまり、カーネルドライバーの最初のステップはハードウェアとの通信ではなく、ファームウェアABIを理解することです。 ファームウェアABIは、このプロジェクトで最も厄介な部分でした。なぜなら、合理的なABIと優れたインターフェースを設計するという賢明なことをする代わりに、Appleは通常のカーネルドライバーを半分に切り取り、その半分をAGXに配置してファームウェアと呼び、もう半分のカーネルドライバーがメモリ内の共有構造体を使って通信するようにしたからです。これらの構造体の多くには、ファームウェアが所有するフィールド(決して変更してはならず、リバースエンジニアリングから学ぶ必要がある)と、ホストが制御するフィールドが混在しています。ABIの複雑さの例として、M1/M2での共有メモリツリーは次のようになります: Asahi Linaは、M1/M2カーネルドライバーを構築するために、骨の折れる12時間の日々を費やしてこれをすべて解明しました。これは驚くべき技術的偉業です。残念ながら、A18 ProファームウェアABI(私はMacBook NeoでRE作業を開始し、後にM4 Mac Miniに移行しました)は、すでに非常に複雑なM1ファームウェアABIよりも大幅に複雑です: Apple、一体どうしたんだ。A18では、構造体が1.5倍、ポインタが2倍、作業の送信プロセスが著しく複雑になっていることに注目してください: 他にも、REプロセスに摩擦を加える問題が数多くあります。ファームウェアABIに関するドキュメントはありましたが、非常に不完全で、正直言ってあまり役に立ちませんでした。 私の方法はシンプルで、M1/M2マシンを正常にリバースエンジニアリングするために使用されたアプローチに基づいています。macOSの動作を観察し、それを再生し、それから自分たちで試すというものです。これはハイパーバイザーによって可能になります。 このアプローチをLLMに説明したところ、LLMは再生を非常に文字通りに解釈しました。最初に行ったことは、最初のファームウェアで認識されるイベント(「キック」と呼ばれます)が発生するのを待ち、GPUメモリ状態全体のコピーを保存することでした。再起動後、保存されたメモリ状態をそのままホストメモリにコピーし、キックを実行して、出力ページが変更されるのを確認しました。その後、これらのオブジェクトをコードで再構築しようとし、すべてのポインタをたどり、内容を理解しました。 successiveな実験を通じて、Codexはコピーするページ数を減らし、最終的には再生される状態がなくなり、すべてソースから構築されるようになりました。驚くべきことに、Codexはいつハードウェアをさらにプッシュすべきか、いつハイパーバイザーを実行して状態をキャプチャすべきかについて、良いセンスを持っていました。 ホストの作業のクリーンなキャプチャを取得できなかったことが原因で、3つの大きな問題が発生しました。 最初の問題は、GPUファームウェアが開始した後に送信されたレンダリング作業でした。ファームウェアが開始する前に作業を準備し、GPUを開始すれば、その作業は期待通りに完了しましたが、ファームウェアが開始すると、送信された作業は実際には何も行われずにACKされ、破棄されるだけでした。ファームウェアが開始すると、状態のキャプチャははるかに困難になります。なぜなら、すべてが動的になり、ファームウェアは簡単に再生できない状態を持つステートフルなオブジェクトになるからです。 この時点で私が介入し、Codexのプロセスを調べる必要がありました。それは、AGXのライフサイクルのかなり遅い段階でのキャプチャを再生しようとしていたことが判明しました。そこでは、すでに多くの以前のイベントが発生していました。私が、ファームウェア開始後の最初のキャプチャである、AGXのライフサイクルのかなり早い段階でのキャプチャを選択するように指示したところ、Codexはほぼ即座に問題を特定できました(単一バイトのディスクリプタが欠落していました)。これには数日かかりました。 2番目の、そして唯一の主要なブロックとなった問題は、コンピューティングでした。AGXは、大まかに言って、コンピューティングとレンダリングの2種類の作業をサポートしています。通常のGUIパスでは、コンピューティング作業は、かなりの量のレンダリング作業がすでに実行された後にのみスケジュールされます。そのため、コンピューティングワークロードのクリーンなキャプチャを取得するには長い時間がかかり、Codexが最終的に取得したとき、それは336MBで再生不可能でした(長時間試みましたが、失敗しました)。また、キャプチャを見てオブジェクト自体を構築しようとし、1週間以上費やしましたが、最終的には成功しませんでした。あまりにも多くの無意味な情報がありました。これは私の側の問題によって悪化しました。レンダリングが機能するようになった後、コンピューティング作業の送信はより簡単になると予想していましたが(ファームウェアABIは確かにシンプルなので、ここでは正しかった)、謙虚さを失い、数時間しかかからない簡単な作業だと考えました。そのため、LLMのためにタスクを適切に構造化しませんでした。 修正は、別のCodexセッションで提供されました。要するに: GUIを無効にするために、シングルユーザーモードでブートします。これにより、レンダリング作業は行われません。 Metal(Apple独自のグラフィックスフレームワーク)が利用可能になった直後の最も早い時点で実行されるLaunchDaemonをインストールします。 提供した小さなMetalプログラムを実行します。 この小さな、純粋なコンピューティングトレースをキャプチャして再生します。 トレースは正常にキャプチャされました。数時間以内に、Codexはそれを分解し、数時間以内に