biosbootからロードされたboot loaderプログラムは、最初にsrt0.S内にあるstartupルーチンが走り、boot loaderプログラムが動作するための初期化処理を行ないます。そして初期化終了後、startup ルーチン内からboot関数が呼ばれます。
biosbootからロードされたboot loaderプログラムは、最初にsrt0.S内にあるstartupルーチンが走り、boot loaderプログラムが動作するための初期化処理を行ないます。そして初期化終了後、startup ルーチン内からboot関数が呼ばれます。
biosbootがスタックに格納した、BOOTMAGICの値が正しいかチェックします。BOOTMAGICはMakeから渡される定数であり、sys/arch/i386/stand/Makefile.incに0xc001d00d
と定義されています。しかし、BOOTMAGICの値が正しくなくても特にエラーにしたりはしていないようです。
stackに格納されている、起動ドライブ番号をedx レジスタにストアします。
セグメントの設定とリアルモードに移行処理を行うので割込み禁止にします。この後、boot関数をcallするまで割込み禁止になります。
Gdtrの内容を、GDTR(Global Descriptor Table Register)にセットします。これにより、gdtにあるSegment Descriptor Tableが有効になり、これ以降プロテクトモード用のセグメントが扱えるようになります。
設定後は各セグメントはSelectorと呼ばれる値でアクセスすることになり、各セレクタ値のアクセス範囲・アクセス属性は下記のようになります。
cr0 レジスタのPE bitを1にしてプロテクトモードに移行します。
jumpを実行し、パイプラインのクリアとCode Segment(CS) Registerをプロテクトモード用の値に再設定を行います。jump後Code Segment(CS) Registerはセレクタ値32bit code(0x8)で動作するようになります。
CS以外のセグメントレジスタを32bit data(0x10)に設定します。
sp レジスタをBOOTSTACKに設定します。Stackは0xfffcになります。
設定後、各レジスタの設定値は下記のようになります。
起動ドライブ番号をStackに格納し、起動ドライブ番号をbios_bootdevに格納します。
pmm_initをcallして割込みの設定をします。
読込んだbootプログラムはELF形式であり、幾つかのセッションに分かれている。この中の未初期化データセッションである、bss領域をゼロクリアする。
elfは幾つかのセッションに分かれている。
と言いましたが、これらのセッション情報はelf Headerに書かれています。elf Headerの情報は、objdump コマンドの"-h"オプションを使うことによって、可読可能な形式で見ることが出来きます。
例えば、bootのelf Header情報を表示させるには、objdumpを使えば必要な情報を見ることができます。
# objdump -h /usr/src/sys/arch/i386/stand/boot/obj/bootreadelf コマンドを使用すれば、さらに詳しい情報も見る事もできます。
# readelf -a /usr/src/sys/arch/i386/stand/boot/obj/boot
objdump コマンドを使用して、bootのマッピングを表示すると、下記のようになります。
boot: file format elf32-i386 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0000858f 00040120 00040120 00000120 2**4 CONTENTS, ALLOC, LOAD, CODE 1 .rodata 00001dae 000486c0 000486c0 000086c0 2**5 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .data 00000110 0004a470 0004a470 0000a470 2**2 CONTENTS, ALLOC, LOAD, DATA 3 .bss 000011c8 0004a580 0004a580 0000a580 2**5 ALLOC
これを視覚化すると下記のようになります。
図を見ると分かるようにbbsの最初のアドレスには_edata、最後のアドレスには_endと言うラベルが定義されています(これらのラベルは、objdump -x bootで見ることが出来る)。bbsをクリアするには( _end - _edata )でオフセットを求め、_edataからオフセット分クリアすれば出来ますね。
boot関数をcallする。
本来、boot関数からkernelに移行してしまうので、_rtt関数が呼ばれることは無い。( と思う ;-) )