TF-A と EDK II が Raspberry Pi 3 (RPi3) にポーティングされているということで、これらをビルドして Linux kernel の起動までの手順を追ってみました。
過去記事では、RPi3 では U-Boot からの kernel 起動、QEMU では TF-A/EDK II からの kernel 起動を試しています。
一部これらの手順は流用しますが、QEMU と RPi3 では異なる部分もあるため全て一から流してみます。
clone & build
QEMUの場合 は EDK II の FV (firmware volume) を BL33 イメージとして TF-A に指定して FIP に統合しましたが、ここでは TF-A の BLx イメージを EDK II の FV に統合することにします。
git clone https://github.com/ARM-software/arm-trusted-firmware cd arm-trusted-firmwaremake PLAT=rpi3 realclean make PLAT=rpi3 CROSS_COMPILE=aarch64-linux-gnu- all fip \ PRELOADED_BL33_BASE=0x30000 export ATF_BUILD_DIR=${PWD}/build/rpi3/release cd -
BL33 は含まないので、外部バイナリの位置を表す"PRELOADED_BL33_BASE"で offset を指定します。
この値は EDK II の RPi3 イメージ構成に含まれる "UEFI image" の位置を指します。
後で TF-A のビルド結果を参照するため、ATF_BUILD_DIR に入れておきます。ここには BLx イメージバイナリが含まれています。
EDK II リポジトリを clone します。RPi3 をターゲットにするため、新たに edk2-platforms と edk2-non-osi リポジトリを clone しています。これらは edk2/ 以下にある必要があります。
git clone https://github.com/tianocore/edk2 git checkout -b stable git reset --hard edk2-stable202002 cd edk2 git clone https://github.com/tianocore/edk2-platforms git clone https://github.com/tianocore/edk2-non-osi export PACKAGES_PATH=${PWD}:${PWD}/edk2-platforms:${PWD}/edk2-non-osi export GCC5_AARCH64_PREFIX=aarch64-linux-gnu-
おまじないを実行して EDK II をビルドします。
make -C BaseTools . edksetup.sh build -a AARCH64 -t GCC5 -p Platform/RaspberryPi/RPi3/RPi3.dsc \ -D TFA_BUILD_ARTIFACT=${ATF_BUILD_DIR}
TFA_BUILD_ARTIFACT マクロに TF-A のビルド結果ディレクトリとして ATF_BUILD_DIR を指定します。
このビルドで "edk2/Build/RPi3/DEBUG_GCC5/FV/RPI_EFI.fd" が生成されます。ここに TF-A で生成された BLx イメージも含まれています。
Linux build
Linux もビルドします。そのまま upstream kernel を持ってきた場合、upstream kernel に含まれる RPi3 は起動方法が "spin-table" になっているため、TF-A を介する場合は Sub CPU が起動できません。TF-A を介して起動する場合は "psci" に変更する必要があります。これは devicetree にて行います。
例として GitHub に新たに "psci" に対応するための RPi3 用 branch を置いています。以下の方法でビルドすると、追加した devicetree の bcm2837-rpi-3-b-tfa.dtb が得られます。
export ARCH=arm64 export CROSS_COMPILE=aarch64-linux-gnu- git clone https://github.com/kunih/linux.git -b rpi3-tfa --single-branch make defconfig make Image dtbs cd -
準備
全てのバイナリが用意出来たら、microSD による起動準備を行います。ここからは GitHub にあるドキュメントの手順に従います。
まず microSD を FAT32 でフォーマットしてマウントし、上記ビルドで生成したイメージ "RPI_EFI.fd" を microSD カードにコピーします。
※ PC 上の microSD のデバイス名 (以下 /dev/sdX) は dmesg などで確認してください。microSD 購入時はほぼ1パーティションが作成済のため、パーティションは存在する前提にしています。
sudo mkfs.vfat /dev/sdX1 sudo mount /dev/sdX1 /mnt sudo cp edk2/Build/RPi3/DEBUG_GCC5/FV/RPI_EFI.fd /mnt/
boot に必要なバイナリを RPi の GitHub から取得し、microSD カードにコピーします。
wget https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin wget https://github.com/raspberrypi/firmware/raw/master/boot/fixup.dat wget https://github.com/raspberrypi/firmware/raw/master/boot/start.elf sudo cp bootcode.bin fixup.dat start.elf /mnt/
さらに microSD 上に config.txt
を作成します。
arm_control=0x200
enable_uart=1
armstub=RPI_EFI.fd
disable_commandline_tags=2
ビルドした Linux kernel と devicetree、さらに ramdisk イメージを microSD にコピーします。ramdisk イメージは過去記事などを参照して入手してください。形式は cpio でも cpio.gz でも uboot image でも通ります。
sudo cp linux/arch/arm64/boot/Image /mnt/ sudo cp linux/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b-tfa.dtb /mnt/ sudo cp rootfs.cpio.gz /mnt/
全てをコピーしたら、microSD をアンマウントして PC から RPi3 本体に移します。
sudo umount /mnt
シリアルケーブルを PC に接続して端末ソフトを開いておきます。
起動
RPi3 に AC アダプタを接続して電源を入れます。
シリアル端末には TF-A が起動するログが出始めます。その後、EDK II の起動ログが出始め、HDMI 出力には大きな RPi ロゴが出てきます。
起動が完了すると EDK II の 標準ブートローダ画面が出てきますが、途中で [F1] を押すと EFI Shell が起動します。ここではブートローダは用意していないので、EFI Shell を起動するようにしてください。
arm64 の場合 "CONFIG_EFI_STUB" が有効になっていますが、この定義が有効になっていると kernel イメージをそのまま UEFI Application として扱うことができます。EFI Shell にて以下のように入力して kernel を起動します。
Shell> FS0: FS0:\> \Image dtb=\bcm2837-rpi-3-b-tfa.dtb initrd=\rootfs.cpio.gz console=ttyS1,115200 rootfstype=ramfs rdinit=/sbin/init rootwait debug
この例では、準備のところで microSD にコピーしたファイルを指定しています。
\Image | microSD 上の kernel イメージファイル (= EFI Application) |
dtb=\bcm2837-rpi-3-b-tfa.dtb | microSD 上の devicetree blob ファイル |
initrd=\rootfs.cpio.gz | microSD 上の rootfs イメージファイル |
上記以外は kernel command line としてそのまま取り込まれます。シリアル端子(microUSB)は ttyS1、HDMI 出力コンソールは tty0 に割り当てられているため、それに合わせて指定します。
console=ttyS1,115200 | コンソール出力をシリアル端子、115200bps に設定 |
rootfstype=ramfs | rootfs 種別に RAM filesystem を指定 |
rdinit=/sbin/init | RAM filesystem の初期起動プログラムを指定 |
rootwait | rootfs が使えるようになるまで待つ |
debug | ログレベルを debug に設定 |
うまく起動できるとおなじみの kernel message が console で指定したデバイスに表示されます。
参考
- https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/docs/plat/rpi3.rst
- https://github.com/tianocore/edk2-platforms/tree/master/Platform/RaspberryPi/RPi3
- https://www.kernel.org/doc/Documentation/efi-stub.txt
- https://www.workofard.com/2017/02/uefi-on-the-pi/
コメント