RaspbianをCygwin上のNFSサーバのnfsrootとWindows上のqemuで起動するメモ

追記: nfs.enable_ino64=0 が必要なので追記、あとinit=/bin/shでrootパスワードをセットする必要があるのも。
そろそろteslawireのエンジン側をARM11に載せる算段を考えないといけないので、Raspbianの開発環境をWindows上で揃えることにした。
というわけで先ずはリモート起動環境を作る。

Raspbianイメージの展開

RaspbianはSDカードイメージの形で配布されているため、単純にloopback mountできない。
というわけで先ずはpartdに掛けてファイルシステムの先頭アドレスを出さないといけない。ext4をマウントする必要があるので、ここだけはLinuxで作業しないといけない

# partd 2016-09-23-raspbian-jessie-lite.img
(parted) unit b
(parted) print
モデル:  (file)
ディスク /home/oku/2016-09-23-raspbian-jessie-lite.img: 1389363200B
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos
ディスクフラグ:

番号  開始       終了         サイズ       タイプ   ファイルシステム  フラグ
 1    4194304B   70254591B    66060288B    primary  fat16             lba
 2    70254592B  1389363199B  1319108608B  primary  ext4

"unit b"でバイト単位表示にしてから"print"でパーティションテーブルを表示することで、ファイルシステムまでのオフセットがわかる。このオフセットを指定して、

# mount ./2016-09-23-raspbian-jessie-lite.img /mnt -o loop,offset=70254592

のように、loopオプションにoffsetを渡してマウントする。マウントしたら

# tar cf raspb.tar /mnt

として.tarを作り、これをCygwin側に持ち込んで展開する。

イメージの編集

Raspbianのイメージはそのままではnfsrootでもqemuでも起動しないため、展開したファイルを修正する必要がある。

  • /etc/fstab
proc            /proc           proc    defaults          0       0
#/dev/mmcblk0p1  /boot           vfat    defaults          0       2
#/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1

fstabからはmmcblkのエントリをコメントアウトする。nfsrootだとこれらはマウントする必要が無い。

  • /etc/ld.so.preload
# /usr/lib/arm-linux-gnueabihf/libarmmem.so

ld.so.preloadからは謎のlibarmmem.soのpreloadをコメントアウトする。これをやらないとinitが上がらずカーネルパニックする。

TAP-Win32のインストールと設定

パケットキャプチャ等の都合も有るので、qemuのネットワークにはTAP-Win32を使うのがおすすめ。
OpenVPNのサイトから https://openvpn.net/index.php/download/community-downloads.html Tap-windowsのNDIS 6版をダウンロードしてインストールする。

インストールしたらTAPインターフェースの名前をQemuTapのような指定しやすい名前に変更しておく。また、IPv4アドレスを割り当てる必要がある。
Windowsの自動ネットワーク構成はデフォルトルートの無いインターフェースは"識別できないネットワーク"に分類してしまうため、グループポリシか何かで識別できないネットワークをプライベートネットワークにしておく( http://www.atmarkit.co.jp/ait/articles/1012/24/news127.html )。ただし、この設定はクソ危険なので移動するシステムにはオススメできない。

NFSサーバの起動

Cygwinのunfs3パッケージをインストールし、

/usr/sbin/unfsd -e /home/oku/netboot/exports -l 192.168.150.1 -p -s -d

のようにして起動する。オプションの意味はman unfsd参照。重要なのは -p オプションで、これを指定することでportmapへの登録を省略できる。
NFSはいくつかの歴史的事情でちゃんとしたSun RPC環境を要求するが、portmapサーバを別に上げるのも面倒なので。Linuxのnfsrootではポート番号を明示的に指定することでportmapサーバを省略できる
exportsは普通のNFSと同様のフォーマットで書くことができる。

/home/oku/netboot/raspb 192.168.150.0/24(rw)

qemuの起動

qemuには内蔵のLinuxカーネルローダが有るため、単にカーネルイメージを持ってくればカーネルまでは起動する。

ファイル kernel-qemu-4.4.13-jessie とQemuのバイナリをダウンロードして適当な所に配置し、

/cygdrive/c/prog/Qemu-windows-2.6.0/qemu-system-arm.exe -kernel kernel-qemu-4.4.13-jessie -cpu arm1176 \
 -m 256 -M versatileab -serial stdio \
 -append "root=/dev/nfs rw nfsroot=192.168.150.1:/home/oku/netboot/raspb,vers=3,port=2049,mountport=2049,tcp ip=192.168.150.200 console=ttyAMA0 nfs.enable_ino64=0" \
 -net nic -net tap,id=tap,ifname=QemuTap

のようにして起動する。ifname=QemuTapは先程インストールしたTAP-Win32インターフェースの名前にする。nfsrootのオプションにはportとmountportの両方を含める必要がある。2049はNFSのデフォルトポートで、unfs3はNFSとMOUNTの両方に同じポート番号を指定できる。
ここでは、NFSサーバ、つまりWindows側のIPv4アドレスが192.168.150.1で、Qemu内のLinux側が192.168.150.200という指定になっている。このように全てを手動で設定すれば、portmapやdhcpサーバは無くても正常に動作する。
ブートパラメタは普通のnfsrootのもので十分だが、nfs.enable_ino64=0を付けてinode番号を32bitに抑えないとかなりミステリアスな起動失敗を起こす。あと、setuidビットをエミュレートできないので、sshやsudo等動作にsetuidビットが立っている必要のあるバイナリが動作しない。init=/bin/shで一旦シェルを起動し、passwdでrootパスワードを設定してから起動することになる。