OSを作成します:問題2

こんにちは。 これもまた、私たちileypehatで、シリーズ「Writing Your Own OS」の待望の2番目の記事(最初の記事はこちら )です。 最初の記事の後に大きな一時停止があったことをおworkび申し上げます。作業の今後の方向性を判断するのに時間がかかりました。 この問題では、32ビットIntelプロセッサの保護モードについて簡単に検討します。 包括的な理論データを提供するという目標を設定していないことを改めて強調します。



少し前のリリースからのプログラムを思い出しましょう。 オペレーティングシステムの代わりに起動し、「Hello world」というメッセージを表示しました。 このプログラムは16ビットアセンブラーで記述され、いわゆるリアルモードで動作しました 。 おそらくご存知のように、リアルモードでアドレス指定する場合、物理アドレスはセグメントオフセットを使用して形成され、20ビットの次元を持ちます。 単純な数学では、この方法では1メガバイトのRAMにしかアクセスできないことがわかります。



使用可能なメモリの少量は、リアルモードでの唯一の問題ではありません。 メモリにオペレーティングシステムと複数のアプリケーションプログラムが同時に含まれている状況を想像してください。 リアルモードでは、アプリケーションプログラムが別のプログラムまたはオペレーティングシステムに属するアドレスにアクセスすることを妨げるものは何もありません。 したがって、1つのプログラムでエラーが発生すると、システム全体が崩壊する可能性があります。



これらの2つの問題を解決するために、Intelはかつて、RAMをアドレス指定するより複雑な新しい方法を開発しました。 より正確には、Intelはいくつかのアドレッシング方法を開発しましたが、それらはすべてプロテクトモードと総称されています



そのため、保護モードでのアドレス指定は、セグメント、ページ、セグメントページの3つのモード(トートロジーではごめんなさい)のいずれかで発生する可能性があります。 この問題では、セグメント化モードのみを考慮します。



奇妙なことに、セグメントはセグメント化モードでメモリに割り当てられます 。 これらのセグメントは、リアルモードのシンプルで使い慣れたセグメントとは大きく異なります。 この場合、セグメントは、ベース、制限(つまり、大まかに言って、サイズ)およびいくつかの追加属性によって特徴付けられるメモリの連続領域です。 ベースは、大まかに言って、セグメントの開始元の物理アドレスです(実際、これは物理アドレスではなく、いわゆる線形アドレスですが、後で詳しく説明します)。 リアルモードセグメントとは異なり、セグメントのサイズは任意であることに注意してください。 各セグメントのベース、サイズ、および属性は、 記述子に格納されます 。 記述子は特別なテーブルに保存され、 セレクタはテーブルから特定の記述子を選択するために使用されます。 このようなたくさんの新しい怖い言葉に驚かないでください。今から整理します。



したがって、メモリにアクセスするたびに、プロセッサはレジスタ(CSなど)、セレクタから取得し、テーブルから記述子を検索し、記述子からセグメントの先頭のアドレスを抽出し、開始アドレスにオフセットを追加し、線形アドレスを取得します(線形アドレスに注意してください)物理アドレスは2つの異なるものですが、この段階では、これは同一であると想定できます。 さらに、プロセッサは、受信したアドレスがセグメント境界を超えているかどうか、およびセグメント属性が現在そのアドレスへのアクセスを許可しているかどうかを確認します。 線形アドレス計算スキームは次のようになります。







したがって、すでに述べたように、ベース(ベースアドレス)、サイズ(セグメント制限)、および追加のセグメント属性は記述子に格納されます。 記述子スキーマを見てみましょう。







ベースには32ビットが割り当てられ、制限には20ビットしか割り当てられていません。メガバイトよりも大きいセグメントを作成することはどうして不可能でしょうか。 できます。 これには単純なトリックが使用されます。 ビットGが 1に設定されている場合、制限はバイト単位ではなく、4キロバイトのブロック単位であると見なされます。 制限には、セグメントサイズから粒度の単位で1を引いた値が含まれることに注意してください。 制限が0の場合、セグメントのサイズは1バイトまたは4 KBです。 さらに、ご覧のとおり、記述子にはさらにいくつかのフィールドが含まれています。 それらの詳細な説明は、たとえばここにあります



前述のように、記述子は特別なテーブルに保存されます。 複数のテーブルが存在する可能性がありますが、いずれにしても、 GDT (グローバル記述子テーブル)はメモリ内に存在する必要があります。 彼女は、オペレーティングシステム全体の1つです。 同様に、各タスク、つまりプロセスは、0、1、またはそれ以上のLDT-ローカル記述子テーブルを持つことができます。 GDTのアドレスとサイズはGDTRレジスタに保存され、現在のLDTはLDTRレジスタに保存されます。 LDTは必要ありませんが。 さらに、割り込み記述子のIDTテーブルもありますが、それらの検討は次のリリースまで延期します。



セレクターと呼ばれるデータ構造を使用して、テーブルから記述子を選択します。 これは次のようなものです。







セレクタには、ローカルまたはグローバル(L / G)記述子を検索するテーブルと記述子番号自体を示すビットが含まれています。 さらに、セレクターにはRPLフィールドがありますが、まだ興味がありません。



それでは、ビジネスに取り掛かりましょう!



;16- ,

use16

org 0x7c00

start :

jmp 0x0000 : entry ; CS=0, IP=0x7c00

entry :

mov ax , cs

mov ds , ax



;

mov ax , 0x0003

int 0x10



; A20

in al , 0x92

or al , 2

out 0x92 , al



; GDT GDTR

lgdt [ gdtr ]

;

cli

;

in al , 0x70

or al , 0x80

out 0x70 , al



;

mov eax , cr0

or al , 1

mov cr0 , eax



; CS:EIP

O32 jmp 00001000b : pm_entry



;32-

use32

;

pm_entry :

; ( SS)

mov ax , cs

mov ds , ax

mov es , ax



mov edi , 0xB8000 ; 0x3

mov esi , msg ;

cld

. loop ;

lodsb ;

test al , al ; 0

jz . exit ;

stosb ;

mov al , 7 ;

stosb

jmp . loop

. exit



jmp $ ;



msg :

db 'Hello World!' , 0



; .

; !

gdt :

db 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00

db 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 10011010b , 11001111b , 0x00

gdt_size equ $ - gdt



;, GDTR

gdtr :

dw gdt_size - 1

dd gdt



finish :

times 0x1FE - finish + start db 0

db 0x55 , 0xAA ;








それでは、コードについて少し説明しましょう。



行で:

in al , 0x92 <br/>

or al , 2 <br/>

out 0x92 , al






アドレス行A20のロックが解除されます。 これはどういう意味ですか? リアルモードでは、1 MBのメモリを:



でアドレス指定できることを思い出してください:



形式(アドレスあたり20ビット)。 ただし、たとえば、アドレスFFFF:FFFF



に連絡することにより、このバーより少し高い「ジャンプ」ができ、結果のアドレスは21ビット長になります。 80286までのプロセッサでは、最も古い(ゼロから数えて20番目)が破棄されたため、古いプログラムとの互換性のために、A20アドレスラインロックが導入されました。 現在、すべてのオペレーティングシステムは保護モードで動作しているため、アドレス指定の必要に応じて、できるだけ早くこのビットのロックを解除する必要があります。 そのようなこと。



グローバル記述子テーブルのアドレスと制限をGDTRレジスタにロードした後、割り込みの一般的な禁止があります。 これは、すべての割り込みハンドラーがリアルモードで動作し、保護モードでアドレス指定の問題が始まるという事実によって説明されます。 したがって、中断を厳密に禁止します。



保護モードを有効にするには、 CR0



レジスタの下位ビットを設定します。 保護モードへの移行直後には、一定の不確実性が生じますCS:EIP



では、 :



形式でエントリポイントを保護モードに設定する必要がありますが、実際のモードの残りはまだあります。 したがって、次の命令を実行します。

O32 jmp 00001000b : pm_entry





ここでは、 O32



オペランドのビット深度変換プレフィックスが使用されます。これにより、32ビットオフセットで長い無条件ジャンプを行うことができます。 やっと、私たちはついに保護体制の魅力を活用できるようになりました!



プログラムを実行するには、 最初の記事で説明した操作と完全に一致する操作を行う必要があります。 保護モードについて詳しく知りたい場合は、参照のリストがあります。



プロテクトモードの本当の可能性を示すものに対する十分な想像力がなかったため、再び「Hello、World!」と表示しますが、既にビデオメモリへの直接アクセスを使用しています。 美しいことを行うには、割り込みを使用すると便利です。 シリーズの次の記事では、保護モードでの使用方法について説明します。 そして、2番目の記事はこれで終わりです。 ご意見やご提案をお待ちしております。



All Articles