プロテクトモード
プロテクトモードとは、80286以降のx86アーキテクチャのCPUの動作モードの一つ。正式名称は Protected Virtual Address Mode(保護仮想アドレスモード)である[1]。メモリやI/Oの保護を行うと共に、アドレス空間の拡張を行ったモードである。このモードでは仮想記憶、ページング、安全なマルチタスクといった機能をシステムソフトウェアが使えるようになり、アプリケーションソフトウェアへのオペレーティングシステム (OS) の制御能力が向上するよう設計されている[2][3]。
目次
概要
プロテクトモードとはIntel 80286以降のCPUの命令セットアーキテクチャの動作モードの一つであり、これらのCPUの本来の動作モードといえる物である。特徴づけるものは、その名前の通り、階層的な特権管理(リングプロテクション)や、タスク間のメモリ保護(プロテクト)を行う事が可能な事である。
プロテクトモードをサポートしているx86系プロセッサを起動したとき、それ以前のx86系プロセッサとの後方互換を保つためリアルモードで命令を実行する[4]。プロテクトモードに移行するには、システムソフトウェアがいくつかのディスクリプタテーブルを設定してから、コントロールレジスタ0 (CR0) のPE (Protection Enable) ビットをセットしなければならない[5]。
プロテクトモードは1982年、インテルの 80286 (286) プロセッサのリリースと共にx86アーキテクチャに追加され[6]、1985年の 80386 (386) で拡張された[7]。プロテクトモードの追加による機能向上によって広く採用され、同時にそれがその後のx86アーキテクチャの全ての強化の基礎となった[8]。
プロテクトモードにおいては、メモリ上のグローバルディスクリプタテーブル (GDT) およびローカルディスクリプタテーブル (LDT) という構造体の配列をそれぞれ最大8192個管理する。この構造体はそれぞれ、リニアアドレスのポインタおよび大きさおよび保護情報を持つセグメントディスクリプタや、タスクセグメントへのポインタとサイズ、保護情報を持つタスクディスクリプタ、ローカルディスクリプタテーブルへのポインタを保持するもの、そしてコールゲートと呼ばれる特権等を変更するための呼び先である、ゲートディスクリプタ等を持つ。セグメントレジスタはセレクタと呼ばれGDTまたはLDTのオフセットでセグメントディスクリプタを指すものとなる。無効なディスクリプタをセレクタにロードしたりすると例外を発生するようになった。タスクセグメントは、LDTRを含むレジスタ等の実行環境を保持する。
また、割り込み/例外ベクタも、最低位アドレスに固定される事無く、割り込みディスクリプタテーブル (IDT) にあるゲートディスクリプタの配列により設定されることになった。
80286以降で物理メモリの拡張も行われたが、100000h以降(1MB以降)のアドレスの物理メモリは、HMAを除けばプロテクトモードを使用しない限り、CPUからはアクセスできない領域であった。そのため、この実質的にプロテクトモード専用の1MB以降の物理メモリ領域は通称プロテクトメモリと呼ばれた。 プロテクトモードでは、コンベンショナルメモリ以外にプロテクトメモリも利用できるので、利用可能な物理メモリも増大した。この利用可能なメモリの増大もプロテクトモードを使用する利点の一つである。 プロテクトメモリを利用するためには、本来UNIXやOS/2等のマルチタスクOSが必要であるが、MS-DOS上でもEMS・XMSドライバ、DOSエクステンダ等を使用すれば利用可能であった。
歴史
286に先立つ Intel 8086 での本来の設計では、メモリアクセス用アドレスバスは20ビット幅だった[9]。これにより 220 バイト、すなわち1メガバイトのメモリにアクセス可能である[9]。当時は1メガバイトといえばかなり大容量のメモリという感覚だったので[10]、IBM PC の設計者らは先頭640キロバイトをアプリケーションとオペレーティングシステムで使用し、残る384キロバイトをBIOS (Basic Input/Output System) や周辺機器で使用する設計にした[11]。
元々、8086においては、セグメント方式の仮想記憶を意識したセグメントレジスタがあり、コード、データおよびスタックの量がそれぞれ64キロバイト以内であれば、ロードされた物理アドレスを意識すること無く、タスクの実行が可能であったが、論理アドレスから物理アドレスへの変換は単純にセグメントレジスタを4ビットシフトして足すと言うもので、タスク間の保護もなかった。
メモリ価格が低下し、メモリ使用量が増えてくると、1MBという制約が大きな問題となってきた。インテルはこの制限に対処するためもあって 286 をリリースした[11]。
286
テンプレート:See also 最初に導入された80286においては24ビットの物理アドレス空間へのアクセス、そして1セグメントあたり最大64キロバイトの空間を提供していた。これまでに述べた機能を使ってセグメントレジスタを意識したセグメント方式の仮想記憶を使ったOS環境を作成することが可能になった。しかし286リリース当時、プロテクトモードはすぐに広く使われたわけではない[11]。プロセッサをリセットする以外にリアルモードに戻ることができないため、BIOSまたはDOSコールにアクセスすることができないなどの欠点があり、幅広い採用が妨げられた[12]。幅広い採用が見送られた他の要因として、286 では4本のセグメントレジスタでそれぞれ16ビットのセグメントしかアクセスできなかった。すなわち一度にアクセスできるメモリの範囲は 4*216 バイト(256キロバイト)に限られていた[11]。
286 は 8086 との互換性を保つため、起動時にはリアルモードで動作を開始するようになっていた[4]。リアルモードでは 8086 と全く同じ動作をするので、古いソフトウェアも 286 で修正せずに動作することができる。286 の拡張機能にアクセスするには、オペレーティングシステムがプロセッサをプロテクトモードに移行させる必要がある。それによって24ビット・アドレッシングが可能になり、224 バイト(16メガバイト)のメモリにアクセス可能となる[9]。
保護された空間外へのメモリアクセスなどを行うと、たちどころに例外割り込みが発生して処理が停止する。80286の16ビット PVAMにおいて全ての保護違反例外はただひとつの割り込みベクタ…悪名高き「一般保護例外 (General Protection Fault)」INT#13 に割り当てられており、一旦これが起きてしまうとそのプロセス(あるいはタスクまたはスレッド)の処理を復帰させる方法がない。すなわち外部記憶装置を持つ仮想記憶システムにおいてページ違反を例外処理として検出し、ページスワップを行ってからプロセスを再開させることができない(セグメントディスクリプタには「不在ビット」の属性があり、これを用いた仮想記憶管理を行うことは可能なのだが、メモリアクセス違反を流用した仮想記憶管理ができない)。これはそもそもインテルが80286を80186の上位機種であり「最高の信頼性を持つ制御用プロセッサ」として位置づけており、外部仮想記憶を持つような高度な処理はiAPX432 32ビットプロセッサによって実現する構想を抱いていたことに起因する。80286は、工業用ミニコンピュータのアーキテクチャにならったもので、間違った前提のまま動き続けるよりは間違いを検出して止まったほうが被害が少ない、という思想に基づくものであった。
しかし、インテルの思惑とは裏腹に、80286は汎用コンピュータである IBM PC/ATおよびIBM PS/2に搭載されてしまい、その上で汎用OSであるOS/2を動かす必要に迫られ、80286は、本来の設計思想とは異なる領域で、アーキテクチャを無理にねじ曲げた使い方を強いられることになってしまい、「石頭」「こんだけ苦労してたった16MB」「しかも相変わらずセグメントあたり64KBの制限つき」「Brain Damaged CPU」などの悪名を高めてしまうことになる。
386
1985年[7]にリリースされた 386 では、プロテクトモードの採用を妨げていた様々な問題への対処が行われている[11]。386のアドレスバスは32ビット幅で、232 バイト(4ギガバイト)のメモリにアクセス可能である[13]。セグメントも32ビットに拡大され、複数のセグメントを切り換えることなく4ギガバイトのアドレス空間にアクセス可能となった[13]。アドレスバスとセグメントレジスタの拡大に加えて、セキュリティと安定性を向上させるための様々な機能が追加されている[14]。
プロテクトモードはその後、Microsoft Windows やLinuxなどほぼ全てのx86アーキテクチャ(IA-32)上のオペレーティングシステムで使われるようになった[15]。
386のプロテクトモードで追加された機能
386のリリースに際し、以下の機能がプロテクトモードに追加された[2]。
- ページング方式
- 32ビット物理および仮想アドレス空間(32ビット物理アドレス空間は 80386SX ではサポートされておらず、他の386派生プロセッサも古い286型のバスを使用していた[16])
- 32ビットのセグメントオフセット
- リアルモードへの復帰
- 仮想86モード
プロテクトモードへの(からの)移行
386がリリースされる以前は、プロテクトモードにいったん入った後、リアルモードに戻る直接的方法が提供されていなかった。IBMはその対処として、キーボードコントローラからCPUをリセットする技法を考案し、その際にシステムレジスタ群、スタックポインタ、割り込みマスクをリアルタイムクロックのチップにあるRAMにセーブするようにした。これによりBIOSがCPUをほぼ同じ状態に復旧することが可能となり、リセット前のコードの実行を続行できる。その後、トリプルフォールトを発生させて286CPUをリセットするという手段がとられた。こちらの方がキーボードコントローラを使うよりも高速できれいだった。
プロテクトモードに入るには、少なくとも3エントリ(ヌルディスクリプタ、コードセグメントディスクリプタ、データセグメントディスクリプタ)を持つGDTを作る必要がある。そして、アドレスバスのA20線(21番目のアドレス線)をイネーブルにし、1メガバイトを越えるメモリにアクセスできるようにする必要がある(電源投入直後は、古いソフトウェアの互換性を保証するため、20番目までのアドレス線しか使わない設定になっている)。この2段階を実行後、CR0レジスタのPEビットをセットして、far jump 命令を実行することで命令プリフェッチキューをクリアしなければならない。
; set PE bit
mov eax, cr0
or eax, 1
mov cr0, eax
; far jump (cs = selector of code segment)
jmp cs:@pm
@pm:
; Now we are in PM.
386以降でプロテクトモードからリアルモードに移行するには、セグメントレジスタをリアルモードの値にし、A20 線をディセーブルし、CR0レジスタのPEビットをクリアすればよく、286で必要だった初期設定が不要になった。
機能と特徴
プロテクトモードには、セキュリティとシステム安定性を向上させるべくOSのアプリケーションに対する制御を強化するよう設計された機能がいくつかある[3]。それら追加機能により、適切なハードウェアサポートなしではかなり難しいか不可能であったOS機能の実装が可能になった[17]。
特権レベル
特権は数字が低い程高い特権で0,1,2,3の4段階の特権リングがある。リングの使用により、システムソフトウェアがタスクのデータやコールゲートへのアクセスまたは特権命令実行を制限できるようになった[18]。大抵の環境では、OSと一部のデバイスドライバがリング0で、アプリケーションがリング3で動作する[18]。高い特権のコードへの呼出はゲートディスクリプタや例外を通す必要がある。
リアルモードのアプリケーションの互換性
Intel 80286 Programmer's Reference Manual には以下のような記述がある[19]。
リアルモードのコードとのバイナリ互換性の観点で、アプリケーションプログラマにとって最も明白な変化は、物理メモリが最大16MBまでアクセス可能になった点と1GBの仮想記憶であった[19]。これには制限がなかったわけではなく、以下のような技法を使っていたアプリケーションはプロテクトモードでは動作しなかった[20]。
- セグメント計算
- 特権命令を使用
- ハードウェアへの直接アクセス
- コードセグメントの書き換え
- データの実行
- セグメントのオーバーラップ
- インテルによって確保されているBIOS割り込みのためにBIOS関数を使用[21]
実際にはほぼ全てのDOSアプリケーションプログラムがこれらのルールのいずれかに反していた[22]。そのため、386では仮想86モードが導入された。そういった潜在的問題はあったが、Windows 3.0 とその後継では Windows 2.x でのリアルモードの各種アプリケーションをプロテクトモードで動作させるのに互換性を利用している[23]。
仮想86モード
テンプレート:Main 386のプロテクトモードでは、インテルが仮想8086モード (virtual 8086 mode) と呼ぶものを提供している。仮想86モードは8086向けのコードを修正することなく、プロテクトモードのOS上のタスクの1つとして安全に動作させることができる[24]。ただし完全な後方互換性があるわけではない。セグメント操作や特権命令、ハードウェアへの直接アクセス、自己書き換えコードなどを使っているプログラムの場合、例外が発生するのでOSがそれに対処しなければならない[25]。さらに仮想86モードで動作するアプリケーションが入出力 (I/O) 関連命令を使用するとトラップ処理が行われるので、性能が低下する[26]。そういった制約があるため、8086上のプログラムの一部は仮想86モードでは動作できない。結果としてシステムソフトウェアは、古いソフトウェアを扱う際にセキュリティか互換性のいずれかを犠牲にすることになった。例えば Windows NT では互換性を犠牲にし、行儀の悪いDOSアプリケーションをサポートしなくなった[27]。
セグメント方式
テンプレート:See also
リアルモードでは、論理アドレスは2つの16ビットの部分で構成されており、論理アドレスが直接物理的メモリ位置に対応している。論理アドレスのセグメント部は16バイトでセグメントのベースアドレスを指しており、セグメントは物理アドレス 0, 16, 32, ..., 220-16 から始まる。論理アドレスのオフセット部はセグメント内のオフセットであり、物理アドレスは physical_address : = segment_part × 16 + offset
という式で計算できる(アドレスバスのA20線がイネーブル状態の場合[28])。各セグメントのサイズは 216 バイトである。
プロテクトモード
プロテクトモードでは、セグメント部が16ビットの「セレクタ」で置き換えられ、セレクタの上位13ビット(ビット 3 から ビット15)がディスクリプタテーブル内のエントリのインデックスとなっている。下位2ビット(ビット 0 とビット 1)は要求の特権レベルを定義しており、0から3までの値をとり、0が最も特権が高く、3が最も特権が低い。残るビット(ビット 2)はGDTかLDTの指定に使われる。
ディスクリプタテーブルのエントリには以下の情報が含まれる。
- セグメントの「リニア」アドレス
- セグメントの大きさを示すリミット値
- いくつかの属性ビット群(フラグ)
286
ディスクリプタテーブルのエントリ内のセグメントアドレスは24ビットであり、リアルモードとは異なり任意のアドレスからセグメントを開始できるようになった。リミット値は16ビット幅であり、セグメントの大きさは1バイトから 216 バイトまで指定可能である。これに基づいて計算したリニアアドレスは物理メモリアドレスとなる。
386
ディスクリプタテーブルのエントリ内のセグメントアドレスは32ビットに拡張されている。リミット値は20ビット幅に拡張され、しかもG-ビットで倍率を指定できる。
- G-ビットがゼロなら、リミット値の倍率は1であり、セグメントの大きさは1バイトから 220 バイトまでとなる。
- G-ビットが1なら、リミット値の倍率は4096倍であり、セグメントの大きさは 4096 の倍数で4096バイトから4ギガバイトまでとなる。
ページングを使わない場合、リニアアドレスはそのまま物理メモリアドレスとなるが、ページングを使う場合はリニアアドレスはページング機構への入力となる。
386プロセッサではアドレスオフセットとしても32ビットの値を使用する。
286のプロテクトモードとの互換性を維持するため、D-ビットが追加されている。D-ビットがオフとなっているコードセグメントは16ビット・セグメントと解釈され、その中の命令は16ビット命令として実行される。
セグメントディスクリプタのエントリの構造
B | ビット | 80286 | 80386 | B |
---|---|---|---|---|
0 | 00..07,0..7 | リミット値 | リミット値のビット0..15 | 0 |
1 | 08..15,0..7 | 1 | ||
2 | 16..23,0..7 | セグメントベースアドレス | ベースアドレスのビット 0..23 | 2 |
3 | 24..31,0..7 | 3 | ||
4 | 32..39,0..7 | 4 | ||
5 | 40..47,0..7 | 属性フラグ #1 | 5 | |
6 | 48..51,0..3 | 未使用 | リミット値のビット 16..19 | 6 |
52..55,4..7 | 属性フラグ #2 | |||
7 | 56..63,0..7 | ベースアドレスのビット 24..31 | 7 |
- カラム B: エントリ内のバイトオフセット
- カラム ビット の前半部: エントリ内のビットオフセット
- カラム ビット の後半部: バイト内のビットオフセット
属性フラグ #2 | ||
---|---|---|
52 | 4 | 未使用でありOSで使用可能 |
53 | 5 | 予約済み。常に0 |
54 | 6 | D-ビット(0:16ビット・セグメント、1:32ビットセグメント) |
55 | 7 | G-bit(リミット値の倍率、1なら4096倍) |
ページング方式
386では仮想86モードだけでなく、ページング方式がプロテクトモードに追加された[29]。ページングを使うことでシステムソフトウェアはページと呼ばれるメモリ単位毎にタスクのアクセスを制限し制御することができる。多くのOSでページングを使ってタスク毎の仮想アドレス空間を作成している。それによって、あるタスクが他のタスクのメモリを操作することを防ぐ。また、ページ単位で主記憶装置からより低速だが大容量の補助記憶装置(ハードディスクなど)へ移すこともできる[30]。それにより、主記憶装置の物理メモリ量以上のメモリを使用可能となる[30]。x86アーキテクチャではページの制御をページディレクトリとページテーブルという2段階の配列で行う。1ページは基本的には4キロバイトであり、ページテーブルはプロセッサによって直接読まれるため構造が決まっている。元々、ページディレクトリの大きさは1ページ、すなわち4キロバイトで、1,024個のページ・ディレクトリ・エントリ (PDE) がそこに格納されていた。PDEにはページテーブルへのポインタが含まれている。ページテーブルも本来は4キロバイトの大きさで、1,024個のページ・テーブル・エントリ (PTE) で構成されている。PTEには実際の物理ページのアドレス(ポインタ)が含まれており、その場合の各ページは4キロバイトである。これで1024×1024×4096バイト、すなわち4GBの論理空間を全て指すことが可能になる。
Pentium Pro以降に導入された物理アドレス拡張(PAE)が有効である場合は、一エントリあたりが8バイトとなり、もう一段4つのエントリを持つ32バイトのテーブルが追加される。これにより、64ビットの物理アドレス空間の任意のアドレスを4×512×512×4096バイトの32ビットの空間に配置することが可能になるが、実際の物理アドレス空間はもっと少ない。[31] このページエントリの構造は段数を増やしてAMD64でも継承されている。また、Pentiumからはページサイズ拡張 (PSE) という拡張により、一部のページを一段へらして4メガバイト(PAEの時は2メガバイト)の大きなページとしてマップすることが可能になっている。Pentium II以降においては、PAEでない通常のページテーブルエントリの構造を保ちながら4MBのページに限り36ビットの物理空間にアクセス可能な36ビットPSEとよばれる機能も提供している。
マルチタスク
テンプレート:See also 286で導入されたリング、特権コールゲート、テンプレート:仮リンク (TSS) により、x86アーキテクチャでプリエンプティブマルチタスクが可能となった。TSSにより、他のタスクに影響を及ぼすことなく汎用レジスタ群、セグメントセレクタフィールド群、スタック群を書き換え可能となった。TSSはまた、タスク毎に特権レベルやI/Oポート許可マップを設定できる。
多くのOSはTSSの全機能を使ってはいない[32]。これは移植性を考慮したためでもあるし、ハードウェアによるタスク切り替えが性能問題を抱えているためでもある[32]。結果として多くのOSはハードウェアとソフトウェアを共に活用してマルチタスクを実現している[33]。
OS
OS/2 1.x では、プロテクトモードとリアルモードの切り替えを試みている。しかしその技法は低速であり、リアルモードではコンピュータが容易にクラッシュするため、安全ではなかった。OS/2 1.x ではプログラミングに制約を課して、両方のモードで動作可能なプログラムを書ける方法を定義していた。初期のUNIX、OS/2 1.x、Windows でこのようなモードの使い方が見られた。Windows 3.0 ではリアルモードのプログラムを16ビット・プロテクトモードで動作させることができた。Windows 3.0 でプロテクトモードに切り換えたとき、リアルモードと同じ単一特権レベルのモデルを保持したため、Windows ではアプリケーションやDLLが割り込みにフックを仕掛けることができ、ハードウェアに直接アクセス可能になっていた。この方式は Windows 9x系でもそのまま維持されている。Windows 1.x や 2.x のプログラムが行儀よくプログラミングされていて、セグメントアドレスを計算で求めたりしていない場合、リアルモードでもプロテクトモードでも同じように動作可能だった。Windowsではソフトウェアによる仮想記憶を実装しており、プログラムが動作していないときにそのコードやデータのセグメントを移動させることがあったため、絶対アドレスを操作することは危険であり、一般に Windows プログラムはセグメント計算を避けていた。動作していないプログラムが保持できるのはメモリブロックのハンドルだけだった。プロテクトモードで動作中の Windows 3.0 で古いプログラムを起動すると、Windowsをリアルモードで動作させるかアプリケーションの新しいバージョンを取得することを推奨する警告ダイアログが表示されていた。Windows 3.1 ではリアルモードが廃止されている。
16ビットのプロテクトモードは今も一部アプリケーションで使われている。例えばDPMI互換のDOSエクステンダプログラム(仮想DOSマシン経由)、Windows 3.x 用アプリケーション(テンプレート:仮リンク サブシステム経由)、OS/2 2.0 以降での一部のデバイスドライバ(たとえばBIOS機能を使ってディスプレイの解像度を変更するもの)などであり、いずれも32ビットカーネルの制御下で動作する。
脚注
参考文献
関連項目
外部リンク
- Protected Mode Basics
- Introduction to Protected-Mode
- Overview of the Protected Mode Operations of the Intel Architecture
- Intel 64 and IA-32 Architectures Software Developer's Manuals
- TurboIRC.COM tutorial to enter protected mode from DOS
- Code Project Protected Mode Tutorial
- Akernelloader switching from real mode to protected mode
- ↑ テンプレート:Cite web
- ↑ 2.0 2.1 テンプレート:Harvnb
- ↑ 3.0 3.1 テンプレート:Cite web
- ↑ 4.0 4.1 テンプレート:Harvnb
- ↑ テンプレート:Cite web
- ↑ テンプレート:Harvnb
- ↑ 7.0 7.1 テンプレート:Cite web
- ↑ テンプレート:Harvnb
- ↑ 9.0 9.1 9.2 テンプレート:Cite web
- ↑ テンプレート:Cite web
- ↑ 11.0 11.1 11.2 11.3 11.4 テンプレート:Cite web
- ↑ テンプレート:Cite book
- ↑ 13.0 13.1 テンプレート:Cite book
- ↑ テンプレート:Harvnb
- ↑ テンプレート:Cite book
- ↑ テンプレート:Cite web
- ↑ テンプレート:Cite book
- ↑ 18.0 18.1 テンプレート:Harvnb
- ↑ 19.0 19.1 テンプレート:Harvnb 引用エラー: 無効な
<ref>
タグ; name "286_compatibility"が異なる内容で複数回定義されています - ↑ テンプレート:Harvnb
- ↑ テンプレート:Cite web
- ↑ テンプレート:Cite web
- ↑ テンプレート:Cite web
- ↑ テンプレート:Harvnb
- ↑ テンプレート:Harvnb
- ↑ テンプレート:Cite web
- ↑ テンプレート:Cite book
- ↑ A20線がディセーブル状態なら (segment_part × 16 + offset) mod 220 となる
- ↑ テンプレート:Cite web
- ↑ 30.0 30.1 テンプレート:Cite web
- ↑ 当初の上限は36ビット、x64命令セットをサポートしている場合は52ビット。x64命令セットではページテーブルの最上位ビットをNXビットに利用しているため、将来の拡張で残りを全部物理アドレスにしたとしても63ビットである
- ↑ 32.0 32.1 テンプレート:Cite web
- ↑ テンプレート:Cite web