組込みLinux Beaglebone Black でやってみる その2 ブートシーケンス、U-BOOT基本知識、(参考) eMMC書込み方法

いろいろ試してみて分ってきたことを書いてます。 このページだけ実践がありませんが、『その3』以降は全て実践があります。

U-BOOTとは

このU-BOOTの設定次第でブートの方法や、Kernelの一部の設定をカスタマイズするWindowsでいうカスタムBIOSのようなもの。 そして、U-BOOT専用のコマンドラインも持ちKernelが完全に立ち上がるまでのデバッグもサポートしています。

ブートシーケンス

BBBのTI社製 am3358 SoCでU-BOOTを使う場合です。

  1. RBL(ROM Boot Loader)
    • RBLはBBBに使用されているam3358 SoCの出荷前にROMに書き込まれているので変更出来ません。 挙動はテクニカルリファレンスを見て理解しなければなりません。
    • RBLはam3358 SoCのSYSBOOT[15:0]端子(『BOOTシーケンス参考』)を見てSPL(Secondary Program Loader)を探すデバイスを選択します。
    • am3358 Soc含むTIの場合はSPLでなく、SPLにヘッダーをつけたMLO(Memory Loader)を探しに行きます。
    • MLOはTI専用ですが、U-BOOTをビルドすると、u-boot.imgと一緒にMLOもSPLも生成されます。
    • RBLはMLOを探しに行く以外に、CPU周りの初期化を行います。
  2. MLO(Memory Loader)
    • TI専用のspl(Second Program Loader)で、RBLの次に実行される初期U-BOOT。 splにU-BOOTイメージのKernelのDDRへのロードアドレス等のヘッダが付いたものがMLO。
    • microSDやeMMCからUART起動をする時はsplを使いますが、それ以外の場合はMLOを使います。 今回のシリーズでは全てMLOを使います。
    • MLOがデバイス(このシリーズではmicroSDがメイン)から呼び出され、SoC内の小さなRAMにロードされ実行されます。
    • MLOは大規模DDR等を初期化をして、MLOと同じデバイスの同じパーティションにあるU-BOOT本体イメージのu-boot.img起動します。
  3. u-boot.img
    • u-boot.imgは、U-BOOTをビルドすると生成されるU-BOOTイメージ。(ここではイメージとは実行可能なバイナリ形式になったファイルの事。)
    • Linuxが立ち上がらなくても、Ethernet TFTPやUART通信、SDやeMMCへのアクセス等が出来るU-BOOTコマンドとU-BOOT変数が用意されています。 
    • u-boot.imgはDDRにロードされ実行されます。 
    • これでU-BOOTコマンドや変数にアクセスできるようになります。
    • 次に、U-BOOTの設定で、待ち時間を設定できます。(初期値は2秒)この間に、半角スペースを押していると、U-BOOTコマンドプロンプトに移行し、ブートスクリプトboot.scrを実行せずに、手動でKernelの起動や、ブートスクリプトの検証等が可能になります。(このシリーズでも実施します。) 一度、U-BOOTコマンドプロンプトにはいると、boot.scrは実行されません。 その際は、再度RBLからの起動となります。
    • U-BOOTコマンドプロンプトに移行しなければ、u-boot.imgは、ブートスクリプトboot.scrを探しに行きboot.scrに記述されている通りに実行します。(U-BOOT 2021以降はuEnv.txtが使えず、boot.scrのみ使用でき、boot.scrはU-BOOT用のブートスクリプト) 
    • もし、boot.scrが存在しない場合は、U-BOOTのデフォルトの動作で、U-BOOT用のLinux Kernel uImageを探しに行き、なんとかLinux Kernelを起動しようとします。
  4. (bootstrap)
    • uImage(Linux Kernel)を圧縮(Compress)して作成した場合、圧縮されたuImageを解凍をする。 今回は圧縮していないので、このステップはありません。
  5. uImage(Linux Kernel:以下Kernel)
    • uImageは、Linux Kernelする際に、uImage指定をすると生成されるU-BOOT用のKernelイメージ。
    • uImageは通常のLinux Kernel zImageにU-BOOT用のヘッダー情報を付け足したものです。
    • u-boot.imgがuImageを起動すると、u-boot.imgは権限をKernelに渡すので、ここからはU-BOOTコマンドは使えません。U-BOOT領域も削除されていると思います。 
    • Kernelは回路構成や情報が入ったデバイスツリー(.dtb:Device Tree Blob)を見てKernelに組み込まれたドライバをインストールしデバイスを使用出来るようにします。 
    • この際、KernelはEthernetやUART、SD、eMMCへのアクセスやその他のデバイスをドライバを起動します。 
    • これらのドライバ、例えばEthernetのドライバはu-bootのEthernetドライバとは全く別のドライバです。 
    • u-bootは軽量にするために、簡単なTFTP等しかマウント出来ません。 Kernelは更にセキュリティの高いNFS等もマウントできます。
    • ドライバでデバイスアクセスを可能にしたら、Kernelはルートファイルシステム(ルートFS)をマウントしてファイルにアクセス出来るようにします。
    • ドライバーは、非常に沢山あるため、全てをKernelに入れてしまうと、Kernelが肥大化して、eMMCなどに収まらなくなるので、特に組込の場合は、Kernelに組み込むドライバーは最小限にして、残りのドライバは、取り込まないか、モジュール(ファイルのまま)として予めルートFSにインストールしておきます。 これらはKernelをビルドする際に設定できます。
    • すると、ルートFSがマウントされると、それらのドライバーも使用可能となる。 どのドライバーをKernelに組み込むかはKernelをビルドする際に設定できます。
    • Kernelにドライバを組み込むメリットは、マウントするのに必要なものは組み込んでおきます。(Kernelを最小化するにはinitramfsを使う方法もあります。) それと高速化が必要なドライバは入れておきます。 デメリットはKernelの肥大化と、どのドライバを入れたかファイルを見てもわからないことです。
  6. Init(初期化)
    • その後、Kernelは/sbin/init, /etc/init, /bin/init, /bin/shの順番に初期化コマンドを実行します。
    • 今回のシリーズでは、組込用ではルートFSを軽量にできる人気の高いBusyBoxを使います。
    • そのBusyBoxでは/sbin/initが起動され、/sbin/initからrcSというシェルスクリプトファイルが呼び出されます。
    • /sbin/iniはBusyBoxが生成しているためです。
    • 初期化の前にルートFSは既にマウントされているため、Linuxシェルコマンドを使用できます。
    • rcSファイルに、他のドライバの起動やSSH用のsshdやTelnet用のtelnetdを設定や駆動するなどするスクリプトを書いておきます。 また量産用には、rootの設定や初期ユーザーの設定等も記述しておけます。
  7. これらが完了すると、ログイン画面が現れてLinux Distroの起動完了します。(ログイン設定なしの場合はコマンドプロンプトが現れる)*本来の意味のLinuxとはLinux Kernelの事で、uBuntuやCentOS等は、Linux Distributionと呼ばれるようです。 なので上記ではKernelと区別するため、Linux Distroと表現しました。

U-BOOT コマンドプロンプト

今回はU-BOOTを使ってLinuxをブートします。 U-BOOTはいろいろな機能があり、どの機能を使うか設定出来るので非常に便利です。 U-BOOTはLinuxが立ち上がる前に、各種通信(Ethernet:TFTP、UART:Xmodem、Ymodem、Zmode等、USB)やデバイスアクセス(SD/eMMC、SPI/I2C通信NAND/NOR Flash)等を使用でき、それらの通信やデバイス経由でのLinuxのブートができます。 U-BOOT用のブートスクリプト(バッチファイルのようなもの)のboot.scrでの自動ブートもできますが、U-BOOT コマンドプロンプトに入ってU-BOOTコマンドプロンプトで、Linuxをブートすることもできまるのです。

U-BOOTコマンドプロンプトに入るには、BBBをブートし直している間、『半角スペース』を押したままにしているとU-BOOTプロンプトに入ることができます。 BBBのeMMCに組み込まれたU-BOOTの待ち時間は0秒なので、電源を入れる前から、minicomなどの端末から半角スペースを押した状態で電源を入れて、U-BOOTコマンドプロンプトに入るまで待たなければなりません。 私の場合は、U-BOOTをビルドする前に、menuconfigでBOOT_DELAYを=4秒としています。

// 私の場合、U-BOOTコマンドプロンプトにはいると、『=>』が表示されるので、
// 以下は『=>』をU-BOOTコマンドプロンプトを表しています。
=> printenv   // u-boot予約変数と、作った変数とその値の一覧表示
=> printenv bootargs   //U-BOOTからKernelに引数を渡す変数表示
=> load mmc 0:2 0x82000000 /boot/uImage     // image_header をDDRにコピー『重要』
4002080 bytes read in 285 ms (13.4 MiB/s)    
=> md 0x82000000 10                          // 4変数を読み出し。 mdは32bit単位で読み出し
82000000: 56190527 62153da1 50d07e51 e0103d00    '..V.=.bQ~.P.=..                                         
82000010: 00800080 00800080 d58b5329 00020205    ........)S......                                         
82000020: 73676e41 6d6f7274 382e332f 2f30312e    Angstrom/3.8.10/                                         
82000030: 67616562 6f62656c 0000656e 00000000    beaglebone......
=> imi 0x82000000                          // image_headerのinformationを表示する 
## Checking Image at 82000000 ...                                                                         
   Legacy image found                                                                                     
   Image Name:   Angstrom/3.8.10/beaglebone                                                               
   Created:      2013-04-29  19:56:00 UTC                                                                 
   Image Type:   ARM Linux Kernel Image (uncompressed)                                                    
   Data Size:    4002016 Bytes = 3.8 MiB                                      uImage                            
   Load Address: 80008000                                                                                 
   Entry Point:  80008000                                                                                 
   Verifying Checksum ... OK        
type struct image_header{
    uint32_t    ih_magic     
}

データ型(md:メモリーダンプ時)

普通はあまりメモリーダンプ(メモリーに直接値を書き込む事。例えばアドレス0x80000000に0x0000_a000を書き込むなど。)は使わないと思いますが、デバッグ時再ビルドする時間より、数箇所だけの変更で確認する場合などに使います。

私の場合mdはKernel立ち上げでトラブった時に、どこで止まっているかKernelの内部ログを覗くために使いました。

U-BOOTプロンプトで見るuint32_tの数値は以下のようになる。U-BOOTプロンプト上 e0103d00  ==> 実際 003d10e0 これはヘッダの64バイトを含まないサイズを表す(4002016)

U-BOOTコマンド・変数 一覧消してしまったU-BOOTコマンド・変数 一覧

まだほとんど埋めれていません。 順次埋めていきます。

コマンド・予約変数説明
?コマンドhelpと同じ
addrmapコマンド32bit CPU用の仮想物理メモリマップを表示する
=> addrmap
vaddr paddr size
================ ================ ================
e0000000 fe0000000 00100000
00000000 00000000 04000000
04000000 04000000 04000000
80000000 c00000000 10000000
90000000 c10000000 10000000
a0000000 fe1000000 00010000
askenvコマンド­標準入力からの入力で環境変数を設定...

=> askenv env1;echo $?
Please enter 'env1': val1
autoscrコマンドメモリ上のスクリプトの実行
baseコマンド­標準入力からの入力で環境変数を設定...
bdinfoコマンドボードの情報を表示
bootコマンドブートコマンド
bootelfコマンドELF イメージの u­boot 用アプリケーションの実行..
bootm $kernel_addr $dtb_addrコマンド指定した番地に格納されているカーネルとinitを起動させる。
//指定したKernelアドレスとdtbアドレスを指定してブートを開始する
bootm 0x82000000 - 0x88000000

OSとアプリケーションの起動
bootpコマンドBOOTP プロトコルで IPv4 アドレスを取得.
bootvxコマンド­ ELF イメージの vxWorks を起動
bootz ${loadaddr} - ${fdtaddr}コマンドbootmと同じ。zImage時に使う。
bubt ${file_name}コマンドu-bootを更新するときに使う。仕組みはtftpで$file_nameのイメージファイルを取得しFlash ROMなど適切なところにオーバライドする。 予めネットワークの設定が必要 Burn an ATF image on the Boot Nand Flash ?
cat コマンドファイル内を文字列で表示

=> cat mmc 0:1 hello
hello world
chpartコマンドアクティブパーティションの変更
cmpコマンドメモリの比較
coninfoコマンドコンソールデバイスの表示
cpコマンドメモリ間のコピー
dhcpコマンドDHCP プロトコルで IPv4 アドレスを取得
echoコマンドu-boot.imgがブートしている時に表示できる。
"ありでもなしでも動作する。 
echo ****ichiri****
echo "****ichiri****"
echpコマンドテキストを表示.
setexpr[.b, .w, .l .s] [*] [*]
setexpr[.b, .w, .l] [*]
setexpr fmt [value]...
setexpr gsub r s [t]
setexpr sub r s [t]
コマンド評価の結果により環境変数に値を書き込む

また、指定のフォーマットで値を設定する
=> setexpr foo fmt %d 0x100
=> echo $foo
256

=> setexpr foo fmt 0x%08x 63
=> echo $foo
0x00000063
cmp [.b, .w, .l, .q] addr1 addr2 countコマンドメモリの内容比較

=> cmp 0x1000000 0x101000 0xc
envコマンド環境変数操作
env default --- 指定した環境変数を初期値に戻す
env default -a --- 全ての環境変数を初期値に戻す
env delete  -- 指定した環境変数削除
env grep --- 文字列を環境変数から検索
env print --- printenvと同じ
env save --- saveenvと同じ
env set ---setenvと同じ
env import 0x80200000 No_of_bytesコマンドenv import 0x80200000 No_of_bytes
eraseコマンドフラッシュメモリの消去.
eventコマンドEVENT_SPYで発行された一覧を表示する。 デバッグ用。
もしCONFIG_EVENT_DEBUG=yでなければ、SPYのラベルは『unknown』で表示される。
exitコマンドスクリプトの終了
ext4loadコマンドext4フォーマットからファイル読み出し
fatloadコマンドfatファイルシステムからファイルを読むコマンド
flinfoコマンドファイルシステム情報の表示
for in ; do ; doneコマンドforループコマンド
=> for c in 1 2 3; do echo item ${c}; done
item 1
item 2
item 3
fpga loadb mmcコマンドu-bootからFPGAをコンフィグレーションするための変数
fsinfoコマンドフラッシュ上のファイルシステムからファイルのロード
goコマンドu­boot 用アプリケーションの実行
gpio
gpio read
gpio status [-a] [|]
コマンドGeneral Purpose IOのアクセス
gpio input:入力モード変更
gpio set:出力モード変更、出力のOn/Off
gppio clear
gpio toggle
gpio read
gpio status
help
help
コマンドhelp
imiinfoコマンドアプリケーションイメージヘッダの表示
imlsコマンドフラッシュの中にあるイメージを探す.
itestコマンド整数と文字列の比較テスト
loadコマンドファイルを読み出してデバイスに書き込み。
//SDカードのパーティション2の/boot/ディレクトリからam33x-boneblack.dtbを読み出し、DDRの0x88000000に書き込む。
load mmc 0:2 0x88000000 /boot/am33x-boneblack.dtb

またメモリから読み出してファイルに書き込むことも可能
=> load mmc 0:1 $loadaddr test.txt
260096 bytes read in 13 ms (19.1 MiB/s)
loadbコマンドシリアル経由でファイルのダウンロード(kermit モード)....
loadsコマンドシリアル経由で S レコード形式のファイルのダウンロード
loadx ?????コマンドXmodemでファイル転送?
loopコマンド指定した範囲のアドレスを読み続ける無限ループ.
lsコマンドファイルシステムの中身を一覧表示
ls [] [directory]

ls mmc 0:1
md <.オプション> $addr <$len>コマンドMemory Dump  指定したアドレスの内容を表示する

例:md.l 0xc1932588 0x500

<オプション>
b:1バイト
w:2バイト(Word)
l:4バイト(Long:default)
q:8バイト(Quadword)

<$len> 16進
0x40 (default)
mm $addrコマンドアドレス番地を指定した後に、ライトしたい内容を16進で記述
mmc指定コマンドmmc info --現在指定されているmmcデバイス情報表示
mmc list --使用できるmmcデバイス一覧表示
mmc --- で指定したデバイス表示。 番号指定でもよい。
mtestコマンド簡単なメモリのテスト
mwコマンドnm mmと同じだが、同じアドレスを終了するまで何回も変更できる。GPIOレジスタを指定して次々値を変化させるなどで便利。
nandコマンドnand メモリへのアクセス
nand erase 0x200000 0x1e0000
nand erase.part kernel
nand erase.part rootfs
nand write 0x18100000 0x200000 0x1e0000
nand write 0x18100000 kernel 0x600000
nand write.trimffs 0x18100000 rootfs $filesize
nfs コマンドNFS プロトコルでファイルをダウンロード

nfs 0x82000000 192.168.27.1:/srv/nfs/bbb/uImage
nmコマンド同一アドレスのメモリ内容を対話的に変更
pciコマンドPCI バスの一覧と、PCI コンフィグレーションスペースのアクセス.
printenv
print
コマンド変数を指定しないと、変数一覧を表示
printenv $env
print $env
コマンド変数$envを表示する。ちなみに、printとだけ打つと全部の変数が出てくる。

printenv soc //特定の環境変数を見れる
pingコマンド­ ICMP ECHO_REQUEST パケットを指定ホストに送る
protectコマンドフラッシュメモリのプロテクトの設定
rarpbootコマンド­ RARP プロトコルで IPv4 アドレスを取得
resetコマンドCPU のリセット
run $envコマンド変数内容を実行する
sleepコマンド­ 指定した秒数遅延させる
setenv $env arg
setenv $env 'arg1 arg2'
コマンド変数を$envをセットする。
setenv serverip 192.168.27.2

スペースがある場合は囲いが必要。
setenv my_own_var 'mmc list'

シェルスクリプトのように複数のコマンドを変数を入れrunで実行することもできる。 コマンドの区切りは『;』
run my_own_var

// ログメッセージの出力先指定とルートファイルシステムの場所指定
setenv bootargs console=ttyO0,115200 root=/dev/mmcblk0p2 rw //mmcblk0p2はmmc 0:2と同じだが、bootargsには/dev/mmcblk0p2で指定する。
saveenvコマンドsetenvで内容を書き換えても揮発する。これでFlash ROMなどに値が保存される 実際のセーブ先はソースコードを見る。
soundコマンドCONFIG_CMD_SOUND=y時
sound init サウンドドライバ初期化
sound playビープを鳴らす
len: 初期値1000ms
freq:初期値400Hz

testコマンドシェルライクな test の最小限の実装.
temperature list
temperature get [thermal device name]
コマンド!ERROR! C69 -> Formula Error: Unexpected operator '>'
tftpboot アドレス ファイル名コマンドtftpサーバーのtftpルートフォルダからファイルを取得して、アドレスにロードする
tftpput address size [[hostIPaddr:]filename]コマンドClientのメモリからHostにファイルを転送する。先にファイルサイズを知る必要がある。

=> load mmc 0:1 $loadaddr test.txt
260096 bytes read in 13 ms (19.1 MiB/s)
=> tftpput $loadaddr $filesize 192.168.1.3:upload/test.txt


ums [] コマンドUSBメモリにアクセス確認

devは、mmc, sata, scsi, usb, ...

CONFIG_CMD_USB_MASS_STORAGE=yが必須で、CONFIG_USB_USB_GADGET と CONFIG_BLKの設定次第
usb start
usb reset
usb stop
コマンドUSBをホストとして使う時に使用
usb info
usb storage
usb dev
usb part
usb read addr blk# cnt
usb write addr blk# cnt
versionコマンドu­boot のバージョンの表示
wget address [[hostIPaddr:]path]コマンドHTTP over TCP @Port:80

=> wget ${loadaddr} 192.168.1.254:/index.html
xxd コマンド文字列やファイルを簡単にメモリに書き込める
=> xxd mmc 0:1 hello
00000000: 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 00 01 02 03 hello world.....
00000010: 04 05
IFS予約変数Hush パーサのトークンのセパレータで
autoload予約変数bootp, dhcp, rarpboot コマンドで IP アドレスを取得したあと、自動的にファイルをダウンロードするかどうかを決める
autoscript予約変数
autostart予約変数
boudrate予約変数コンソールのボーレートを設定。
9600 | 19200 | 38400 | 57600 | 115200
初期値115200
bootaddr予約変数
bootargs予約変数OS に渡す起動パラメータを設定。
bootcmd予約変数デフォルトのカーネル起動方法を設定。
起動時に自動で実行されるコマンドを入れておく変数。 tftp $kernel_addr uImage; tftp $initrd_addr initramfs: bootm $kernel_addr $initrd_addr などと書いておけば、tftpして起動する。

bootdelay予約変数
bootfile予約変数
boot_scripts予約変数setenv boot_scripts boot.scr2
boot

初期値では、boot.scrしか見ないが、別のブートスクリプトファイル名を指定してブート出来る。 デバッグ時、boot.scrで失敗した後、U-BOOTプロンプトからバックアップのboot.scr2で起動することが出来る。
dnsip2予約変数
domain予約変数
ethact予約変数
ethaddr予約変数
eth1addr予約変数
ethprime予約変数
fileaddr予約変数
filesize予約変数
ftdaddr予約変数デバイスツリーブロブの.dtbをDDRにロードする先頭アドレス。
BBBの場合は、初期値は0x88000000
gatewayip予約変数
hostname予約変数
ipaddr予約変数自身のIPアドレス
loadaddr予約変数Linux KernelのバイナリuImageをロードする先頭アドレス
BBBの場合は、初期値は0x82000000
Zynq-7000 DDRベースアドレス
0x30000000
Zynq-UltraScale+ MPSoCとVersal ACAP DDRベースアドレス
0x20000000
loads_echo予約変数
netmask予約変数255.255.255.0でスラッシュ24と同じ意味
netentry予約変数
nfsargs予約変数
nfsbase予約変数setenv nfsbase 192.168.3.91:/usr/src/mldbox/
nvlan予約変数
preboot予約変数自動起動の前に実行するコマンドを設定
rootpath予約変数
serveripclear予約変数tftpのサーバやnfsサーバドレス設定
stdin予約変数標準入力に使うデバイスを設定
stdout予約変数標準出力に使うデバイスを設定
stderr予約変数標準エラー出力に使うデバイスを設定
verify予約変数
vlanコマンド.q:8バイト(Quadword)予約変数
uenvcmduEnv.txt専用変数uEnv.txtの最後の行に書くお決まりの変数名。
u-boot.imgはuEnv.txt内の左辺のuenvcmdを見ると、その右辺を実行していく
rootbootargs引数マウントする場所を指定
rwがないと書き込みできない
root=/dev/nfs rw
rootfsstypebootargs引数SDの第2パーティションにルートFSがあり、フォーマットがext4のとき
rootfstype=ext4

NFSにルートFSがある時
rootfstype=nfs
consolebootargs引数UARTコンソール入出力ポート名と転送Baudrateとフォーマット指定
console=ttyS0,115200n8
debugbootargs引数Kernelブート詳細メッセージ表示

今回のLinux取得

以下からzipをダウンロードして展開。 上記ファイルは、 /arch/arm/boot/compressed/にあるアセンブリファイル。 BOOTからKernelに制御を渡さず、Bootstrapに来るのは、LinuxイメージはCompressedされているので、まずBootstrapがLinuxイメージDecompress(解凍)する。

GitHub - beagleboard/linux: The official Read Only BeagleBoard and BeagleBone kernel repository https://git.beagleboard.org/beagleboard/linux
The official Read Only BeagleBoard and BeagleBone kernel repository - beagleboard/linux

Architecture specific initialization

  1. CPU specific intialization
  2. Check for valid processor architecture
  3. Page table inits
  4. Initialize and prepare MMU for the identified Processor architecture
  5. Enable MMU to support virtual memory
  6. Calls “start kernel” function of the main.c

Kernel の解凍はしない。

microSDカードに起動用ファイルを入れるには

基本手順

  1. パーティション作成
  2. ファイルシステムタイプ指定してそれぞれのパーティションをフォーマット
  3. bootフラグを付ける
  4. それから各種ファイルを指定のパーティションにコピーする

uBuntuの場合

この方法は『組込み Linux Beaglebone Black でやってみる その1 microSDで起動』を参考にしてください。

後はファイルやディレクトリの転送だけ。

私の場合、/dev/mmcblk1p1/は/media/ichiri/BOOT/としてuBuntuのファイルシステムに自動的にマウントされてたので、LinuxのcpコマンドやuBuntuのGUIのファイルエクスプローラでコピーしても使えました。

マウントされていない時は、mountコマンドで手動でマウントするか、ddコマンドでコピーするようです。

sudo dd if=u-boot.img of=/dev/mmcblk1p1

ddコマンドでSDの/BOOT/内に書き込まれる。(ddだとマウントしなくても良い?)

Windowsの場合

uBuntuのみの検証しています。 以下のWindows用は未検証です。 一応将来のメモとして残しています。

  1. Linuxイメージダウンロード
  2. 解凍
  3. Windowsの場合win32 disk imageソフトで上記イメージをSDカードに転送
  4. SDカードをEject
  5. BBBにSDカードを挿し起動
  6. Beagleboard.orgからイメージをダウンロードした場合、/opt/scripts/tools/eMMC/init-eMMC-flasher-v3.sh
  7. もしflash用の.shファイルがあれば、uEnv.txt(boot.scr同様)内に、以下を記述cmdline=init=/opt/scripts/tools/eMMC/init-eMMC-flasher-v3.sh
  8. flashには20分かかる
cmdline=init=/opt/scripts/tools/eMMC/init-eMMC-flasher-v3.sh

eMMCに起動用ファイルを入れるには

これも未検証ので、将来eMMCに転送する際の備忘録です。

基本手順

  1. SDカードでFlasherスクリプトでeMMCに転送する
  2. SDカードでLinuxを立ち上げる
  3. eMMCのfatファイルシステムの第1パーティションとext4ファイルシステムの第1パーティションを作る
  4. それらのfatパーティションとext4パーティションをフォーマットする
  5. fatパーティションには”boot”フラグを付ける
  6. fatパーティションにMLO、SPL、u-boot.img、boot.scr、uImage、dtb(,intramfs)をコピーする
  7. ext4パーティションにルートファイルシステムをコピーする

fatとext4のパーティション作成方法

//自動スクリプト時は
#  fdisk 1G /dev/mmcblk1p1    // eMMC パーティション作成 mmcblk1がeMMCの場合
# fdisk /dev/mmcblk1p2        //mmcblk1p2は第2パーティション
//以下、手動の場合におすすめ
# cfdisk /dev/mmcblk1    // eMMC パーティション作成 mmcblk1がeMMCの場合
// コマンド例 ---cfdiskをすると、パーティション作成モードに入り簡単に
// n:空き領域から新しいパーティション作成。 パーティションサイズを聞いてくる
//  b:選択しているパーティションにbootフラグを付ける

初期化してファイルシステムを作る

# mkfs.fat -F 16 /dev/mmcblk1p1  // eMMCのパーティション1がmmcblk1p1。 eMMCの場合はLabelを付けない。 
# mkfs.ext4 /dev/mmcblk1p2
// -c ファイルシステム作成前にデバイスに対して不良ブロック検査実施 (保証されていないがほとんど場合使用可能)

eMMCのパーティションにコピーする

// /dev/mmcblk1p1や/dev/mmcblk1p2を/mediaにmountして、必要ファイルをコピーか転送してunmountする。
// uBuntu ではmmcblk1がSDカード。 BBBではmmcblk0がmicroSDカード。
$ cd /media
$ sudo mkdir ichiri
$ mount /dev/mmcblk1p1 /media/ichiri
$ cp ..... /media/ichiri
// 必要ファイルをコピー後、アンマウントする
$ unmount /media/ichiri

Boot flagを付ける

$ fdisk /dev/mmcblk0    // パーティション番号はつけない
Command(m for help): a  // aはbootフラグをトグル
Partition number (a,2, default 2): 1  //bootフラグはいつもパーティション1
The bootable flag on partition 1 is enabled now.                                
Command (m for help): w     //これで書き込み
The partition table has been altered.                                           
Syncing disks. 
$ fdisk -l     //これで全てのパーティションを見て、ちゃんとbootフラグがONになっているか確認

uEnv.txt、boot.scrとは

SPLやMLOで開始して、U-BOOTイメージが展開された後、Linuxイメージを展開する前に、uEnv.txtやboot.scr(ブートスクリプト)に沿ってLinux Kernelイメージ(uImage)、dtb、initramfs(*initramfsは使用しない場合もある)が展開される。 uEnv.txtもboot.scrもU-BOOTのコマンドを実行してKernelを立ち上げるバッチスクリプトのようなもの。 U-BOOT-2021以降はboot.scrしか受け付けてくれませんでした。 なので、このシリーズではboot.scrで進めていくので、uEnv.txtは使いません。

U-BOOT source treeとは

  • U-BOOTのソースコードファイル、ディレイクトリ群全体の事
  • architecture毎に用意されているので、自作の場合は、/archディレクトリに移動
  • 開発ボードを使用している場合はメーカーごとに用意されているので、BBBの場合は/board/ti/am335xにある ボード専用のboard.cによって設定されている board initializationがここにある

USBのネットワーク設定する場合

USBでもIPアドレスを設定して、Ethernetプロトコルを走らせることが出来ます。 そうすればBBBをUSB~ホストPC経由~ホストPCのWifi経由でインターネット接続できます。

uBuntuの場合

BBBに設定

//minicom BBB console
# cd /opt/scripts/boot/
# sudo sh autoconfigure_usbo.sh  //<---not needed in my case
# ping       //返ってくるのを確認する
# sudo vi /etc/resolv.conf
nameserver 8.8.8.8     <-------DNSと同じIPを入れる
nameserver 8.8.4.4
# route add default gw 192.168.7.1 usb0
# route add default gw 192.168.6.1 usb1

uBuntuに設定

//uBuntu console
$ ifconfig      //これでBBBとUSB接続しているIPアドレスがみれる
$ ping           //返ってくるのを確認する
$ echo 1 > /proc/sys/net/ipv4/ip_forward    //これでIPポート転送(フォワード)を許可する
$ sudo iptables --table nat --append POSTROUTING --out-interface wlp2s0 -j MASQUERADE   //wlp2s0がWifiです
$ sudo iptables --append FORWARD --in-interface enx94a9a87a3c95 -j ACCEPT  //enx94a9a87a3c95がホストPC側のUSB
//minicom BBB console
# ping www.google.com   //これでpingが返ってきます

上記でBBBとUSB接続〜uBuntu PCのWifi経由でBBBはapt updateなどができるようになる。

Windowsの場合

WindowsはIPv4 Propertyで同じドメイン設定と、DNS 8.8.8.8 と 8.8.4.4を設定。

BBBに設定

# sudo route add default gw 192.168 7.1 usb0    // BBBは192.168.7.2で、uBuntuのUSBが192.168.7.1
# sudo vi /etc/resolv.conf
nameserver 8.8.8.8     <-------GoogleのDNSのIPアドレスを入れておく
nameserver 8.8.4.4

組込みLinuxを進める時に役に立つかもしれない豆知識

Control Flow

Bootloaderのプロセスは

  • MLO/SPLが起動され以下のプログラムが実行される
    1. start.S (Assembly file)
      • /u-boot/arch/cpu/armv7/start.S
    2. head.S –u-boot
    3. misc.c —u-boot
    4. またhead.S —u-boot
    5. head-common.c —kernel
    6. main.c —kernel
    7. init —busybox
    8. rcS —ルートFS

bootm.c (U-BOOT)

  • bootmはDDRに先頭アドレス指定でロードしたuImageと.dtb(デバイスツリー)を指定してブートするメモリーブートコマンド。
  • u-boot-xxxxx/arch/arm/lib/bootm.cのboot_jump_linux()関数でkernelを起動するシーケンスが記述されている。
  • 上記関数内の kernel_entry(0, machid, r2); —Kernel にr2:FTD(.dtb)の場所を渡している
  • r1:machine ID
  • r2:DDRのFDTのアドレス(ポインタ)
  • r10は選択されたProcessorタイプの構造体を保持する。
ichiri@ichiri-VPCF128FJ:~/Downloads/u-boot-2017.05-rc2$ grep -r "bootm_headers_t" *
include/image.h:} bootm_headers_t;   // ここにあるのがわかる。 他にもいろいろ表示される。

head.S misc.c (Bootstrap loader)

  • .Sはアセンブラ言語(これはarch dependentなので/linux/arch下にある)
  • .cはC言語。 しかし、インラインにアセンブラが使われている標準CでないC言語
  • 上記の両方は、バイナリにビルドするためにGCC(GNU C Complier)が必要
  • この後使用するコンパイラはarm-linux-gnueabihf-

head.S

  • /linux/arch/arm/kernel/head.S(CPU専用のLow level(Assembly) CPU Initialization)
  • 今回のSoC AM3358 SoCのCPUはArm Cortex-A8なので、Arm用のhead.S
  • プロセッサーのアーキテクチャーを確認する
  • テーブルのページ初期化
  • プロセッサーのMMU(メモリ管理ユニット)の初期化
  • head.S内の重要ルーティン Start:

main.c in /linux/init

  • Architectureに依存しない初期化
  • 重要なファイル
  • *start_kernel()でいろいろな初期設定をする
  • kernel_init()で初期化で使用した関数のメモリーを開放する。
  • Initで使用するのはPID 1
  • 初期化のデバッグ時はカスタマイズしてデバッグプログラムを書き込んだりする場合もある。

U-BOOT確認

U-BOOTコマンド

ビルド時間やコンパイラのバージョンも見れるので、デバッグ時には便利でです。

=> vsersion
U-Boot 2022.10 (Feb 15 2023 - 11:54:00 +0900)

arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0
GNU ld (GNU Binutils for Ubuntu) 2.34

ルートFSマウント後

$ strings /dev/mtd0 | grep U-Boot  

コメント