2012年10月14日作成

Plan 9カーネルのブートまわり自分用まとめ

Plan 9のブートまわりは、思ったよりいろいろな動きをしていたので、 調べた範囲でメモ。

ブート全体の動き

Plan 9のブートは、カーネルだけじゃなくて、 bootというプログラム(ソースは/sys/src/9/bootあたり)が動いています。 bootには引数として、plan9.iniのbootargsがbargc, bargvになって渡されます。 何をしているのかというと、最低限必要なファイルツリーを作ったり、 factotumなど必要なプロセスを立ち上げているようです。

もう少し細かくいうと、bootは、boot/boot.c:authenticate()の中で factotumを実行します。このとき、カーネルコンフィグ(CONF=pcauthとか)により 呼び出し方法が変わります。 具体的には、cpu/authサーバならコンフィグにcpuflagが立つのですが、 cpuflagが立っていると、bootはfactotumを-Sオプション付きで実行します。 その結果、factotumはサーバモードになって、nvramを読むようになります。

また、bootが実行するfactotumには、条件は忘れましたが-aオプションにより plan9.iniのauth=値が認証サーバのIPアドレスとして渡されます。 auth=がなければブート時に訪ねられます。たとえばILの場合は、 オプションとして"-a il!xxx.xxx.xxx.xxx!566"となります。

次に、bootはルートファイルシステムをマウントします。 ここで面白いのは、まだルートファイルシステムを得ていないのに すでにbootがfactotumを実行しているのですが(もっと言えばboot自体もですね)、 じゃあこのfactotumはどこから持ってきたのかという話。

これは、カーネルコンフィグのbootdirセクションに挙げたファイルを、 カーネルが最初からもってビルドされているようです。 実際に/bootをみれば、ビルドした時点のファイルが入っているはずです。

ルートファイルシステムのマウントに戻すと、 bootは、plan9.iniのbootargsがlocal!で始まっていない場合、 bootargsの値を使ってipconfigを実行し、ネットワーク設定を行います。 次に、bootはネットワークからファイルシステムをマウントしようとします。 ファイルサーバのIPアドレスは、plan9.iniのfs=を使って、 もしfs=がなければブート時に入力を受け付けます。 ファイルサーバは、それが認証を必要とするなら、マウント時に接続元へ伝えます。 fauth()の結果が0以上なら認証が必要です。

あまり使わないと思いますが、Ken fsの場合、 以下のコマンドで認証が不要になります。

fs: flag authdisable

再度有効にしたい場合は同じコマンドをもう一度。

bootは、認証が必要と分かったので、factotumを経由してp9anyプロトコルで 通信をはじめます。p9anyはそれ自体が認証をするわけではなく、 どの認証プロトコルを使って認証するかを決めるためのものです。 このプロトコルはとても簡単で、 サーバ(この場合はファイルサーバ)が理解できる認証プロトコルをリストで返して、 共通して使えるものをクライアント(ブート中のシステム)が選択するだけです。 ファイルサーバをマウントする場合はだいたいp9sk1が選ばれます。 p9sk1はPlan 9の共有鍵認証なのでまあ普通ですね。

具体的な認証プロトコルが決まったら、p9anyはリレー状態に入って、 あとはp9sk1が認証を行います。このあたりは、factotumのソースコードの p9any.c, p9sk1.cあたりに書かれています。

plan9.iniにfactotumopts=-dと書いておくと、bootがfactotumを起動するときに 引数として渡してくれているので、認証の動きが見れて楽しい。

続いて、factotumはp9sk1で共有鍵認証を開始しますが、 ブートしている対象(ファイルサーバからみて接続元)が認証サーバの場合、 ファイルサーバは認証しろと言っているのに、認証サーバが立ち上がりきっていなくて、 認証するための情報(/admやkeyfs)はファイルサーバにある、という状況になります。 具体的には、(ILの場合)il566をlistenするのはcpurcの中なので、 この時点ではまだlistenしていません。この場合、誰が認証するのか、という話。

答えは、認証サーバとなるサーバのfactotumが、自分で認証チケットを作っています。 認証サーバとなるべきサーバは、bootから-aオプションで渡されたIPアドレス(自分)と 通信しますが、このときlistenをしていないので、il566はrejectします。 なのでil.cのilrejectが呼ばれて、接続拒否されます。 factotumは接続拒否を受けて、かつ自分自身がブート中だと判断されれば、 自分でチケットを生成して、そのチケットで認証を行います。 チケット生成にはサーバキーを使っているんでしょうけど、詳しくは追ってません。 ソースコードでいうと、factotum/util.c:_authdialあたり。

その後、9dosとかinitとかを実行して、cpurcへ進みます。

カーネルコンフィグ

上でちょっと出たので、カーネルコンフィグについて少しメモ。

dev, link, misc, ipセクション

普通にデバイスドライバのソースコード名。 書けばビルド対象ファイルに含まれます。

bootセクション

boot/mkboot, port/mkextractを使って、Method構造体の配列を作る。 文字列"xx", configxx, connectxx(xxはbootセクションの各行)な関数と、 2個目のフィールドを持つ構造体。

Method構造体の配列は、plan9.iniに書かれているbootargsと比較して、 bootargsの最初の'!'まで、またはMethod分だけ一致したものを使います。 plan9.iniにnobootpromptがあれば、bootargsの代わりに nobootpromptの値を使います(boot/boot.c:rootserver())。

それと、bootセクション開始行はいくつかパターンがあり、非常にわかりづらい。

confcpuflagbootprogbootdiskrootdir
boot cpu1boot#S/sdC0//root
boot cpu boot xxxx1bootxxxx/root
boot rootdir xxxx0boot#S/sdC0/xxxx
boot bboot1bboot#S/sdC0//root
boot romboot1romboot#S/sdC0//root
boot dosboot1dosboot#S/sdC0//root
boot boot xxxx0bootxxxx/root

cpuflagは、1ならfactotum -Sで動作します。

plan9.ini

ブートにかかわるplan9.iniのエントリ。

bootargs
ネットワーク設定と、どこからルートをマウントするかなど
nobootprompt
bootargsに代わって、プロンプトを出さない版
fs
マウントするファイルサーバのIPアドレス
auth
認証サーバのIPアドレス
factotumopts
bootが実行するfactotumに渡すオプション
debugboot
1なら、デバッグ出力を有効にする