組込みLinux Beaglebone Black でやってみる その4 TFPTで起動

実際に、BBBをmicroSDカードからブートし、TFTP転送でKernelイメージのuImageとデバイスツリーのam355x-boneblack.dtbをホストPCからダウンロードして起動してみます。 TFTPとNFSが出来ると、一度U-BOOTのboot.scrでのブート手順が決まれば、microSDカードを抜き差しをせずに、ホストPCだけで作業が出来るようになります。 microSDカードはスロットもカード自体も強くなく、一日何十回も繰り返し挿抜を繰り返すとスロットもエラーを起こしやすくなる可能性があります。

Ethernetを使った起動

Ethernet起動では、ブート時にEthernet経由でLinuxイメージやルートファイルなど転送します。 Ethernetの転送でもいろいろ転送があるけど、TFTPが軽快で良いみたい。 通常のAckのやり取りがあるTCP/IPでなく、TFTPはAckのないUDP/IPで通信する方法。 U-BOOTはTFTP機能が搭載されているので、Linux Kernelが立ち上がる前に、U-BOOTのu-boot.imgがBBBのmicroSD内あればTFTP転送できる。 ここではTFTP転送でのブートをしてみる。 ホストPC側にはFTPサーバーと同様にTFTPのサーバーを設定しておく必要がある。

Ethernetでの転送プロトコル種類

  • TFTP —- Trivial FTP —U-BOOTに搭載されているのでこれを使います
  • HTTP
  • FTP
  • NFS — U-BOOTに搭載されていますが、その7でKernel起動後にKernel組込ドライバからNFSを使います。
  • SMTP
  • etc

TFTP ブート

U-BOOT(u-boot.img) がtftp通信をサポートしているのでuImageと.dtbファイルを転送してブートする。

ここでは『その1』でダウンロードしたファイルを使います。

  1. MLO
  2. u-boot.img
  3. uImage
  4. am335x-boneblack.dtb
  5. initramfs
  6. ルートファイルシステム

microSDカード

以下の3つのファイルをmicroSDのfat16 フォーマットでbootフラグがついている/BOOT/第1パーティション入れて、BBBに挿入する。 この時、第2パーティションの/ROOFS/には、前回のままルートファイルシステムが入っている状態です。

  • MLO —- U-BOOTをビルドした時に生成
  • u-boot.img—- U-BOOTをビルドした時に生成
  • boot.scr

microSDの/BOOT/パーティションから、以下ファイルを削除しておきます。

  • am33x-boneblack.dtb
  • uImage
  • initramfs —- これは『その1』で入れてませんが、存在しないことを確認。

ホストPCの/var/lib/tftpboot/

ホストPC(uBuntu)の/var/lib/tftpboot/がホストPCのtftpサーバーのルートディレクトリとします。 このディレクトリに以下のファイルを入れます。 * /var/lib/tftpboot/は自動的に設定される場合もあるみたいですが、私の場合は/etc/xinetd.d/tftpにserver_argでルートとして設定する必要がありました。

  1. BBB内のROMからmicroSDにMLOを探しに行く
  2. MLOが起動して
  3. u-boot.imgをDDRの先頭にロードして起動する
  4. boot.scrの記述を実行して
  5. ホストPCのIPアドレスとディレクトリアドレスとTFTPプロトコルを使ってホストPCから以下のファイルを順番にダウンロードして、boot.scrで指定したDDRアドレスに配置する
  6. boot.scrに書かれたDDRのアドレスを元にブートする

LANケーブル接続

BBBのLANポートとuBuntu PCのLANポートを接続します。

ホストPC(uBuntu)でTFTPサーバーのインストール〜設定〜起動

  1. xinetd、tftp、tftpdのインストール(以下、下のコード参照)
  2. /etc/xinetd.d/tftpにtftpの設定
  3. service tftp内の、server_args = -s /var/lib/tftpboot がtftpのルートディレクトリ指定
  4. ホストPCとBBBのをLAN接続する
  5. ホストPCのIPアドレスを設定する。 ifconfigでLANの調べて、ifconfig <LAN名> <IPアドレス>で設定する。 私のPCではLAN名はenp4s0でした。
  6. これらを以下の様に実行していきます。
uBuntu
uBuntu $ sudo apt install xinetd tftp tftpd
uBuntu $ sudo vi /etc/xinetd.d/tftp
//以下を記述する。 ディレクトリは先に作成する。
service tftp
{
    protocol = udp
    port = 69
    socket_type = dgram
    wait = yes
    user = nobody
    server = /usr/sbin/in.tftpd
    server_args = -s /var/lib/tftpboot
}
// TFTPサーバーのルートディレクトリ準備
uBuntu $ sudo mkdir -p /var/lib/tftpboot
// /var/lib/tftpboot/ に このシリーズの『その1』ダウンロードした、tftpboot用のuImage, initramfs, am335x-boneblack.dtb を入れておく
uBuntu $ cd ~/BBB_Workspace/EmbeddedLinuxBBB/pre-built-images/tftp-boot
uBuntu $ sudo cp -r * /var/lib/tftpboot/
uBuntu $ sudo chmod -R 777 /var/lib/tftpboot  
uBuntu $ sudo chown -R nobody /var/lib/tftpboot
uBuntu $ systemctl status xinetd  //tftp動作状態の確認
uBuntu $ service xinetd stop      //tftp停止
uBuntu $ service xinetd start     //tftp動作再起動して、service tftpの/var/lib/tftpbootを確実に反映させる。
uBuntu $ ifconfig                 //これでLANポートの名前を調べる。 enp4s0 eth0 みたいな感じ。 wifiはwlp2s0みたいな感じ
uBuntu $ sudo ifconfig enp4s0 192.168.27.1   //ifconfigでIPアドレス設定は仮設定なので、すぐ消える場合がある

minicomでU-BOOTで1コマンドずつ実行

そして、もう一つのコマンドプロンプトを開いて、minicomを立ち上げて、1行ずつ実行していきます。 1行ずつ実行するのは、動作の確認やデバッグ等で非常に有効なのでここで慣れておきましょう。 今回はまず1行ずつ実行し、U-BOOTコマンド実行にも慣れ、ブートの順番も理解していきます。

  1. (BBBとホストPCはUARTーUSBで接続されている状態で)sudo minicomを立ち上げて、ホストPC半角モードにして、minicomにフォーカスがあたっている状態で、
  2. BBBのS3(POWER)を長押しして電源を切る。
  3. uBuntuからmicroSDをumount(eject)して、BBBに装着する。
  4. S2(BOOT_SEL)を押し続けながら、S3(POWER)を押して、すぐホストPCのスペースキーを押しっぱなしにして、minicomでU-BOOTコマンドプロンプトに入る。 
  5. プロンプトは『U-BOOT>』と表示されるか、私の場合は単に『=>』の表示となりました。
  6. 下のコードを1行づつ実行する。『BBB =>』はminicomでBBB側の設定という意味で、『BBB』とは表示されません。
minicom BBB
//minicomを立ち上げて、BBB の電源を入れて、minicomで半角スペースを押しっぱなしにしてu-bootコマンドプロンプトにはいる
BBB => setenv serverip 192.168.27.1             // uBuntuのLANに設定したIPアドレスと同じ
BBB => setenv ipaddr 192.168.27.2               // uBuntuのLANに設定したネットマスク部は同じで、一番右の数字違い
BBB => ping 192.168.27.1                        // これでuBuntuとの接続確認
BBB => tftpboot 0x82000000 uImage             //memory(DDR)アドレス0x82000000にuImageを書き込む
BBB => tftpboot 0x88000000 am335x-boneblack.dtb  //memory(DDR)アドレス0x88000000にam335x-boneblack.dtbを書き込む
BBB => tftpboot 0x88080000 initramfs           //memory(DDR)アドレス0x88080000にinitramfsを書き込む
BBB => setenv bootargs console=ttyO0,115200 root=/dev/ram0 rw initrd=0x88080000 bootwait
BBB => bootm 0x82000000 0x88080000 0x88000000     //memory(DDR)からのブート

立ち上がりました。 このようになれば成功で『その4』は完了です。 因みに、下の図でChangedとなっているのはルートFS の/etc/issueファイルを変更したからです。 

参考:BBBのボタンの場所

boot.scrを準備

  • 次は、boot.scrを使って、動作した上記のtftp経由でのブートの自動化をしていきます。
  • U-BOOT-2021以降は、boot.scrは何も設定しなくても認識しましたがた、uEvn.txtはbootfileに=uEnv.txtと書き込んでも認識してくれませんでした。
  • boot.scrはU-BOOTの予約環境変数で、boot_scripts=boot.scr.uimg boot.scr 様に指定されているので実行されます。
  • boot.scrは、u-boot.imgが起動後に呼び出されて実行されるバイナリのスクリプト。
  • まず、テキストエディタを使ってU-BOOTコマンドプロンプト形式で以下のテキストファイルを作成します。 拡張子は.cmdでも.txtでも構まいません。 
boot_sd_tftp.cmd
echo **************boot_sd started************
setenv ipaddr 192.168.27.2
setenv serverip 192.168.27.1
echo ********** Booting from microSD ... *******
setenv autoload no
tftpboot 0x82000000 uImage
tftpboot 0x88000000 am335x-boneblack.dtb
tftpboot 0x88080000 initramfs
setenv bootargs console=ttyS0,115200 root=/dev/ram0 rw initrd=0x88080000 bootwait mem=512m debug rootwait
bootm 0x82000000 0x88080000 0x88000000

boot.scrバイナリファイルに変換

boot_sd_tftp.cmdのあるディレクトリで以下を実行して、boot.scrを生成します。 ファイル名はboot.scrでなければなりません。

$ mkimage -A arm -O linux -T script -C none -a 0 -e 0 -d boot_sd_tftp.cmd boot.scr

mkimageは予めuBuntuにインストールしておかなければなりません。

$ sudo apt update
$ sudo apt install uboot-mkimage      // これか
$ sudo apt install uboot-tools        // これ。 これはmkimage以外のツールもインストール。

boot.scrで実行

microSDの/BOOT/にboot.scrをコピーする。 私の場合、SDカードをuBuntuに挿入すると、自動的に/media/ichiri/BOOT/と/media/ichiri/ROOFS/がマウントされるので、以下のようにコピー。 マウントの仕方は、『その1』参照ください。 dmesgでSDデバイス名を特定して、/mntか/media上にmoutすればOKです。 

$ cp boot.scr /media/ichiri/BOOT/

microSDの/BOOT/に、uImage, am335x-boneblack.dtb.dtb、initramfsが存在しないことを確認。

tftpでダウンロード出来ない時の確認事項

  1. ダウンロードするファイルの所有(nobody)とアクセス権(777)を確認
  2. 次はuBuntu PCのLAN portのIPアドレス確認。 ちゃんとBBBで設定したserveripと同じになっているか確認。 違っていたら、sudo ifconfigを使ってuBuntu PCのLANのIPアドレスを設定する。 ifconfigはIPアドレスの仮設定なので、uBuntuを起動した時はまた設定しなければなりません。 そして、何度も接続するまでは、数分で消えてしまうので、何度も設定し直しが必要です。
  3. tftp serviceが動作している確認。 systemctl status xinetd

その他 

intramfsを使わない場合のboot.scr例

intramfsも使わないのでnointrd。 ルートFSは、microSDに入っているので、root=/dev/mmcblk0p2となる。

boot_sd.cmd
echo **************boot_sd started************
setenv ipaddr 192.168.27.2
setenv serverip 192.168.27.1
setenv loadaddr 0x82000000
setenv fdtaddr 0x88000000
echo ********** Booting from microSD ... *******
setenv autoload no
tftpboot ${loadaddr} uImage
tftpboot ${fdtaddr} am335x-boneblack.dtb
setenv bootargs console=ttyS0,115200n8 noinitrd rootfstype=ext3 root=/dev/mmcblk0p2 rw mem=512m debug rootwait
bootm ${loadaddr} - ${fdtaddr}

tftp経由でファイルを取得

上記ではU-BOOTコマンドプロンプトでtftpbootコマンドでファイルを取得し、メモリ(DDR)の指定アドレスに格納した。 ここではtftpbootコマンドでなく、Linuxのコマンドプロンプトからtftpコマンドでファイルを取得しこのコマンドを実行したディレクトリにファイルを格納する。

BBB # tftp -r ichiri.txt -g 192.168.27.1  //これで ホストPCの/var/lib/tftpboot/からファイルをとってこれる。 tftpbootコマンドはu-bootのコマンド。 Linuxコマンドはtftpで、相手のIPアドレスの指定が必要。
//tftp: sendto: Network is unreachable が出たら、ifconfigでLANポートのipアドレス設定する

その他 シリアル(UART)

ボードにEthernetがない場合などに有効。 U-BOOTがシリアル通信をサポートしているので転送できる。 実際やっていないので不備があるかもしれませんが、今後使うときのために調べたことを載せておきます。

  1. BBBの場合、USB電源で供給せず、電源アダプターで供給しSDを抜いて、S2を押しながら電源を入れると、UARTで転送できる。(BBBはUART転送時、4.5分以内でBOOTすれば良い)
  2. S2を押して起動すると、SPI0〜MMC0〜USB0~UART0の順番で起動しようとします。 UARTブート時、USB電源で供給出来ないのは、USB電源ポートもUSB機能があり、USB0となっています。 なので、UART0より先にUSB0でブートしようとしてUART0でブート出来ないからです。
  3. 115200,8n1
  4. RAMDISK or initramfs @0x88080000
  5. ROMブートローダーはUART経由でXmodemプロトコルでSPL(u-boot-spl.bin)を受け取るのを待つ。
  6. ボード上でSPLが実行されu-boot.imgを受け取るのを待つ。(*もしXmodem転送で問題があれば、Ymodemでu-boot.imgを転送する by TI)
  7. ボード上でu-bootが起動したら、U-BOOTコマンドを使えるので、XmodemかYmodemで他の全てのブートイメージでDDRメモリーに転送する。
  8. tftpboot 0x82000000 uImageそしてubootコマンドboot(bootcmd in uEnv.txt)でブートする。
  9. 転送必要イメージファイル
    • am33x-boneblack.dtb
    • initramfs
    • u-boot.img —一般的なu-boot。 SDからの起動でもどんな時でも使用
    • u-boot-spl.bin —2nd stage bootloader, MLOはSDやeMMC起動の場合のみ。SDやeMMCがない状態でUARTで起動する際はこのファイルを使う。 U-BOOTをビルドする時に生成される。
    • uImage —U-BOOTを使用する時のkernel image。 uImage = zImage + U-BOOT用ヘッダ
    • https://github.com/niekiran/EmbeddedLinuxBBB/tree/master/pre-built-images/serial-boot からサンプルを取ってきて実行できる
  10. sudo minicom 起動して
  11. S2(BOOT_SEL)を押しながらS3(POWER)でBBBを起動
  12. minicom上に『CCCC…』とBBBが送ってくる
  13. Ctrl+A ー> sでプロトコル選択。 最初はXmodemを選択。 Xmodemが動作したら、少し高速のZmodemも試してみる。
  14. プロトコルを選択するとuBuntu PC内のディレクトリやファイルを選択できる画面が現れるので、
  15. u-boot-spl.binのあるディレクトリを選択(スペースを2回押して選択)して、転送するu-boot-spl.binファイルを選択(u-boot-spl.binスペース1回、ENTER)する
  16. これでSPLがBBB のam3358 SoC内部のRAMに転送されて、
  17. SPLが起動する。
  18. SPLはu-boot.imgを待つので15と同様にu-boot.imgを転送する
  19. 転送が完了したらSPLがu-boot.imgを起動するので
  20. 半角スペースを押したままにしてU-BOOTコマンドプロンプトに入るのを待つ
  21. loadxコマンド(Xmodemでのロード)でuImage(kernel image)をDDRメモリの0x82000000に転送する。 loadx 0x82000000 *新しいu-bootは高速のZmodemも使用できる。
  22. uImageを選択してダウンロード (8分くらいかかる)
  23. loadx 0x88000000 でam335x_boneblack.dtbをDDRメモリの0x88000000にダウンロードする
  24. loadx 0x88080000 でinitramfsををDDRメモリの0x88080000にダウンロードする(8分くらいかかる)
  25. 以下をU-BOOTコマンドプロンプトから実行
  26. setenv bootargs console=ttyS0,115200 root=/dev/ram0 rw initrd=0x88080000
  27. bootm 0x82000000 0x88080000 0x88000000
  28. これでKernelが立ち上がり、デバイスツリーを読み込みドライバを割り当てて、ルートファイルシステムをマウントし、各種初期設定をしてLinuxが立ち上がる。
  29. bootm ${kernel_load_addr} ${initramfs_load_addr} ${dtb_load_addr} –boot from memory

UART通信プロトコル

  • Xmodem
  • Ymodem
  • Zmodem
  • Kermit
  • etc

ログイン名変更

Linuxにログインした時表示されるロゴやプロンプトの表示を変更するためのファイル

  • /etc/hostname   —- Login name
  • /etc/issue  — ロゴ変更
  • initramfs を作り直す必要がある

initramfsとは

  • RAMの中に作るBOOT用の初期のファイルシステム。
  • Linux Kernelイメージにドライバーを沢山取り込むとKernelが肥大化する。
  • ブート時にルートFSをマウントするまでに必要最小限のドライバーを、ルートFSから取り込んでinitramfsバイナリファイルにしてRAMに入れることができる。
  • initramfsはKernelが実行開始したら、Kernelにマウントされる。
  • initramfsがロードされたDDRのメモリはルートFSがマウントされたらルートFSに入っているドライバを使用できるので、DDR内のinitramfsを削除しても良い。 これでDDRメモリを有効に使用できる。
  • 例えば、自社開発商品のドライバーなどだけを入れる。
  • 各商品で基板が異なり、使用するチップが異なると、ドライバが異なる。 例えば、SDカードドライバ。(SDHC、SDXCなど) その為、ルートFSのマウントまでに必要なKernelに入れ込むと、そのKernelはその商品でしか使えなくなるし、全てのドライバを入れ込むとKernelイメージが大きくなる。 Kernelが大きくなるとDDRを占有する。 これらを避けるための仕組み。
  • 以前は、initrd。

initramfs作り方

  • u-boot-toolsをインストールしてmkImageツールを使えるようにしておく。( u-boot-toolsのインストール)
  • mkimageは、U-BOOTを使用する時、KernelイメージがuImageでなければならない。
  • uImageはzImageにU-BOOT用ヘッダを付け足している。 mkimageがこのU-BOOT用ヘッダを付け足して、uImageを生成する。
  • その後、ルートファイルシステムがあるディレクトリに移動してinitramfsファイルを作成する
$ sudo apt update
$ sudo apt install u-boot-tools
$ cd <rootfs_directory>
<rootfs_directory> $ find . | cpio -H newc -o > ../initramfs.cpio
<rootfs_directory> $ cat ../initramfs.cpio | gzip > ../initramfs.gz
<rootfs_directory> $ mkimage -A arm -O Linux -T ramdisk -C none -a 0x80800000 -n "Root Filesystem" -d ../initramfs.gz  ../initramfs

コメント