コンテンツにスキップ

Apple Silicon MacでのオープンOSエコシステム

2025/4/7時点のopen-os-interopの翻訳

訳注: プロジェクトページへのリンクは対応する日本語訳のページに置き換え


序文

この文書は、Apple Silicon (M1 以降) Mac 上でオープンな OS がどのように相互運用されるべきかについての我々のビジョンを 示すものです。まず、Apple Siliconの紹介 を読み、Apple の観点からこのプラットフォームがどのように設計されているかを 学ぶことをお勧めします。

この文書にある考え方は、厳しい要求や規則を設定することを意図したものではありません。むしろ、エンドユーザーが異なるOSを 共存させ、インストールしやすくするための一連の標準に合意し、可能な限りシンプルでシームレス、かつ将来性のある プロセスを目指したいと考えています。

この文書はドラフトであり、これらのプラットフォーム上でオープンなOSのエコシステムを構築する方法の将来を形作るのを助けるために、 すべてのコメントと議論を歓迎します。一般的な質問やフィードバックがある場合は OFTC の #asahi に、開発者で技術的な議論を したい場合は #asahi-dev にアクセスしてください (さらなる情報)。

OS のレイアウトと起動

Apple Silicon機器のOSは、Appleのツールで見ると、APFSコンテナパーティションの一部を意味します。機器はネイティブに マルチブートに対応し、プラットフォームのセキュリティ設計に適合するため(例:SEPは起動したOSを知るべき)、 インストールされたOSとプラットフォームから見たOSを1対1に対応付けることを推奨します。

サードパーティ製 OS については、OS ごとに以下の GPT パーティション構造を提案します:

  1. APFSコンテナパーティション(『スタブmacOS』)(〜2.5GB)
  2. iBoot2、ファームウェア、XNU カーネル、RecoveryOS (すべてプラットフォームが要求するもの)
  3. fuOS カーネルとしてのm1n1、チェーンローディング設定は EFI パーティションを指す
  4. 空のroot/dataファイルシステムサブボリューム
  5. EFI システムパーティション (FAT32) (~512MB):
  6. m1n1ステージ2 + DeviceTree + U-Boot
  7. GRUBもしくは他のUEFI OSローダーがターゲットカーネルを起動
  8. root/boot/等のパーティション(OS固有)

根拠(Rationale):この配置は、Appleのツールから見たサードパーティ製OSとAPFS常駐OSを対にし、ユーザーがネイティブブートピッカー (a11y対応付き)を使用できるようにします。これは、複数のOSが共有OSのコンテキストでSEPを管理しようとすることで 発生する可能性のあるトラブルを回避することができます。また、OS のセキュアブートチェーンも独立させることができます (実装されれば)。fuOS イメージは後続のブートステージのための信頼のルートを含み、インストール時にユーザーが機器 所有者の認証情報を使ってマシンの信頼のチェーンに橋渡しされます。

1つのAPFSコンテナを複数のOS(さらにはmacOS)で共有することは可能ですが、OSごとにわずかなディスクスペースを節約する 以外には、あまり意味がないでしょう。インストールした OS ごとに個別のコンテナパーティションを使用することで、 ワイプして最初からやり直すことが容易になります。そうでなければ、特定の APFS サブボリュームを削除したり、 他のディレクトリツリーをワイプするなど、より複雑なクリーンアップ処理が必要になります。

この設計では、従来とは異なり、インストールされたOSごとにEFIシステムパーティション(訳注: EFI System Partition、ESP)を用意します。1つは、各OSは 論理的に『コンテナ』であり、EFIの実装自体も含まれているため、他のOSのESPと分離することが理にかなっているからです。 第二に、EFI可変ランタイム・サービス(後述)がないため、共存する複数のOSがESPを共有し、それぞれのEFIブート・エントリを 設定することが困難であることです。別々のESPを持つことで、デフォルトのブートパス(EFI BOOT</BOOTAA64.EFI) を 使用するだけで、ブート設定を持続させる必要がありません。また、ESP を OS の /boot パーティションとして直接使用 することができ、複数の OS が互いに衝突することもありません (必要な場合)。

将来的には、オープン APFS ドライバがrootファイルシステムとして使用できるほど安定していると判断されれば、macOS との完全な空間共有共存をサポートしたいと考えています。その時点では、既存の APFS macOS コンテナの上に EFI システム パーティションだけが必要になるでしょう。

複数のESPが存在するため、OSはどれが自分たちのESPであるかを把握する方法が必要になります。一般的なマウントとブート目的では、 パーティション/FS ID で可能です。これは、インストール時にブートローダをどの ESP にインストールするかを決める必要がある OS インストーラにとって、ほとんど関わることでしょう。この目的のために、m1n1 ステージ 1 は通常インストール時に、EFI システムパーティション PARTUUID 値を含む asahi,efi-system-partition という名前の DeviceTree /chosen プロパティを提供(および転送)するように設定されます。

ブートの概要

リファレンス Linux システムの典型的なブートは、ブートフローセクションの続きで、次のようになります。

  • iBoot2 が m1n1 のビルドであるカスタムカーネルをロード
  • m1n1ステージ1を実行
  • Apple DeviceTree (ADT) を解析してマシン固有の情報を取得
  • ハードウェアを追加初期化(機器固有)
    • 例:メモリコントローラの詳細、USB-C給電、HDMIディスプレイ(Mac Miniの場合)
  • 画面にロゴを表示(アップルロゴに置き換わる)
  • 組み込み構成をロードし、FAT32パーティションからチェーンロードするように指示
  • NVMeコントローラーを初期化
  • チェーンロード用に設定されたパーティションをPARTUUIDによりGPTから検索
  • パーティションをFAT32としてマウント
  • チェーンロード用に設定されたファイル名を検索しロード
  • NVMeコントローラをシャットダウン
  • ロードされた m1n1 のインスタンス(生のバイナリ blob として)にチェーンロードし、その組み込み構成で見つかった /chosen プロパティ構成を転送
  • m1n1ステージ2を実行
  • Apple DeviceTree (ADT) を解析してマシン固有の情報を取得
  • ハードウェアの再初期化、ステージ1が行わなかったことも含む(例:より古いため)
  • DeviceTreeと埋め込みU-Bootイメージを見つけるために、その埋め込まれたペイロードを検索
  • 現在のプラットフォームに適した組み込みDeviceTree(FDT)を選択
  • ADTから移植された動的な情報でFDTをパーソナライズ
  • その他ハードウェアの初期化を行い、Linuxのためのマシン環境を準備
  • 組み込み用 U-Boot イメージをロードし、そこにジャンプ
  • U-Bootを実行
  • FDTをパース
  • 入力用キーボードを初期化
  • NVMeを初期化
  • 要求があればシェルに入るようユーザーに促す
  • 適切なEFIシステムパーティションをマウント
  • 基本的なEFIサービスを起動
  • ESP 内のデフォルト EFI ブートローダー(例:GRUB)を検索しブート
  • GRUBを実行
  • EFI ディスクアクセスサービスを使用して /boot ファイルシステムをマウント(ESP 自体である可能性もあるし、他のものである可能性もあり)
  • 設定ファイルおよび追加コンポーネントを検索
  • EFIコンソール/入力サービスを使用して、ブートメニューをユーザーに提示
  • カーネルとinitramfsを/bootからロード
  • カーネルにジャンプ
  • Linux カーネルは、他の UEFI+FDT プラットフォームと同様に起動

このブートチェーンは、システムを徐々に『典型的な』ARM64マシンに近づけていくように設計されており、後続のレイヤーは Apple Silicon機器の特殊性をあまり気にする必要がなくなります。

m1n1

m1n1 は Apple Silicon システムのための第一段階のブートストラップです。その目的は、XNU ブートプロトコルと DeviceTree / ARM64 Linux ブートプロトコルの間の橋渡しをし、後続のブートステージがそれを気にする必要がないように、 ローレベルのbring-upを行うことです。動作の詳細は [[m1n1:ユーザガイド]] を参照してください。

m1n1 は開発およびリバースエンジニアリングの目的で USB 経由で操ることもでき、カーネルをロードして非常に高速な ビルド・テストサイクルを可能にします。また、LinuxやmacOSを起動し、USB経由で仮想化されたUARTを提供できるベアメタル ハイパーバイザーや、Pythonベースの高度なイベントトレーシングフレームワークも備えています。これらの機能はエンド ユーザ向けではありませんが、これらのプラットフォームでのOSの開発やテストを少しでも楽しくしてくれることを願っています。

m1n1はシングルステージとしてインストールできますが、プロダクションシステムでは2つのバージョンに分割し、ステージ1のビルドは EFIシステムパーティションから第2ステージをチェーンロードする必要があります。これは、ステージ1が1TRでブート することによってのみユーザーによって変更可能であり、他のOSによって直接更新されることがないため重要です。 NVMeから第2ステージをロードすることで、そのペイロードも含めて更新可能にすることができます。

将来的には、ステージ2のチェーンロードは、署名されたm1n1イメージを介して、セキュアブートチェーンを維持することができるように する予定です。このため、FAT32からステージ2をロードするための現在のチェーンロードコードは、セキュアブート攻撃面の一部で あるため、Rustで記述されています。これにより、コードのその部分で悪用可能なメモリ安全性のバグが発生する可能性を本質的に 排除しています。このため、署名検証もRustで実装される予定です。検証用の公開鍵は、ステージ1のインストール時に設定され、 その特定のOSコンテナのステージ2ビルドを提供することが期待されるエンティティのものとなります。

U-Boot

U-Bootは、ローカル(キーボード)ユーザーとの最初の対話のポイントを提供し、USBやその他の外部デバイスからのブートに 対応します。また、プラットフォームの仕様を隠す EFI サービスを実装し、典型的な UEFI 機器のように見えるようにします。

注意すべき点は、U-Bootは特に有用な EFI ランタイムサービスを提供できないことです。このプラットフォームは、EFI可変ストアを 持たず、例えばNVMeアクセスを実行中のOSと共有することは現実的ではないため、実行中のOSからEFIブートコンフィグに変更を 加えることはできません。その代わりに、設定ファイルを直接変更して変更する必要があります。これは異なる OS が異なる ESP を使用する場合には問題にはなりません。

GRUB

GRUBはLinuxの最終的なロードを行い、ユーザーにおなじみのカーネル選択とオプション編集メニューを提供します。GRUB は EFI サービスに完全に依存しているため、Apple Silicon機器で動作させるためのパッチは必要ありません。ここで何を 使うかはOSディストリビューション次第であり、GRUBは単なる一例です。

ブートスキーム

インストールされたOSは、m1n1ステージ2以降のブートチェーンを管理しているため、自由に管理することができます。例えば、 Linux ディストリビューションは Linux カーネルを m1n1 ステージ 2 に直接追加したり、U-Boot を直接使ってカーネルを 起動したり、Linux EFI stub や GRUB を使ったりすることが可能です。

USB 経由で起動する OS インストーラは、Asahi Linuxインストーラ の UEFI onlyセットアップモード (そのような m1n1 stage 2 + U-Boot set upをインストール) で動作させたい場合は、FAT32 EFI System Partition から標準 UEFI boot protocol を使う必要があります。インストーラはインストールの一部としてこれを別のメカニズムで上書きする こともできますが、正当な理由がなければお勧めできません。Asahi Linux インストーラがインストールしたセカンドステージ ブロブを残しておけば、プラットフォームの下位互換性が十分にあれば、新しいDeviceTreeを提供して特定の OS リリースが まだ対応していない機器でのブートを可能にすることができます。

典型的な m1n1+U-Boot 方式を採用している OS では、ESP で既存のバージョンを確認し、OS が提供するパッケージが 古い場合は自動的にダウングレードを拒否することが推奨されています。これによっても、将来のハードウェアがインストーラの 更新だけで部分的にサポートされることが可能になります。TODO: このバージョンチェックがどのように機能すべきかを 指定します (m1n1 ビルドのタグ付けを適切に開始する必要があります)。

初期インストール

これらの機器では、サードパーティのカーネルが起動する時点 (iBoot2後) と、必要な OS ごとの起動コンポーネント (iBoot2 本体、ファームウェア、その他のファイル、および recoveryOS イメージとそのための XNU カーネルなど) の間に ミスマッチがあります。さらに、Appleのツールでは、OSの選択メニューで正しく動作させるためにこれらのファイルを Prebootパーティション内に特定の方法でレイアウトする必要があり、iBoot1/2自身が課す要件を超えています。事実上、 新しいOSコンテナを作成するには、実質的にrootファイルシステムを除いたmacOSをインストールする必要があります。

ありがたいことに、必要なコンポーネントはすべて よく知られた 安定したURLで公開されている 復元イメージ (IPSW ファイル) から取得することが可能です。私たちはこのプロセスを asahi-installerという、1TRから実行することを想定した Pythonベースのインストールフレームワークで実装しています。これは IPSW ファイルの必要なビットをストリームし、 完全なダウンロードを避け、recoveryOS とそれに続くカスタムカーネルを起動するために必要なパーティションとコンテンツを セットアップします。

私たちは OS がこの特別な車輪を再発明したいとは思っていません (信じてね、そうしたくないでしょう) ので、インストーラを 異なるインストールフローの起動に対応できるよう柔軟にしたいと思います。

インストーラーの起動

現時点では、Asahi Linuxのインストーラは純粋なオンラインインストーラ(curl | shスタイル)としてサポートされており、 macOSまたはrecoveryOSから起動することができます。インストールするOSは、オンデマンドでダウンロードされます (中間ストレージを介さずにターゲットディスクに直接ストリーミングされます)。あるいは、ユーザは UEFI ブート環境のみを インストールすることもできます。この環境では、標準的なメカニズムを使って USB から任意の OS をブートでき、OS インストーラなどが使用するパーティションされていない領域が残ります。

将来的には以下のようなインストールオプションが考えられます:

  • USB netinstall イメージ/バンドル、インストーラを「ブータブルインストールメディア」としてセットアップ。 これは USB ドライブの FAT32 パーティションにいくつかのファイルを解凍するだけでセットアップできるので、ユーザに とって使いやすく、ブートピッカーからインストーラを選択できるようになる (この魔法の仕組みについては より詳しい情報 を 参照)。この場合でもインターネットからインストールするOSを取得
  • USB ローカルインストールイメージ/バンドルは、あとであるいは他のプラットフォームのための UEFI インストールメディアと しても機能する。これは USB からターゲット OS をインストールするが、Apple の CDN を使って Apple のコンポーネントを 取得するため、本当のオフラインインストールではない
  • エンドユーザが USB ドライブからスクリプトを実行するなどして Apple コンポーネントを追加し、完全にオフラインにする オプション
  • エンドユーザが USB インストーラを作成する際に、Apple コンポーネントを追加するオプション
    • これをオンラインインストーラの機能として追加したい。例えば、実際にインストールを行う代わりに、 『起動可能な USB インストーラを作成する』
  • macOS アプリとしてのパッケージング (これはいずれにせよ USB インストールモードの一部として既に存在)
  • ブラウザから『普通に』ダウンロードした場合、署名のないダウンロードでGateKeeperの問題が発生...
ファームウェアのプロビジョニング

Apple Silicon機器は、動作するために多くのファームウェアブロブに依存しています。これらの大部分はサードパーティのOSが 起動するまでにすでにロードされていますが、ロードされていない小さなサブセットも存在します。これらのブロブには再配布可能な ライセンスがないため、ブロブにアクセスする必要があるOSにとって問題となります。ありがたいことに、Blobは初期インストーラが 作成するIPSWファイルの中で利用できます。私たちは、起動/インストールされたOSにこれらのブロブを渡すための 『ベンダーファームウェア』メカニズムを提案します。

注:現在、インストーラには、すべてのファームウェア raw を ESP 内の別の場所にダンプするハックがあるため、ユーザーが 残りの部分をこの仕様に準拠した形式に抽出できるようなスクリプトを提供することができるようになっています。これは すべての抽出が終わると解消され、一時的なアドホックなものとして意図されたものです。

パッケージ化されたブロブ

現在、次のブロブがパッケージ化されています: * Broadcom FullMAC WiFiファームウェア * Broadcom Bluetoothファームウェア * ASMedia xHCIファームウェア * Apple MTP マルチタッチファームウェア (M2マシン)

これらのブロブはまだパッケージ化されていません: * AVD (Apple Video Decoder) Cortex-M3 ファームウェア

Broadcom FullMAC WiFi ファームウェアの命名に関する詳細: https://lore.kernel.org/all/20220104072658.69756-10-marcan@marcan.st/

VendorFW パッケージ

スタブOSのインストーラーはIPSWから利用可能なプラットフォームファームウェアを収集し、cpioアーカイブとしてパッケージ化します。 cpioファイルは次のものを含みます:

  • vendorfwサブディレクトリ以下の/lib/firmware 階層形式のファームウェア (例: /vendorfw/brcm/foo.bin)
  • firmware.tar: ファームウェアを含む Tarball。/lib/firmware 階層と互換性のある構造 (例: brcm/foo.bin)

  • /vendorfw/.vendorfw.manifest: 以下のの2つの形式の行を含むテキストファイル:

  • LINK <src> <tgt> : ハードリンク
  • FILE <name> SHA256 <hash>: ファイル

このcpioは firmware.cpio と名付けられ、EFIシステムパーティションの vendorfw ディレクトリの下に配置されます。

OSの処理

理想的には、ブートローダが firmware.cpio アーカイブを初期の initrd として直接ロードし、起動した OS が競合状態や複雑な問題を引き起こさずに ファームウェアにアクセスできるようにすることです。しかし、この仕組みは直接インストールされたOS を起動する場合にのみ実用的で、 SB からインストーラを起動する場合はそうでないでしょうし、すべてのブートローダで動作するとは限りません。

OSはinitrdがブートローダによってロードされたかどうかを確認すべきです。そうでなければ、以下のアルゴリズムを使って initrd を 見つけ、ロードする必要があります:

  • asahi,efi-system-partition /chosen Device Tree プロパティを探索し、ESP UUID (上記参照) を見つける。見つからなければ中断
  • 内部 NVMe ドライバをロードし、デバイスが利用可能になるまで待機
  • 上記で取得した UUID を使用して ESP を見つける
  • ESPをマウント(読み取り専用でかまわない)
  • /vendorfw/firmware.cpio ファイルを見つける
  • 必要に応じて伸長するか利用可能にする

OS は、ロードされたファームウェアが現在のブートの間はメモリ内に保持されることを確認する必要があります。

Linux特有の話

Linuxに対して、/lib/firmware/vendor を Linux カーネルのファームウェアロードのパスリストに 追加するパッチを提案します。 これにより、vendorファームウェアをディストリビューションが管理するファームウェア (例:Linux-firmware) から分離したままにでき、 tmpfs や他のマウントに格納してrootファイルシステム(これは不変の可能性あり)から分離できることを意味します。また、必要であれば、 linux-firmwareでインストールされたファームウェアを上書きすることもできます(私たちはこれを想定していませんが、必要なときに オプションを持っていると便利です)。

ディストリビューションは、初期にロードされたファームウェアをカーネルが直接ロードできるように、/lib/firmware/vendor -> /vendorfw シンボリックリンクを持つ initramfs を出荷すべきです。可能であれば、ブートローダが CPIO を直接ロードするようにすべきです。 しかし、これは、外部ブートシナリオや、/boot が直接 ESP でない場合には、難しいかもしれません。フォールバックシナリオに 対応するために、いくつかの要件があります:

  • ファームウェアを必要とするすべてのドライバは、モジュールとしてビルドする必要あり
  • ファームウェアはudevが起動する前に展開されロードされる必要あり。これはudevが任意にモジュールのロードとデバイスのプローブを 引き起こすことができるからで(直接トリガーされていなくてもinitramfsが実行中にカーネルは例えばPCIデバイスを発見可能)、 これにより必要なときにファームウェアが利用できない競合状態が発生

それから、initramfsはこのファームウェアを最終的なrootファイルシステムに転送しなければなりません。このために推奨される仕組みは、 対象のrootファイルシステムツリーの下の /lib/firmware/vendor に tmpfs をマウントして、そこにファームウェアをコピーすることです。

Linux 用の実装例は asahi-scripts リポジトリで見ることができます。

もし /boot が ESP のマウントポイントであれば (つまり GRUB とカーネルが ESP に直接インストールされていれば)、 純正 GRUB で直接 CPIO ロードすることが可能で、 /etc/default/grubGRUB_EARLY_INITRD_LINUX_STOCK=vendorfw/firmware.cpio を使用します。

注意: この文書の 古いバージョン では、rootファイルシステム上のファームウェアの tarball とインクリメンタルアップデートによる代替機構を提案していました。これはエラーが起こりやすく、initramfs が関与していないときには不十分であり、 不変root セットアップ(immutable-root setups)と互換性がないので、現在は非推奨です。