みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

Yocto Honister ラズパイ4でTPMエミュレータを使ってみる

はじめに

Yocto ProjectでもいくつかのレイヤでTPMをサポートしている。

ラズベリーパイシリーズはTPMモジュールは搭載されておらず、外付けのモジュールを使用するか エミュレータを使用する必要がある。

meta-security配下にあるmeta-tpmではTPMエミュレータを使用することができる。

今回はmeta-tpmにあるエミュレータを使用してTPM2.0の機能を使ってみる。

run_crops.sh

今回も前回作成したrun_crops.shを使用する。

#!/bin/sh

cmd="source $(pwd)/poky/oe-init-build-env"
if [ $# -gt 0 ] ; then
    cmd="${cmd} && $@"
fi

docker run --rm -it -v $(pwd):$(pwd) crops/poky --workdir=$(pwd) bash -c "${cmd}"

実行権限をつけてPATHの通ったところに置いておく。

$ chmod +x ./run_crops.sh

今回は前回まで/workdirだった部分を$(pwd)にして、ホストとコンテナ内のパスが完全に一致するようにしてみた。

Pokyの作成

作業場所は~/work/yocto/rpi_honisterとする。

$ mkdir -p ~/work/yocto/rpi_honister && cd ~/work/yocto/rpi_honister

Pokyの取得

まずはPoky一式を取得する。

$ git clone git://git.yoctoproject.org/poky.git -b honister

meta-raspberrypiの取得

meta-raspberrypiをbitbake-layers layerindex-fetchで取得する。

$ run_crops.sh bitbake-layers layerindex-fetch meta-raspberrypi

meta-securityの取得

meta-securityはいくつかのレイヤに依存しているので下記の手順で取得する。

$ run_crops.sh bitbake-layers layerindex-fetch meta-oe
$ run_crops.sh bitbake-layers layerindex-fetch meta-python
$ run_crops.sh bitbake-layers layerindex-fetch meta-networking
$ run_crops.sh bitbake-layers layerindex-fetch meta-perl
$ run_crops.sh bitbake-layers layerindex-fetch meta-security

meta-tpmをビルド対象に追加

meta-securityのサブレイヤとして定義されているmeta-tpmはlayerindex-fetchできないので、add-layerする。

$ run_crops.sh bitbake-layers add-layer ../poky/meta-security/meta-tpm

レイヤの確認

$ run_crops.sh bitbake-layers show-layers

### Shell environment set up for builds. ###

You can now run 'bitbake <target>'

Common targets are:
    core-image-minimal
    core-image-full-cmdline
    core-image-sato
    core-image-weston
    meta-toolchain
    meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks
NOTE: Starting bitbake server...
layer                 path                                      priority
==========================================================================
meta                  /home/mickey/work/yocto/rpi_honister/poky/meta  5
meta-poky             /home/mickey/work/yocto/rpi_honister/poky/meta-poky  5
meta-yocto-bsp        /home/mickey/work/yocto/rpi_honister/poky/meta-yocto-bsp  5
meta-raspberrypi      /home/mickey/work/yocto/rpi_honister/poky/meta-raspberrypi  9
meta-oe               /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-oe  6
meta-python           /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-python  7
meta-networking       /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-networking  5
meta-perl             /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-perl  6
meta-security         /home/mickey/work/yocto/rpi_honister/poky/meta-security  8
meta-tpm              /home/mickey/work/yocto/rpi_honister/poky/meta-security/meta-tpm  10

local.confの修正

build/conf/local.confに下記の内容を追加する。

MACHINE = "raspberrypi4-64"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES:append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# connman
IMAGE_INSTALL:append = " connman \
               connman-client \
"

# for meta-security and meta-tpm
DISTRO_FEATURES:append = " security tpm2"

ビルド

TPM2.0を使いたいのでsecurity-tpm2-imageをビルドする。

$ run_crops.sh bitbake security-tpm2-image

SDに書き込み

./build/tmp/deploy/images/raspberrypi4-64/security-tpm2-image-raspberrypi4-64.wic.bz2が書き込み用イメージとなる。

これをbmaptoolでマイクロSDカードに書き込む。

$ sudo bmaptool copy ./build/tmp/deploy/images/raspberrypi4-64/security-tpm2-image-raspberrypi4-64.wic.bz2 /dev/sdX

/dev/sdXは適宜読み替え。

TPMエミュレータを使ってみる

security-tpm2-imageにはibmswtpm2というTPMエミュレータが組み込まれている。

ibmswtpm2を使用してtpm2-toolsを実行するための情報はあまり多くない。

Setup Virtual TPMというところに欲しかった情報がまとまっていたので参考にする。

security-tpm2-imageで起動した状態からibmswtpm2を使用するためには以下の手順が必要になる。

  1. tpm_serverの起動
  2. tpm2-abrmdの起動

tpm_serverがibmswtpm2が提供するユーザーランドのインターフェースとなる。

tpm2-abrmdはTPM2 Access Broker & Resource Manager Daemonの略で、 tpm2-toolsのコマンドはこのデーモンを介してTPM2.0の機能にアクセスする。 なので、今回のケースではtpm2-abrmdがtpm_serverのクライアントとして接続される必要がある。

試しに、これらの手順を実施しない状態でtpm2_clearを実行してみる。

# tpm2_clear

** (process:330): CRITICAL **: 17:20:50.253: failed to allocate dbus proxy object: Error calling StartServiceByName for com.intel.tss2.Tabrmd: Timeout was reached
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:79:tcti_from_init() TCTI init for function 0x7fb856c280 failed with a0008
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:109:tcti_from_info() Could not initialize TCTI named: tcti-abrmd
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr-dl.c:154:tcti_from_file() Could not initialize TCTI file: libtss2-tcti-tabrmd.so.0
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tcti-device.c:440:Tss2_Tcti_Device_Init() Failed to open specified TCTI device file /dev/tpmrm0: No such file or directory
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:79:tcti_from_init() TCTI init for function 0x7fb6fbd590 failed with a000a
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:109:tcti_from_info() Could not initialize TCTI named: tcti-device
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr-dl.c:154:tcti_from_file() Could not initialize TCTI file: libtss2-tcti-device.so.0
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tcti-device.c:440:Tss2_Tcti_Device_Init() Failed to open specified TCTI device file /dev/tpm0: No such file or directory
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:79:tcti_from_init() TCTI init for function 0x7fb6fbd590 failed with a000a
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:109:tcti_from_info() Could not initialize TCTI named: tcti-device
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr-dl.c:154:tcti_from_file() Could not initialize TCTI file: libtss2-tcti-device.so.0
WARNING:tcti:../tpm2-tss-3.0.3/src/util/io.c:252:socket_connect() Failed to connect to host 127.0.0.1, port 2321: errno 111: Connection refused
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tcti-swtpm.c:592:Tss2_Tcti_Swtpm_Init() Cannot connect to swtpm TPM socket
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:79:tcti_from_init() TCTI init for function 0x7fb6fbd9e0 failed with a000a
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:109:tcti_from_info() Could not initialize TCTI named: tcti-swtpm
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr-dl.c:154:tcti_from_file() Could not initialize TCTI file: libtss2-tcti-swtpm.so.0
WARNING:tcti:../tpm2-tss-3.0.3/src/util/io.c:252:socket_connect() Failed to connect to host 127.0.0.1, port 2321: errno 111: Connection refused
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:79:tcti_from_init() TCTI init for function 0x7fb6fbd9b0 failed with a000a
WARNING:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:109:tcti_from_info() Could not initialize TCTI named: tcti-socket
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr-dl.c:154:tcti_from_file() Could not initialize TCTI file: libtss2-tcti-mssim.so.0
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr-dl.c:254:tctildr_get_default() No standard TCTI could be loaded
ERROR:tcti:../tpm2-tss-3.0.3/src/tss2-tcti/tctildr.c:416:Tss2_TctiLdr_Initialize_Ex() Failed to instantiate TCTI
ERROR: Could not load tcti, got: "(null)"

このようにエラーとなる。

tpm_serverの起動

単純にデーモンとして起動する。

# tpm_server &
LIBRARY_COMPATIBILITY_CHECK is ON
Manufacturing NV state...
Size of OBJECT = 2600
Size of components in TPMT_SENSITIVE = 1096
    TPMI_ALG_PUBLIC                 2
    TPM2B_AUTH                      66
    TPM2B_DIGEST                    66
    TPMU_SENSITIVE_COMPOSITE        962
Starting ACT thread...
TPM command server listening on port 2321
Platform server listening on port 2322

tpm2-abrmdは2321のポートに接続する必要がある。

tpm2-abrmdの起動

本来はsystemdのサービスとして起動できるが、提供されているtpm2-abrmd.serviceは、エミュレータではなく物理デバイスを使用した場合の設定が記述されているので、とりあえず手動で起動する。

# tpm2-abrmd --tcti=mssim:host=localhost,port=2321 --allow-root &

tpm2-toolsの使用

とりあえず、tpm2_clearを実行してみる。

# tpm2_clear

これでエラーが発生しなければTPM2.0は使えてるはず。

試しにtpm2_createakのEXAMPLES]を実行してみる。

# tpm2_createek -c ek.handle -G rsa -u ek.pub
# tpm2_createak -C ek.handle -c ak.ctx -u ak.pub -n ak.name
# tpm2_evictcontrol -C o -c ak.ctx 0x81010002
# ls
ak.ctx     ak.name    ak.pub     ek.handle  ek.pub

これらのコマンドでエラーが発生せず、上記の5つのファイルが作成されていれば問題ない。

サービス化

ここまでで以下の手順を実施することでTPM2.0エミュレータが使用できることが確認できた。

  1. tpm_serverの起動
  2. tpm2-abrmdの起動

これらをsystemdのサービスとして実行できるようにする。

tpm_serverのサービス化

/etc/systemd/system/tpm-server.serviceを以下の内容で作成する。

[Unit]
Description=IBM's Software TPM2.0 server

[Service]
ExecStart=/usr/bin/tpm_server
Restart=on-failure
Type=simple

[Install]
WantedBy=multi-user.target

サービスを起動する。

# systemctl start tpm-server

systemctl statusで起動できていることを確認する。

# systemctl status tpm-server
* tpm-server.service - IBM Software TPM2.0 Emulater server
     Loaded: loaded (8;;file://raspberrypi4-64/etc/systemd/system/tpm-server.service/etc/systemd/system/tpm-server.service8;;; disabled; vendor preset: disabled)
     Active: active (running) since Fri 2021-11-19 17:30:26 UTC; 9s ago
   Main PID: 362 (tpm_server)
      Tasks: 3 (limit: 4163)
     CGroup: /system.slice/tpm-server.service
             `-362 /usr/bin/tpm_server

Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]: Manufacturing NV state...
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]: Size of OBJECT = 2600
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]: Size of components in TPMT_SENSITIVE = 1096
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]:     TPMI_ALG_PUBLIC                 2
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]:     TPM2B_AUTH                      66
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]:     TPM2B_DIGEST                    66
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]:     TPMU_SENSITIVE_COMPOSITE        962
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]: Starting ACT thread...
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]: TPM command server listening on port 2321
Nov 19 17:30:26 raspberrypi4-64 tpm_server[362]: Platform server listening on port 2322

次回起動時のためにenableしておく。

# systemctl enable tpm-server

tpm2_abrmd.serviceの修正

/lib/systemd/system/tpm2-abrmd.serviceを以下の内容に修正する。

[Unit]
Description=TPM2 Access Broker and Resource Management Daemon
# These settings are needed when using the device TCTI. If the
# TCP mssim is used then the settings should be commented out.
After=tpm-server.service
Requires=tpm-server.service

[Service]
Type=dbus
BusName=com.intel.tss2.Tabrmd
ExecStart=/usr/sbin/tpm2-abrmd --tcti=mssim:host=localhost,port=2321
User=tss

[Install]
WantedBy=multi-user.target

User=tssしているのでExecStart行では--allow-rootは不要となる。

変更後のユニットファイルをリロードする。

# systemctl daemon-reload

サービスを起動する。

# systemctl start tpm2-abrmd

systemctl statusで起動できていることを確認する。

# systemctl status tpm2-abrmd
* tpm2-abrmd.service - TPM2 Access Broker and Resource Management Daemon
     Loaded: loaded (8;;file://raspberrypi4-64/lib/systemd/system/tpm2-abrmd.service/lib/systemd/system/tpm2-abrmd.service8;;; disabled; vendor preset: disabled)
     Active: active (running) since Fri 2021-11-19 17:39:30 UTC; 1min 17s ago
   Main PID: 409 (tpm2-abrmd)
      Tasks: 6 (limit: 4163)
     CGroup: /system.slice/tpm2-abrmd.service
             `-409 /usr/sbin/tpm2-abrmd --tcti=mssim:host=localhost,port=2321

Nov 19 17:39:30 raspberrypi4-64 systemd[1]: Starting TPM2 Access Broker and Resource Management Daemon...
Nov 19 17:39:30 raspberrypi4-64 tpm2-abrmd[409]: tcti_conf before: "(null)"
Nov 19 17:39:30 raspberrypi4-64 tpm2-abrmd[409]: tcti_conf after: "mssim:host=localhost,port=2321"
Nov 19 17:39:30 raspberrypi4-64 systemd[1]: Started TPM2 Access Broker and Resource Management Daemon.

こちらも次回起動時のためにenableしておく。

# systemctl enable tpm2-abrmd

再起動して動作確認

ラズベリーパイをリブートしてtpm2_clearを実行してみる。

# tpm2_clear

これでエラーが出なければ成功。

レシピに組み込む

これまでラズベリーパイ上で行ってきた変更の内容をレシピに組み込む。

meta-vtpm2-helperの作成

$ run_crops.sh bitbake-layers create-layer -p 20 meta-vtpm2-helper
$ mv build/meta-vtpm2-helper poky
$ run_crops.sh bitbake-layers add-layer ../poky/meta-vtpm2-helper

bitbake-layers show-layersで次のようになっていることを確認する。

$ run_crops.sh bitbake-layers show-layers
...(snip)...
layer                 path                                      priority
==========================================================================
meta                  /home/mickey/work/yocto/rpi_honister/poky/meta  5
meta-poky             /home/mickey/work/yocto/rpi_honister/poky/meta-poky  5
meta-yocto-bsp        /home/mickey/work/yocto/rpi_honister/poky/meta-yocto-bsp  5
meta-raspberrypi      /home/mickey/work/yocto/rpi_honister/poky/meta-raspberrypi  9
meta-oe               /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-oe  6
meta-python           /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-python  7
meta-networking       /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-networking  5
meta-perl             /home/mickey/work/yocto/rpi_honister/poky/meta-openembedded/meta-perl  6
meta-security         /home/mickey/work/yocto/rpi_honister/poky/meta-security  8
meta-tpm              /home/mickey/work/yocto/rpi_honister/poky/meta-security/meta-tpm  10
meta-vtpm2-helper     /home/mickey/work/yocto/rpi_honister/poky/meta-vtpm2-helper  20

tpm2-abrmdのbbappendの作成

パッチファイルの作成

~/work/yocto/rpi_honister/tpm2-abrmd.service.in.patchを以下の内容で作成する。

diff --git a/dist/tpm2-abrmd.service.in b/dist/tpm2-abrmd.service.in
index b0b562c..2252d93 100644
--- a/dist/tpm2-abrmd.service.in
+++ b/dist/tpm2-abrmd.service.in
@@ -2,13 +2,13 @@
 Description=TPM2 Access Broker and Resource Management Daemon
 # These settings are needed when using the device TCTI. If the
 # TCP mssim is used then the settings should be commented out.
-After=dev-tpm0.device
-Requires=dev-tpm0.device
+After=tpm-server.service
+Requires=tpm-server.service

 [Service]
 Type=dbus
 BusName=com.intel.tss2.Tabrmd
-ExecStart=@SBINDIR@/tpm2-abrmd
+ExecStart=/usr/sbin/tpm2-abrmd --tcti=mssim:host=localhost,port=2321
 User=tss

 [Install]

bbappendの作成

recipetoolを使用して、作成したパッチを登録する。

$ run_crops.sh recipetool appendsrcfile ../poky/meta-vtpm2-helper tpm2-abrmd ~/work/yocto/rpi_honister/tpm2-abrmd.service.in.patch

bbappendの修正

tpm2-abrmdのレシピではサービスがdisableになっているため、これをenableにするために、 ~/work/yocto/rpi_honister/poky/meta-vtpm2-helper/recipes-tpm2/tpm2-abrmd/tpm2-abrmd_2.4.0.bbappendを以下の内容に修正する。

SRC_URI += "file://tpm2-abrmd.service.in.patch;subdir=git"

FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

SYSTEMD_AUTO_ENABLE:${PN} = "enable"

ibmswtpm2のbbappendの作成

tpm-server.serviceの作成

~/work/yocto/rpi_honister/tpm-server.serviceを以下の内容で作成する。

[Unit]
Description=IBM's Software TPM2.0 server

[Service]
ExecStart=/usr/bin/tpm_server
Restart=on-failure
Type=simple

[Install]
WantedBy=multi-user.target

bbappendの作成

recipetoolを使用して、作成したユニットファイルを登録する。

$ run_crops.sh recipetool appendsrcfile ../poky/meta-vtpm2-helper ibmswtpm2 ~/work/yocto/rpi_honister/tpm-server.service

bbappendの修正

追加したユニットファイルをsystemdのサービスとして登録するために、 ~/work/yocto/rpi_honister/poky/meta-vtpm2-helper/recipes-tpm2/ibmswtpm2/ibmswtpm2_1661.bbappendを以下の内容に修正する。

SRC_URI += "file://tpm-server.service;subdir=src"

FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

SYSTEMD_PACKAGES += "${PN}"
SYSTEMD_SERVICE:${PN} = "tpm-server.service"
SYSTEMD_AUTO_ENABLE:${PN} = "enable"

do_install:append() {
   install -d ${D}${systemd_unitdir}/system
   install -m 0644 ${S}/tpm-server.service ${D}${systemd_unitdir}/system
}

FILES:${PN} += "${systemd_unitdir}/system/tpm-server.service"

イメージの再作成、動作確認

$ run_crops.sh bitbake security-tpm2-image

作成されたイメージを書き込んだマイクロSDカードでラズベリーパイ4を起動して、tpm_clearなどを実行してエラーが発生しないことを確認する。

起動直後にtpm-server.serviceとtpm2-abrmd.serviceの状態を確認すると下記のようになった。

# systemctl status tpm-server
* tpm-server.service - IBM's Software TPM2.0 server
     Loaded: loaded (8;;file://raspberrypi4-64/lib/systemd/system/tpm-server.service/lib/systemd/system/tpm-server.service8;;; disabled; vendor preset: disabled)
     Active: active (running) since Fri 2021-11-19 17:19:33 UTC; 20s ago
   Main PID: 278 (tpm_server)
      Tasks: 3 (limit: 4163)
     CGroup: /system.slice/tpm-server.service
             `-278 /usr/bin/tpm_server

Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]: Size of components in TPMT_SENSITIVE = 1096
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]:     TPMI_ALG_PUBLIC                 2
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]:     TPM2B_AUTH                      66
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]:     TPM2B_DIGEST                    66
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]:     TPMU_SENSITIVE_COMPOSITE        962
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]: Starting ACT thread...
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]: TPM command server listening on port 2321
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]: Platform server listening on port 2322
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]: Command IPv6 client accepted
Nov 19 17:19:33 raspberrypi4-64 tpm_server[278]: Platform IPv6 client accepted
# systemctl status tpm2-abrmd
* tpm2-abrmd.service - TPM2 Access Broker and Resource Management Daemon
     Loaded: loaded (8;;file://raspberrypi4-64/lib/systemd/system/tpm2-abrmd.service/lib/systemd/system/tpm2-abrmd.service8;;; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2021-11-19 17:19:33 UTC; 28s ago
   Main PID: 280 (tpm2-abrmd)
      Tasks: 6 (limit: 4163)
     CGroup: /system.slice/tpm2-abrmd.service
             `-280 /usr/sbin/tpm2-abrmd --tcti=mssim:host=localhost,port=2321

Nov 19 17:19:33 raspberrypi4-64 systemd[1]: Starting TPM2 Access Broker and Resource Management Daemon...
Nov 19 17:19:33 raspberrypi4-64 tpm2-abrmd[280]: tcti_conf before: "(null)"
Nov 19 17:19:33 raspberrypi4-64 tpm2-abrmd[280]: tcti_conf after: "mssim:host=localhost,port=2321"
Nov 19 17:19:33 raspberrypi4-64 systemd[1]: Started TPM2 Access Broker and Resource Management Daemon.

意図通りの動作をしている。

まとめ

ラズベリーパイ4でTPM2.0エミュレータを動かした。 HONISTER向けのbbappendなども作ってみたが、だいたい問題なく作業できた。

あくまでもエミュレータなので、実際にラズベリーパイでTPM2.0を使う場合は、TPM2.0モジュールを使用する必要があるが、 TPM2.0を使用するソフトウェアの開発をモジュール無しでも行える環境を作成できることはわかった。

CROPSを使用しても、作業内容には大きな影響はない感じだった。

Yocto Project CROPSでmeta-raspberrypiをビルド

はじめに

前回CROPSを動かしてみた。

CROPSでmeta-raspberrypiをビルドしてみる。今回はラズベリーパイ4を使用する。

run_crops.sh

今回も前回作成したrun_crops.shを使用する。

#!/bin/sh

cmd="source /workdir/poky/oe-init-build-env"
if [ $# -gt 0 ] ; then
    cmd="${cmd} && $@"
fi

docker run --rm -it -v $(pwd):/workdir crops/poky --workdir=/workdir bash -c "${cmd}"

実行権限をつけてPATHの通ったところに置いておく。

$ chmod +x ./run_crops.sh

余談だが、/workdirの部分を$(pwd)にすると、ホストとコンテナ内のパスが完全に一致するので、便利になるかもしれない。

Pokyの作成

作業場所は~/work/yocto/rpi_honisterとする。

$ mkdir -p ~/work/yocto/rpi_honister && cd ~/work/yocto/rpi_honister

Pokyの取得

まずはPoky一式を取得する。

$ git clone git://git.yoctoproject.org/poky.git -b honister

meta-raspberrypiの取得

meta-raspberrypiはbitbake-layers layerindex-fetchで取得できる。

$ run_crops.sh bitbake-layers layerindex-fetch meta-raspberrypi

ダウンロードから、ビルド対象への追加まで一括でやってくれるので便利。

レイヤの確認

$ run_crops.sh bitbake-layers show-layers

### Shell environment set up for builds. ###

You can now run 'bitbake <target>'

Common targets are:
    core-image-minimal
    core-image-full-cmdline
    core-image-sato
    core-image-weston
    meta-toolchain
    meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks
NOTE: Starting bitbake server...
layer                 path                                      priority
==========================================================================
meta                  /workdir/poky/meta                        5
meta-poky             /workdir/poky/meta-poky                   5
meta-yocto-bsp        /workdir/poky/meta-yocto-bsp              5
meta-raspberrypi      /workdir/poky/meta-raspberrypi            9

docker runする時に--workdir=/workdirとしているので全てのパスの起点が/workdirになっている。

local.confの修正

build/conf/local.confに下記の内容を追加する。

MACHINE = "raspberrypi4-64"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# connman
IMAGE_INSTALL:append = " connman \
                 connman-client \
"

HONISTERでは_append:appendになっているので注意が必要。

Upgrading to Yocto Honisterが親切。

こちらで少し解説した。

ビルド

$ run_crops.sh bitbake core-image-base

SDに書き込み

./build/tmp/deploy/images/raspberrypi4-64/core-image-base-raspberrypi4-64.wic.bz2が書き込み用イメージとなる。

これをbmaptoolでマイクロSDカードに書き込む。

$ sudo bmaptool copy ./build/tmp/deploy/images/raspberrypi4-64/core-image-base-raspberrypi4-64.wic.bz2 /dev/sdX

/dev/sdXは適宜読み替え。

起動確認

マイクロSDカードをラズパイ4に挿入し電源を投入する。

Poky (Yocto Project Reference Distro) 3.4.1 raspberrypi4-64 ttyS0

raspberrypi4-64 login: root
Last login: Fri Nov 19 17:19:42 UTC 2021 on ttyS0
root@raspberrypi4-64:~# cat /etc/os-release 
ID=poky
NAME="Poky (Yocto Project Reference Distro)"
VERSION="3.4.1 (honister)"
VERSION_ID=3.4.1
PRETTY_NAME="Poky (Yocto Project Reference Distro) 3.4.1 (honister)"
root@raspberrypi4-64:~# 

VERSION="3.4.1 (honister)"起動した。

まとめ

CROPS経由でもmeta-raspberrypiがビルドできることを確認できた。 環境構築の手間はホストマシンが汚れるのを防ぐ意味でもCROPSは有用だと思う。

bitbake-layersなどのbitbake以外のツールも問題なく使えているので、CROPSを使用することで不便なことはなさそう。

Yocto Project HONISTERの注意点

はじめに

最近やっと時間が取れるようになって(なってない)、HONISTERをいじり始めた。

初っ端から下記のようなエラーが発生して面食らった。

ERROR: Variable DISTRO_FEATURES_append contains an operation using the old override syntax. Please convert this layer/metadata before attempting to use with a newer bitbake.

Upgrading to Yocto Honisterで、まず引っかかりそうなポイントをまとめてくれている。

大きい変更点

まずは、migration-guidesを見る。

Override syntax changes

一番大きい変更点は、Override syntax changesだろう。

下記のような記述が、

SRC_URI_qemux86 = "file://somefile"

このように変更されている。

SRC_URI:qemux86 = "file://somefile"

Override syntax(ここではqemux86)と変数名の区切りが_から:に変更されている。この変更自体は、変数名に入っている単語を区切るための_とOverride syntaxのための_でよく混乱していたので、歓迎されるべきものだと思っているが、 使用頻度が高いところでもあるので、ユーザーがよく引っかかると思う。

その他のものも含めて、migration-guideではこのように例示されている。

変更前

SRC_URI_append = " file://somefile"
SRC_URI_append_qemux86 = " file://somefile2"
SRC_URI_remove_qemux86-64 = " file://somefile3"
SRC_URI_prepend_qemuarm = "file://somefile4 "
FILES_${PN}-ptest = "${bindir}/xyz"
IMAGE_CMD_tar = "tar"
BASE_LIB_tune-cortexa76 = "lib"
SRCREV_pn-bash = "abc"
BB_TASK_NICE_LEVEL_task-testimage = '0'

変更後

SRC_URI:append = " file://somefile"
SRC_URI:append:qemux86 = " file://somefile2"
SRC_URI:remove:qemux86-64 = " file://somefile3"
SRC_URI:prepend:qemuarm = "file://somefile4 "
FILES:${PN}-ptest = "${bindir}/xyz"
IMAGE_CMD:tar = "tar"
BASE_LIB:tune-cortexa76 = "lib"
SRCREV:pn-bash = "abc"
BB_TASK_NICE_LEVEL:task-testimage = '0'

変更後のほうが明らかにわかりやすくなっている。

ほかにもgetVar("RDEPENDS_${PN}")getVar("RDEPENDS:${PN}").になったりする。

とにかく、下記のようなメッセージに出くわしたら_:に変えられないか疑ってみよう。

ERROR: Variable XXXXXXX contains an operation using the old override syntax. Please convert this layer/metadata before attempting to use with a newer bitbake.

host dependencyの追加

bitbakeを実行する際にHost側にインストールしておくべきパッケージが追加されている。

  • lz4c
  • pzstd
  • zstd

以前からYocto Projectを使っている環境で不意にエラーになるとびっくりするかもしれない。

Virtual runtime providesの廃止

virtual/がRPROVIDESやRDEPENDSで使用できなくなった。あまりユースケースが思い浮かばないが、過去の資産のレイヤを使い回す時に出くわすかもしれない。

Tuneファイルがアーキテクチャ毎のディレクトリに移動

BSP用のレイヤを作ったりする場合に出くわす可能性がある。tune-corei7.incとかtune-cortexa8.incなどのファイルのこと。

これらがアーキテクチャごとにx86系であればx86MIPS系であればmips、ARM系の場合はarmv8aarmv8mなどのサブディレクトリごとに配置されるようになった。

まとめ

Override syntaxが変更したのはかなり大きい。過去のバージョンからHONISTERに移行する場合は注意が必要となる。

詳細かつ正確な情報はmigration-guidesを参照してほしい。

参考

Yocto Project CROPSを使ってみる

はじめに

Yocto Projectではbitbakeを実行するためのDockerコンテナを提供している。

そのコンテナはCROss PlatformS(CROPS)と呼ばれている。CROPSの詳細はSetting Up to Use CROss PlatformS (CROPS)を参照のこと。

使い方

使い方についてのざっくりな説明はgithubのリポジトリにある。

$ docker run --rm -it -v /home/myuser/mystuff:/workdir crops/poky --workdir=/workdir

/home/myuser/mystruffを実際の作業場所に置き換えると良い。

HONISTER(3.4.1)をビルドしてみる

作業場所は~/work/yocto/honisterとする。

$ mkdir -p ~/work/yocto/honister && cd ~/work/yocto/honister

ソースの取得

下記のコマンドでpokyのビルド環境をダウンロードする。

$ git clone -b honister git://git.yoctoproject.org/poky.git

CROPSの実行

docker runする時にコマンドを特に指定しない場合はbash -iされて、コンテナの中に入ることができる。

$ docker run --rm -it -v $(pwd):/workdir crops/poky --workdir=/workdir 

一般的には、コンテナの中に入って通常通りのコマンドを実行することになる。

$ source poky/oe-init-build-env
$ bitbake core-image-minimal

コンテナ外からの実行

bashでコンテナに入った状態にならないで、bitbakeできる方法は下記のようになる。

$ docker run --rm -it -v $(pwd):/workdir crops/poky --workdir=/workdir bash -c "source /workdir/poky/oe-init-build-env && bitbake core-image-minimal"

CROPSの実行を簡単にする

run_crops.shなどの名前で下記のような内容をシェルスクリプトを作成する。

#!/bin/sh

cmd="source /workdir/poky/oe-init-build-env"
if [ $# -gt 0 ] ; then
    cmd="${cmd} && $@"
fi

docker run --rm -it -v $(pwd):/workdir crops/poky --workdir=/workdir bash -c "${cmd}"

実行権限をつけておく。

$ chmod +x ./run_crops.sh

これをPATHの通った場所に格納しておくと下記のように実行できる。

$ run_crops.sh

run_crops.shはpokyディレクトリがある階層で実行する必要がある。

run_crops.shを使ってみる

pokyディレクトリは~/work/yocto/honisterに作成してあるので下記のようにする。

$ cd ~/work/yocto/honister
$ run_crops.sh

これでbuildディレクトリが作成されるので、必要に応じてlocal.confを編集する。

下記のようにしてbitbakeを実行できる。

$ run_crops.sh bitbake core-image-minimal

古いバージョンのビルド

古いバージョンPokyをビルドする場合、ビルド環境にUbuntu16.04などを使いたい場合がある。 その場合は、下記のようにしてベースのディストリビューションを指定することができる。

$ docker run --rm -it -v $(pwd):/workdir crops/poky:ubuntu-16.04 --workdir=/workdir 

これは、CROPSのDockerイメージにタグを打ってあるから。 使用できるベースディストリビューションdocker hubを参照すると良い。

distro-check.shの方がわかりやすいかもしれない。

下記が2022/01/14時点でサポートしているベースディストリビューションとなる。

distros["centos-7"]="CentOS Linux 7 (Core)"
distros["centos-8"]="CentOS Linux 8"
distros["debian-9"]="Debian GNU/Linux 9 (stretch)"
distros["debian-10"]="Debian GNU/Linux 10 (buster)"
distros["debian-11"]="Debian GNU/Linux 11 (bullseye)"
distros["fedora-33"]="Fedora 33 (Container Image)"
distros["fedora-34"]="Fedora 34 (Container Image)"
distros["opensuse-15.2"]="openSUSE Leap 15.2"
distros["ubuntu-16.04"]="Ubuntu 16.04"
distros["ubuntu-18.04"]="Ubuntu 18.04"
distros["ubuntu-20.04"]="Ubuntu 20.04"

まとめ

いまいち、日本語での情報が少ないCROPSについて調査してみた。

CROPSが使えるようになると、Dockerさえ動いていれば基本的にはどんな環境でもbitbakeができるので、Yocto Projectを使用するハードルは下がると思う。 (hostがarmの場合は知らん)

Windows環境でもVMやWSL2を使用するよりは、実行時パフォーマンスやサポート的なところでちょっとはマシになる気がする。

Yocto Zephyrアプリケーションのコンフィグを変更する

はじめに

YoctoでビルドするZephyrアプリケーションのコンフィグを設定する方法を調べた。

micro:bitの5x5 LEDを有効化する。

最終的にはmeta-armの修正が必要となった。

作業環境はYocto Zephyrに独自のアプリケーションを追加してみるを使用する。

環境変数の読み込み

最初にこれを実行し、bitbake関連のツールを使用できるようにしておく。

$ source poky/oe-init-build-env build

do_menuconfigタスク

ZephyrはLinuxカーネルのようにKconfigによってコンフィグを変更したりできる。

Yoctoでは、このような機能を持つコンポーネントのレシピの場合do_menuconfigタスクを実装している場合が多く、 それが利用でいないか確認してみた。

$ bitbake my-app -c listtasks | grep menuconfig
do_menuconfig       Runs 'make menuconfig' for the kernel

見つかったので実行してみる。

$ bitbake my-app -c menuconfig

残念ながら使用できない。

make: *** No rule to make target 'menuconfig'.  Stop.
Command failed.
Press any key to continue... 

コンフィグの変更

Zephyrアプリケーションのコンフィグを変更するにはprj.confに記述する。

Yocto Zephyrに独自のアプリケーションを追加してみるで作成した時には下記のように空の設定を記述していた。

# nothing here

micro:bitの5x5 LEDを有効化するためにはCONFIG_MICROBIT_DISPLAYを有効化する。

CONFIG_MICROBIT_DISPLAY=y

メニューコンフィグ

実際にこの設定が反映されているか確認したい。

本来であればメニューコンフィグを実行するのが手っ取り早い。do_menuconfigタスクは使えないが、実のところZephyrのメニューコンフィグを実行する手段はある。

下記のようにdo_configureタスクを実行したあと、do_devshellタスクを実行する。

$ bitbake my-app -c configure
$ bitbake my-app -c devshell

すると、開発用シェルが${S}で設定されているディレクトリに移動された状態で開く。

ビルドディレクトリにはbuild.ninjaが生成されているので、下記のようにするとZephyrのメニューコンフィグを実行することができる。

$ cd ../build
$ ninja menuconfig

f:id:mickey_happygolucky:20210828094011p:plain
メニューコンフィグ

MICROBIT_DISPLAY=n

/を押したあとでキーワードを入力すると、コンフィグを検索することができる。

MICROBIT_と入力すると下記のような画面になる。

f:id:mickey_happygolucky:20210828094033p:plain
MICROBIT_DISPLAY(=n)

MICROBIT_DISPLAY=nとなっており、prj.confの設定が反映されていない。

?で詳細を表示してこのコンフィグの依存関係を表示すると下記のようになっている。

Direct dependencies (=n):
     BOARD_BBC_MICROBIT(undefined/n)
  && PRINTK(=y)
  && GPIO(=y)
  && DISPLAY(=n)

BOARD_BBC_MICROBITDISPLAYの設定がnになっていることがわかる。

BOARDの設定を見るとBOARD_BBC_MICROBITはボード設定に依存しているようだ。 BOARD(=qemu_cortex_m0)はZephyrがターゲットボードを決定するための基本的な設定なのだろう。

f:id:mickey_happygolucky:20210828094020p:plain
BOARD(=qemu_cortex_m0)

BOARD(=qemu_cortex_m0)BOARD_QEMU_CORTEX_M0(=y)は対応しているように見える。つまりBOARD(=bbc_microbit)と設定すればBOARD_BBC_MICROBIT(=y)にできるのではないか。

BOARDの設定

meta-arm/conf/machine/microbit-v1.confを調べると、下記の様な記述が見つかる。

# Zephyr RTOS settings
ZEPHYR_BOARD = "qemu_cortex_m0"
ZEPHYR_INHERIT_CLASSES += "zephyr-qemuboot"
ARCH_qemu-cortex-m0 = "arm"

ZEPHYR_BOARDに設定された値がZephyrのCONFIG_BOARDに設定されているようだ。

ZEPHYR_BOARDの値を変更すれば良さそうだが、できればmeta-armを直接変更したくない。local.confで下記のようにすることで設定を上書きできないだろうか。

ZEPHYR_BOARD = "bbc_microbit"

結論から言うとこれはできない。

microbit-v1.confでZEPHYR_BOARDを設定する時に=を使って代入しているため、変数をあとから上書きすることができないからだ。

下記のように?=を使用して代入していればlocal.confなどであとから変更することができるようになる。

ZEPHYR_BOARD ?= "qemu_cortex_m0"

レイヤやMACHINEの定義をする場合、RAMのベースアドレスのように絶対に上書きされたくないような物以外はできるだけ?=を使うようにしてほしいが、 microbit-v1.confはmicro:bit専用の設定であるので、下記のように修正するのが本来正しい気もする。

- ZEPHYR_BOARD = "qemu_cortex_m0"
+ ZEPHYR_BOARD = "bbc_microbit"

最終的なコンフィグ

meta-arm/conf/machine/microbit-v1.confZEPHYR_BOARDbbc_microbitに設定したあと、meta-zephyr-app/recipes-example/my-app/files/my-app/prj.confを下記のように変更する。

CONFIG_DISPLAY=y
CONFIG_MICROBIT_DISPLAY=y

bitbake my-app -c configureでdo_configureタスクを実行したあとに、devshellでninja menuconfigを実行して、コンフィグの依存関係を確認すると下記のようになっている。

Direct dependencies (=y):
     BOARD_BBC_MICROBIT(=y)
  && PRINTK(=y)
  && GPIO(=y)
  && DISPLAY(=y)

これでCONFIG_MICROBIT_DISPLAYは有効化できた。

プログラムの修正

VSCode + PlatformIO で micro:bit + Zephyr RTOS 開発がちょうどZephyrで5x5 LEDを使用する方法を紹介している。

meta-zephyr-app/recipes-example/my-app/files/my-app/src/main.cを下記のように変更する。

#include <zephyr.h>
#include <display/mb_display.h>

void main(void)
{
    struct mb_image patterns[] = {
        MB_IMAGE(
            {0, 0, 0, 0, 0},
            {1, 1, 0, 1, 1},
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 1, 1, 1, 0},
        ),
    };
    struct mb_display *dpy = mb_display_get();
    mb_display_image(dpy, MB_DISPLAY_MODE_DEFAULT, SYS_FOREVER_MS, patterns, 1);
}

レシピの修正

5x5 LEDは実機に書き込まないと確認できないし、毎回objcopyでbinary形式に変換するのも面倒なので、 do_deployタスクに処理を追加する。

include recipes-kernel/zephyr-kernel/zephyr-sample.inc

SRC_URI += "file://my-app"

ZEPHYR_SRC_DIR = "${S}/../my-app"

do_deploy_append () {
    local src=${B}/zephyr/${ZEPHYR_MAKE_OUTPUT}
    src=${src%.*}.bin
    install -D ${src} ${DEPLOYDIR}/${PN}.bin
}

ビルド

下記のようにビルドを実行する。

$ bitbake my-app

すると、tmp/deploy/images/microbit-v1my-app.binも配置されるようになった。

これをmicro:bitの実機に書き込んで動作を確認してみる。

$ cd tmp/deploy/images/microbit-v1/
$ cp ./my-app.bin /media/mickey/MICROBIT/

f:id:mickey_happygolucky:20210828094041j:plain
実行結果

まとめ

YoctoでビルドするZephyrアプリケーションのコンフィグを変更して5x5 LEDを使用してみた。

下記のような問題があったが力技でなんとか回避した。

  1. do_menuconfigタスクが使えない
  2. ZephyrのBOARDコンフィグの変更のためにmeta-armを直接修正する必要がある。

ちょっと標準と外れたことをしようとすると、急にハードルが上がってしまうのがこの手の便利ツールのツライところ。

ただ、その中でもYoctoは比較的自由度が高いため、いろいろ手を尽くして回避することができる。 meta-arm提供のconfファイルで=で代入しているため、変更したいところがlocal.confで上書きできないのは、ちょっと気に入らない。

Yocto Zephyrのアプリケーションをqemu + gdbでデバッグ

はじめに

Yocto ProjectでビルドしたZephyrのアプリケーション(elf)をqemugdbデバッグしてみる。

環境

下記で作成した環境をベースに作業する

gdb-multiarch

使用するgdbUbuntu 20.04でaptで導入可能なgdb-multiarch

$ sudo apt install gdb-multiarch

runqemuについて

bitbakeでZephyrアプリケーションをビルドすると、下記のコマンドでqemuを使用してアプリケーションを実行することができる。

$ runqemu

bitbake同様に下記を実行して環境変数を設定しておく必要がある。

$ source poky/oe-init-build-env build

runqemuqemuを実行するためのラッパスクリプトで、実行するelfのパスや環境変数、必要なオプションなどを自動的にqemuに渡してくれるようになっている。

下記のようにすると追加で任意にパラメータをqemuに渡すことができる。

$ runqemu qemuparams='-serial mon:stdio'

runqemuの詳しい使い方は下記のように調べることができる。

$ runqemu --help

Usage: you can run this script with any valid combination
of the following environment variables (in any order):
  KERNEL - the kernel image file to use
  BIOS - the bios image file to use
  ROOTFS - the rootfs image file or nfsroot directory to use
  DEVICE_TREE - the device tree blob to use
  MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
  Simplified QEMU command-line options can be passed with:
    nographic - disable video console
    novga - Disable VGA emulation completely
    sdl - choose the SDL UI frontend
    gtk - choose the Gtk UI frontend
    gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
    gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
    egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
    serial - enable a serial console on /dev/ttyS0
    serialstdio - enable a serial console on the console (regardless of graphics mode)
    slirp - enable user networking, no root privileges is required
    snapshot - don't write changes to back to images
    kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
    kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
    publicvnc - enable a VNC server open to all hosts
    audio - enable audio
    [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
  tcpserial=<port> - specify tcp serial port number
  qemuparams=<xyz> - specify custom parameters to QEMU
  bootparams=<xyz> - specify custom kernel parameters during boot
  help, -h, --help: print this text
  -d, --debug: Enable debug output
  -q, --quiet: Hide most output except error messages

Examples:
  runqemu
  runqemu qemuarm
  runqemu tmp/deploy/images/qemuarm
  runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
  runqemu qemux86-64 core-image-sato ext4
  runqemu qemux86-64 wic-image-minimal wic
  runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
  runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
  runqemu qemux86 qemuparams="-m 256"
  runqemu qemux86 bootparams="psplash=false"
  runqemu path/to/<image>-<machine>.wic
  runqemu path/to/<image>-<machine>.wic.vmdk
  runqemu path/to/<image>-<machine>.wic.vhdx
  runqemu path/to/<image>-<machine>.wic.vhd

qemugdb serverとして実行する

qemuqemu上で実行中のelfをデバッグするために、gdb serverのプロトコル喋れるようになっている。 (厳密には多分いろいろ違うと思うけど、とりあえず。)

runqemuに下記のオプションをつけて実行すると、elfの先頭でブレーク状態に、localhostの1234ポートで接続できるようになる。

$ runqemu qemuparams="-S -s"
runqemu - INFO - Running bitbake -e ...
runqemu - INFO - Continuing with the following parameters:
KERNEL: [/home/mickey/work/yocto/x86-hardknott/mb-hardknott/build/tmp/deploy/images/microbit-v1/my-app.elf]
MACHINE: [microbit-v1]
FSTYPE: [elf]
ROOTFS: [/home/mickey/work/yocto/x86-hardknott/mb-hardknott/build/tmp/deploy/images/microbit-v1/my-app-image-microbit-v1.elf]
CONFFILE: [/home/mickey/work/yocto/x86-hardknott/mb-hardknott/build/tmp/deploy/images/microbit-v1/my-app-image-microbit-v1.qemuboot.conf]

runqemu - INFO - Running /home/mickey/work/yocto/x86-hardknott/mb-hardknott/build/tmp/work/x86_64-linux/qemu-helper-native/1.0-r1/recipe-sysroot-native/usr/bin/qemu-system-arm    -nographic -vga none  -machine microbit -cpu cortex-m0 -m 256  -s -S -serial mon:vc -serial null  -kernel /home/mickey/work/yocto/x86-hardknott/mb-hardknott/build/tmp/deploy/images/microbit-v1/my-app.elf -append '  mem=256M  '

この状態で、別の端末を開き下記のように実行する

$ cd tmp/deploy/images/microbit-v1/
$ gdb-multiarch my-app.elf

するとgdbのプロンプトが表示されるので下記のように入力する。

(gdb) target remote :1234

これでqemuに接続される。

下記のようにしてmain関数にブレークを貼る。

(gdb) b main

実行してみる。

(gdb) c
Continuing.

Breakpoint 1, main ()
    at /home/mickey/work/yocto/x86-hardknott/mb-hardknott/build/tmp/work/cortexm0-poky-eabi/my-app/2.5.0+gitAUTOINC+fe7c2efca8_c3bd2094f9-r0/my-app/src/main.c:10
10      printk("Hello My Application World! %s\n", CONFIG_BOARD);

きちんとmain関数でブレークすることが確認できた。

まとめ

YoctoでビルドしたZephyrアプリケーションのelfをqemu + gdbデバッグできることを確認した。

meta-armでmicrobit-v1のマシン定義をした人が丁寧にqemu対応のための設定も作り込んでくれているため、 micro:bit向けのelfをqemu向けにリビルドすることなくqemu + gdbデバッグができるようになっている。

BSP次第だが、Yoctoではこういうこともできるのがおもしろい。

(おまけ) micro:bitのrunqemuのための設定

meta-arm/conf/machine/microbit-v1.confの下記の部分。

runqemuで呼び出されるqemu-systemや、それに渡されるオプションを定義している。

# For runqemu
QB_SYSTEM_NAME = "qemu-system-arm"
QB_MACHINE = "-machine microbit"
QB_CPU = "-cpu cortex-m0"
QB_OPT_APPEND = "-nographic -vga none"
QB_RNG = ""

Yocto Zephyrに独自のアプリケーションを追加してみる

はじめに

Yocto ProjectでビルドするZephyrに独自のアプリケーションを追加してみる。

環境構築

この環境をベースに作業する。

環境変数設定

bitbake関連のツールを使用するために環境変数の読み込みは行う。

$ source poky/oe-init-build-env build

レイヤの作成

ビルド対象への追加も行う。

$ bitbake-layers create-layer meta-zephyr-app
$ rm -rf ./meta-zephyr-app/recipes-example/example
$ mv meta-zephyr-app ../poky
$ bitbake-layers add-layer ../poky/meta-zephyr-app

自動生成されるexampleのレシピはディレクトリごと削除する。

ソースコードの作成

下記のようなディレクトリツリーを作成する。

my-app
├── CMakeLists.txt
├── prj.conf
└── src
    └── main.c

CMakeLists.txt

Zephyrのhello_worldを参考に作成。ライセンスもApache-2.0とした。

# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my-app)

target_sources(app PRIVATE src/main.c)

prj.conf

下記の一行だけ。

# nothing here

echoとかで適当に作成してよい。

$ echo '# nothing here' > prj.conf

src/main.c

プログラム本体。

これもhello_world.cを参考に作成(というか丸パクリ)した。

/*
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <sys/printk.h>

void main(void)
{
    printk("Hello My Application World! %s\n", CONFIG_BOARD);
}

レシピの作成

既存のOSSなどWeb上にあるリポジトリソースコードアーカイブからレシピを作成する場合はrecipetoolが便利だが、 ローカルのソースツリーからレシピを作る機能はないため、手動で作成する必要がある。

my-app_0.1.bb

レシピはmeta-zephyr-app/recipes-example/my-appに配置されるようにする。

buildディレクトリで作業する場合下記のようにディレクトリを作成する。

$ mkdir -p ../poky/meta-zephyr-app/recipes/example/my-app

そして、その配下にmy-app_0.1.bbを下記の内容で作成する。

include recipes-kernel/zephyr-kernel/zephyr-sample.inc

SRC_URI += "file://my-app"

ZEPHYR_SRC_DIR = "${S}/../my-app"

自分のレイヤ以外のレイヤに存在するincファイルをincludeする場合、レイヤ直下からの相対パスを記述する必要がある。

下記のようにファイル名だけ記述した場合、includeされずにその行は無視されるので注意が必要となる。

include zephyr-sample.inc

ソースコードの配置

先ほど作成したmy-app以下のソースツリーをmeta-zephyr-app/recipes-example/my-app/filesに配置する。

my-appディレクトリがbuildディレクトリに作成していた場合下記のようにして移動する。

$ mkdir -p ../poky/meta-zephyr-app/recipes/example/my-app/files
$ mv ./my-app ../poky/meta-zephyr-app/recipes/example/my-app/files

最終的にmeta-zephyr-appは下記のようなツリーになる。

meta-zephyr-app/
├── COPYING.MIT
├── README
├── conf
│   └── layer.conf
└── recipes-example
    └── my-app
        ├── files
        │   └── my-app
        │       ├── CMakeLists.txt
        │       ├── prj.conf
        │       └── src
        │           └── main.c
        └── my-app_0.1.bb

ビルド

bitbakeを実行する。

$ bitbake my-app

recipes-kernel/zephyr-kernel/zephyr-sample.incが正しくインクルードできていれば、pokyではなくzephyrがビルドされる。

qemuで実行

$ runqemu qemux86 qemuparams="-serial mon:stdio"
(... snip ...)

*** Booting Zephyr OS build zephyr-v2.5.0  ***
Hello My Application World! qemu_cortex_m0

まとめ

意外と少ない手順でZephyrのアプリケーションを追加することができた。

YoctoでLinux以外のOSだったり、Cortext-M向けのバイナリをビルドするということにピンと来ていなかったが、意外と便利に使えるのかもしれない。