保護モードとは何ですか?

OSを作成するには、多くの詳細を理解する必要があります。 それでは、少しあなたに啓発させてください(しかし、あなたがマナを自分で読んで、話をすることに同意することにしましょう)。

正直なところ、ネットワークの広大さの中でPMには大量の脂肪質の素材があり、 ileypehatはこのモードについて少し話しましたが、とにかく一般的なフレームワークでそれを説明するように頼まれました。 次に、理論を簡単に説明します(一般的に、Intelはこのために特別にマナを書きました)。次に、コードの記述を開始します。





プロテクトモードの概要。

したがって、PMはDOSのリアルモード(RM)以降、通常のすべてとは大きく異なります。 慣れる必要があります。64キロバイトの静的セグメント、1キロバイトの割り込みテーブル、セグメントレジスタのデータベースベースのアドレスなど、まったく新しい世界です。

セグメントは現在、 グローバル記述子テーブル(GDT)に記述されています。 このテーブルは、1つのコピーにのみ含めることができます。 彼女は心の構造です。 セグメントではありません! メモリ内のどこにでも配置できますが、アドレスと制限はGDTRレジスタに記録されます。 その構造は次のとおりです。



画像



テーブル自体は、次の構造のエントリで構成されています(ところで、nullエントリは空です。これは重要です。null記述子で「記述」されたメモリにアクセスする場合、#GP-General Protection Faultを取得します)

この構造を詳しく見てみましょう。

画像



1.セグメント制限:

このフィールドの目的は名前で明確ですが、微妙です。 犬はビットG(粒状性)に埋もれています。

インストールされていない場合、メモリはバイト単位で「カウント」されます。 この場合、セグメントサイズは、1バイトのサイズごとに1バイトから1メガバイトまで変化します。

1に設定すると、ページングメモリが入力されます。 次に、4キロバイト(ページサイズ)にサイズを変更して、4キロバイトから4ギガバイトのRAMをアドレス指定できます。 一般に、ページのアドレス指定が推奨されます((1MB + 64Kb-16byte)と4GBを比較してください)。 この投稿では、セグメント化されたアドレス指定についてのみ説明しましょう。 ページングについては、別の議論に値します。



2.ベースアドレス:

ここでは、データベースの物理アドレスを示します。



3.タイプフィールド:

ビットの組み合わせにより、セグメントのタイプが決まります。

画像



4. S(記述子タイプ):

Intelのドキュメントには、このビットが設定されていない場合、この記述子はシステムセグメント用であり、そうでない場合はコードまたはデータであると書かれています。 システムとは、LDT、TSS、割り込みゲート、およびそれらのような他のものを意味します(それらについては後で)。



5. DPL(記述子特権レベル):

説明されたセグメントの特権。 誰もがリングを知っています。



6. P(セグメントが存在する):

このビットが設定されている場合、プロセッサはセグメントが既にメモリ内にあることを「認識」します(ただし、有効と言う方が良いです)。 未定義のPビットを持つ記述子セレクターをセグメントレジスタにロードすると、#NP(存在しない)例外が発生します。 一般に、この華やかなフレーズの意味については少し後で説明します。



7. D / B:

異なるタイプのセグメントでは、解釈が異なります。

1.コードセグメントの場合:

32または16ビットの有効なアドレス長とオペランドサイズ。

(1-32; 0-16);

2.スタックの場合:

スタックポインターは32ビットまたは16ビットです。 (1-32; 0-16);



8. G:

セグメント制限が測定される単位(バイト、ページ)に影響します。 一般に、CR0レジスタの31ビットを設定することにより、PMに切り替えるときにページングを有効にできます。



詳細情報:

グローバルという言葉は無駄ではなかったと思います。 そのため、まだ何らかの種類のタブレットがあります。 確かに、 ローカル記述子テーブルもあります。 非常に多くの可能性があります。 たとえば、タスクの実装などに使用できます。 しかし、 LDTはすでにセグメントです! 「ローカル記述子プレートのセグメント記述子」のようなフレーズに慣れてください。



テーブルについて説明した後、テーブルをGDTRレジスタにロードする必要があります。 これはmovにはほど遠い。 GDTRには lgdt fwordコマンド(値) 入力されます 。 つまり、この構造を独立して形成し、前述のレジスタにロードする必要があります。 このレジスターで作業しているチームはまだありますが、ヨーロッパ中を駆け巡っています。



別のポイント。 PMでは、セグメントレジスタはセグメントのベースアドレス(RMなど)ではなく、 セレクターと呼ばれる特別にトレーニングされたピースを格納します。 その構造は次のとおりです。

画像

ここで、インデックスはテーブル内の記述子のシリアル番号です。

TIは、記述子を探す場所を示します( GDTまたはLDTで )。



テーブルの作成方法がすでに明確になったので、PMに切り替える方法について説明します(これはRMからのみ実行できることに注意してください)。 一般的には、CR0制御レジスタのビット0を設定するだけです。 私は嘘をついていますが。 まず、すべての割り込み( NMIマスク不可割り込みを含む)を含む)を禁止し、 A20アドレス行を開いて(32ビットのアドレス指定が可能になるように)、 GDTRを読み込み、開始マークにジャンプする必要があります。



ブートローダー(KOLIBRI'skyを使用できます)を使用して、アドレス1000h:0(RM'ovsky、アドレスをメモします)でコードをロードします。

ブートローダーから直接PMに切り替えると、それらの人ほどスムーズではありません。 すべてが少し複雑です。 しかし、最初に、ブートローダーがロードするコードを見てみましょう(すべてFASMに記述します)。 これは一種のhelloworldです。 起動し、PMに移動して挨拶を印刷します。 それだけです



format binary

xor ax,ax

cli ;

mov ss,ax

xor sp,sp

sti

mov ax,3

int 10h



jmp 1000h:r_start



r_start:



mov ax,1000h;

mov ds,ax

mov es,ax



in al, 0x92; A20

or al, 2

out 0x92, al



cli ;

mov al,8Fh; NMI

out 70h,al

in al,71h



lgdt fword [GDTR]; GDTR

mov eax,cr0

or al,1; 0-

mov cr0,eax; PM



jmp fword 08h:Startup32; PM



align 8 ;

GDT:

dq 0 ;

db 0FFh,0FFh,0,0,0,9Ah,0CFh,0 ;

db 0FFh,0FFh,0,0,0,92h,0CFh,0;

db 0FFh,0FFh,0,80h,0Bh,92h,40h,0 ;

label GDT_SIZE at $-GDT

GDTR:

dw GDT_SIZE-1

dd GDT+10000h

; 32- . 1000h, 1000h*10h ( ; ) => GDTR (!) = 10000h ( )+offset



virtual ;, ,

rb 10000h-$;

end virtual

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;PM32 Entry;;;;;;;;;;;;;;;;;;;

use32

org $+10000h; : PM Flat-, ; PM org', ; Flat . .



Startup32: ; PM

mov ax,10h ; . (!

mov es,ax ;) - 08h. - 10h, - 18h

mov ds,ax

mov fs,ax

mov ss,ax

mov esp,10000h;

mov ax,18h

mov gs,ax



mov esi,hi_string ;,

call print

jmp $



;ESI -

print:

pushad

xor ebx,ebx

mov ah,07h;

puts:

mov al,[esi+ebx]

mov [gs:(ebx*2)],ax

inc ebx

test al,al

jnz puts

popad

ret

hi_string db 'Welcome to PM, dude',0









私たちは何をしましたか? ブートローダーは1000h:0で正常にロードし、そこから実行を継続しました。 まず、 A20をオンにし、すべての割り込みを禁止し、適切な値をGDTRにロードして、入力ラベルにジャンプしました。 飛びついたことに気づいた

jmp fword 08h:Startup32





つまり、08hはコード記述子セレクターです。 それに慣れてください。



次に、この奇跡を開始する方法。 個人的には、WinImageとVirtualBoxを使用しています。 ブートローダーをディスケットのブートセクターにプッシュし、.binファイルをルートに配置します。 .vfdに保存し、仮想マシンのプロパティにディスクイメージへのパスを書き込み、起動して結果を確認します。



次の号では、割り込み、フォールト、トラップ、アボート、およびそれらの動作、キャッチ、デバッグについて検討します。 アーキテクチャについて話を始めましょう。



情報源。

1)真の道を指摘し、最初に私を助けてくれたPhantom_84別名エゴに感謝したい。 それがなければ、私がそれを理解するのははるかに難しいでしょう。

2) BrokenSword'aの記事BrokenSword'aの記事。 彼らは注意を払う価値があります。

3) Intelシステムプログラミングガイド



All Articles