コンテンツにスキップ

Apple GPU (AGX)

2025/3/9時点のagxの翻訳


Lina氏のAGXノート

読者注意、ここにドラゴン(dragon)がいます。ポインター型のドラゴン。たくさんいます (訳注:おそらくドラゴンとは非常に厄介なものを意味しているのかと思います)。

この文書はカーネルドライバの観点からAGXアーキテクチャに焦点を当てています。シェーダやテクスチャサンプリング、 パイプラインコマンドエンコーダなど、純粋にユーザ空間の関心事である側面については説明しません。

概要

AGX は PowerVR に強くインスパイアされた(しかし大部分はオーダーメイドの) GPU デザインです。OSインターフェースは、 Appleファームウェアを実行するASC(ARM64)コプロセッサーを介して、ほぼ排他的に仲介されています。すべての通信は、 共有メモリといくつかのmailboxのdoorbellメッセージを介して行われます。すべてのメモリは、私たちが知る限り コヒーレントです(私たちはまだキャッシュ管理命令を1つも使っていませんが、すべてがまだ機能しています)。

画面に三角形を表示するために必要なレイヤーは、おおよそ次の通りです:

  • UAT (MMU)
  • ファームウェア初期化
  • GPU チャネル
  • GPU コンテクスト
  • ワークキュー
  • ワークアイテム
  • マイクロシーケンス
  • Tilerバッファ管理
  • イベント管理
  • (すべてのユーザ空間のものがここ)
UAT (Unified Address Translator, 統合アドレス変換器)

UATはAGXのMMUです。基本的にはARM64のMMUで、同一のページテーブルを使用します。実際、AGX ASCは文字通りUATの ページテーブルベースをTTBR0/1レジスタとして構成しています。GPU がページパーミッションやその他の属性を どのように解釈するかについては、CPU の解釈と異なる点があるかもしれませんが、これはまだほとんど未解明です。

GPUのVA(訳注:Virtual Address,仮想アドレスのことかと)は40ビットで、最上位ビットは符号拡張されて64ビットになります。 ARM64と同様に、カーネル/ユーザーアドレス空間が分割されています。ページは常に16Kです。

GPUコンテキストページテーブルのベースアドレスを含む、グローバルで固定されたメモリページが存在します。最大16の コンテキストがあり(TODO: この制限が本物でドライバ制御でないと再確認)、それぞれにカーネル/ユーザページテーブルの ための2つのベースレジスタがあります。

macOS は常にコンテキスト 0 をカーネルにのみ使用し(コンテキスト 0 にはユーザページはありません)、すべての コンテキストでページテーブルのカーネル側半分を共有します。ユーザーVA空間は、コンテキストごとにユニークです。

カーネルのアドレス空間は文字通り ASC ファームウェアもマップします、それは ASC の CPU ページテーブルで あるからです。これはファームウェアがそれ自身で制御し、初期化中に設定するVA範囲にあります。ホストOSはアドレス空間の この半分のページテーブルの残りに責任を持ち、そこにすべてのGPU制御構造が行きます。

注:ファームウェアはブートローダによってロードされ、書き込み保護されます。ページテーブルをハイジャックして AGXファームウェアを完全に乗っ取ることは、現在の設計でも可能ですが、明らかにAppleの意図と方向性は ASCファームウェアが乗っ取りに対して強化されることなので、現時点では私たち自身のファームウェアを使うことは 考えていません。このGPUを動作させるためにAppleのファームウェアと対話することは、避けられないと考えられています。

TODO: UATのTLB無効化。共有メモリ関係とファームウェアを突くなど

ファームウェア初期化

ファームウェアとの通信は、他のASCと共通のRTKitフレームワークを使用しており、ここではカバーしません。メモリ/バッファ管理だけが 異なります(他のASCはDART IOMMU、SARTアドレスフィルタを使用するか、専用SRAMにのみアクセス可能です)。

GPUを初期化するために、初期化データ構造へのポインタを含む1つのメッセージが送信されます。これはより多くのポインターを持つ 複雑なネストされたデータ構造で、次のものを含んでいます:

  • チャネルリングバッファ制御領域とリングバッファ領域へのポインタ
  • DVFS状態を含む電源管理データ
  • 様々な(主に未知の)データを含む共有メモリ領域
  • 色空間変換係数
  • MMIOマッピングリスト(ASCがアクセスする必要のあるMMIO領域をマッピングするのはOSの役割)
  • UATレイアウト情報
  • 様々な未知のバッファ

データ構造: initdata.pyを参照

チャンネル

ファームウェアとの通信は、チャネルリングバッファを介して行われます。チャネルは、リングバッファの中にインラインで配信される 小さな固定サイズのメッセージを保持します。制御構造体は読み込み/書き込みバッファのポインタとサイズを持ち、GPU/CPU が常に バウンスしないようにキャッシュラインに整列されます。チャネルは両方向にあります。

データ構造: channels.pyを参照

CPU->GPU チャンネル
  • ワークチャンネル: 4つのグループ(0-3)、それぞれ3つのチャンネル、GPUワークタイプごとに1つ
  • TA (Tile Accelator、頂点処理)
  • 3D (3D、ピクセル処理)
  • CP (Compute)

TODO: 異なるチャンネルに送られたワークにはおそらく何らかの優先順位スキームがあるはず

  • デバイス制御チャネル、デバイス全体のメッセージ用 (例: GPU init とおそらく電源管理関連のもの)
GPU->CPU チャンネル
  • イベント:作業関連のイベント通知
  • 作業完了フラグイベント
    • これらはどのイベントインデックスが発火しているかを示す128ビットの配列を持つ
  • フォールト通知
    • GPUのフォールトはStop-the-Worldの処理としてはかなり貧弱に見える。macOS は実際に GPU MMIO レジスタを直接ダンプするようになり、ファームウェアは何をすべきかについてとても無知であるように思われる
  • 統計メッセージ
  • 私たちはこれらを無視。バッファがオーバーフローした場合、ファームウェアは一度だけ文句を言うが、無害
  • ファームウェアのsyslog
  • これは、何らかの理由で複数のサブチャンネルが存在し、制御構造が連続的にレイアウトされているという点で奇妙
  • 未知のチャネル(トレース?)
GPU コンテキスト

GPU コンテキストはいくつかの小さな共有構造体にマッピングされ、いくつかは ASC によって生成されます。これはほとんどまだ未定です。

ワークキュー

ワークキューは特定のタイプのワークアイテムを保持します。通常、コンテキストごとに複数のワークキューがあります (例えば、3D レンダリングには少なくとも 3D と TA が存在)。ワークキューはいくつかの構造体で表現され、主に CPU が初期化し GPU が管理するメイン構造体と、コンテキスト構造体へのポインタ、リングバッファ、リングバッファ ポインタブロックなどで構成されています。リングバッファは個々のアイテムへのポインタの配列です(インラインではない)。

ワークキューを処理するために、OSはワークキュー管理構造体へのポインタと最新のリングバッファ書き込みポインタをワーク チャンネルに送信します。加えて作業完了を知らせるイベントインデックスや新しいワークキューからの最初の送信かどうかを 示すフラグを持つメッセージを送信します。

データ構造: cmdqueue.py を参照

ワークアイテム

ワークアイテムは GPU 作業または関連する操作を表します。これらは、特定のレイアウトで埋め込まれたサブ構造を含む かなり大きなバッファです(これらのサブ構造のいくつかへのポインタは存在しますが、ファームウェアは特定の方法で埋め込むことを 想定しているので、レイアウトは尊重されなければなりません)。

データ構造: cmdqueue.py を参照

マイクロシーケンサー

ASCファームウェアは、作業コマンドの一部としてかなり複雑な『スクリプト』を実行できるコマンドシーケンサーを含んでいますが、 通常は明らかに基本的な方法で使用されます。これらのシーケンスは、ワークアイテムの一部として実行されるコマンドのバッファを パックしたものです。典型的なシーケンスは:

  • 開始(3D/TA/CP)
  • タイムスタンプの書き込み
  • アイドル待ち
  • タイムスタンプの書き込み
  • 終了(3D/TA/CP)

データ構造: microsequence.py を参照

Tilerバッファの管理

GPU Tiler は頂点属性とプリミティブデータを格納するバッファを必要とします。これはドライバが提供するいくつかの固定サイズのバッファと、 GPU ファームウェアが任意に割り当てるヒープによって行われます。ヒープはバッファマネージャオブジェクトと、それが指す いくつかのバッファを介して管理され、CPUはブロック(4ページ)とページ(各32KでVA空間(!)内でアライン)のリストを提供します。

Tilerバッファオーバーフロー/一部保存/再ロード処理は、ASCファームウェアによって完全に管理されています。

データ構造: microsequence.py の BufferManager* を参照

イベント管理

ワーク完了は、RAM上の32ビットワードであるスタンプオブジェクトへの値の書き込みによって通知されます。通常0に初期化され、 ワークアイテムが処理されるたびに0x100ずつ増加します。各ワークアイテムは、イベントID(0〜127)とイベント管理構造体にも 関連付けられます。この構造体は、1人のユーザーに対して3D/TA間で共有され、ベースバリア値と(?)イベント数閾値を含んでいます。 閾値に達すると、対応するイベントIDがCPUへのイベントメッセージでアサートされます。これがどのように動作しカウントされるかは まだ不明で、というのも、同じ構造がロックステップで増加する異なるバリアオブジェクトを使用する異なるキュー間で共有されていますが、 両方のイベントを取得するために閾値は2でなければならないのにそれぞれが1ずつ増加するだけだからです... 未確定

3D フレームの描画

フレームを描画するには、まず以下のものが必要です。

  • 使用する一対の 3D/TA チャンネル
  • 完了通知のためのイベントインデックスのペア
  • WorkQueueのペア
  • UATのコンテキストID
  • 共有コンテキスト構造体
  • Tilerの静的バッファ
  • Tiler ヒープマネージャと関連するバッファ/リスト
  • 4つのスタンプオブジェクト
Tilerバッファ
  • (U) TVB タイル配列 (タイル数に依存)
  • (U) TVB リスト配列 (タイル数に依存?)
  • (U) TVB ヒープメタデータブロック (固定サイズ?)
  • (K) TVB ヒープマネージャ & (U) ヒープ (任意のサイズ >= 3 128K ブロック、CPU は将来のフレームに対するオーバーフローに対応して動的に調整可能)

macOSはこれらをカーネルで割り当てています。カーネルでやりたいですか?ユーザースペースでやりたいですか?ユーザースペースは 少なくともサイジングを制御する必要があるのではないでしょうか?カーネルに決定させるか、または、ユーザスペースがバッファマネージャにページを 提供するようにすることができます。カーネルは少なくともヒープマネージャの構造を扱う必要があります。

スタンプオブジェクトとイベント管理

128 個のイベントインデックスがあります。レンダーは 4 つのスタンプオブジェクトを必要とし、TA/3D のためにそれぞれ 2 つ必要です。 現在の理論では、1つのスタンプは作業の完了を示し、もう1つは完了イベントがCPUに配信されたことを示します(ワークが刈り取られた?)

TAワーク

TAワークは通常このような感じです:

ヒープマネージャの初期化

初回またはヒープサイズが変更されたときに必要です。CPUが管理ストラクタを再初期化したことをGPUに伝えます。

未知のコンテキスト関連IDが含まれます。これはヒープマネージャのIDかも?新しい)TAスタンプの値も渡されます。

TAを実行

注:これはすべて要約であり、大量の未知/固定/マジックナンバーを無視

  • このジョブのUATコンテキストID
  • イベント管理構造体ポインタ
  • ヒープマネージャ構造体ポインタ
  • ある種の関連するバッファ記述子へのポインタ(?)
  • 未知のバッファへのポインタ(空)
  • タイムスタンプ 1 ptr
  • タイムスタンプ 2 ptr
  • タイムスタンプ 3 ptr
  • 未知のバッファ 2 (空)
  • 組み込みの構造体:
  • Tilerパラメータ(タイル数など)
  • TAワーク構造体2
    • TVB タイルマップ ptr
    • TVB リスト ptr
    • ユーザースペースから渡される3つの小さなバッファ(aryssa氏はこれらを『deflake』と呼ぶ)
    • コマンドエンコーダーptr (つまりユーザー空間から実際に実行されるgfxパイプライン)
    • パイプラインウィンドウのベース(32ビットシェーダのポインタに使用されるVAへの4GiBウィンドウ)
  • TAワーク構造体3
    • deflake ptrの1つ
    • エンコーダーID(ユニークなID、GPUはおそらく触れない)
    • 『未知のバッファ』(ユーザースペースからの数字が増加)
    • TAバリア1、2へのポインタ
    • 完了時に書き込むスタンプの値
    • いくつかのUUID
  • マイクロシーケンスポインタ
  • 再度完了スタンプ値(Completion stamp value again)
  • 3Dに割り当てられたイベント番号(TVBオーバーフロー時のTA/3D連携のため?)
  • TVBタイル配列の再作成とサイズ
TAマイクロシーケンス

マイクロシーケンスはTAワークによって指し示され、以下のコマンドを持つ。

TAを開始
  • タイリングパラメータ ptr
  • TAワーク構造体2 ptr
  • ヒープマネージャ ptr
  • バッファ記述子へのポインタ
  • 共有GPUのinitdataの配列への何らかのポインタ(?)
  • ワークキュー制御構造体へのポインタ
  • UATコンテキストID
  • その他のコンテキスト関連ID(ヒープmgr ID?)
  • TAワーク構造体3 ptr
  • Execute TAの未知バッファの1つへのポインタ
  • UUID
タイムスタンプ
  • Flag=1
  • 実行TA内の3つのタイムスタンプポインタへのポインタ。#1, #2, #2
  • UUID
アイドルを待つ
  • Args: 1, 0, 0
タイムスタンプ
  • フラグ=0
  • 実行TAで3つのタイムスタンプポインタを指す。#1, #2, #3 (違いに注意)
  • UUID
TAを終了
  • そのバッファのものへのポインタ(Pointer to that buffer thing)
  • ヒープマネージャへのポインタ
  • TA開始時と同じinitdataポインタ
  • 作業キューへのポインタ
  • UATコンテキストID
  • TA構造体3へのポインタ
  • UUID
  • TAスタンプ2へのポインタ
  • 書き込むスタンプの値
  • Start TA micro コマンドまでのオフセット(おそらく部分レンダリング用に再開するため?)
3Dワーク
バリア(スタンプ待ち)

TA が終了するまで 3D をブロックします。部分レンダリングを動作させる黒魔術が何なのかは不明です。

  • TAスタンプへのポインタ #2
  • 待機する値
  • 再び待機する値(範囲?)
  • TAイベントのインデックス(おそらくこれは実際のポールを通知します。)
  • UUID
3Dを実行
  • イベント管理構造体ポインタ
  • タイラーヒープマネージャーポインタ
  • バッファディスクリプターへのポインター
  • 未知の空バッファ
  • TVBタイル配列ポインタ
  • その他の未知の空のバッファ
  • タイムスタンプ 1 ptr
  • タイムスタンプ 2 ptr
  • タイムスタンプ 3 ptr
  • 組み込みの構造体
  • 3Dワーク構造体1
    • いくつかのフロート?
    • デプスクリア値
    • ステンシルクリア値
    • デプスバイアス配列 ptr
    • シザー配列 ptr

(未完成)

phireのM1x GPU infodump

phire-gpu-infodump.md からここに移動

全ての作業はMacOS 12.2搭載のT6000 14インチM1 Maxで行われました。

今のところ、これは大雑把にいってGPUにどのようにワークを送り込むかを見つけるための挑戦です。

UAT iommu (別名: Unified Address Translator、統合アドレス変換器)

m1n1/hw/uat.py に十分に完成したUATの実装があります。

4レベルのページテーブルです:

  • L0: 2エントリ
  • L1: 8エントリ
  • L2: 2048エントリ
  • L3: 2048エントリ

ページは16KB固定サイズです。

(少し奇妙な) レイアウトにより、共有 VM 領域 (0xf80_00000000 以上) は L0[1] に、 コンテキストごとの割り当てはすべて L0[0] に配置され、新しいコンテキスト用に L0 テーブルを簡単に構築できます。

TTBRレジスタは見当たりませんしレジスタもありません。gfx-ascがこの iommu を完全に制御しているようです。 プライベート IO 領域のために自身のページテーブルをセットアップします。

これはセキュリティの意味合いがあり、gfx-asc はすべての物理ページと、(すべてではないにしても) いくつかの MMIOレジスタにアクセスできます。macOSカーネルからのパニックメッセージは"microPPL"がgfx-ascコプロ上で 動作している可能性を示唆しており、macOSでのPPLと類似しており、これはおそらくページテーブルを変更できる唯一の 部品でしょう。

macOS のカーネルには便利なカーネルオプション、iouat_debug=1 があり、このアドレス空間のすべての割り当てと解放を 記録します。

PTEの形式の詳細については m1n1.hw.PTE を参照してください。

GPU 仮想アドレス空間

macOS は(少なくとも私のマシンでは)以下の範囲の GPU VA を使用します:

0x015_00000000: ほとんどのユーザ空間割り当て
0x011_00000000: いくつかの追加のユーザ空間割り当て
0x06f_ffff8000: 不明。単一ページのみ 0xf80_00000000: ASC のプライベート VM 領域。ASC のプライベート VM 領域で、自分自身で割り当て。主にASCファームウェアが含まれる この領域は /arm-io/sgx/rtkit-private-vm-region-base と同じ位置

0xf80_10000000: ASC ファームウェアによってマップされた IO 領域。ASCメールボックスレジスタのみが含まれる 0xfa0_00000000: macos カーネルが物を割り当てる領域 0xfa0_10000000: macOS によってマップされた IO 領域。 ASC 領域、PMRG レジスタ、MCC レジスタへのポインタ (他にも?)

ポインタは符号拡張されることがあるので、以下のような範囲のポインタを見かけることがあります。 0xffffff80_000000000xffffffa0_00000000 といった範囲のポインタを見かけることがありますが、実際には40ビットしかありません。 しかし、実際には40ビットのアドレス空間しかありません。カーネルからのログは通常44ビットを報告しているので、0xfa_000000というアドレスになります。

UATはこのアドレス空間を制御しています。

gfx-asc

ASC インターフェースは、ワークを送り込むための自然なインターフェースのように思われます。

けれども、このインターフェイスには衝撃的なほど少ないトラフィックしかありません。特に私が見てきたDCPと比べると。

エンドポイント

0x0: 標準的な管理、特におかしなところはなし 0x1: 標準的なクラッシュログのエンドポイント 0x20:これをPongと呼ぶ。通常の "pongs"を受信 0x21:これをKickと呼ぶ

クラッシュログ エンドポイント

全体のトラフィック:

RX 0x00120000000000
TX 0x00104fa00c388000

そして、初期化直後に発生

0xfa00c388000 は GPU VA で、単一ページのアロケーションを指しています。 初期化中に繰り返されるパターン(16KBの0xefバイトの繰り返し)で埋められ、その後二度と触れません。

Pongエンドポイント

クラッシュログはこのエンドポイントを "User01 "と呼んでいます

Pongメッセージの数がキックと一致しないので、おそらく名前を間違えたのでしょう。もっと多いかもしれない。 あるいは、GFX ファームウェアが CPU にページテーブルに触れたことを伝えているのかもしれません。

また、このエンドポイントでは、Initエンドポイントの後に、さらにいくつかの初期化が行われます。

メッセージ RX 0x00420000000000: pongです。下位ビットを0以外に設定することはなし TX 0x00810fa0000b0000: 初期化、一度だけ送信

初期化データとしてヌルポインタを送ると、クラッシュログ上で次のようなクラッシュが発生します:

GFX PANIC - Unable to grab init data from host - agx_background(2)

painclogには次のアクティブなタスクが表示されます:

  • rtk_ep_work
  • power
  • agx_background

そして、パニックは agx_background で発生します。 このエンドポイントはagx_backgroundに属しているということでしょうか?

初期化データが提供された後、このエンドポイントにメッセージを送ってクラッシュさせることができませんでした。

Pong初期化

これにはGPU VAも含まれており、CPUによって事前に埋められるデータ構造を指しています:

>> chexdump32(gfx.uat.ioread(0, 0xfa0000b0000, 0x4000))
00000000 000b8000 ffffffa0 00000000 00000000 0c338000 ffffffa0 00020000 ffffffa0
00000020 000c0000 ffffffa0 030e4000 240e0e08 40000008 00000001 00000000 ffffc000
00000040 000003ff 00000000 00000070 190e0e08 40000800 00000001 00000000 ffffc000
00000060 000003ff fe000000 0000000f 0e0e0e08 40000800 00000001 00000000 ffffc000
00000080 000003ff 01ffc000 00000000 00000000 00000000 00000000 00000000 00000000
000000a0 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000000c0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

このControlStructをm1n1/trace/agx.pyのコードで呼び出してみました。

初期化後、CPUはこれに触れることはありません。

unkptr_18 は asc-firmware が使うヒープかスタックのようです?

Kick

クラッシュログはこのエンドポイントを 『User02』と呼んでいます。

メッセージ:

0x83000000000000: TAチャンネルを送信 0x83000000000001: 3Dチャンネルを送信 0x83000000000002: CLチャンネルを送信 0x83000000000010: ファームウェアのkick 0x83000000000011: 機器制御

これらのKickはワークの送り込みをトリガーしているかもしれませんが、5ビットのエントロピーしかないため、実際の情報は共有メモリのどこかにあるはずです。 しかし、現時点では、キック間で変更される共有メモリは見つかっていません。また、私はこれを間違って表示した可能性があり、kickは実際にはTLBの無効化です。

範囲外のkickで 0x008300000000 メッセージを送ってもメッセージタイプ 0x84 と 0x85のクラッシュは起こりません。

チャンネル
チャンネル 0

0x8300000000 で使用: TA チャンネルを送信

00000000 0c000000 ffffffa0 00000002 00000000 00000001 00000000 0C3A8000 FFFFFA0 00000002 00000002 00000001 00000000 0C000000 FFFFFA0 00000003 00000000 00000000 00000000 0C3A8000 FFFFFA0 00000003 00000002 00000000 00000000 0C3A8000 FFFFFA0 00000004 00000002 00000000 00000000 0C3A8000 FFFFFA0 00000005 00000002 00000000

チャンネル1

0x83000000000001 で使用: 3D チャンネルを送り込み

00000001 0C002CC0 FFFFFA0 00000002 00000001 00000001 00000001 0C3AACC0 FFFFFA0 00000002 00000003 00000001 00000001 0C002CC0 FFFFFA0 00000004 00000001 00000000 00000001 0C3AACC0 FFFFFA0 00000004 00000003 00000000 00000001 0C3AACC0 FFFFFA0 00000006 00000003 00000000 00000001 0C3AACC0 FFFFFA0 00000008 00000003 00000000 00000001 0C002CC0 FFFFFA0 00000006 00000001 00000000 00000001 0C3AACC0 FFFFFA0 0000000A 00000003 00000000

チャンネル12

機器制御で使用 - 0x83000000000011

カーネルは gfx-asc を初期化する前に、チャンネル 12 にメッセージを入れます。

メッセージタイプ 0x19

次に

メッセージタイプ 0x23

メッセージ 0x17:

メタルテストを起動するときに表示
チャンネル13

GPU ファームウェアから?

転送は通常の 0x30 ではなく、実際には 0x38 バイトの長さです。

# chan[13]->ptrB (0xffffffa000031f00..0xffffffa0000350af)
ffffffa000031f00 00000001 00000000 00000000 00000000 4b430000 534b5452 4b434154 | .................... CKRTKSTACK
ffffffa000031f20 534b5452 4b434154 534b5452 4b434154 534b5452 4b434154 00000001 00000002 | RTKSTACKRTKSTACKRTKSTACK...............FFFFFFA000031f40 534B5452 4B534154 534BRKSTACK......FFFFFFA000031f40
ffffa000031f40 00000000 00000000 ffff0000 00000080 00000000 000040e8 00000000 | .........................@.
FFFFFFA000031F60 00001180 00021300 000040E0 00000000 00000000 00000000 00000000 | ......@...............................................@........@....@....@...
チャンネル14

チャンネル12への返信?

メッセージタイプ 0x4

チャンネル16

タイムスタンプチャンネル。

これはgpu ファームウェア (または gpu 自体) が持っていたすべての機能またはモードのタイムスタンプを表示する可能性があります。

Type 0xc - 何も起こっていない

タスク

crashlogsによると、gfx-ascは以下のタスクを実行しています。

  • rtk_ep_work
  • power
  • agx_background
  • agx_recovery
  • agx_interrupt
  • agx_power
  • agx_sample
/arm-io/sgx の様々な共有メモリ範囲

共有メモリに関して、これらは明確なものです。ibootによって割り当てられ、ADTにリストアップされています。

gpu-region-base:

UATのL0テーブルを含む単一ページ。CPUによって制御されます。

与えられたコンテキストの L0 は gpu-region-base + context * 0x10 で見つけられます。

gfx-shared-region-base:

初期化中にgfx-ascが自ら割り当てる、すべてのプライベートなページテーブルが含まれます。

ほとんどはgfx-ascによって制御されるが、CPUはPPE L0[1][2] を制御し、自身のメモリ内のL2テーブルにそれを指し示します。

L0[1]のPTEはこの領域の開始を指し示すようです。

gfx-handoff-base:

0x10ffb4000 : u64 - microPPL マジック値 0x4b1d000000000002 0x10ffb4008 : u64 - microPPL マジック値 0x4b1d000000000002

この値を破壊すると、以下のようなパニックが発生します:

panic(cpu 4 caller 0xfffffe0013c5d848): UAT PPL 0xfffffdf030af4160 (IOUAT): 
Invalid microPPL magic value found in the handoff region. 
Expected: 0x4b1d000000000002, Actual: 0x0

0x10ffb4018 : u32 - 一般に u8 として読み込み - 0xffffff に初期化 0x10ffb4038 : u32 - フラッシュ状態 (一般に 0 か 2 に設定される)

CPUはこれを2にセットし、以下の値をいくつか変更した後、0に戻すというパターンを持っています。 これはミューテックス(mutex)ではないでしょうか?

CPUが想定していないときにこれを2に変更すると、次のようにパニックを起こします。

panic(cpu 0 caller 0xfffffe0013b6d8c4): UAT PPL 0xfffffdf0429d0160 (IOUAT): 
    Attempted to call prepareFWUnmap() before completing previous cache flush. 
    flush_state: 2 | start_addr: 0x150e540000 | size: 0x730000 | context_id: 1

0x10ffb4040 : u64 - CPU が GPU の VA をここにときどき書き込む 0x10ffb4048 : u64 - サイズは 0x28000 や 0x8000 のような丸められた数字に設定 0x10ffb4050 : u64 - CPU が GPU の VA をここにときどき書き込む 0x10ffb4058 : u64 - 別のサイズ。

0x10ffb4098 : u64 - 4038 と同じように扱われるが、40a0 に触れる場合 0x10ffb40a0 : u64 - CPU は時々ここに GPU VA を書き込む、私はメタルアプリを実行している時にしか見たことがない
0x10ffb40a8 : u64 - サイズ?

0x10ffb4620 : u32 - ?
0x10ffb4638 : u8 - 0x4038 が変更される前に常にチェック

CPUはこの範囲に興味深いGPU VAポインタを書き込みます。私は長い間、これがGPUへのワークの送り込み方法に違いないと考えていました。 しかし、kickやpongとは関係なさそうです。時々カーネルはポインタを何度も上書きして、その間にkickやpongがゼロになることもあります。 また、この領域で何も変更せずに何百回ものkickを行うこともあります。

私の現在の仮説では、この領域はページテーブルの更新状況を追跡するために排他的に使用されています。 MacOSとgfx-ascの両方からアクセス可能で、ページテーブルの更新のためにアクセスを同期させることができます。

次のパニックメッセージ `GFX PANIC - Host-mapped FW allocations are disabled, but FW only supports enabled`` (initdataの0xa0バイトを0に設定したときに表示) は、この『ファームウェアによるページテーブルの制御』が機能的に有効でないことを示唆しています。 機能的にこのファームウェアのバージョンで実際に有効になっていないことを示唆しています。
運が良ければ、このハンドオフ領域には何も書き込まないようにできるかもしれません。

sgx レジスタ

CPU はこれらのレジスタに書き込まず、読み込むだけです。

これらのレジスタは初期化時に一度だけ読み込まれます:

0x4000 : u32 - バージョン番号? 0x4042000
0x4010 : u32 - バージョン番号? 0x30808
0x4014 : u32 - バージョン番号? 0x40404
0x4018 : u32 - 不明 0x1320200
0x401c : u32 - 0x204311
0x4008 : u32 - 0x40a06
0x1500 : u32 - 0xffffffff
0x1514 : u32 - 0x0
0x8024 : u32 = 0x43ffffe - (これはADTのsgx/ttbat-phys-addr-baseに適合)

これらのステータス・レジスタは、CPU上のsomethingによって継続的にチェックされます。

0x11008 : u32 - 作業が行われるたびに常にカウントアップされます。
0x1100c : u32 - 通常は0です。
0x11010 : u32 - 別の作業カウンタ?
0x11014 : u32 - 使用頻度0

ASCのPongとKicksに比べると、これらのステータス・レジスタが読まれるタイミングに多くの関係はないようです。