LAB_1_Part2_The Boot Loader


1. LAB_1_Booting a PC
1.1. Part 2: The Boot Loader
6.828では、従来のHDD起動メカニズムを使用します.これは、boot loader512バイトを満たす必要があることを意味します.
boot loaderは、1つのアセンブリ言語ソースファイルboot / boot.Sと1つのCソースファイルboot / main.cからなる.
1.1.1. boot.S
BIOSは、boot.Sこのコードをハードディスクの最初のセクタloadから物理アドレス0x7c00までの位置で、CPUはreal modeで動作する.boot.SCPUの動作モードを実モードから32ビットの保護モードに変換し、jumpからC言語プログラムに移行する必要がある.
ソース読解、知識点:1.cli (clear interrupt) 2. cld (clear direction flag)
df:方向フラグビット.シリアル処理命令では,操作毎にsi,diの増減を制御する.(df=0で、操作毎にsi、diが増加し、df=1で、操作毎にsi、diが減少する).
以前のPC機との互換性のため、A20アドレス線が接地されているため、アドレスが1 M範囲より大きい場合はデフォルトで0にロールバックされる.従って32ビットモードに移行する前に、イネーブルA20が必要となる.3.test論理演算命令は、2つのオペランドに対してAND操作を行い、かつ修正PSWtestAND命令と唯一異なる点は、TEST命令が目標オペランドを修正しないことである.
test al, 00001001b ;    0    3
  • lgdt gdtdescグローバル記述子テーブルをロードし、グローバル記述テーブルがどのように生成されたかにかかわらず一時的に関係なくロードする.
  • cr0,control register,制御レジスタ.

  • CR 0には6つの事前定義フラグが含まれており、0ビットは保護許可ビットPE(Protedted Enable)であり、保護モードを起動するために使用され、PE位置1の場合、保護モードが起動し、PE=0の場合、実モードで動作する.
  • ljmp $PROT_MODE_CSEG, $protcseg ,

  • PROT_MODE_CSEG=8、この値はこだわりがあるようで、『自分でオペレーティングシステムを書く』という本で見たことがあります.この時点で既に32ビット実モードに入っているため、このときの8実モードでは簡単ではないcsとなっており、GDTに関係しているようで、当時はGDTの初期化が複雑だと思っていたが、ここではまず深く考えない.
    1.1.2. デバッグS terminal中cdからlabディレクトリの下で、make qemu-gdbを実行します.もう一つterminal実行make gdb.
    BIOSはboot loaderを0 x 7 c 00の位置にロードするので、ブレークポイントb *0x7c00を設定します.再実行c、QUMU端末に表示されるBooting from hard disk.x/30i 0x7c00を実行するとboot.Sと似たようなアセンブリコードが表示されます.
    この記事には、詳細なデバッグプロセスMIT 6.828 JOS学習ノートがあります.Exercise 1.3ですが、しばらくは細かく分析する必要はないと思います.
    1.1.3. カーネルのロード
    次に、boot loaderのC言語部分を分析します.
    まず以下のCポインタを熟知しています.コンパイル運転pointer.c結果.a[]bのアドレスの違いは、両者が格納されているセグメントが異なるため、かなり異なることがわかります.
    1: a = 0xbfa8bdbc, b = 0x9e3a160, c = (nil)
    2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103
    3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302
    4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
    5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302
    
    // b = a + 4
    6: a = 0xbfa8bdbc, b = 0xbfa8bdc0, c = 0xbfa8bdbd
    
    

    ELFフォーマットは非常に強力で複雑ですが、ほとんどの複雑な部分は共有ライブラリの動的ロードをサポートするために使用され、6.828コースでは使用されません.このコースでは、ELF実行可能ファイルを簡単にロード情報付きヘッダーと見なすことができます.その後、いくつかのプログラムセクションに続き、各プログラムセクションは連続したコードブロックまたはデータであり、指定したメモリにロードされます.
    私たちが関心を持つProgram Sectionは次のとおりです.
  • .text:実行可能命令
  • .rodata:文字列定数などの読取り専用データセグメント.(ただし、書き込みを禁止するためにハードウェアを設定するのに苦労しません.)
  • .data:初期化されたデータを格納する
  • .bss:初期化されていない変数を格納が、ELFには記録のみが必要である.bssの開始アドレスと長さ.Loaderandprogram自分でやらなければならない.bssセグメントクリア.

  • 各プログラムヘッダのph-> p_paフィールドにはセグメントのターゲット物理アドレスが含まれている(この場合、ELF仕様はフィールドの実際の意味が曖昧であるにもかかわらず、実際には物理アドレスである)
    BIOSはブートセクタの内容を0 x 7 c 00の位置にロードし、ブートプログラムも0 x 7 C 00の位置から実行します.-Ttext 0x7C00・・boot / Makefragのリンクにリンクアドレスを渡すことで、リンクは生成したコードに正しいメモリアドレスを生成します.
    一部の情報に加えて、ELFヘッダにはe_entryという私たちにとって重要なフィールドがあります.このフィールドは、プログラム内のエントリポイントのリンクアドレスを保存します.プログラムが実行を開始すべきコードセグメントの記憶アドレスです.逆アセンブリコードでは、最後のcallが0 x 10018アドレスであることがわかります.
    ((void (*)(void)) (ELFHDR->e_entry))();
        7d6b:	ff 15 18 00 01 00    	call   *0x10018
    

    0 x 7 d 6 bのブレークポイント後、csi一度、実際のジャンプアドレスビット0 x 10000 cを発見
    (gdb) b *0x7d6b
    Breakpoint 3 at 0x7d6b
    (gdb) c
    Continuing.
    => 0x7d6b:	call   *0x10018
    
    Breakpoint 3, 0x00007d6b in ?? ()
    (gdb) si
    => 0x10000c:	movw   $0x1234,0x472
    

    実際に実行したobjdump -f kernelの結果と一致する.
    ../kern/kernel:     file format elf32-i386
    architecture: i386, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x0010000c
    

    1.1.4. Exercise 6
    BIOSがBoot loaderに入ったときにメモリの8文字が0 x 0100000であることを確認し、ブートローダがカーネルに入ったときに再度チェックします.彼らはどうして違うのですか.2番目のブレークポイントは何ですか?△あなたは本当にQEMUでこの質問に答える必要はありません.考えてみてください.
    答えは明らかであるべきで、BIOSがBoot loaderに入る時、0 x 10000000メモリの後の8文字はすべてゼロで、この時カーネルプログラムはまだメモリにロードしていないためです.カーネルのロードはbootmain関数で完了します.
    gdbでデバッグする必要がある場合は、x/8x 0x100000メモリの内容を表示します.