チュートリアル

2022年版U-bootの作り方

4~5年前に「ZYNQのLinuxが動くまで」という小冊子を執筆したのですが、なにぶん4~5年前のことなので、最新のu-boot-xlnxでは少しずつ違うところが出てきてしまい、当時のやり方どおりにu-bootをビルドしようとしてもうまくいかなくなってきました。

そこで、冊子の内容をアップデートする意味も兼ねて、最新のu-boot-xlnxをビルドしてみることにしました。

 

なお、u-boot-xlnxにはXILINXの純正評価ボードであるZC702や、XILINXと密接な関係にあるZYBOやZEDといったボードに関してはあらかじめ設定ファイルが用意されているのですが、皆様が本当に知りたいことは

「自作のZYNQボード用にどうやってU-BOOTを作ればよいか」

ということだと思いますので、この点に焦点をあてて解説していきます。

この記事では、XILINXの標準u-bootに入っていない「Zynqberry(ジンクベリー)」というRaspiberryPi形状のZYNQボードのu-bootを作ります。

Te0726_0_20220217150901

ZYBOやPYNQを卒業したい方や、自分でボードを作りたいけどPetaLinuxは使いたくないという方、中で何が起きているかを把握したいという方はぜひとも参考にしてみてください。

 

準備編

開発用マシンの準備

u-bootを作る際に初めにやらなければならないことはUbuntu 18以上のマシンを用意することです。

UbuntuはWSL2上のUbuntuでもよいし、AWSやさくらのクラウドでもよく、もちろんバーチャルマシンでも構いません。私はUbuntu 18と20で下記の手順でu-bootがビルドできることを確認していますが、CentOSやDebianなどその他のディストリビューションのことはわかりません。

XSDKのセットアップ

Ubuntu Linuxが用意できたらXILINXのXSDKを入れます。当方ではXSDK2018.3で確認しています。おそらく最新のVitisでもよいと思いますが試してはいません。

パスと環境変数の設定

XSDKをインストールしたら、下記のコマンドで環境をセットアップします。

$ export CROSS_COMPILE=arm-linux-gnueabihf-
$ source /tools/Xilinx/SDK/2018.3/settings64.sh

gitから最新のu-boot-xlnxをダウンロード

そして、Xilinxのgitからu-boot-xlnxをダウンロードして移動します。

$ git clone git://github.com/Xilinx/u-boot-xlnx.git
$ cd u-boot-xlnx

u-boot-xlnxのビルドに必要なツールのインストール

また、u-bootをコンパイルするには、以下のものが必要なので入れておきます。

$ sudo apt install build-essential
$ sudo apt install libssl-dev
$ sudo apt install bison
$ sudo apt install flex

build-essentialというのは、makeとかccなどビルドに必要なツール群です。

また、libssl-devが入っていないと、

In file included from tools/aisimage.c:9:0:
include/image.h:1133:12: fatal error: openssl/evp.h: No such file or directory
# include 
^~~~~~~~~~~~~~~

というエラーで止まります。

Image2_20220217150701

しばらくは、こういうエラーとの闘いが続きます。

 

最後のbisonとflexというのは字句解析ツールなのですが、これが入っていないと、

  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  YACC    scripts/kconfig/zconf.tab.c
/bin/sh: 1: bison: not found
scripts/Makefile.lib:222: ターゲット 'scripts/kconfig/zconf.tab.c' のレシピで失敗しました

というエラーが出てビルドに失敗してしまいます。2017年のu-bootでは求められなかったので、最近のアップデートで求められるようになったのでしょう。

 

また、ビルドするLinuxがUbuntu 16だと以下のエラーが出てうまくいきません。パッチを当てるなどいろいろ試したのですが、どうしてもビルドを通すことができませんでした。

HOSTLD tools/dumpimage
tools/lib/ecdsa/ecdsa-libcrypto.o: 関数 `prepare_ctx' 内:
ecdsa-libcrypto.c:(.text+0xcd): `OPENSSL_init_ssl' に対する定義されていない参照です
ecdsa-libcrypto.c:(.text+0x185): `EC_GROUP_order_bits' に対する定義されていない参照です
tools/lib/ecdsa/ecdsa-libcrypto.o: 関数 `ecdsa_check_signature.isra.3' 内:
ecdsa-libcrypto.c:(.text+0x3ad): `ECDSA_SIG_set0' に対する定義されていない参照です
tools/lib/ecdsa/ecdsa-libcrypto.o: 関数 `ecdsa_sign' 内:
ecdsa-libcrypto.c:(.text+0x497): `ECDSA_SIG_get0' に対する定義されていない参照です
ecdsa-libcrypto.c:(.text+0x4ae): `BN_bn2binpad' に対する定義されていない参照です
ecdsa-libcrypto.c:(.text+0x4c0): `BN_bn2binpad' に対する定義されていない参照です
tools/lib/ecdsa/ecdsa-libcrypto.o: 関数 `ecdsa_add_verify_data' 内:
ecdsa-libcrypto.c:(.text+0x68a): `EC_GROUP_order_bits' に対する定義されていない参照です
ecdsa-libcrypto.c:(.text+0x6d2): `EC_POINT_get_affine_coordinates' に対する定義されていない参照です
tools/lib/rsa/rsa-sign.o: 関数 `rsa_sign' 内:
rsa-sign.c:(.text+0x525): `OPENSSL_init_ssl' に対する定義されていない参照です
rsa-sign.c:(.text+0x756): `EVP_MD_CTX_reset' に対する定義されていない参照です
tools/lib/rsa/rsa-sign.o: 関数 `rsa_get_params' 内:
rsa-sign.c:(.text+0x953): `RSA_get0_key' に対する定義されていない参照です
rsa-sign.c:(.text+0x9a0): `RSA_get0_key' に対する定義されていない参照です
tools/lib/rsa/rsa-sign.o: 関数 `rsa_add_verify_data' 内:
rsa-sign.c:(.text+0xe01): `EVP_PKEY_get0_RSA' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
scripts/Makefile.host:104: ターゲット 'tools/dumpimage' のレシピで失敗しました
make[1]: *** [tools/dumpimage] エラー 1

標準構成をテストビルドする

この4つが入っていれば、

$ make xilinx_zynq_virt_defconfig
$ make

で、標準構成であるxilinx_zynq_virt_defconfig用のu-bootのビルドに成功するはずです。

2017年ごろのu-bootでは、ZYBOやMicrozedなどボードごとに設定ファイルが分かれていたのですが、2022年のu-bootではzynq_virtという一つのファイルにまとまっています。

まずは、環境が正しく作れたかどうかの確認としてxilinx_zynq_virt_defconfigをビルドしてみるのがよいでしょう。

自分のボード用のdefconfigを作る

defconfigを作る

そうしたら、自分のボード用のdefconfigを作るのですが、xilinx_zynq_virt_defconfigをひな形にして、ここにzynqberry_defconfigを作りましょう。defconfigはu-boot-xlnx/configs にあります。

$ cp configs/xilinx_zynq_virt_defconfig configs/zynqberry_defconfig

コピーしてきたdefconfigを変更する場所は3か所です。

CONFIG_DEFAULT_DEVICE_TREE="zynq-zc706"
CONFIG_OF_LIST="zynq-zc702 zynq-zc706 zynq-zc770-xm010 zynq-zc770-xm011 zynq-zc770-xm011-x16 zynq-zc770-xm012 zynq-zc770-xm013 zynq-cc108 zynq-microzed zynq-minized zynq-picozed zynq-zed zynq-zturn zynq-zturn-v5 zynq-zybo zynq-zybo-z7 zynq-dlc20-rev1.0"
CONFIG_ZYNQ_GEM=y

CONFIG_DEFAULT_DEVICE_TREE="zynq-zynqberry"
CONFIG_OF_LIST="zynq-zynqberry"
CONFIG_ZYNQ_GEM=n

にします。

できれば追加しておきたい項目として、

CONFIG_IDENT_STRING=" for TE0726"
CONFIG_SYS_PROMPT="Berry> "

も付けておくと、出来上がったときに違いがわかりやすくなります。

なお、CONFIG_DEFAULT_DEVICE_TREEで指定したデバイスツリーファイルを参照して周辺装置の設定をします。CONFIG_OF_LISTを変更しないと

Device Tree Source (arch/arm/dts/zynq-zynqberry.dtb) is not correctly specified.
Please define 'CONFIG_DEFAULT_DEVICE_TREE'
or build with 'DEVICE_TREE=' argument

というエラーで止まります。

デバイスツリーを作る 

次に、defconfig内のCONFIG_DEFAULT_DEVICE_TREEで指定したzynq-zynqberryという名前のデバイスツリーを作ります。u-boot用のデバイスツリーはarch/arm/dts/に置くことになっているので、zynq-zynqberry.dtsをここに置きます。

このdtsファイルを置いたらarch/arm/dts/Makefileを編集し、324行目あたりにzynq-zynqberry.dtbがビルドされるように登録します。

最後のzynq-zybo-z7.dtbの後ろに\を置いて1行挿入すればよいでしょう。

zynq-zturn-v5.dtb \
zynq-zybo.dtb \
zynq-zybo-z7.dtb \
zynq-zynqberry.dtb
dtb-$(CONFIG_ARCH_ZYNQMP) += \

ビルドする

これでビルドするとu-boot.elfが出来上がります。ビルドは make のみで行えます。

Image3

 

boot.binを作る

以下のような内容でmkboot.bifというファイルを作ります。

the_ROM_image:
{
  [bootloader]fsbl_myhdf.elf
  zynqberrydemo2.bit
  u-boot.elf
}

別途作っておいたfsblとbitとbootgenで結合します。

bootgen -w -image mkboot.bif -o i boot.bin

でboot.binが生成されます。

起動の確認

このboot.binをQSPI ROMに書き込めば、自分で作成したu-bootで起動するようになるというわけです。

Kidou

Model名はデバイスツリーに書かれた名前になり、DRAMサイズもデバイスツリーで指定します。ボードごとの差異はデバイスツリーに書くのがいまどきのようです。

ただし一つだけ問題があります。このu-bootは自動で起動しないのです。

カーネルロードの調整

printenvで見て見ると、

bootcmd=run distro_bootcmd
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
boot_targets=qspi jtag mmc0 mmc1 qspi nand nor usb0 usb1 pxe dhcp

となっています。bootcmdはdistro_bootcmdを呼び出し、distro_bootcmdはbootcmd_qspi、bootcmd_jtag、bootcmd_mmc0・・・などを順番に試していくというスキームなのですが、最後までLinuxが起動してくれません。

Bootfail

もちろん、u-bootのコンソールで

fatload mmc 0 0x03000000 uImage && fatload mmc 0 0x02A00000 devicetree.dtb && bootm 0x03000000 - 0x02A00000

と入力すればLinuxは起動してくれるので、上のコマンドでbootcmdを上書きしてしまいましょう。

あまりエレガントではありませんが、include/configs/zynq-common.h を書き換えます。

200行目付近にCONFIG_EXTRA_ENV_SETTINGSというマクロ定義があるので、

#define CONFIG_EXTRA_ENV_SETTINGS       \
        "scriptaddr=0x20000%MAIN_CONTENTS%"  \
        "script_size_f=0x40000%MAIN_CONTENTS%"       \
        "fdt_addr_r=0x1f00000%MAIN_CONTENTS%"        \
        "pxefile_addr_r=0x2000000%MAIN_CONTENTS%"    \
        "kernel_addr_r=0x2000000%MAIN_CONTENTS%"     \
        "scriptaddr=0x3000000%MAIN_CONTENTS%"        \
        "ramdisk_addr_r=0x3100000%MAIN_CONTENTS%"    \
        BOOTENV                         \
        "bootcmd=fatload mmc 0 0x03000000 uImage && fatload mmc 0 0x02A00000 " \
        "devicetree.dtb && bootm 0x03000000 - 0x02A00000%MAIN_CONTENTS%"

にします。

強制的にbootcmdを書き換えて、distro_bootcmdを起動させることなくSDカードからカーネルを読み込ませました。

これでもう、自作ボードであってもPetalinuxを使うことなく、u-bootを作成できますね。