この記事は、NetBSD Advent Calendar 2019の14日目の記事です。
NetBSDの UEFI ブートローダー
build.sh を
build.sh -m amd64 -R RELEASEDIR release install-image
と実行すると、RELEASEDIR/images/NetBSD-VERSION-amd64-uefi-install.img.gz ができます。
このファイルをddとかでUSBメモリに書き込むと UEFI から起動できるインストールメディアになります。
インストーラーイメージは、GPTでパーティションが切られていて、次のように構成されています。
start size index contents
0 1 PMBR
1 1 Pri GPT header
2 32 Pri GPT table
34 2014 Unused
2048 262144 1 GPT part - EFI System
264192 2908160 2 GPT part - NetBSD FFSv1/FFSv2
3172352 2015 Unused
3174367 32 Sec GPT table
3174399 1 Sec GPT header
UEFIブートローダーのソースを読んでいた時1、GPTパーティションを hd0a, hd0b, ... のように指定できることに気付きました。
上の構成では、 EFI パーティションが hd0a、NetBSDが hd0b になります。 ということは、 もう一つNetBSDのパーティションを作ってブートローダーのプロンプトから
boot hd0c:netbsd
と起動してやれば、追加のパーティションから NetBSDが起動できそうです。一個のUSBメモリで -current、9.0_BETA、8.1 を選んでインストールできるようになるわけです。
マルチバージョン化の手順
- 元のインストールイメージの先頭からEFIパーティションまでをコピーして出力イメージとする。
- 出力イメージにNetBSDパーティションを入れる部分を確保して、さらに1MiBytesの領域を追加。これはGPTのセカンダリテーブルに使われる。
- GPTテーブルを新規作成2。元イメージの構成にあわせてEFIパーティションを追加。
- 各バージョンについて、
- NetBSDのGPTパーティションを作成。このパーティションのGUIDを後で使用する。
- 元のインストールイメージからNetBSDパーティションの部分を取り出す。
- ファイルシステムイメージをマウントして、
- /etc/fstab の ルートデバイスのNAME= 指定を新しいGUIDに書き換える。
- /boot.cfg からメニューを取得。最初のNetBSDパーティションでは、/boot.cfg を書き換えてメニューを増やす。
- 更新したファイルシステムイメージを出力イメージの中に書き込む
以上を実行するシェルスクリプトを github に置きました。
作成例
-current, 9.0_BETA, 8.1_STABLE の三つのイメージからマルチバージョン化したインストーラーイメージのGPTパーティションは、次のようになります。
start size index contents
0 1 PMBR
1 1 Pri GPT header
2 32 Pri GPT table
34 2014 Unused
2048 262144 1 GPT part - EFI System
264192 2908160 2 GPT part - NetBSD FFSv1/FFSv2 # -current
3172352 2908160 3 GPT part - NetBSD FFSv1/FFSv2 # 9.0_BETA
6080512 2560000 4 GPT part - NetBSD FFSv1/FFSv2 # 8.1_STABLE
8640512 2015 Unused
8642527 32 Sec GPT table
8642559 1 Sec GPT header
ブートローダーのメニュー
banner=Welcome to the NetBSD/amd64 (multiple versions) installation image
banner================================================================================
banner=
banner=If you encounter a problem while booting, report a bug at
banner=https://www.NetBSD.org/.
menu=Install NetBSD/amd64 9.99.17:boot netbsd
menu=Install NetBSD/amd64 8.1_STABLE:boot hd0d:netbsd
menu=Install NetBSD/amd64 8.1_STABLE (no ACPI):boot hd0d:netbsd -2
menu=Install NetBSD/amd64 8.1_STABLE (no ACPI, no SMP):boot hd0d:netbsd -12
menu=Install NetBSD/amd64 9.0_BETA:boot hd0c:netbsd
menu=Drop to boot prompt:prompt
timeout=30
clear=1
おまけ
gpt(8)コマンドの不思議仕様
この記事を書いていて気付いたのですが、TOOLDIR/bin/nbgpt3 の引数の順番が gpt(8) と異なっています。
/sbin/gpt command args device
TOOLDIR/bin/nbgpt device command args
gpt(8)のソースを見ると、argv[0]が "gpt" の時とそうでない時で、わざわざ動作を変えています。
src/sbin/gpt/Makefile を見ると、
#LINKS= ${BINDIR}/gpt ${BINDIR}/gptlabel
#MLINKS= gpt.8 gptlabel.8
となっていて、コメントアウトされていますが、gptlabel という名前にして引数の順番を変更しようとしたのかな、と推測します。
gptコマンドの引数の順番は、atactl, dkctl などと統一がとれてなくて気持悪い、とは私も思うので意図は理解できますが、gpt と nbgpt で動作が違うのはダメじゃないかな。
Linux の mount -o loop
この作業では、ファイルシステムイメージの中身をいぢる手順があります。 (/etc/fstab と /boot.cfg を変更)
NetBSDでこれをやるには、
vndconfig -c vnd0 image_file
mount /dev/vnd0d /mnt
edit /mnt/foo
umount /mnt
vndconfig -u vnd0
と実行します。Linuxでは、同等のことを
mount -o loop image_file /mnt
edit /mnt/foo
umount /mnt
と、より簡単にできます。NetBSDの方法だと、vnd0 がすでに使われている場合に失敗するので、空いているvndを探すという手間も発生します。
ちょっと頑張れば、mount -o loop は実装できそうな気がするんですが……