ZRouterで作るU-Bootイメージ

ZRouterではU-Bootで利用できるFlashイメージファイルを作成できます。いくつかのパターンを紹介します。

FreeBSDではu-boot用のブートローダーがあるようですが、私が扱っているターゲットはFlashが小さかったりするので、それは使わず古くからあるu-bootからkernelを直接起動する方法を使っています。

cfiのflashでflashから直接起動できるようにビルドする方法もあるようです。ただspiでは使えなかったり、圧縮してないカーネルの必要がありサイズが大きくなり現実的ではないので、すべてのターゲットでkernelをSDRAMにロードして実行しています。

最近のU-BOOTはELFを直接起動できますが、古いU-BOOTは対応していないケースもあり、objdumpでkbinにしています。

イメージをFlashに焼く前にKernelイメージをメモリにロードして実行するとデバイスの認識の確認ができます。

U-Bootをつぶしてしまうと文鎮（煉瓦）にになってしまうので、焼くときには十分に注意してください。

作成するイメージはboardディレクトリの下にあるターゲットのboard.mkで設定されます。

外部のSDやUSBメモリから起動できるU-Bootもありますが、ここで書いているのはFlashだけでブートする方法になります。

```mf:board.mk
PACKING_KERNEL_IMAGE?=kernel.kbin.oldlzma.uboot.sync
PACKING_ROOTFS_IMAGE?=rootfs_clean.iso.ulzma
```

このように設定していると、kernelはkbin,oldlzma,uboot,syncの順で変換されます。rootfsも同じようにiso,ulzmaの順に変換されます。変換されたkernelとrootfsをcatして実際にflashに焼いて利用できるイメージとしています。変換のためのMakefileはshare/mk/convertersの下にあります。

kernelの圧縮形式の対応はu-bootビルド時の設定によっていて、gz,lzma,bzip2などがあります。lzmaは古いターゲットで問題があったためか古いlzmaのコマンドがZRouterに入ってます。

通常は変換のためのコマンドはホストにpkgなどでインストールされたコマンドではなくZRouterに入っている物を使っています。これはホストの状況に依存せずにビルドできるようにしたかったためではないかと思われます。

![ubootimg1.png](https://qiita-image-store.s3.amazonaws.com/0/104066/29aa8e36-090e-dd4f-2f58-b64c10918e7f.png)

Null Paddingはsyncの変換でFlashのブロックサイズに境界を合わせるためのものです。ブロックサイズはPACKING_KERNEL_ROUNDで指定しますが、指定されていない場合は64Kバイトとなります。

u-bootイメージを作るときに引数でカーネルのエントリーポイントを指定するのですが、リンクしてできたカーネルのelfのそれと一致してないと起動できません。ただしarmの場合kvaでリンクしているので、elfのエントリーポイントのkvaの物理アドレスのになります。元々のZRouterでは変数で固定のアドレスをしていていたのですが、カーネルのコンフィグレーションやコンパイラにより変わって起動しなくなるので、readelfの値から自動設定できるように修正しました。


```
microserver % readelf -h Fon_FON2305E_FDT_kernel
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 09 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - FreeBSD
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x80001100
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4638784 (bytes into file)
  Flags:                             0x50001001, noreorder, o32, mips32
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         32
  Section header string table index: 29
```

設定されたイメージファイルをfileコマンドで確認すると以下のようになります。

```
microserver % file Fon_FON2305E_FDT_kernel.kbin.oldlzma.uboot
Fon_FON2305E_FDT_kernel.kbin.oldlzma.uboot: u-boot legacy uImage, FreeBSD Kernel
 Image, Linux/MIPS, OS Kernel Image (lzma), 1109483 bytes, Mon Apr 10 06:37:22 2
017, Load Address: 0x80001100, Entry Point: 0x80001100, Header CRC: 0x4054D880, 
Data CRC: 0x14411264
```

ELFのエントリーポイントとu-bootのエントリーポイントは以下のような関係にあります。

||ELF Entry point address|u-boot Entry Point|
|:--|:--|:--|
|ARM|0xc00500e0|0x000500E0|
|MIPS|0x80001100|0x80001100|

ほとんどのARMの物理アドレスは0x00000000でMIPSは0x80000000ですが、たまに違う物もあります。

U-Bootが利用するのはkernelのU-Bootイメージだけでrootfsにはアクセスしません。

kernelはrootfsがどこにあるか知る必要があるので、hintsベースのgeom_flashmapやfdtのgeom_mapを利用します。geom_flashmapは先頭を検索する機能が実装されていますが、geom_mapにはその機能がないので、一度ビルドしてサイズを確認してdtsにrootfsの位置を設定する必要があります。この場合カーネルのコンフィグレーションを変えるとサイズが変わり位置がずれてrootfsがマウントできなくなる事があり注意が必要です。

古いU-BootやU-Bootビルド時のコンフィグレーションによってはoldlzma(lzma)が使えない事もあるので、その時はgzを利用します。

U-Bootは環境変数で、bootの設定を行えます。ところがそれが使えなく、また先頭ではないところにU-Bootイメージがなければいけないターゲットがありました。そのために以下のオプションを追加しました

```mf:board.mk
UBOOT_HEAD_WHITESPACE=20
```
この設定を行うと先頭に20バイトの空白をいれてu-bootイメージを作成します。

![ubootimg2.png](https://qiita-image-store.s3.amazonaws.com/0/104066/0bf12311-39c7-3c9f-c3aa-1db1db2d996f.png)

MarvellのU-BootなどではU-Bootイメージではなくcramfsのファイルシステムの中のkernelファイルをロードしてブートします。このようなターゲットのイメージを作成する場合は以下のように設定します。

```mf:board.mk
PACKING_KERNEL_IMAGE?=kernel.kbin.cramfs
PACKING_ROOTFS_IMAGE?=rootfs_clean.iso.ulzma
```

![ubootimg3.png](https://qiita-image-store.s3.amazonaws.com/0/104066/ec91ae8d-c876-aaef-ef8f-d4dc160dc2f2.png)

このケースでは変換にmkcramfsコマンドを使用しているのですが、このコマンドはZRouterには含まれていないのでpkgでホストシステムにインストールが必要です。

TP-LinkのU-Bootは特殊な形式のイメージを利用していて、古い形式と新しい形式があります。

```mf:board.mk
PACKING_KERNEL_IMAGE=kernel.kbin.oldlzma
PACKING_ROOTFS_IMAGE?=rootfs_clean.iso.ulzma

IMAGE_SUFFIX=bin
NEW_IMAGE_TYPE=tplink_new_image

TPLINK_CONFIG_STYLE=NEW

TPLINK_ROOTFS_START="0x00180000"

# New-style board config
TPLINK_HARDWARE_ID=0x04700001
TPLINK_HARDWARE_VER=1
TPLINK_HARDWARE_FLASHID=16M
TPLINK_FIRMWARE_RESERV=0x20000

KERNEL_MAP_START=0x00020000
```

Onion Omegaの設定ですが、このようにするとTP Linkの新しいタイプのイメージが作成できます。

これまでLoad addressとEntory pointが同じケースだけだったのですが、armv7でkernel elfの先頭にbuild idが付くようになって違うようになりました。

KERNEL_HAS_BUILDIDを定義するとEntory pointから計算してくれるようにしました。

```
% file Pcduino_Lite_kernel.kbin.gz.uboot
Pcduino_Lite_kernel.kbin.gz.uboot: u-boot legacy uImage, FreeBSD Kernel Image, L
inux/ARM, OS Kernel Image (gzip), 1684570 bytes, Mon Apr 15 01:53:10 2019, Load 
Address: 0x40200154, Entry Point: 0x40200180, Header CRC: 0xFF6EE122, Data CRC: 
0xD43BB6D0
```