プロローグ
PC の電源を入れると、まずは BIOS が走っていろいろします。
この辺りは FreeBSD じゃないのでさっくりと飛ばして……
BIOS のブート優先順位に従って「HDD0!君に決めた!」となると、
HDD0 の第0セクタを、メモリの 0x7c00 から1セクタ分(512Byte)だけ読んで、
命令ポインタを 0x7c00 に設定します。
boot0 その1
第0セクタが 0x7c00 以降に読みこまれて、その先頭を指しています。
実際に書かれているデータは id:Hossy:20120117 で読んだ通り。
このセクタは前半にブートローダ(boot0)が実装されていて、
後半にスライステーブルが記録されているという構造になっています。
ブートローダ部分のソースコードはこちら。
/usr/src/sys/boot/i386/boot0
boot0.S の start: ラベルのある命令、
start: cld # String ops inc
これが 0x7c00 にある命令です。
ここからずらずらと実行を始めますが、最初に行う処理はこれです。
movw $LOAD,%sp # stack movw %sp,%si # Source movw $start,%di # Destination movw $0x100,%cx # Word count rep # Relocate movsw # code
$LOAD ( 0x7c00 ) から 0x100 word ( 512Byte ) を、$start ( 0x600 ) に転送する処理です。
C で書くと
memcpy( (void*)0x600, (void*)0x7c00, 512 );
こういうことです。
$LOAD boot0.S の上の方で定義されているマクロで、
第0セクタが読まれたアドレス、0x7c00 を示します。
$start は先ほどの start ラベルのアドレスなので、リンク時に解決します。
Makefile では
BOOT_BOOT0_ORG ?= 600
と定義したマクロを使って -Ttext ${BOOT_BOOT0_ORG} とリンカに指定しているため、
boot0 は 0x600 から始まる、と仮定してアドレス解決を行います。
そのため、$start は 0x600 としてハードコーディングされます。
コードのコピーが終わったら、
jmp main-LOAD+ORIGIN # Jump to relocated code main:
現在は 0x7c00 から始まる方の main ラベル直前にいますが、
この jmp 命令で、0x600 から始まる方の main ラベルに飛びます。
大体想像できると思いますが、ORIGIN は 0x600 と定義されているマクロです。
なぜ一旦 0x600 に自身をコピーしてそっちに移動するかと言うと、
この後で boot1 を 0x7c00 に読みこむので、int 命令から戻ってこれなくなるためです。
boot0 その2
さて、第0セクタが丸々 0x600 以降に読みこみ済みということは、
スライステーブルはただのメモリアクセスで読める状態です。
以降はアドレスを計算しながらテーブルをパースして、
おなじみのブートセレクタを画面に出力する処理があります。
この辺り特に面白い処理はないですが、C だと通常 \0 ヌル文字で終端する所、
1バイト節約のためか、最上位ビットを立てた文字で終端判定をしています。
os_win: .ascii "Wi"; .byte 'n'|0x80
boot0 その3
さて、ブートセレクタで適当なスライスが選択された場合、
check_selection ラベルの 3 という地点から boot1 に接続します。
3: movw $LOAD,%bx # Address for read movb $0x2,%ah # Read sector callw intx13 # from disk
intx13 サブルーチンはその名の通り、int 13h 命令を発行するコードです。
処理内容は ah で指定している通り、HDD から1セクタだけ読みこみます。
読みこむセクタは、サブルーチン中でスライステーブルから計算します。
id:Hossy:20120117 で読んだ BSD スライスの第0セクタ、つまり boot1 を指します。
また、読みこみ先は boot0 の実行が始まったアドレスと同じく 0x7c00 です。
現在地は 0x600 に転送した方のイメージ内なので、自分自身が書き換わることもありません。
cmpw $MAGIC,0x1fe(%bx) # Bootable? jne beep # No
読んだセクタの簡単な検査。末尾に MBR と同様にマジックナンバーがあるそうです。
id:Hossy:20120117 確かに。開始番地だけでなくマジックナンバーも揃えているなんて。
jmp *%bx # Invoke bootstrap
で、boot1 を読みこんだ 0x7c00 へ FAR ジャンプして boot0 は終了です。