Raspberry Pi 3 Model BにFreeBSD 12-CURRENTを入れてみた

[10/21 05:04 仮公開。まだまだ不完全ですがとりあえず]
[11/04 13:00 ちょっとだけ画像追加。本文はそろそろいじるところなさそうです]
[予告：FreeBSD Wikiにpkgは11用を指定すればいいよって案内が出たのできっと次の記事はそれです]
[17/03/08 01:17 予告以降思ったように作業できていません。とりあえずアレな画像を一枚差し替え]
[03/09 00:00 FreeBSD wikiからビルド手順が消えてるのを確認したので本文中のリンクのみ版固定リンクに書き換えときました。この記事としてはたぶんこれで最終版]

# 前置き
従前、PenMの化石ノートにFreeBSD 10.3-RELEASEを入れておうちハックの準備を進めていたすらっぐです。
どうやらノートPCサーバは火事を引き起こすようなので、最近流行のRaspberry Piを買ってみました。

ところがある程度でもFreeBSD側の対応が進んでいるのはPi2まで。手元にあるのはPi 3 Model B。やってしまった。
で、調べてみれば全く動かないわけではないし、イメージを作っている人もいないではない感じでした。
と言いつつ夏以降の新しいイメージは引っかかって来ませんし、FreeBSD Wikiには誇らしげにInitial RPI3 supportのリビジョンナンバーが貼られていたわけです。

んじゃま、手元でビルドしてみますか……というのがこの記事です。

# 用意したもの
- Elements14版の本体。ケースに入った状態で、ヒートシンクとかゴム足とかがついて来て割と親切。
- Pi対応を保証してくれるところ（~~どこだっけ……後で追記します~~11/04きばん本舗さんでした！）の2.5A対応USBスイッチング電源と1mくらいのケーブル。
    - そこが言うにはそこそこの頻度で何かしら動かないのが混じるそうなので、信用するなら保証は必要。
- 東芝製microSD 8GB
    - 発掘品。昔使っていたT-01Aについて来たものではなかろうか。

以上、まぁごく一般的なRasPiですね。
LANケーブルとかの意識的に用意しなくとも済むやつは略してあります。

# 手順のあらまし
さて、ここで[くだんのページ](https://wiki.freebsd.org/action/recall/arm64/rpi3?action=recall&rev=19)を見て手順を確認します。

1. クロスコンパイル親のFreeBSDを準備
    2. githubからファームウェアを有難くいただく。
    3. u-bootをダウンロード（って書いてあるけど試しに[sysutils/u-boot-rpi3を見に行ったら完成してた](https://www.freshports.org/sysutils/u-boot-rpi3/)のでこいつを）、ビルド。
    4. 対応する[aarch64向けbinutils](http://www.freshports.org/devel/aarch64-binutils)をpkg install。
5. WorldとKernelをビルド
    6. MAKEOBJDIRPREFIX環境変数にお好みのパスを指定
    7. make buildworldとmake buildkernelを実行。
8. SDカードを突っ込んでパーティションを整備
    9. まずgpartで切る
    10. EFI用のFAT16をマウントしてu-bootとfirmwareとEFIをコピる
    11. メインのufsをマウントしてmake installworld installkernelする
    12. fstabにufsを記述
13. >Plop in the sdcard to the RPI3, attach the serial cable, and watch **awesomeness** happen.
    14. と言いつつ、実はHDMIケーブルさえあればちゃんとディスプレイに出力してくれる。イケメンさんである

と、まあ言われてみると当たり前の手順。特別なのはブートローダを外から持ってくることとファームウェアを用意する必要がある程度でしょうか？
そうは言っても私自身がカーネルのクロスコンパイルなんざ全くの未経験なのでやはり苦労するのであります。

# 親になるFreeBSDをVirtualBox上に準備
さすがにPenMの旧ノートPCにカーネルビルドをやらすわけにはいかないし、10.3や11.0のRELEASEビルドに12-CURRENTのビルド作業をさせるのもなんとなく不安だ。
よろしい、ならばVMだ。

## ビルド作業を行うマシンについて
本来はi7-6700Kを積んだちゃんとしたマシンがあるんですが、運悪く現在は故障中。
仕方がないのでサブというか付けっ放し担当のMac mini(Mid 2011)梅さんに腰を上げてもらいます。CPUはi5 2.3GHz。確かモデルナンバーは2415Mとかだったと記憶（また確認して書きます→11:10追記：正解でした→11/04追記：画像追加）。<img width="698" alt="sierra.png" src="https://qiita-image-store.s3.amazonaws.com/0/130200/f47c27b2-220d-e469-4b66-2d1d6dcc13e7.png">

Sandisk Extreme Pro SSDを積んだりRAMはDDR3-1600 16GBだったり通常の範囲でできる改造はことごとくやり尽くしてある子です。
VMホストにはVirtualBoxを愛用しており、この時のバージョンは5.1.6 r110634。Vagrantは使っていません。

## VirtualBoxに仮想マシンを準備
いつも通りISOイメージをダウンロードして、いつも通り仮想マシンを構築します。仮想と言ってもエミュレータにするわけではないので、普通にamd64版でいいです。
そしていつも通りお怒りのVirtualBoxさんをガン無視してCPUには論理コア4つ全部指定。![screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/130200/5e296efa-68e6-f2d3-b8a0-fecbaa94c052.png)

注意することといえば、できる限り準仮想化を使うように設定しておくことでしょうかね。nicにvirtio-netはやり忘れがちです。（11/04追記：他のOSと違って準仮想化インタフェースが自動じゃ動きません。しかも正解はまさかのHyper-Vだそうで）
後々SDカードのIOを流し込みますが、それはまたその時に。

# FreeBSD上で一式ビルド
一通りのセットアップが済むと、ようやくWikiの記述を参照します。

## Prerequisitesの準備
この単語、意味はわかりますが読み方はよくわからんとです。

```csh:freebsd-amd64
fetch https://github.com/raspberrypi/firmware/archive/master.zip
unzip master.zip
mv -r firmware-master arm64
pkg_replace -N sysutils/u-boot-rpi3
pkg install aarch64-binutils
```
はい、私はどっかにいるのかいないのかわからないpkg_replace派の一人です。
まあ、投稿直後に読まれた方以外はu-boot-rpi3もpkg(8)で行けると思います。

unzipしたファームウェアはfirmware-masterというディレクトリに展開されていたので名前を変更してあります。まぁこのあたりは各自の都合に合わせてください。

## Worldのビルド

```csh:freebsd-amd64
setenv MAKEOBJDIRPREFIX /usr/local/rpi3-build/obj
make buildworld TARGET=arm64 TARGET_ARCH=aarch64
```
Wikiにはpath-to-suitable-objdirって書いてあるのですが、要はどこでもいいということかと思います。常識不足につきどこに置くと良いのか調べようとしましたが、結局わかりませんでした。
例には書いてませんが-jで並列処理は普通にやらせてます。

## Kernelのビルド

```csh:freebsd-amd64
setenv MAKEOBJDIRPREFIX /usr/local/rpi3-build/obj
make buildkernel TARGET=arm64 TARGET_ARCH=aarch64 KERNCONF=GENERIC-UP
```
これ、当初はWikiに`KERNCONF=RPI3`って書いてあったのですが私の作業の真っ最中にGENERIC-UPになったらしく、散々悩みました。
普段RELEASEビルドでまったりしてきた人間が「ああ、これこそCURRENTブランチ！」って実感した瞬間でした。

# SDカードの準備
## VirtualBox VMにSDカードを接続
まあ普通にパーティションを切るんですが、何分VMなもので物理マシンにカード挿せばすぐ編集できるわけではありません。Mac miniの内臓カードスロットにアダプタ噛ませたMicroSDを入れたら以下：

```bash:Terminal.app
diskutil list
sudo diskutil unMountDisk /dev/disk5
VBoxManage internalcommands createrawvmdk -filename ./sd-pi3.vmdk -rawdisk /dev/disk5
sudo chmod 777 ./sd-pi3.vmdk
sudo chmod 777 /dev/disk5
```

chmodはググって発見した参考サイト様からの受け売りで、~~もしかしたら必要ないかもしれませぬ~~（11/04追記：ごめんなさいこれめっちゃ必要ですやらなきゃ確実に動きませんやってもたまにダメですorz）。またもちろんdisk番号は単なる例ですので、`diskutil list`の結果を反映させてください。
こうして出来上がったvmdkファイルをVMの設定画面から読み込ませて起動せしめると、しれっと/devに登場します。私の環境ではada1でした。

## gpartでパーティションを切る

```csh:freebsd-amd64
gpart create -s MBR ada1
gpart add -t \!12 -a 63 -s 50m ada1
gpart set -a active -i 1 ada1
newfs_msdos -F 16 /dev/ada1s1
gpart add -t freebsd -a 4m ada1
gpart create -s BSD ada1s2
gpart add -t freebsd-ufs ada1s2
newfs -U ada1s2a
```

Wikiでの記述は全部bashになってるので、私のようにめんどくさがってcshでごまかそうとしている場合は少しずつコマンドに違いが出ます。
exportがsetenvになるのは簡単でしたが、`gpart add '!12'`がエスケープに失敗するのは解決に苦労しました。というかそもそもこの!12とやらが~~単にラベルに書く文字列~~だというのがもう驚きでしたが、私の無知故でしょうか。
（11/04追記：`man gpart(8)`によると、-tはPartition Typeであって!と英数ハイフンで指定するのはscheme-specificという一般的な方法のようです。では`!12`は何かと言うと……manページに載ってないのです。`!11`が`fat32`なので、UEFI用であろうことを考えてもごくそれに近いものと考えて良さそうですが、`efi`という指定と同値になる指定は`!c12a7328-f81f-11d2-ba4b-00a0c93ec93b`という文字入りのめっちゃ長い何かになる謎。。。）

## EFIパーティションを準備

```csh:freebsd-amd64
mount -t msdosfs /dev/ada1s1 /mnt
cp -r ${HOME}/arm64/firmware/boot/* /mnt
cp /usr/local/share/u-boot/* /mnt
```

ここの記述は色々うろ覚えなのでまたパス確認しますorz（11:23追記：手元の状況に合わせてパス修正しました）
ともかく、ファームウェアのコピー元は自分で展開したパスをきちんと指定、ports/pkgを使ったu-boot-rpi3は/usr/localからtabで辿って指定します。

続き、EFIをコピーします。このあたりはもう全くwiki通りでOKです。

```csh:freebsd-amd64
mkdir -p /mnt/EFI/BOOT
cp $MAKEOBJDIRPREFIX/arm64.aarch64/<svn-checkout-path**>/sys/boot/efi/boot1/boot1.efi /mnt/EFI/BOOT/bootaa64.efi
```

今回の私のsvn-checkout-pathは/usr/srcなのですが、さすがにそれを平然と入れると各自編集すべき場所の見分けがつかなくなると思ってここではWikiそのままの記述にしてあります。
大文字ディレクトリを間違えないようにしましょう。
というかPi 3ってUEFIなんすか。すごい。調べてみると脱MicroSDもさして難しくなさそうで嬉しい。

次の作業に入る前に`umount /mnt`してマウントポイントを空けておきます。

## rootfsを準備
ここももう正直書く意味を感じないくらいWiki通りです。

```csh:freebsd-amd64
mount /dev/ada1s2a /mnt
make -s installworld installkernel distribution KERNCONF=GENERIC-UP DESTDIR=/mnt TARGET=arm64 TARGET_ARCH=aarch64
vi /mnt/etc/fstab
```
ビルドで散々迷走してからここまで進んだ私は、このmakeコマンドを打った後でkernelが無い事に気がついてビルドをやり直させています。後々残念な事になった理由はこの辺りにありうるので、カーネルビルドとその配置はやり直してみる予定です。

fstabに書くのは以下：

```fstab:freebsd-amd64/mnt/etc/fstab
/dev/mmcsd0s2a  /  ufs  rw  1  1
```
tabが置けずに各要素間をスペース2つでごまかしてますが実際には全部タブで区切りました。まぁスペースでも問題ないかとは思いますが。

とにかくWiki先生が言うにはこれで完成らしいので、VirtualBox VMさんをpoweroffしてSDカードを取り出します。
この時、vmdkが割り当てられたままだと後でうっかりSDカードがないまま起動させる未来が見えたので取り外しておきました。disk番号が変わりうることを考えると、vmdk自体も削除した方が良いですね。

```bash:Terminal.app
diskutil eject /dev/disk5
```
ejectというサブコマンドを勘で打ちましたが、平然と取り出せました。

# 起動してみる
死ぬほどグダついて何が何だか分からなくなりかかってる状態で取り出したSDカードだったにもかかわらず、平然と起動してくれました。

手元に手頃なUSBキーボードがなく、絶賛BIOS死亡中のメイン機からSideWinder X4を引っこ抜いて挿してみたところきっちり名前を出してくれてちょっとシュールでした（机が汚すぎるので写真撮ってないですすみません）。

さて、とりあえずFreeBSDだとttyv0にはコンソールログが割り込んでくるので`Ctrl`+`Alt`+`F2`してttyv1に切り替えて……wtf、できないじゃないか。

仕方ない、rootでログイン（パスは設定されていない）してpasswd、それからadduserでいつも通りユーザーを追加してsuしてpkg……あれ？

>Bootstrapping pkg from ?pkg+http://pkg.FreeBSD.org/FreeBSD:12:aarch64/latest, please wait...
pkg: Error fetching http://pkg.FreeBSD.org/FreeBSD:12:aarch64/latest/Latest/pkg.txz: Not Found


ああ、あるわけがない。何しろ、つい今CURRENTに乗ったばかりのプロファイルでカーネルをビルドしているのだから……
SDカード寿命のことを考えてだいぶ逡巡したが、観念して

```csh:raspi3-freebsd
portsnap fetch
```

をさせたところ、まさかの

>Verifying snapshot integrity... Segmentation Fault (core dumped)
snapshot corrupt.

すごく絶望の予感がしてますが、とりあえず時刻合わせがまだだったことを思い出したので

```csh:raspi3-freebsd
ntpdate ntp.jst.mfeed.ad.jp
```
してもう一度試してみたものの、やっぱりセグフォ。
TCPで送られてくるデータが壊れてた試しがほぼないので検証とかそれほど……と思いつつググると「なら、直接落としてtarすればいいよ！」とのこと。

```csh:raspi3-freebsd
fetch http://ftp.iij.ad.jp/pub/FreeBSD/ports/ports/ports.tar.gz
tar xvf ./ports.tar.gz -C /usr/
```
当然、portsnap.confのignore設定など反映されるはずがありません。
しかし、久々にverboseオプション使ったらパスの左に出てくるxがなんなのか分からない。疑問が素朴すぎてGoogle先生に尋ねる方法もわからない。

というか、落ち着いて考えてみるとintegrity checkでセグフォしたってことはツールのコンパイル時もガンガンセグフォするのでは？このgzipの展開もうまく行くのだろうか？？うーむ。

ずいぶん時間がかかって展開は無事終了。

```csh:raspi3-freebsd
cd /usr/ports/ports-mgmt/pkg
make install clean
```

> configure: error: in `/usr/ports/ports-mgmt/pkg/work/pkg-1.9.1':
configure: error: C compiler cannot create executables

Oh...
やはり、buildworldを数度やり直してたりbuildworldとinstallworldの間にMAKEOBJDIRPREFIXを移動したりしてるうちに壊れてしまったのでしょうか。

Wikiページには、clang/llvm 3.9.0環境下について"Compiling ports directly on the RPI3 is NOT currently possible"とあるのでそれを素直に読む限り3.8.0ならば大丈夫そうなのですが。

とりあえず、しばらくの間は最新版のビルド、インストール、起動、ports/pkg試行を繰り返すことになりそうです。
ただ、今はこれ以上Mac miniを占領して欲しくないのでメイン機が復活次第再試行してみます。

# 参考URL
一部URL紛失中です。今後追記予定（11:30追記：VirtualBox情報の参照先を追加）
http://qiita.com/s_mitu/items/12aa53d534073489cabc
https://wiki.freebsd.org/arm64/rpi3
http://blog.sato0123.com/2015/03/01/SD_for_Ubuntu/