KV260 が Linux をブートするまでのシーケンス


KV260 に載っている ZynqMP のブートシーケンスは次の手順で進みます。詳しいことは参考資料[「ZynqMPのブートとパワーマネージメント」](https://www.slideshare.net/ssuser479fa3/zynq-mp-58490589)を参照してください。これは[「ZynqMP 勉強会」](https://connpass.com/event/26064/)で @Vengineer さんが発表した資料で、ZynqMP のブートシーケンスが詳しく説明されています。

## ・ステージ0 (内部ROM)

ZynqMP のステージ0ブートローダーは内部ROMにあります。ステージ0ブートローダーは PMU(Platform Management Unit) が実行します。PMU はシステム全体のリソースのパワーアップ、リセットの制御および監視を担当します。PMU はこのため専用のプロセッサで、実態は MicroBlaze という Xilinx が開発したプロセッサのようです。ステージ0ブートローダーは、プライマリブートデバイスにある BOOT.BIN に含まれるステージ1ブートローダー(FSBL) を内部RAMをロードして Coretex-A53 を起動します。

## ・ステージ1~3 BOOT.BIN (プライマリブートデバイス)

KV260 のプライマリブートデバイスは SOM にある QSPI に接続されたフラッシュメモリです。このプライマリブートデバイスに BOOT.BIN があります。

より正確には KV260 のプライマリーデバイスには先頭アドレスに Image Selector があります。まずこの Image Selector が内部メモリにロードされて Coretex-A53 が実行します。Image Selector の目的は、同じプライマリーデバイスにある２つ(リカバリーを含めると３つ）の BOOT.BIN を選択することです。詳細は[『KV260 が Linux をブートするまでのシーケンス (Image Selector編)』](https://qiita.com/ikwzm/items/a9a8d1946fa5899742bd)を参照してください。

Image Selector によって選択された BOOT.BIN には FSBL(First Stage Boot Loader)、PMUFW(Platform Management Unit FirmWare)、BL31(ARM Trusted Firmware Boot Loader Stage 3-1)、U-Boot が含まれています。

### ・ステージ1 (FSBL - First Stage Boot Loader)

ステージ1ブートローダー(FSBL) は内部RAMにロードされます。ステージ1ブートローダー(FSBL)は Coretex-A53 が実行します。ステージ1ブートローダー(FSBL) は次の処理を行います。

 * PS(Processing System) の初期設定
 * BOOT.BIN に含まれる Bitstream を PL(Programmable Logic) へコンフィギュレーション    
   (ただし KV260 の QSPI にある BOOT.BIN には Bitstream は含まれていないため、このステップは無い模様)
 * BOOT.BIN に含まれるステージ2ブートローダー(BL31)を SDRAM にロード
 * BOOT.BIN に含まれるステージ3ブートローダー(U-Boot)を SDRAM にロード
 * BOOT.BIN に含まれるPMUFW(Platform Manager Unit Firmware)を PMU の RAM にロード
 * ステージ2ブートローダー(BL31)に制御を移す

### ・ステージ2 (BL31 - ARM Trusted Firmware Boot Loader Stage 3-1)

ステージ2ブートローダー(BL31) は SDRAM にロードされます。BL31 は ARM が提供している ATF(ARM Trusted Firmware) の EL3 Runtime Firmware です。ATF に関しては次の URL を参照してください。

 * [https://github.com/ARM-software/arm-trusted-firmware](https://github.com/ARM-software/arm-trusted-firmware)

ステージ2ブートローダー(BL31)は Coretex-A53 が実行します。ステージ2ブートローダー(BL31)は自分自身を初期化してステージ3ブートローダー(U-Boot)に制御を移した後でも RAM に常駐して各種サービスを提供します。

### ・ステージ3 (U-Boot)

ステージ3ブートローダー(U-Boot)はファイルシステムを使えたり、スクリプトや環境変数などが使えるので、その設定にしたがって、Linux のカーネルイメージ、Device Tree、場合によってはルートファイルシステムを SDRAMに ロードします。その後、Linuxカーネルイメージに制御を移します。

KV260 の QSPI にある U-Boot は正常に起動すると UART に次のようなメニューを表示します。このメニュー表示後５秒間なにも入力が無いと自動的に「Auto-Select - 1.CC boot 2.SOM boot」に移行します。

![Fig.1 U-Boot Menu](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/35e01bc1-b4a0-a038-97f1-b33ed9e86e92.png)


Auto-Select はセカンダリブートデバイス(KV260の場合は SD-Card)にある boot.scr を呼び出します。

ステージ3ブートローダー(U-Boot)によるブートシーケンスは[『KV260 が Linux をブートするまでのシーケンス (U-Boot編)』](https://qiita.com/ikwzm/items/c261fe7c0d68c326db3d)でもう少し詳細に説明しています。


## ・ステージ4 SD Card (セカンダリブートデバイス)

KV260 のセカンダリーブートデバイスは SD-Card です。

### boot.scr

U-Boot はセカンダリーブートデバイス(KV260の場合は SD-Card)にある boot.scr を呼び出します。

boot.scr は u-boot 専用のスクリプトファイルです。これは一見バイナリファイルに見えますが、実は Petalinux などが生成した boot.scr の場合は特にエンコードされていないので、cat で中を見ることができます。なお、boot.scr の内容はディストリビューションによって異なります。ここでは[「UltraZed/Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux (v2021.1版) ブートイメージの提供」](https://qiita.com/ikwzm/items/a9adc5a7329b2eb36895) で紹介した KV260 用 boot.scr について説明します。

```console
fpga@debian-fpga:/mnt/boot$ file boot.scr
boot.scr: u-boot legacy uImage, Boot script, Linux/ARM, Script File (Not compressed), 2530 bytes, Tue Apr  5 23:00:00 2011, Load Address: 0x00000000, Entry Point: 0x00000000, Header CRC: 0x5C38692B, Data CRC: 0x85779C72
```

```console
fpga@debian-fpga:~$ cat /mnt/boot/boot.scr
'V\8i+M・       窈rBoot script  ﾚ# This is a boot script for U-Boot
# Generate boot.scr:
# mkimage -c none -A arm -T script -d boot.cmd.default boot.scr
#
################


for boot_target in ${boot_targets};
do
        echo "Trying to load boot images from ${boot_target}"
        if test "${boot_target}" = "jtag" ; then
                booti 0x00200000 0x04000000 0x00100000
        fi
        if test "${boot_target}" = "mmc0" || test "${boot_target}" = "mmc1" ; then
                if test -e ${devtype} ${devnum}:${distro_bootpart} /uEnv.txt; then
                        fatload ${devtype} ${devnum}:${distro_bootpart} 0x00200000 uEnv.txt;
                        echo "Importing environment(uEnv.txt) from ${boot_target}..."
                        env import -t 0x00200000 $filesize
                        if test -n $uenvcmd; then
                                echo "Running uenvcmd ...";
                                run uenvcmd;
                        fi
                fi
                if test -e ${devtype} ${devnum}:${distro_bootpart} /image.ub; then
                        fatload ${devtype} ${devnum}:${distro_bootpart} 0x10000000 image.ub;
                        bootm 0x10000000;
                fi
                if test -e ${devtype} ${devnum}:${distro_bootpart} /Image; then
                        fatload ${devtype} ${devnum}:${distro_bootpart} 0x00200000 Image;;
                fi
                if test -e ${devtype} ${devnum}:${distro_bootpart} /system.dtb; then
                        fatload ${devtype} ${devnum}:${distro_bootpart} 0x00100000 system.dtb;
                fi
                if test -e ${devtype} ${devnum}:${distro_bootpart} /ramdisk.cpio.gz.u-boot && test "${skip_tinyramdisk}" != "yes"; then
                        fatload ${devtype} ${devnum}:${distro_bootpart} 0x04000000 ramdisk.cpio.gz.u-boot;
                        booti 0x00200000 0x04000000 0x00100000
                fi
                if test -e ${devtype} ${devnum}:${distro_bootpart} /rootfs.cpio.gz.u-boot && test "${skip_ramdisk}" != "yes"; then
                        fatload ${devtype} ${devnum}:${distro_bootpart} 0x04000000 rootfs.cpio.gz.u-boot;
                        booti 0x00200000 0x04000000 0x00100000
                fi
                booti 0x00200000 - 0x00100000
        fi
        if test "${boot_target}" = "xspi0" || test "${boot_target}" = "qspi" || test "${boot_target}" = "qspi0"; then
                sf probe 0 0 0;
                sf read 0x10000000 0xF40000 0x6400000
                bootm 0x10000000;
                echo "Booting using Fit image failed"

                sf read 0x00200000 0xF00000 0x1D00000
                sf read 0x04000000 0x4000000 0x4000000
                booti 0x00200000 0x04000000 0x00100000;
                echo "Booting using Separate images failed"
        fi
        if test "${boot_target}" = "nand" || test "${boot_target}" = "nand0"; then
                nand info;
                nand read 0x10000000 0x4180000 0x6400000
                bootm 0x10000000;
                echo "Booting using Fit image failed"

                nand read 0x00200000 0x4100000 0x3200000
                nand read 0x04000000 0x7800000 0x3200000
                booti 0x00200000 0x04000000 0x00100000;
                echo "Booting using Separate images failed"
        fi
done
```

### uEnv.txt

前節で紹介した boot.scr は、セカンダリーブートデバイスが SD-Card だった場合、まずブートパーティションに uEnv.txt があるかどうかチェックします。uEnv.txt は環境変数を設定するためのファイルです。もし uEnv.txt があった場合は、その内容を読み込んで環境変数を設定します。

uEnv.txt に環境変数 uenvmd が定義されていた場合はそれを実行します。

例えば次のような uEnv.txt があった場合、SD-Card にある image-5.10.0-xlnx-v2021.1-zynqmp-fpga を Linux Kernel Image として、また devicetree-5.10.0-xlnx-v2021.1-zynqmp-fpga-kv260-revB.dtb を Device Tree Blob として、/dev/mmcblk1p2 をルートファイルシステムとして Linux をブートします。

```text:uEnv.txt
########################################################################
# Linux Kernel Files
#  * linux_kernel_image : Linux Kernel Image File Name
#  * linux_fdt_image    : Linux Device Tree Blob File Name
########################################################################
linux_kernel_image=image-5.10.0-xlnx-v2021.1-zynqmp-fpga
linux_fdt_image=devicetree-5.10.0-xlnx-v2021.1-zynqmp-fpga-kv260-revB.dtb

########################################################################
# Linux Boot Argments
#  * linux_boot_args_console : ex) console=tty1
#                                  console=ttyPS1,115200
#  * linux_boot_args_rootfs  : ex) root=/dev/mmcblk1p2 rw rootwait
#  * linux_boot_args_systemd : ex) systemd.unit=graphical.target
#                                  systemd.unit=multi-user.target
#  * linux_boot_args_cpuidle : ex) cpuidle.off=1
#  * linux_boot_args_cma     : ex) cma=256M
#  * linux_boot_args_uio     : ex) uio=uio_pdrv_genirq.of_id=generic-uio
#  * linux_boot_args_other   :
########################################################################
linux_boot_args_console=console=ttyPS1,115200
linux_boot_args_rootfs=root=/dev/mmcblk1p2 rw rootwait
linux_boot_args_systemd=systemd.unit=multi-user.target
linux_boot_args_cpuidle=cpuidle.off=1
linux_boot_args_cma=1000M
linux_boot_args_uio=uio_pdrv_genirq.of_id=generic-uio
linux_boot_args_other=

########################################################################
# uenvcmd : During sdboot, if uenvcmd is set, uenvcmd will be executed.
########################################################################
linux_img_load_cmd=fatload ${devtype} ${devnum}:${distro_bootpart} 0x00200000 ${linux_kernel_image}
linux_fdt_load_cmd=fatload ${devtype} ${devnum}:${distro_bootpart} 0x00100000 ${linux_fdt_image}
linux_load_cmd=env run linux_img_load_cmd && env run linux_fdt_load_cmd
linux_boot_cmd=booti 0x00200000 - 0x00100000
linux_args_cmd=if env exists linux_boot_args; then; setenv bootargs ${linux_boot_args}; else; setenv bootargs ${linux_boot_args_console} ${linux_boot_args_rootfs} ${linux_boot_args_systemd} ${linux_boot_args_cpuidle} ${linux_boot_args_cma} ${linux_boot_args_uio} ${linux_boot_args_other}; fi
uenvcmd=env run linux_args_cmd && env run linux_load_cmd && env run linux_boot_cmd
```

### image.ub

uEnv.txt に uenvcmd が定義されていなかった場合、boot.scr は ブートパーティションに image.ub があるかチェックします。image.ub は Linux Kernel や Device Tree Blob をまとめた u-boot のブートイメージです。もし image.ub があれば、それをロードして実行します。

### Image

image.ub が無かった場合、boot.scr はブートパーティションに Image があるかチェックします。Image は Linux Kernel のイメージです。Image があれば、それをロードします。

### system.dtb

image.ub が無かった場合、boot.scr はブートパーティションに system.dtb があるかチェックします。system.dtb は Linux Kernel をブートする際のデバイスツリーです。system.dtb があれば、それをロードします。

### ramdisk.cpio.gz.u-boot or rootfs.cpio.gz.u-boot

image.ub が無かった場合、boot.scr はブートパーティションに ramdisk.cpio.gz.u-boot または rootfs.cpio.gz.u-boot があるかチェックします。ramdisk.cpio.gz.u-boot および rootfs.cpio.gz.u-boot は Linux のルートファイルシステムです。ramdisk.cpio.gz.u-boot または rootfs.cpio.gz.u-boot があればそれをロードします。そして 前節、前前節でロードした Image と system.dtb をブートします。

ramdisk.cpio.gz.u-boot および rootfs.cpio.gz.u-boot が無かった場合は、bootargs で設定された root パラメーターにしたがってルートファイルシステムを設定します。例えばこの記事で紹介した uEnv.txt だと ```root=/dev/mmcblk1p2 rw rootwait``` なので SD-Card の第二パーティションをルートファイルシステムとしてマウントします。

## 参考

 * [「ZynqMPのブートとパワーマネージメント」 @Vengineer ZynqMP勉強会資料(2016/2/20)](https://www.slideshare.net/ssuser479fa3/zynq-mp-58490589)
 * [「UltraZed/Ultra96/Ultra96-V2/KV260 向け Debian GNU/Linux (v2021.1版) ブートイメージの提供」@Qiita](https://qiita.com/ikwzm/items/a9adc5a7329b2eb36895)
 * [「Ultra96 向け Debian GNU/Linux (v2018.2版) の構築(Boot Loader編)」@Qiita](https://qiita.com/ikwzm/items/9c78e83298906447fe5d)
 * [「Kria KV260 ビジョン AI スターターキット ユーザーガイド UG1089 (v1.1) 2022年3月16日」](https://japan.xilinx.com/content/dam/xilinx/support/documents/user_guides/som/1_1/j_ug1089-kv260-starter-kit.pdf)
 * [「Zynq UltraScale+ MPSoC Technical Reference Manual, UG1085(v1.7) December 22,2017」](https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf)
 * [『KV260 が Linux をブートするまでのシーケンス (U-Boot編)』@Qiita](https://qiita.com/ikwzm/items/c261fe7c0d68c326db3d)
 * [『KV260 が Linux をブートするまでのシーケンス (Image Selector編)』@Qiita](https://qiita.com/ikwzm/items/a9a8d1946fa5899742bd)
 * [『KV260 の QSPI フラッシュメモリをのぞいてみる』@Qiita](https://qiita.com/ikwzm/items/67902c8e1446c13e8d81)
 * [『KV260 の ブートファームウェアを更新』@Qiita](https://qiita.com/ikwzm/items/4e5e954eabc988bd78d5)