NetBSD/evbarmでbig endianな環境を楽しむ

#はじめに

最近のARMプロセッサはbyte orderを選択できるようになっています。NetBSD/evbarmはデフォルトでlittle endianモードで動作しますが、一部のSoCでbig endianモードをサポートしています[^1]。

[^1]: biendianなSoCでも、big endianモードで動作させるためには、機種依存部にサポートコードが必要になります。実際、sys/arch/evbarm以下にはbyte orderを示すマクロ\_\_ARMEB\_\_や\_\_ARMEL\_\_をチェックしている箇所が沢山あります。NetBSDでbig endianモードをサポートしているのはCubietruckのほかに、Allwinner A80 (Cubieboard4, OptimusBoard等)、Broadcom BCM5301X評価ボード、Banana Pi、Cubieboard2、Merrii Hummingbird A31等があるようです(evbearmv7hf-ebの場合)。

普段x86にどっぷり漬かっていると、気付かないうちにlittle endianを前提としたコーディングをしてしまわないか心配です(謎マシン愛好家の皆さんには釈迦に説法でしょうが...)。そこそこ速くて、そこそこ安い、省電力な実機でbig endianな環境を楽しむには、なんといってもevbarmがオススメです。

今回は[Allwinner A20](http://www.allwinnertech.com/en/clq/processora/A20.html)を搭載した評価ボード、[Cubietruck](http://www.zuiki.co.jp/products/cubietruck/index.html)でbig endianな環境を構築してみます。Cubietruckを選択したポイントは

1. SATAをサポートしている
1. メモリが潤沢 (2GB)
1. NetBSDでSMPが安定している (dual core)

という点です。SDカードは耐久性に問題があるので、1.はポイントが高いと思っています。さて、前置きはこれぐらいにして、早速本題に入りましょう。

##必要なもの

Cubietruckでbig endianな環境を構築するには、次のアイテムが必要です。

* Cubietruck本体、ACアダプタ
* NetBSD/evbarmをクロスビルドできる母艦
* 1GB以上のmicro SDカード
* 母艦で使えるカードリーダ
* USBキーボード
* HDMI接続のディスプレイまたはテレビ
* LANケーブル (オンボードのWiFiアダプタには技適マークもドライバもありません)
* (必要に応じて) 2.5インチのSATAハードディスクまたはSSD、USBマウス

リストアップしてみると、いろいろ必要ですね。。。

##手順

次のような手順でbig endianな環境を構築します。

* スナップショットの作成
* u-bootのインストール
* ライブイメージの起動、必要に応じてSATAディスクへのインストール

これから各ステップを詳しく解説します。

#スナップショットの作成

公式サイトで配布されているNetBSD/evbarmのスナップショット(or リリース)はlittle endianモードで動作するバイナリです。big endian環境を楽しむためには、自分でスナップショットをクロスビルドする必要があります。後述のようにNetBSD 7.0には[Xサーバに不具合](http://gnats.netbsd.org/50356)があるので、-currentをオススメします。

どこのご家庭のメインマシンにも-currentのソース一式がミラーされていると思いますが、そうでない場合は適当なPOSIX compatibleな母艦(NetBSDである必要はありません。クロスビルドについては[昨年のadvent calenderの記事](http://qiita.com/furandon_pig/items/68cd2c191d50a3010d55)が参考になります)に、[日本のanoncvsサーバ](http://www.jp.netbsd.org/ja/JP/Documentation/anoncvs.html)から-currentのsrcとxsrcを取得してください。

まず[/etc/mk.conf](http://man.netbsd.org/HEAD/usr/share/man/html5/mk.conf.html)を次のような内容で作成します。

```
.if ${MACHINE} == "evbarm"
CPUFLAGS+=      -mtune=cortex-a7 -mfpu=neon-vfpv4
.endif

BSDSRCDIR=      /XXX/YYY/src
X11SRCDIR=      /XXX/YYY/xsrc

BSDOBJDIR=      /XXX/YYY/obj/${MACHINE}
DESTDIR=        /XXX/YYY/dest/${MACHINE}
RELEASEDIR=     /XXX/YYY/release
TOOLDIR=        /XXX/YYY/tools
```

CPUFLAGSにはCubietruck向けの最適化オプションを指定しておきます。BSDSRCDIRとX11SRCDIRは-currentのsrcとxsrcの場所を指定する変数です。BSDOBJDIRはクロスビルドの作業ディレクトリ、DESTDIRはでき上がったバイナリ(+α)がインストールされる先、RELEASEDIRはスナップショットのディスクイメージやtar ballがインストールされるディレクトリです。ここで指定したディレクトリはあらかじめ作成しておく必要があります(${MACHINE}にはevbarmが代入されます)。

***2016/01/05追記***

最近の-currentではCubietruckのVGAポートが実験的にサポートされています。デフォルトではVGAをコンソールとして使用するようです(自宅にアナログ入力を受け付けるディスプレイがないため、確認できていません。時代!!)。出力先の切り替えはまだサポートされていないようなので、以前のようにHDMI出力を利用したい場合、src/sys/arch/evbarm/conf/CUBIETRUCKから次の2つのデバイスをコメントアウトしてください:

```
# we need awinii2c to talk to VGA monitors
#awiniic*       at awinio? port ?

# TV encoder / VGA output
#awintve0       at awinio0
```

***追記おわり***

これらの準備の下、

```
% cd /XXX/YYY/src
% ./build.sh -U -m evbearmv7hf-eb -x -j 16 tools release
```

を実行します。ここでbuild.shのオプションは

<dl>
  <dt>-U</dt>
    <dd>一般ユーザー権限で実行する。あらかじめ/XXX/YYY以下の各ディレクトリを書き込み可能にしておく必要があります(常套手段としては自分をwsrcグループに追加、もろもろのディレクトリをchgrp -R wsrc && chmod -R g+xしておく等)。root権限を使う場合は不要です。</dd>
  <dt>-m evbearmv7hf-eb</dt>
    <dd>EABI、ARMv7、FPU、big endianの環境をビルドします。</dd>
  <dt>-x</dt>
    <dd>Xサーバ・クライアントを作成します。</dd>
  <dt>-j n</dt>
    <dd>n個のジョブで並列ビルドします。マシンに実装されているコア数x1〜2程度を指定するといいようです。</dd>
  <dt>tools release</dt>
    <dd>tool chainを作成した後、スナップショットを作成します。</dd>
</dl>

を意味しています。詳しくは[src/BUILDING](http://nxr.netbsd.org/source/xref/src/BUILDING)をご覧ください。

build.shを実行して数十分〜数時間程度待つと、/XXX/YYY/release/evbarm/binary/gzimgにライブイメージarmv7.img.gzが生成されます。

#u-bootのインストール

ライブイメージにはブートローダであるu-bootが書き込まれていないため、[本家wiki](http://wiki.netbsd.org/ports/evbarm/allwinner/)でも解説されている通り、別途取得してインストールする必要があります。

まずはディスクイメージをmicro SDカードに書き込みます。次の例ではUSB接続のカードリーダを介して、micro SDカードがsd0として認識されています。

```
umass0 at uhub1 port 1 configuration 1 interface 0
umass0: Generic USB2.0-CRW, rev 2.00/9.59, addr 2
umass0: using SCSI over Bulk-Only
scsibus0 at umass0: 2 targets, 1 lun per target
sd0 at scsibus0 target 0 lun 0: <Generic-, SD/MMC, 1.00> disk removable
sd0: fabricating a geometry
sd0: 7600 MB, 7600 cyl, 64 head, 32 sec, 512 bytes/sect x 15564800 sectors
sd0: fabricating a geometry
```

[ddコマンド](http://man.netbsd.org/HEAD/usr/share/man/html1/dd.html)で書き込みを行います。ここで対象のデバイス名を間違えると、大事なデータを破損する可能性があります。十分注意してください。

```
% cd /XXX/YYY/release/evbarm/binary/gzimg
% progress -zf armv7.img.gz sudo dd if=/dev/stdin of=/dev/rsd0d bs=1m
```

[progressコマンド](http://man.netbsd.org/HEAD/usr/share/man/html1/progress.html)を使うと、進行状況がチェックできるので、イライラが低減します(しない場合もあります)。bsとして少し大きめの値を取って、raw deviceを指定すると書き込みが高速化します(しない場合もあります)。

次にu-bootを書き込みます。[Cubietruck用のu-bootのバイナリ](http://dl.linux-sunxi.org/nightly/u-boot-sunxi/u-boot-sunxi-mainline/u-boot-sunxi-mainline-latest/u-boot-sunxi-mainline-cubietruck.tar.xz)を取得し[^2]、先のディスクに書き込みます。

[^2]: Cubietruck用のu-bootは最近になって、開発が活発になっているようです。リンク先のバイナリ(最新版)が動作しない場合は、[2015年12月11日版](http://dl.linux-sunxi.org/nightly/u-boot-sunxi/u-boot-sunxi-mainline/u-boot-sunxi-mainline-20151211T184208-0305be1/u-boot-sunxi-mainline-cubietruck.tar.xz)を試してみてください。

```
% tar zxf u-boot-sunxi-mainline-cubietruck.tar.xz
% cd u-boot-sunxi-mainline-cubietruck-*
% sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sd0d bs=1k seek=8
```

ここでは先頭の8KBをseekするために、ブロックデバイスを指定する必要があります。次にu-boot用の設定ファイルboot.cmdを、次のような内容で作成します:

```
env set bootargs root=ld0a console=fb
fatload mmc 0:1 82000000 netbsd-CUBIETRUCK.ub
bootm 82000000
```

クロスビルドの過程で生成される[nbmkubootimageコマンド](http://man.netbsd.org/HEAD/usr/share/man/html1/mkubootimage.html)を使って、バイナリ形式に変換します。

```
% /XXX/YYY/tools/bin/nbmkubootimage -A arm -n armv7 -T script boot.cmd boot.scr
```

micro SDのFAT領域をマウントし、できあがったboot.scrをコピーします。

```
% sudo mount /dev/sd0e /mnt
% sudo cp boot.scr /mnt
% sudo umount /mnt
```

なお、この時点でx86などのlittle endianな母艦からはmicro SDカードのFAT partitionしかマウントできません。これはディスクラベルがbig endianで書き込まれているせいなので、心配は無用です[^3]。

[^3]: FFS_EIオプションが有効になっていると、big endianなFFS partitionもmountすることができるのですが、disklabelの読み替えはできないようです。どうなんでしょうか、この仕様。。。

これで起動ディスクが完成しました。このmicro SDはライブイメージとして使用できます。

#ライブイメージの起動、インストール

micro SDカードをCubietruckに挿入し、ディスプレイ、キーボード等を接続して、電源を投入します。u-bootがカーネルをロードして、おなじみの起動メッセージが表示されます。初回起動時には[resize_ffs](http://man.netbsd.org/HEAD/usr/share/man/html8/resize_ffs.html)が自動的にroot partitionをSDカードの容量に合わせて拡大してくれます。resizeに成功すると、再起動ののち、ログインプロンプトが表示されます。

この時点ではパスワードが設定されていないrootアカウントが有効になっており、そのままmultiuser modeで利用することができます。もちろん、Xサーバも動きます。SATA接続のハードディスク or SSDにインストールする場合でも、復旧用の環境として利用することができます。

micro SDカードの寿命を考慮すると、SATAデバイスへのインストールを行うことをオススメします。[sysinstコマンド](http://man.netbsd.org/HEAD/usr/share/man/html8/sysinst.html)を実行すると、おなじみの対話式インストーラが起動します。あとは[x86の場合](http://qiita.com/furandon_pig/items/8b7d13a119067098d561)と同様にインストールを進めることができます。

ただし、ライブイメージにはインストールに必要なtar ballが含まれていません。事前に母艦の/XXX/YYY/release/evbarm/binary/setsから、適当なディレクトリにコピーしておきましょう。SDカードの書き込み回数が気になる方は、/var/shmに全メモリの1/4≒512MBのram diskが用意されているので、ここを使うといいでしょう。

```
# cd /var/shm
# scp -r hoge@piyo:/XXX/YYY/release/evbarm/binary/sets .
```

SATAデバイスから直接ブートすることはできないので、micro SDカードを起動ディスクとして利用することになります。boot.cmdの書き換えて、root partitionとしてSATAデバイス(この例ではwd0a)を指定します:

```
env set bootargs root=wd0a console=fb
fatload mmc 0:1 82000000 netbsd-CUBIETRUCK.ub
bootm 82000000
```

このboot.cmdからboot.scrを生成し、micro SDカードのFAT partitionにコピーします。

```
# mkubootimage -A arm -n armv7 -T script boot.cmd boot.scr
# mount /dev/ld0e /mnt
# cp boot.scr /mnt
# umount /mnt
```

カーネルは引き続きFAT partitionから読み込まれますので、/etc/fstabに

```
/dev/ld0e       /boot           msdos   rw,noauto
```

を追加しておくと便利です。再起動すると、SATAデバイスがroot partitionとしてマウントされます。

#byte orderを確認

さて、実際にbig endianモードで動作しているかどうか、簡単なプログラムで確認してみましょう。

```
#include <stdint.h>
#include <stdio.h>

int
main(void)
{
        uint16_t i;
        uint8_t *p;

        i = 0x1234;
        p = (uint8_t *)&i;
        printf("original data is 0x%04x\n", i);
        printf("stored in memory as 0x%02x 0x%02x\n", *p, *(p + 1));

        return 0;
}
```

解説するまでもないですが、これは2バイトの値0x1234をメモリ上に書き込んだ後、それを2つの1バイトの値として読み出すプログラムです。x86で実行すると次のような結果が得られます。

```
% uname -ps
NetBSD x86_64
% ./a.out
original data is 0x1234
stored in memory as 0x34 0x12
```

下位バイトが先に収納されていること(little endian)が確認できます。同じプログラムをCubietruckで実行すると、

```
% uname -ps
NetBSD earmv7hfeb
% ./a.out
original data is 0x1234
stored in memory as 0x12 0x34
```

じゃがじゃん!! 上位バイトが先に収納されています。これでbig endianモードで動作していることが確認できました!! なんという地味な結末でしょうか。。。

#おまけ

当初、タイトルを「NetBSD/evbarmでbig endianな環境(のトラブル)を楽しむ」としていましたが、特にトラブルもなくインストールが終了してしまいました。これでは残念(?)なので、以前の-currentに存在した可愛い(??)バグを紹介して、この記事を終わりたいと思います。インストールも終わり、何気なくXサーバを起動すると。。。

<blockquote class="twitter-tweet" lang="ja"><p lang="en" dir="ltr">What&#39;s a miserable xeyes! X server with wsfb on NetBSD/armeb is broken. <a href="https://t.co/wveqOIRbdk">pic.twitter.com/wveqOIRbdk</a></p>&mdash; ぼんくら物理学者 (@LabDrunker) <a href="https://twitter.com/LabDrunker/status/657169238948360192">2015, 10月 22</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

なんとも気の毒な[xeyes](http://man.netbsd.org/HEAD/usr/X11R7/man/html1/xeyes.html)が表示されてしまいます。これはARM環境をlittle endianと決め打ちしていた[Xサーバのバグ](http://gnats.netbsd.org/50356)が原因でした。現在の-currentではこのバグは修正されていますので、元気なxeyesと戯れることができます。

<blockquote class="twitter-tweet" lang="ja"><p lang="en" dir="ltr">Now, it gets sanity. I&#39;ll send PR. <a href="https://t.co/25cPw8zP1B">pic.twitter.com/25cPw8zP1B</a></p>&mdash; ぼんくら物理学者 (@LabDrunker) <a href="https://twitter.com/LabDrunker/status/657169452610400256">2015, 10月 22</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

おしまい。