@ebijunさんのRPI4用NetBSDイメージを使い複数パーティションを持ったUSBブートディスクを作成する

# 普通にできること

「Raspberry Pi 4」（以下、RPI4）で、SDカードではなくHDDやSDD等のUSBディスクからブート可能になりました。

- 参考記事：[SDカード不要？ 「Raspberry Pi 4」をUSBブートさせる方法について](https://gadgetrip.jp/2021/05/raspberry_pi4_usb_boot/)

今回は、[2.5インチのHDD/SSDをUSB3.0で接続するJMS578ベースの透明ケース](https://www.amazon.co.jp/gp/product/B01N4OU8MV/)と、[USBバスパワー機器へ安定した電力を供給するためのACアダプター](https://www.amazon.co.jp/gp/product/B07RWW6BVG/)を使って、 HDDをRPI4へ接続します。

[@ebijunさんのディスクイメージ](http://cdn.netbsd.org/pub/NetBSD/misc/jun/raspberry-pi/)（RPI4用はYYYY-MM-DD-aarch64ディレクトリ）を、USBディスク上で展開すれば、RPI4からUSBブートできます。このディスクイメージはGPT形式で、ブートマネージャを格納したEFIパーティションと、NetBSDのルートファイルシステムのためのffsパーティションの2つの区画に区切られています。

以下は、Macを使った作業手順です。

`/dev/disk4`は、USB接続した750GBの空のHDDです。

```
Darwin$  diskutil list /dev/disk4
/dev/disk4 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                                                   *750.2 GB   disk4

Darwin$  sudo gpt -r show -l /dev/rdisk4
       start        size  index  contents
           0  1465149168    
```

HDDにディスクイメージを転送します。

```
Darwin$ sudo dd if=./2021-12-06-netbsd-raspi-aarch64.img of=/dev/rdisk4 bs=1m
2399+1 records in
2399+1 records out
2516581376 bytes transferred in 25.905331 secs (97145308 bytes/sec)

Darwin$ diskutil list /dev/disk4
/dev/disk4 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *750.2 GB   disk4
   1:                        EFI NO NAME                 83.9 MB    disk4s1
   2: 49F48D5A-B10E-11DC-B99B-0019D1879648               2.4 GB     disk4s2

Darwin$ sudo gpt -r show -l /dev/disk4
       start        size  index  contents
           0           1         PMBR
           1           1         Pri GPT header
           2           1         Pri GPT table
           3      163840      1  GPT part - "EFI"
      163843     4751355      2  GPT part - "netbsd-root"
     4915198  1460233970         
```

このHDDをSDカードを抜いたRPI4にUSB接続して電源投入すればokです。SDカードからブートした場合と同様に、ルートパーティションがディスク容量にあわせたサイズに自動調整され、その後ログイン可能となります。

# 追加パーティションを定義しようとしたらダメだった

このまま使い続けることもできたのですが、swap領域も作りたいですし、`/home`や`/var`も分離したいです。
そこで、「普通にできること」の手順で作成したディスクをRPI4でブートする前に、USBディスクに追加のパーティションを設定することにしました。

パーティションを追加しようとしたところ、セカンダリヘッダがないのでエラーとなりました。
そこで、エラーメッセージの指示に従いリカバリーしました。

```
Darwin$ sudo gpt add -t 49F48D5A-B10E-11DC-B99B-0019D1879648 /dev/disk4
gpt add: /dev/disk4: error: no secondary GPT header; run recover

Darwin$ sudo gpt recover /dev/disk4
gpt recover: /dev/disk4: recovered secondary GPT table from primary
gpt recover: /dev/disk4: recovered secondary GPT header from primary

Darwin$ sudo gpt -r show -l /dev/disk4
       start        size  index  contents
           0           1         PMBR
           1           1         Pri GPT header
           2           1         Pri GPT table
           3      163840      1  GPT part - "EFI"
      163843     4751355      2  GPT part - "netbsd-root"
     4915198  1460233968         
  1465149166           1         Sec GPT table
  1465149167           1         Sec GPT header
```

パーティション追加に再挑戦します。

```
Darwin$ sudo gpt add -t 49F48D5A-B10E-11DC-B99B-0019D1879648 /dev/disk4
gpt add: /dev/disk4: error: no available table entries
```

# GPTテーブルに2つしかパーティションを定義出来ないのはなぜか？

`gpt show`コマンドの出力を眺めていて、GPTテーブルのサイズが1セクタしかないことに気づきました。
[@ebijunさんのディスクイメージ作成用スクリプト](https://github.com/ebijun/NetBSD/blob/master/RPI/RPIimage/Image/aarch64/Makefile)を見てみると、その理由は94行目にありました。

```
gpt create -p 2 ${VND}
```

ここで、最大パーティション数を2としているので、3つ目のパーティションを追加しようとしてエラーになったのでした。
NetBSD版の`gpt header`コマンドを使えばGPTヘッダの内容を観ることが出来ます。確かに、GPTエントリの最大数が2となっています。

```
arm64# gpt header sd0
Media Size: 750156374016 (699G)
Sector Size: 512
Number of Sectors: 1465149168 (1G)

Header Information:
- GPT Header Revision: 1.0
- First Data Sector: 3 (3B)
- Last Data Sector: 4915197 (5M)
- Media GUID: a852220f-8bb8-4918-bb6c-7f504de26c28
- Number of GPT Entries: 2
```

# 解決策：HDD上にGPTを作成し直してイメージファイルからパーティション毎にデータを転送する

まず、作業用Mac上で、ディスクイメージファイルをアタッチします。この例では、`/dev/disk5`が割り当てられました。

```
Darwin$ hdiutil attach -nomount ./2021-12-06-netbsd-raspi-aarch64.img
/dev/disk5          	GUID_partition_scheme          	
/dev/disk5s1        	EFI                            	
/dev/disk5s2        	49F48D5A-B10E-11DC-B99B-0019D18	

Darwin$ diskutil list /dev/disk5
/dev/disk5 (disk image):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        +2.5 GB     disk5
   1:                        EFI NO NAME                 83.9 MB    disk5s1
   2: 49F48D5A-B10E-11DC-B99B-0019D1879648               2.4 GB     disk5s2

Darwin$ gpt -r show -l /dev/disk5
    start     size  index  contents
        0        1         PMBR
        1        1         Pri GPT header
        2        1         Pri GPT table
        3   163840      1  GPT part - "EFI"
   163843  4751355      2  GPT part - "netbsd-root"
```

GPT作成とデータ転送の部分はスクリプトを書きました。`DEV_IMG`にイメージファイルをアタッチしたデバイス名を、`DEV_TGT`に宛先USBディスクのデバイス名を、`GB_ROOT`、`GB_SWAP`、`GB_VAR`に各パーティションのサイズをGB単位で指定します。ディスクの残りは`/home`に割り当てされます。

```shell:copy_NetBSD_image.sh
#!/bin/sh
DEV_IMG=/dev/disk5
DEV_TGT=/dev/disk4
GB_ROOT=8
GB_SWAP=8
GB_VAR=1

SIZE_BOOT=`gpt -r show -l $DEV_IMG | grep EFI | tr -s ' ' | cut -d ' ' -f 3`
SIZE_ROOT=`expr 2 \* 1024 \* 1024 \* $GB_ROOT`
SIZE_SWAP=`expr 2 \* 1024 \* 1024 \* $GB_SWAP`
SIZE_VAR=`expr 2 \* 1024 \* 1024 \* $GB_VAR`
RDEV_TGT=`echo $DEV_TGT | sed s:^/dev/disk:/dev/rdisk:`
UUID_FFS="49F48D5A-B10E-11DC-B99B-0019D1879648"
UUID_SWAP="49F48D32-B10E-11DC-B99B-0019D1879648"

sudo gpt destroy $DEV_TGT > /dev/null 2>&1
sudo dd if=/dev/zero of=$RDEV_TGT count=1
sudo gpt create $DEV_TGT
sudo gpt add -s $SIZE_BOOT -t efi $DEV_TGT
sudo gpt label -i 1 -l EFI $DEV_TGT
sudo gpt add -s $SIZE_ROOT -t $UUID_FFS $DEV_TGT
sudo gpt label -i 2 -l netbsd-root $DEV_TGT
sudo gpt add -s $SIZE_SWAP -t $UUID_SWAP $DEV_TGT
sudo gpt label -i 3 -l swap $DEV_TGT
sudo gpt add -s $SIZE_VAR -t $UUID_FFS $DEV_TGT
sudo gpt label -i 4 -l var $DEV_TGT
sudo gpt add -t $UUID_FFS $DEV_TGT
sudo gpt label -i 5 -l home $DEV_TGT
sudo dd if=${DEV_IMG}s1 of=${DEV_TGT}s1 bs=1m
sudo dd if=${DEV_IMG}s2 of=${DEV_TGT}s2 bs=1m
```

スクリプトの実行結果は、こんな感じです。

```
Darwin$ sh copy_NetBSD_image.sh 
Password:
1+0 records in
1+0 records out
512 bytes transferred in 0.001383 secs (370256 bytes/sec)
/dev/disk4s1 added
/dev/disk4s1 labeled
/dev/disk4s2 added
/dev/disk4s2 labeled
/dev/disk4s3 added
/dev/disk4s3 labeled
/dev/disk4s4 added
/dev/disk4s4 labeled
/dev/disk4s5 added
/dev/disk4s5 labeled
80+0 records in
80+0 records out
83886080 bytes transferred in 4.695826 secs (17863967 bytes/sec)
2319+1 records in
2319+1 records out
2432693760 bytes transferred in 153.070320 secs (15892655 bytes/sec)
```

出来上がったUSB起動ディスクをRPI4に接続してブートしてから、追加パーティションへのファイルシステム準備や`/etc/fstab`の編集を行なっても良いです。既に稼働中の他のNetBSDシステムがあれば、もっと楽に作業出来ます。

まず、USB起動ディスクをNetBSDシステムへ接続します。

```
[ 73801.522527] sd0: GPT GUID: e24dd8c0-fd6b-48c0-a548-8dcd07feb70e
[ 73801.522527] dk2 at sd0: "abdb225f-f796-43b8-8c59-ad2171c6dcf2", 163840 blocks at 34, type: msdos
[ 73801.522527] autoconfiguration error: sd0: wedge named 'EFI' already existed, using 'abdb225f-f796-43b8-8c59-ad2171c6dcf2'
[ 73801.522527] dk3 at sd0: "7c4b78ae-efa1-4416-958a-2dd3ee6a15e7", 16777216 blocks at 163874, type: ffs
[ 73801.522527] autoconfiguration error: sd0: wedge named 'netbsd-root' already existed, using '7c4b78ae-efa1-4416-958a-2dd3ee6a15e7'
[ 73801.522527] dk4 at sd0: "swap", 16777216 blocks at 16941090, type: swap
[ 73801.522527] dk5 at sd0: "var", 2097152 blocks at 33718306, type: ffs
[ 73801.522527] dk6 at sd0: "home", 1429333677 blocks at 35815458, type: ffs
```

この例では、SDカードから起動したNetBSDシステムにUSB起動ディスクを接続しました。
SDカード上に、`EFI`と`netbsd-root`という名前のパーティションが既に存在するためにエラーメッセージが出力されていますが、気にしなくて良いです。
`/var`と`/home`に使うためのパーティションをフォーマットしましょう。

```
arm64# newfs /dev/dk5
/dev/rdk5: 1024.0MB (2097152 sectors) block size 16384, fragment size 2048
	using 6 cylinder groups of 170.67MB, 10923 blks, 21504 inodes.
super-block backups (for fsck_ffs -b #) at:
32, 349568, 699104, 1048640, 1398176, 1747712,
arm64# newfs /dev/dk6
/dev/rdk6: 697916.8MB (1429333672 sectors) block size 32768, fragment size 4096
	using 942 cylinder groups of 740.91MB, 23709 blks, 47104 inodes.
super-block backups (for fsck_ffs -b #) at:
64, 1517440, 3034816, 4552192, 6069568, 7586944, 9104320, 10621696, 12139072,
...............................................................................
```

USB起動ディスクのルートファイルシステムをマウントして`/etc/fstab`を編集します。

```
arm64# mount /dev/dk3 /mnt
arm64# vi /mnt/etc/fstab
arm64# cat /mnt/etc/fstab
# NetBSD /etc/fstab
# See /usr/share/examples/fstab/ for more examples.
NAME=netbsd-root	/		ffs	rw,noatime	1 1
NAME=EFI		/boot		msdos	rw	1 1
ptyfs		/dev/pts	ptyfs	rw
procfs		/proc		procfs	rw
tmpfs		/var/shm	tmpfs	rw,-m1777,-sram%25
NAME=swap	none		swap	sw,dp		0 0
NAME=var	/var		ffs	rw		1 2
NAME=home	/home		ffs	rw		1 2
NAME=swap	/tmp		mfs	rw,-s=132048
```

`/home`のマウントポイントを作成し、`/var`以下も移しておきます（マウントポイント`/mnt2`がなければ作って下さい）。

```
arm64# mkdir /mnt/home
arm64# mount /dev/dk5 /mnt2
arm64# mv /mnt/var /mnt/var.old
arm64# mkdir /mnt/var
arm64# cd /mnt/var.old; tar cf - . | (cd /mnt2; tar xf -)
```

`toor`アカウントでsshできるように証明書もコピーしておきましょう。

```
arm64# cp -pr /root/.ssh /mnt/root
```

# 完了

出来上がったUSBディスクでRPI4からブートします。作業完了です。お疲れ様でした。

```
arm64# dkctl sd0 listwedges
/dev/rsd0: 5 wedges:
dk0: EFI, 163840 blocks at 34, type: msdos
dk1: netbsd-root, 16777216 blocks at 163874, type: ffs
dk2: swap, 16777216 blocks at 16941090, type: swap
dk3: var, 2097152 blocks at 33718306, type: ffs
dk4: home, 1429333677 blocks at 35815458, type: ffs
rm64# df
Filesystem      1K-blocks         Used        Avail %Cap Mounted on
/dev/dk1          8160101      2163136      5588960  27% /
/dev/dk3          1032238        48314       932314   4% /var
/dev/dk0            81820        23228        58592  28% /boot
ptyfs                   1            1            0 100% /dev/pts
procfs                  4            4            0 100% /proc
tmpfs             1005228            0      1005228   0% /var/shm
/dev/dk4        709060004            4    673607000   0% /home
mfs:1086            63927            1        60730   0% /tmp
```