tsutsuiの作業記録置き場

NetBSDとかPC-6001とかの作業記録のうち、Twitterの140字では収まらない内容や記事としてまとめるべき内容をとりあえず置いてみる予定

NetBSD + RaspberryPi を動かしてみたメモ

NetBSDはそれなりにわかっているけど Raspberry Pi を動かすの初めて」という視点での無駄に長い覚え書き

とりあえず起動

えびじゅんさんOSC毎にリリースしている Raspberry Pi 用 NetBSD/evbarm SDイメージ を使うのがとりあえず簡単。

手順

えびじゅんさんの説明ページを読めという話ではあるが、とりあえず抜粋。

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 で、 NetBSDMACHINE_ARCH 的には earmv6hf のバイナリが必要。Raspberry Pi 2 や 3 は ARMv7 で earmv7hf のバイナリでも動く。とのこと。

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 が読み込むが、そのままカーネルに渡されて、カーネルが中身を解釈するらしい。[要出典]

詳細は以下の記事を参照。

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 は LinuxNetBSD とでそれぞれカーネルが解釈するので、 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.ccmdline.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.rpiSYSTEM_LD_TAIL_EXTRA の定義により、カーネルビルド時には必ず netbsd.bin の生バイナリカーネルも生成される。

なお、-current では FDTの対応が追加されていて、 mk.rpi から呼び出される src/sys/arch/evbarm/compile/rpi-mkknlimg.sh により末尾に FDT用の manic number が追加されるようである。

シリアルコンソール設定

このへんはググればいろいろ出てくるので結果だけ。

要は GPIO 用のピンヘッダのうち一部が UART と兼用のピンになっていて、規定のピンに 3.3Vレベルで TX と RX とが出ているので、そこに 3.3V対応の USBシリアル変換 (秋月の FTDI のものとか) をつなげればいいはず。実際にはまだ試してない

Amazonでは GPIOピンヘッダに 1ピンずつ挿せる形態のUSBシリアル変換が売っているようであるが、チップとして uplcom(4) のものが多くて Windowsで使う場合にハマる (ドライバに偽造品チェックが入っていて使えないことがある) 可能性があるので、多少高くても FTDI のを選んだほうがよいという気はする。

UART については前述の 本家 raspberrypi.org のドキュメント も参照。自分はちゃんと読んでない

Raspberry PiNetBSD起動イメージ

えびじゅんさんはどうやってイメージを作っているのだろう、という話もあるが、実は NetBSD 8.0_RC1 の配布バイナリ中にも evbarm-earmv6hf/binary/gzimgディレクトリの中に Raspberry Pi 用の起動イメージ (rpi.img.gzrpi_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.gzi386用の 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.imgbuild.sh release のビルドでデフォルトで作成されるようになっている。

このあたり、リリースバイナリ生成のしくみについては機種固有の記載も多いわりに整理されたドキュメントがなく、それなりに詳しい人でも経験値と野生の勘で各種の Makefile を読み解くという作業が必要だったりする。今回は Raspberry Pi 用の rpi.img.gz についてざっと調べたので、リリースバイナリビルド関連の前提知識と合わせてメモっておく。

build.sh buildbuild.sh release

build.sh のターゲットには buildrelease があり、 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 直下で make build ("build.sh build" 相当)
  • src/etcmake distribution を実行
  • src/etcmake 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 は一見ややこしいが、一応は機種共通部の実装なので、読み解いてしまえば大して難しいことはしていない。やっている内容は以下の通り。

build.shlive-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 ファイルはカーネルビルドと同時に作成されるらしい

まとめ

こんなにとりとめもなく書いてたらまとまんねーよ!
……という感じですが、とりあえず朝になってしまったのでこのへんで。

何かあったらまた書きます。ドキュメント重要。

おまけ

貼る機会を失った雑多なリンク: