|
開発が始まった正確な日付を特定することはできません。次の数カ月間は,何が必要なのかを設計チームが把握し始めていく中で,こまごまとした作業が個別に発生していました。同時に,手順が確立され,それに基づいて開発環境が構築されました。
2001 年末までに,小集団のエンジニアが連携するようになり,一体となって動き始め,勢いが増しました。初版のストリーム・コードの変更が行われたのは 2002年 1 月 15 日でした。
2002 年 2 月 1 日の「The Inquirer」(http://www.theinquirer.net) の ヘッドラインは,我々に対する信頼感が感じられて光栄でした:
「コンパック,Itanic で VMS をブート」
クールです。このときはまだ最初のコード・チェックインの 13 日前でしたが,とにかくこの報道を気持ちよく読みました。
コンパイラ
当初,我々が使用できるのはクロス・ツール環境に限られていました。コンパイラを Alpha 上で実行し,Integrity 用のコードを生成しました。こうした状況が長い間続き,少なくとも,Integrity 上での OpenVMS の実行までに 1 年かかりました。ここでもまた,独創的な考え方が貴重であることが証明されました。
ビルド・コンパイラがすぐに適切なコードを生成していたら,実行可能なシステムにもっと早くたどり着いたことでしょう。C コンパイラ・エンジニアは,プロトタイプの Integrity Linux コンパイラを作成し,生成されたコードと ELF/DWARF の生成を Integrity Linux システムでテストしました。コンパイラに関する Linux の作業のほとんどは,我々が販売していた Alpha Linux コンパイラ用のものがすでに開発されていましたのでこれを利用しました。
2001 年 9 月,Integrity Linux コンパイラの作業が開始し,
コンパイラ・エンジニアたちが,彼らの既存のコンパイラを使ってコード生成の約 95% と,最初のシステム・ブートに必要な ELF/DWARF 生成の大部分をテストしました。
初期のコンパイラ開発で重要だったのは,OpenVMS に必要な組み込み関数を特定する作業でした。コンパイラ・チームはいくつかの既存の Intel コンパイラを調べてその組み込み関数から着手し,オペレーティング・システムに必要なものが明らかになるにつれていくつかを追加しました。MACRO-32 と BLISS に関しては,Integrity に固有の新しいコードは C で書くことになっていたので,組み込み関数はほとんど必要ありませんでした。
OpenVMS は,Alpha 上ではかなりの量の C ASM を使用しています。これにより以下のことが可能です:
- 呼び出し元のリターン・アドレス・レジスタとして R26 を参照
- LOAD-LOCKED / STORE-CONDITIONAL 命令を使用したアトミックなプログラミング
Alpha と Integrity の C コンパイラはどちらも,組み込み関数 __RETURN_ADDRESS および __CMP_SWAP_QUAD を定義していたので,ASM を排除して共通コードを作成しました。
MACRO-32 コードには特別な問題がありました。VAX から Alpha へのポーティングは,(そのように設計したのですが) 呼び出し標準規則がほぼ同じであったため簡単でした。今度は,全く異なる呼び出し標準規則に直面しています。最も大きく異なるのは,Intel の呼び出し標準規則で定義されている汎用保存レジスタは 4 個だけ (R4 〜 R7) であるのに対し,Alpha では 14 個 (R2 〜 R15) だという点です。これは,MACRO-32 コードでの多くの前提条件が,Integrity では適用されないことを意味します。
IMACRO (Integrity 上での MACRO-32 コンパイラ) は,この違いを認識しており,新しいコンパイラ・ディレクティブによって明示的に指定されない限り,危険にさらされる可能性のあるレジスタを保存/復元します。我々は,特にパフォーマンスが重要な処理の場合に,この種のコードを適宜書き直しました。
R0 または R1 以外のレジスタにデータを戻すルーチンへの CALL に対しても,IMACRO が戻された値を上書きしないように,新しいディレクティブによって指定する必要があります。我々は,レジスタの前提条件が有効でない可能性がある場合に,LINKER が診断警告メッセージを提供する手法を考え出しました。最初は手間がかかりましたが,その後これが大いに役立ちました。次に,OpenVMS で使用されているすべてのリンケージを新しいファイル $IA64_LINKAGES で定義しました。このファイルは,ARCH_DEFS.MAR を使って各 MACRO-32 ソース・モジュールに挿入されます。
MACRO-32 のもう 1 つの問題は,CALL の手動によるコーディングでした。つまり,引数を R16 〜 R21 へ移動し,引数の数を R25 に入れ,次に JSBing を標準の CALL ルーチンに置くという作業で,これはもうひどかった! 呼び出し標準規則の違いのために,これらのすべての出現箇所を変更しなければならなかったので,それらのコードを CALL に書き直しました。世の中が完璧だったとしたら,すべてが標準 CALL になっていたはずですが,そのようなことは,OpenVMS 開発の最初の 12 年間は誰も考えもしないことだったのです。ある意味では,「VAX から Alpha へのポーティングでは楽をしすぎた」のです (Alpha から Integrity へのポーティング・プロジェクトの間,これが我々の口癖になりました)。
BLISS グローバル・レジスタは予想外に難関でした。BLISS グローバル・レジスタは,イメージ内のすべてのモジュール (FAB を含む RMS の GLOBAL REGISTER など) で共用するために定義されたレジスタです。これは,どの Intel のレジスタ規約にも合いません。BLISS のコード生成は,「生きた」値が入っている可能性のあるレジスタを認識し,それを保護するように拡張されました。
何年も前に,「The Thing (遊星からの物体 X)」というホラー映画がありました。我々はその子孫の 1 つと思われる NaT (Not a Thing) というものに遭遇しました。NaT は,レジスタの 65 番目のビットです。これが設定されていると,そのレジスタの内容は無効であることを意味します。投機的アクセスの一部として使われます。特殊な命令を使用しない限り,レジスタをメモリに保存できるのは NaT ビットが 0 の場合 (レジスタが NaT でない場合) だけです。つまり,投機的アクセス (変更なし) はできますが変更はできないということです。
これは,IMACRO にとっては新しい概念であり,問題がなくなるまでしばらくかかりました。また,コンテキストの切り替え時に NaT の状態を正しく維持するのは簡単ではないことが分かり,オペレーティング・システムも苦しみました。そしてついに怪物は退治されました。
Itanium アセンブリ言語の学習
我々は,アセンブラ・コードはできる限り書かないという目標を持っていましたが,多少は発生すると分かっていました。アセンブラなしでどう始めればよいでしょう。IAS を OpenVMS にポーティングするまでに数カ月を要します。原因は難易度ではなく,タイミングとスケジュールです。最終的にはオープン・ソースの IAS アセンブラを使い,我々の ELF 拡張をいくつか追加し,比較的小規模の作業となりました。しかし一方で我々は,Itanium1 ProLiant DL590/64 を入手し,Linux をインストールしました。マニュアルを読むのも方法の 1 つですが,3 つの命令で構成されるバンドル,プレディケート,アンワインド・ディレクティブについて本当に学べたのは,Linux システム上で C コードを書いてコンパイルし,生成されたコードを調べることからでした。
実際,以下は最初に Linux 上で GNU C とアセンブラを使ってコンパイルし,デバッグしました。
- SWIS (Software Interrupt Services)
- EXCEPTION
- IVT (Interruption Vector Table)
- TLB (Translation Lookaside Buffer) ミス・ハンドラ
しばらくの間,この重要なコードのコンパイル,リンク,デバッグには, Itanium ハードウェア上で実行する Linux ツールを使用しました。これにより,IPB と SYSBOOT で必要になったときには,OpenVMS の基本機能は準備できていました。
最初に ELF/DWARF を採用すると決定した時点では,初期の開発中にそれがどれだけ役に立つのか認識していませんでした。アプリケーションにとって意味があるとした根拠が,OpenVMS にとっても意味があったわけです。
Alpha PALcode の置き換え
主に,次の 3 つの領域での作業となりました:
- OpenVMS の知識をほとんど必要としない,割り込み,IPL,権限モード管理
- 直接的な PAL ルーチン呼び出し
- メモリ管理
期間短縮と簡便性を考え,我々は,C,IMACRO,BLISS 用に適切なヘッダとライブラリ・ファイルを作成することによって,個々の PAL 呼び出しに対してシステム・サービスを定義しました。これにより作業が迅速に進み,その後のパフォーマンス改善も楽になりました。
SWIS は,OpenVMS を Integrity 上で動作させるための心臓部です。システム内で IPL および権限モードのチェックと操作を必要とするものはすべて SWIS によって制御されます。SWIS ログのデータは,我々が遭遇した重大な問題のデバッグを行う上での頼みの綱となりました。
入り組んだ VAX キュー命令のエミュレーションは,開発の初日から問題となりました。このような命令は多数あり,複雑でもあります。OpenVMS は,これらの命令の多くがないと全く動作しません。
メモリ管理の変更としては,仮想アドレスから物理アドレスへの変換,アラインメント・フォルトの処理,PROBE エミュレーションがありました。我々は,Alpha の仮想アドレス変換および GH リージョンを持つアドレス空間レイアウトを維持する仕組みを,Itanium の基本要素である VHPT を使って考え出しました。
ポーティング・ガイドライン
我々は,コーディング規則を記した簡単なドキュメントを作成しました。我々は,Alpha の V7.3-2 を本格的に開発しながら,Integrity をサポートするためのコードを Alpha のソース・コードに追加していました。
この種の作業を必要としたのはコードそのものだけではありません。共通のソースから,共通のビルド手順で,同じクラスタ上で Alpha と Integrity を対象にビルドを行っていたため,開発環境にも本格的な拡張が必要でした。ポーティングのガイドラインは,プロジェクトのあらゆる面で必須だったのです。この小さなドキュメントは,充実するにつれて我々以外の開発グループにも使われるようになりました。
コンピュータ・プログラマが「バイナリ思考」なのは不思議なことではないと思うかもしれませんが,我々はかなり早い段階で,既存のコード・ベースの一部とビルド環境が,もっと違いを考慮するべきなのに「VAX か Alpha か」と決め打ちするようなアプローチを採っていることに気が付きました。これは,VAX から Alpha へポーティングしたときの我々の状況を考えれば,全く理解できることでした。しかし,それは近視眼的であり,後で多くの手間がかかる要因となります。このような失敗を繰り返すつもりはありませんでした。
我々からプログラマへの最初の注意の 1 つは,「二元論的な条件分岐には注意」し,将来のためにできる限り汎用性のあるものに変更せよ,というものでした。条件分岐はすべて入念にチェックしなければならず,そのうちの多くは変更が必要でした。これは,DCL コマンド・プロシージャや,C や BLISS などに関しても同じでした。このことは非常に重要だったため,条件分岐を 1 つチェックするたびに,コードの変更が必要ない箇所にもエンジニアは「IA64 用に確認済み」というコメントを追加しました。以下にいくつか例を挙げます:
- IF VAX (.....) ELSE (.....) ENDIF
- IF ALPHA (.....) ELSE (.....) ENDIF
- IF VAX (.....) ENDIF の後に IF ALPHA (.....) ENDIF
これらの命令は,3 つ目の選択肢がくると問題になることは明らかです。最初の 2 つの例では,Integrity 上で VAX または Alpha のコードが使われることになります。Integrity 上でコンパイル・エラーまたはリンク・エラーが発生し,プログラマは調整を考えなければなりません。しかし 3 つ目の例は,Integrity 上でエラーは発生しないながらもコードが全く生成されないので,特に問題です。我々は既存の条件付きコードに対して必要な作業量を,明らかに過小評価していました。読みやすい共通のソースを維持するために,我々は頻繁に条件をマクロ内に埋め込みました。
ブート・シーケンスが形になる
2001 年の秋,我々は最初の HP i2000 を購入しました。我々は,まだ COMPAQ に属していましたが,HP との合併提案が発表された直後であり,i2000 があれば新生 HP で
幸先の良いスタートが切れると考えていました。
間もなく,ブート・パスの設計およびコーディングの担当者たちと,定期的な調整ミーティングが必要になりました。第 1 回目の「デバッグ・ミーティング」が 2001 年 11 月 14 日に開催され,その後約 2 年間,週に何回もこのミーティングが続きました。進捗確認のためではなく,我々の無知をさらけ出す間抜けな質問をしたり,特に互いに学び合ったりするためのミーティングで,毎回,どこか愉快なミーティングでした。デバッグ・ミーティングは開発者のみが参加し (マネージャは参加不可),30 〜 45 分程度で仕事に戻りました。何がうまくいき,何がうまく行っていないかや,どうしたら先に進むことができるかを,非常に細かいレベルで話し合うことが目的でした。
2002 年春,我々は,Intel プロセッサ・ベース製品の開発会社に貸し出される,数台の Intel 「ホワイト・ボックス」,すなわち汎用の開発スタート用エンジニアリング・サンプルを入手しました。これらのホワイト・ボックスで,システムの数は 2 倍近くに増えました。さらに,設計と開発に役立つさまざまな実験テクニックを学び始めていました。
議論を進めるに際し,ブート・シーケンスの定義を「ブート・コマンドが発行されてから,必要な execlet (OpenVMS のコアを構成するイメージ一式) をすべて読み込むまでに必要なコード」としました。次のコードがその対象です:
- VMS_LOADER.EFI
- IPB.EXE
- SYSBOOT.EXE
- EXEC_INIT.EXE
コードの説明に入る前に,Intel によって定義されている汎用のブート・パス構造を見てみましょう。
- ブート・デバイス上には FAT (File Allocation Table) パーティションが必要です。OpenVMS には,PC で使われる意味でのディスクの「パーティション」はありません。OpenVMS の FAT パーティションはコンテナ・ファイルです。つまり,システム・ディスク上にあり,特殊な内容とフォーマットを持つ,通常の OpenVMS ファイルです。
- パーティション内のファイルの 1 つがオペレーティング・システムのローダです。このファイルは,Intel Itanium アーキテクチャ・システム上でのブートを目指す各オペレーティング・システム開発グループによって作成されます。
.EFI の拡張子から分かるように,最初に実行するコードは OpenVMS イメージではありません。OpenVMS エンジニアリング・チームによって書かれた EFI (Extensible Firmware Interface) アプリケーションであり,これがシステムを準備して IPB.EXE を読み込み,起動します。IPB は Itanium Primary Bootstrap で,Alpha 上では APB.EXE,VAX 上では VMB.EXE に相当するものです。このローダには,ブート・シーケンス・ロジックに加えてプリミティブなファイル・システムが含まれており,通常のメカニズムが利用可能になる前に,OpenVMS イメージの検索,読み込み,起動を実行できるようになっています。このため,実行される最初の部分のコードも,ELF イメージの内部構造について知っていなければならないのです。
ローダは Integrity 用の全く新しいコードです。
このローダは,IPB.EXE が,Alpha における同等の機能と同じような動作をするための環境を整えるもの,という見方もできます。
VMS_LOADER.EFI は,構成ツリーや HWRPB の作成など,Alpha においてはファームウェアが実行する処理を多数実行します。IPB は,SYSBOOT.EXE のために,Alpha と同じ状態を Integrity 上で実現するためにできる限りのことを行います。
SYSBOOT.EXE を開始した後は,外部インタフェースからは多くのものが Alpha とほぼ同じに見えます (たとえば,システム・パラメータの確認と変更の停止など)。SYSBOOT.EXE はデータ構造体をいくつか作成し,いくつかの execlet を読み込み,最後に EXEC_INIT.EXE に制御を渡します。
この時点で,ブート・シーケンス内のプラットフォーム固有コードの大部分は完了します。EXEC_INIT.EXE は,残りの execlet (通常は 20 数個) を読み込み,その初期化ルーチンを実行し,最後に SWAPPER プロセスに制御を渡します。
このプロジェクトのもう 1 つのテーマは,よほどの理由がない限り「違うものにしない」でした。これは,いくつかの領域で難問になりましたが,特に ACPI とブート・パスとの関係がそうでした。「違うものにしない」ことの 1 つは,HP Integrity 対応の他のオペレーティング・システムが Intel の仕様に従って何かを厳密に行っているのであれば,OpenVMS でも同じアプローチをとる,ということでした。我々は,アーキテクチャやファームウェアの設計者および実装担当者が特別な対処を必要としないように努力しました。システムのブートは,オペレーティング・システムごとに大きく異なる可能性のある領域の 1 つです。具体的に言うと,ほとんどのオペレーティング・システムは,1 つの巨大なファイルを読み込むことによってブートが成り立っているのです。
必要なコードはすべて実行中であるとの認識のもとで,ACPI は,次にすべての構成データを提供します。これは,明らかにこれまでの説明の中で見てきた OpenVMS モデルとは異なります。
原始的なデバッグ作業
デバッガが持つぜいたくな機能やクラッシュ・ダンプをとる能力が得られるまでは,さまざまな形の print 文が,問題を見つけ出す唯一の手段でした。我々はしばらく print 文に苦しみながらも進展はしていました。そこに特効薬の 1 つが実現しました。命令デコーダです。呼び出し可能な命令デコーダと命令フォーマット構造定義は,DELTA,XDELTA,DEBUG,SDA,SCD,PCA など,あらゆるデバッグ・ツールの土台となります。さらに,フォーマット定義はアラインメント・フォルト処理の際に基本オペレーティング・システムによって使われます。XDELTA が機能するようになると,ブレークポイントとシングル・ステップ命令が生産性向上に役立ちました。基盤となるデバッグ・コードのほとんどは,Integrity 用の全く新しいコードでしたが,「古い」ものが 1 つありました。PSR.ss ビットの設定方法が VAX の Tbit にそっくりなのです。ただし Alpha 上よりはずっとシンプルです。
クラッシュ・ダンプが可能になったことは,このようなプロジェクトでは重大な出来事であり,最初の 1 回は全く意図せずに起こりました。何かのコードを進めているときに,ブレークポイントが間違って配置されました。";P" によって ACCVIO が発生し,そして何と,クラッシュ・ダンプが完璧に実行されたのです。コードはコンパイルとリンクが済んでおり,実行されるのを待つだけでした。クラッシュが喜ばしい出来事になった,めったに見られない瞬間でした。
初期の「ビルド」
2002 年の前半中に,かなりの量のコードが書かれ,コンパイラも形になり始めました。いくつかの OpenVMS ソース・コードの機能は,まだ Integrity 向けビルドの初期段階にありました。
Integrity 用のフル・ビルドを行うにはあまりに早い段階だったので,前進するために必要ないくつかの部分に集中しました。初期のうちは,これらのいくつかのイメージだけをビルドして OpenVMS Alpha システム・ディスクにコピーし,それを使ってブートの初期段階をテストしました。少し面倒なやり方でしたが,少数のエンジニアで実行でき,とてもうまく進行しました。業界標準コンポーネントの採用で良かった点の 1 つは,新たにいくつかディスクが必要になったときには,いつでも CompUSA に行って入手できるという点があります。これは OpenVMS の開発にとって,全く新しい概念です。
プロジェクトのこの段階では,OpenVMS の大部分をどのようにビルドするかを司る LIBRARIAN はまだありませんでした。我々は,大きなオブジェクト・ライブラリは作成せず,.OBJ も削除しないようにビルド・プロセスを変更し,次に,すべてのリンク・プロシージャを変更して,.OBJ をライブラリから取り出すのではなく直接含めるようにしました。
当時は LINKER の機能が非常に限定されていたため,OpenVMS を通常とは全く異なる方法で実行せざるを得ませんでした。LINKER は単一のイメージをリンクしますが,ベース・イメージを通じて次々と呼び出される execlet をまだリンクできていませんでした。20 年前 (V5.0 よりも前) に OpenVMS を経験していた人であれば,モノリシックな SYS.EXE のことを思い出すでしょう。それは現在の我々が必要としているものではありませんでしたが,非常に巨大な SYSBOOT.EXE が役割を担いました。
成長するブート・パスのデバッグの非常に早い段階では,通常は execlet にある必要なすべてのコードが,SYSBOOT.EXE にリンクされました。この先祖返りのような方法は長く使うものではありませんでしたが,1 日も無駄にはできませんでしたし,これにより我々は前に進むことができました。
2002 年 6 月 3 日,最初の全体的な Integrity ビルドが発表されました。ブート・パスの作業を担当したエンジニアを除き,一般のエンジニアにとっては,ここがコードのコンパイル結果を見るための実際の出発点でした (大まかに言い換えると,この段階で,MACRO-32 コードが新しい IMACRO コンパイラをうまく通ったことが明らかになった,ということになります)。
HP の一員となった我々は,2002 年 7月,rx2600 の最初の社内向けのハードウェアを受け取りました。プロジェクトは,ほぼ通常どおりに開始されていました。
つまり,定期的なビルドと定例ミーティングが行われ,開発システム数は増加し,いくつかのコードがうまく動作しました。i2000,ホワイト・ボックス,さらに rx2600 が導入され,重要な作業を行うそれぞれの開発者に,独占的に使用できるシステムが行き渡りました。実装担当者の数が増えるにつれて,もっと優れた,多数のプロシージャが必要になりました。
Integrity 上でまだネットワーク機能がなかったため,開発クラスタから Integrity システムへ手早く簡単にファイルを移動する手段が必要でした。我々は HP ディスク・システム 2100 (「ピザ・ボックス」あるいは「パンケーキ」などと呼んでいました) を持ってきて,これを小さな OpenVMS Alpha システムと 6 台の Integrity システムに接続し,同じ構成をいくつも再現しました。当然これは完全にサポート対象外の構成でしたが,マウントとマウント解除を慎重に行うことでうまく動作しました。我々は開発クラスタ上でビルドし,生成されたキットを「サーバ」の Alpha にコピーしました。各開発者は,指定されたディスク上でシステム・ディスク・ビルドを実行し,それをマウント解除し,Integrity システムをブートしました。
この頃から,再配置データ,アンワインド・データ,命令の数により,Integrity 上のイメージ・サイズがどれほど大きくなるのか認識し始めました。それに加え,現場のデバッグを支援するために作成すると決定したトレースバック情報もあります。その結果,Integrity 上の OpenVMS イメージ (アプリケーション・ソフトウェアとオペレーティング・システムのイメージ) は,Alpha 上の対応するイメージの約 3 倍の大きさでした。
|