2009年7月30日木曜日

startup_32

プロテクトモードに設定するとアドレス0x100000へジャンプしています。ここがプロテクトモードにおけるカーネルのエントリポイントとなります。プロテクトモードにおけるカーネルはいわばカーネル本体なのであって、ここは圧縮された状態で読み込まれます。ソースのパスとしては「arch/x86/boot/compressed」以下のソースに当たります。ここのリンカスクリプトを見てみると(ここでは32bitマシンを想定して「vmlinux_32.lds」を見てみます)、エントリポイントとして「startup_32」を指定していることが分かります。
それではこのstartup_32を見てみましょう。

arch/x86/boot/compressed/head_32.S
=============
startup_32:
cld /* 割り込み禁止 */
/* KEEP_SEGMENTSフラグが立っていたらセグメントを書き換えない */
testb $(1<<6), BP_loadflags(%esi)
jnz 1f

cli /* 割り込み許可 */

/* __BOOT_DSでセグメントレジスタを更新する */
movl $(__BOOT_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
movl %eax,%ss
1:

/* スタックポインタを再計算。よく分かりません */
leal (0x1e4+4)(%esi), %esp
call 1f

1: popl %ebp
subl $1b, %ebp

/* EBPにはロードされた場所が入っている。EBXには圧縮したカーネルイメージを
 安全に解凍すべきアドレスを入れてやる。*
#ifdef CONFIG_RELOCATABLE
movl %ebp, %ebx
addl $(CONFIG_PHYSICAL_ALIGN - 1), %ebx
andl $(~(CONFIG_PHYSICAL_ALIGN - 1)), %ebx
#else
movl $LOAD_PHYSICAL_ADDR, %ebx
#endif

/* 圧縮データサイズと解凍したデータサイズを入れ替える */
/* あとはアライメントやら何やらの設定。よく分かりません */
subl input_len(%ebp), %ebx
movl output_len(%ebp), %eax
addl %eax, %ebx
/* Add 8 bytes for every 32K input block */
shrl $12, %eax
addl %eax, %ebx
/* Add 32K + 18 bytes of extra slack */
addl $(32768 + 18), %ebx
/* Align on a 4K boundary */
addl $4095, %ebx
andl $~4095, %ebx

/* 解凍したカーネルをコピーする */
pushl %esi
leal _end(%ebp), %esi
leal _end(%ebx), %edi
movl $(_end - startup_32), %ecx
std
rep
movsb
cld
popl %esi

/* カーネルのスタートアドレスを算出してEBPへ格納 */
#ifdef CONFIG_RELOCATABLE
addl $(CONFIG_PHYSICAL_ALIGN - 1), %ebp
andl $(~(CONFIG_PHYSICAL_ALIGN - 1)), %ebp
#else
movl $LOAD_PHYSICAL_ADDR, %ebp
#endif

/*
* カーネルを再配置する
*/
leal relocated(%ebx), %eax
jmp *%eax

=============

ブートしていた頃のアセンブラよりも複雑でなかなか読み進められません。思いっきり要約してしまうと、セグメントレジスタを初期化して、圧縮されているカーネルの展開先アドレスを
決めて、最終的にジャンプしています。最後のジャンプ先は「leal relocated(%ebx), %ebx」となっていて、これは「EBX=relocated + EBX」ということ。そしてその「relocated」というのはすぐ下にあります。
こちらは次回。

0 件のコメント:

コメントを投稿