「NetBSDはそれなりにわかっているけど Raspberry Pi を動かすの初めて」という視点での無駄に長い覚え書き
とりあえず起動
えびじゅんさんが OSC毎にリリースしている Raspberry Pi 用 NetBSD/evbarm SDイメージ を使うのがとりあえず簡単。
えびじゅんさんのイメージでサクッとラズパイ起動した。簡単すぎる件。 pic.twitter.com/goaPLS6NxZ
— Izumi Tsutsui (@tsutsuii) 2018年4月30日
手順
えびじゅんさんの説明ページを読めという話ではあるが、とりあえず抜粋。
- イメージをダウンロード
http://cdn.netbsd.org/pub/NetBSD/misc/jun/raspberry-pi/
OSC毎に更新されるので適宜 port-arm の ML を探してください - Windows上の rawrite32 もしくは NetBSD上で gunzip(1) + dd(1) 等を使って SDに書き込み
- ラズパイに SDを挿して、 USBキーボードと HDMIなディスプレーと Ethernet を接続して電源ON
- 初回起動時はイメージサイズを実SDサイズに合わせる
/etc/rc.d/resize_root
が走るのでしばらく待つ login:
のプロンプトが出たら root でログイン
Raspberry Pi のブートストラップ
「FATパーティションの中にいろいろと入っている」という情報だけはなんとなく聞いていたけれど、詳細は把握していなかったので適当に調べた結果をメモ。
Raspberry Pi ブート解説記事
以下の記事がわかりやすい。
ブートパーティション
前項の The Pi Boot Process の記事に "should be formatted in fat32 (at least for the first partition)" とあるとおり、 FDISKパーティションの1番目のパーティション、かつ、FAT32フォーマットである必要があるらしい。
5/12追記: FAT16で問題ない ようです。というか、そもそも FAT32の場合に起動するのかどうか……
パーティションIDに制約があるのかどうかの記載はないが、えびじゅんさんの SDイメージでは以下の通り sysid 6 (Primary 'big' DOS, 16-bit FAT (> 32MB) ) になっていて、必ずしも sysid 0xb (32bit FAT) や sysid 0xc (32-bit FAT, LBA-mapped) 等である必要はないようである。
# vndconfig vnd0 2018-04-14-netbsd-raspi-earmv6hf.img
# fdisk vnd0
Disk: /dev/rvnd0
NetBSD disklabel disk geometry:
cylinders: 2300, heads: 64, sectors/track: 32 (2048 sectors/cylinder)
total sectors: 4710400, bytes/sector: 512
BIOS disk geometry:
cylinders: 294, heads: 255, sectors/track: 63 (16065 sectors/cylinder)
total sectors: 4710400
Partitions aligned to 2048 sector boundaries, offset 63
Partition table:
0: Primary 'big' DOS, 16-bit FAT (> 32MB) (sysid 6)
start 8192, size 188416 (92 MB, Cyls 0/130/3-12/60/48)
1: NetBSD (sysid 169)
start 458752, size 4251648 (2076 MB, Cyls 28/141/50-293/53/16)
PBR is not bootable: All bytes are identical (0x00)
2: <UNUSED
>
3: <UNUSED>
Bootselector disabled.
No active partition.
Drive serial number: 0 (0x00000000)
ブート時の動作
上述の記事にあるとおり、FATのパーティション上の以下のファイルを順番に読んでいく。
bootcode.bin
loader.bin
start.elf
kernel.img
もしくはkernel7.img
初代 Raspberry Pi の CPU は ARMv6 で、 NetBSD の MACHINE_ARCH
的には earmv6hf のバイナリが必要。Raspberry Pi 2 や 3 は ARMv7 で earmv7hf のバイナリでも動く。とのこと。
初代RPiはARMv6でないと駄目ですが、2や3はARMv7でいけます
— Kimihiro Nonaka (@nonakap) 2018年4月30日
Raspberry Pi のファームウェアというか start.elf
的には、初代RPIなら kernel.img
を、RPI2 および RPI3 では kernel7.img
を読み込む、ということらしい。
NetBSD的には、RPI用あるいは RPI2,3用のディストリビューションというものはなく、 NetBSD/evbarm 用バイナリとして evbarm-earmv6hf と evbarm-earmv7hf の2種類のセットが用意されている。
RPI2,3 では evbarm-earmv7hf のバイナリを使う、ということも可能だが、えびじゅんさんのイメージおよび NetBSDのリリースバイナリに含まれる RPI用イメージ (くわしくは後述) では、 RPI と RPI2,3 とでイメージを共用できるように evbarmv6hf のバイナリを使う、というスタンスのようである。
その他のブート用ファイル
ブート用の FATパーティションにはほかに以下のファイルがある。
config.txt
cmdline.txt
それぞれ start.elf
によって読み込まれる。
config.txt
のほうは start.elf
が解釈して各種デバイスの初期化その他の設定をするらしい。[要出典]
cmdline.txt
については start.elf
が読み込むが、そのままカーネルに渡されて、カーネルが中身を解釈するらしい。[要出典]
詳細は以下の記事を参照。
- www.raspberrypi.org の CONFIG.TXT の説明
- https://raw.githubusercontent.com/Evilpaul/RPi-config/master/config.txt
- www.raspberrypi.org の The Kernel Command Line の説明
- elinux.org の RPi cmdline.txt の説明
config.txt
の中身
えびじゅんさんのイメージの中に入っている config.txt
の中身は以下
# UART settings, see https://www.raspberrypi.org/documentation/configuration/uart.md
enable_uart=1
force_turbo=0
#
#bcm2708_fb.fbswap=0
# http://mail-index.netbsd.org/port-arm/2014/07/24/msg002527.html
# http://elinux.org/RPiconfig
#for 1024x768
#hdmi_group=2
#hdmi_mode=16
#
# for omxplayer
#gpu_mem=16,64,128,256
UART については後で記載するが、とりあえず上記の通り https://www.raspberrypi.org/documentation/configuration/uart.md を見ればよい?
HDMIについてもデフォルトで映らない場合だけ対処すればいいと思われる。必要にかられていないので詳細は見ていない
cmdline.txt
の中身
cmdline.txt は Linux と NetBSD とでそれぞれカーネルが解釈するので、 Linux用の説明はあまり役に立たず、 NetBSDとしての説明を読む必要があると思われる。これもまだちゃんと調べていない
えびじゅんさんのイメージの中に入っている cmdline.txt
の中身は以下
root=ld0a console=fb
#fb=1280x1024 # to select a mode, otherwise try EDID
#fb=disable # to disable fb completely
NetBSD 8.0_RC1 のカーネルでは、 src/sys/arch/evbarm/rpi/rpi_machdep.c
で cmdline.txt
の内容を MIなブートパラメータの boot_args
に入れているようである。具体的には 278行目の struct vb
のメンバー .vbt_cmdline
に入れられてファームから渡される (と思われる) 値を 746行目あたりで boot_args
に設定している。
-current では FDT対応の変更で rpi_machdep.c
自体がなくなっている。 struct vb
の構造体の定義は src/sys/arch/arm/broadcom/bcm283x_platform.c
にあるが、 cmdline
の中身をコピーしているところはぱっと見た感じでは見当たらなかった。特に、 root device の指定の記述がどう処置されているのか謎だが、すべて FDT の .dts
のファイルに記載するのかも? 8.0_RC1 で動かしているのでこのへんの詳細は未確認
kernel.img
kernel.img
および kernel7.img
は ELF形式ではなく、 objcopy -O binary
で作成される生バイナリ (≒実メモリ上にロードされた状態と同じレイアウトのバイナリ) であるっぽい。NetBSD/evbarm のビルドの枠組みとしては、 src/sys/arch/evbarm/conf/mk.rpi
の SYSTEM_LD_TAIL_EXTRA
の定義により、カーネルビルド時には必ず netbsd.bin
の生バイナリカーネルも生成される。
なお、-current では FDTの対応が追加されていて、 mk.rpi
から呼び出される src/sys/arch/evbarm/compile/rpi-mkknlimg.sh
により末尾に FDT用の manic number が追加されるようである。
シリアルコンソール設定
このへんはググればいろいろ出てくるので結果だけ。
ラズベリーパイ3のシリアルコンソール https://t.co/47fUnvBpdk
— Izumi Tsutsui (@tsutsuii) 2018年4月30日
要は GPIO 用のピンヘッダのうち一部が UART と兼用のピンになっていて、規定のピンに 3.3Vレベルで TX と RX とが出ているので、そこに 3.3V対応の USBシリアル変換 (秋月の FTDI のものとか) をつなげればいいはず。実際にはまだ試してない
Amazonでは GPIOピンヘッダに 1ピンずつ挿せる形態のUSBシリアル変換が売っているようであるが、チップとして uplcom(4)
のものが多くて Windowsで使う場合にハマる (ドライバに偽造品チェックが入っていて使えないことがある) 可能性があるので、多少高くても FTDI のを選んだほうがよいという気はする。
UART については前述の 本家 raspberrypi.org のドキュメント も参照。自分はちゃんと読んでない
Raspberry Pi用 NetBSD起動イメージ
えびじゅんさんはどうやってイメージを作っているのだろう、という話もあるが、実は NetBSD 8.0_RC1 の配布バイナリ中にも evbarm-earmv6hf/binary/gzimg
のディレクトリの中に Raspberry Pi 用の起動イメージ (rpi.img.gz
と rpi_inst.img.gz
) が用意されている。
evbarm-earmhv7hf はどうか、と見てみると、evbarm-earmv7hf/binary/gzimg
のディレクトリの中には armv7.img.gz
というものだけがあり RPI用のイメージはないという状態だったりする。
起動イメージのビルド方法
ところで、 https://ftp.netbsd.org/pub/NetBSD/NetBSD-8.0_RC1/images/
の下には amd64用の NetBSD-8.0_RC1-amd64-install.img.gz
と i386用の NetBSD-8.0_RC1-i386-install.img.gz
というインストール用イメージがある。これらのイメージは build.sh
スクリプトのターゲットとして install-image
を指定すると作られる。また、インストール用の ISOイメージは build.sh
のターゲット iso-image
で作成される。
一方、試してみるとわかるが、 evbarm-earmv6hf の rpi.img.gz
および evbarm-earmv7hf の armv7.img
は build.sh release
のビルドでデフォルトで作成されるようになっている。
このあたり、リリースバイナリ生成のしくみについては機種固有の記載も多いわりに整理されたドキュメントがなく、それなりに詳しい人でも経験値と野生の勘で各種の Makefile
を読み解くという作業が必要だったりする。今回は Raspberry Pi 用の rpi.img.gz
についてざっと調べたので、リリースバイナリビルド関連の前提知識と合わせてメモっておく。
build.sh build
と build.sh release
build.sh
のターゲットには build
と release
があり、 src/BUILDING
の説明には以下のように書かれている。
build Build the entire NetBSD system (except the kernel). This
orders portions of the source tree such that prerequisites
will be built in the proper order.
release Do a "make distribution", build kernels, distribution
media, and install sets (this as per "make sets"), and then
package the system into a standard release layout as
described by release(7). This requires that RELEASEDIR be
set (see above).
"build" の説明の "the entire NetBSD system (except the kernel)" って結局何やねん、という話もあるが、文字通り「カーネル以外のバイナリ一式」と考えて差し支えない。
具体的には、「カーネル以外のバイナリ」に含まれない「リリース用のバイナリ」については主に src/distrib
以下に関連するコードがあるが、 build.sh build
の場合はこれらのビルド関連のコマンドは実行されない。また、 src/etc
以下にある設定ファイルや /dev/MAKEDEV
等のファイルも生成されない。詳細は src/Makefile
のコメントを参照。
"release" については、 src/BUILDING
の説明にあるとおりで build.sh build
の処理に加えて以下を実施する。
- リリースバイナリに含まれるカーネルのビルド
- インストール用のカーネル埋め込み用 RAMDISKや miniroot の作成
- インストールメディア (CD ISOを除く、フロッピーやテープ等) の作成
- base.tgz, comp.tgz 等のインストール用バイナリの tar ball の作成
- ftpサイトにあるのと同様のリリース様式のディレクトリツリー作成
src/Makefile
の release ターゲットおよびそこで指定されている distribution
のターゲットを読み解くとわかるが、 build.sh release
時の具体的な呼び出しとしては以下が行われる。
src/etc
での make distribution
では以下が行われる。
make install-etc-files
による/etc
以下の設定ファイルおよび/dev/MAKEDEV
等の生成とDESTDIR
へのインストール- 同様に X関連の
/etc
以下の設定ファイルの生成とインストール (xetc.tgz
に相当) src/distrib
以下でバイナリセット (base.tgz, comp.tgz
等) に含まれるべきファイルがDESTDIR
にそろっているかのチェック
src/etc
での make release
では以下が行われる。
snap_post
ターゲットの依存にあるbuild_kernelsets
およびbuild_releasekernels
によるカーネルビルドおよびリリースバイナリ用のカーネルセット作成
作成するカーネルは MD のsrc/etc/etc.${MACHINE}/Makefile.inc
で指定されるsnap_post
ターゲットによるsrc/distrib
以下のインストール用バイナリ (RAMDISKや miniroot、インストールカーネルへの RAMDISK埋め込み、インストールフロッピーイメージ 等) の生成snap_md_post
ターゲットによる機種依存のリリースバイナリビルド処理
NetBSD/evbarm での起動イメージ生成
書き始めると止まらなくて Raspberry Pi と関係ない話になりつつあるが、ここでのポイントは前項の最後に書いた src/etc/Makefile
での snap_md_post
のターゲットである。 evbarm の場合は src/etc/etc.evbarm/Makefile.inc
に雑多なものが詰め込まれている。
細かいところは省略するが、イメージ作成は ${MKIMAGE}
で指定されている src/distrib/utils/embedded
以下のスクリプトで指定されている。適当に src/etc/etc.evbarm/Makefile.inc
の記述を読み解くと、イメージは以下のパターンで作成されるようになっている。
MACHINE_ARCH
が armv7 の場合は armv7 用のイメージを作成- 作成されるカーネルに
RPI
が含まれる場合 (= 定義上はMACHINE_ARCH
が earmv6 or earmv6hf の場合) は rpi 用のイメージを作成
src/etc/etc.evbarm/Makefile.inc
のターゲットとして smp_rpi
といった記述があるが、ここでの "smp" は snap_md_post
の略と思われる。そういうわかりづらい名前はやめようよ……
src/distrib/utils/embedded
でのイメージ生成
build.sh
での iso-image
および live-image
によるイメージ作成の場合は src/distrib
以下でのイメージ作成 → ${RELEASEDIR}
へのコピーという順序でイメージが用意される。よって、 src/distrib
以下のイメージ作成対象のディレクトリの Makefile
(たとえば src/distrib/i386/liveimage/usbimage/Makefile
等) を見れば何をどうしているかを (一応) 追うことが可能。
一方、 src/distrib/utils/embedded
の場合は前述の通り「src/etc/etc.evbarm/Makefile.inc
から直接呼ばれるスクリプトが置いてあるだけの場所」という扱いで、当該ディレクトリには Makefile
がない。src/distrib/utils/embedded/mkimage
のスクリプトにはマニュアルもなく簡単な usage があるだけなので、実際の呼び出し箇所がわからないと読み解くのが困難な実装になっている。
なんでこんな構造にしたのかという気はしないでもないが、おそらく、スクリプトが先にできていて、それをとりあえずで適当(?)にビルドに組み込んだ、という順番?
mkimage
スクリプトの処理
src/distrib/utils/embedded/mkimage
は一見ややこしいが、一応は機種共通部の実装なので、読み解いてしまえば大して難しいことはしていない。やっている内容は以下の通り。
- 展開するバイナリセット (=イメージに含めるファイル) の選択と設定
- 機種固有設定の呼び出し
- (指定されている場合)
makefs(8)
による boot用の FATパーティションの作成 makefs(8)
による NetBSD パーティションの作成fdisk(8)
による MBRパーティション情報の書き込みdisklabel(8)
による disklabel のパーティション情報書き込みgzip(1)
による圧縮
build.sh
の live-image
等の処理と比較すると、 NetBSD 5.x 以降で実装された makefs(8)
の拡張機能を使っているため、以下のようにだいぶシンプルになっている。live-image
側の記述も直そうと思いつつサボっている
makefs(8)
のイメージ作成元のファイルを含むディレクトリを複数指定できるようになった
→標準のファイルは${DESTDIR}
のインストール先ディレクトリをそのまま参照し、標準と異なるファイル、新たに追加するファイルは別のディレクトリに置く、という指定が可能になったmakefs(8)
でターゲットのイメージファイルのどの位置にファイルシステムを作成するかのオフセットを指定できるようになった
→従来はイメージの先頭 (オフセット0) から指定サイズのファイルシステムを作成することしかできなかったので、FDISKパーティション部、disklabel部、各パーティションのファイルシステムをそれぞれ個別に作って最後にcat(1)
で結合していた
mkimage
のスクリプトでややわかりづらかったのは以下の 3点。
- 機種固有設定を指定する引数について、 usage 中に
-h <host-arch>
と書いてあるだけで、何を指定するものなのかの記載がない
ちゃんと読み解けば後で${h}
の変数に入れられてconf/${h}.conf
で機種固有設定ファイルを読んでるとわかるんですが、この変数名も検索性が最低 - 上述の「標準と異なるファイル、新たに追加するファイルを置くための別のディレクトリ」が
${mnt}
という直感的でない変数名で、実態は/tmp
以下にmktemp(1)
で作られるディレクトリになっている - 一見機種固有のファイルである FATパーティションについて、暗黙で
${mnt}/boot
以下のディレクトリに対象ファイルが入っているものとして作成される/boot
に当該 FATパーティションを mount しているのは Raspbian 由来と思うが、それをそこに書くか、みたいな
mkimage
スクリプトの機種固有の処理
mkimage
スクリプトの機種固有処理は src/distrib/utils/embedded/conf
以下の各ファイルに記述されている。現状のファイルを見ると evbarm 用以外に x86 や usermode といったファイルがあるが、これらを実際に呼び出す実装が現状存在するかは確認していない。
長々とイメージ作成について書いてきたが、結局 Raspberry Pi 固有設定としてはここが本題で、要は src/distrib/utils/embedded/conf/rpi.conf
を見ればすべてがわかるという話だったりする。なお、FDT対応の絡みで rpi.conf
は 8.0_RC1 と -current とで設定が異なるのでそれぞれ記載する。
8.0_RC1 の rpi.conf
https://nxr.netbsd.org/xref/src/distrib/utils/embedded/conf/rpi.conf?r=1.29
- /boot の FATパーティションに置く起動用カーネルは
src/sys/arch/evbarm/compile/RPI/netbsd-RPI{,2}.bin
RPI2 用カーネルが無いと思ったら別の行で定義されてる罠 - 各種 Raspberry Pi 用の起動ファイルは
src/external/broadcom/rpi-firmware/dist
からコピー - コピーされる起動ファイルは以下
LICENCE.broadcom bootcode.bin fixup.dat fixup_cd.dat start.elf start_cd.elf
fixup.data
とかstart_cd.elf
ってなんだ - evbarm 共通の設定は
evbarm.conf
にあるdisklabel(5)
,fstab(5)
,rc.conf(5)
, イメージリサイズ設定 等々 rc.local(8)
の中に Raspberry Pi の CPUクロックを最大クロックにする設定を入れている
起動時にメッセージが出ているのを見て気づいたけれど、普通わからんcmdline.txt
の作成
rpi.conf
の中に埋め込まれていてcat(1)
で生成
-current (2018/5/4現在) の rpi.conf
https://nxr.netbsd.org/xref/src/distrib/utils/embedded/conf/rpi.conf?r=1.34
8.0_RC1 との差分のみ記載。
- インストールするカーネルについて、FDT対応データが付加された
netbsd-RPI{,2}.img
に変更
なんかカーネルが 2回コピーされているような…… config.txt
の作成cmdline.txt
と同様にrpi.conf
の中に埋め込まれていてcat(1)
で生成- FDT対応の
.dtb
ファイルのコピー.dtb
ファイルはカーネルビルドと同時に作成されるらしい
まとめ
こんなにとりとめもなく書いてたらまとまんねーよ!
……という感じですが、とりあえず朝になってしまったのでこのへんで。
何かあったらまた書きます。ドキュメント重要。
おまけ
貼る機会を失った雑多なリンク: