#navi_header|技術| Linux Kernel 2.6.x を使ってフロッピーディスクから起動するLinuxシステムを作ってみます。 #more|| #outline|| ---- * Kernel 2.6.x の入手とコンパイル 今回は linux-2.6.38.2.tar.bz2 を使います。 入手先: - The Linux Kernel Archives -- http://www.kernel.org/ $ 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後の差分: #pre||> $ 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できます。 #pre||> $ 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) ||< ここまで来ればコンパイルは成功です。 * FDブート用のイメージを作成 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になります。 * SYSLINUXを使ったrootデバイスの設定 rdev(util-linuxパッケージ)を使う方法と "root=" カーネルパラメータを使う方法があります。 将来GRUBなど他のBootLoaderを使うときにもお世話になり、柔軟性もある後者の方法を紹介します。 今回はSYSLINUXをBootLoaderに使っているので、SYSLINUXの流儀でカーネルパラメータを指定します。FDブートの場合はFD直下の"SYSLINUX.CFG"ファイルを編集します。 参考: - Syslinux Wiki -- http://syslinux.zytor.com/ - How do I Configure SYSLINUX -- http://syslinux.zytor.com/wiki/index.php/SYSLINUX#How_do_I_Configure_SYSLINUX.3F 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フロッピーディスクサイズ - Ext2ファイルシステム - 最小限度のディレクトリとデバイスファイル まず1.4MBフロッピーディスクサイズのデータファイルを作成します。 #pre||> $ 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コマンドで作成します。 #pre||> $ su # /sbin/mke2fs -F root_fs.img mke2fs 1.39 (29-May-2006) Filesystem label= OS type: Linux Block size=1024 (log=0) ... # ||< loopbackマウントしてみます。 #pre||> # 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/ これで「容器」は出来上がりました: #pre||> # 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の紹介は下記参照: - 組み込みLinuxで際立つ「BusyBox」の魅力(1/2) - @IT MONOist -- http://monoist.atmarkit.co.jp/fembedded/articles/busybox/busyboxa.html 今回は busybox-1.18.4.tar.bz2 を使いました。 - BusyBox -- http://www.busybox.net/ ビルドの際はlinux kernelと同様cursesベースの設定ツールを使います。 - 技術/UNIX/BusyBox - Glamenv-Septzen.net -- http://www.glamenv-septzen.net/view/507 #pre||> $ 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 その他は最低限以下の機能を有効にします。 - Core Utilities : 適当に最低限度のファイルシステム操作を出来るコマンドを選択しておきます。 - Shell : ashで良いと思います。sh, bashにエイリアスできます。 - Init Utilities : poweroff, halt, reboot, initを入れます。 - Linux System Utilities : mount, umountを入れます。"-a"オプションを有効にしておきます。 あとはお好みで有効化し、ビルドします。 $ 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: 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コマンドを確認できます。 &image(yb://images/reduced.linux/01_01.png) * 上手く動かない時は: ・カーネルをロードできない → "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ブートするためだけにこんなにハマらなくっちゃならないんだ・・・。 ** "make mrproper" と .config ファイル 色々試行錯誤する中でKernel Configurationを変更し、一旦全部クリーンしてからリビルドする場合があると思います。 make mrproper すると Kernel Configuration を保存した ".config" も消してしまうので注意してください。(kernel-2.4.xの場合も同様) #navi_footer|技術|