ラズパイ用純正 Debian/Linux 起動SDカードを作る手順

更新：おおむね以下の方法で作成したSDカードが http://114.150.218.139:63873/RPi-boot-images/ にあります

自分でSDカードを作るメリットには

* btrfs にできる (Raspberry Pi OSでこれをやるのはちょっとむずかしい印象)
* http://raspi.debian.net にあるラズパイ3とラズパイ4用SDカードイメージでは、実行ファイルがすべて64bit ARM用になっていてメモリ消費が無駄に多いが、自分で作れば32bit実行ファイルを用いて消費メモリを節約できる。
* swap パーティションも作れる
* インストールするパッケージを自分で決めることができて、使わないものを最初から入れないようにできる
* ネットワーク設定にifupdownではなくてNetwork Managerやsystemd-networkdを使える

などがあります。以下の手順はSDカードを作るパソコンではUbuntuかDebianが稼働している必要があります。SDカードにインストールするDebianのバージョンは11 (Bullseye)です。Raspberry Pi OSではないです。だいたい以下のことを自動でやるスクリプトが https://github.com/emojifreak/debian-rpi-image-script にあります。

# 必要パッケージインストール
パソコンのほうで `sudo apt-get install gparted systemd-container qemu-user-static binfmt-support btrfs-progs` として必要パッケージをインストールして下さい。

# SDカードのパーティション切りとフォーマット

* パソコンに空のSDカードを挿入して、`sudo gparted /dev/mmcblk0` としてgpartedを起動します。
* 必要なら「デバイス」→「パーティションテーブルの作成」を選んでパーティションテーブルを作成します（以下スクリーンショット）
![スクリーンショット 2020-10-02 20-36-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259309/a877f29d-9f1e-1af2-b0e3-e0229f45c4b3.png)
* 以下のように起動パーティションを256MB程度でつくります。ファイルシステムをfat32にしてラベルをRASPIFIRMとします。
![スクリーンショット 2020-10-02 20-39-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259309/f9c95eda-c3b4-5ad7-b414-4157be491b7b.png)
* 以下のようにルートファイルシステムを作ります。btrfsでもそうでなくてもいいですが、ラベルをRASPIROOTにします。大きさもなんでもいいです。
![スクリーンショット 2020-10-02 20-43-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259309/082b5909-a7b7-37aa-25d8-308e736f16d0.png)
* スワップパーティションを以下のように作ります。大きさはラズパイの実メモリ量よりも少し大きめにするとよいと思います。ラベルをRASPISWAPとします。
![スクリーンショット 2020-10-02 20-50-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259309/e1cccd2d-b79e-1e83-03b5-6030254f37a5.png)

上記の作業を終えると、SDカードのパーティションとファイルシステムは下記のようになるはずです。問題なくできていたらgpartedを終了します。
![スクリーンショット 2020-10-02 20-54-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259309/691fee8d-59a7-f61f-732c-628c4dd0875b.png)

# mmdebstrap によるルートファイルシステムへのラズパイ用パッケージ
## 基本作業(ラズパイ3または4)
`sudo bash` などとしてパソコンのrootプロンプトで以下を実行して下さい。

1. `mount -o async,discard,lazytime,noatime,compress-force=lzo,nobarrier,commit=300 /dev/mmcblk0p2 /mnt`
2. `mmdebstrap --architectures=arm64 --variant=apt --components="main contrib non-free" --include=linux-image-arm64,udev,kmod,e2fsprogs,btrfs-progs,systemd-sysv,libpam-systemd,libnss-systemd,dbus-user-session,locales,tzdata,openssh-server,bash,apt-utils,whiptail,vim-tiny,less,man-db,wpasupplicant,crda,raspi-firmware,firmware-brcm80211,firmware-linux-free,firmware-misc-nonfree,keyboard-configuration,console-setup,fake-hwclock bullseye /mnt `

## ifupdown を用いる場合の変更
`ifupdown,isc-dhcp-client` を`mmdebstrap`に与えるパッケージとして追加する

## Network Manager を用いる場合の変更
`network-manager` を`mmdebstrap`に与えるパッケージとして追加する


## ラズパイ2の場合の変更
`--architectures=arm64` を `--architectures=armhf` に変更し `linux-image-arm64` を `linux-image-armmp` に変更します

## ラズパイ0または1の場合の変更
`--architectures=arm64` を `--architectures=armel` に変更し `linux-image-arm64` を `linux-image-rpi` に変更します

## ラズパイ3または4でメモリ節約のために実行ファイルを32bit版にする場合
`--architectures=arm64` を `--architectures=armhf,arm64` に変更します。カーネルは `linux-image-arm64` のままでよいです。

## 不要なパッケージ
以下のような場合には mmdebstrap の引数に渡すパッケージから以下のものを除くことができます。

* キーボードを使わないなら keyboard-configuration,console-setup は不要
* 無線LANを使わないなら wpasupplicant,crda は不要
* btrfs を使わないなら btrfs-progs は不要
* SSHによるリモートログインをしないなら openssh-server は不要

## 標準的なパッケージの追加
mmdebstrap の `--variant=apt` を `--variant=important` または `--variant=standard` に置き換えることで、重要なパッケージや標準的なパッケージがインストールされるようになります。


# ブートパーティションの作成

1. `mount -o async,discard,lazytime,noatime /dev/mmcblk0p1 /mnt2`
2. `cp -Rp /mnt/boot/firmware/* /mnt2`
3. `rm -rf /mnt/boot/firmware/*`
4. `umount /mnt2`
5. `mount -o async,discard,lazytime,noatime /dev/mmcblk0p1 /mnt/boot/firmware`
6. `/mnt/boot/firmware/cmdline.txt` 内の `root=` の部分を `root=LABEL=RASPIROOT` に変更します
7. ラズパイ3または4でarmhf (32ビット実行ファイル) を動かす場合 `/mnt/boot/firmware/config.txt` の先頭に `arm_64bit=1` を加え、`cp /mnt/usr/lib/linux-image-*-arm64/broadcom/bcm*rpi*.dtb /mnt/boot/firmware` して下さい。

# 諸々の設定
SDカード上のルートファイルシステムが/mntにあるとして、以下のファイルを適切に設定します。ssh でリモートログインする場合 `/mnt/root/.ssh/authorized_keys` に適切な公開鍵を置くか、`/mnt/etc/ssh/sshd_config` を編集してパスワードによるrootログインを許可します。

```config:/mnt/etc/hostname
raspi-bullseye  (好きなように)
```

```config:/mnt/etc/fstab
LABEL=RASPIROOT / btrfs rw,async,lazytime,discard,compress-force=lzo 0 1
LABEL=RASPIFIRM /boot/firmware vfat rw,async,lazytime,discard 0 2
LABEL=RASPISWAP none swap sw,discard 0 0
```

```config:/mnt/etc/resolv.conf
options inet6 edns0
nameserver 192.168.1.2
```

```config:/mnt/etc/environment
http_proxy=http://192.168.1.2:3128/
https_proxy=http://192.168.1.2:3128/
```

# ネットワーク設定
## Network Manager を用いる場合
```config:/mnt/etc/default/crda
REGDOMAIN=JP
```

上記以外の設定は、起動後に `nmtui-edit` で設定する


## systemd-networkd を用いる場合
```config:/mnt/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
country=JP

network={
  ssid="あなたの無線SSID"
  scan_ssid=1
  key_mgmt=WPA-PSK
  psk="あなたの無線パスフレーズ"
}
```

```config:/mnt/etc/systemd/network/wlan.network
[Match]
Name=wlan0 # もし無線LANを使うなら。有線ならeth0とする

[Network]
DHCP=yes
NTP=ntp.nict.jp
```

`systemctl enable wpa_supplicant@wlan0 systemd-networkd` とします。

## ifupdown の場合

```config:/mnt/etc/default/crda
REGDOMAIN=JP
```

```config:/mnt/etc/network/interfaces
# 以下を追加
auto wlan0
iface wlan0 inet dhcp
    wpa-ssid 無線のSSID
    wpa-psk 無線のパスフレーズ
```

# ARMエミュレーターによる最後の仕上げ

1. `chroot /mnt passwd root`
2. `chroot /mnt dpkg-reconfigure tzdata` でタイムゾーンの設定をします
3. `chroot /mnt dpkg-reconfigure locales` でロケールの設定をします
4. `chroot /mnt dpkg-reconfigure keyboard-configuration` でキーボード配列を設定します。「Generic 105-key PC (intl.)」→「Japanese」と選びます。
5. `chroot /mnt fake-hwclock save` で時刻を保存します

# SDカードのアンマウント
1. `umount /mnt/boot/firmware`
2. `umount /mnt`

以上で、SDカードを抜いてラズパイに挿して電源入れると自家製Debianが起動します。