「EmulatingLinusTorvalds」というタイトルのこの一連の投稿へようこそ。 今日はGDTを見ていきます。 まず、GDTが何であるかを確認する必要があります。 ウィキペディアによると:
グローバルディスクリプタテーブル or GDT によって使用されるデータ構造です インテル x86-で始まるファミリプロセッサ 80286 ベースアドレス、サイズ、実行可能性や書き込み可能性などのアクセス権限など、プログラムの実行中に使用されるさまざまなメモリ領域の特性を定義するため
変換されるのは、グローバル記述子テーブルです。これは、プログラムの実行中に使用されるさまざまなメモリ領域の特性を定義するために86以降Intelx80286プロセッサで使用されているデータ構造です。
要約すると、Intel x86プロセッサを使用している場合は、メモリを正しく使用するためにGDTを定義する必要があります。 それほど複雑にすることはなく、テーブルに3つのエントリを定義します。
- すべてのテーブルに必要なNULLエントリ。
- セクションのチケット データ、32ビットで4GBの最大値を使用します。
- セクションのチケット コード、32ビットで4GBの最大値を使用します。
ご覧のとおり、データとコードは同じスペースを使用します。 さて、これから実装します。 このために、XNUMXつの構造を使用します。最初の構造は、GDTの実際のデータへのポインターを含むことを担当します。 そしてXNUMX番目はGDTエントリを持つ配列になります。 最初にそれらを定義しましょう
struct Entry{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));
構造の最後に奇妙な__attribute __((packed))があることに気づいたかもしれません。 これは、GCCに構造を最適化しないように指示します。これは、データをそのままプロセッサに渡すためです。 次に、GDTをインストールする関数を作成します。 構造を宣言する前に、構造を初期化します。
struct ND::GDT::Entry gdt[3];
struct ND::GDT::Ptr gp;
void ND::GDT::Install()
{
gp.limit=(sizeof(struct ND::GDT::Entry)*3)-1;
gp.base=(uint32_t)&gdt;
}
したがって、3入力テーブルに移動するポインターを作成します。
void ND::GDT::SetGate(int num, uint32_t base, uint32_t limit, uint8_t access,uint8_t gran)
{
gdt[num].base_low=(base & 0xFFFF);
gdt[num].base_middle=(base >> 16) & 0xFF;
gdt[num].base_high=(base >> 24) & 0xFF;
gdt[num].limit_low=(limit & 0xFFFF);
gdt[num].granularity=(limit >> 16) & 0x0F;
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access=access;
}
そして、インストール関数から3回呼び出します
ND::GDT::SetGate(0,0,0,0,0); /* NULL segmente entry */
ND::GDT::SetGate(1,0,0xFFFFFFFF,0x9A,0xCF); /* 4 GiB for Code Segment */
ND::GDT::SetGate(2,0,0xFFFFFFFF,0x92,0xCF); /* 4 GiB for Data segment */
最後に、GDTをロードできるように、プロセッサにGDTがあることを通知する必要があります。この場合、カーネルにGRUBをロードするときに、GRUBGDTを上書きします。 GDTをロードするには、asmにlgdt(または構文によってはlgdtl)という命令があります。これを使用します。
asm volatile("lgdtl (gp)");
asm volatile(
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movw %ax, %ss \n"
"ljmp $0x08, $next \n"
"next: \n"
);
これが完了すると、システムにはすでにGDTがあります。 次の章では、IDTについて説明します。これは、GDTと非常によく似ていますが、中断があります。 GDTにいくつかのステータスメッセージと確認メッセージを入れたので、NextDivelは次のようになります。
おそらく64ビット構造がこれらの時代により適しています、それは8086を使い続けることはバックログです。
x86_64のGDTに関する情報を探していましたが、特別なフラグが付いた古いモデルに従っていると思います。 32ビットアドレスは引き続き使用されます。 今、私はそれを正しく行う方法を正確に知りません。 いくつかのリンク:
http://wiki.osdev.org/Entering_Long_Mode_Directly
http://f.osdev.org/viewtopic.php?f=1&t=16275
最初の非常に良いあなたの貢献ですが、タイトルは
「リチャード・ストールマンをエミュレートする」または少なくとも私はそう思う、、、
よろしく
LinusはLinuxカーネルを作成し、StallmanはUnixツールとコマンドであるGNUを作成しました。
ニュークリアスを作成しているので、タイトルは適切です。
挨拶!
すべての質問に答えてくれてありがとう、そして辛抱強く、私はアセンブラーとしての基本を知っているだけで、Cについてはほとんど何も知りませんが、私はそれが本当に好きです、今私はGDTと少し混乱しています、私が理解する。
GDTには、任意のプログラムから常にアクセスできるグローバルな「記述子」があり、これらの記述子は、(プログラム)が実行されるセクションを指しますか? またはそれ以外の場合です。