Raspberry Pi ZeroなどをUSB接続したマシンからmicroSDなしでOS起動

Last modified: 2019-10-22

USBデバイスブートモード” と呼ばれる起動方式によるカーネル起動とinitrdからのNFSブートによってRaspberry Pi ZeroなどをmicroSDカードなしで起動してRaspbianを動かす。

この起動方式はZero系以外にPi A系/3A+/4BやCompute Module系にも対応しているとされているが、以下はPi Zero系を前提として手元のPi Zero WHについての内容を扱う。

背景:Pi Zero WHで使用していたmicroSDカードが異常発熱して壊れた話

2019年9月上旬に手元の環境で使用していたPi Zero WHが使用開始数日後に異常発熱するようになってしまい、当時異常発熱の原因がはっきりする前にOSの設定をいじっていたところ、SoC温度が75℃を超えた上に再起動などの全ての外部コマンドが突然I/Oエラーにより実行不能となり、危険を感じて直ちにケーブルを抜いて動作を止めたがmicroSDカードがこの時から認識されなくなり、本体に加えて同時に使用していたGPIO接続のサウンドカードも(microSDの故障によりOSが動かせなくなって)使えなくなってとても悔しい思いをした。

前述の通り、Pi ZeroにはmicroSDカードを使用しない起動方法が実際に存在し、記事冒頭のリンク先の公式ドキュメントにも書かれているのだが、異常発熱が起きてmicroSDカードが壊れた時点では目にする機会はなく、新しいカードを買うべきか悩みながら “microSDカードなしでのZero系の起動なんてできないだろう” と思いつつも調べていたら、偶然にもそれがこの方式によって可能だと分かり、途中色々と問題が出たものの、最終的にはOSが動くところまで持っていくことができ、サウンドカードも含めて再び使えるようにできた1

なお、異常発熱を最初に確認した時に扇風機の風を送ってみたところ、SoC温度は風を送っている間だけ70℃台前半から57℃付近2まで下がったが

  • microSDカードの部分だけが、SoC温度が当時60℃近くだった3B+のものと比べても手に触れたときにすぐ離してしまうレベルで異常に熱いままだった
  • 高温になったときにI/Oエラーが出るようになって壊れた
  • microSDカードなしで使うようになってからのSoC温度が9月下旬から10月上旬にかけて700MHz動作で50℃程度までにしか上がっていない

といったことから、異常な “熱” はmicroSDカードから来ていた可能性が極めて高いと考えている。

ここからが本題となるが、かなり長くなっている。

起動させたいPi以外に用意するもの

  • 空きUSBポートのある別マシンとNFSサーバにする別マシン(これらは同じでも可)
    • OSのデータを置いてNFSサーバにするマシンが1台必要で、Zero W/WHでは内蔵無線LANがあるおかげでそのマシンとは別のマシンにUSBケーブルを接続して無線LANを用いてNFSブートすることもできる
      • 無印ZeroではUSBケーブルでNFSサーバのマシンと接続してUSBイーサネット通信を用いてNFSブートを行う以外にNFSサーバとの通信手段がない
    • NFSサーバにするマシンにはNFSサーバ機能のパッケージ(Debian系ディストリではnfs-kernel-server)をインストールしておく
    • Piの起動はUSBケーブルの接続されたマシンから行うことになるため、このマシンにはPiの起動に用いるツール(後述)を入れておく必要がある
    • NFSブートについてはinitrd内にOSのファイルを全て入れることができれば理論上は必要がないのだが、実際にはサイズの制約があるためにそのままの形で全ての入ったinitrdを転送することは極めて困難
  • MicroUSB端子とUSB(A)端子を接続する両オスケーブル
    • 基本的にはOTG/ホストケーブルではないものを使用する
    • MicroUSB側の端子の”ID” の線が内部で隣の “GND” の線につながっているものは “USBホストケーブル” と呼ばれ、Zero側から周辺機器を制御する場合に使われる
      • Zeroは本体真ん中の “USB” 端子に別の機器が接続されたときに端子がホストケーブルのものかどうかをチェックして、外部機器を制御する側になるのか、それとも自分が周辺機器として振る舞う側になるのかを判断することができる(dwc2の使用時のみで、これを使用していない場合は常に前者)
      • OSの起動は “ホストケーブル + 両オスUSBケーブル” で接続した場合でも可能だが、その場合Zero側に周辺機器として振る舞わせてUSBケーブルでイーサネット通信をする機能は正しく動作しない
        • MicroUSB端子とUSB端子を接続する両オスケーブルには内部で電源供給の線しかつながっていない安価な “電源(または充電)ケーブル” と呼ばれるものがあり、これはOSの起動やイーサネット通信に使うことができない
        • “電源(充電) + 通信” のような記載があり “ホストケーブル” と表記されていないものが最も適している
    • Zero系以外のモデルではそれに合ったケーブルで接続する

起動に成功して稼働中のZeroからlsusbdmesgを実行したときにUSBルートハブなどの情報が出力に含まれている場合、Zeroは(PCと同様)外部機器を制御する側として振る舞っている。この動作モード(ホストモード)ではUSBケーブルを接続してイーサネット通信をすることはできない(USBイーサネット用のネットワークインターフェース自体がZero内で認識されている場合も含む)。

無線LANを使用する場合

手元のZero WHでは内蔵無線LANを通してNFSサーバ兼アクセスポイント3の3B+(常時稼働)に接続しつつ、USBケーブルはJACK Audio Connection Kit4の音データをPCから送信するためにPCのUSB端子と接続してイーサネット通信を行っている。これはZero WHにある2つの通信手段を両方活用しつつ給電も同時に行える形となる。

無線LANを使用する場合には無線LAN親機(アクセスポイント)が必要なのは当然だが、無線LANネットワーク経由でNFSサーバのマシンに到達できる必要がある。

NFSブートを行うにはinitrdが必要となるのだが、無線LANを使用するためのカーネルモジュールやファームウェアが標準では入っておらず、無線LANインターフェース特有の幾つかの処理を行うためのスクリプトもそれらとあわせて導入する必要がある。これらの追加手順については後述する。

トラブルシューティングにおけるHDMIケーブルの有用性

ZeroのHDMIディスプレイ出力を用いずに使いたい場合であっても、Piの起動をさせてしばらく待ってもSSHでのログインができる状態にならなかった場合に問題解決の手がかりとなるエラーメッセージを画面に表示することがあり、実際に手がかりになったことが複数回あったので、Zero系の端子(Mini HDMI)に合うHDMIケーブルは用意しておいたほうがよい。

ただし、1回目の試行でSSHログインまでうまく動けば絶対に必要というわけでもないので、設定などを見直せるだけ見直した上で何回か試して、それでもうまくいかないという場合にのみ用意するとよい。

準備

この準備はPCで行うことを推奨する。以下はDebian/Ubuntuで行うことを前提とする。

USBデバイスブートモードによるRaspbian起動の流れを理解する

  1. 後述のrpibootというツールを実行し、Raspbianのイメージにおける第1パーティション(/boot/に相当)内の起動用ファイル群(ブートローダや起動についての設定ファイル)をPiに送信する
  2. Zeroが起動用ファイル群を処理し、虹色画面(Rainbow screen)になった後でカーネルがメッセージを出し始める(確認にはHDMI接続したディスプレイが必要)
  3. 処理がinitrdに移る(config.txtに記述が必要・設定内容については後述)
  4. cmdline.txtip=パラメータに従ってinitrd内でネットワークインターフェースが設定される
  5. 同ファイルのnfsroot=パラメータに従ってinitrdからNFSサーバのファイルシステムに移動してOSが起動する
  6. NFSサーバのファイルシステムに入った後は通常通りOSの起動処理が進む(ネットワーク関係は改めてこちらの設定ファイルに基づいて設定される)
  7. SSHサービスが起動した後でSSHログインができるようになる

initrdの作成には別の稼働中のPiを用いる方法もあるが、qemu-user-staticパッケージを導入したDebian/UbuntuでRaspbianのOSイメージからディレクトリツリーをコピーしてローカルディスクに配置してchrootを実行することにする。Raspbianの既存のイメージファイルに対してループバックデバイスを用いて操作することもできるが、毎回これを行うのは面倒な上に容量の制約も厳しいためこれは行わない。

Raspbianのイメージファイルからデータの取り出しを行う簡易的なスクリプト

下のスクリプトを保存して実行属性を付け、Raspbian LiteのOSイメージの場所とローカルディスクの書き込み先ディレクトリを順に引数として指定して管理者権限で実行すると、イメージ内のファイルが指定ディレクトリにコピーされる。

[任意]ファイル名:create_raspbian_chroot.sh ライセンス:CC0
#! /bin/sh

if test $# -ne 2; then
    printf "USAGE: %s [RASPBIAN IMAGE] [DEST]\\n" "$0" >&2
    exit 1
fi

img="$1"
chrootdir="$2"

if ! mkdir -p "$chrootdir"; then
    echo "mkdir -p $chrootdir failed." >&2
    exit 1
fi

if ! loopdev=$(losetup -f -P --show "$img"); then
    echo "losetup -f -P --show $img failed." >&2
    exit 1
fi

if ! mount "${loopdev}"p2 -o ro /mnt; then
    echo "mount ${loopdev}p2 -o ro /mnt failed." >&2
    losetup -d "$loopdev"
    exit 1
fi

if ! cp -a /mnt/* "$chrootdir/"; then
    echo "cp -a /mnt/* $chrootdir/ failed." >&2
    umount /mnt
    losetup -d "$loopdev"
    exit 1
fi

umount /mnt

if ! mount "${loopdev}"p1 -o ro /mnt; then
    echo "mount ${loopdev}p1 -o ro /mnt failed." >&2
    losetup -d "$loopdev"
    exit 1
fi

if ! cp -a /mnt/* "$chrootdir/boot/"; then
    echo "cp -a /mnt/* $chrootdir/boot/ failed." >&2
    umount /mnt
    losetup -d "$loopdev"
    exit 1
fi

umount /mnt
losetup -d "$loopdev"
echo "Done."

手元では特定バージョンのRaspbian Liteに対して

  • ZeroのOS用に必要最低限のパッケージだけが入っているもの
  • パッケージのビルド用(発熱の原因となる実機でのビルド作業を回避する目的)

の2つのディレクトリツリーを用意している。

QEMUのchroot環境に入るスクリプト

コード中のCHROOT_DIRを上のスクリプトで配置したディレクトリに合わせる。

/etc/ld.so.preloadの名前変更処理はエラー回避のためのものだが、なくても動作自体に支障はない。

[任意]ファイル名:chroot-raspbian.sh ライセンス:CC0
#! /bin/sh
CHROOT_DIR=/path/to/raspbian/rootfs
for D in dev proc sys dev/shm dev/pts; do mount -o bind /${D} ${CHROOT_DIR}/${D}; done
mv ${CHROOT_DIR}/etc/ld.so.preload ${CHROOT_DIR}/etc/ld.so.preload.orig
chroot ${CHROOT_DIR} /bin/bash
mv ${CHROOT_DIR}/etc/ld.so.preload.orig ${CHROOT_DIR}/etc/ld.so.preload
for D in dev/pts dev/shm dev proc sys; do umount ${CHROOT_DIR}/${D}; done

手元ではZeroのOS用とパッケージのビルド用の2つの環境それぞれにスクリプトを用意している。

chroot環境内で行う必要のあること

以下に記した中の一部は、OSを正しく起動させるために必須となる。一つずつ注意深く処理していく必要がある。

パッケージを削除してOSデータを小さくしたい場合、raspi-configは依存パッケージの関係で削除対象となることがあるため、同ツールで設定しておきたいことは最初にこのツールで設定しておくとよい。

  • /etc/fstab内の “microSDカード内のパーティションをマウント” する記述を削除またはコメントアウト
    • これをしないとRaspbianの起動が途中で止まってしまう原因となる
    • 必要があれば他のマウントポイントについての記述を追加(tmpfsなども含む)
  • SSHのサービスが自動的に起動するようにするために/etc/systemd/system/multi-user.target.wants/に入って/lib/systemd/system/ssh.serviceへの同名のシンボリックリンクを作成する
    • /etc/ssh/sshd_configでSSHのポート番号などの設定の変更を行う
  • 無線LANを使用する場合は/etc/wpa_supplicant/wpa_supplicant.confに無線LANアクセスポイントの設定を記述
  • Jessieを使用する場合、/etc/init.d/networkingの先頭付近にexit 0を記述
    • これをしないとネットワークインターフェースの有効化処理がずっと止まったままになってRaspbianの起動が進まなくなるという事があった
  • Raspbian起動後のIPアドレスを固定するために/etc/dhcpcd.confで設定を行う
  • dpkg-reconfigureでロケール(locales)やタイムゾーン(tzdata)を引数に指定してそれぞれ設定
  • 必要に応じてパッケージを追加・削除(apt install packagename...,apt remove|purge packagename...)
  • ユーザpiのパスワードを変更(passwd pi)したり、必要があれば他のユーザを追加したりする(後からでも可)
  • ホスト名を変更するための編集(raspi-configの実行または/etc/hostname/etc/hostsの直接編集)

無線LANネットワークに接続して通信を行うためにはwpasupplicantパッケージも必要で、RaspbianにはLite版でもはじめからインストールされているが、これは間違って削除しないようにする。

initrdの作成と設定

カーネルの起動後にNFSサーバに接続してRaspbianの起動を行うためにはinitrdの作成とこれを読み込ませるための設定が必要。

ここの設定が不適切だとRaspbianの起動が失敗する原因となるので注意が必要。

  • initramfs-toolsをインストール
    • Zero W/WHで内蔵無線LANをNFSマウント時に使用する場合、追加でbusyboxをインストールして、更に必要なファイル群を手動で配置する
      • initramfs-toolsで無線LANを用いるための仕組みは元々用意されていないが、GitHubのGistにこれを実現するためのサンプルを公開している人がいるので、それを元にZero W/WHの内蔵無線LANを使用するための修正を適用して公開した(元のバージョンの派生版一覧ページから “kakurasan” の “View fork” をたどる)
        • 内蔵無線LANを動かすのに必要なカーネルモジュールやファームウェアはこれによりinitrdに含まれるようになる
        • 全てのスクリプトには実行属性が必要
        • /etc/initramfs-tools/wpa_supplicant.confとして保存するファイルは/etc/wpa_supplicant/wpa_supplicant.confに近いがctrl_interface=/tmp/wpa_supplicantの行が入るようにする必要がある
          • このinitrd用wpa_supplicant.confの内容が正しくないと、NFSブートのためのネットワークインターフェースに無線LANを使う場合にインターフェース自体が認識されていても無線LANネットワークに入れずに起動がうまくいかなくなる
  • USB端子とMicroUSB端子を接続する両オスケーブルでZeroの真ん中にあるMicroUSB端子に接続したときにZeroを周辺機器として扱ってイーサネット通信を行う機能をNFSマウントの際に用いたい場合は/etc/initramfs-tools/modulesに以下のモジュール名を記述する(無印Zeroでは必須)
    • g_ether
    • libcomposite
    • u_ether
    • udc-core
    • usb_f_rndis
    • usb_f_ecm

initrdの作成にはupdate-initramfsを用いるが、カーネルのバージョン(-k)は/lib/modules/以下にあるX.Y.Z+の形(Zeroが使用するカーネルはこの形のもの)のディレクトリ名を用いて指定する。先述のGistのファイル群にあるfinal_commands.shの改造版では/lib/modules/にある-を含まないバージョンのディレクトリ名からカーネルのバージョンを指定している。

(手動でupdate-initramfsを実行する場合)
QEMU# update-initramfs -k "X.Y.Z+" -c
QEMU# mv "/boot/initrd.img-X.Y.Z+" /boot/initrd.img

(無線LANをinitrdで使うためのスクリプトに実行属性を付けた後で/boot/initrd.imgとしてinitrdを書き出す)
QEMU# /path/to/final_commands.sh
/etc/initramfs-tools/initramfs.confの設定

基本的には好みでよい。初期状態でも動作はする。

  • MODULES: netbootのカーネルモジュール群で十分なのでmostよりinitrdのサイズがやや減らせる
  • COMPRESS: gzipxzを比べると結構サイズが変わるので、小さくしたいのであればxzがよいが、Pi側で伸長する時間も長くなる

initrdを更に小さくしたい場合、MODULESlistにしてinitrdに入れるカーネルモジュールを全て自分で管理するようにしたり、CPIO書庫から不必要なファームウェアファイルを手動で取り除いて書庫を作り直したりするなど、方法はある。

Pi用OSファイルシステムのファイルをNFSサーバに用意

Raspbianのイメージファイルを直接使ってもよいが、パーティションが複数あって処理が面倒なので、小さめのext4ファイルシステムのファイルを作って必要最小限のOSデータをここに入れてNFSサーバ側でマウントする形をとることにした。

ddmkfs.ext4を組み合わせてext4ファイルシステムのファイルを用意し、NFSサーバのマシンに送ってNFSサーバ側でループバックマウントするのだが、-T smallオプションを指定すると指定なしと比べてある程度ディスク使用量が減らせた。

作成するイメージのサイズがどれぐらい必要かは、最初に大きめのサイズのファイルを作ってから中身のコピーまでを行ってdf -hなどを実行して使用量と空きを確認してから見直して決めるのがよい。つまりサイズのチェック用と本番用とで2回作業をすることになる。なお、/boot以下は入れる必要がないので、コピー対象からは除外する。

(400MiBのext4ファイルシステムのファイルを作成する例)
$ dd if=/dev/zero of=raspbian.ext4 bs=1M count=400
$ mkfs.ext4 -m 0 -T small raspbian.ext4

(ファイルシステムをマウントしてローカルディスク上のRaspbianのディレクトリの中身をこの中にコピーする)
$ sudo mount -t ext4 -o loop raspbian.ext4 /mnt
$ sudo cp -a /path/to/raspbian/{bin,etc,dev,home,lib,lost+found,media,mnt,opt,proc,root,run,sbin,srv,sys,tmp,usr,var} /mnt/
$ sudo umount /mnt

中身が全てこの中に入ったら、NFSサーバマシンが別の場合にのみ転送を行ってからNFSサーバマシン上でマウントする。

(/pizeroというディレクトリにマウントする場合の例・mkdirは一度行えばその後は不要)
NFSサーバマシン$ sudo mkdir /pizero
NFSサーバマシン$ sudo mount -t ext4 -o loop /path/to/raspbian.ext4 /pizero

下はZeroのIPアドレスを192.168.11.222に固定した場合のNFSの設定の例。

[追記]ファイル名:/etc/exports

/pizero 192.168.11.222(rw,sync,no_subtree_check,no_root_squash)

設定反映も忘れずに行う。

(NFS設定変更の反映・Debian系の場合)
NFSサーバマシン$ sudo systemctl restart nfs-server; sudo exportfs -a

不要なパッケージやファイルの削除 (任意)

Lite版のRaspbianをインストールしても消せるパッケージは多くあるので、NFSサーバ上に配置するOS用ファイルシステムのファイルを小さくしたい場合はファイルシステムへのコピーよりも前に不要なパッケージを削除しておく。apt cleanも実行しておくと空きが確保できる。

手元の環境ではZero用のファイルシステムのファイルを3B+のtmpfs領域に置いているため、メモリ圧迫を最低限にするためにOS内の不要なファイルは可能な限り消す必要があった。

パッケージとして削除できないファイルを動作に支障のない範囲で手動で消してもよい。パッケージ管理システム関係の一時ファイルも結構大きいので、消したほうが領域を確保できる。下はソフトウェアのビルドをしない用途で安全に消せる場所の例。

  • /usr/include/*
  • /usr/share/bug/*
  • /usr/share/common-licenses/*
  • /usr/share/doc/*
  • /usr/share/gcc-*
  • /usr/share/info/*
  • /usr/share/lintian/*
  • /usr/share/man/*
  • /usr/share/mime/*
  • /usr/share/sounds/alsa/*
  • /var/lib/apt/lists/*_InRelease
  • /var/lib/apt/lists/*_Packages
  • /var/cache/debconf/*-old
  • /var/lib/dpkg/*-old
  • /lib/modules/*-*

以下のディレクトリは一部のファイルやディレクトリのみが安全に消せる。

  • /usr/share/X11/locale/*
  • /usr/share/zoneinfo/*
  • /usr/share/locale/*

cmdline.txtとconfig.txtの設定例

cmdline.txt

  • OSデータを置いているNFSサーバのルート: 192.168.11.1:/pizero
  • ZeroのIPアドレス: 192.168.11.222
  • NFSサーバ: 192.168.11.1
  • ネットマスク: 255.255.255.0
  • クライアント名: zerowh
  • ネットワークインターフェース: wlan0

とする場合の例。IPアドレスの固定設定はNFSブート後にサーバ側のZero用OSのファイルシステムにある/etc/dhcpcd.confから読み込まれ更新されるため、そちらにも等価な設定を記述する必要がある。

[追記]ファイル名:/boot/cmdline.txt

dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=192.168.11.1:/pizero rw ip=192.168.11.222:192.168.11.1::255.255.255.0:zerowh:wlan0:off elevator=deadline fsck.repair=yes modules-load=dwc2,g_ether rootwait g_ether.host_addr=11:22:33:44:55:66

ip=オプションの詳細な説明は以下などを参照: http://archive.linux.or.jp/JF/JFdocs/kernel-docs-2.6/filesystems/nfsroot.txt.html

USBイーサネット通信をNFSブートに用いる場合のネットワークインターフェース名はusb0となる。無印Zeroの場合は無線LAN機能がないため、USBケーブルで接続された機器をNFSサーバにしてこのネットワークインターフェースで通信を行ってNFSブートを行う形しかとれない。

config.txt

[追記]ファイル名:/boot/config.txt

# Zeroを周辺機器としてUSBケーブルでイーサネット通信するために必要
dtoverlay=dwc2,dr_mode=peripheral

# initrd.imgというファイル名のinitrdを読み込む
initramfs initrd.img followkernel

手元の環境では更にGPIO接続サウンドカードのJustBoom DAC Zeroに関する記述を入れているが、正常に動作している。

dwc2の行についてはZeroが外部機器を制御するのではなく自身が周辺機器として振る舞う側になれるようにするために必要で、USBイーサネット通信をする場合はこちらのモードで動作している必要がある。

followkernelはカーネルが使用するメモリ領域のすぐ後ろにinitrdを読み込むという指定。公式のドキュメントに説明がある。

rpiboot

概要とビルド

Raspberry Pi公式のリポジトリで開発されている本ツールは、対応しているPi製品をUSBマスストレージデバイスとして使用するのにも使えるが、RaspbianのOSイメージ内の第1パーティション(/boot/)のディレクトリまたはこれを複製したディレクトリを指定することでファイル転送のやりとり5を行ってOSを起動することができる。

ビルドにはlibusb-1.0の開発パッケージ(Debian系ディストリではlibusb-1.0-0-dev)が必要。

このリポジトリからソースを取得してmakeを実行するとrpibootという実行ファイルが生成されるので、これを任意の場所に配置しておく。ソースツリーには他のファイルもあるが、OSの起動にはこの実行ファイル以外のファイルは必要ない。

rpibootの使い方

USB接続されたPiをマスストレージデバイスとして起動する場合は引数を付けずに実行すればよいが、Raspbianの起動に用いる場合は事前にconfig.txtcmdline.txtなどを含んだディレクトリを-dオプションで指定する。

この際、事前にconfig.txtcmdline.txtが適切に設定されていないとOSの起動に失敗する。

(実行例と出力例)
$ /path/to/rpiboot -d /path/to/boot
Waiting for BCM2835/6/7
Sending bootcode.bin
Successful read 4 bytes
Waiting for BCM2835/6/7
Second stage boot server
File read: config.txt
File read: start.elf
...
File read: overlays/dwc2.dtbo
File read: overlays/justboom-dac.dtbo
Second stage boot server done

“Second stage boot server done” までが出力されればカーネルまでの起動処理は完了となり、Raspbian側も含めて正しく設定されていれば、しばらく経過した後にSSHログインができるようになる。initrdの中やRaspbianの起動処理で止まった場合はrpibootのメッセージからは何も分からず、Piの画面出力を見ないと詳細を確認できない。

Piがrpibootによる起動を受け付ける状態となっているのはUSBケーブルをPCなどに接続して給電を開始してからmicroSDからの起動を試みて失敗した後だけとなる(他の状態で実行しても処理が進まない)。このときにlsusbなどではBroadcom社のデバイスがPiと接続された機器から認識される。検出されるデバイスの名前は “BCM2708 Boot” (初期状態)または “BCM2710 Boot” (“Second stage boot server” の段階)となり、デバイスのシリアル番号(“SerialNumber”)もその段階によってデバイス名と同時に変わる。

問題点

“Second stage boot server” が完了しないケース

“Second stage boot server done” の段階で、Raspbianのバージョンなどの条件によっては途中でメッセージの出力が止まってしまい、Pi側の画面はずっと虹色画面のままとなってしまう。

PCで完了しない(x86_64とx86_32の両方向けにビルドして確認)バージョンのカーネルでも3B+に接続してRaspbianでビルドしたrpibootを実行すると普通に完了するケースもあり、条件はよく分からないが、2017-2018年頃のRaspbianだと正しく動く場合が多い?

原因がカーネルなのかPi自体に対するファームウェア(/boot/以下の一部ファイル)なのか、あるいはrpibootの不具合ということになるのかについてはよく分からない。

処理が止まった場合、単純に再実行しても反応はないが、電源が落ちないようにしつつ再度USBデバイスを認識させてrpibootを再実行すれば続きから処理が進んで起動を完了できる。

initrdのサイズについての制限

手元では試していないが、initrdのサイズが大きすぎると起動に失敗するらしい。rpiboot側の問題は既に修正されているようだが、rpibootでのやりとりをしている際にPiのほうで扱えるメモリ領域に制約があるといった見方もされており、ファイルサイズが約32MiBを超えると正しく動かないという。

initrdをカスタマイズしてNFSサーバなども一切使用せずにinitrdだけを用いてOSを動かすことを考えている場合、initrdの圧縮をXZで行っても限界があるということは認識しておく必要がある。

ただ、 “OS用のファイルシステムのデータを別マシンからダウンロードしてPiのtmpfs領域に保存してマウントして使う” という形であればinitrdのスクリプトを編集することで実現可能と考えられ、NFSサーバも不要にできるかもしれない。その場合、Pi側のメモリを圧迫する点には注意が必要。

ホストケーブルでないUSBケーブルを使ってもUSBイーサネット用のデバイスが検出されない

手元のZero WHではBusterのRaspbianを使っているときにUSBケーブルで接続した機器(Ubuntu 19.04のPCとRaspbian Stretchの3B+)から “イーサネットガジェット” のデバイスが検出されなかったので調べていたところ、 “2017-06-21では動いていたがStretchになって動かなくなった” という人の書き込みを公式のフォーラムで見つけたのでこの(Jessie時代の末期に出た)バージョンを試してみたところ、PCと3B+のいずれも “イーサネットガジェット” のデバイスと仮想的なネットワークインターフェースがRaspbianの起動直後から検出された。

(lsusbでの見え方の例)
PC$ lsusb
...
Bus 001 Device 006: ID 0525:a4a2 Netchip Technology, Inc. Linux-USB Ethernet/RNDIS Gadget
...

PC$ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/10p, 480M
    ...
    |__ Port 7: Dev 6, If 0, Class=Communications, Driver=cdc_ether, 480M
    ...

手元のZero WHでは以前USBケーブルの種類を間違えて試しており、これを正しいものに替えてからの検証がまだ不十分なので、今後部分的にソフトウェア(ファームウェアやカーネル)のバージョンを変えてみたり、新しいバージョンのRaspbianを試してみたりして追加で調査を行う必要がありそう。

Jessieではinitramfs-toolsで無線LANを使う場合の一部ファイルの書き方を変える必要があり、copy_file()の代わりにcopy_exec()を使うようにし、copy_file()の最初の引数は消してその後ろの引数群をそのまま使うように書き換える。

なお、StretchでもPiに接続したマシンのOSがWindowsであればPiの “イーサネットガジェット” が検出されたという人もいる。


  1. microSDカードの故障時には本体やサウンドカードも含めて損傷した可能性も少しは考えていたので、microSDカードなしで実際に使えたのを確認したときに無事と分かって安心した [return]
  2. 異常発熱する前に安定していた温度で、かつCPUは500MHzにクロックダウンさせていた [return]
  3. 3B+をアクセスポイントにした理由は単純に自宅に無線LAN親機の専用機がないため [return]
  4. “Netjack” と呼ばれ、ネットワーク上の他のマシンにJACK対応アプリケーションの音データをリアルタイムで渡せる [return]
  5. 一番最初の起動用ファイルをPiに送信した後は “Second stage boot server” というサーバとなってPiからの要求を待ち受けて要求されたファイルを送信する処理を必要なだけ繰り返す [return]