みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

YoctoProject fitImageを試す

はじめに

バイスツリーとカーネルのイメージが通常別々のファイルとして作成され、u-bootなどのブートローダに設定して起動するケースが多い。

ブートに必要なファイルが複数存在すると管理が煩雑になるため、1つのファイルにまとまっている方が扱いやすい。

fitImageを使用することで以下のものを結合し1つのファイルとして取り扱えるようになる。

initramfsに関してはカーネルをビルドする場合に結合することもできるが、別々に作成したものをfitImageとして結合することも可能となっている。 今回はそのあたりも実験してみる。

使用するYoctoProjectのバージョンはkirkstone。ターゲットボードはBeagleBone Blackとする。

fitImageについて

fitImageに関する説明は Flattened uImage Tree (FIT) Imagesがわかりやすい。

fitImageを作成するには、its(Image Tree Source)というデバイスツリーの形式で記述されたテキストファイルを作成する。このファイルをmkimageコマンドに与えることによって、itsに従って必要なファイルを1つのファイルに結合する。 結合されたそれぞれのファイルがメモリ上のどの位置にロードされるべきかなどの情報もitsに記述しておく。これによってブートローダはどのデータをどこにロードするのかを判断することができる。

YoctoProjectでのfitImage

local.confなどのconfファイルで下記のような設定を記述するとbitbakeでfitImageを作成することができる。

KERNEL_IMAGETYPES:append = " fitImage"
KERNEL_CLASSES += "kernel-fitimage"

KERNEL_DEVICETREEやUBOOT_LOADADDRESS、UBOOT_ENTRYPOINTなどの変数を参照し、itsも自動的に生成されるようになっている。 実際にfitImageを生成する処理はkernel-fitimage.bbclassで行われる。 詳細はmegamanualの5.56 kernel-fitimage.bbclassを参照。

環境構築

Pokyの取得

作業ディレクトリの作成し、pokyを取得する。

$ mkdir -p ~/yocto/bbb-kirkstone
$ cd ~/yocto/bbb-kirkstone
$ git clone git://git.yoctoproject.org/poky.git -b kirkstone

環境変数の設定

$ source poky/oe-init-build-env

作業用レイヤの作成

$ bitbake-layers create-layer -p 20 meta-work
$ bitbake-layers add-layer ./meta-work

extlinux.confは使用しないので下記も実行する。

$ mkdir meta-work/wic
$ cat << 'EOF' > meta-work/wic/beaglebone-yocto-wo-extlinux.wks
part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4 --fixed-size 32 --use-uuid
part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root --align 4 --use-uuid
bootloader --append="console=ttyS0,115200"
EOF
$ echo 'WKS_FILE = "beaglebone-yocto-wo-extlinux.wks"' >> conf/local.conf

詳細はYoctoProject u-bootのextlinux.confを使う を参照。

local.conf

MACHINEにbeaglebone-yoctoを設定しfitImageの作成必要な最低限の設定を追加する。

MACHINE ?= "beaglebone-yocto"
DL_DIR = "${TOPDIR}/../downloads"

KERNEL_IMAGETYPES:append = " fitImage"
KERNEL_CLASSES += "kernel-fitimage"
KERNEL_DEVICETREE:beaglebone-yocto = "am335x-boneblack.dtb"

fitImageに組み込むデバイスツリーを限定するために、KERNEL_DEVICETREEEを設定している。

ビルド

とりあえずcore-image-minimalを作成する。

$ bitbake core-image-minimal

起動確認

boot.scrの作成

fitImageを使用するためにu-bootのブートスクリプトを作成する。

boot.cmdを下記の内容で作成する。

setenv bootargs "root=/dev/mmcblk0p2 rootwait rw debug console=${console},${baudrate}";
fatload mmc 0:1 ${loadaddr} fitimage;
bootm;

下記のコマンドを実行しレシピを作成する。

$ recipetool appendsrcfile -W ./meta-work virtual/bootloader ./boot.cmd
$ recipetool setvar -r ./meta-work/recipes-bsp/u-boot/u-boot_2022.01.bbappend UBOOT_ENV boot
$ recipetool setvar -r ./meta-work/recipes-bsp/u-boot/u-boot_2022.01.bbappend UBOOT_ENV_SUFFIX scr

local.confの修正

イメージにfitImageとboot.scrを組み込むためにlocal.confに下記を追加する。

IMAGE_BOOT_FILES:beaglebone-yocto ?= " \
    ${SPL_BINARY} \
    u-boot.${UBOOT_SUFFIX} \
    fitImage \
    boot.scr \
"

これでcore-image-minimalを作り直す。

起動ログ

作成したイメージで実機を起動したときのログ。fitImageで起動していることがわかる。

... (snip) ...
## Loading kernel from FIT Image at 82000000 ...
   Using 'conf-am335x-bone.dtb' configuration
   Trying 'kernel-1' kernel subimage
     Description:  Linux kernel
     Created:      2022-07-14  18:52:26 UTC
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x82000124
     Data Size:    7845608 Bytes = 7.5 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x80008000
     Entry Point:  0x80008000
     Hash algo:    sha256
     Hash value:   9832e7accda3fb11285bc751c8963d079e4a21f9a22ab0b32d60496c8309e036
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 82000000 ...
   Using 'conf-am335x-bone.dtb' configuration
   Trying 'fdt-am335x-bone.dtb' fdt subimage
     Description:  Flattened Device Tree blob
     Created:      2022-07-14  18:52:26 UTC
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x8277b918
     Data Size:    63701 Bytes = 62.2 KiB
     Architecture: ARM
     Hash algo:    sha256
     Hash value:   8248d6abf5e839c5c63bf7fabe2943cd93c70efbdaf9e6db245113012cfc12d0
   Verifying Hash Integrity ... sha256+ OK
   Booting using the fdt blob at 0x8277b918
   Loading Kernel Image ... OK
   Loading Device Tree to 8ffed000, end 8ffff8d4 ... OK

Starting kernel ...
... (snip) ...
Poky (Yocto Project Reference Distro) 4.0.9 beaglebone-yocto /dev/ttyS0

beaglebone-yocto login:

itsファイル(カーネル+デバイスツリー)

このfitImageを作成する際に自動生成されたitsの抜粋を下記に示す。

カーネルのビルドディレクトリ(linux-beaglebone_yocto-standard-build)に fit-image.its として作成される。

/dts-v1/;

/ {
        description = "Kernel fitImage for Poky (Yocto Project Reference Distro)/5.15.54+gitAUTOINC+441f5fe000_9aabbaa89f/beaglebone-yocto";
        #address-cells = <1>;

        images {
                kernel-1 {
                        description = "Linux kernel";
                        data = /incbin/("linux.bin");
                        type = "kernel";
                        arch = "arm";
                        os = "linux";
                        compression = "none";
                        load = <0x80008000>;
                        entry = <0x80008000>;
                        hash-1 {
                                algo = "sha256";
                        };
                };
                fdt-am335x-bone.dtb {
                        description = "Flattened Device Tree blob";
                        data = /incbin/("arch/arm/boot/dts/am335x-bone.dtb");
                        type = "flat_dt";
                        arch = "arm";
                        compression = "none";
                        
                        hash-1 {
                                algo = "sha256";
                        };
                };
... (snip) ...
        };

        configurations {
                default = "conf-am335x-bone.dtb";
                conf-am335x-bone.dtb {
                        description = "1 Linux kernel, FDT blob";
                        kernel = "kernel-1";
                        fdt = "fdt-am335x-bone.dtb";
                        
                        
                        
                        hash-1 {
                                algo = "sha256";
                        };
                };
... (snip) ...
        };
};

initramfsの追加

独立したinitramfs

fitImageにinitramfsを含める。

local.confの修正

local.confに次の内容を追加する。

INITRAMFS_IMAGE = "core-image-minimal-initramfs"
INITRAMFS_FSTYPES = "cpio.gz"
IMAGE_FSTYPES_pn-${INITRAMFS_IMAGE} = "${INITRAMFS_FSTYPES}"

INITRAMFS_SCRIPTS = "\
                      initramfs-framework-base \
                      initramfs-module-debug \
 "

fitImageをinitramfs付きのものに変更するために下記も修正する。

 IMAGE_BOOT_FILES:beaglebone-yocto ?= " \
     ${SPL_BINARY} \
     u-boot.${UBOOT_SUFFIX} \
-    fitImage \
+    fitImage-${INITRAMFS_IMAGE}-${MACHINE}-${MACHINE};fitImage \
     boot.scr \
 "

起動ログ

この状態でイメージを作り直して起動する。

## Loading kernel from FIT Image at 82000000 ...
   Using 'conf-am335x-boneblack.dtb' configuration
   Trying 'kernel-1' kernel subimage
     Description:  Linux kernel
     Created:      2022-07-14  18:52:26 UTC
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x82000124
     Data Size:    7849952 Bytes = 7.5 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x80008000
     Entry Point:  0x80008000
     Hash algo:    sha256
     Hash value:   e1afb1b3dd372fca45fc86e061ae7b9323efe864e51fe6d4ce30cd79ef38c606
   Verifying Hash Integrity ... sha256+ OK
## Loading ramdisk from FIT Image at 82000000 ...
   Using 'conf-am335x-boneblack.dtb' configuration
   Trying 'ramdisk-1' ramdisk subimage
     Description:  core-image-minimal-initramfs
     Created:      2022-07-14  18:52:26 UTC
     Type:         RAMDisk Image
     Compression:  uncompressed
     Data Start:   0x8278d13c
     Data Size:    2555385 Bytes = 2.4 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x88080000
     Entry Point:  0x88080000
     Hash algo:    sha256
     Hash value:   a7a1fe20b206c9f1cdeec6c143c9289c0feab287d220d8f3720a361f92e453f1
   Verifying Hash Integrity ... sha256+ OK
   Loading ramdisk from 0x8278d13c to 0x88080000
## Loading fdt from FIT Image at 82000000 ...
   Using 'conf-am335x-boneblack.dtb' configuration
   Trying 'fdt-am335x-boneblack.dtb' fdt subimage
     Description:  Flattened Device Tree blob
     Created:      2022-07-14  18:52:26 UTC
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x8277ca18
     Data Size:    67151 Bytes = 65.6 KiB
     Architecture: ARM
     Hash algo:    sha256
     Hash value:   aa3212fe1c49bb2b8fa6997043abe1fb37930a58cfdde63b93e3a7b39b6524cd
   Verifying Hash Integrity ... sha256+ OK
   Booting using the fdt blob at 0x8277ca18
   Loading Kernel Image ... OK
   Loading Ramdisk to 8fd90000, end 8ffffdf9 ... OK
   Loading Device Tree to 8fd7c000, end 8fd8f64e ... OK

Starting kernel ...

fitImageからinitmrafsもロードしていることが確認できる。

カーネルにバンドルしたinitramfs

カーネル、デバイスツリー、initramfsをそれぞれ別に作成してfitImageとして結合する方法の他に、 予めinitramfsをカーネルにバンドルした状態で作成して、カーネルとデバイスツリーをfitImageとして結合することもできる。

local.confの修正

initramfsをカーネルにバンドルするためにlocal.confに下記を追加する。

    INITRAMFS_IMAGE_BUNDLE = "1"

fitImageのファイル名も元に戻す。

 IMAGE_BOOT_FILES:beaglebone-yocto ?= " \
     ${SPL_BINARY} \
     u-boot.${UBOOT_SUFFIX} \
-    fitImage-${INITRAMFS_IMAGE}-${MACHINE}-${MACHINE};fitImage \
+    fitImage \
     boot.scr \
 "

起動ログ

作成したイメージで実機を起動したときのログ。

## Loading kernel from FIT Image at 82000000 ...
   Using 'conf-am335x-boneblack.dtb' configuration
   Trying 'kernel-1' kernel subimage
     Description:  Linux kernel
     Created:      2022-07-14  18:52:26 UTC
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x82000124
     Data Size:    10381632 Bytes = 9.9 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x80008000
     Entry Point:  0x80008000
     Hash algo:    sha256
     Hash value:   cfb1a49a957de9f1712e728ad9e5ef8017d9ccae1e719cb79baa1551df0f1b27
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 82000000 ...
   Using 'conf-am335x-boneblack.dtb' configuration
   Trying 'fdt-am335x-boneblack.dtb' fdt subimage
     Description:  Flattened Device Tree blob
     Created:      2022-07-14  18:52:26 UTC
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x829e6b78
     Data Size:    67151 Bytes = 65.6 KiB
     Architecture: ARM
     Hash algo:    sha256
     Hash value:   aa3212fe1c49bb2b8fa6997043abe1fb37930a58cfdde63b93e3a7b39b6524cd
   Verifying Hash Integrity ... sha256+ OK
   Booting using the fdt blob at 0x829e6b78
   Loading Kernel Image ... OK
   Loading Device Tree to 8ffec000, end 8ffff64e ... OK

Starting kernel ...

fitImageとしてはカーネルとデバイスツリーだけ読み込まれている。

そして以下のログからカーネルにバンドルされたinitramfsが実行されていることが確認できる。

DEBUG: Loading module debug
DEBUG: Running debug_run
DEBUG: Calling module hook (post): debug_hook_handler
DEBUG: Finished module hook (post): debug_hook_handler
DEBUG: Loading module rootfs
DEBUG: Calling module hook (pre): debug_hook_handler
DEBUG: Finished module hook (pre): debug_hook_handler
DEBUG: Running rootfs_run
DEBUG: No e2fs compatible filesystem has been mounted, mounting /dev/mmcblk0p2...
EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null). Quota mode: disabled.
DEBUG: Calling module hook (post): debug_hook_handler
DEBUG: Finished module hook (post): debug_hook_handler
DEBUG: Loading module finish
DEBUG: Calling module hook (pre): debug_hook_handler
DEBUG: Finished module hook (pre): debug_hook_handler
DEBUG: Running finish_run
DEBUG: Moving basic mounts onto rootfs
DEBUG: Moving /dev, /proc and /sys onto rootfs...

まとめ

下記のものを1つのファイルとして結合できるfitImageを試してみた。

YoctoProjectでは作り方自体は簡単になっている。

fitImageを使用するメリットとしてはブートのために必要なファイルの数を減らすことができることだけではなく、 u-bootに実装されているfitImageの署名検証の機能を使用してセキュアブートさせることもできる。

YoctoProjectではfitImageの署名検証の機能をサポートするための変数などが用意されているため、 fitImageを使用することで比較的簡単にセキュアブートを実現することができる。 セキュアブートについては今後調査していきたい。