Pi3向けFreeBSD 12-CURRENTビルドの手順がWikiから消えちゃったけど、めげずにSMP対応版をビルドしてみる

# 前置き
リアル事情とか体調とかでうまく取り組めずにいるうちにFreeBSDのPi3対応実装は進み、ついに[SMPが動くようになったという記事](http://qiita.com/s_mitu/items/b0c4209e6ca88079ae71)が登場しました。
手元に動いているFreeBSDがあるのだから、ビルド済みのkernelとworldをチャチャっと`scp`してきて`installkernel`と`installworld`をやればお話はおしまいだろうと多寡をくくっているとまた痛い目にあったわけです。

何しろ、`portsnap extract`に失敗する有様でコンパイラがきちんと動くわけもなく、ビルドが終わっていてインストールだけだからビルドシステムのどっかがおかしくてもなんとかなるだろうという甘々妄想は見事打ち砕かれたというわけです。というか「`cc --version`でセグフォすんだけど」とか言われてさすがに狼狽。コンパイラが動くとかうごかないとかいうレベルを超越してしまっている。
こういう事態になっている理由は明らかに、とある当時何故か書かなかった愚行のせいなのですが……この後それに近いことをもう一回やるため、その時このことを説明いたします。

そんなわけでまるでOSの体をなしていないとはいえ構築済みのシステムのことは可愛いので、今回も近い手順を取りまして~~現行FreeBSD12-CURRENT~~原稿書いてから公開までだいぶかかってしまったのでちょっと前のFreeBSD12-CURRENTへのカーネルアップデートを行います。

# 用意したもの
基本[前回](http://qiita.com/slug/items/d235c5d3d7972c90605d#%E7%94%A8%E6%84%8F%E3%81%97%E3%81%9F%E3%82%82%E3%81%AE)のままです。ビルド環境は前回作業時故障中だったi7-6700K/DDR4-2133 16GB*4 ~~など無駄に豪華~~なWindowsデスクトップ上のVirtualBox VMに変更したので、Mac miniの内蔵SDカードスロットの代わりとして

・大昔愛用してた初代eye-fiに付いてきたUSB2.0用SDHCリーダライタ

が追加されています。

# 手順のあらまし
[自分で書いた記事](http://qiita.com/slug/items/d235c5d3d7972c90605d)と、[s_mitu先生のありがたい記事](http://qiita.com/s_mitu/items/b0c4209e6ca88079ae71)と、~~「愚民はそんなことせんでよろしい！」とばかりに手動ビルド手順が消されてしまった~~[FreeBSD WikiのRPI3ページ](https://wiki.freebsd.org/arm64/rpi3)との三つを見比べながら手順を練った結果が以下：

1. 単なるNTPサーバとして動いてたPiに`shutdown -p now`を投げて止める
    2. なお、切れてもメインの赤いLEDは消えてくれないのでLAN端子のLEDが全部消えてからざっくり2秒くらい待って電源を引っこ抜いている。もちろん内部でトラブってたら破綻するので本来はきちんとディスプレイを繋いでやるべき。
    3. ついでに言えば今の所`shutdown -r now`でも同じ感じになって電源抜き差ししない限りきちんとリブートしてくれないのでこの辺はまだ暫定版な感じ。
3. 停止したPiからmicroSDを引っこ抜いて（適切なアダプタで）PCに挿す。
4. VirtualBox VMを起動して、USBアダプタをVMに割り当てる。
    5. 仮にUSBじゃないアダプタを用いた場合があったとしたら、Macでやったvmdk生成をWinでやる必要がある。当然欠片もUNIXじゃない感じのコマンドやパス類に狼狽することになるけど問題なく可能だと聞いている
6. WorldとKernelをビルド
    7. `MAKEOBJDIRPREFIX`をきちんと指定してあれば、このビルドは事前にやっておいてもOK
    8. SMPを試すべく、`kernconf=GENERIC-UP`ではなく`GENERIC`でビルドする。
9. VMに刺さっているmicroSDはすでにパーティションが切ってあるのでそれぞれに入ったり出たりしながらSMP仕様に整備する
    10. EFI領域をマウントして、[「3つのポイント」](http://qiita.com/s_mitu/items/b0c4209e6ca88079ae71#%EF%BC%93%E3%81%A4%E3%81%AE%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88)に従った変更を施す。
    11. ufsをマウントして、`make installworld installkernel`する
12. SDカードを挿し直し、念のためディスプレイケーブルも繋いどいてwatch awesomeness happen.
    13. もう初回起動じゃないのに何がawesomeなのかというと、binutilsがOS側にないからpkg(8)からaarch64-binutilsを入れてシンボリックリンクという泥臭い行為はもはや不要らしく、かつて3.9.0が動かないと言っていたclangはいま4.0.0が完全動作しているらしいのだ！

ある程度前回とった手順をなぞるように手順を並べているので、効率を考えるといくつかの作業は並列にやったり並び替えたりできます。真似する~~……人はいない気がしますが、万一取り組む~~ならばその辺りをお好みで調整してください。

# 新しいシステムをビルド

## ビルド作業を行うマシンについて
今回は前回書いたところの「ちゃんとしたマシン」で、i7-6700K/DDR4-2133 64GB/システムSATA 250GB SSD/VM用NVMe 400GB*2 RAID0/Win10 Pro 1607という~~明らかに無駄な~~ハイスペックPC上にVirtualBox VMを構築……と言いたいところでしたが~~なぜか最近のCURRENTをインストールしようとするとtxzダウンロードの段でcorruptって言われちゃうので諦めて~~スナップショットイメージ（3月1日版VHDK、r314495）を拝借、使用しました。
VirtualBox仮想マシンの設定は前回と同様を基本にプロセッサ数だけ8つにしてあります。

## VirtualBox起動、USBデバイスフィルタ指定
まあ特別なことはありませんね。VMを通常起動してPCにリーダライタを挿し、ウィンドウ下のUSBアイコンからリーダライタの名前を選択してOKです。デスクトップPCの場合はリーダライタを積んでても大概は内部4ピンなので同じ手順で大丈夫でしょう。
ノートPCなどでアイコン押した時のリストにリーダライタらしき名前が出てこなかった場合は……御愁傷様です（笑）。なんとかググってrawvmdkを作りましょう。
なお、デバイスフィルタの設定は中を触り始める寸前でも別に問題はないので、worldとkernelのビルドだけ先にやらせることもできます。

## Worldのビルド

```csh:freebsd-amd64
setenv MAKEOBJDIRPREFIX /usr/local/rpi3-build/obj
make buildworld TARGET=arm64 TARGET_ARCH=aarch64
```

worldの方は前回と一切同一です。並列コンパイル用指定も私の場合は`-j9`を付け足しました。
Gentooのカーネルビルドだったらおやつを食べる程度の時間でやってくれるi7-6700Kですが、FreeBSDではGENERICカーネルなのも大きいかとは思いますが特にWorld側で思った以上に待ちました（けどきちんと測ってないという絶妙な無能具合orz）

### 最適化について
さて、ここでお話しするのが前回私がやらかした「愚かなこと」で、ソース内に[bsd.cpu.mkというファイル](https://github.com/freebsd/freebsd/blob/08e629b875bb571c26c1d6417bf20ac7a5f752fa/share/mk/bsd.cpu.mk)があります。

ここにはCPUTYPEに関するひっじょ〜〜に賢く行き届いた指定の仕組みが書かれているのですが、末尾に`# Add in any architecture-specific CFLAGS.`となんでも書き加えられる場所がありまして、極めて愚かなる私はここに`mcpu=cortex-a53`を書き加えてから両方をビルドしたのであります。CPUの命令セットとシステムの動作可否に密接な関係があるのは`/etc/make.conf`記述における常識であり、私自身~~Intel CPUですら一定以上新しいモデルはカーネルコンパイル時に指定するとシステムが動かなくなってしまうことを知っていたにも拘らず~~興味本位でその行動に走り、挙句記事に書いた時にはこれを隠してすっとぼけるというあまりに迷惑なアウトプットに繋がっておりまして……あの記事にはこの旨も書き足さねばなりませんな。

そして懲りたかというと……反省してここに記述こそしていますが懲りてはいないのであります、当該ソースのコメントにcortex-a53が登場したのを見てベースシステムのビルドに使えると決まったわけじゃないのに大喜びで`CPUTYPE=cortex-a53`を付け加えています。真似しなければ普通のまともなFreeBSD（といってもCURRENTな訳ですが）が出来上がるはずです。

## Kernelのビルド
こちらはSMP対応のことが入ってくるので少し違う指定をします。
なお、出来上がりのシステムに要らなくなったとはいってもこっちは当然`devel/aarch64-binutils`が必要ですから、なければpkg(8)にて調達しておいてください。

```csh:freebsd-amd64
setenv MAKEOBJDIRPREFIX /usr/local/rpi3-build/obj
make buildkernel TARGET=arm64 TARGET_ARCH=aarch64 KERNCONF=GENERIC
```

一つ目のポイントであるところの新しいGENERICカーネルを指定しておきます。GENERICなので、この指定は省略できる可能性もあります。

# SDカードの準備
## USBデバイスを特定
まぁ、VMなので「あっ、こいつが挿したUSBだな」ってのはわかります。確実なのはdmesg、あるいはtty1に流れたり`/var/log/messages`に流れたりした接続ログからそれってものを見つけ出す方法ですが、正直言って`ls /dev`でパーティション構成がそれっぽいのを見つけてEFI領域らしきものをマウント、中にu-bootがあるのを確認するのが一番楽な気がしてます。

## EFI領域をいじる
手元の環境ではUSBリーダライタの中身が`da0`になってました。適宜自分の環境に読み替えてください。

```csh:freebsd-amd64
ls /dev
mount -t msdosfs /dev/da0s1 /mnt
```

### 書き換わったかもしれないEFIを上書きしとく
ついSMP環境向けの準備にばかり気を取られそうになりますが、当然UEFIに読ますブートローダもリビルドされてるわけなので忘れずに上書きしておきます。

```csh:freebsd-amd64
cd /mnt
cp -r /usr/local/rpi3-build/obj/arm64.aarch64/usr/src/sys/boot/efi/boot1/boot1.efi /mnt/EFI/BOOT/bootaa64.efi
```

### 2つのファイルを設置
s_mitu先生記事の記述に従います。

```csh:freebsd-amd64
cd /mnt
fetch https://people.freebsd.org/~gonzo/arm/rpi3-smp/u-boot.bin
fetch https://people.freebsd.org/~gonzo/arm/rpi3-smp/armstub8.bin
```
なお、httpsは`security/ca_root_nss`をインストールしてないと使えないので、なければpkg(8)しときましょう。

### config.txtを編集
device_tree_addressの行編集ですが、これは.txtであって.confではないので行頭に#でコメントアウトができるか不明。思い切って完全に書き換えちゃった方が良いですね。

## rootfsをマウントして新しいシステムをインストール

```csh:freebsd-amd64
umount /mnt
mount ufs /dev/da0s2a /mnt
cd /usr/src
make -s installworld installkernel distribution KERNCONF=GENERIC  DESTDIR=/mnt TARGET=arm64 TARGET_ARCH=aarch64
umount /mnt
```
前回に引き続き、ソースフォルダを`/usr/src`以外としてる人は適宜読み替えてください。

# 起動試験
待望のwatch awesomeness happenタイムですが、私は前述の通り不穏な行為に走っているので~~悪い意味で~~ワクワクしながらの起動試験です。

久々にHDMIケーブルを挿し、LANケーブルは抜いておいて、MicroUSBケーブルを……南無三！！

起動に成功しました。CPUも四つあります！
そしてユーザ情報が消えててrootに空パスで入れる、そのくせrc.confは無事というおっそろしぃ状態でした。mdnsd（何故か最近機能してないのですが）もsshdも生きてる。ルータにファイアウォールがあるとはいえ、LANケーブルを挿さなくてよかった。

サクッと`adduser`とrootの`passwd`を済ませ、LANを接続。鬼門だった`portsnap fetch`に挑戦！

→これも成功！もちろんextractも成功しました。~~一晩かかったけど~~

# ソフトウェアビルドテスト

さて、Piはつつがなく起動し、書庫解凍もつつがなくできたわけですがビルドシステムがしっかり動作することまで確認できないと安心できないのが人情というもの。
一方使い古しSDカードの寿命をマッハで消耗する行為に少したりとも過剰規模のソフトウェアは選べません。
ここで思いついたのは`ftp/axel`、分割ダウンロードするマン。運用はアクセス先の方針と衝突しない程度にどうぞ。なお`/etc/make.conf`は真っ白なのでここでは`CPUTYPE`無しです。

```csh:Pi3-freebsd
pkg install gmake
pkg install gettext-tools
cd /usr/ports/ftp/axel
make config-recursive
make
```

驚くべきことなのか当たり前なのか、これもあっさり成功。

```csh:Pi3-freebsd
make install
make deinstall
make clean
```

使うわけじゃないのでインストールが正常終了したこと（本当は動くことまで確認したほうがいいかもしれませんが……）までで満足しておきます。

しかし、とりあえず比較的安泰という状況を目指すにあたっては、まだ

 - どうにか`/var`をSDカード以外に。できれば`/usr/src`や`/usr/ports`もどっかに。
 - でも本当は[ベータファームウェアでシステム自体をUSBブート](https://www.raspberrypi.org/blog/pi-3-booting-part-i-usb-mass-storage-boot/)させたい。。
 - こういったことがもうしばらくできないとすれば、流石に8GBは手狭じゃない？

……うーん。

さて、最後にdmesg貼って終わりにしたいところですが一言だけ。
手組みしたこの環境では*topコマンドちゃんと動いてます。*
ので、多分現行のddイメージは大丈夫なのかな？無線LAN対応の不足やwatchdogといった細部を除けばだいぶ完全に近いシステムになっていると思います。

# 付録：組み上がったシステムのdmseg
cpufreq0の「電圧データもらえないんですけど」が入ってる辺りがs_mitu先生とこのと違ってます。powerdを走らせてみているのですが、この分ではまだきちんと機能しないのかもしれません。

```dmesg
KDB: debugger backends: ddb
KDB: current backend: ddb
Copyright (c) 1992-2017 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
	The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 12.0-CURRENT #0 r314984: Fri Mar 10 03:38:47 UTC 2017
    ****@******.****:/usr/local/rpi3-build/obj/arm64.aarch64/usr/src/sys/GENERIC arm64
FreeBSD clang version 4.0.0 (branches/release_40 296509) (based on LLVM 4.0.0)
WARNING: WITNESS option enabled, expect reduced performance.
VT: init without driver.
Starting CPU 1 (1)
Starting CPU 2 (2)
Starting CPU 3 (3)
FreeBSD/SMP: Multiprocessor System Detected: 4 CPUs
random: unblocking device.
random: entropy device external interface
kbd0 at kbdmux0
ofwbus0: <Open Firmware Device Tree>
simplebus0: <Flattened device tree simple bus> on ofwbus0
ofw_clkbus0: <OFW clocks bus> on ofwbus0
clk_fixed0: <Fixed clock> on ofw_clkbus0
clk_fixed1: <Fixed clock> on ofw_clkbus0
clk_fixed2: <Fixed clock> on ofw_clkbus0
clk_fixed3: <Fixed clock> on ofw_clkbus0
clk_fixed4: <Fixed clock> on ofw_clkbus0
clk_fixed5: <Fixed factor clock> on ofw_clkbus0
clk_fixed6: <Fixed clock> on ofw_clkbus0
psci0: <ARM Power State Co-ordination Interface Driver> on ofwbus0
local_intc0: <BCM2836 Interrupt Controller> mem 0x40000000-0x400000ff on simplebus0
intc0: <BCM2835 Interrupt Controller> mem 0x7e00b200-0x7e00b3ff irq 16 on simplebus0
generic_timer0: <ARMv7 Generic Timer> irq 37,38,39,40 on simplebus0
Timecounter "ARM MPCore Timecounter" frequency 19200000 Hz quality 1000
Event timer "ARM MPCore Eventtimer" frequency 19200000 Hz quality 1000
bcm_dma0: <BCM2835 DMA Controller> mem 0x7e007000-0x7e007eff irq 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 on simplebus0
mbox0: <BCM2835 VideoCore Mailbox> mem 0x7e00b880-0x7e00b8bf irq 17 on simplebus0
bcmwd0: <BCM2708/2835 Watchdog> mem 0x7e100000-0x7e100027 on simplebus0
gpio0: <BCM2708/2835 GPIO controller> mem 0x7e200000-0x7e2000b3 irq 18,19 on simplebus0
gpiobus0: <OFW GPIO bus> on gpio0
gpioc0: <GPIO controller> on gpio0
uart0: <PrimeCell UART (PL011)> mem 0x7e201000-0x7e201fff irq 20 on simplebus0
uart0: console (115200,n,8,1)
spi0: <BCM2708/2835 SPI controller> mem 0x7e204000-0x7e204fff irq 22 on simplebus0
spibus0: <OFW SPI bus> on spi0
spibus0: <unknown card> at cs 0 mode 0
spibus0: <unknown card> at cs 1 mode 0
sdhci_bcm0: <Broadcom 2708 SDHCI controller> mem 0x7e300000-0x7e3000ff irq 27 on simplebus0
mmc0: <MMC/SD bus> on sdhci_bcm0
iichb0: <BCM2708/2835 BSC controller> mem 0x7e804000-0x7e804fff irq 31 on simplebus0
iicbus0: <OFW I2C bus> on iichb0
bcm283x_dwcotg0: <DWC OTG 2.0 integrated USB controller (bcm283x)> mem 0x7e980000-0x7e98ffff,0x7e006000-0x7e006fff irq 33,34 on simplebus0
usbus0 on bcm283x_dwcotg0
gpioled0: <GPIO LEDs> on simplebus0
gpioled0: <led0> failed to map pin
fb0: <BCM2835 VT framebuffer driver> on simplebus0
fbd0 on fb0
VT: initialize with new VT driver "fb".
fb0: 656x416(656x416@0,0) 24bpp
fb0: fbswap: 1, pitch 1968, base 0x3db33000, screen_size 818688
pmu0: <Performance Monitoring Unit> irq 36 on simplebus0
cpulist0: <Open Firmware CPU Group> on ofwbus0
cpu0: <Open Firmware CPU> on cpulist0
bcm2835_cpufreq0: <CPU Frequency Control> on cpu0
cpu1: <Open Firmware CPU> on cpulist0
cpu2: <Open Firmware CPU> on cpulist0
cpu3: <Open Firmware CPU> on cpulist0
cryptosoft0: <software crypto>
Timecounters tick every 1.000 msec
usbus0: 480Mbps High Speed USB v2.0
ugen0.1: <DWCOTG OTG Root HUB> at usbus0
uhub0: <DWCOTG OTG Root HUB, class 9/0, rev 2.00/1.00, addr 1> on usbus0
mmcsd0: 8GB <SDHC SD08G 3.8 SN C243D48B MFG 07/2009 by 2 TM> at mmc0 41.6MHz/4bit/65535-block
mbox0: mbox response error
bcm2835_cpufreq0: can't get turbo
mbox0: mbox response error
bcm2835_cpufreq0: can't get voltage
mbox0: mbox response error
bcm2835_cpufreq0: can't get voltage
bcm2835_cpufreq0: ARM 600MHz, Core 250MHz, SDRAM 400MHz, Turbo OFF
Release APs
CPU  0: ARM Cortex-A53 r0p4 affinity:  0
 Instruction Set Attributes 0 = <CRC32>
 Instruction Set Attributes 1 = <0>
         Processor Features 0 = <AdvSIMD,Float,EL3 32,EL2 32,EL1 32,EL0 32>
         Processor Features 1 = <0>
      Memory Model Features 0 = <4k Granule,64k Granule,MixedEndian,S/NS Mem,16bit ASID,1TB PA>
      Memory Model Features 1 = <>
             Debug Features 0 = <2 CTX Breakpoints,4 Watchpoints,6 Breakpoints,PMUv3,Debug v8>
             Debug Features 1 = <0>
         Auxiliary Features 0 = <0>
         Auxiliary Features 1 = <0>
CPU  1: ARM Cortex-A53 r0p4 affinity:  1
CPU  2: ARM Cortex-A53 r0p4 affinity:  2
CPU  3: ARM Cortex-A53 r0p4 affinity:  3
WARNING: WITNESS option enabled, expect reduced performance.
Trying to mount root from ufs:/dev/mmcsd0s2a [rw]...
warning: no time-of-day clock registered, system time will not be set accurately
uhub0: 1 port with 1 removable, self powered
ugen0.2: <vendor 0x0424 product 0x9514> at usbus0
uhub1 on uhub0
uhub1: <vendor 0x0424 product 0x9514, class 9/0, rev 2.00/2.00, addr 2> on usbus0
uhub1: MTT enabled
uhub1: 5 ports with 4 removable, self powered
ugen0.3: <vendor 0x0424 product 0xec00> at usbus0
smsc0 on uhub1
smsc0: <vendor 0x0424 product 0xec00, rev 2.00/2.00, addr 3> on usbus0
smsc0: chip 0xec00, rev. 0002
miibus0: <MII bus> on smsc0
smscphy0: <SMC LAN8700 10/100 interface> PHY 1 on miibus0
smscphy0:  10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto
ue0: <USB Ethernet> on smsc0
ue0: Ethernet address: **:**:**:**:**:**
fuse-freebsd: version 0.4.4, FUSE ABI 7.8
smsc0: chip 0xec00, rev. 0002
ue0: link state changed to DOWN
ue0: link state changed to UP
```