MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(U-Boot 標準編)


## はじめに

MPFS-DISCO-KIT(Microchip PolarFire SoC FPGA Discovery Kit) で動作する Ubuntu 22.04 を構築する方法をいくつかの記事に分けて説明します。

 * [イントロ編](https://qiita.com/ikwzm/items/1866dc3bb5292fd1f6e1)
 * [HSS(Hart Software Services)編](https://qiita.com/ikwzm/items/3d6d0a645dd60aba90d2)
 * __U-Boot 標準編 (この記事)__
 * [U-Boot bootmenu 改造編](https://qiita.com/ikwzm/items/72f75ec704dd227f9cbd)
 * [Linux Kernel linux4microchip+fpga-2024.09 編](https://qiita.com/ikwzm/items/0ca6aea057a3696bd5f0)
 * [Linux Kernel mpfs-fpga-first 編](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73)
 * [Ubuntu 22.04 Root File System 編](https://qiita.com/ikwzm/items/a3d1d5a1a9640b400872)
 * [SD-Card 作成編](https://qiita.com/ikwzm/items/19af3dd7a34ea5364683)
 * [SD-Card 起動編](https://qiita.com/ikwzm/items/42a1aa7b355734a813f8)

上記の記事で構築した MPFS-DISCO-KIT 向け Ubuntu 22.04 Root File System、Linux Kernel、Boot Loader は次の URL にて公開しています。なお、これらはオフシャルなものではなく、筆者の魔改造がはいっています。ご使用の際はこの点にご留意してください。

 * https://github.com/ikwzm/MPFS-FPGA-Ubuntu22.04
 * https://github.com/ikwzm/MPFS-FPGA-Linux-Kernel-6.6
 * https://github.com/ikwzm/MPFS-DISCO-KIT-U-Boot


この記事では、Microchip Technology 社が提供する U-Boot (u-boot-mchp) のビルドについて解説します。
なお、この記事で紹介した U-boot のビルド済みのファイル(uboot.imd、uboot.env、boot.scr) は以下の URL にあります。ビルドが面倒な方はどうぞ。

 * https://github.com/ikwzm/MPFS-DISCO-KIT-U-Boot/tree/v2024.09

## ダウンロード

### u-boot-mchp

Microchip Technology 社が提供する U-Boot は、以下の URL にあります。

 * https://github.com/linux4microchip/u-boot-mchp.git

この記事では、linux4microchip+fpga-2024.09 というタグのついたものを使用するので、ダウンロードしておきます。

```console
shell$ git clone --depth 1 --branch linux4microchip+fpga-2024.09 https://github.com/linux4microchip/u-boot-mchp.git u-boot-mchp-fpga-2024.09
```

### meta-polarfire-soc-yocto-bsp

上記のリポジトリの他に、meta-polarfire-soc-yocto-bsp からビルドの際に必要なファイルをダウンロードしておきます。必要なファイルの詳細な説明は後述します。

 * https://github.com/polarfire-soc/meta-polarfire-soc-yocto-bsp/tree/v2024.09/meta-polarfire-soc-bsp/recipes-bsp/u-boot/files/mpfs-disco-kit/

必要なのは、recipes-bsp/u-boot/files/mpfs-disco-kit/ の中身です。./files あたりにコピーしておきます。

```console
shell$ git clone --depth 1 --branch v2024.09 https://github.com/polarfire-soc/meta-polarfire-soc-yocto-bsp.git meta-polarfire-soc-yocto-bsp-v2024.09
shell$ cp -r meta-polarfire-soc-yocto-bsp-v2024.09/meta-polarfire-soc-bsp/recipes-bsp/u-boot/files/mpfs-disco-kit/ ./files
```

### hart-software-services

最終的に HSS からブートするためには、[HSS(Hart Software Services)編](https://qiita.com/ikwzm/items/3d6d0a645dd60aba90d2) で紹介した HSS Payload Generator が必要です。HSS Payload Generator は以下の URL にあります。必要に応じてダウンロードしておきます。

 * https://github.com/polarfire-soc/hart-software-services/tools/hss-payload-generator/

```console
shell$ git clone --depth 1 --branch v2024.09 https://github.com/polarfire-soc/hart-software-services.git hart-software-services-v2024.09
```

## ファイルの説明

今回は u-boot-mchp を単純に make するだけではなく、meta-polarfire-soc-yocto-bsp から幾つかファイルを追加してカスタマイズしています。ここではこれらのファイルの説明をします。

### mpfs-disco-kit.cfg

u-boot-mchp についてくる microchip_mpfs_discokit_defconfig(MPFS-DISCO-KIT 用の def_config)に加えて、あらたに変更＆追加するコンフィギュレーションです。

```config:files/mpfs-disco-kit.cfg
CONFIG_MPFS_PRIORITISE_QSPI_BOOT=n
CONFIG_ENV_IS_NOWHERE=n
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_FAT_INTERFACE="mmc"
CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"
CONFIG_ENV_FAT_FILE="uboot.env"
```

主に u-boot が最初に設定する環境変数に関する変更点が記述されています。
u-boot は起動後に環境変数を設定するのですが、環境変数を外部ファイルから読むことを指定しています。
ここでは、アクセスするファイルシステムを FAT に、デバイスを mmc に、ドライブを 0 に、パーティションを 1 に、ファイル名を uboot.env にしています。

### uEnv.txt

前述した uboot.env のソースコードです。uEnv.txt を mkenvimage というツールを使って uboot.env に変換します。

<details><summary>uEnv.txt (長いので折りたたみ)</summary>

```:files/uEnv.txt
arch=riscv
baudrate=115200
board=mpfs_discokit
board_name=mpfs_discokit
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootriscv64.efi; if fdt addr -q ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_efi_bootmgr=if fdt addr -q ${fdt_addr_r}; then bootefi bootmgr ${fdt_addr_r};else bootefi bootmgr;fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf}
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_syslinux_conf=extlinux/extlinux.conf
boot_targets= mmc0 dhcp
bootcmd=run distro_bootcmd
bootcmd_dhcp=devtype=dhcp; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00027:UNDI:003000;setenv bootp_arch 0x1b;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr -q ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=devnum=0; run mmc_boot
bootdelay=2
bootm_size=0x10000000
cpu=mpfs
design_overlays=if test -n ${no_of_overlays}; then setenv inc 1; setenv idx 0; fdt resize ${dtbo_size}; while test $idx -ne ${no_of_overlays}; do setenv dtbo_name dtbo_image${idx}; setenv fdt_cmd "fdt apply $"$dtbo_name; run fdt_cmd; setexpr idx $inc + $idx; done; fi;
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
efi_dtb_prefixes=/ /dtb/ /dtb/current/
fdt_addr_r=0x8a000000
fdtoverlay_addr_r=0x8a080000
kernel_addr_r=0x80200000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
loadaddr=0x80200000
mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi
mtdparts=mtdparts=spi-nand0:2m(payload),128k(env),119m(rootfs)
ramdisk_addr_r=0x8aa00000
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then part uuid ${devtype} ${devnum}:${distro_bootpart} distro_bootpart_uuid ; run scan_dev_for_boot; fi; done; setenv devplist
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;run boot_efi_bootmgr;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootriscv64.efi; then echo Found EFI removable media binary efi/boot/bootriscv64.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then echo Found ${prefix}${boot_syslinux_conf}; run boot_extlinux; echo EXTLINUX FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x8e000000
vendor=microchip
```

</details>


uEnv.txt では主にブートシーケンスの記述が含まれています。実はこのブートシーケンスは KV260 とほぼ同じです。詳しいブートシーケンスについては以下の記事を参照してください。

 * [『KV260 が Linux をブートするまでのシーケンス (U-Boot編)』 @Qiita](https://qiita.com/ikwzm/items/c261fe7c0d68c326db3d)

最終的に boot_a_script コマンドによりブートデバイスにある boot.scr の実行を試みます。

### boot.cmd

前述した boot.scr のソースコードです。boot.cmd を mkimage というツールを使って boot.scr に変換します。

```:boot.cmd
setenv fdt_high 0xffffffffffffffff
setenv initrd_high 0xffffffffffffffff

load mmc 0:${distro_bootpart} ${scriptaddr} fitImage
bootm start ${scriptaddr};
bootm loados ${scriptaddr};
# Try to load a ramdisk if available inside fitImage
bootm ramdisk;
bootm prep;
fdt set /soc/ethernet@20110000 mac-address ${discokit_mac_addr0};
run design_overlays;
bootm go;
```

ブートデバイスにある fitImage というファイルをロードしています。fitImage は u-boot で使用される FIT(Flattend Image Tree) というフォーマットのファイルです。このスクリプトでは、ロードした Device Tree になにやら ethernet の MAC Address を上書きしているようです。


### uboot.yaml

u-boot.bin を hss-payload-generator を使って uboot.img(HSS からブートできるイメージファイル)に変換する際に使用する設定ファイルです。

```yaml:uboot.yaml
#
# HSS Payload Generator
#

# First, we can optionally set a name for our image, otherwise one will be created dynamically
set-name: 'PolarFire-SoC-HSS::U-Boot'

#
# Next, we'll define the entry point addresses for each hart, as follows:
#
hart-entry-points: {u54_1: '0x80200000', u54_2: '0x80200000', u54_3: '0x80200000', u54_4: '0x80200000'}

#
# Finally, we'll define some payloads (source ELF files) that will be placed at certain regions in memory
# The payload section is defined with the keyword payloads, and then a number of individual
# payload descriptors.
#
# Each payload has a name (path to its ELF file), an owner-hart, and optionally 1-3 secondary-harts.
#
# Additionally, it has a privilege mode in which it will start execution.
#  * Valid privilege modes are PRV_M, PRV_S and PRV_U.
#
#
# In the following example:
#  * test/baremetal is assumed to be a bare metal application that runs in u54_3,
#    and expects to start in PRV_M
#  * test/u-boot is the Das U-Boot bootloader, and it runs on U54_1, U54_2 and U54_4.
#    It expects to start in PRV_S.
#
# Case only matters for the ELF path names, not the keywords.
#
payloads:
  u-boot.bin: {exec-addr: '0x80200000', owner-hart: u54_1, secondary-hart: u54_2, secondary-hart: u54_3, secondary-hart: u54_4, priv-mode: prv_s}
```
名前やブートするファイル名や各々の CPU が実行する際のエントリーポイントが記述されています。

## ビルド

### ディレクトリ構造とファイル

 * files/
   + boot.cmd
   + mpfs-disco-kit.cfg
   + uEnv.txt
   + uboot.yaml
 * u-boot-mchp-fpga-2024.09/
 * hart-software-services-v2024.09/
   + tools/
     - hss-payload-generator/
 * __uboot.img (ビルドした成果物)__
 * __uboot.env (ビルドした成果物)__
 * __boot.scr  (ビルドした成果物)__
  
### 環境設定

```console
shell$ cd u-boot-mchp-fpga-2024.09
shell$ export CROSS_COMPILE=riscv64-unknown-linux-gnu-
```

### uboot.img のビルド

#### hss-payload-generator のビルド

あらかじめ hss-payload-generator をビルドしておきます。

```console
shell$ make -C ../hart-software-services-v2024.09/tools/hss-payload-generator/ 
```

#### microchip_mpfs_discokit_defconfig のコンフィギュレーション

まずは u-boot-mchp にある microchip_mpfs_discokit_defconfig でコンフィギュレーションします。同時に scripts にある各種ツールもビルドされます。

```console
shell$ make microchip_mpfs_discokit_defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  YACC    scripts/kconfig/zconf.tab.c
  LEX     scripts/kconfig/zconf.lex.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#
```

#### files/mpfs-disco-kit.cfg のマージ

recipes-bsp/u-boot/files/mpfs-disco-kit/mpfs-disco-kit.cfg を .config にマージします。

```console
shell$ ./scripts/kconfig/merge_config.sh .config ../files/mpfs-disco-kit.cfg
Using .config as base
Merging ../files/mpfs-disco-kit.cfg
Value of CONFIG_MPFS_PRIORITISE_QSPI_BOOT is redefined by fragment ../files/mpfs-disco-kit.cfg:
Previous value: CONFIG_MPFS_PRIORITISE_QSPI_BOOT=y
New value: CONFIG_MPFS_PRIORITISE_QSPI_BOOT=n

Value of CONFIG_ENV_IS_NOWHERE is redefined by fragment ../files/mpfs-disco-kit.cfg:
Previous value: CONFIG_ENV_IS_NOWHERE=y
New value: CONFIG_ENV_IS_NOWHERE=n

Value of CONFIG_ENV_IS_IN_FAT is redefined by fragment ../files/mpfs-disco-kit.cfg:
Previous value: # CONFIG_ENV_IS_IN_FAT is not set
New value: CONFIG_ENV_IS_IN_FAT=y

scripts/kconfig/conf  --alldefconfig Kconfig
#
# configuration written to .config
#
Value requested for CONFIG_MPFS_PRIORITISE_QSPI_BOOT not in final .config
Requested value:  CONFIG_MPFS_PRIORITISE_QSPI_BOOT=n
Actual value:     # CONFIG_MPFS_PRIORITISE_QSPI_BOOT is not set

Value requested for CONFIG_ENV_IS_NOWHERE not in final .config
Requested value:  CONFIG_ENV_IS_NOWHERE=n
Actual value:     # CONFIG_ENV_IS_NOWHERE is not set
```

#### u-boot.bin のビルド

```console
shell$ make
   :
 (中略)
   :
  LD      u-boot
  OBJCOPY u-boot.srec
  OBJCOPY u-boot-nodtb.bin
  DTC     arch/riscv/dts/microchip-mpfs-disco-kit.dtb
  SHIPPED dts/dt.dtb
  CAT     u-boot-dtb.bin
  COPY    u-boot.bin
  SYM     u-boot.sym
  COPY    u-boot.dtb
  OFCHK   .config
```

#### uboot.img に変換

u-boot.bin を hss-payload-generator を使って uboot.img(HSS からブートできるイメージファイル)に変換します。

```console
shell$ ../hart-software-services-v2024.09/tools/hss-payload-generator/hss-payload-generator -c ../files/uboot.yaml -v ../uboot.img
```

### uboot.env

files/uEnv.txt (uboot.env のソースコード)を uboot.env(u-boot が直接扱う環境変数ファイル) に変換します。
その際、u-boot をビルドした際に設定した CONFIG_ENV_SIZE が必要です。

```console
shell$ grep CONFIG_ENV_SIZE .config
CONFIG_ENV_SIZE=0x2000
shell$ export CONFIG_ENV_SIZE=0x2000
shell$ tools/mkenvimage -s $CONFIG_ENV_SIZE ../files/uEnv.txt -o ../uboot.env
```

### boot.scr

files/boot.cmd(boot.scr のソースコード) を boot.scr(u-boot が直接扱うスクリプトファイル) に変換します。

```console
shell$ tools/mkimage -c none -A riscv -T script -d ../files/boot.cmd ../boot.scr
Image Name:   
Created:      Fri Nov 22 15:18:44 2024
Image Type:   RISC-V Linux Script (gzip compressed)
Data Size:    368 Bytes = 0.36 KiB = 0.00 MiB
Load Address: 00000000
Entry Point:  00000000
Contents:
   Image 0: 360 Bytes = 0.35 KiB = 0.00 MiB
```

## 参考

 * https://github.com/linux4microchip/u-boot-mchp
 * https://github.com/polarfire-soc/meta-polarfire-soc-yocto-bsp
 * https://github.com/polarfire-soc/hart-software-services
 * [『KV260 が Linux をブートするまでのシーケンス (U-Boot編)』 @Qiita](https://qiita.com/ikwzm/items/c261fe7c0d68c326db3d)