要約
GDTの各フィールドと,はりぼてOSのasmhead.nas内で仮設定するGDTのヌルセレクタ以外の2つのエントリの各ビットがどのような意味を持つのかを解説します.
背景
RamenOSにページングを実装するために,カーネルやIDT,GDTの位置を動かしていましたが,GDTの設定も変える必要があることに気づきました.GDTの各ビットの意味はあまり気にしていなかったので,一度纏めておきたいと思い記事を書きました.
GDTのエントリ構成
作りながら学ぶOSカーネル保護モードプログラミングの基本と実践
- 作者:金 凡峻
- 発売日: 2009/04/15
- メディア: 単行本
Limitの15-0ビット(16) | |||||||||||||||
Base Addressの15-0ビット(16) | |||||||||||||||
P(1) | DPL(2) | S(1) | Type(4) | Base Addressの23-16ビット(8) | |||||||||||
Base Addressの31-24ビット(8) | G(1) | D/B(1) | L(1) | AVL(1) | Limitの19-16ビット(4) |
フィールド解説
解説はIntel® 64 and IA-32 Architectures Software Developer’s ManualsのVolume 3,3.4.5を基にしています.
- Limit
- セグメントの大きさを指定します.後述するTypeフィールドのExpansion-directionも確認してください.
- Base Address
- 物理メモリ上でのセグメントの開始番地です.
- P
- セグメントがメモリ上に存在するかを示します.このフィールドが0だとセグメントがメモリ上にないことを示し,1だとセグメントがメモリ上にあることを示します.
- DPL
- セグメントの特権レベルを指定します.0が最強の権限で,基本的にはOSのカーネルが持ちます.3はアプリケーションの権限です.1と2はOSのサービスの権限ですが,サービスがない場合,0と3の値のみ使用します.
- S
- セグメントの種類を指定します.このフィールドが0だとセグメントはシステムセグメントとなり,1だとコードセグメントあるいはデータセグメントとなります.
- Type
- セグメントの種類を指定します(後述).
- G
- Limit値の解釈を指定します.このフィールドが0の場合,実際のセグメントの大きさは(limit値)Byteとなり,1の場合,実際のセグメントの大きさは(limit値)×4KBとなります.
- D/B
- 既定のオペラントまたはスタックポインタのサイズ,あるいはスタックの上限を指定します(後述).
- L
- IA-32eの場合,このフィールドはセグメントが64ビットコードを格納しているかを示します.0の場合,互換モードで実行し,1の場合,このセグメント内の命令は64ビットモードで実行されます.非IA-32eやコードセグメントでない場合,このビットは常に0にするべきです.
- AVL
- このフィールドはシステムソフトウェアが自由に使用して構いません.
Typeフィールド
Sフィールドに設定されているビットによってTypeフィールドの値の解釈が変わります.
共通
Sフィールドの値に関わらず常に同じ意味を持ちます.
- 最下位ビット:Accessed
- セグメントにアクセスされるとこのビットが1になります.
Sフィールドが0の場合
セグメントはシステムセグメントです.TSS等が該当します.ここでは割愛します.
Sフィールドが1の場合
セグメントはデータセグメントあるいはコードセグメントです.Typeフィールドの最上位ビットが0の場合,セグメントはデータセグメントとなり,1の場合,セグメントはコードセグメントとなります.
Typeフィールドの最上位ビットが0の場合
- 第1ビット:Write-enable
- このビットが0の場合,セグメントは読み取り専用となります.1の場合,セグメントへの書き込みが可能となります.
- 第2ビット:Expansion-direction
- このビットが0の場合,セグメントの論理アドレスの範囲は0からlimit値までとなります.1の場合,セグメントの論理アドレスの範囲は(limit値 + 1)から0xFFFF(D/Bフィールドの値が0の場合)または0xFFFFFFFF(D/Bフィールドの値が1の場合)となります.
Typeフィールドの最上位ビットが1の場合
- 第1ビット:Read enable
- このビットが0の場合,セグメントの実行のみが可能です.1の場合,実行に加えて読み取りが可能になります.
- 第2ビット:Conforming
- このビットが0の,且つ特権レベルの異なるセグメントへの実行の遷移を行おうとすると,一般保護例外が発生します.このビットが1の,且つ特権レベルの異なるセグメントへの実行の遷移を行っても,それまでの特権レベルを維持したまま実行を継続することが出来ます.
D/Bフィールド
セグメントの種類によって何を意味するのかが変わります.
実行可能コードセグメントの場合
セグメント内の命令で参照される実効アドレスやオペラントの既定の長さを指定します.このビットが0の場合,既定では16ビットアドレス,16ビットまたは8ビットのオペラントと見做されます.1の場合,既定では32ビットアドレス,32ビットまたは8ビットのオペラントと見做されます.
スタックセグメントの場合*1
スタックポインタのサイズを指定します.このビットが0の場合,16ビットのスタックポインタが使用され,1の場合,32ビットのスタックポインタが使用されます.
Expansion-directionなデータセグメントの場合
セグメントの上限を指定します.このビットが0の場合,上限は0xFFFFで,1の場合,上限は0xFFFFFFFFです.
他のセグメントの場合
このビットは常に1にしなければなりません.
asmhead.nasで仮に設定するGDTのエントリ確認
はりぼてOSでは保護モードに切り替える際,仮のGDTを設定します.最初のヌルセレクタ以外の2つのエントリを確認します.
DW 0xFFFF,0x0000,0x9200,0x00CF ; 読み書き可能セグメント32bit DW 0xFFFF,0x0000,0x9A28,0x0047 ; 実行可能セグメント32bit(bootpack用)
1番目のエントリ
項目 | 値 |
---|---|
Limit | 0xFFFFF |
Base Address | 0 |
P | 1 |
DPL | 0 |
S | 1 |
Type | 0b0010 |
G | 1 |
D/B | 1 |
L | 0 |
AVL | 0 |
Gフィールドの値が1のため,実際のセグメントの大きさは(0xFFFFF + 1) × 4KB = 4GBとなります.すなわちこのセグメントの領域はメモリの全範囲です.
現在セグメントはメモリ上に存在するので,Pフィールドの値は1です.
セグメントはアプリケーション用ではないため,DPLフィールドの値は0です.
Sフィールドが1で且つTypeフィールドが0b0010のため,このセグメントは読み書き可能なデータセグメントで,セグメントの論理アドレスの範囲は0から0xFFFFFまでとなります.そのためD/Bフィールドは1,Lフィールドは0にする必要があります.
2番目のエントリ
項目 | 値 |
---|---|
Limit | 0x7FFFF |
Base Address | 0x00280000 |
P | 1 |
DPL | 0 |
S | 1 |
Type | 0b1010 |
G | 0 |
D/B | 1 |
L | 0 |
AVL | 0 |
Gフィールドの値が0のため,実際のセグメントの大きさは(0x7FFFF + 1)KB = 512KBです.これはbootpack.hrbの,想定される最大の大きさと一致しています.
現在セグメントはメモリ上に存在するので,Pフィールドの値は1です.
セグメントはOS用なので,DPLフィールドの値は0です.
Sフィールドが1で且つ,Typeフィールドが0b1010のため,このセグメントは実行と読み取りが可能なコードセグメントです.はりぼてOSでは32ビット保護モードを使用するため,D/Bフィールドは1にする必要があります.はりぼてOSは64ビットモードを使用しないため,Lフィールドは0にします.
Base Addressはasmhead.nas内でBOTPAK
としてだったり,Cファイル内でも定数として定義されています.
BOTPAK EQU 0x00280000