Operating Systems Development Series
System Architecture
by Mike, 2008, 2009

はじめに

ようこそ!:)前回のチュートリアルでは、ついにブートローダを完成させました!やったー!今のところ、とにかく :)

FAT12 ファイルシステムを詳しく説明し、ステージ 2 の読み込み、解析、実行について見てきました。

このチュートリアルは、前回の続きとなります。まず、x86 アーキテクチャについて詳しく見ていきます。これは特にプロテクトモードにおいて重要であり、プロテクトモードがどのように機能するかを理解することができます。

起動時のBIOSとの関係を理解するために、他のプロセッサを「起動」させることができることを思い出してください。BIOSはメインプロセッサでこれを行い、私たちはマルチプロセッサシステムをサポートするために同じことを行うことができます。

これから取り上げます。

  • 80x86 レジスタ
  • システム構成
  • システムバス
  • リアルモードメモリマップ
  • 命令の実行方法
  • ソフトウェアポート
ある意味、これはシステムアーキテクチャのチュートリアルのようなものです。しかし、OS 開発の観点からアーキテクチャを見ていきます。また、アーキテクチャの中のあらゆることをカバーします。

基本的な概念を理解することで、プロテクトモードをより詳細に理解することができます。 次のチュートリアルでは、ここで学んだことをすべて使って、プロテクトモードに切り替えてみましょう。

それでは、お楽しみに......。

プロテクトモードの世界

この言葉は聞いたことがありますね?80286以降のプロセッサで利用可能な動作モードです。PModeは、主にシステムの安定性を高めるために設計されました。

これまでのチュートリアルでご存知のように、リアルモードにはいくつかの大きな問題があります。一つは、好きなところにバイトを書き込むことができることです。これは、ソフトウェアポートやプロセッサ、あるいは私たち自身によって使用されるかもしれないコードやデータを上書きすることができます。しかも、直接的にも間接的にも、4,000以上の異なる方法でこれを行うことができるのです。

リアルモードにはメモリープロテクトがありません。すべてのデータとコードは、単一の多目的使用メモリブロックにダンプされます。

リアルモードでは、16ビットのレジスターに制限されます。このため、1MBのメモリに制限されます。

ハードウェアレベルのMemory Protectionや Multitaskingはサポートされていない。

最大の問題点は、「リング」というものが存在しないことだ。すべてのプログラムはリング0レベルで実行され、すべてのプログラムがシステムを完全に制御できる。つまり、シングルタスク環境では、気をつけないとたった1つの命令(cli/hltなど)でOS全体がクラッシュする可能性があるのです。

このあたりは、リアルモードについて詳しく説明したときと同じように思えるかもしれません。Protected Modeは、これらの問題をすべて解決してくれます。

プロテクトモード

  • メモリ保護機能
  • 仮想メモリと タスクステートスイッチング(TSS)のハードウェアサポート
  • プログラムの相互実行をハードウェアでサポートしている
  • 4つの動作モード。リング0、リング1、リング2、リング3
  • 32ビットレジスタへのアクセス
  • 最大4GBのメモリへのアクセス
以前のチュートリアルで、アセンブリ言語のリングを取り上げました。通常のアプリケーションはRing3(Useually)ですが、私たちはRing0にいることを覚えておいてください。通常のアプリケーションにはない特別な命令やレジスタにアクセスすることができます。このチュートリアルでは、LGDT命令、独自に定義したセグメントを使用したファージャンププロセッサ制御レジスタの使用について説明します。 このようなことは、通常のプログラムではできません。

システム・アーキテクチャとプロセッサの仕組みを理解することで、このOSの理解が深まります。

システム・アーキテクチャ

x86ファミリーのコンピュータは、ヴァン・ノイマン・アーキテクチャーに準拠しています。ヴァンニューマンアーキテクチャとは、典型的なコンピュータシステムが3つの主要な構成要素を持っているという設計仕様である。
  • 中央処理装置(CPU)
  • メモリ
  • 入力/出力(IO)
例えば、以下のようなものです。

重要なことが2つあります。ご存知のように、CPU はメモリからデータや命令を取り込みます。メモリコントローラは、それが存在する正確なRAMチップとメモリセルを計算する役割を担っています。このため、The CPUはMemory Controllerと通信を行います。

また、「I/Oデバイス」にもご注目ください。これらはシステムバスに接続されている。すべてのI/Oポートは、所定のメモリ位置にマッピングされています。これにより、IN命令とOUT命令を使うことができるのです。

ハードウェアデバイスは、システムバスを介してメモリにアクセスすることができます。また、何かが起こったときにデバイスに通知することもできる。例えば、ハードウェアデバイスコントローラが読み出すために、あるメモリ位置にバイトを書き込むと、プロセッサはそのアドレスにデータがあることをデバイスに通知することができます。これは、システムバス全体のうち、コントロールバスの部分を通じて行われます。これが、ソフトウェアがハードウェアデバイスとやりとりする基本的な方法です。後ほど詳しく説明しますが、プロテクトモードのデバイスと通信する唯一の方法であり、重要なポイントです。

まず、すべてを詳しく説明します。次に、ハードウェアレベルで命令が実行されるのを見ながら、それらを組み合わせて、どのように動作するかを学びます。ここからは、I/Oポートについて、また、ソフトウェアがハードウェアとどのように相互作用するかについて説明します。

x86アセンブリの経験をお持ちの方なら、一部、あるいは大部分を知っているはずです。しかし、ほとんどのアセンブリの本では詳しく説明されていないことをたくさん取り上げるつもりです。具体的には、Ring 0のプログラムに特化した内容です。

システムバス

システムバスは フロントサイドバスとも呼ばれ、CPUをマザーボード上のノースブリッジに接続します。

システムバスは、データバスアドレスバスコントロールバスを組み合わせたもので、このバスの各電子線が1つのビットを表します。0」と「1」を表す電圧レベルは、TTL(Standard Transistor-Transistor Logic)レベルをベースにしている。しかし、このことを知る必要はありません。TTLは、コンピュータを構成するデジタル・ロジック・エレクトロニクスの一部である。

ご存知のように、システムバスは3つのバスで構成されています。それでは詳しく見ていきましょう。

データバス

データバスは、データを伝送することができる一連の電子線である。データバスのサイズは、16本/ビット、32本/ビット、64本/ビット。電子線と1ビットが直接関係していることに注意してください。

32ビットプロセッサは、32ビットのデータバスを持ち、使用します。つまり、4バイトのデータを同時に扱うことができるのです。これを知っていれば、プログラムのデータサイズに気を配ることができ、高速化につながります。

どのように?プロセッサは、1、2、4、8、16ビットのデータをデータバスのサイズに合わせて0を詰める必要があります。大きなデータピースは、プロセッサがデータバス上で正しくバイトを送信できるように分割(およびパディング)する必要があります。データバスの大きさのデータ片を送信すると、余分な処理がないため、より高速になります。

たとえば、64ビットのデータ型があり、32ビットのデータバスがあるとします。最初のクロックサイクルでは、最初の32ビットだけがデータバスを介してメモリコントローラに送信されます。2番目のクロックサイクルでは、プロセッサは最後の32ビットを参照します。注:データ型が大きくなればなるほど、より多くのクロックサイクルを要することに注意してください

一般に、「32ビットプロセッサ」、「16ビットプロセッサ」などの用語は、データバスの大きさを指しています。つまり、「32ビットプロセッサー」は32ビットのデータバスを使用します。

アドレスバス

プロセッサやI/Oデバイスがメモリを参照する必要がある場合、アドレスバスにそのアドレスを置きます。 さて、メモリアドレスがメモリ内の位置を表すことは、誰もが知っていることです。しかし、これは抽象的な表現です。

メモリーアドレスとは、メモリーコントローラーが使用する番号のことです。それだけです。メモリコントローラは、このバスから数字を取り出し、それをメモリの場所として解釈します。各RAMチップの大きさを知っていれば、メモリコントローラは簡単に正確なRAMチップとその中のバイトオフセットを参照することができます。メモリコントローラは、メモリセル0を起点に、このオフセットを、目的のアドレスとして積分する。

アドレスバスは、コントロールユニット(CU)I/Oコントローラを介してプロセッサに接続されている。コントロールユニットはプロセッサ内部にあるので、後で見ていくことにする。I/Oコントローラは、ハードウェアデバイスとのインターフェイスを制御する。これについても後ほど見ていきます。

データバスと同様に、各電子ラインは1つのビットを表します。1ビットに含まれる値は2つだけなので、CPUがアクセスできるアドレスは厳密に2^n個となります。したがって、アドレスバスのビット数/ライン数は、CPUがアクセスできる最大メモリ数を表しています。

8080から80186までのプロセッサでは、それぞれ20ライン/ビットのアドレスバスを備えていた。80286と80386は24本/ビット、80386+は32本/ビットである。

x86ファミリ全体が、古いプロセッサとも移植できるように設計されていることを忘れてはならない。そのため、Real Modeでスタートする。そのため、x86ファミリーのプロセッサは、0番から19番までの20本のアドレスラインしか利用できず、1MBに制限されていました。

この制限は、今でも私たちに適用されるからです。そこで、20本目のアドレスからアクセスできるようにするのです。これでOSは4GB以上のメモリにアクセスできるようになります。詳しくは後述します。

コントロールバス

では、Data Busにデータを載せて、Address Busでメモリアドレスを参照する。 しかし、このデータをどう扱うのか?メモリから読み出すのか?それともデータを書き込むのか?

コントロールバスは、デバイスが何をしようとしているかを表す一連のライン/ビットです。例えば、プロセッサはREADビットやWRITEビットを設定して、アドレスバスに格納されたメモリ位置からデータバスのデータを読み取りまたは書き込みたいことをメモリコントローラーに知らせます。

また、Control Busは、プロセッサがデバイスに信号を送るためのものです。これは、デバイスに注意を促すためのものです。例えば、アドレスバスのメモリ位置から読み出すようにデバイスに要求することもできます。このように、デバイスに必要なことを知らせることができます。これは、I/Oソフトウェアポートで重要なことです。

もちろん、システムバスはハードウェアデバイスに直接接続されているわけではないことを忘れてはならない。代わりに、それは中央のコントローラに接続されている -I / Oコントローラ、順番に、デバイスに信号を送信します。

それはすべて

システムバスは以上である。システムバスは、プロセッサ(CU)とI/Oデバイス(I/Oコントローラ)から、メモリコントローラ(RAMチップを正確に計算し、アクセスしたいメモリセルを見つける役割)へ、メモリにアクセスしたり読み出すための経路となっています。

「コントローラ」...あなたは私がこの用語alotを言うのを聞くことができます。その理由は後ほど説明します。

メモリコントローラ

メモリコントローラは、マザーボード上のシステムバス(フロントサイドバス、FSB)と物理的なRAMチップの間の主要なインターフェイスです。

コントローラという言葉はよく耳にしますよね。コントローラとはいったい何なのでしょうか?

コントローラ

コントローラは、基本的なハードウェア制御機能を提供します。また、ハードウェアとソフトウェアの間の基本的なインターフェイスを提供します。これは私たちにとって重要なことです。保護モードでは、割り込みが使えないことを忘れないでください。ブートローダでは、ハードウェアと通信するためにいくつかの割り込みを使用しました。これらの割り込みをプロテクトモードで使用すると、トリプルフォールトが発生します。 どうすればいいのでしょう?

私たちはハードウェアと直接通信する必要があります。これはコントローラを通して行います(コントローラの仕組みについては、後ほどI/Oサブシステムを取り上げる際に詳しく説明します)。

メモリコントローラ

メモリコントローラは、ソフトウェアでメモリの位置を読み書きする方法を提供します。 メモリコントローラは、RAMチップが情報を保持できるように、常にリフレッシュする役割も担っています。

メモリコントローラには、マルチプレクサと デマルチプレクサ回路があり、アドレスバスのアドレスを参照する正確なRAMチップと位置を選択します。

ダブルデータレート(DDR)コントローラ

DDRコントローラはDDR SDRAMのリフレッシュに使用され、システムクロックパルスを使用してメモリの読み取りと書き込みを可能にします。

デュアルチャネルコントローラ

デュアルチャネルコントローラは、DRAMデバイスを2つの小さなバスに分離し、一度に2つのメモリロケーションの読み取りと書き込みを可能にするために使用されます。これにより、RAMへのアクセスが高速化されます。

メモリコントローラ まとめ

メモリコントローラは、私たちがアドレスバスに入力したアドレスを受け取ります。でも、どうやってメモリコントローラにメモリの読み書きを指示するのでしょうか?メモリを読み出す場合、プロセッサはControl BusにReadビットをセットします。同様に、プロセッサはControl Bus にメモリを書き込むときにWriteビットをセットします。

コントロールバスは、プロセッサが他のデバイスのバスの使い方を制御するためのものであることを忘れないでください。

メモリコントローラが使用するデータはData Busの中にある。使用するAddressはAddress Busの中にある。

メモリの読み出し

メモリを読み出す場合、プロセッサは読み出す絶対アドレスをアドレス・バスに置きます。 その後、プロセッサはREADコントロール・ラインをセットします。

メモリコントローラが制御します。コントローラは、マルチプレクサ回路を使用して絶対アドレスを物理的なRAM位置に変換し、データをデータバスに格納します。その後、READビットを0にリセットし、READYビットを設定します。

プロセッサは、データがデータ・バスにあることを認識します。このデータをコピーして、残りの命令を実行し、BXに格納するのでしょうか。

メモリの書き込み

メモリを書き込むプロセスも同様です。

まず、プロセッサはメモリのアドレスをアドレスバスに格納します。次に、書き込むデータをデータバスに格納する。そして、コントロールバスにWRITEビットをセットする。

これにより、メモリコントローラは、データバスのデータをアドレスバスの絶対アドレスに書き込むことを知ることができる。書き込みが完了すると、メモリコントローラはWRITEビットをリセットし、Control BusにREADYビットを設定します。

まとめ

ソフトウェアで直接メモリコントローラと通信するのではなく、間接的に通信する。メモリの読み書きをするときは、必ずメモリコントローラを使用します。これが、ソフトウェアとメモリコントローラ/RAMチップのハードウェアのインターフェースです。

さて、I/Oサブシステムを見てみましょう。あ、そうだ。1337マルチプレクサの回路はどうなっているのでしょうか?あれはメモリコントローラの中にある物理的な電子回路です。その仕組みを理解するには、デジタルロジックの知識が必要だ。しかし、これは私たちには理解しがたいことなので、ここでは説明しません。もっと知りたい方は、Google!

I/Oサブシステム

I/Oサブシステムは、簡単に言うとポートI/Oを表しています。ソフトウェアとハードウェア・コントローラの間のインターフェイスを提供する基本的なシステムです。

もっと詳しく見てみましょう。

ポート

ポートとは、簡単に言えば、2つのデバイス間のインターフェイスを提供するものです。ポートには、ハードウェアポートとソフトウェアポートの2種類がある。

ハードウェア・ポート

ハードウェアポートは、2つの物理デバイス間のインターフェイスを提供します。このポートは、通常、一種の接続デバイスです。これには、以下のものが含まれますが、これらに限定されるものではありません。シリアルポート、パラレルポート、PS/2ポート、1394、FireWire、USBポートなど。

これらのポートは、通常、一般的なコンピュータシステムの側面/背面/前面にあります。

さて...あなたがポートを見たい場合は、ちょうどあなたのコンピュータに接続されている任意の行をたどってください。どうか、ジーヴスのために、これらが何をするものなのか私に聞かないでください。マジで!?

一般的な電子機器では、これらのポートのピンは、ハードウェアデバイスによって異なることを表す信号を伝達します。これらのピンは、システムバスと同じように......ちょっと待てよ。ビットです。各ピンは1つのビットを表します。そう、それです。

ハードウェアのポートには、「オス」と「メス」の2つの分類があります。オス型は、ピンがコネクタから出ている接続です。ハードウェアポートには、コントローラからアクセスします。詳しくは後ほど...

ソフトウェアのポート

これは私たちにとって非常に重要なものです。これは、ハードウェアとのインターフェイスです。ソフトウェア・ポートとは番号です。それだけです。この番号はハードウェアのコントローラーを表している...という感じです。

複数のポート番号が同じコントローラを表すことがあるのはご存知でしょう。その理由は?メモリマップドI/Oです。基本的な考え方は、特定のメモリアドレスを指定してハードウェアと通信する。ポート番号はこのアドレスを表す...もう一回、ちょっとだけ。アドレスの意味は、デバイスの特定のレジスタを表したり、制御レジスタを表したりします。

後でもっと詳しく見ていきます。

メモリマッピング

x86アーキテクチャでは、プロセッサは特定のメモリ・ロケーションを使用して特定のものを表します。

例えば、0xA000:0というアドレスは、ビデオカード内のVRAMの先頭を表しています。この場所にバイトを書き込むことで、現在ビデオメモリにあるものを実質的に変更し、画面に表示されるものを実質的に変更することができます。

他のメモリアドレスは、例えばフロッピーディスクドライブコントローラ(FDC)用のレジスタなど、他のものを表すことができます。

どのアドレスが何であるかを理解することは、私たちにとって非常に重要です。

x86 Real Mode Memory Map

一般的なx86のリアルモードメモリマップ。
  • 0x00000000 - 0x000003FF - Real Mode Interrupt Vector Table
  • 0x00000400 - 0x000004FF - BIOS Data Area
  • 0x00000500 - 0x00007BFF - Unused
  • 0x00007C00 - 0x00007DFF - Our Bootloader
  • 0x00007E00 - 0x0009FFFF - Unused
  • 0x000A0000 - 0x000BFFFF - Video RAM (VRAM) Memory
  • 0x000B0000 - 0x000B7777 - Monochrome Video Memory
  • 0x000B8000 - 0x000BFFFF - Color Video Memory
  • 0x000C0000 - 0x000C7FFF - Video ROM BIOS
  • 0x000C8000 - 0x000EFFFF - BIOS Shadow Area
  • 0x000F0000 - 0x000FFFFF - System BIOS

注:上記のデバイスをすべて再マップして、メモリの異なる領域を使用することが可能です。これは、BIOS POSTがデバイスを上の表にマップするために行うものです。

さて、これはクールですべてです。これらのアドレスは異なるものを表しているので、特定のアドレスを読み(書き)出すことで、コンピュータのさまざまな部分から簡単に情報を入手(変更)することができます。

例えば、INT 0x19の話をしましたね。0x0040:0x0072に0x1234という値を書き込み、0xFFFF:0にジャンプすると、実質的にコンピュータをウォームリブートさせることができると説明しました。(Windowsのctrl+alt+delと同じです。) セグ:オフセットアドレッシングモードと絶対アドレスの変換を思い出して、0x0040:0x0072を絶対アドレス0x000000472、BIOSデータエリア内の1バイトに変換できます。

もう一つの例は、テキスト出力です。0x000B8000に2バイト書き込めば、テキストモードメモリの内容を実質的に変更することができます。これは表示時に常にリフレッシュされるため、実質的に画面に文字を表示することになります。どうです?

ポートマッピングに戻りましょう。この表は後でもっとたくさん見返すことになります。

ポートマッピング - メモリマップドI/O

ポートアドレス」は、各コントローラがリッスンする特別な番号です。起動時に、ROM BIOSはこれらのコントローラデバイスに異なる番号を割り当てます。プライマリプロセッサーを起動し、BIOSプログラムを0xFFFF:0にロードします(これを覚えていますか? 前のセクションの表と比較してみてください)。

ROM BIOSは、これらの番号を異なるコントローラに割り当て、コントローラは自分自身を識別する方法を持っています。これは、BIOSは、この特別な番号を使用してハードウェアに通信する割り込みベクターテーブルをセットアップすることができます。

プロセッサは、I/Oコントローラと連携する際に、同じシステムバスを使用します。プロセッサは、あたかもメモリを読み込むかのように、特別なポート番号をアドレスバスに入力します。また、同様にコントロールバスのREADまたはWRITEラインを設定します。しかし、プロセッサはメモリの書き込みとコントローラへのアクセスをどのように区別しているのでしょうか?

プロセッサは、コントロールバス上にもう1本、I/O ACCESSラインを設定します。このラインが設定されると、I/Oサブシステム内のI/Oコントローラは、アドレスバスを監視します。アドレス・バスがデバイスに割り当てられた番号に反応した場合、そのデバイスはデータ・バスからその値を受け取って動作する。 メモリコントローラは、このラインが設定されている場合、いかなるリクエストも無視します。したがって、ポート番号が割り当てられていない場合は、まったく何も起こりません。どのコントローラも動作せず、メモリコントローラはそれを無視します。

では、このポート番号について見てみましょう。これは非常に重要です。これは、プロテクトモードのハードウェアと通信するための*唯一の*方法です!:

警告。この表は大きいです。

Default x86 Port Address Assignments
Address Range First QWORD Second QWORD Third QWORD Fourth QWORD
0x000-0x00F DMA Controller Channels 0-3
0x010-0x01F System Use
0x020-0x02F Interrupt Controller 1 System Use
0x030-0x03F System Use
0x040-0x04F System Timers System Use
0x050-0x05F System Use
0x060-0x06F Keyboard/PS2 Moude (Port 0x60)
Speaker (0x61)
Keyboard/PS2 Mouse (0x64) System Use
0x070-0x07F RTC/CMOS/NMI (0x70, 0x71) DMA Controller Channels 0-3
0x080-0x08F DMA Page Register 0-2 (0x81 - 0x83) DMA Page Register 3 (0x87) DMA Page Register 4-6 (0x89-0x8B) DMA Page Register 7 (0x8F)
0x090-0x09F System Use
0x0A0-0x0AF Interrupt Controller 2 (0xA0-0xA1) System Use
0x0B0-0x0BF System Use
0x0C0-0x0CF DMA Controller Channels 4-7 (0x0C0-0x0DF), bytes 1-16
0x0D0-0x0DF DMA Controller Channels 4-7 (0x0C0-0x0DF), bytes 16-32
0x0E0-0x0EF System Use
0x0F0-0x0FF Floating Point Unit (FPU/NPU/Mah Copprocessor)
0x100-0x10F System Use
0x110-0x11F System Use
0x120-0x12F System Use
0x130-0x13F SCSI Host Adapter (0x130-0x14F), bytes 1-16
0x140-0x14F SCSI Host Adapter (0x130-0x14F), bytes 17-32 SCSI Host Adapter (0x140-0x15F), bytes 1-16
0x150-0x15F SCSI Host Adapter (0x140-0x15F), bytes 17-32
0x160-0x16F System Use Quaternary IDE Controller, master slave
0x170-0x17F Secondary IDE Controller, Master drive System Use
0x180-0x18F System Use
0x190-0x19F System Use
0x1A0-0x1AF System Use
0x1B0-0x1BF System Use
0x1C0-0x1CF System Use
0x1D0-0x1DF System Use
0x1E0-0x1EF System Use Tertiary IDE Controller, master slave
0x1F0-0x1FF Primary IDE Controller, master slave System Use
0x200-0x20F Joystick Port System Use
0x210-0x21F System Use
0x220-0x22F
Sound Card
Non-NE2000 Network Card System Use
0x230-0x23F SCSI Host Adapter (0x220-0x23F), bytes 17-32)
0x240-0x24F
Sound Card
Non-NE2000 Network Card System Use
NE2000 Network Card (0x240-0x25F) Bytes 1-16
0x250-0x25F NE2000 Network Card (0x240-0x25F) Bytes 17-32
0x260-0x26F
Sound Card
Non-NE2000 Network Card System Use
NE2000 Network Card (0x240-0x27F) Bytes 1-16
0x270-0x27F
System Use Plug and Play System Devices LPT2 - Second Parallel Port
System Use LPT3 - Third Parallel Port (Monochrome Systems)
NE2000 Network Card (0x260-0x27F) Bytes 17-32
0x280-0x28F
Sound Card
Non NE2000 Network Card System Use
NE2000 Network Card (0x280-0x29F) Bytes 1-16
0x290-0x29F NE2000 Network Card (0x280-0x29F) Bytes 17-32
0x2A0-0x2AF
Non NE2000 Network Card System Use
NE2000 Network Card (0x280-0x29F) Bytes 1-16
0x2B0-0x2BF NE2000 Network Card (0x280-0x29F) Bytes 17-32
0x2C0-0x2CF System Use
0x2D0-0x2DF System Use
0x2E0-0x2EF System Use COM4 - Fourth Serial Port
0x2F0-0x2FF System Use COM2 - Second Serial Port
0x300-0x30F
Sound Card / MIDI Port System Use
Non NE2000 Network Card System Use
NE2000 Network Card (0x300-0x31F) Bytes 1-16
0x310-0x31F NE2000 Network Card (0x300-0x32F) Bytes 17-32
0x320-0x32F
Sound Card / MIDI Port (0x330, 0x331) System Use
NE2000 Network Card (0x300-0x31F) Bytes 17-32
SCSI Host Adapter (0x330-0x34F) Bytes 1-16
0x330-0x33F
Sound Card / MIDI Port System Use
Non NE2000 Network Card System Use
NE2000 Network Card (0x300-0x31F) Bytes 1-16
0x340-0x34F
SCSI Host Adapter (0x330-0x34F) Bytes 17-32
SCSI Host Adapter (0x340-0x35F) Bytes 1-16
Non NE2000 Network Card System Use
NE2000 Network Card (0x340-0x35F) Bytes 1-16
0x350-0x35F
SCSI Host Adapter (0x340-0x35F) Bytes 17-32
NE2000 Network Card (0x300-0x31F) Bytes 1-16
0x360-0x36F
Tape Accelerator Card (0x360) System Use Quaternary IDE Controller (Slave Drive)(0x36E-0x36F)
Non NE2000 Network Card System Use
NE2000 Network Card (0x300-0x31F) Bytes 1-16
0x370-0x37F
Tape Accelerator Card (0x370) Secondary IDE Controller (Slave Drive) LPT1 - First Parallel Port (Color systems)
System Use LPT2 - Second Parallel Port (Monochrome Systems)
NE2000 Network Card (0x360-0x37F) Bytes 1-16
0x380-0x38F System Use Sound Card (FM Synthesizer) System Use
0x390-0x39F System Use
0x3A0-0x3AF System Use
0x3B0-0x3BF VGA/Monochrome Video LPT1 - First Parallel Port (Monochrome Systems)
0x3C0-0x3CF VGA/CGA Video
0x3D0-0x3DF VGA/CGA Video
0x3E0-0x3EF
Tape Accelerator Card (0x370) System Use COM3 - Third Serial Port
System Use Tertiary IDE Controller (Slave Drive)(0x3EE-0x3EF)
0x3F0-0x3FF
Floppy Disk Controller COM1 - First Serial Port
Tape Accelerator Card (0x3F0) Primary IDE Controller (Slave Drive)(0x3F6-0x3F7) System Use

この表は完全ではありませんが、間違いがないことを祈ります。時間が経過し、より多くのデバイスが開発されたら、この表を追加する予定です。

上の表にあるように、これらのメモリ範囲はすべて、特定のコントローラで使用されます。ポートアドレスの正確な意味は、コントローラによって異なります。コントロールレジスタ、ステートレジスタ、その他何でもありです。これは不愉快なことです。

上の表をプリントアウトしておくことを強くお勧めします。ハードウェアと通信するたびに、この表を参照する必要があります。

この表を更新した場合は、チュートリアルの最初のほうで更新します。そうすれば、この表を再度印刷することができ、誰もが最新のコピーを手にすることができます。

これらのことを念頭に置きながら、すべてをまとめてみましょう...

IN命令とOUT命令

x86プロセッサは、ポートI/Oに使用する2つの命令を持っています。INと OUTです。

デバイスと通信することをプロセッサに伝える命令です。これは、プロセッサがコントロールバスのI/O DEVICEラインを設定することを保証するものです。

キーボードコントローラのインプットバッファから読み出すことができるかどうか、試してみましょう。

上のポート表を見ると、キーボードコントローラはポートアドレス0x60から0x6Fにあることがわかります。この表から、最初のQWORDと2番目のQWORD(ポートアドレス0x60から始まる)は、キーボードとPS/2マウス用であることがわかります。最後の2つのQWORDはシステム用なので、無視することにします。

さて、キーボードコントローラはポート0x60から、厳密にはポート0x68にマッピングされています。これは素晴らしいことですが、私たちにとってどういう意味があるのでしょうか?これはデバイス固有のものです。

キーボードの場合、ポート0x60はコントロールレジスタ、ポート0x64はステータスレジスタです。前にも言いましたが、この用語はもっといろいろな文脈で使われます。ステータス・レジスタのビット1がセットされていれば、データは入力バッファの中にある。つまり、...CONTROLレジスタをREADに設定すれば、入力バッファの内容をどこかにコピーすることができます。

WaitLoop: in al, 64h ; Get status register value and al, 10b ; Test bit 1 of status register jz WaitLoop ; If status register bit not set, no data is in buffer in al, 60h ; Its set--Get the byte from the buffer (Port 0x60), and store it
これが、ハードウェアプログラミングとデバイスドライバの基本です。

IN命令では、プロセッサはポートアドレス(0x64など)をアドレスバスに入れ、コントロールバスにI/O DEVICEライン、それに続いてREADラインを設定します。ROM BIOSで0x60に割り当てられたデバイス(この場合、キーボードコントローラのステータスレジスタ)は、READラインが設定されているので、読み取り操作であることを認識します。そこで、キーボード・レジスタ内のある場所からデータをデータ・バスにコピーし、コントロール・バスのREADラインとI/O DEVICEラインをリセットして、READYラインをセットします。これで、プロセッサは読み込んだデータバスのデータを手に入れることができます。

OUT命令も同様です。プロセッサは、書き込むバイトをデータバスにコピーします(データバス幅にゼロ拡張します)。次に、コントロールバスのWRITEラインとI/O DEVICEラインをセットする。そして、ポートアドレス(仮に0x60とする)をAddress Busにコピーする。I/O DEVICEラインが設定されているのは、すべてのコントローラにアドレスバスを監視するように伝える信号である。もし、アドレス・バス上の数字が、割り当てられた数字と一致すれば、そのデバイスはそのデータに対して動作する。この場合、キーボード・コントローラは、コントロール・バスにWRITEラインが設定されているので、WRITE操作であることがわかる。 そこで、データ・バス上の値を、ポート・アドレス0x60が割り当てられた制御レジスタにコピーする。 キーボード・コントローラは、WRITEとI/O DEVICEラインをリセットしてコントロール・バスにREADYラインを設定して、プロセッサを再び制御状態にする。

ポートマッピングとポートI/Oは、非常に重要です。これは、プロテクトモードのハードウェアと通信するための唯一の方法です。割り込みは、書き込まないと使えません。入力や出力のようなハードウェアルーチンとともに、割り込みを書き込むには、ドライバを書く必要があります。そのためには、ハードウェアに直接アクセスする必要があります。もし、あなたが慣れないうちは、まず少し練習してから、このセクションを読み直してください。何か質問があれば、私に知らせてください。

プロセッサー

特別な指示

80x86の命令のほとんどは、どんなプログラムでも実行することができる。しかし、Kernelレベルのソフトウェアだけがアクセスできる命令もある。このため、読者の皆さんには馴染みのない命令もあるかもしれません。これらの命令のほとんどを使用する必要がありますので、理解することが重要です。
Privileged Level (Ring 0) Instructions
Instruction Description
LGDT Loads an address of a GDT into GDTR
LLDT Loads an address of a LDT into LDTR
LTR Loads a Task Register into TR
MOV Control Register Copy data and store in Control Registers
LMSW Load a new Machine Status WORD
CLTS Clear Task Switch Flag in Control Register CR0
MOV Debug Register Copy data and store in debug registers
INVD Invalidate Cache without writeback
INVLPG Invalidate TLB Entry
WBINVD Invalidate Cache with writeback
HLT Halt Processor
RDMSR Read Model Specific Registers (MSR)
WRMSR Write Model Specific Registers (MSR)
RDPMC Read Performance Monitoring Counter
RDTSC Read time Stamp Counter

上記の命令をカーネルモードアクセス(リング0)でない他のプログラムが実行すると、General Protection Fault、またはTriple Faultが発生します。

これらの命令が分からなくても心配しないでください。このシリーズを通して、必要に応じてそれぞれを解説していきます。

80x86のレジスタ

x86プロセッサは、現在の状態を保存するために多くの異なるレジスタを持っています。ほとんどのアプリケーションは、一般フラグ、セグメントフラグ、およびEフラグにのみアクセスできます。その他のレジスタは、KernelのようなRing 0プログラム専用です。

x86ファミリには、次のようなレジスタがあります。RAX(EAX(AX/AH/AL))、RBX(EBX(BX/BH/BL))、RCX(EXX(CX/CH/CL))、RDX(EXX(DH/DL))、CS、SS、ES、DS、FS、GS、RSI(ESI(Si))、RDI(EDI(DI))、RBP(EBP(BP))、RSP(ESP(SP))。RSP(ESP(SP))、RIP(EIP(IP))、RFLAG(EFLAG(フラグ))、DR0、DR1、DR2、DR3、DR4、DR5、DR6、DR7、TR1、TR2、TR3、TR4、TR5、TR6、TR7、CR0、CR1,CR2、CR3、CR4、CR8、ST、mm0、mm1、mm2、mm3、mm4、mm5、mm6、mm7、xmm0、xmm1、xmm2、xmm3、xmm4、xmm5、xmm6、xmm7、GDTR、LDTR、IDTR、MSRおよびTRです。これらのレジスタはすべて、プロセッサ内部のレジスタファイルと呼ばれる特別なメモリ領域に格納されています。詳しくは、プロセッサ・アーキテクチャのセクションを参照してください。その他のレジスタには、レジスターファイルにないものもありますが、次のようなものがあります。PC、IR、ベクターレジスター、ハードウェアレジスターなどです。

これらのレジスタの多くは、リアルモードリング0プログラムでのみ使用可能です。これには非常に大きな理由があります。これらのレジスタのほとんどは、プロセッサ内の多くの状態に影響を与えます。これらのレジスタの設定を誤ると、CPUは簡単にトリプルフォールトを起こすことができます。また、CPUの誤動作を引き起こす可能性もあります。(特にTR4,TR5,TR6,TR7を使用した場合)。

その他のレジスタの中には、CPUの内部にあり、通常の方法ではアクセスできないものがあります。 これらにアクセスするためには、プロセッサ自体を再プログラムする必要があります。最も顕著なのは、IRと呼ばれるベクターレジスタです。

これらの特殊なレジスタを知る必要があるので、詳しく見ていきましょう。

注:CPUを通常のデバイスと同じように考え、通信を行う必要があります。コントロールレジスタの概念(およびレジスタ自体)は、後ほど他のデバイスと通信する際に重要になります。

また、いくつかのレジスタは文書化されていないことに注意してください。このため、リストアップされたものよりも多くのレジスタが存在する可能性があります。もし知っているのであれば、追加できるように教えてください。

汎用レジスタ

これらのレジスタは32ビットで、ほとんどすべての目的に使用できます。しかし、これらのレジスタはそれぞれ特別な目的も持っています。
  • EAX - アキュムレータレジスタ。主な目的:数学の計算
  • EBX - ベースアドレスレジスタ。主な用途:ベースアドレスを介してメモリに間接的にアクセスする。
  • ECX - カウンター・レジスタ。主な用途:カウント、ループ処理
  • EDX - データレジスタ。主な用途:データを格納する。はい、そんなところです(笑)。

これらの32ビットレジスタは、それぞれ2つの部分から構成されています。高次ワードと 低次ワードです。 高次ワードは上位16ビットです。低位ワードとは下位16ビットのことです。

64ビットプロセッサの場合、これらのレジスタは64ビット幅で、RAX, RBX, RCX, RDXという名前になります。下位32ビットは32ビットEAXレジスタです。

上位16ビットには特別な名称はありません。これらの名称は、上位8ビットの場合はH、下位8ビットの場合はLが付加されています。

例えばRAXでは

+--- AH ----+--- AL ---+ | | | +-------------------------------------------------------------+ | | | | +-------------------------------------------------------------+ | | | | +--------EAX lower 32 bits--------------| -- Available only on 32 bit processors. | | |------------------ RAX Complete 64 bits----------------------| -- Available only on 64 bit processors.
これはどういうことかというとAHとALはAXの一部であり、AXはEAXの一部です。したがって、これらの名前のいずれかを変更すると、実質的に同じレジスタ - EAXを変更します。

これは、順番に、64ビットマシン上でRAXを変更する。

上記はBX、CX、DXにも当てはまります。

汎用レジスタは、Ring0からRing4まで、どのプログラム内でも使用することができます。基本的なアセンブリ言語なので、どのように動作するかはすでにご存じであると仮定します。

セグメントレジスタ

セグメントレジスタは、リアルモードでの現在のセグメントアドレスを変更します。これらはすべて16ビットです。
  • CS - Segment address of code segment
  • DS - Segment address of data segment
  • ES - Segment address of extra segment
  • SS - Segment address of stack segment
  • FS - Far Segment address
  • GS - General Purpose Register
覚えておいてください。リアルモードは、セグメント:オフセットメモリーアドレッシングモデルを使用します。 セグメントアドレスは、セグメントレジスタ内に格納されます。BP、SP、BXなどの別のレジスタには、オフセットアドレスを格納することができます。

通常、次のように参照されます。DS:SI、DSはセグメントアドレス、SIはオフセットアドレスです。

セグメントレジスタは、Ring 0からRing 4まで、どのようなプログラムでも使用することができます。セグメントレジスタは基本的なアセンブリ言語であるため、その仕組みはすでにご存知だと思います。

インデックスレジスタ

x86は、メモリにアクセスする際に役立つレジスタをいくつか使用しています。
  • SI - Source Index
  • DI - Destination Index
  • BP - Base Pointer
  • SP - Stack Pointer
これらのレジスタは、それぞれ16ビットのベースアドレス(オフセットアドレスとしても使用可能)を格納している。

32ビットプロセッサの場合、これらのレジスタは32ビットで、ESI、EDI、EBP、ESPという名前が付いています。

64ビットプロセッサの場合、各レジスタは64ビットで、RSI、RDI、RBP、RSPという名前になります。

16ビットレジスタは32ビットレジスタのサブセットで、それは64ビットレジスタのサブセットです。

スタックポインターは、特定の命令に遭遇するたびに、自動的に一定量のバイトをインクリメント、デクリメントします。このような命令には、push*、pop*命令、ret/iret、call、syscallなどがある。

C 言語は、ほとんどの言語で、スタックを定期的に使用します。C 言語が正しく動作するように、スタックを適切なアドレスに設定する必要があります。また、覚えておいてください。スタックは下に向かって成長します。

命令ポインタ / プログラムカウンタ

命令ポインタ(IP)レジスタは、現在実行中の命令の現在のオフセット・アドレスを格納します。 覚えておいてください。これはオフセット・アドレスであり、絶対的なアドレスではありません。

命令ポインタ(IP)は、プログラムカウンタ(PC)とも呼ばれることがあります。

32ビットマシンでは、IPは32ビットサイズで、EIPと呼ばれます。

64ビット機では、IPは64ビットで、RIPという名称が使われます。

命令レジスタ

これは、通常の手段ではアクセスできないプロセッサ内部のレジスタです。これは、命令キャッシュ内のプロセッサの制御ユニット(CU)内に格納されています。プロセッサが内部で使用するためにマイクロ命令に変換されている現在の命令が格納されます。詳しくは、プロセッサ・アーキテクチャを参照してください。

EFlagsレジスタ

EFLAGSレジスタは、x86プロセッサのステータスレジスタです。このレジスタは、現在の状態を判断するために使用されます。 これまで、実際にたくさん使ってきました。簡単な例:jc, jnc, jb, jnb命令

ほとんどの命令では、EFLAGSレジスタを操作して、その値が他の値より低いか高いかといった条件を調べることができます。

EFLAGSは FLAGSレジスタで構成されています。同様に、RFLAGSは EFLAGSと FLAGSで構成されています。

+---------- EFLAGS (32 Bits) ----+ | | |-- FLAGS (16 bits)- | | | | ==================================================================== < Register Bits | | +------------------------- RFLAGS (64 Bits) -----------------------+ | | Bit 0 Bit 63

FLAGS Register Status Bits
Bit Number Abbrivation Description
0 CF Carry Flag - Status bit
1 Reserved
PF CF Parity Flag
3 Reserved
4 AF Adjust Flag - Status bit
5 Reserved
6 ZF Zero Flag - Status bit
7 SF Sign Flag - Status bit
8 TF Trap Flag (Single Step) - System Flag
9 IF Interrupt Enabled Flag - System Flag
10 DF Direction Flag - Control Flag
11 OF Overflow Flag - Status bit
12-13 IOPL I/O Priviledge Level (286+ Only) - Control Flag
14 NT Nested Task Flag (286+ Only) - Control Flag
15 Reserved
EFLAGS Register Status Bits
Bit Number Abbrivation Description
16 RF Resume Flag (386+ Only) - Control Flag
17 VM v8086 Mode Flag (386+ Only) - Control Flag
18 AC Alignment Check (486SX+ Only) - Control Flag
19 VIF Virtual Interrupt Flag (Pentium+ Only) - Control Flag
20 VIP Virtual Interrupt Pending (Pentium+ Only) - Control Flag
21 ID Identification (Pentium+ Only) - Control Flag
22-31 Reserved
RFLAGS Register Status Bits
Bit Number Abbrivation Description
32-63 Reserved

IO特権レベル(IOPL)は、特定の命令を使用するために必要な現在のリングレベルを制御します。 例えば、CLI、STI、INおよびOUT命令は、現在の特権レベルがIOPLと同じかそれ以上の場合にのみ実行されます。そうでない場合は、プロセッサによってGPF(General Protection Fault)が生成されます。

ほとんどのオペレーティングシステムでは、IOPFは0または1に設定されています。これは、カーネルレベルのソフトウェアだけがこれらの命令を使用できることを意味します。 これは非常に良いことです。結局のところ、アプリケーションがCLIを発行すれば、実質的にKernelの実行を停止させることができるのです。

ほとんどの操作では、FLAGSレジスタを使うだけでよいのです。RFLAGSレジスタの最後の32ビットは、nill、null、non existant、見る楽しみのために存在することに注意してください。つまり...そうです。もちろんスピードアップのためですが、多くのバイトが無駄になっています... ...そうです。

この表はサイズが大きいので、後で参照するためにプリントアウトすることをお勧めします。

テストレジスタ

x86ファミリーはテスト用にいくつかのレジスタを使用しています。これらのレジスタの多くは、文書化されていません。x86シリーズでは、TR4,TR5,TR6,TR7がこれにあたります。

TR6はコマンドテストに、TR7はテストデータ用レジスタとして最もよく使われます。MOV命令でアクセスできます。このレジスタは、pmモードとリアルモードの両方において、リング0でのみ使用可能です。他の方法でアクセスすると、GPF(General Protection Fault)が発生し、トリプルフォルトになります。

デバッグレジスタ

これらのレジスタはプログラムのデバッグに使用されます。DR0、DR1、DR2、DR3、DR4、DR5、DR6、DR7です。テスト・レジスタと同様に、MOV命令でアクセスすることができます。

ブレークポイント レジスタ

DR0、DR1、DR2、DR3レジスタには、ブレークポイント条件の絶対アドレスが格納されます。ページングが有効な場合、アドレスは絶対アドレスに変換されます。 これらのブレークポイント条件は、DR7でさらに定義されています。

Debug Control Register

DR7は32ビット・レジスタで、ビット・パターンを使用して現在のデバッグ・タスクを識別します。 ここにそれがあります。
  • Bit 0...7 - Enable the four debug registers (See below)
  • Bit 8...14 - ?
  • Bit 15...23 - When the breakpoints will trigger. Each 2 bits represents a single Debug Register. This can be one of the following:
    • 00 - Break on execution
    • 01 - Break on data write
    • 10 - Break on IO read or write. No hardware currently supports this.
    • 11 - Break on data read or write
  • Bit 24...31 - Defines how large of memory to watch. Each 2 bits represents a single Debug Register. This can be one of the following:
    • 00 - One bytes
    • 01 - Two bytes
    • 10 - Eight bytes
    • 11 - Four bytes
デバッグ・レジスタは、2つの方法で有効にします。これは、LocalレベルとGlobalレベルです。異なるタスクページングなど)を使用している場合、すべてのローカルデバッグの変更は、そのタスクにのみ影響します。プロセッサは、タスク間の切り替え時に、すべてのローカルな変更を自動的にクリアします。しかし、Globalタスクは、そうではありません。

上記リストのビット0...7では,以下のようになります。

  • Bit 0: Enable local DR0 register
  • Bit 1: Enable global DR0 register
  • Bit 2: Enable local DR1 register
  • Bit 3: Enable global DR1 register
  • Bit 4: Enable local DR2 register
  • Bit 5: Enable global DR2 register
  • Bit 6: Enable local DR3 register
  • Bit 7: Enable global DR3 register

デバッグステータスレジスタ

これは、エラーが発生したときに何が起こったかを判断するためにデバッガで使用されます。プロセッサは、有効な例外エラーに遭遇すると、このレジスタの下位4ビットを設定し、例外ハンドラを実行します。

警告デバッグ・ステータス・レジスタ(DR6)は決してクリアされません。プログラムを続行させる場合は、このレジスタを必ずクリアしてください。

モデル固有レジスタ

これは、他のプロセッサにはないような、プロセッサ固有の機能を提供する特別な制御レジスタです。これらはシステムレベルであるため、リング0プログラムのみがこのレジスタにアクセスすることができます。

これらのレジスタは各プロセッサに固有であるため、実際のレジスタは変更される可能性があります。

x86には、このレジスタにアクセスするための特別な命令が2つあります。

  • RDMSR- MSRから読み出し
  • WRMSR- MSRからの書き込み

このレジスタは非常にプロセッサに依存するものです。このため、それらを使用する前にCPUID命令を使用するのが賢明です。

あるレジスタにアクセスするには、アクセスしたいレジスタを表すAddressを命令に渡さなければなりません。

インテルは長年にわたり、マシン固有ではないMSRをいくつか使用してきました。これらのMSRは、x86アーキテクチャでは一般的なものです。

Model Specific Registers (MSRs)
Register Address Register Name IA-32 Processor Family
0x0 IA32_PS_MC_ADDR Pentium Processors
0x1 IA32_PS_MC_TYPE Pentium 4 Processors
0x6 IA32_PS_MONITOR_FILTER_SIZE Pentium Processors
0x10 IA32_TIME_STAMP_COUNTER Pentium Processors
0x17 IA32_PLATFORM_ID P6 Processors
0x1B IA32_APIC_BASE P6 Processors
0x3A IA32_FEATURE_CONTROL Pentium 4 / Processor 673
0x79 IA32_BIOS_UPDT_TRIG P6 Processors
0x8B IA32_BIOS_SIGN_ID P6 Processors
0x9B IA32_SMM_MONITOR_CTL Pentium 4 / Processor 672
0xC1 IA32_PMC0 Intel Core Duo
0xC2 IA32_PMC1 Intel Core Duo
0xE7 IA32_MPERF Intel Core Duo
0xE8 IA32_APERF Intel Core Duo
0xFE IA32_MTRRCAP P6 Processors
0x174 IA32_SYSENTER_CS P6 Processors
0x175 IA32_SYSENTER_ESP P6 Processors
0x176 IA32_SYSENTER_IP P6 Processors

ここに挙げた以外にも、MSRはたくさんあります。完全なリストは、 インテル開発マニュアルの付録Bを参照してください。

このシリーズはまだ開発中なので、どのMSRを参照するかはわかりません。必要に応じて、このリストに追加していく予定です。

RDMSR命令

この命令では、CXで指定されたMSRをEDX:EAXにロードします。

この命令は特権的な命令で、Ring 0 (Kernel Level)でしか実行することができません。特権のないアプリケーションがこの命令を実行しようとした場合、またはCSの値が有効なMSRアドレスを表していない場合、一般保護フォールトまたはトリプル・フォールトが発生します。

この命令は、いかなるフラグにも影響を与えません。

この命令の使用例です(チュートリアルの後半で再び登場します)。

; This reads from the IA32_SYSENTER_CS MSR mov cx, 0x174 ; Register 0x174: IA32_SYSENTER_CS rdmsr ; Read in the MSR ; Now EDX:EAX contains the lower and upper 32 bits of the 64 bit register

WRMSR命令

EDX:EAX に格納されている 64 ビット値を CX で指定された MSR に書き込みます。

この命令は特権命令であり、Ring0(Kernel Level)でのみ実行可能です。特権のないアプリケーションがこの命令を実行しようとした場合、またはCSの値が有効なMSRアドレスを表していない場合、一般保護フォールトまたはトリプル・フォールトが発生します。

この命令は、いかなるフラグにも影響を与えません。

以下は、その使用例です。

; This writes to the IA32_SYSENTER_CS MSR mov cx, 0x174 ; Register 0x174: IA32_SYSENTER_CS wrmsr ; Write EDX:EAX into the MSR

制御レジスタ

ここが重要なポイントになりそうです。

コントロール・レジスタは、プロセッサの動作を変更するためのものです。それらはCR0、CR1、CR2、CR3、CR4です。

CR0 コントロール・レジスタ

CR0は主制御レジスタです。32ビットで、次のように定義されています。
  • Bit 0 (PE) : Puts the system into protected mode
  • Bit 1 (MP) : Monitor Coprocessor Flag This controls the operation of the WAIT instruction.
  • Bit 2 (EM) : Emulate Flag. When set, coprocessor instructions will generate an exception
  • Bit 3 (TS) : Task Switched Flag This will be set when the processor switches to another task.
  • Bit 4 (ET) : ExtensionType Flag. This tells us what type of coprocesor is installed.
    • 0 - 80287 is installed
    • 1 - 80387 is installed.
  • Bit 5 (NE): Numeric Error
    • 0 - Enable standard error reporting
    • 1 - Enable internal x87 FPU error reporting
  • Bits 6-15 : Unused
  • Bit 16 (WP): Write Protect
  • Bit 17: Unused
  • Bit 18 (AM): Alignment Mask
    • 0 - Alignment Check Disable
    • 1 - Alignment Check Enabled (Also requires AC flag set in EFLAGS and ring 3)
  • Bits 19-28: Unused
  • Bit 29 (NW): Not Write-Through
  • Bit 30 (CD): Cache Disable
  • Bit 31 (PG) : Enables Memory Paging.
    • 0 - Disable
    • 1 - Enabled and use CR3 register
わあ...新しいものがたくさんあるね?Bit 0は、システムをプロテクトモードにします。つまり、CR0レジスタのBit 0を設定することで、実質的にプロテクトモードに入ることになります。

例えば、以下のようになります。

mov ax, cr0 ; get value in CR0 or ax, 1 ; set bit 0--enter protected mode mov cr0, ax ; Bit 0 now set, we are in 32 bit mode!
すごい、簡単ですね。そうでもないですよ。

このコードをブートローダにダンプすると、ほぼ確実にトリプルフォールトになります。プロテクトモードは、リアルモードとは異なるメモリアドレシングシステムを使用します。また、pmodeには割り込みがないので、1回のタイマ割り込みでトリプルフォルトになります。また、異なるアドレッシングモデルを使用しているため、CSは無効です。32ビットコードに移行するためには、CSを更新する必要があります。さらに、メモリマップの特権レベルは設定されていません。

詳しくは後ほど説明します。

CR1コントロール・レジスタ

インテルが予約したもので、使用しないでください。

CR2コントロール・レジスタ

Page Fault Linear Address (ページフォルトリニアアドレス)。Page Fault Exceptionが発生した場合,CR2にはアクセスしようとしたアドレスが格納されます.

CR3コントロールレジスタ

CR0のPGビットがセットされている場合に使用されます.下位20ビットにページディレクトリ・ベース・レジスタ(PDBR)が格納されています.

CR4コントロールレジスタ

プロテクトモードにおいて、v8086モード、I/Oブレークポイントの有効化、ページサイズ拡張、マシンチェック例外などの動作を制御するために使用されます。

これらのフラグを使用するかどうかはわかりません。これらのフラグが何であるか理解できなくても、あまり気にしないでください。

  • Bit 0 (VME) : Enables Virtual 8086 Mode Extensions
  • Bit 1 (PVI) : Enables Protected Mode Virtual Interrupts
  • Bit 2 (TSD) : Time Stamp Enable
    • 0 - RDTSC instruction can be used in any privilege level
    • 1 - RDTSC instruction can only be used in ring 0
  • Bit 3 (DE) : Enable Debugging Extensions
  • Bit 4 (PSE) : Page Size Extension
    • 0 - Page size is 4KB
    • 1 - Page size is 4MB. With PAE, it is 2MB.
  • Bit 5 (PAE) : Physical Address Extension
  • Bits 6 (MCE) : Machine Check Exception
  • Bits 7 (PGE) : Page Global Enable
  • Bits 8 (PCE) : Performance Monitoring Counter Enable
    • 0 - RDPMC instruction can be used in any privilege level
    • 1 - RDPMC instruction can only be used in ring 0
  • Bits 9 (OSFXSR) : OS Support for FXSAVE and FXSTOR instructions (SSE)
  • Bits 10 (OSXMMEXCPT) : OS Support for unmasked SIMD FPU exceptions
  • Bits 11-12 : Unused
  • Bits 13 (VMXE) : VMX Enable

CR8制御レジスタ

タスクプライオリティレジスタ(TPR)へのリード/ライトアクセスを提供します。

PModeセグメンテーションレジスタ

x86ファミリーは、各セグメントディスクリプタの現在のリニアアドレスを格納するために、いくつかのレジスタを使用します。 これについては後で詳しく説明します。

これらのレジスタは

  • GDTR - Global Descriptor Table Register
  • IDTR - Interrupt Descriptor Table Register
  • GDTR - Local Descriptor Table Register
  • TR - Task Register
これらのレジスタについては、次のセクションで詳しく見ていきます。

プロセッサ・アーキテクチャ

このシリーズを読んでいると、プロセッサとマイクロコントローラの間に多くの類似点があることに気づきます。 つまり、マイクロコントローラにはレジスタがあり、プロセッサと同じように命令を実行することができるのです。CPU自体は特殊なコントローラチップに過ぎません。

ブートプロセスについては、もう少し後で、非常に低レベルの観点から、再度見ていきます。これは、BIOS POSTが実際にどのように開始され、POSTを実行し、プライマリプロセッサを開始し、BIOSをロードするかに関する多くの質問に答えるものです。何をするかは説明しましたが、どのようにするかはまだ説明していません。

注意:このセクションはかなり技術的です。もし、すべてを理解できなくても、心配しないでください。このセクションは、どんなコンピューターシステムにも必要な主要コンポーネントであり、私たちのコードを実行する役割を担っているものに飛び込むためです。機械語はどのように私たちのコードを実行するのでしょうか?機械語は何がそんなに特別なのか?これらについては、ここですべてお答えします。

この後、カーネルと デバイスドライバの開発に入りますが、基本的なハードウェアコントローラのコンポーネントを理解することは、素晴らしい学習体験になるだけでなく、そのコントローラのプログラミング方法を理解するために必要な場合もあることを学ぶことができます。

プロセッサを分解する

ここでは、説明のためにPentium IIIプロセッサを取り上げます。まず、このプロセッサを開いて、個々の部品に分解してみましょう。

Alot of things in the processor, huh? Notice how complex this is. We are not going to learn much from this picture alone, so lets look at each component.

  • L2: Level 2 Cache
  • CLK: Clock
  • PIC: Programmable Interrupt Controller
  • EBL: Front Bus Logic
  • BBL: Back Bus Logic
  • IEU: Integer Execution Unit
  • FEU: Floating Point Execution Unit
  • MOB: Memory Order Buffer
  • MIU / MMU: Memory Interface Unit / Memory Management Unit
  • DCU: Data Cach Unit
  • IFU: Instruction Fetch Unit
  • ID: Instruction Decoder
  • ROB: Re-Order Buffer
  • MS: Microinstruction Sequencer
  • BTB: Branch Target Buffer
  • BAC: Branch Allocater Buffer
  • RAT: Register Alias Table
  • SIMD: Packed floating point
  • DTLB: Data TLB
  • RS: Reservation Station
  • PMH: Page Miss Handler
  • PFU: Pre-Fetch Unit
  • TAP: Test Access Port

命令の実行方法

IPレジスタには現在実行中の命令のオフセット・アドレスが格納され、CSにはセグメント・アドレスが格納されていることを思い出してください。

では、プロセッサが命令を実行する場合、具体的にはどのようなことが起こるのでしょうか。

まず、読み出すべき絶対アドレスが計算されます。セグメント:オフセットモデルでは、絶対アドレス=セグメント×16+オフセットであることを思い出してください。あるいは、本質的には、絶対アドレス = CS * 16 + IPです。

プロセッサは、このアドレスをアドレスバスにコピーします。アドレスバイトは、それぞれが1つのビットを表す一連の電子線であることを忘れないでください。このビットパターンは、次の命令の絶対アドレスのバイナリ形式を表しています。

この後、プロセッサは「Read Memory」ラインを有効にします(そのビットを1に設定することで)。これは、メモリコントローラに対して、メモリからの読み出しが必要であることを伝えるものです。

メモリコントローラは制御を行う。メモリコントローラは、アドレスバスからアドレスをコピーし、パリキュラーRAMチップの正確な位置を計算します。メモリコントローラは、この位置を参照し、データバスにコピーする。これは、Control Busに "Read Memory "ラインが設定されているためです。

メモリコントローラはコントロールバスをリセットし、プロセッサが実行を終了したことを認識できるようにします。プロセッサはデータバスの値を受け取り、デジタル・ロジック・ゲートで「実行」します。この「値」は機械命令のバイナリ表現に過ぎず、一連の電子パルスとしてエンコードされています。

例えば、mov ax, 0x4c00という命令があった場合、プロセッサのデータバスには0xB8004Cという値が格納されます。0xB8004Cは、オペレーションコード(OPCode)と呼ばれるものです。すべての命令には、それに関連するオペコードがあります。i86アーキテクチャの場合、この命令はオペコード0xB8004Cと評価されます。 この数字を2進数に変換すると、電子線としてパターンを見ることができ、1がハイ(アクティブ)、0がローを意味します。

101110000000000001001100
プロセッサは、CPUのデジタル論理回路に組み込まれた一連の個別命令に従います。 これらの命令は、プロセッサに一連のビットをどのように符号化するかを指示します。すべてのx86プロセッサは、このビットパターンをmov ax, 0x4c00命令としてエンコードします。

命令が複雑化したため、新しいプロセッサのほとんどは、実際に独自の内部命令セットに従っています。マイクロコントローラの多くは、電子回路の複雑さを軽減するために、複数の内部命令セットを使用しています。通常、これらはマクロコードと マイクロコードである。

マクロコードは、プロセッサが命令をマイクロコードにデコードするために使用する抽象的な命令セットである。マクロコードは通常、電子技術者が開発した特殊なマクロ言語で記述され、コントローラ内部のROMチップに格納され、マクロアセンブラでコンパイルされる。マクロアセンブラは、マクロコードをさらに低レベルの言語(コントローラの言語)にアセンブルする。これがコントローラの言語である「マイクロコード」である。

マイクロコードは、電子技術者が開発した非常に低レベルな言語である。マイクロコードは、コントローラやプロセッサが命令(例えば、0xB8004C(mov ax, 0x4c00)命令)をデコードするために使用される。

CPUはALU(Arithmitic Logic Unit)を使って、0x4C00という数字を得ることができます。そして、それをAXにコピーします(単純なビットコピー)。

この例は、すべてがどのように組み合わされるかを示しています。CPUはシステムバスを使用し、メモリコントローラがメモリロケーションをデコードし、コントロールバスをフォローする仕組みになっています。

これは重要なコンセプトです。Software Portsも同じようにメモリコントローラに依存しています。

プロテクトモード - 理論

さて、ではなぜアーキテクチャの話をしたのでしょうか。実は、プロテクトモードでは割り込みが発生しないのです。つまり...割り込みがない。システムコールもない。標準ライブラリもないすべて自分でやらなければならないのです。そのため、私たちを導いてくれる助けの手はありません。 一つの間違いでシステムがクラッシュしたり、気をつけないとハードウェアが壊れたりします。 フロッピーディスクだけでなく、ハードディスク、外部(および内部)デバイスなどです。

システムアーキテクチャを理解することは、私たちが多くの間違いを犯さないように、すべてをより良く理解するのに役立ちます。また、直接ハードウェアプログラミングの入門にもなりますし、私たちができるのはこれだけですから。

あなたはこう思うかもしれません。待ってください、あなたが約束した超高性能の1337 Cカーネルはどうなっているんですか!そうですね...C言語はある意味、低レベルの言語であることを思い出してください。インライン・アセンブルによって、ハードウェアとのインターフェイスを作ることができたのです。そしてCは、C++と同じように、プロセッサが直接実行できるx86マシン命令しか生成しない。ただし、標準ライブラリがないことだけは覚えておいてほしい。そして、たとえ高級言語を使っていても、非常に低レベルな環境でプログラミングをしていることになる。

この点については、カーネルを開始するときに説明します。

まとめ

私はこの種のチュートリアルを書くのが好きではありません。チュートリアルは、膨大な量の情報を詰め込み、コードは少なく、理解しやすいようにコンセプトを具体的に表示します。単純に書くのが大変なんです、わかりますか?

私は、すべてを十分に説明できたと思います。メモリマッピング、ポートマッピング、x86のポートアドレス、x86の全レジスタ、x86メモリマップ、システムアーキテクチャ、IN/OUTキーワードとその実行方法、そして命令の実行方法について、順を追って見ていきました。また、基本的なハードウェアのプログラミングについても取り上げました。

次回のチュートリアルでは、32ビットの世界へようこそ!ということで、32ビットへの移行を行います。また、GDTについても詳しく見ていきますが、これは切り替えの際に必要となるものです。 また、各ステップごとに、よくあるエラーに対する警告を出すつもりです。前にも言いましたが、プロテクトモードに入るときにちょっとしたミスをするとプログラムがクラッシュしてしまいます。

楽しくなりそうです... :)