関連記事
- Raspberry Pi 3Bの場合は こちら
- Raspberry Pi 4Bの場合は こちら
概要
Raspberry Pi 3 Model B からSDカードに保存したU-Boot (32bit) を起動し、メインラインカーネル (32bit) をネットワークブートするための環境を構築する方法を解説する。
Raspberry Pi 3B にはU-Bootのバイナリを格納したSDカードを差し込み、U-Bootを利用してネットワーク上にあるネットワークサーバからブートイメージとルートファイルシステムを取得する。
ネットワークブート用のサーバはUbuntu 18.04 LTS上にdnsmasqとnfs-kernel-serverをインストールし、メインラインカーネル (32bit) *1をビルドしておく。
はじめに
組込みシステムの開発段階やデバッグ段階では、ネットワーク経由でシステムを起動(ネットワークブート)できることが理想的である。
ネットワークブートができる環境が構築されていれば、組込みシステムのファームウェアに再書き込みせずにデータを共有することができる。
Raspberry Pi はARMプロセッサを搭載したシングルボードコンピュータの一つで、手軽に入手できる点や世の中に情報が多い点から組込みシステムの入門として用いられることが多い。
しかし、Raspberry Pi 3B のROMには幾つかの不具合が報告されており、ROMによるネットワークブートが安定しない。
一方で、Raspberry Pi 3B のROMから別のブートローダをロードし、そのブートローダからネットワークブートする手法が存在する。 Das U-Boot(U-Boot)は組込み系で広く利用されているブートローダの一つであり、Raspberry Pi 3B でも利用することができる。
U-Bootには機能として、簡易のコマンドやドライバが組み込まれており、TFTPを利用してネットワークブートすることができる。
U-Bootでネットワークブートを用いたRaspberry Pi 3Bの起動シーケンスを下記に示す。
- Raspberry Pi 3Bに電源投入すると、GPUが起動する。
- GPUは1段目のブートローダを起動。One-Time Programmable (OTP) メモリのブートモードに従ったメディア(今回はSD card)から、2段目のブートローダ
bootcode.bin
を取得する。 bootcode.bin
を実行する。bootcode.bin
はSDRAMを有効化する。bootcode.bin
はファームウェアstart.elf
を読み込む。start.elf
を実行する。start.elf
はconfig.txt
を指定されたメディア(今回はSD card)から取得する。start.elf
はcmdline.txt
を指定されたメディア(今回はSD card)から取得する。start.elf
はconfig.txt
のkernelパラメータで指定されたu-boot.bin
を指定されたメディア(今回はSD card)から取得する。u-boot
を実行する。u-boot
はTFTP経由でzImage
を取得する。- 取得した
zImage
をSDRAMの指定場所(${kernel_addr_r}
)に読み込む。 u-boot
はTFTP経由でbcm2837-rpi-3-b.dtb
を取得する。- 取得した
bcm2837-rpi-3-b.dtb
をSDRAMの指定場所(${fdt_addr_r}
)に読み込む。 - ARM coreにリセット指令を発行し、${kernel_addr_r}番地からプログラムを実行する。
下記は、Raspberry Pi 3Bが起動するまでに必要となるファイル群である。
ファイル名 | 格納先 | 概要 |
---|---|---|
config.txt | SD card | システム構成パラメータ |
cmdline.txt | SD card | カーネルコマンドライン(もしかしたら不要) |
start.elf | SD card | 一般的なRaspberry Piのファームウェア |
fixup.dat | SD card | start.elfのリンカファイル |
bootcode.bin | SD card | SoCに備わっているブートローダ |
zImage | Server | カーネルイメージ |
bcm2710-rpi-3-b.dtb | Server | デバイスツリーファイル (bcm2837-rpi-3-b.dtb でも代用可能) |
そこで本記事では、Raspberry Pi 3B でメインラインカーネルをネットワークブートするための環境を構築する方法を紹介する。
各手法における特徴
前回の記事では、Raspberry Pi 3Bをネットワークブートする手法としてROM内のファームウェアを利用した。 ネットワークブートのための手法はいくつか存在しているが、それぞれ特徴が異なる。
ROM内のファームウェアを利用した場合の特徴としては以下の点があげられる。
- SDカード周りのデバッグやドライバの開発などに向いている。
- 運用中はSDカードが不要なので、SDカードを用意するコストを最小限に抑えることができる。
一方、U-Bootを利用した場合の特徴としては以下の点があげられる。
- 起動対象の差を設定によって吸収できる。
- (Raspberry Pi 3B の場合) 不具合の影響が小さい。
- U-Bootに備わっている簡易コマンドを利用することで、別プログラムをロードしたり多段ブートすることを容易である。
実行環境
実験環境は前回構築した環境をそのまま使用する。
ネットワークブートサーバの詳細は下記のとおりである。
名前 | 詳細 |
---|---|
OS | Ubuntu 18.04 LTS |
Kernel | 5.3.0-40-generic |
NIC (ホームセグメント側) | eth0 |
NIC (開発側セグメント) | eth1 |
IPアドレス (eth0) | 192.168.1.11 |
IPアドレス (eth1) | 172.16.1.1 |
ホスト名 | server |
Raspberry Pi 3Bのブートイメージ格納予定 | /srv/boot |
Raspberry Pi 3Bのルートファイルシステム格納予定 | /srv/rootfs |
シリアルデバイスファイル | /dev/ttyUSB0 |
シリアルポートのボーレート | 115200 |
また、使用するRaspberry Pi 3Bの詳細は下記のとおりである。
名前 | 詳細 |
---|---|
ファームウェア | 4.19.97-v7+ |
NIC | eth0 |
IPアドレス | 172.16.1.2 (DHCP) |
ホスト名 | raspberry |
ストレージ | HIDISC microSDHCカード 8GB CLASS10 UHS-1対応 |
デバイスファイル | /dev/sdb1 |
目標とする環境
本記事が目標とするネットワークブートの構成は下記のとおりである。
「Raspberry Pi 3Bに挿入してあるU-Boot(32bit)がネットワークブートを担うこと」と「起動するカーネルはメインラインカーネルのARM(32bit)」以外は前回の記事と同様のことを目指す。
今回使用したソフトウェアのバージョンは下記のとおりである。
名前 | バージョン |
---|---|
U-Boot | v2020.07-rc1 |
mainline kernel | v5.7-rc3 |
dnsmasq | v2.79-1 |
nfs-common | 1:1.3.4-2.1ubuntu5.2 |
Raspberry Pi 3Bの設定
ネットワークブートサーバの設定は、システム構成パラメータの更新とU-Bootの構築の2ステップ必要になる。
システム構成パラメータの更新
config.txtから読み込むカーネルファイルをU-Bootに変更する。
user@server:~$ mount /dev/sdb1 /mnt/tmp user@server:~$ echo 'kernel=u-boot.bin' > /mnt/tmp/config.txt user@server:~$ umount /mnt/tmp
U-Bootの構築
今回の手順では、別のマシン(ネットワークブートサーバ)でU-Bootをビルドし、バイナリをSDカードにコピーする。
リポジトリをダウンロードする。
user@server:~$ git clone git://git.denx.de/u-boot.git user@server:~$ cd u-boot
ARMクロスコンパイラ(32bit)をインストールする。
user@server:~$ apt-get install gcc-arm-linux-gnueabi
Raspberry Pi 3B (32bit) 用の
.config
ファイルを生成する。user@server:~$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- USE_PRIVATE_LIBGCC=yes rpi_3_32b_defconfig
U-Bootをビルドする。
user@server:~$ make
生成したバイナリ(
u-boot.bin
)をSDカードにコピーする。user@server:~$ mount /dev/sdb1 /mnt/tmp user@server:~$ cp u-boot.bin /mnt/tmp/ user@server:~$ umount /mnt/tmp
ネットワークブートサーバの設定
ネットワークブートサーバの設定は、ブートイメージの作成、ルートファイルシステムの取得、DHCP/TFTPサーバの構築、NFSサーバの構築の4ステップ必要になる。
ブートイメージの作成以外は、前回の記事で構築した環境をそのまま利用する。
ブートイメージの作成
メインラインカーネルをビルドするのに必要なパッケージをインストールする。
user@server:~$ sudo apt install git bc bison flex libssl-dev make
mainlineのkernelのソースコードをクローンする。
user@server:~$ git clone --depth=1 https://github.com/torvalds/linux.git user@server:~$ cd linux
ARM用の
.config
ファイルを生成する。user@server:~$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
カーネルをビルドする。
user@server:~$ make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- zImage modules dtbs
生成したカーネルイメージとDevice Tree blobsをtftpサーバの公開ディレクトリにコピーする。
user@server:~$ sudo cp arch/arm/boot/dts/*.dtb /srv/tftpboot/ user@server:~$ sudo cp arch/arm/boot/zImage /srv/tftpboot/
実行結果
minicom経由でアクセスする。
U-Boot 2020.04-00687-gd16d37bcd4 (Apr 29 2020 - 09:27:20 +0000) DRAM: 948 MiB RPI 3 Model B (0xa32082) MMC: mmc@7e202000: 0, sdhci@7e300000: 1 Loading Environment from FAT... *** Warning - bad CRC, using default environment In: serial Out: vidconsole Err: vidconsole Net: No ethernet found. starting USB... Bus usb@7e980000: scanning bus usb@7e980000 for devices... 3 USB Device(s) found scanning usb for storage devices... 0 Storage Device(s) found
Device Tree blobsをメモリ上に展開する。
U-Boot> dhcp ${fdt_addr_r} ${fdtfile} Waiting for Ethernet connection... done. BOOTP broadcast 1 DHCP client bound to address 172.16.1.2 (6 ms) Using smsc95xx_eth device TFTP from server 172.16.1.1 our IP address is 172.16.1.2 Filename 'bcm2837-rpi-3-b.dtb'. Load address: 0x2600000 Loading: ################################################## 13.8 KiB 1 MiB/s done
カーネルイメージをメモリ上に展開する。
U-Boot> tftp ${kernel_addr_r} zImage Waiting for Ethernet connection... done. Using smsc95xx_eth device TFTP from server 172.16.1.1; our IP address is 172.16.1.2 Filename 'zImage'. Load address: 0x80000 Loading: ################################################## 9.3 MiB 2.7 MiB/s done
カーネルパラメータを設定する。
U-Boot> setenv bootargs root=/dev/nfs nfsroot=${serverip}:/srv/rootfs,vers=3,proto=tcp rw rootwait ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}::eth0:off nfsrootdebug
指定番地にあるカーネルを起動させる。
U-Boot> bootz ${kernel_addr_r} - ${fdt_addr_r} Kernel image @ 0x080000 [ 0x000000 - 0x953200 ] ## Flattened Device Tree blob at 02600000 Booting using the fdt blob at 0x2600000 Using Device Tree in place at 02600000, end 02606725 Starting kernel ... <<< snip >>> Raspbian GNU/Linux 10 raspberrypi ttyS0 raspberrypi login:
おわりに
本記事では、U-Bootを利用してRaspberry Pi 3Bをネットワークブートする方法を紹介した。
U-Bootを利用したことで、Raspberry Pi 3Bで報告されている不具合を回避しながらも、SDカードの書き込み寿命の浪費を防ぐことができる。
U-Bootにはあらかじめスクリプトを書いておき、起動時に自動で実行する機構もあるので、そちらを利用するとより一層便利になる。
変更履歴
- 2020/04/30: 記事公開
- 2022/06/07: 章構成の修正
参考
SDカードを使わず、network bootでRaspberry Pi 3Bを起動する
- Raspberry Pi Documentation - Raspberry Pi hardware
- らずぱい3で、ネットワークブート(SDなし) - Qiita
- Raspberry PI でNetwork Bootに挑戦 - Qiita
- Raspberry Pi Documentation - Raspberry Pi hardware
- Raspberry Pi 3でPXEネットワークブート - あっきぃ日誌
SDカードにU-Bootのみ格納し、network bootでRaspberry Pi 3Bを起動する
- 上野家のホームページ - 資料室 : PC/RaspberryPi/ネットワークブートネットワークブート
- Raspberry Pi を Network boot (with LANDISK NFS) | ず@沖縄
- RPi U-Boot - eLinux.org
SDカードを使わず、network bootでRaspberry Pi 3B+を起動する
Raspberry Pi の起動シーケンス
- Raspberry Pi Documentation - Raspberry Pi hardware
- Raspberry Pi Documentation - Raspberry Pi hardware
- Raspberry Pi Documentation - Raspberry Pi hardware
- Raspberry Pi 3 の起動シーケンス - simplegifts#tech
Raspberry Pi のブートで必要なファイル郡
U-Bootに関する説明資料