カーネル+initrd→RAMDISKに切り替え
USBがmountできなかった理由が判明しました。
なんとびっくり。nashのmkdirコマンドは「-p」で途中のディレクトリをすべて作る機能が入ってなかった!
nashのソースコードはこんな感じ。
int mkdirCommand(char * cmd, char * end) { char * dir; int ignoreExists = 0; cmd = getArg(cmd, end, &dir); if (cmd && !strcmp(dir, "-p")) { ignoreExists = 1; cmd = getArg(cmd, end, &dir); } if (!cmd) { printf("mkdir: directory expected\n"); return 1; } if (mkdir(dir, 0755)) { if (!ignoreExists && errno == EEXIST) { printf("mkdir: failed to create %s: %d\n", dir, errno); return 1; } } return 0; }
ご覧のとおり、「-p」は「すでにディレクトリが存在していてもエラーにしない」機能になってたり。。。
initrdにbusyboxを追加していたのでmkdirを/bin/mkdirとしてnashのmkdirを使わないように変更したら無事mountできた。
う〜ん。どうやらnashはソースコードをよく見てからじゃないと使い方がわからないらしい。
とりあえずカーネルにinitrdを忍び込ませるのはできたので、USBからRAMDISKファイルをロードしてルートファイルシステムを切り替えてみた。
gunzip -c /mnt/usb/ramdisk.img.gz > /dev/ram0 mount -t ext2 /dev/ram0 /sysroot switchroot dummy /sysroot
ここでさらにはまってしまった。
nash はどうやらパイプ「|」が使えないっぽい。リダイレクト「>」は使えた。。。
あと、今回使ったバージョンのswitchrootは第一引数にダミー文字列を入れていないとダメっぽい。
cmd = getArg(cmd, end, &new); if (cmd) { if (!strcmp(new, "--movedev")) moveDev = 1; cmd = getArg(cmd, end, &new); } if (!cmd) { printf("switchroot: new root mount point expected\n"); return 1; }
nashのソースコードのこの部分がおかしい。
newに切り替え先ルートファイルシステムのトップディレクトリ(今回なら/sysroot)を指定するっぽいけど、引数に「--movedev」が入っているかどうか確認するコードのせいで第一引数が捨てられているし。
ということで、引数は無駄に2つ必要になってる。
ちなみにnashのバージョン6系のソースコードを見たらこの問題は修正されてるっぽい。
今回使ったソースは4系。なんで6系のソース使わないかって?コンパイルエラーでコケたからデス!
そんなこんなで最終的にnashのスクリプトはこんな感じなった。(initrdの/initととして保存)
#!/sbin/nash mount -t proc /proc /proc setquiet echo nash script: Mounting proc filesystem echo nash script: Mounting sysfs filesystem mount -t sysfs /sys /sys echo nash script: Creating /dev mount -o mode=0755 -t tmpfs /dev /dev mkdir /dev/pts mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts mkdir /dev/shm mkdir /dev/mapper echo nash script: Creating initial device nodes mknod /dev/null c 1 3 mknod /dev/zero c 1 5 mknod /dev/systty c 4 0 mknod /dev/tty c 5 0 mknod /dev/console c 5 1 mknod /dev/ptmx c 5 2 mknod /dev/rtc c 10 135 mknod /dev/tty0 c 4 0 mknod /dev/tty1 c 4 1 mknod /dev/tty2 c 4 2 mknod /dev/tty3 c 4 3 mknod /dev/tty4 c 4 4 mknod /dev/tty5 c 4 5 mknod /dev/tty6 c 4 6 mknod /dev/tty7 c 4 7 mknod /dev/tty8 c 4 8 mknod /dev/tty9 c 4 9 mknod /dev/tty10 c 4 10 mknod /dev/tty11 c 4 11 mknod /dev/tty12 c 4 12 mknod /dev/ttyS0 c 4 64 mknod /dev/ttyS1 c 4 65 mknod /dev/ttyS2 c 4 66 mknod /dev/ttyS3 c 4 67 mknod /dev/ram0 b 1 0 sleep 10 # USBはLinuxに認識されるまでに5secくらい時間がかかるのでちょっと待ってみる /bin/mkdir -p /mnt/usb mknod /dev/sdb b 8 16 mknod /dev/sdb1 b 8 17 mount -t ext3 /dev/sdb1 /mnt/usb echo nash script: loading RAMDISK image. gunzip -c /mnt/usb/ramdisk.img.gz > /dev/ram0 echo nash script: Mounting root filesystem. mount -t ext2 /dev/ram0 /sysroot # 今回使ったRAMDISKはext2イメージファイル echo nash script: Switching to new root and running init. sleep 1 switchroot dummy /sysroot echo nash script: Booting has failed. # 本来ここには到達しない。エラーがあって/が切り替わらなかった場合にここにくる /sbin/getty -L 115200 ttyS0 # シリアル(ttyS0)が使えるならここでloginできるようになるので救済される sleep -1
initrdはいろいろ入れちゃったけど最低限以下が入っていればいけるはず。
gettyでの救済措置に対応するには追加でbin/login、etc/passwd、etc/shadow、bin/shが必要かな。
init bin/busybox bin/mkdir -> busybox bin/gunzip -> busybox sbin/nash proc sys dev
sleepとかecho、mountとかはnashのコマンドとして実装されているのでいらない。何がnashに入っているかはソースコードを見るのが一番っぽい。
ちなみにnashのコマンドに入っていないコマンドの場合は「/usr/bin:/bin:/sbin:/usr/sbin」のどこかにコマンドが入っていれば自動的にパスを探してきて実行するっぽい。nashのコマンドを使わずに自前のコマンドを実行する場合は絶対パスでコマンドを指定すれば良いっぽい。
最後にカーネルの引数はRAMDISK(容量32MB)に対応させるためにこんな感じで。
rw root=/dev/ram0 ramdisk_size=32768 console=ttyS0,115200