0

私は自分のカーネルを構築しようとしていますが、現在GDTをセットアップしています。ローダー用のアセンブリファイルを使用し、Cで記述されたカーネルを呼び出して、GDTを機能させようとしています。カーネルはGRUBから起動し、GRUBによるGDTセットアップをフラッシュします。GDTエントリを(適切な制限とオフセットを使用して)セグメントとして設定している場合、QEMUとペンドライブから起動するときの両方で、カーネルの再起動時にある種のトリプルフォールトがあると想定しています。

私の質問は次のとおりです。

x86アーキテクチャのセグメント化されたモデルを実装できますか?保護モードで実行する必要がありますか?ジョブが完了したら、保護モードを解除するにはどうすればよいですか?

ここにコードを投稿しますが、そのほとんどはチュートリアルからのものであり、アセンブリとCコードを混同すると、乱雑になります。主なことは、Cカーネルのコードセグメントとデータセグメントの両方のエントリのベース以外を「0」として実行すると、カーネルが再起動するだけであるということです。さらに、4KBページングを無効にするように粒度を設定した場合にも同じことが起こります。必要に応じて、詳細をお尋ねください。ありがとう :) :)

編集:これは、asmブートローダーとCカーネルファイルをリンクするために使用するlinker.ldファイルです。asmファイルとCファイルの両方から、メモリセグメンテーションに関連するセグメントを投稿しました。

リンカ:

ENTRY (loader)

SECTIONS
{
    . = 0x00100000;

    .text ALIGN (0x1000) :
    {
        *(.text)
    }

    .rodata ALIGN (0x1000) :
    {
        *(.rodata*)
    }

    .data ALIGN (0x1000) :
    {
        *(.data)
    }

    .bss :
    {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}

GDTとインスタンス化関数を設定するC関数:

void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
    /* Setup the descriptor base address */
    gdt[num].base_low = (base & 0xFFFF);
    gdt[num].base_middle = (base >> 16) & 0xFF;
    gdt[num].base_high = (base >> 24) & 0xFF;

    /* Setup the descriptor limits */
    gdt[num].limit_low = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);

    /* Finally, set up the granularity and access flags */
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

void gdt_install()
{
    /* Setup the GDT pointer and limit */
    gp.limit = (sizeof(struct gdt_entry) * 35) - 1;
    gp.base = &gdt;

    /* Our NULL descriptor */
    gdt_set_gate(0, 0, 0, 0, 0);

   gdt_set_gate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF); //Setting Code Segment


  gdt_set_gate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF); //Setting Data Segment

  //In the above two, if the second parameter is anything other 
  //than 0 i.e. base is not 0, the kernel doesn't run. 
 //Moreover, setting the last to 0x4F, which is byte accessing rather 
 //than 4KB paging gives the same malfunction too. 

 //gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
  //gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);     
    //TASK STATE SEGMENT -1 
    //TASK STATE SEGMENT -2 

    //gdt_set_gate(3, 0, 0xFFFFFFFF, 0x89, 0xCF);
   // gdt_set_gate(4, 0, 0xFFFFFFFF, 0x89, 0xCF);   









    /* Flush out the old GDT and install the new changes! */
    gdt_flush();
}

最後に、ASMで記述されたGDTフラッシュ関数は次のとおりです。

gdt_flush:    
    lgdt [gp]        ; Load the GDT with our '_gp' which is a special pointer   
    ;ltr [0x18]
    mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:flush2   ; 0x08 is the offset to our code segment: Far jump!
flush2:
    ret

C関数とASM関数はフラットメモリモデルで動作するため、正しいことを知っています。これに特定の変更を加える必要がありますか。セグメンテーションまたはセグメント化されたページングを設定するためのリンカーファイルについてアドバイスをお願いします。

4

2 に答える 2

1

x86 アーキテクチャのセグメント化されたモデルを実装できますか?

もちろん、できます。

保護モードで実行する必要がありますか?

選択肢は 2 つしかありません。ぎこちなくリアル モードと 64K などの制限と保護モードです。

ジョブが完了したら、保護モードを終了するにはどうすればよいですか?

簡単に言うと、ページ変換をオフにし (オンになっている場合)、16 ビット コード セグメントにジャンプし、リアル モードと互換性のある記述子を指すセレクターを使用してセグメント レジスタをロードし、CR0.PE をクリアします。詳細は他にもたくさんあります (例: タスクの切り替え、割り込み処理)。それらは、公式ドキュメントとオンラインのチュートリアルで見つけることができます。

C カーネルのコード セグメント エントリとデータ セグメント エントリの両方のベースを '0' にする以外のことをすると、カーネルが再起動します。

x86 コードは位置に依存せず、アドレス X に再配置されてもアドレス Y にロードされると正しく機能しないことを理解する必要があります。つまり、コード/データを移動するためにセグメント記述子のベースだけを調整する必要があるわけではありません。どこかだけでなく、リンカーは実行可能イメージを別の場所に再配置する必要があります。これが機能しないのは、1 つの単純なミスで十分です。

さらに、4KB ページングを無効にするように粒度を設定すると、同じことが起こります。

その発言は何の意味もありません。記述子の粒度ビットを操作してページングを無効にすることはありません。

必要に応じて、詳細をお尋ねください。

私たちがあなたを助けるのに十分な情報を提供するのはあなたの仕事です. 現在述べられているように、質問には完全に回答するための詳細がありません。バグのあるコードがありますが、それを見せてくれません。どうすればよいでしょうか?

于 2013-02-02T20:31:00.660 に答える
0

GDT をロードした後、これを asm コードで使用します。

mov eax, cr0
or eax, 1b
mov cr0, eax
于 2016-12-04T10:35:36.093 に答える