Operating Systems Development Series | |
Enabling A20
はじめにようこそ!:) 前回のチュートリアルでは、プロセッサを 32 ビットモードに切り替える方法を説明しました。 また、最大 4 GB のメモリにアクセスする方法を学びました。これは素晴らしいことですが、どうやって? また、PCはリアルモードで起動しますが、このモードでは16ビットレジスタ、つまり16ビットセグメントアドレッシングという制約があることも覚えておいてください。このため、まだ1GBのメモリにさえアクセスできない。1MBの壁さえも越えられない。どうすればいいのか?20番目のアドレスラインを有効にする必要があります。これには直接ハードウェアプログラミングが必要なので、それについても説明します。 というわけで、メニューはこんな感じです。
C言語などの高級言語を使用する場合、1MB以上のメモリにアクセスできることが重要なポイントになります。このため、A20 (アドレスライン20) を有効にすることが重要です! 注意:まだ1MB以上のメモリにアクセスできないことを忘れないでください。トリプルフォールトの原因となります。 また、このチュートリアルでは、ハードウェアの直接プログラミングを行うため、これまでのチュートリアルより少し複雑になります。心配しないでください - この後、カーネル用のデバイスドライバを開発するときに、直接ハードウェアプログラミングをより経験することができます。 準備はいいですか? 準備完了ここまでお付き合いいただいた方々は、OS開発がいかに大変なものであるかをご存知だと思います。しかし、私たちはまだ難しいことに手をつけていません。ここで紹介するのは、まだ基本的でありながら、かなり高度な概念ばかりです。しかし。これからもっともっと難しくなっていくのです。すべてのコントローラは、正しく動作するために特別な方法でプログラムされる必要があります。例えば、ハードディスクを書き込む(または読む)には、まずそれがIDEかSCSIかを判断する必要があります。そして、IDEとSCSIの接続を制御するIDEコントローラまたはSCSIコントローラを使用して、ドライブ番号を決定し、プログラムを作成しなければなりません。この2つのコントローラはそれぞれ異なるものです。 さらに複雑なことに、「セクタ」は512バイトとは限りません。従って、「セクタの読み書き」は曖昧です。 次に、メモリ管理とフラグメンテーションがあります。ここで、ページング、仮想アドレス空間、メモリ管理ユニット(MMU)が登場します。 ドライブの読み書きは、他のドライブとは全く異なります。典型的なフォーマットとファイルシステムはメディアによって異なるので、FAT12フロッピーから起動するコードは、CDFSファイルシステムのCD ROMを起動するためには動作しません。ハードウェア固有のコード(と低レベルのコード)を抽象化することで、ほとんどのコードをこれらのデバイスで動作させることができます。 ハードディスクにファイルを書き込む」と言うとき、通常、「ファイル」とは何かを定義したくはないでしょう。これが、抽象化が非常に重要な理由です。 ここにあるものはすべて保護モード(つまり32ビットコード)用のものですが、リアルモードでも同様に動作します。このため、プロテクテッドモードのルールを覚えておいてください。
カーネルデバッグデバッグは芸術です。デバッグは芸術であり、問題を捕捉し、深刻な事態になる前にソフトウェアのエラーを修正する方法を提供するものです。カーネルデバッグは、カーネルレベルのリング0プログラムのデバッグに関係します。 これは決して簡単な作業ではありません。高レベル言語におけるデバッガCやC++のような言語のデバッガは、実行時に変数名やルーチン名、その値や場所などを表示する方法を提供しています。しかし、問題は私たちのプログラムには、まだシンボリックネームがありません。まだバイナリレベルで動いているのです。ダイレクトハードウェアプログラミング - 理論編オペレーティングシステムの開発では、ここからが大変なのです。「ハードウェアの直接プログラミング」とは、個々のチップと直接通信する(制御する)ことを指します。これらのチップが(何らかの方法で)プログラム可能である限り、我々はそれらを制御することができます。 チュートリアル7では、システムがどのように動作するかを非常に詳細に見てきました。 また、ソフトウェアポートがどのように動作するか、ポートマッピング、IN命令とOUT命令について話し、x86アーキテクチャにおける一般的なポートマッピングの大きな表も示しました。 プロセッサがIN命令やOUT命令を受け取ると、コントロールバスの I/Oアクセスラインが有効になることを覚えておいてください。システムバスはメモリコントローラと I/Oコントローラの両方に接続されているため、両コントローラはコントロールバスの特定のアドレスとアクティブなラインを聞き取る。I/Oアクセスラインがセットされていれば(電気が通っている、つまりアクティブ(1))、I/Oコントローラはそのアドレスを取得する。 I/Oコントローラは、次に他のすべてのデバイスにポートアドレスを与え、コントローラチップからの信号を待ちます(それが何らかのデバイスに属していることを意味します - だから、そのデバイスにどんなデータでも与えます)。もし、コントローラチップからの応答がなく、ポートアドレスがセットバックされると、それは無視される。 これがポートマッピングの仕組みです。(詳しくはチュートリアル7を参照してください)。 また、1つのコントローラチップに複数のポートアドレスを割り当てることができることも覚えておいてください。 ポートアドレスは、BIOSがロードされ実行される前でも、BIOS POSTによって割り当てられます。 なぜでしょうか?多くのデバイスは、さまざまな種類の情報を必要とします。あるポートは「レジスタ」を表し、他のポートは「データ」または「レディ」を表すかもしれません。醜いことです、わかっています。しかし、もっと悪いことがあります。システムによって、ポートアドレスが大きく異なることがあるのです。x86アーキテクチャはバックワード互換なので、基本的なデバイス(キーボードやマウスなど)は通常、常に同じアドレスになります。しかし、より複雑なデバイスはそうではないかもしれません。 ハードウェアの直接プログラミングとコントローラすべてがどのように動くかをよりよく理解するために、コントローラについて見てみましょう。結局のところ、私たちは、特にプロテクトモードにおいて、コントローラと頻繁に会話することになるのです。多くのPCは、初期のインテル8042マイクロコントローラ・チップをベースにしています。このコントローラチップは、IC(Intergrated Circuit)チップとして組み込まれているか、マザーボードに直接組み込まれています。これは通常、サウスブリッジに配置されています。 このマイクロコントローラーは、キーボードに接続されたコードを通じて、キーボード内の別のマイクロコントローラーチップと通信を行います。 キーボードのキーを押すと、キーの下にあるラバードームが押されます。 ラバードームの下側には導電性の接点があり、押されるとキーボード回路の2つの導電性の接点と接触します。このため、電流を流すことができる。各キーは1対の電気線で接続されています。それぞれの信号が変化すると(天候によってキーが押される)、(一連のラインから)メイクコードが生成されます。このメイクコードがキーボード内部のマイコンチップに送られ、コンピュータのハードウェアポートに接続するコードを通じて送られます。これは、オンとオフの電気プッシュのシリーズとして送信されます。クロックサイクルに応じて、各パルスはビットパターンを表す一連のビットに変換することができます。 私たちはマザーボードの上にいます。この一連のビットは、電気信号としてサウスブリッジを通り、8042マイクロコントローラにまで届きます。このマイコンは、メイクコードをスキャンコードにデコードし、内部のレジスタに格納します。つまり、バッファです。内部レジスタはEEPROMチップなどでもよいので、好きなときに電気的にデータを上書きすることができる。 起動時にBIOSのPOSTが各デバイス(I/Oコントローラを通して)にポートアドレスを割り当てる。これは、デバイスを照会することによって行われる。つまり、ポート0x60を参照すると、この内部レジスタからの読み出しを要求していることになります。 ポートマッピングやIN/OUT命令については、あとはご存知の通りですので、このレジスタから読み出してみましょう。 おそらく推測できるように、8042マイクロコントローラはキーボードコントローラです。このチップの様々なレジスタと通信することで、キーボードからの入力を読んだり、スキャンコードをマップしたり、他にもいろいろなことができます。A20を有効にするように。 A20を有効にするために、なぜキーボードコントローラと通信しなければならないのか不思議に思われるかもしれません。これについては、次に説明します。 ゲートA20 - 理論編最後に、A20について説明します。このチュートリアルのほとんどは、A20とは直接関係のない他のトピックを扱っています。しかし、A20を有効にするには、マイクロコントローラのプログラミングと一緒に、ハードウェアの直接プログラミングの基礎から始めたいと思いました。A20ラインを有効にするには、キーボードマイクロコントローラをプログラミングする必要があるかもしれません。このため、キーボードコントローラのプログラミングについて少し取り上げますが、キーボードのプログラミングにはまだ踏み込みません。 ちょっとした歴史IBMがIBM PC ATマシンを設計したとき、新しいIntel 80286マイクロプロセッサを使用しましたが、これはリアルモードでは以前のx86マイクロプロセッサと完全に互換性があるわけではありませんでした。問題点は?古いx86プロセッサには、アドレス線A20〜A31がない。そのサイズのアドレスバスをまだ持っていなかったのだ。そのため、最初の1MBを超えると、プログラムが折り返して表示されてしまうのだ。80286のアドレス空間では、32本のアドレス線が必要だったのだ。しかし、32本すべてがアクセス可能だと、また折り返しの問題が出てくる。 この問題を解決するために、Intelはプロセッサとシステムバスの間の20番目のアドレスラインにロジックゲートを設置した。 このロジックゲートは、有効にも無効にもできることから、Gate A20と名付けられた。古いプログラムではラップラウンドに依存するため無効化され、新しいプログラムでは有効化される。 起動時、BIOSはメモリのカウントとテスト時にA20を有効にし、OSに制御を委ねる前に再び無効にする。 A20を有効にする方法はたくさんあります。A20ゲートを有効にすると、アドレスバスの32本のラインすべてにアクセスできるようになり、32ビットアドレス、つまり最大0xFFFFFFF(4GB)のメモリを参照できるようになる。 A20ゲートは、もともと8042マイコン(キーボード・コントローラ)のP21ラインに接続されていた電子ORゲートである。このゲートは、出力ポート・データのBit 1として扱われる出力ラインである。このデータを受信するコマンドを送ったり、データを変更することも可能です。このビットをセットして、出力線のデータを書き込めば、マイコンがORゲートをセットして、A20線を有効にすることができます。この方法は、自分で直接行うことも、間接的に行うこともできます。詳しくは次のセクションで説明します。 起動時に、BIOSはA20ラインを有効にして、メモリをテストします。メモリテストの後、BIOSは古いプロセッサとの互換性を維持するためにA20ラインを無効にします。このため、デフォルトでは、オペレーティングシステムでは、A20 ラインは無効になっています。 マザーボードの構成によって、A20 ゲートを再び有効にする方法がいくつかあります。このため、ここでは、A20を有効にするための一般的な方法をいくつか取り上げます。 次は、この方法について見てみましょう。 ゲートA20の有効化A20を有効にするには、さまざまな方法があることを覚えておいてください。携帯性を重視するのであれば、複数の方法を併用する必要があるかもしれません。方法1:システム制御ポートAこれは、A20アドレスラインを有効にするための非常に高速な方法ですが、移植性が低いです。MCAやEISAを含むいくつかのシステムでは、システム制御ポートI/O 0x92からA20を制御することができます。ポート0x92の正確な詳細と機能は、メーカーにより大きく異なります。しかし、一般的に使用されるビットがいくつかある。
このポートでできることは他にもたくさんあることに注意してください。
警告!この方法は簡単な方法の1つですが、他のハードウェアデバイスと衝突するのを見たことがあります。通常、システムが停止してしまうでしょう。もしこの方法を使いたいのであれば(そしてそれがうまくいくのであれば)、私はこの方法を使うことにこだわりますが、この点には留意してください。その他のポート...システムによっては、他のI/Oポートを使用してA20を有効または無効にすることができることを述べておく必要があると思います。最も一般的なのは、I/Oポート0xEEです。これらのシステムでI/Oポート0xEE(「FAST A20 GATE」)を有効にすると、このポートから読み出すとA20が有効になり、書き込むとA20が無効になる。ポート0xEF(「FAST CPU RESET」)も、システムをリセットするために同様の効果があります。 他のシステムでは、異なるポートを使用するかもしれません(例えば、AT&T 6300+では、A20を有効にするにはI/Oポート0x3f20に0x90を書き込み、A20を無効にするには0を書き込むことが必要です)。また、I/Oポート0x65のビット2やI/Oポート0x1f8のビット0を使ってA20を有効/無効にするシステムも存在するという噂もある(0:無効、1:有効)。 このように、A20を使用する場合、多くの問題があります。確実な方法は、マザーボードメーカーに問い合わせることです。 方法2:Bios多くのBiosは、A20の有効化と無効化のための割り込みを可能にしています。INT 0x15 関数 2400 - A20を無効にするこの機能は、A20のゲートを無効にするものです。使い方はとても簡単です。以下を返します。 CF = 成功すればクリア AH = 0 CF = エラー時に設定 AH = ステータス (01=キーボードコントローラがセキュアモード、0x86=機能がサポートされていない) INT 0x15 ファンクション 2401 - A20を有効にするこの関数は、A20 ゲートを有効にする。戻り値。 成功した場合、CF = クリア AH = 0 CF = エラーの場合、設定される AH = ステータス (01=キーボードコントローラがセキュアモード、0x86=機能がサポートされていない) INT 0x15 ファンクション 2402 - A20の状態この関数は、A20 ゲートの現在のステータスを返す。を返す。 CF = 成功すればクリア AH = ステータス (01:キーボードコントローラはセキュアモード、0x86:機能はサポートされていません) AL = 現在の状態 (00: 無効、01: 有効) CX = 0xc000の読み取りでキーボードコントローラがレディでない場合、0xffffに設定される。 CF = エラー時に設定 INT 0x15 ファンクション 2403 - A20のサポート状況を問い合わせるこの関数は、A20のサポートについてシステムに問い合わせるために使用される。戻り値 CF = 成功すればクリア AH = ステータス (01: キーボードコントローラはセキュアモード、0x86: 機能がサポートされていない) BX = ステータス。 BX にはビットパターンが含まれます。
方法3:キーボード・コントローラこれはおそらく、A20を有効にする最も一般的な方法です。非常に簡単ですが、キーボードマイクロコントローラのプログラミングの知識が必要です。この方法は、最も移植性が高いと思われるので、私が使用することになります。この方法は、キーボードマイコンのプログラミングの知識が必要なので、まず、そのことについて少し見ておく必要があります。これは、私が最初にハードウェアプログラミングをカバーしたいと思った理由でもあります。ハードウェアの直接プログラミングとはどういうものか、初めて垣間見ることができるのです。心配しないでください、そんなに悪いことではありませんよ。でも、時にはとても複雑になることもあります。) 8043キーボードコントローラ - ポートマッピングこのコントローラと通信するためには、コントローラがどのI/Oポートを使っているかを知っておく必要があります。このコントローラのポートマッピングは以下の通りです。
I/Oポート0x64にコマンドバイトを書き込むことによって、このコントローラにコマンドを送信します。コマンドがパラメータを受け取る場合、このパラメータはポート0x60に送信されます。 同様に、コマンドによって返された結果は、ポート0x60から読み取ることができる。 キーボードコントローラ自体は非常に低速であることに注意する必要があります。私たちのコードはキーボードコントローラより速く実行されるので、先に進む前にコントローラの準備ができるのを待つ方法を提供しなければなりません。 これは通常、コントローラのステータスを問い合わせることで行います。もし、これが混乱するようであれば、心配しないでください、全てはすぐに明らかになります。 8043 キーボード・コントローラ・ステータス・レジスタでは、コントローラの状態を知るにはどうしたらいいのでしょうか?上の表を見ると、I/Oポート0x64から読まなければならないことがわかります。このレジスタから読み出される値は、特定のフォーマットに従った8ビット値です。それがこちら...
ご覧のとおり、いろいろなことが起こっています。重要なビットは上記の太字で、コントローラの出力または入力バッファが満杯かどうかを示しています。 以下に例を示します。例えば、あるコマンドをコントローラに送るとします。このコマンドはコントローラの入力バッファに格納されます。つまり、このバッファがまだいっぱいであれば、コマンドはまだ実行されていることになります。次のようなコードになります。 これは、入力バッファと出力バッファの両方に対して行う必要があります。 コントローラを待つことができるようになったので、今度は実際にコントローラに何をして欲しいかを伝えることができなければなりません。これはコマンドバイトによって行われます。それでは見てみましょう。 8043キーボード・コントローラのコマンド・レジスタI/Oポートの表を見ると、コントローラにコマンドを送信するために、I/Oポート0x64に書き込む必要があることがわかります。キーボードコントローラは多くのコマンドを持っています。これはキーボードプログラミングのチュートリアルではないので、ここにすべてをリストアップすることはしません。しかし、より重要なものはリストアップします。
繰り返しますが、これ以外にも多くのコマンドがあることに注意してください。 後で全部見ますので、心配しないでください :) 方法3.1:キーボードコントローラでA20を使用可能にする上の表のコマンドバイト0xDDと 0xDFに注目してください。 これは、キーボードコントローラーを使用してA20を有効にする1つの方法です。すべてのキーボード・コントローラーがこの機能をサポートしているわけではありません。すべてのキーボードコントローラーがこの機能をサポートしているわけではありません。 方法3.2:出力ポートからA20を使用可能にするA20を有効にするもう一つの方法は、キーボードコントローラの出力ポートを使うことです。 これを行うには、コマンドD0とD1を使って出力ポートを読み書きする必要があります (参考までに、キーボードコントローラのコマンド表をもう一度見てください)。この方法は、他の方法より少し複雑ですが、それほど悪くありません。 基本的には、キーボードを無効にして、コントローラから出力ポートを読み取ります。 8042は、1つのポートを含んでいます。1つは入力、もう1つは出力です。そうだったのか...。3つ目はテスト用です。これらの「ポート」は、マイクロコントローラのハードウェア・ピンに過ぎません。 物事をシンプルにするために(そしてこれはキーボードプログラミングのチュートリアルではないので)、今は出力ポートだけを見てみましょう。 さて、出力ポートを読み出すには、次のような出力ポート読み出しコマンド(0xD0)をコントローラに送ります(参考までにキーボードコントローラコマンドの表をご覧ください)。 これで、出力ポートのデータが取得できました。しかし、これではあまり意味がありません。 では、見てみましょう。
これらのビットのほとんどは、変更する必要はありません。ビット0を1に設定するとコンピュータがリセットされ、ビット1を設定するとゲートA20が有効になります。他のビットが触れないように、この値をORしてビットを設定する必要があります。ビットを設定した後は、その値を書き戻すだけです(コマンド・バイト 0xD1)。 出力ポートの読み取りと書き込みに使用されるコマンドは、データのためにコントローラの入力と出力バッファを使用します。 つまり、出力ポートを読み出すと、読み出されたデータはコントローラの入力バッファ・レジスタに格納されます。I/Oポートの表を見ると、I/Oポート0x60から読み込んだデータを取得することになります。 例を見てみましょう。wait_inputは入力バッファが空になるのを待ち、wait_outputは出力バッファが空になるのを待ちます。 それだけなんです。:)この方法は、他の方法より少し複雑ですが、最も移植性があります。 探すべき注意点エミュレーションなので、これらのほとんどはBochsには適用されませんが、代わりに実際のハードウェアに適用されます。コントローラが間違ったコマンドを実行した例えば、in al,0x60の代わりにin al, 0x61を使うと、キーボードマイコンのステータスレジスタ(ポート0x60)ではなく、別のレジスタから読み込むことになります。コントローラの未知のコマンドほとんどのコントローラは、知らないコマンドは無視し、そのまま破棄します(コマンドレジスタがある場合はクリアします)。ただし、コントローラによっては誤動作することがあります。詳しくは、「誤動作」の項をご覧ください。 コントローラーの誤動作このようなことは稀にしか起こらないが、可能性はある。2つの顕著な例はPentiumプロセッサで、悪名高いFDIVとfoofのバグがあります。FDIVバグは、CPU内部の設計上の欠陥で、プロセッサ内部のFPUが誤った結果を出すというものです。foofの問題はよりシリーズ化されています。プロセッサにコマンドバイト0xf0 0x0f 0xc7 0xc8が与えられたとき、これはHCF(Hault and Catch Fire)命令の一例です。(これらの命令のほとんどは、プロセッサ自体をロックしてしまい、ユーザーをハードリブートさせる可能性があります。また、これらの命令の使用により、異常な副作用が発生する場合もあります。 これらの起こりうる問題については、よく考えておく必要があります。実際に起こることであり、コントローラも例外ではありません(命令バイトを個々のポートに送信することを思い出してください)。例えば、ポート0x64--キーボードコントローラのコマンドレジスタ)。 しかし、これらの不具合のほとんどは、デバイスの「設計上の欠陥」と考えるのが妥当でしょう。 ハードウェアの物理的な故障また、稀ではあるが、ソフトウェアによってハードウェアの損傷を与えることも可能である。簡単な例としては、フロッピーディスクドライブがあります。フロッピードライブのモーターは、フロッピードライブコントローラ(FDC)を通じて直接制御する必要がある。モーターを停止させるコマンドを送り忘れると、フロッピーディスクドライブが摩耗して壊れてしまうことがあります。ご注意ください。トリプル障害マイクロコントローラは、コントロールバスを介してプライマリプロセッサーに問題があることを知らせることがあります。この場合、プロセッサーは例外を通知し、もちろんコンピューターは再起動します。Bochsのコントローラの問題コントローラに問題がある場合、Bochsはトリプルフォルトを発生させ、その情報(問題)をログに記録します。例えば、キーボードコントローラに未知のコマンド(例えば0)を送ろうとした場合。 Bochsはトリプルフォルトを発生させ、その情報をログに記録します。 「KBD」は、キーボードコントローラデバイスによってログが書き込まれたことを表します。 まとめすごい、すごい。このチュートリアルは、私が当初期待したよりも大きなものです。新しい概念をたくさん見ることができました。また、ハードウェアプログラミングの経験もできました。 覚えておいてください。プロテクトモードでのハードウェアとの通信は、この方法しかありません。 さようなら、割り込み。さようならBIOS。もう完全に自分たちでやるしかないんです。 これで、Windowsをもう少し評価できるようになるかもしれませんね。)結局のところ、彼らは皆、私たちのレベルから始めなければならなかったのです。 まだすべてを理解していなくても心配しないでください--複雑なのは分かっています。Kernelに到達したら、キーボードマイクロコントローラのプログラミングとそのドライバを書くことに特化した全体のチュートリアルを用意するつもりです。いいでしょ? 次のチュートリアルはもっと簡単です。プロテクトモードは一旦保留にして、リアルモードのコードに戻りましょう。FAT12 のロードコードを追加して、カーネルをロードします。 A20 が有効になっているので、1MB でロードできます! また、BIOS情報を取得したり、その他、思いついたことを何でもやっていきます :)それではまた。 |