手作り組み込み Linux -- BeagleBone Black 編


[手作り組み込み Linux](https://qiita.com/propella/items/58258651ccda4979207f) の続きです。次は本物の開発ボードである BeagleBone Black (別名 BBB) に手作り Linux をインストールします。

[BeagleBone Black 備忘録](https://qiita.com/propella/items/cc507da0f2e1b7b99809) のように BeagleBone Black と開発機を FTDI USB to Serial cable で接続した状態から作業します。

## BBB 用に crosstool-NG を使った ツールチェインの作成

まず、BBB 用にツールチェインを作成します。QEMU で作った `arm-unknown-linux-gnueabi` の代わりに `arm-arm-cortex_a8-linux-gnueabi` をカスタマイズして作ります。

```shell
cd ~/src/crosstool-ng/
bin/ct-ng distclean
bin/ct-ng show-arm-cortex_a8-linux-gnueabi
bin/ct-ng arm-arm-cortex_a8-linux-gnueabi
bin/ct-ng menuconfig
```

`menuconfig` でカスタマイズ画面になるので以下のように設定します。ツールチェインを書き込み可能にする所と、FPU の設定です。

* In Paths and misc options
    * Render the toolchain read-only (`CT_PREFIX_DIR_RO`): 無効 (ツールチェインを書き込み可能にする)
* In Target options
    * Use specific FPU (`CT_ARCH_FPU`): neon を入力 (FPU の名前を入力)
    * Floating point (`CT_ARCH_FLOAT_HW`): hardware (FPU) を選択

.config の差分

```shell
# CT_PREFIX_DIR_RO is not set
CT_ARCH_FPU="neon"
CT_ARCH_FLOAT_HW=y
```

ビルドします。例によって 30 分以上待ちます。

    bin/ct-ng build

FPU を有効にしたので、prefix は `arm-cortex_a8-linux-gnueabi` から `arm-cortex_a8-linux-gnuembihf` に変わりました。このツールチェインを使うには以下のように環境変数を設定します。

```shell
PATH=${HOME}/x-tools/arm-cortex_a8-linux-gnueabihf/bin/:$PATH
export CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-
export ARCH=arm
```

## BBB 用にブートローダー u-boot の作成

QEMU とは違って BBB ではカーネルの起動にブートローダーが必要です。ここでは標準的な [u-boot](https://u-boot.readthedocs.io/en/latest/) を使います。

```shell
cd ~/src
git clone git://git.denx.de/u-boot.git
cd u-boot
git checkout v2021.01
```

ビルドの前に設定を `config/` の中から設定を選びます。選び方がよくわからないのですが、`am335x_evm_defconfig` で良いそうです。https://source.denx.de/u-boot/u-boot/-/tree/v2021.01/board/ti/am335x に BBB 対応と書いてあります。特にカスタマイズ無しでそのままビルドします。

```shell
make am335x_evm_defconfig
make
```

ここで色んなファイルができますが、必要なのは以下の二つです。

* MLO: BBB の ROM に入っている最初のブートローダーが呼び出すプログラムです。u-boot をロードする役割があります。
* u-boot.img: u-boot 本体にヘッダがついた物です。

### SD カードの作成

SD カードを刺して `lsblk` でデバイス名を調べ、64MiB の FAT32 (bootable) と 1GiB の ext4 パーティションを作ります。ハマリ所としては、パーティションラベルに gpt (新しいパーティション方式) は使えなかったのでご注意下さい。[Linux の SD カードで遊ぶ - Qiita](https://qiita.com/propella/items/a1fec01458b204ebcc81) にパーティションの作り方を書きました。

```shell
cat <<EOT | sudo sfdisk /dev/sdc
label: dos
size=64MiB, name="boot", type=c, bootable
size=1GiB, name="rootfs"
EOT
sudo mkfs.vfat -F 16 -n BOOT /dev/sdc1
sudo mkfs.ext4 -L rootfs /dev/sdc2
```

最初のパーティションをマウントして MLO と u-boot.img をコピーします。

```shell
mkdir -p ~/mnt/boot
sudo mount -o uid=$UID /dev/sdc1 ~/mnt/boot/
cp MLO u-boot.img ~/mnt/boot/
sudo umount ~/mnt/boot/
```

### U-boot の動作確認

ここで早速作った u-boot を BBB で動かしてみます。開発機と BBB を USB シリアルケーブルで接続し、picocom かなんかでターミナルを開きます。

    picocom -b 115200 /dev/tty.usbserial-なんとか

電源を落とした BBB に SD カードを刺して、SD カードスロット近くの USER/BOOT ボタンを押しながら電源コードを刺します。すかさずシリアルでスペースを押すとコマンドが打てる画面になります。表示されたバージョンを確認すれば成功です。間違って GPT でパーティションを作ると古い U-boot が起動する事があります。

```shell
U-Boot SPL 2021.01 (May 01 2023 - 16:45:37 +0900)
Trying to boot from MMC1
...
Hit any key to stop autoboot:  0 
=>
```

## BBB 用に Linux カーネルを作成

次に BBB 用に Linux カーネルをビルドします。u-boot のビルドで紹介したように `PATH`, `CROSS_COMPILE`, `ARCH` を設定してから以下のようにビルドします。

```shell
cd ~/src/linux-stable
make mrproper
make multi_v7_defconfig
make -j4 zImage
make -j4 mudules
make dtbs
```

u-boot を入れた SD カードにカーネル zImage と dtb をコピーします。

```shell
sudo mount -o uid=$UID /dev/sdc1 ~/mnt/boot/
cp zImage dts/am335x-boneblack.dtb ~/mnt/boot/
sudo umount ~/mnt/boot/
```

USER/BOOT を押しながら BBB を起動して u-boot のコンソールの fatload コマンドでカーネルと dtb をメモリに読み込み、bootz コマンドでカーネルを起動します。

```shell
Boot# fatload mmc 0:1 0x80200000 zImage
Boot# fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb
Boot# setenv bootargs console=ttyO0,115200
Boot# bootz 0x80200000 - 0x80f00000
## Flattened Device Tree blob at 80f00000
   Booting using the fdt blob at 0x80f00000
   Loading Device Tree to 8ffee000, end 8ffff46e ... OK

Starting kernel ...
...
[    2.522542] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
```

カーネルがパニックすれば成功です。

## ルートファイルシステム作成

[ルートファイルシステムの作成](https://qiita.com/propella/items/58258651ccda4979207f#%E3%83%AB%E3%83%BC%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E4%BD%9C%E6%88%90) などを参考にルートファイルシステムを作ります。重複しますが、`~/rootfs/` にルートファイルシステムを作った後、以下のように uRamdisk ファイルを作ります。uRamdisk というのは initramfs.cpio.gz に u-boot ヘッダを付けた物らしいです。(実はよく分かっていない)

```shell
cd ~/rootfs
find . | cpio -H newc -ov --owner root:root > ../initramfs.cpio
cd ..
gzip initramfs.cpio
mkimage -A arm -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk
```

## BBB で手作り LInux を実行する

BBB を起動して u-boot のコンソールで以下のように入力すれば作成したルートファイルシステムをカーネルに与える事が出来ます。

```shell
fatload mmc 0:1 0x80200000 zImage
fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb
fatload mmc 0:1 0x81000000 uRamdisk
setenv bootargs console=ttyO0,115200 rdinit=/bin/sh
bootz 0x80200000 0x81000000 0x80f00000
```

毎回手で入力するのは面倒なので今回は自動起動をしてみましょう。uEnv.txt というファイルに `key=value` 形式でブート時に必要な情報を書き込みます。

```shell
cat <<EOT > uEnv.txt
bootargs=console=ttyO0,115200 rdinit=/bin/sh
uenvcmd=fatload mmc 0:1 0x80200000 zImage; fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb; fatload mmc 0:1 0x81000000 uRamdisk; bootz 0x80200000 0x81000000 0x80f00000
EOT
```

実はこの eEnv.txt は u-boot 本体ではなく、各プラットフォームごとに決まっている仕組みのようです。かなり探しましたが仕様のような物が見当たらなかったので、[uEnv.txt](https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition/blob/master/Chapter05/uEnv.txt) のサンプルを見ながら作りました。多分ですが、以下のような仕様なのでは無いかと思います。

* `key=value` のように書いてあると、`setenv key value` を実行したように変数が設定される。
* 最後に `uenvcmd=` に設定した内容が実行される。

ルートファイルシステムと uEnv.txt を SD カードにコピーしたら完成です。

```shell
sudo mount -o uid=$UID /dev/sdc1 ~/mnt/boot/
cp uRamdisk uEnv.txt ~/mnt/boot/
sudo umount ~/mnt/boot/
```

検索するとどうやら boot.scr というファイルに u-boot の初期化スクリプトを書けるようですが、私の所では `Wrong image format for "source" command` というエラーが出てうまく行きませんでした。

これで手作り Linux が起動して /bin/sh が使えるようになりました！

## 参考

* https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition
    * サポートサイト
* https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition/blob/master/Chapter05/uEnv.txt
    * uEnv.txt の例
* [AM335x Generation — Das U-Boot unknown version documentation](https://u-boot.readthedocs.io/en/latest/board/ti/am335x_evm.html)

## 宿題

* tftp を使って開発をスピードアップ。
* RAM filesystem ではなくファイルシステムをマウントしたい。
* /bin/sh ではなく init を実行する。
* ネットワークアクセスを有効にする。
* u-boot で色々遊ぶ。