Linux Kernel 2.6.x を使ってフロッピーディスクから起動するLinuxシステムを作ってみます。
今回は linux-2.6.38.2.tar.bz2 を使います。
入手先:
$ pwd /home/msakamoto/reduced.linux $ ls ... linux-2.6.38.2.tar.bz2 ... $ tar jxf linux-2.6.38.2.tar.bz2
出力用ディレクトリを作成し、KBUILD_OUTPUT環境変数を使ってmake menuconfigします。makeコマンドの詳細は "make help" で確認して下さい。
$ mkdir 01_boot_from_fd_2.6 $ export KBUILD_OUTPUT=`pwd`/01_boot_from_fd_2.6 $ echo $KBUILD_OUTPUT /home/msakamoto/reduced.linux/01_boot_from_fd_2.6 $ cd linux-2.6.38.2/ $ make help ... # "make O=/foo/bar" としてmakeの都度ディレクトリを指定する方法もありますが、 # 今回は使いません。 ... $ make allnoconfig ... $ make menuconfig
CONFIG_IKCONFIG, EFL実行ファイルのサポート, FDのBlockDevice, Ext2ファイルシステムを追加します。
allnoconfigとmenuconfig後の差分:
$ diff .config .config.old 4c4 < # Wed Apr 6 09:33:17 2011 --- > # Wed Apr 6 09:31:49 2011 99,100c99 < CONFIG_IKCONFIG=y < CONFIG_IKCONFIG_PROC=y --- > # CONFIG_IKCONFIG is not set 369,370c368 < CONFIG_BINFMT_ELF=y < # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set --- > # CONFIG_BINFMT_ELF is not set 395,405c393 < CONFIG_BLK_DEV=y < CONFIG_BLK_DEV_FD=y < # CONFIG_BLK_DEV_COW_COMMON is not set < # CONFIG_BLK_DEV_LOOP is not set < < # < # DRBD disabled because PROC_FS, INET or CONNECTOR not selected < # < # CONFIG_BLK_DEV_RAM is not set < # CONFIG_CDROM_PKTCDVD is not set < # CONFIG_BLK_DEV_HD is not set --- > # CONFIG_BLK_DEV is not set 586,588c574 < CONFIG_EXT2_FS=y < # CONFIG_EXT2_FS_XATTR is not set < # CONFIG_EXT2_FS_XIP is not set --- > # CONFIG_EXT2_FS is not set
ビルドします。 "01_..." にcdすれば、KBUILD_OUTPUT環境変数が空でもbuildできます。
$ cd 01_boot_from_fd_2.6 $ make ... HOSTCC arch/x86/boot/tools/build BUILD arch/x86/boot/bzImage Root device is (253, 0) Setup is 14008 bytes (padded to 14336 bytes). System is 763 kB CRC 43289aa6 Kernel: arch/x86/boot/bzImage is ready (#1)
ここまで来ればコンパイルは成功です。
2.6.xの場合、bzImageのままではFDブート出来ません。
SYSLINUXをインストールした上で
make fdimage
します。CentOS 5.5 の場合はパッケージとしてsyslinuxが提供されていますので、そのまま使います。入っていなければyumなどで適宜インストールしておきます。
# which syslinux /usr/bin/syslinux # rpm -qf /usr/bin/syslinux syslinux-3.11-4
というわけで make してみます。
$ make fdimage ... dd if=/dev/zero of=arch/x86/boot/fdimage bs=1024 count=1440 ... $ wc -c arch/x86/boot/fdimage 1474560 arch/x86/boot/fdimage
root側でloopbackマウントし、中身を確認してみます。
# mount .../x86/boot/fdimage /mnt/floppy -t msdos -o loop # ls /mnt/floppy/ ldlinux.sys linux syslinux.cfg # cat /mnt/floppy/syslinux.cfg default linux
SYSLINUXでFDブートの場合はFAT16フォーマットされたFDの中にカーネルイメージが"linux"というファイル名で配置されます。
ここまで来ればVMwareなど適当な仮想マシン上で動かせます。
ところが、実際に動かしてみると分かりますが root デバイスが見つからずpanicになります。
rdev(util-linuxパッケージ)を使う方法と "root=" カーネルパラメータを使う方法があります。
将来GRUBなど他のBootLoaderを使うときにもお世話になり、柔軟性もある後者の方法を紹介します。
今回はSYSLINUXをBootLoaderに使っているので、SYSLINUXの流儀でカーネルパラメータを指定します。FDブートの場合はFD直下の"SYSLINUX.CFG"ファイルを編集します。
参考:
SYSLINUX.CFG:
default linux
→
default linux label linux kernel linux append root=0200
なお改行はDOSの流儀に従い
\r\n
にする必要があります。vimでは
:set fileformat=dos
とすれば自動でDOS改行に変換してくれます。
NOTE: "root="オプションですが、
root=/dev/fd0
や
root=/dev/VolGroup00/LogVol00
のようにデバイスファイル名を指定するパターンを多く見かけると思います。これらはinitrdがそうしたデバイスファイルを作っています。
今回はinitrdを使わないため、デバイスファイル名を指定できません。
そういう場合は今回のようにデバイスのmajor, minor番号をhexで指定できます。
というわけでもう一度VMwareなりBochsなりで動かしてみると、今度は
VFS: Insert root floppy and press ENTER
という表示で停止しました。
ではいよいよルートファイルシステムを作ってみます。
今回作るルートファイルシステム:
まず1.4MBフロッピーディスクサイズのデータファイルを作成します。
$ pwd /home/msakamoto/reduced.linux/01_boot_from_fd_2.6 $ dd if=/dev/zero of=root_fs.img bs=1024 count=1440 1440+0 records in 1440+0 records out 1474560 bytes (1.5 MB) copied, 0.0135535 seconds, 109 MB/s
続いてExt2ファイルシステムを作成します。mke2fsコマンドで作成します。
$ su # /sbin/mke2fs -F root_fs.img mke2fs 1.39 (29-May-2006) Filesystem label= OS type: Linux Block size=1024 (log=0) ... #
loopbackマウントしてみます。
# mkdir /mnt/loop0 # mount root_fs.img /mnt/loop0 -t ext2 -o loop # ls -l /mnt/loop0/ 合計 12 drwx------ 2 root root 12288 4月 5 21:01 lost+found
/dev, /proc, /etc ディレクトリを作成します。
# pushd /mnt/loop0; mkdir dev proc etc; popd
最低限度必要なデバイスファイルを "cp -a" でコピーします。
# cp -a \ /dev/console \ /dev/tty[0-4] \ /dev/null \ /dev/zero \ /dev/urandom \ /dev/fd0 \ /mnt/loop0/dev/
これで「容器」は出来上がりました:
# tree /mnt/loop0/ /mnt/loop0/ |-- dev | |-- console | |-- fd0 | |-- null | |-- tty0 | |-- tty1 | |-- tty2 | |-- tty3 | |-- tty4 | |-- urandom | `-- zero |-- etc |-- lost+found `-- proc 4 directories, 10 files
続いてBusyBoxを使って「中身」を入れます。root_fs.imgはloopbackマウントしたままにしておきます。
BusyBoxの紹介は下記参照:
今回は busybox-1.18.4.tar.bz2 を使いました。
ビルドの際はlinux kernelと同様cursesベースの設定ツールを使います。
$ pwd /home/msakamoto/reduced.linux $ ls ... busybox-1.18.4.tar.bz2 ... $ tar jxf busybox-1.18.4.tar.bz2 $ cd busybox-1.18.4/ $ make help Cleaning: clean - delete temporary files created by build distclean - delete all non-source files (including .config) doc-clean - delete all generated documentation Build: all - Executable and documentation busybox - the swiss-army executable doc - docs/BusyBox.{txt,html,1} html - create html-based cross-reference ...
BusyBoxの場合、makeコマンドの"O="オプションで別ディレクトリに出力できます。
$ pwd /home/msakamoto/reduced.linux $ mkdir 01_busy_box_build $ cd busybox-1.18.4/ $ make O=../01_busy_box_build allnoconfig ...
これで一旦全オプションを無効化した設定になります。続いて "make menuconfig" で必要分だけ有効化します。
$ cd ../01_busy_box_build/ $ make menuconfig
今回は静的バイナリとしてビルドするので、下記オプションを有効にします。
Busybox Settings ---> Build Options ---> Build BusyBox as a static binary (no shared libs)
また mount コマンドを有効にするため、下記オプションを有効にします。
Busybox Settings ---> General Configuration ---> Enable Linux-specific applets and features
その他は最低限以下の機能を有効にします。
あとはお好みで有効化し、ビルドします。
$ make ... LINK busybox_unstripped Trying libraries: crypt m Library crypt is not needed, excluding it Library m is not needed, excluding it Final link with: <none> DOC busybox.pod DOC BusyBox.txt DOC busybox.1 DOC BusyBox.html $ ls ... busybox* busybox_unstripped* ... $ wc -c busybox 716096 busybox $ file busybox busybox: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), \ for GNU/Linux 2.6.9, statically linked, for GNU/Linux 2.6.9, stripped
ルートファイルシステムにインストールします。
$ su # make CONFIG_PREFIX=/mnt/loop0 install
inittabを用意します。
# vim /mnt/loop0/etc/inittab
内容:
::sysinit:/etc/rcS ::askfirst:-/bin/sh tty2::askfirst:-/bin/sh tty3::askfirst:-/bin/sh tty4::askfirst:-/bin/sh ::restart:/sbin/init ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r
BusyBoxのinittabの文法に付いてはソースコード "init/init.c" の末尾に書かれていますのでそちらを参照してください。
続いて "/etc/rcS" ファイルを用意します。
# vim /mnt/loop0/etc/rcS
内容:
#!/bin/sh mount -a /bin/sh
実行属性をつけるのを忘れずに。
# chmod +x /mnt/loop0/etc/rcS
最後にmountコマンドが使う fstab を用意します。
# vim /mnt/loop0/etc/fstab
内容:
/proc /proc proc defaults /dev/fd0 / ext2 defaults
loopbackマウントを解除します。
# umount /mnt/loop0
あとはVMwareなりBochsを動かすホストマシンにroot_fs.imgを転送し、fdimageから立ち上げます。
VFS: Insert root floppy and press ENTER
と出たらroot_fs.imgに切り替え、ENTERを入力します。
上手く行けば下図のようにシェルが立ち上がり、mountコマンドを確認できます。
・カーネルをロードできない
→ "make fdimage"、および syslinux.cfg の修正までの作業工程を確認してみてください。
・ルートファイルシステムをマウントできない
→ Ext2ファイルシステムをbuilt-inしているか確認して下さい : CONFIG_EXT2_FS
→ Floppy Diskを有効化しているか確認して下さい : CONFIG_BLK_DEV, CONFIG_BLK_DEV_FD
・"/sbin/init"が起動しない
→ ホストマシンでルートファイルシステムを調整した後、ちゃんとumountしてからVMwareなりBochsに読み込ませているか確認して下さい。
→ "/etc/inittab", "/etc/rcS" の内容および "/etc/rcS" の実行権限を確認して下さい。
→ どうしても動かないようであれば、syslinux.cfg で "init=/bin/sh" パラメータを追加指定し、とりあえずシェルが立ち上がるか確認してみてください。
・その他、"VFS: ..." で始まるエラーメッセージが表示される
→ Kernelソースの "init" ディレクトリの中をエラーメッセージでgrepしてみてください。ルートファイルシステムやinitrd絡みのエラーメッセージはこの中でprintk()されている事が多いです。ソースを読んで、必要なら詳しいprintk()を自分で挿入しリビルドしてみるなどしていくと、原因が分かるかもしれません。
自分が嵌ってしまったのは「Ext2モジュール組み込み忘れ」と「ホストマシンでルートファイルシステムをumountせずに使ってしまった」の2点です。
umountを忘れたせいか、"/sbin/init"が起動せず困りました。
KGDBを有効化してホストマシンからのgdbデバッグも試みたのですが、あれこれ試してるうちに "init=" パラメータを追加すれば動くようになって、「じゃあ大丈夫じゃね?」と "init=" パラメータを外してKGDBも外して元に戻してみたらちゃんと動き始めたり・・・。
なかなか悩ましいです。ってかマジでなんでFDブートするためだけにこんなにハマらなくっちゃならないんだ・・・。
色々試行錯誤する中でKernel Configurationを変更し、一旦全部クリーンしてからリビルドする場合があると思います。
make mrproper
すると Kernel Configuration を保存した ".config" も消してしまうので注意してください。(kernel-2.4.xの場合も同様)