linuxカーネル読書がしたい(ブート編1)
linuxカーネル読書をしようしようって言ってずっとやっていなかったのでついに一歩踏み出そうという気になれた.
全体的な流れとしては,まずはざっと読んでから詳細に調べたりして行く予定なので,書いたらそれっきりではなく随時追記していくつもりである.
とはいえ実際にはメモ書きのためなので可読性には欠けている.
まずはブートについて.
Intelプロセッサはリアルモードとプロテクトモードの2種類存在する.
リアルモードでプロテクトモードへの移行の準備をする.
コンピュータの電源を入れた直後はリアルモードでしか動作しない.BIOS手続きはリアルモードでのみ動かすため,プロテクトモード移行後にはBIOSは一切使用しない.
はじめはbootのheader.S
過去のbootsect.Sとsetup.Sをベースに作られたもの.
この時点では64kセグメントのリアルモードであるため,各リニアアドレスを取得するためには"seg_addr * 16"をしなければならない.
例えば,ブートセクタのアドレスが0x7c00から始まるということになっているので,セグメントによる表記ではBOOTSEG = 0x7c0となる.
bootsect_start:
まずは上記のBOOTSEGセグメントのstart2オフセットにljmpする.(ljmpはセグメント間ジャンプとも言われる.)
start2:
csレジスタ = dsレジスタ = esレジスタ = ssレジスタに設定し,spレジスタ = 0にする.
sti:EFLAGSの割り込みフラグ(IF)をセットし,次の命令から割り込みへの応答を開始する.
cld:EFLAGSのDFフラグをクリアし,バイト操作する際のインデックスレジスタ(edi, esi)をインクリメント方向に設定する.(DF=1でデクリメント)
siレジスタを"Use a boot loader. Remove disk an d press any key to reboot ..."の先頭に設定する.
...
start_of_setup:
%es = %dsをし,cld
%ds == %ss(スタックセグメント)*1
→ 真ならスタックが正常に設定されているとして2へ
→ 偽ならスタック領域の再計算を行う
ロードフラグと比較してヒープ領域が使用できるなら,%dx = heap_end_ptr + STACK_SIZE
(heap_end_ptr = _end + STACK_SIZE - 512なので実質_end - 512)
2
%dxの下位2bitのalignment(dword align)
0になったら%dx = 0xfffc
3
%ss = %ds
%esp = %dx(上位16bitはクリア)
IFフラグをセットする(この時点でスタック領域は使用可能になっている)
push %ds
push 6
ret
6
セットアップシグネチャ(0x5a5aaa55)の照会
→ 失敗なら最終的にhlt, die()
%eax = 0
%cx = ( ( _end + 3 ) - __bss_start ) >> 2
rep; stosl(%cxが0になるまで0クリアを繰り返す)
stosはeax(ax, al)をsi:(e)diにストアする.命令サイズに応じて(e)diをインクリメント(デクリメント)する.
call main(Cで書かれたコードへの移行)
次回にmainを読んでいく.
参考
http://d.hatena.ne.jp/outland_karasu/20070509/1178697916
http://softwaretechnique.jp/OS_Development/index.html
http://www.mztn.org/lxasm64/x86_x64_table.html