sh-liloアドレスマップ

最近ブートローダ自体に詳しくなってきたので、sh-liloソースコードを読み直してアドレスマップもどきを起こしてみた。(first.Sとsecond.cから起こした)


全部1枚に収めたせいでごちゃっとしていて見づらいけど、個人的に1枚に書いてないと把握できないのでご勘弁を。
それで、図の話なんだけど、MBRとImage Descripter部分の黄色く塗りつぶしてあるところはliloがディスクから読みだすフィールドだったりする。だから黄色の部分はliloを焼きこんだ後で念のため確認しておくと焼きミスを減らせそうな予感。
あと、図は全体的に左側がディスク上の話で右側がメモリ上の話だったりする。左のディスク側からデータを右のメモリ側へロードする、的な。

liloに限らずブートローダはあまりまじめにファイルシステムを見ないっぽい。少なくともsh-liloファイルシステムは一切気にせず直接ドライブのアドレスを指定してデータを読み出すみたい。
じゃぁどうやってデータのアドレスを知るのかっていうと、liloのインストール時にbzImageとboot.bのアドレスを調べてディスクに書き込んでいるっぽい。
ディスクのアドレスはセクタ番号で表現してて、1セクタが512バイトになる単位らしい。liloではセクタ番号は3バイト分あるみたいなので、最大512B*0xffffff=8GBっぽいね。
それから、図のMBRのすぐ左下に小さく描いてある「Sector Descripter」ってのがデータが置いてある「セクタ番号」とかの情報が入ってる5バイトの数値で、あちこちに書いてある「xxx sector」みたいなフィールドはこの5バイトの情報になってるみたい。
ややこしいんだけど、セクタ番号自体は1バイト目、2バイト目、4バイト目ととびとびで入っててしかも順番が逆になってる(リトルエンディアン的な並び)。んで、間に挟まってる3バイト目が0xCxになってて、下位4ビットがデバイス(ディスクドライブ)番号らしい。んで、最後の5バイト目がセクタ数を表すらしいけど、見た感じ基本「1」になってるっぽい。ので、複数セクタにまたがる(512バイト以上の)データは複数個のSector Descripterが必要になるっぽい。

あと前提としてboot.bってのが1stローダと2ndローダが合体したバイナリファイルになってる。BIOSが1stローダ(512B)を読み出して1stローダが2ndローダを読み出して、2ndローダがどの設定でkernelを読み出すか決めてbzImageを読み出す感じで。


では、さっそく個別に整理。

ディスクの先頭セクタにMBRがあるのは、他のブートローダと同じっぽい。んで、MBRの最初の方に1stローダが書かれていて最後にパーティションテーブルが書かれている構造も同じみたい。1バイト目はjump命令になっていて1stローダの先頭まで一気にskipするようになってて、jump命令から1stローダの先頭までのスキマ部分にセクタ番号とかの設定値類が書かれているみたい。設定値は図のとおりなので省略。基本的にセクタ番号だけだし。

  • mapファイル

mapファイルが実は重要かもしれない。デフォルト?だと/boot/mapってファイルが実体になると思うけど、こいつはliloのインストール時に生成されるファイルで、ざっくりとは図の通り(図中の左側のど真ん中)。
で、個別に見ていくと。Default Command Line Parameterってのはkernelのコマンドライン引数のデフォルト値を入れておく場所みたい。たぶん1セクタ分しかなさそうなので最大512バイト分になるね。
Image Descripter table#1, #2はあとで別途整理。
Keyboard Translationとかってのはキーコードか何かの読み変えテーブルっぽいのでとりあえず気にしなくて良さそう。
で、最後?に「kernelコマンドライン引数」と「bzImageのmap情報」が入ってるみたい。このmap情報というのがすさまじくて、5バイトのセクタ情報が延々と並んでいてどうやらディスク上でのbzImageの先頭セクタから最終セクタまでのすべての情報が入ってるっぽい。ざっと見た感じ全部セクタ数部分が1なので、2〜3MBのbzImageなら40〜60個くらい並んでるはず。2ndローダはこのmap情報を5バイトずつ読みだして1セクタずつbzImageを読み出すっぽい。

  • Image Descripter

mapファイルのところで出てきたImage Descripter table#1, #2ってところに書かれていて構造は図の一番右下の「Image Descripter」ってやつなんだけど、これはどうやら起動時に「何番目の設定で起動しますか?」的な選択画面で出す情報になってるみたい。
ただ、sh-liloはimage nameフィールドとstart sectorフィールドしか見てないっぽい。つまりramdisk起動には非対応っぽい。
んで、image nameフィールドは見たまんま設定の名前で、start sectorがmapファイルのkernelコマンドライン引数とかbzImageのmap情報とかの先頭セクタを指してるっぽい。(正確には、「???」と書いてある謎のall 0部分のセクタ番号を指している)

  • kernel引数

arch/sh/kernel/head.Sの最初にこんなのが書いてあって

	.section	.empty_zero_page, "aw"
ENTRY(empty_zero_page)
	.long	1		/* MOUNT_ROOT_RDONLY */
	.long	0		/* RAMDISK_FLAGS */
	.long	0x0200		/* ORIG_ROOT_DEV */
	.long	1		/* LOADER_TYPE */
	.long	0x00360000	/* INITRD_START */
	.long	0x000a0000	/* INITRD_SIZE */
	.long	0

どうもこれがkernelへの引数になるっぽい。arch/sh/kernel/vmlinux.lds.Sを見るとセクションの開始がCONFIG_PAGE_OFFSET + CONFIG_MEMORY_START + CONFIG_ZERO_PAGE_OFFSETになってるからこれが先頭アドレスになるような気がする。
liloMakefileとsecond.cを見る限り、kernelへのパラメータはメモリの0x8c001000に置くっぽい。でも、2.6.36とかのdefconfigを見るとCONFIG_PAGE_OFFSET + CONFIG_MEMORY_START + CONFIG_ZERO_PAGE_OFFSETは0x8c010000になって一致しない気がする…
どっちかを変更しないとダメな気がするなぁ…
それはさておき。sh-liloはどうやらパラメータを"/dev/hda1から起動、ramdiskなし"の固定設定で入れちゃうっぽい。

    *(long *)parm      = 1;	/* Read only mount? */
    *(long *)(parm+4)  = 0;	/* RAMDISK Flags */
    *(long *)(parm+8)  = 0x0301; /* Root device: XXX should get from cls.. */
    *(long *)(parm+12) = 1;	/* Loader type (LILO = 1) */
    *(long *)(parm+16) = 0;	/* Initrd start */
    *(long *)(parm+20) = 0;	/* Initrd size */
    *(long *)(parm+24) = 0;	/* Not defined yet */

こんな感じで。0x0301ってのがどうやら/dev/hda1を指すらしい。
RAMDISKに関してはそもそもロードに対応してないから良いとして、rootドライブが固定って大丈夫か?と思ったらkernel側のprepare_namespace() @ init/do_mounts.cでコマンドライン引数の「root=」設定に置き換えるみたいなので大丈夫っぽい。
それで、そのコマンドライン引数はまずデフォルトのコマンドライン引数を入れた後に、Image Descripterから読んだ先のbzImageの直前に置いてある個別のコマンドライン引数を追記するっぽい。たぶん個別の引数は/etc/lilo.confのappend行に書いたやつじゃないかと思う。



と、まぁこんな感じっぽいのでカーネルのアドレスだけ気になるけど、liloのインストール後はこれを見ながらディスクにちゃんと焼けたかチェックすれば良さそう。


[2010/11/25 00:13追記]
って図が縮小されて字がつぶれてる−。
後で分割して差し替えかな。
[2010/11/25 23:43追記]
フォトライフからupしなおしてみた。これで大丈夫?