ＢＩＯＳブートとＵＥＦＩブートでＧＰＴパーティションの違いと作り方

昨今のＦｒｅｅＢＳＤにおける「パーティショニング」は、ディスクサイズによらず、ＧＰＴ（GUID Partition Table）で構成するの一択と言っても過言ではない。しかし、いわゆる「ＢＩＯＳからのブート」と「ＵＥＦＩからのブート」では、パーティション構成に若干の違いが発生してしまう[^備考１]。ここでは両者の違いを示し、ブートが可能なパーティション構成例を示す。

[^備考１]: ＢＩＯＳではいわゆる「ＭＢＲ（Master Boot Record）ブート」となるが、２ＴｉＢ制限のキツさと、マルチブートの要求がほぼ無くなっていることから、ＧＰＴブート（？）に一本化されている

## 前提条件
ここではＳＡＴＡディスクでの操作を前提とし、デバイス名は (/dev/)ada0 と表現する。ＳＡＳ／ＵＳＢディスクを対象とする場合は daｎ を、ＮＶＭｅでは nvdｎ、ｅＭＭＣでは mmcsdｎ と読み替えること（ｎは０以上の整数）。

## 共通事項
ＧＰＴパーティションなので、いずれの手順においても、実行しなければいけないコマンドがある[^備考２]。

[^備考２]: このコマンドの実行に過不足は無い。むしろデフォルト値に手を入れる余地は（互換性的な観点から）全く無い。

```
gpart create -s GPT ada0
```
なお話をシンプルにするために、以下のようなパーティションを切るものとする。

| デバイス名 | パーティションタイプ             | 用途               |
|:----------:|:--------------------------------:|:------------------:|
| ada0p1     | freebsd-boot ないしは efi        | ブート領域         |
| ada0p2     | freebsd-ufs[^備考３]             | ＦｒｅｅＢＳＤ領域 |
| ada0p3     | freebsd-swap                     | スワップ領域       |

[^備考３]: freebsd-ufs は freebsd-zfs にしたい場合もあるだろうが、今回の話ではこの程度で省略しておく。

今回の話で言うところの ada0p2 と ada0p3 は同じく共通手順となる。つまり「今この瞬間では実用性の無い」下記のコマンドの実行である[^注意１][^注意２][^注意３]。

[^注意１]: 実際には `-s サイズ` オプションを使用してサイズを指定する必要がある。指定が無い場合は残り全てを意味する。つまりこのまま実行するとスワップ領域が確保できない。

[^注意２]: 実行順序に注意。このまま進めるとブート領域が割り当てられない（パーティション番号が一つずれる）。

[^注意３]: `-i パーティション番号` ないしは `-b 開始位置 -i パーティション番号` オプションを使用して制御可能ではあるが、エッセンシャルな説明としては却下したいところ。

```
gpart add -t freebsd-ufs  ada0
gpart add -t freebsd-swap ada0
```

## ＢＩＯＳブートの場合
```
gpart add -t freebsd-boot -b 40 -s 984  ada0
```
* ブートパーティションとして「freebsd-boot」パーティションを定義する。「freebsd-boot」でない場合、ブートローダーはこの領域を発見できない。
* 開始位置の指定（`-b`）を４０セクタ（ＬＢＡ４０）から開始するものとする。
    * ＧＰＴパーティションのヘッダーサイズ（ＧＰＴヘッダーサイズ）は３４セクタ[^備考４]であるが、４ＫｉＢセクタ対応ディスクの場合、セクタ開始位置が８の倍数に丸められるられてしまう。
    * それを見越してか最近、ＧＰＴヘッダーサイズが４０セクタになってたりする（いつのバージョンからかは未確認）。
* 領域サイズの指定（`-s`）は９８４セクタを**個人的**に推奨する。
    * 次の領域（freebsd-ufs）の開始位置が５１２ＫｉＢ境界から始まるというメリットがある[^備考５]。
    * ブートローダーの制限により５４５ＫｉＢ未満のサイズに抑えなければならない（今回のサイズは４９２ＫｉＢ）。
    * よって小さくする意味も無ければ、これ以上大きくする余地も無い。

[^備考４]: ＬＢＡ０～ＬＢＡ３３までがＧＰＴヘッダー領域

[^備考５]: 昨今の様々メディア（４ＫｉＢセクタＨＤＤはもちろんのことＮＡＮＤフラッシュなど）における**境界**の最小公倍数（５１２ＫｉＢもあればどのメディアでも境界に該当する）としては、そう悪くない数字である（はず）。

```
gpart bootcode -b /boot/pmbr -p /boot/gptboot    -i 1 ada0
        または
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
```
以上でブートローダーのインストールが完了する。ブートローダーはＦｒｅｅＢＳＤ領域[^備考６][^注意４]を探して /boot/loader または /boot/zfsloader に処理を引き渡す。

[^備考６]: gptboot は freebsd-ufs 領域の /boot/loader を、gptzfsboot は freebsd-zfs 領域の /boot/zfsloader を探す。

[^注意４]: あくまでも「/boot/(zfs)loader」が収容されている領域のファイルシステムに紐付くため、ブート時はＵＦＳ、ルートはＺＦＳと言ったハイブリッド環境の場合「ＵＦＳ」として解釈する。

## ＵＥＦＩブートの場合
```
gpart add -t efi -b 40 -s 409560 ada0
```
* ブートパーティションとして「efi」パーティションを定義する。「efi」でない場合、ＵＥＦＩはこの領域を発見できない。
* 開始位置の指定（`-b`）を４０セクタ（ＬＢＡ４０）から開始するものとする。
    * ＧＰＴパーティションのヘッダーサイズ（ＧＰＴヘッダーサイズ）は３４セクタ[^備考４]であるが、４ＫｉＢセクタ対応ディスクの場合、セクタ開始位置が８の倍数に丸められるられてしまう。
    * それを見越してか最近、ＧＰＴヘッダーサイズが４０セクタになってたりする（いつのバージョンからかは未確認）。
* 領域サイズの指定（`-s`）は推奨に合わせて４０９５６０セクタを指定する。
    * この指定の場合、次の領域（freebsd-ufs）の開始位置が２００ＭｉＢ境界から始まる[^備考５]。
    * 領域サイズは、厳密には、２００ＭｉＢより若干小さい（４０セクタ分＝２０ＫＢ）が、まぁ誤差である。
    * なお 11.2-R 現在のブート領域（/boot/boot1.efifat）のサイズは８００ＫｉＢである。
    * [10.4-R](https://www.freebsd.org/releases/10.4R/relnotes.html#userland-installer) ないしは [11.1-R](https://www.freebsd.org/releases/11.1R/relnotes.html#userland-installer) ではこの領域を２００ＭｉＢ確保することが推奨されているが、他のＯＳ（ｍａｃＯＳなど）との互換性を考慮しての設定なので、単体でＦｒｅｅＢＳＤを起動するならこの８００ＫｉＢでも**現時点では**問題無い。
    * [bsdinstall: increase EFI partition size to 200MB](https://reviews.freebsd.org/D6935)の議論では、ファームウェアアップデートツールのようなＥＦＩアプリケーションのための領域も必要とあるので流石に８００ＫｉＢは少なすぎるという認識である。

```
newfs_msdos -F 32 -c 1 -L EFISYS /dev/ada0p1
mount -t msdosfs /dev/ada0p1 /mnt
mkdir -p /mnt/EFI/BOOT
cp /boot/loader.efi /mnt/EFI/BOOT/BOOTX64.efi
umount /mnt
        または
gpart bootcode -p /boot/boot1.efifat -i 1 ada0
```
以上でブートローダーのインストールが完了する。ブートローダーはＦｒｅｅＢＳＤ領域[^備考７]を探してブート処理を進める。

[^備考７]: freebsd-ufs または freebsd-zfs 領域を探すが、その優先順位と制御はここでは説明しにくいので、[参考文献](https://qiita.com/mzaki/items/76acac14c16ac6789e68#loaderefi)を参考にしてもらうこととする。いや難しくないんだけどね。ファイルシステムレベルの調整が必須なのでもちっと踏み込む必要がある。

# よくある質問とその答え

## Ｑ．[Bootable UEFI memory stick or Hard Disk](https://wiki.freebsd.org/UEFI#Bootable_UEFI_memory_stick_or_Hard_Disk)に書いてある手順と違うぢゃないかー。
Ａ．大丈夫だ。問題無い。時代が我に追いついていた。

## Ｑ．色々調べてみると /boot/boot1.efifat の、領域への書き込み方法に色々な手順があるようだが、何が正しいんだ？
Ａ．大丈夫だ。どれも問題無い。が、どの手順もお勧めしない。

/boot/boot1.efifat はパーティションイメージそのものである。極端に言えば `dd if=/dev/ada0p1 of=/boot/boot1.efifat` したものと思えばいい。実際ビルド時に似たようなことをしている。

よって、dd(8) で書き込むのも gpart(8) で書き込むのも同じである。が、どちらがよりスマートであるかは言うまでも無く gpart(8) である。

ただできれば /boot/boot1.efifat は使用しない方がよい。先の２００ＭｉＢ拡張の話になるが、これを使用すると２００ＭｉＢの領域の内８００ＫｉＢにしかアクセスできない領域になるからである。

せっかく２００ＭｉＢの領域を確保したにもかかわらずＥＦＩアプリケ－ションが全く動かせない環境となる。

また新規インストールでは問題無いが、更新したい場合、必要以上に消えてしまう手順でもある。

## Ｑ．ＯＳアップデート時にブートパーティションの更新は必要ですか？
Ａ．常にでは無いが必要。ただし…

1. ＵＦＳから起動する場合、ＵＦＳ自体のフォーマットの安定性から、ブートローダーのコードに変更が無いため、ほぼしくじることが無い。問題があったとしたら、おそらくＵＦＳ１→ＵＦＳ２の時くらいと思われる。よって更新されてなかったとしても問題が起きることが無い、ないしは発覚しない。
2. ＺＦＳから起動する場合、ＺＦＳ自体のフォーマットの不安定性から、ブートローダーのコードに変更が発生するため、こまめにメンテしてやらないと、起動しない可能性がある。この辺りは `/usr/src/UPDATING` の `ZFS notes` に「ブートするＺＦＳプールのバージョンを新しくするときはブートローダーを更新しろ」という記述がある。

この事から更新手順とは別にブートローダーの更新も手順化しておくべきである。

freebsd-boot パーティションに対しては `gpart bootcode -p` で上書きしてやればよいが、efi パーティションに対してはマウントの上、`cp` で上書きすることになる（[後述](#付録ブートローダーの更新)）。

## Ｑ．パーティション番号が１でなくても良いならなんでもありぢゃないかー。
Ａ．大丈夫じゃない、やめとけ。

ＭＢＲには２ＴｉＢ制限があるので、後の領域を使うと、ＢＩＯＳがブートローダー（freebsd-boot）を見つけてくれない可能性がある。
またパーティション番号と領域の順番が不一致の場合、管理が面倒になること請け合いである。

## Ｑ．我々はＧＰＴを扱ってるのであってＭＢＲは関係ないはず。
Ａ．残念だ。ＧＰＴはＭＢＲの上位互換なので諦めろ。

ＵＥＦＩブートに関して言えば、ＧＰＴはＧＰＴとして解釈されるので「関係ない」と言い切ってかまわない。

しかしＢＩＯＳブートに関して言えば、ＧＰＴはＭＢＲとして振る舞う形になる。よって先頭から２ＴｉＢまので領域にブートローダー（freebsd-boot）が存在する必要がある。ただし、存在だけが問題なだけであって、パーティション番号がいくつであるかは問題とされない。

## Ｑ．システムがＵＥＦＩブートなのかＢＩＯＳブートなのか分かりません！

Ａ．めーｋ（ちゅどーん）。インストールＣＤをＬｉｖｅＣＤとして立ち上げた後 `sysctl machdep.bootmethod` コマンドを事項して、ＵＥＦＩならＵＥＦＩブート、ＢＩＯＳならＢＩＯＳブートであることを確認することができる。

# 【付録】ブートローダーの更新

## ＢＩＯＳブートの場合
```
gpart bootcode -p /boot/gptboot    -i 1 ada0
        または
gpart bootcode -p /boot/gptzfsboot -i 1 ada0
```

## ＵＥＦＩブートの場合
まずは /etc/fstab に以下の設定を追加する。

```
# Device    Mountpoint FStype  Options Dump Pass #
/dev/ada0p1 /boot/efi  msdosfs rw      0    0
```

次に /boot/efi ディレクトリを作成する。

```
mkdir -p /boot/efi
```

一度は手動でマウントを実行しておく。もちろん再起動して気にしないのもあり。

```
mount /boot/efi
```

以後の更新は下記の通り。

```
cp /boot/loader.efi /boot/efi/EFI/BOOT/BOOTX64.efi
```

# 参考文献
* [gpart(8)のマニュアル](https://www.freebsd.org/cgi/man.cgi?gpart(8))
* [FreeBSDのブートプロセス](https://qiita.com/mzaki/items/76acac14c16ac6789e68)
* [UEFI - FreeBSD Wiki](https://wiki.freebsd.org/UEFI)
* [FreeBSD 10.4 Release Notes](https://www.freebsd.org/releases/10.4R/relnotes.html)
* [FreeBSD 11.1 Release Notes](https://www.freebsd.org/releases/11.1R/relnotes.html)
* [bsdinstall: increase EFI partition size to 200MB](https://reviews.freebsd.org/D6935)
* [GPTとMBRはどのように違うのか？](http://syuu1228.hatenablog.com/entry/20130103/1357165915)
* [コマンドラインによるFreeBSD起動パーティションの作成など](http://nao550.hateblo.jp/entry/2018/02/12/214146)
* [FreeBSD/UEFIについて](http://zenno.com/pukiwiki/index.php?FreeBSD%2FUEFI%A4%CB%A4%C4%A4%A4%A4%C6)