grubの探検

asm.S

asm.Sの先頭からstage2の実行は始まる。
但し、このファイル全体が初期化シーケンスという事ではなくて、BIOSアクセスなど一連のローレベルなコードが大体ここに入っている。

ENTRY(main)

codestartへlong jump。
割り込みフラグをクリア、各セグメントレジスタに0を代入。
スタックアドレスを設定してreal_to_prot()を呼び出しプロテクトモードへ移行、最後にinit_bios_info()を呼び出す。
#疑問 帰ってこないの?

ENTRY(real_to_prot)

割り込みフラグをクリア、GDTRをロード。
cr0レジスタのprotection enableフラグに1をたてる。
CPUのプリフェッチキューをフラッシュしてCSレジスタをセットする為、プロテクトモードコードセグメントのprotcseg()へlong jumpする。
ここから32ビットコード:
DS, ES, FS, GS, SSレジスタにプロテクトモードデータセグメントのセレクタをロードする。
リターンアドレスをリアルモードのスタックにプッシュしておく。
プロテクトモードスタックのアドレスをセットする。
リターンアドレスをプロテクトモードのスタックにプッシュしておく。

#なんかやたらとリターンアドレスをプッシュするのは何事ですか?
#GDTだけ設定されてれば実はそれで良いわけ?486の本ではもっと色々やれとか書いてあるけど。
#リアルモードのスタックアドレス、どこで保存した?

ENTRY(prot_to_real)

GDTRをロード。
プロテクトモードのスタックアドレスを保存。
リターンアドレスをスタックから取得。
リアルモードのスタックを設定。
疑似リアルモードデータセグメントをDS, ES, FS, GS, SSに設定。
疑似リアルモードコードセグメントのtmpcseg()にlong jump。
ここから16ビットコード:
cr0レジスタのprotection enableフラグをクリア。
セグメントアドレス0のrealcsegにlong jump。
ds, es, fs, gs, ssレジスタを0クリア。
割り込み状態をリストア。

gdt

nullセグメント、プロテクトモードコードセグメント、プロテクトモードデータセグメント、リアルモードコードセグメント、リアルモードデータセグメ
ントをそれぞれ定義している。
単に0-4GBの全領域をセグメントに割り当てているだけ。
細かいパラメータはめんどいので誰か解析してください。

        .word   0, 0
        .byte   0, 0, 0, 0

        /* code segment */
        .word   0xFFFF, 0
        .byte   0, 0x9A, 0xCF, 0

        /* data segment */
        .word   0xFFFF, 0
        .byte   0, 0x92, 0xCF, 0

        /* 16 bit real mode CS */
        .word   0xFFFF, 0
        .byte   0, 0x9E, 0, 0

        /* 16 bit real mode DS */
        .word   0xFFFF, 0
        .byte   0, 0x92, 0, 0
gdtdesc

GDTR。 lgdtではこの変数を渡す。

/* this is the GDT descriptor */
gdtdesc:
        .word   0x27                    /* limit */
        .long   gdt                     /* addr */

今日の感想

GRUBアセンブリコードは意外ときれいだ。わかりやすい。

ところで、コードをgrepしてみた限りでは、IDT, TSSの初期化をやってる形跡が見あたらない。
はじめて読む486ではプロテクトモードの初期化に必要と書いていたが、実は要らない??