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を作ります。
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^~~~~~~~~~~~~~~
というエラーで止まります。
しばらくは、こういうエラーとの闘いが続きます。
最後の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 のみで行えます。
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で起動するようになるというわけです。
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が起動してくれません。
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を作成できますね。