みつきんのメモ

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

pandoc-crossref + PlantUML

はじめに

pandocには図や表、ソースコードを相互参照するための便利な機能として pandoc-crossrefというフィルタある。

PlantUMLで作成した図をpandoc-crossrefで相互参照したい。

以前は

PlantUMLの図はテキストで記述するためコードブロックを使用する。

 ```plantuml

  start

  :test;

  stop
 ```

以前はコードブロックの参照にfigが使えなかったため、下記の様なワークアラウンドを行っていた。

 <div id="fig:uml-diagram">

 ```{.plantuml}
 start

 :test;

 stop
 ```

 UMLダイアグラム

 </div>

今(2022/03)は...!

PlantUMLのコードブロックにfigが使えるため下記のように書ける。

 ```{#fig:diagram .plantuml caption="ダイアグラム"}
 start

 :test;

 stop
 ```

結果

図 1 は下記のようになる。

f:id:mickey_happygolucky:20220318100610p:plain
図1:ダイアグラム

まとめ

意外とpandoc-crossrefとPlantUMLの組み合わせは情報が少ない。

誰しもが使いたいと思う機能ではあると思う。

pandoc環境を秘伝のタレ化して長年、化石のようなバージョンの環境を使用しているうちに できないと思っていたことができるようになっていた。

Dockerを使用して比較的新しいpandoc環境を簡単に構築する

pandocは便利で手放せないが、LTSのディストロを使用したりすると、 更新が追いつかなくなる。

自前で環境を作るにも結構手間がかかるため、一度環境を作ってしまうと、 秘伝のタレのように使い続けてしまいがちになる。

そのため、PCを新調したり、OSを入れ替えたりすると環境の構築が面倒くさい。

Dockerイメージ使用して、比較的新しいpandocの環境を簡単に作れないか模索する。

縛りは下記の点。

  1. pandoc公式のイメージをベースにする
  2. plantumlは使いたい
  3. emacsmarkdown-previewは使いたい

Dockerを使う場合でも、野良のイメージを使うと継続的なメンテナンスが行われていないケースが多く、 結局秘伝のタレになりがちなため、公式が提供しているpandoc/coreなどのイメージを使用する。

pandoc/coreを使う

entrypointにpandocが設定されているため下記のように使うことができる。

$ docker run --rm \
       --volume "$(pwd):/data" \
       --user $(id -u):$(id -g) \
       pandoc/core README.md -o outfile.html

--userを設定することで、出力ファイルの所有権が実行時のユーザーとなるように配慮されている。 これをしないと軒並みroot:rootとなるため、いちいちchownするのが面倒くさい。

dockerhubの例ではpdfを出力しようとしているが、 実際に試すと「pdflatex not found. Please select a different --pdf-engine or install pdflatex」となる。

下記のようなaliasを切っておくと、もうpandocそのもののように使える!(言い過ぎ)

alias pandock=\
'docker run --rm -v "$(pwd):/data" -u $(id -u):$(id -g) pandoc/core'

このイメージのベースとなるOSはAlpineなので、比較的サイズが小さい。 Ubuntuベースのイメージも提供されている。

この状態でpandoc-crossrefは使用可能となっているらしい。

plantumlを使う

インターネッツを徘徊した結果このDockerfileが一番求めていたものに近そう。

frozenbonito/docker-pandoc-eisvogel-ja

下記の点でこれをベースに作業することにした。

  • FROM行のfrozenbonito/pandoc-eisvogel-japandoc/latexがベース
  • 行数が短く処理内容がシンプル

ただ、このDockerfileは下記の点でそのまま使用することはしない。

  1. FROMをpandoc/coreにしたい
  2. ユーザーがrootであることを前提としている部分がある

あと、ダウンロードサイトをみると、githubからダウンロードするのが筋が良さそう。 sourceforge.netでも提供してるみたいだけど。

Dockerfile

こんな感じでDockerfileを作成する。

FROM pandoc/core

RUN apk add --no-cache \
    graphviz \
    openjdk11 \
    python3 \
    py3-pip \
    ttf-droid \
    ttf-droid-nonlatin

ARG plantuml_version="1.2022.2"
RUN wget https://github.com/plantuml/plantuml/releases/download/v${plantuml_version}/plantuml-${plantuml_version}.jar -O /opt/plantuml.jar

RUN pip3 install --upgrade pip
RUN pip3 install pandoc-plantuml-filter

ENV PLANTUML_BIN="java -jar /opt/plantuml.jar"

ENTRYPOINT [ "/usr/local/bin/pandoc" ]

適当にmypandocとかタグをつけてビルドする。

$ docker build . -t mypandoc

plantumlを使う

pandoc-plantumlフィルタがインストールされるので下記のように使用する。

$ docker run --rm \
       --volume "$(pwd):/data" \
       --user $(id -u):$(id -g) \
       mypandoc \
       README.md \
       --filter pandoc-plantuml \
       -o outfile.html

emacsmarkdown-previewを使えるようにする

gfm-modeの機能としてmarkdown-previewがある。 これはmarkdown-commandに設定されたコマンドが実行されると、編集中のmarkdownがhtmlに変換され、 その結果をブラウザで表示するものとなる。

その時に実行されるコマンドは標準入力からmarkdownを受け取り、標準出力にhtmlを吐き出す。 デフォルトではmarkdown-commandはmarkdownと設定されているためPATHが通っているところに markdownという名前のコマンドがあれば良いことになる。 markdownコマンドは次のような使い方が想定される。

$ markdown < README.md
<h1 id="test">test</h1>
<p>testtest</p>

markdownコマンドの内容は下記のようになる。

#!/bin/bash

docker run --rm \
       -i \
       --volume "$(pwd):/data" \
       --user $(id -u):$(id -g) \
       mypandoc \
       -s \
       -o tmp.html

cat tmp.html
rm tmp.html

docker runの時に-iコマンドで標準入力を受け付けるようにするところがポイントとなる。 一時ファイルtmp.htmlを出力し、catすることでhtmlを標準出力に吐き出している。

cssなどを指定したい場合にはコンテナに予め取り込んでおくか、--volumeなどでホスト上のファイルを コンテナから参照できるようにする必要がある。

まとめ

Dockerを使用して比較的新しいpandocの環境を 比較的簡単に作成する方法を調べた。

余談だが、新し目のplantumlはエレメントのデフォルト色が、 あのなんとなく残念なクリーム色では無くなっている。

Dockerは意外と便利。

Yocto Honister ラズパイ4でVulkanデモプログラムを動かしてみる

はじめに

なんとなく、Raspberry Pi 4 で Vulkan を試すと同様のことをPokyでもやってみた。

使用するのはラズベリーパイ4

動かすデモプログラムはSaschaWillems/Vulkan。これはVulkanのデモとしては有名なものらしい。

run_crops.sh(余談)

gistに置いた。

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

$ chmod +x ./run_crops.sh

Pokyの作成

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

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

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

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 = ""

core-image-x11をビルド

今回はGUIを使用するのでcore-image-x11をビルドする。

$ run_crops.sh bitbake core-image-x11

これが作業の土台となる。

ラズパイ向けのVulkanドライバ

下記コマンドでmesaのdevshellに入る。

$ run_crops.sh bitbake mesa -c devshell

Vulkanドライバがビルドされているかを確認する。

sh-4.4# ls -lha ../packages-split/mesa-vulkan-drivers
total 8.0K
drwxrwxrwx  2 root root 4.0K Oct 14 19:59 .
drwxr-xr-x 30 root root 4.0K Oct 14 19:59 ..

この時点でなにも生成されてないことがわかる。

devshellはexitコマンドで抜けられる。

mesaのレシピ

poky/meta/recipes-graphics/mesa/mesa.incの以下の部分を確認する。

# Vulkan drivers need dri3 enabled
# amd could be enabled as well but requires gallium-llvm with llvm >= 3.9
VULKAN_DRIVERS = ""
VULKAN_DRIVERS:append:x86:class-target = ",intel"
VULKAN_DRIVERS:append:x86-64:class-target = ",intel"
VULKAN_DRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'freedreno', ',freedreno', '', d)}"
PACKAGECONFIG[vulkan] = "-Dvulkan-drivers=${@strip_comma('${VULKAN_DRIVERS}')}, -Dvulkan-drivers='',"

ラズパイ向けのVulkanドライバをビルドするには、mesaのビルド時点で-Dvulkan-drivers=broadcomという設定が必要になるらしい。 この定義を見ていると、VULKAN_DRIVERSに",broadcom"が設定されていれば良さそう。

手早く確認するためにbuild/conf/local.confに下記の1行を追加してみる。

VULKAN_DRIVERS:append:rpi:pn-mesa = ",broadcom"

Override Syntaxが_から:に変わっているので、この辺りの表現はわかりやすくなっている。

この状態でmesaのレシピをビルドしてみる。

$ run_crops.sh bitbake mesa -f

再度devshellを起動してmeta-vulkan-driversの内容を確認する。

sh-4.4# ls -lha ../packages-split/mesa-vulkan-drivers
total 12K
drwxrwxrwx  3 root root 4.0K Oct 14 19:59 .
drwxr-xr-x 30 root root 4.0K Oct 14 19:59 ..
drwxr-xr-x  4 root root 4.0K Oct 14 19:59 usr

usrディレクトリが生成されている。

sh-4.4# find ../packages-split/mesa-vulkan-drivers -type f
../packages-split/mesa-vulkan-drivers/usr/share/vulkan/icd.d/broadcom_icd.aarch64.json
../packages-split/mesa-vulkan-drivers/usr/lib/libvulkan_broadcom.so

ドライバとjsonファイルが生成されていることがわかる。

ここまででラズベリーパイ向けのVulkanドライバがビルドできた。

Vulkanデモプログラム

デモプログラムはSaschaWillems/Vulkanを使用する。

いつもの三角形や歯車が実装されている。

レイヤの作成

デモプログラムをPokyに組み込むためにレシピを作成することになるが、そのレシピを組み込むためのレイヤを作成する。

$ run_crops.sh bitbake-layers create-layer meta-vulkan-demo
$ rm -rf ./build/meta-vulkan-demo/recipes-example
$ rm -rf ./build/meta-vulkan-demo/recipes-recipetool
$ mkdir -p ./build/meta-vulkan-demo/recipes-graphics/vulkan
$ mv ./build/meta-vulkan-demo ./poky
$ run_crops.sh bitbake-layers add-layer $(pwd)/poky/meta-vulkan-demo

レシピの作成

recipetoolを使用してデモプログラムのレシピを作成する。

$ run_crops.sh recipetool create https://github.com/SaschaWillems/Vulkan.git -o $(pwd)/poky/meta-vulkan-demo/recipes-graphics/vulkan/vulkan-demo_git.bb

do_configureでエラー

試しにビルドすると下記のようなエラーが発生した。

(...snip...)
| CMake Error at CMakeLists.txt:46 (find_library):
|   Could not find Vulkan_LIBRARY using the following names: vulkan
(...snip...)
ERROR: Task (/home/mickey/work/yocto/rpi_honister_vulkan/poky/meta-vulkan-demo/recipes-graphics/vulkan/vulkan-demo_git.bb:do_configure) failed with exit code '1'
NOTE: Tasks Summary: Attempted 773 tasks of which 771 didn't need to be rerun and 1 failed.

Summary: 1 task failed:
  /home/mickey/work/yocto/rpi_honister_vulkan/poky/meta-vulkan-demo/recipes-graphics/vulkan/vulkan-demo_git.bb:do_configure
Summary: There was 1 ERROR message shown, returning a non-zero exit code.

vulkan関連のモジュールが見つからないのでdo_configureタスクに失敗している。

diff --git a/recipes-graphics/vulkan/vulkan-demo_git.bb b/recipes-graphics/vulkan/vulkan-demo_git.bb
index c173b4f..a837a5d 100644
--- a/recipes-graphics/vulkan/vulkan-demo_git.bb
+++ b/recipes-graphics/vulkan/vulkan-demo_git.bb
@@ -35,7 +35,7 @@ S = "${WORKDIR}/git"
 # NOTE: unable to map the following CMake package dependencies: Wayland Vulkan DirectFB
 # NOTE: the following library dependencies are unknown, ignoring: vulkan-1 vulkan
 #       (this is based on recipes that have previously been built and packaged)
-DEPENDS = "libxcb wayland-protocols"
+DEPENDS = "libxcb wayland-protocols vulkan-loader"

 inherit cmake pkgconfig

ビルド時の依存関係にvulkan-loaderを追加すると、do_configureは成功し、do_compile以降の処理が実行されるようになる。

do_installのエラー

do_installタスクで下記のエラーが発生した。cmakeでのビルドプロセスにinstallターゲットが存在しないとなっている。

... (snip) ...
| ninja: error: unknown target 'install'
... (snip) ...

SaschaWillems/Vulkanの作業例ではmake install的なコマンドは見当たらなかった。 インストールプロセスをまともに実装していないのかもしれない。

その場合は自分でdo_installタスクを作成する。

devshellでビルド結果を確認してみる。

sh-4.4# find ../build -type f | less
(...snip...)
../build/bin/indirectdraw
../build/bin/texturearray
../build/bin/computenbody
../build/bin/gltfscenerendering
../build/bin/deferredshadows
../build/.ninja_lo

必要そうなものはbuild/bin以下に固まっているようだ。なのでとりあえず下記のようなdo_installタスクを実装する。

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/bin/* ${D}${bindir}
}

よく使用される変数

do_installタスクを実装する際にだいたい必要になることが多い変数を下記にまとめる。

変数 概要 ディレクト
WORKDIR bitbake中のレシピの作業ディレクト (..snip..)/vulkan-demo/1.0+gitAUTOINC+821a0659a7-r0
S bitbake中のソースディレクト ${WORKDIR}/git
D bitbake中のinstall先 ${WORKDIR}//build
B bitbake中のビルドディレクト ${WORKDIR}//image
bindir ルートFS中の実行ファイルインストール先 /usr/bin
datadir ルートFS中のデータインストール先 /usr/share

WORKDIRは通常、bitbake時のbuild/tmp/work/CPUアーキ/レシピ/バージョンとなる。

これらの変数の実際の値は下記のように確認することができる。

$ run_crops.sh bitbake "レシピ名" -e | grep '^変数名='

# vulkan-demoでDを確認したい場合は下記のようにする。
$ run_crops.sh bitbake vulkan-demo -e | grep '^D='
D="/home/mickey/work/yocto/rpi_honister_vulkan/build/tmp/work/cortexa72-poky-linux/vulkan-demo/1.0+gitAUTOINC+821a0659a7-r0/image"

デモプログラムの実行

bitbakeが通るようになったので、vulkan-demoを組み込んだイメージを作成する。

local.confに下記の一行を追加する。

IMAGE_INSTALL:append = " vulkan-demo"

core-image-x11を作成し、ラズパイ上でデモプログラムであるgearsを実行する。 すると下記のようなエラーが発生する。

root@raspberrypi4-64:~# gears
Error: Could not find asset path in /home/mickey/work/yocto/rpi_honister_vulkan/build/tmp/work/cortexa72-poky-linux/vulkan-demo/1.0+gitAUTOINC+821a0659a7-r0/git/data/

エラー自体はデモプログラムの実行に必要なアセットと呼ばれるデータファイルが見つからないというもの。 このエラーの問題点は下記の2つ

  1. ルートFSにアセットが含まれていない
  2. デモプログラムがアセットを読み出すパスがプログラムのビルド時のホストPC上のディレクトリを指している

アセットの取得

SaschaWillems/VulkanAssets の項にアセットを取得する方法が記載してある。

python download_assets.py

vulkan-demoのレシピにこの処理を追加してやれば良さそう。

do_compile:append() {
    cd ${S}
    ./download_assets.py
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/bin/* ${D}${bindir}

    install -d ${D}${datadir}/Vulkan/data
    cp -r ${S}/data ${D}${datadir}/Vulkan
}

FILES:${PN} += "${datadir}/Vulkan/data"

do_compileタスクに処理を追加し、download_assets.pyを実行する。 次に、取得したアセットがビルドしたパッケージに含まれるようにインストールする。 最後に、インストールされたデータがきちんとパッケージに反映されるようにFILESに追加する。

この修正後にbitbakeしてdevshellでpackages-split/vulkan-demoディレクトリを確認してみる。

$ run_crops.sh bitake vulkan-demo
$ run_crops.sh bitake vulkan-demo -c devshell
sh-4.4# cd ../packages-split/vulkan-demo/usr/share
sh-4.4# ls
Vulkan

packages-split/vulkan-demo/usr/share以下にVulkanディレクトリがあれば、 アセットはルートFSに含まれるようになっている。

アセットのロードパスの変更

アセットがルートFSに含まれるようになったが、問題はデモプログラムがアセットを読み込むパスを変更する必要があるということ。 このパスはどのように決定されているのだろうか。

ソースコードにエラーメッセージの一部の文字列でgrepしてみる。

$ run_crops.sh bitbake vulkan-demo -c devshell
$ grep -r 'Could not find asset path in' .
base/vulkanexamplebase.cpp:             std::cerr << "Error: Could not find asset path in " << getAssetPath() << "\n";

getAssetPath()の実装を確認する。外部関数として実装されているのはbase/VulkanTools.cppのみのようだ。

const std::string getAssetPath()
{
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
    return "";
#elif defined(VK_EXAMPLE_DATA_DIR)
    return VK_EXAMPLE_DATA_DIR;
#else
    return "./../data/";
#endif
}

VK_EXAMPLE_DATA_DIRが定義されていればその内容が展開されreturnされるようだ。 VK_EXAMPLE_DATA_DIRがどのように定義されているかを確認する。

$ grep -r VK_EXAMPLE_DATA_DIR .
./CMakeLists.txt:   add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${RESOURCE_INSTALL_DIR}/\")
./CMakeLists.txt:   add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${CMAKE_SOURCE_DIR}/data/\")

CMakeLists.txtで定義されているようだ。

if(RESOURCE_INSTALL_DIR)
    add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${RESOURCE_INSTALL_DIR}/\")
    install(DIRECTORY data/ DESTINATION ${RESOURCE_INSTALL_DIR}/)
else()
    add_definitions(-DVK_EXAMPLE_DATA_DIR=\"${CMAKE_SOURCE_DIR}/data/\")
endif()

内容を確認すると、RESOURCE_INSTALL_DIRが定義されている場合はその内容がVK_EXAMPLE_DATA_DIRに代入されるように見える。 RESOURCE_INSTALL_DIRはレシピ中のEXTRA_OECMAKEで定義することができる。

レシピに下記の行を変更する。

EXTRA_OECMAKE = "-DRESOURCE_INSTALL_DIR=${datadir}/Vulkan/data"

vulkan-demo_git.bb完成形

vulkan-demo_git.bbの完成形を以下に示す。

LICENSE = "MIT & Unknown"
LIC_FILES_CHKSUM = "file://LICENSE.md;md5=dcf473723faabf17baa9b5f2207599d0 \
                    file://external/glm/util/conan-package/lib_licenses/LICENSE1.txt;md5=37ffc29490b4cf57fd7e5ffba4c1b185 \
                    file://external/glm/util/conan-package/lib_licenses/LICENSE2.txt;md5=f7f57caa96a29810aa62259f274b374b \
                    file://external/tinygltf/LICENSE;md5=82f6e601fc2fd27cbf55d6f5f176d017 \
                    file://external/imgui/LICENSE.txt;md5=f3c4ae64ab2a23f1b8734609e1a2d48a \
                    file://external/ktx/LICENSE.md;md5=654c86a2bade5bb7fbcde45788f98a61"

SRC_URI = "gitsm://github.com/SaschaWillems/Vulkan.git;protocol=https;branch=master"

PV = "1.0+git${SRCPV}"
SRCREV = "821a0659a76131662b1fc4a77c5a1ee6a9a330d8"

S = "${WORKDIR}/git"

DEPENDS = "libxcb wayland-protocols vulkan-loader"

inherit cmake pkgconfig

EXTRA_OECMAKE = "-DRESOURCE_INSTALL_DIR=${datadir}/Vulkan/data"

do_compile:append() {
    cd ${S}
    ./download_assets.py
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/bin/* ${D}${bindir}

    install -d ${D}${datadir}/Vulkan/data
    cp -r ${S}/data ${D}${datadir}/Vulkan
}

FILES:${PN} += "${datadir}/Vulkan/data"

gitリポジトリにサブモジュールが含まれている場合、 recipetool createによってSRC_URIのfetcherがgitsm://になっている。 このようにできる範囲で最適なfetcherを選択してくれる。

動作画面

ラズパイ4でgearsが動いた。

f:id:mickey_happygolucky:20220305123817j:plain
Vulkanのgears

まとめ

ラズパイ4でVulkanのデモプログラムを動かしてみた。 デフォルト設定ではvulkanドライバが作成されないので、VULKAN_DRIVERS",broadcom"を追加する必要がある。

recipetoolを使って新規にレシピを作成する方法もざっと紹介した。

ラズパイ4のVulkanは結構ヌルヌル動いた。

Yocot Project CROPSを拡張してkasを使ってみる

はじめに

CROPSを使用すると、ホスト環境の差異を気にせずにbitbakeができるようになるので 非常に便利だが、たまにhosttoolsを追加したりCROPSのDockerイメージを拡張したいケースがある。

その際にCROPSの提供物を直接編集することなく、Dockerイメージを拡張できる方法を模索する。

今回は例としてmeta-raspberrypiをkasでビルドしてみる。

kasのインストール

kasについては以前にYocto meta-raspberrypi環境の作成にkasを使ってみるで扱ったことがある。

bitbakeをラップして、操作を簡略化するためのツール

kasは当然bitbakeを実行するためのホストPCにインストールする必要があるが、CROPSのDockerイメージには入っていない。

Dockerfileを作成する

CROPSのイメージをカスタマイズして、オリジナルのDockerイメージを作成する。

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

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

CROPSのDockerイメージはdockerhubで提供されているため、FROM crops/pokyのように指定することができる。

FROM crops/poky

RUN apt-get update && apt-get upgrade -y && \
    apt-get install -y python3-pip && \
    pip3 install kas

こんな感じでいけるはず。

my-cropsという名前で一回ビルドしてみる。

$ docker build -t my-crops .

下記のようなエラーが発生する。

E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
E: Unable to lock directory /var/lib/apt/lists/

13: Permission denied

apt-getするための権限がないと言っている。

まず、このイメージの中でのユーザーを確認する。

FROM crops/poky

RUN whoami && id
$ docker build -t my-crops .
(... snip ...)
Step 2/2 : RUN whoami && id
 ---> Running in bc35fe0307ff
usersetup
uid=70(usersetup) gid=70(usersetup) groups=70(usersetup)
Removing intermediate container bc35fe0307ff
 ---> abc853892995

usersetupというユーザーでuid=70gid=70らしい。

CROPSのDockerfileを確認すると次のようになっている。

( ...snip ...)
USER usersetup
ENV LANG=en_US.UTF-8
( ...snip ...)

USERでユーザーを指定している。

USERを切り替える

Dockerfileを下記のように書換えてみる。

FROM crops/poky

USER root

RUN apt-get update && apt-get upgrade -y && \
    apt-get install -y python3-pip && \
    pip3 install kas

USER usersetup
$ docker build -t my-crops .
(... snip ...)
uccessfully tagged my-crops:latest

Dockerイメージまではできたようだ。

meta-raspberrypiをkasでビルド

meta-raspberrypiの取得

gitでmeta-raspberrypiを取得する。

$ git clone git://git.yoctoproject.org/meta-raspberrypi -b honister

ブランチの変更

meta-raspberrypiからみて、外部のリポジトリをダウンロードしてくる。

  • poky
  • meta-openembedded
  • meta-qt5

これらはmasterブランチを使用するようになっているが、今回はhonisterを使用したい。

プロジェクトファイルとなるkas-poky-rpi.ymlのrefspecで、closeするリポジトリのブランチやコミットハッシュを指定することができる。

今回は下記のようにしてrefspecをmasterからhonisterに書き換える。

$ sed 's/master/honister/' -i meta-raspberrypi/kas-poky-rpi.yml

CROPSでkasを実行

my-cropsからコンテナを作成しkasを実行する。

$ docker run --rm -it -v "$(pwd)":"$(pwd)" my-crops --workdir="$(pwd)" bash -c "kas build $(pwd)/meta-raspberrypi/kas-poky-rpi.yml"

まとめ

CROPSのDockerイメージにパッケージを追加する方法試してみた。

Dockerfileを新規に作成し、FROM poky/cropsとすることで、CROPSが提供するDockerfileを編集するすることなく CROPSのイメージを拡張することができた。

USERがroot以外になっているため、apt-getのようなroot権限が必要なコマンドの前にユーザーを切り替える必要があることに気をつける必要がある。

サードパーティのBSPなどレイヤの作りによっては、ホストPC側にコマンドを追加したいケースがあるが、 そういう場合でもCROPSを使用できることが確認できた。

Yocto Project CROPSでmeta-riscvをビルドしてみる

はじめに

私の記事を参考にして、meta-riscvをビルドしようとしたけどうまくいかないらしかったので試してみる。

大まかな流れ。

Quick Startの手順をなぞる。

  1. repoの設定(省略)
  2. ワークスペースの作成
  3. setup.shの実行
  4. bitbake実行

CROPSは3の手順から使用する。

ワークスペースの作成

mkdir riscv-yocto && cd riscv-yocto
repo init -u git://github.com/riscv/meta-riscv  -b master -m tools/manifests/riscv-yocto.xml
repo sync
repo start work --all

setup.shの実行

CROPS実行用のスクリプトを作成

riscv-yoctoディレクトリにrun_meta-riscv.shとかの名前で下記のようなスクリプトを作成する。

引数なしで実行した場合は、meta-riscv/setup.shを実行し、 引数を指定した場合は、そのコマンドラインを実行する。

#!/bin/sh

BUILD_DIR=build

cmd="source $(pwd)/openembedded-core/oe-init-build-env ${BUILD_DIR}"
if [ $# -gt 0 ] ; then
    cmd="${cmd} && $*"
else
    cmd="source $(pwd)/meta-riscv/setup.sh"
fi

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

実行権限をつけておく。

$ chmod +x ./run_meta-riscv.sh

setup.shを実行する

$ ./run_meta-riscv.sh

bitbake実行

setup.shの結果、buildディレクトリが作成され、その中にauto.confやlocal.conf、bblayers.confなどが作成される。

local.conf

local.confの末尾に次の一行を追加する。

USER_CLASSES:remove = "image-prelink"

bitbakeを実行する

次のようにしてCROPSでbitbakeを実行する。

$ ./run_meta-riscv.sh MACHINE=qemuriscv64 bitbake core-image-full-cmdline

これでイメージがビルドされる。

runqemuの実行

qemuの実行はネットワークデバイスの設定などが面倒臭いのでCROPSは使わずに直接ホストマシンで実行する。

$ source openembedded-core/oe-init-build-env
$ MACHINE=qemuriscv64 runqemu nographic

ホストマシンに必要なパッケージが不足しているとエラーになるが、基本的にはこれでいけるはず。

こんな感じで起動が面がでてくる。

OpenSBI v0.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : timer,mfdeleg
Platform HART Count       : 4
Firmware Base             : 0x80000000
Firmware Size             : 124 KB
Runtime SBI Version       : 0.2

Domain0 Name              : root
Domain0 Boot HART         : 3
Domain0 HARTs             : 0*,1*,2*,3*
Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 3
Boot HART Domain          : root
Boot HART ISA             : rv64imafdcsu
Boot HART Features        : scounteren,mcounteren,time
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count      : 0
Boot HART MHPM Count      : 0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
[    0.000000] Linux version 5.15.16-yocto-standard (oe-user@oe-host) (riscv64-oe-linux-gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37.20210721) #1 SMP PREEMPT Tue Jan 25 14:32:29 UTC 2022
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Machine model: riscv-virtio,qemu
[    0.000000] Memory limited to 256MB
[    0.000000] efi: UEFI not found.
[    0.000000] Zone ranges:

rootでログインしてみる。

OpenEmbedded nodistro.0 qemuriscv64 ttyS0

qemuriscv64 login: root
root@qemuriscv64:~# cat /etc/os-release 
ID=nodistro
NAME="OpenEmbedded"
VERSION="nodistro.0"
VERSION_ID=nodistro.0
PRETTY_NAME="OpenEmbedded nodistro.0"
root@qemuriscv64:~#

まとめ

いちおう、問題なくビルドはできていそう。

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を使用することで不便なことはなさそう。