仙石浩明の日記

2008年1月4日

La Fonera をディスクレス (flash less) ブートする

あけましておめでとうございます。 今年もよろしくお願いします。

La Fonera (La Fonera+) のブート・ローダ RedBoot は、 TFTP サーバに置いたカーネルを load して起動することができる。 したがって、 TFTP サーバから load したカーネルで root ファイル・システムを NFS マウントすれば、 フラッシュ・メモリを使わずにブートすること (ディスクレス・ブート、というか flashless boot) ができる。

La Fonera のようなルータをディスクレスにして 何が嬉しいのかと思う人もいるかもしれないが、 ルータのように常時ネットワークにつなぐものだからこそ、 ネットワークにつないでいないと使えないという ディスクレス最大の欠点が欠点にならないわけで、 むしろルータこそ積極的にディスクレスにすべきではなかろうか。

ディスクレス化することによるメリットは数多い。 なんといっても最大のメリットは、 フラッシュ・メモリを書き換えること無く、 手軽にファームウェアを入れ替えられる (OpenWrt や DD-WRT など) こと。 フラッシュ・メモリを書き換える時間が節約できるし、 書き換え回数を気にする必要もなくなるわけで、 心置き無くファームウェアのデバッグを行なえる。 また、 NFS を使えばフラッシュ・メモリの容量をはるかに超えるディスク領域を使えるので、 セルフ・コンパイル環境の構築なども容易に行なうことができる。

試しに、 La Fonera のフラッシュ・メモリを書き換えること無く DD-WRT をブートさせてみる。

まず、 DD-WRT の root ファイル・システムを NFS サーバ上に展開する。 ここでは例として /nfs/dd-wrt に展開した。 /usr/sbin/udhcpc の実行を抑制するため (理由は後述) に、 /nfs/dd-wrt/usr/sbin/udhcpc を削除しておく。

senri:/tmp # wget "http://www.dd-wrt.com/dd-wrtv2/downloads/release candidates/DD-WRT v24 RC6.2/Atheros WiSoc/Fonera/root.fs"
--09:22:37--  http://www.dd-wrt.com/dd-wrtv2/downloads/release%20candidates/DD-WRT%20v24%20RC6.2/Atheros%20WiSoc/Fonera/root.fs
           => `root.fs'
	...
09:22:46 (335.56 KB/s) - `root.fs' saved [2834432/2834432]

senri:/tmp # unsquashfs.dd-wrt -dest /nfs/dd-wrt root.fs
Reading a different endian SQUASHFS filesystem on root.fs

created 347 files
created 61 directories
created 159 symlinks
created 0 devices
created 0 fifos
senri:/tmp # rm /nfs/dd-wrt/usr/sbin/udhcpc

次に DD-WRT のカーネルを改変して、 root ファイル・システムを NFS マウントできるようにする。 DD-WRT のソースを取得:

senri:/usr/local/src % svn co svn://svn.dd-wrt.com/DD-WRT
A    DD-WRT/tools
A    DD-WRT/tools/addpattern_gs
A    DD-WRT/tools/uemf.h
	...

La Fonera 用のカーネルは、 DD-WRT/src/linux/ar531x/linux-2.6.23 にある。
SVN revision 8745 の場合は Linux 2.6.23.12 ベース。
ビルド用ディレクトリ /usr/local/src/linux-2.6.23.12-dd-wrt を作って、 DD-WRT のデフォルトのカーネル設定ファイル .config_generic をコピーし、 make menuconfig を実行:

senri:/usr/local/src % mkdir /usr/local/src/linux-2.6.23.12-dd-wrt
senri:/usr/local/src % cd DD-WRT/src/linux/ar531x/linux-2.6.23
senri:/usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 % make O=/usr/local/src/linux-2.6.23.12-dd-wrt menuconfig
	...
senri:/usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 % cp .config_generic /usr/local/src/linux-2.6.23.12-dd-wrt/
senri:/usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 % cd /usr/local/src/linux-2.6.23.12-dd-wrt
senri:/usr/local/src/linux-2.6.23.12-dd-wrt % make menuconfig

root ファイル・システムを NFS マウントするために必要な変更は以下の通り:

まず、 カーネルによる IP アドレス自動取得機能を有効にする:

-> Networking
  -> Networking support (NET [=y])
    -> Networking options
      -> TCP/IP networking (INET [=y])
        -> IP: kernel level autoconfiguration (IP_PNP [=y])
          -> IP: DHCP support (IP_PNP_DHCP [=y])

DD-WRT のデフォルト設定では、 ネットワーク・ドライバ AR2313 がモジュールになっているので、 カーネル組み込み (built-in) に変更する:

-> Device Drivers
  -> Network device support (NETDEVICES [=y])
    -> Ethernet (10 or 100Mbit) (NET_ETHERNET [=y])
      -> AR2313 Ethernet support (AR2313 [=y])

そして NFS サポートを有効にする:

-> File systems
  -> Network File Systems
    -> NFS file system support (NFS_FS [=y])
      -> Provide NFSv3 client support (NFS_V3 [=y])
    -> Root file system on NFS (ROOT_NFS [=y])

root ファイル・システムはカーネルがマウントするので mount コマンドは使用しないが、 La Fonera 用の DD-WRT の mount コマンド (busybox に含まれる) は、 NFS をサポートしていないので注意が必要である。 NFS サーバ上の root ディレクトリ /nfs/dd-wrt には、 NFS マウントをサポートした mount コマンドをインストールしておいたほうが便利。

RedBoot の exec コマンドでカーネル・コマンドラインを指定しても、 なぜか無視される (バグ?) ので、 カーネルのデフォルト・コマンドラインを変更しておく:

-> Kernel hacking
  -> Default kernel command string (CMDLINE)

カーネル・コマンドラインとして以下を設定:

console=ttyS0,115200 ip=on root=/dev/nfs nfsroot=/nfs/dd-wrt,nolock

以上まとめると、 カーネル設定ファイル .config の変更箇所は以下の通り:

CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y

CONFIG_AR2313=y

CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y

CONFIG_CMDLINE="console=ttyS0,115200 ip=on root=/dev/nfs nfsroot=/nfs/dd-wrt,nolock"

make して TFTP サーバに置く:

senri:/usr/local/src/linux-2.6.23.12-dd-wrt % make
make -C /usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 O=/usr/local/src/linux-2.6.23.12-dd-wrt
  GEN     /usr/local/src/linux-2.6.23.12-dd-wrt/Makefile
	...
  LD      vmlinux
  SYSMAP  System.map
	...
senri:/usr/local/src/linux-2.6.23.12-dd-wrt % mips-linux-uclibc-objcopy -O binary vmlinux /var/tftp/vmlinux.bin

La Fonera で読み込んでブートさせる... といいたいところだが、 DD-WRT のカーネルは、 flash RAM に「nvram」という名前の区画がないと Kernel panic を起こす:

RedBoot> load -r -b 0x80041000 vmlinux.bin
Using default protocol (TFTP)
Raw file loaded 0x80041000-0x8031cbb3, assumed entry at 0x80041000
RedBoot> exec
Now booting linux kernel:
 Base address 0x80030000 Entry 0x80041000
 Cmdline : 
Linux version 2.6.23.12 (sengoku@senri.gcd.org) (gcc version 4.1.2) #14 Wed Jan 2 13:20:19 JST 2008
	...
Unhandled kernel unaligned access[#1]:
Cpu 0
$ 0   : 00000000 0000006e ffffffed 00000000
	...
Kernel panic - not syncing: Attempted to kill init!

flash-less boot の主旨には反するが (^^;)、 nvram 区画を作って再度ブートを試みる。

もちろん、 flash を一切参照しないファームウェアを作ることも可能ではあるが、 わずか 64k byte の flash 領域を確保するだけで、 既存のファームウェアをほとんどそのまま流用できるのであれば、 費用対効果の高い flash の使い方と言えるのではないだろうか。

nvram 区画の内容は「空っぽ」でも構わないのであるが、 ついでなので他の DD-WRT マシンで設定した nvram (nvram.bin) をコピーすれば、 設定の手間が省ける。

DD-WRT のデフォルト設定では、 ath0 (無線LAN) と eth0 (WAN) がブリッジとして動作する設定になっていて、 ブート途中で一時的に eth0 を停止する (ifconfig eth0 down あるいは ifconfig eth0 0.0.0.0)。 当然、eth0 を止めた瞬間に NFS サーバと通信できなくなり、 「nfs: server not responding, still trying」という ログを出し続けてハングしてしまう。 したがって、 eth0 を止めない設定にしておく必要がある。 例えば、 NFS サーバ上の root ディレクトリから /usr/sbin/udhcpc を削除した上で、 「WAN Connection Type」を DHCP に設定する。

ついで^2 に、 La Fonera+ には、 「image2」という名前の普段は使われていない区画がある (「image」区画が壊れた場合の非常用) ので削除しておく (fis delete image2)。

RedBoot> fis delete image2
Delete image 'image2' - continue (y/n)? y
... Erase from 0xa8660000-0xa87a0000: ....................
... Erase from 0xa87e0000-0xa87f0000: .
... Program from 0x80ff0000-0x81000000 at 0xa87e0000: .
RedBoot> load -r -b 0x80041000 nvram.bin
Using default protocol (TFTP)
Raw file loaded 0x80041000-0x80050fff, assumed entry at 0x80041000
RedBoot> fis create -b 0x80041000 -f 0xA87D0000 -l 0x00010000 nvram
... Erase from 0xa87d0000-0xa87e0000: .
... Program from 0x80041000-0x80051000 at 0xa87d0000: .
... Erase from 0xa87e0000-0xa87f0000: .
... Program from 0x80ff0000-0x81000000 at 0xa87e0000: .
RedBoot> fis list
Name              FLASH addr  Mem addr    Length      Entry point
RedBoot           0xA8000000  0xA8000000  0x00030000  0x00000000
loader            0xA8030000  0x80100000  0x00010000  0x80100000
image             0xA8040000  0x80040400  0x00240230  0x80040400
nvram             0xA87D0000  0xA87D0000  0x00010000  0x80041000
FIS directory     0xA87E0000  0xA87E0000  0x0000F000  0x00000000
RedBoot config    0xA87EF000  0xA87EF000  0x00001000  0x00000000

flash がこの状態でも、 La Fonera+ のオリジナル・ファームウェアは正常に起動する。 また、 ここでは La Fonera+ を使用しているが、 La Fonera でも同様に nvram 区画を作ればよい。

より正確に言うと、 区画の個数が同じ (/dev/mtd9 が board_config) でないと、 オリジナル・ファームウェアでは fonsmcd が異常終了してしまって、 FON ルータが接続されていないと FON にみなされてしまう。 image2 を削除して nvram を追加すれば区画の個数は変わらないので、 fonsmcd を正常に動かすことができる。
ついでに言うと、 区画の個数が変化してしまっても、 /usr/sbin/fonsmcd ファイル中の「/dev/mtd9ro」という部分を変更すれば、 この問題は回避できる。 MTD パーティションの番号をハードコーディングしてしまっているのは、 この部分だけだと思われる。

再度ブート:

RedBoot> load -r -b 0x80041000 vmlinux.bin
Using default protocol (TFTP)
Raw file loaded 0x80041000-0x8031cbb3, assumed entry at 0x80041000
RedBoot> exec
Now booting linux kernel:
 Base address 0x80030000 Entry 0x80041000
 Cmdline : 
Linux version 2.6.23.12 (sengoku@senri.gcd.org) (gcc version 4.1.2) #14 Wed Jan 2 13:20:19 JST 2008
	...
IP-Config: Got DHCP answer from 172.16.255.254, my address is 172.16.191.230
IP-Config: Complete:
      device=eth0, addr=172.16.191.230, mask=255.255.0.0, gw=172.16.255.254,
     host=172.16.191.230, domain=, nis-domain=(none),
     bootserver=172.16.255.254, rootserver=172.16.255.254, rootpath=
Looking up port of RPC 100003/2 on 172.16.255.254
Looking up port of RPC 100005/1 on 172.16.255.254
VFS: Mounted root (nfs filesystem) readonly.
Mounted devfs on /dev
Freeing unused kernel memory: 140k freed
	...
DD-WRT v24 std (c) 2008 NewMedia-NET GmbH
Release: 01/02/08 (SVN revision: 8743)

DD-WRT login: root
Password: XXXXXXXX
==========================================================

 ____  ___    __        ______ _____         ____  _  _
 | _ \| _ \   \ \      / /  _ \_   _| __   _|___ \| || |
 || | || ||____\ \ /\ / /| |_) || |   \ \ / / __) | || |
 ||_| ||_||_____\ V  V / |  _ < | |    \ V / / __/|__   _|
 |___/|___/      \_/\_/  |_| \_\|_|     \_/ |_____|  |_|

                       DD-WRT v24
                   http://www.dd-wrt.com

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


BusyBox v1.4.2 (2008-01-02 03:34:20 CET) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

root@DD-WRT:~# cat /proc/mounts
rootfs / rootfs rw 0 0
/dev/root / nfs ro,vers=2,rsize=4096,wsize=4096,hard,nolock,proto=udp,timeo=10,retrans=2,sec=sys,addr=172.16.255.254 0 0
none /dev devfs rw 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
ramfs /tmp ramfs rw 0 0
devpts /dev/pts devpts rw 0 0
root@DD-WRT:~# 

NFS サーバに置いた DD-WRT の root ディレクトリ /nfs/dd-wrt が、 無事 / にマウントできている。

Filed under: La Fonera — hiroaki_sengoku @ 08:38

No Comments »

No comments yet.

RSS feed for comments on this post.

Leave a comment