STM32MP157 でLinuxを走らせる
Overview
STM32シリーズは最近需要の拡大に伴って高機能なシリーズや低電力シリーズなど新たな製品を続々投入しています。そのひとつにSTM32MP1シリーズがあります。
MP1は、Cortex-A7(Single or Dual)とCortex-M4を両方備えたチップで、主にLinuxを動作させることを想定した高性能チップです。OpenGLの計算が行えるほか、TrustZoneにも対応し、外部にDDRやeMMCといったメモリを取り付けることができる柔軟な設計になっています。
様々な方法がありますが、Cortex-Aで画像処理などLinuxの基本機能を動作させ、Cortex-Mでマイコンらしくリアルタイム性の求められる処理を行う等の使い方となります。FPGAとCortexを同居させるチップが時々ありますが、あのイメージで良いかと思います。
従来画面が出せるSTMマイコンといえばH7とかがありましたが、あれとはCortex-Aを積んでいるかどうかが大きな差となるわけです。MPの「P」はプロセッサーがウリなことを意味しています。
ここでは、STMicro公式から公開されているOpenSTLinux distributionをMP1上で動作させます。
STM32MP157F-DK2
MP157はCortex-A7 Dual と Cortex-M4を備えています。今回は画面付きの開発者キットであるSTM32MP157F-DK2を使用します。
AプロセッサでOpenSTLinuxを動作させ、Mでリアルタイム性の必要な動作をさせます。
AとMはOpenAMPベースの通信が内部で行えるようになっているので、Mが実行するプログラムはA側から指定してあげるような形で動作させることとなります。
また、このボードはLinuxをSDカードから起動するような形になります。
そもそもなんでLinuxいれようって話になったんだっけ?
マイコンで画面表示などの計算をさせようとすると当然ながら垂直水平同期して...みたいな話になりますが、これらの計算を効率よく実施するにはRTOSなどある種のOSが必要になってきます。
しかしながらRTOSにも向き不向きがあり、リアルタイム性を担保するためにプロセスの優先順位に気を配る必要があったりします。
だとしても通常OSを積めばリアルタイム性が失われるケースは当然あるわけで、選択を迫られてきました。
MP1の場合、リアルタイム性の必要な処理とOSをチップごと分けられるので、Linuxのような保守性の高いソフトウェアならOSを積んで、Mの方でRTOSやベアメタルで実行すれば、より柔軟な大規模システム開発が可能になります。
ファイルシステムが必要な処理や、グラフィックアプリケーションはOSの方が有利ですし、今まで開発されてきたLinux向けのソフトウェアが、OpenSTLinuxのカーネルがハードウェアの差異を吸収するのでほぼそのまま実行できることとなります。これによってOpenGLやその他プロトコルスタックを使った開発がいい感じにできるようになる(はず)です。
Yocto
さて、組み込み向けにLinuxを使うとはいっても、その組み込み製品にあるLinux distributionのすべての機能が必要かと言われると大体の場合そうではないでしょう。
たとえば、UbuntuやDebianをいれるまでもないことが多いはずです。一般的なLinuxはGBレベルのファイル容量になりつつあるので、組み込み向けにそのまま使うのは少々厳しいものがあります。また、そのようなdistributionはCPUアーキテクチャの指定があって入れられないなんてこともあるでしょう(arm64がないとか)
Yoctoは、Linuxをそれぞれ組み込み機器が持つ特性に合わせた最適化した構成で作成するツールです。具体的には、使いたいLinuxのカーネルや機能を選んだ「レシピ」をYoctoに読み込んでビルドすることで自分に必要なコンパクトなLinuxを作成することができるわけです。
組み込み向けだと近年デファクトになっており、OpenSTLinuxとよばれるものもこのYoctoでビルドされたものです。
左側のグレーの部分が、Linuxに必要なブートローダやカーネル、上の黄色部分が各ボード向けに追加したいアプリケーションやソフトウェアです。
これを「レシピ」と言います。Yoctoはレシピに従い必要なソースを自動ダウンロードし、最終的なLinuxイメージを生成します。
これによって
- Linuxのファイルサイズを最小限に抑えられる
- 様々なアーキテクチャに対応できる
- セキュリティホールになりうるソフトを減らせる
等のメリットが生まれます。
環⭐︎境⭐︎構⭐︎築
組み込み開発なので、当然開発ボードとケーブルをつないだりするわけですが、OSがいくつか登場するので整理していきましょう.
まず大前提として、開発ボードとPCはケーブルでつなぎますが、OSを書き込むときはSDカードから行います。
また、話の流れで、MP1に乗っているLinux、PCのOS、PC上に仮想環境で作ったLinuxが出現するので気をつけてください。
Yoctoののビルド自体は様々なOSで使用できるように作られていますが(CROSという機能を使うことで実現できる)、大概なにかしら本質的でないところで問題が発生して詰まることになるので、おとなしくLinuxをVMをたてて実行することをお勧めします。
ただし、Yoctoのビルドはそれなりにマシンパワーが必要かつ時間がかかるので、ノートPCなど持ち運ぶ環境しかない場合はクラウドコンピューティングで計算させてしまうという手もあると思います。
ここでは、Windows11にVirtualBoxを使ってUbuntu 20.04を構成して作業します。
一応Yocto 公式ドキュメントではWSLを使う方法なども紹介されていますのでそちらがいい人はチャレンジしてみてください。
My Build Environment
- AMD Ryzen 5 3600 6-Core Processor 3.59 GHz
- RAM 48GB
- Windows 11 Pro 22H2
- VirtualBox 7.0.6
仮想マシン - Ubuntu 20.04.5 LTS (Focal Fossa) 64bit
- RAM 24GB
- ROM 200GB
とりあえずこんな感じのやや高スペックめな仮想マシンを作ってみました.
依存関係のインストール
Ubuntuはすでにはいっているものとして話を進めていきます。
sudo apt-get update
sudo apt-get install gawk wget git diffstat unzip texinfo gcc-multilib build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev pylint xterm bsdmainutils libssl-dev libgmp-dev libmpc-dev lz4 zstd
sudo apt-get install make xsltproc docbook-utils fop dblatex xmlto
sudo apt-get install libmpc-dev libgmp-dev
sudo apt-get install build-essential libncurses-dev libyaml-dev libssl-dev
sudo apt-get install repo
sudo apt-get install coreutils bsdmainutils sed curl bc lrzsz corkscrew cvs subversion mercurial nfs-common nfs-kernel-server libarchive-zip-perl dos2unix texi2html diffstat libxml2-utils
sudo apt install gitsome
続いて,repoという管理ツールをいれます
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
OpenSTLinux
ここから本題のLinuxのビルド作業にはいっていきます。
Yoctoによっていくら簡単にLinuxが構成できるようになったとはいっても、レシピをすべて手書きするためにはそれなりに高度なハードウェアとLinuxへの理解が必要です。
各ペリフェラルを動かすドライバや機能レイヤーを実装するのが以下に面倒かはなんとなく想像できることかと思います。
そこで多くのSoCベンダーは、ボードサポートパッケージ(BSP)とよばれるそのチップで動く標準的なLinuxをビルドできるYoctoレシピを公開していることが多いです。
STMicroの場合はそれがOpenSTLinuxにあたるわけです。
このBSPに関する考え方は各社ちょっとずつ違うので一概にはいえませんが、大体の場合オープンソースかつ無保証の場合が多いです。また、信頼性が必要なターゲットに向けて保証付きのLinuxを売っている会社さんなんかもあったりします。
詳しくはライセンスをよんでください。
公開されているOpenSTLinuxには、図に示す通り、Starter package、Developer package、Distribution Packageの3つがあります。
Starterは、MP1の開発ボードでしか動かない、ビルド済みファイルです。
Developerは、MP1の開発ボードでしか動かない、ビルド済みファイルとYoctoレシピが公開されています。
DistributionはYoctoレシピしか公開されておらず、カスタムボードを作ったり開発環境をビルドできる全部入りパッケージみたいなやつです。製品をつくるとき使います。
STMicroのチュートリアルでは簡単なほうから試せ的なことが書いてありますが、英語はむずかしくてよめない!!!!!!!!!ということで、Distribution Packageをビルドしていきます。
コードはGitHubからおとして使います。
が、どういうわけか先ほどいれたrepoコマンドなる環境を使ってやれという指示がドキュメントにあるので、従っていきます。
今回は、openstlinux-5.15-yocto-kirkstone-mp1-v22.11.23をビルドしていきます。(
現時点での最新)
Let's Build
作業場所としてtmpフォルダを選びました。
cd /tmp
mkdir Distribution-Package
cd Distribution-Package
repo init -u https://github.com/STMicroelectronics/oe-manifest.git -b refs/tags/openstlinux-5.15-yocto-kirkstone-mp1-v22.11.23
このときに、global configにgitのユーザー情報が入っていないと失敗するパターンがあるようなので、その場合は適宜設定してください。↓
git config --global user.name "waarrk"
git config --global user.email waarrk@example.com
git pull的なことをやります。(repo
は複数のgitリポジトリを同時に相手できるバージョン管理ツールのようです)
repo sync
ビルドツールを初期化するセットアップスクリプトを実行します。
DISTRO=openstlinux-weston MACHINE=stm32mp1 source layers/meta-st/scripts/envsetup.sh
EULAという画面が出ると思います。内容としてはOpenSTLinuxを動かすための依存ソフトウェアのライセンスについて説明するから読んで承認してねという内容です。
あとはこの命令を打つとビルドが始まります。実行したが最後数時間はかかるので注意してください
bitbake st-image-weston
見てる感じメモリよりCPUが重要っぽいですね。
コーヒーブレイク
bitbake
最後に実行したbitbakeは言ってしまえばYoctoのビルドツールです。
(C言語で言うところのmakeのような立ち位置)
さまざまなレシピ情報から最後のイメージファイルを作り出すのが役目です。
weston
さきほどからしばしば登場するwestonというワードは、正式名称をwayland/westonといい、Linuxアプリケーションとそれを描画するOpenGLの橋渡しをする。ディスプレイサーバーの一種です。
ディスプレイサーバは、カーネルと実際に描画したいGUIを記述しているアプリケーションを仲介してくれるのがです。これがないと結局ユーザーは全てを手書きすることになり、フレームワークの優位性が失われます。
Wikipedia ディスプレイサーバ
ディスプレイサーバの代表格はX Window SystemとかWaylandです。特にXはめちゃめちゃ有名なのでご存知の方も多いでしょう。
しかしXは1980年代に開発された技術です。UbuntuもDebianもまだ登場していなかった時代ですから、コンピューターを取り巻く状況は大きく様変わりし、長い歴史の中で機能拡張を繰り返した結果巨大で複雑なシステムになりました。そんな中登場したのがWaylandです。最小のディスプレイサーバとウィンドウコンポジッタを組み合わせたもので、軽量なので組み込み用途に適しています。最近ではUbuntuのデフォルトのディスプレイサーバがWaylandに切り替え始められていたりと、徐々に流行りのディスプレイサーバはWaylandに移りつつあります。
組み込み界隈でもこれは例外ではなく、BSP上で稼働するLinuxでもWaylandがスタンダードになってきています。
Graphical User Interface Using Flutter in Embedded Systems - Hidenori Matsubayashi, Sony https://www.youtube.com/watch?v=KN-ileJvorg
SDカード作成
さて、ビルドが終わると、/tmp/Distribution-Package/build-openstlinuxweston-stm32mp1/tmp-glibc/deploy/images/stm32mp1/flashlayout_st-image-weston/trusted/
の下にずらずらとビルド後のファイルがはきだされているのがわかります。いろんな石やブート方法向けのやつが生成されていますが、今回はSDブートなのでFlashLayout_sdcard_stm32mp157f-dk2-trusted.tsv
を選択して、フラッシュ書き込み用のファイルを生成します。
/tmp/Distribution-Package/build-openstlinuxweston-stm32mp1/tmp-glibc/deploy/images/stm32mp1/scripts
以下で次のコマンドを実行します。
cd tmp-glibc/deploy/images/stm32mp1/scripts
sudo ./create_sdcard_from_flashlayout.sh /tmp/Distribution-Package/build-openstlinuxweston-stm32mp1/tmp-glibc/deploy/images/stm32mp1/flashlayout_st-image-weston/trusted/FlashLayout_sdcard_stm32mp157f-dk2-trusted.tsv
なにやらパーティションを切ったり作業している様子が見えますね
SDカードへの書き込み
あとはPCにSDカードを差し込んで、dd
コマンドでコピーしてやれば完了です。
sudo dd if=/tmp/Distribution-Package/build-openstlinuxweston-stm32mp1/tmp-glibc/deploy/images/stm32mp1/flashlayout_st-image-weston/trusted/../../FlashLayout_sdcard_stm32mp157f-dk2-trusted.raw of=/dev/sdb bs=8M conv=fdatasync status=progress
うごいたぜ!!!!
USB-Cケーブルを差し込んんでーーー
STMicro謹製のOpenGLサンプルや動画なんかが再生できました。
ここからいろいろOSをカスタムしていきたいですね。
OpenSTLinuxの内部構造
詳しいファイル構成については今ここで書くと終盤でなくなるので、リンクをはるにとどめますが、
OpenSTLinuxは次の5つくらいのシステムの集合体として構成されています。
聞きなれない言葉として、U-Boot、TF-A、OP-TEEという言葉があるかと思うのでそれぞれ軽く触れておきます。
U-Boot
U-Bootは、MP1上でLinuxを起動させるためのオープンソースのブートローダです。
Arduinoに置き換えて考えると分かりやすいかと思いますが、あれは起動する際にハードウェアの差分を吸収するために不揮発領域に書き込んだブートローダを使ってFlashにピン情報とかを読み出しますね?
これと同様のことを行っています。(正確にはもっと多い)
FSBLとSSBL
たったいま、Arduinoのブートローダを例に挙げましたが、実はその理解は正しくありません。
Linuxをブートする場合、ブートローダは三段階で発動します。
まず0段目、電源を投入すると、組み込まれた専用のROMから、数十キロバイトのデータを読み出し組み込みのSYSRAMに展開します。(これがArduinoでいうブートローダに近いでしょう)
次にFSBLというブートローダが動きます。(first stage bootloader)
FSBLは、MP1に取り付けられた外部のメモリを初期化するためのブートローダです。多くの場合はDDRとかになるでしょうが、これを初期化してそのメモリ空間に必要な情報を書き込みます。
これがU-bootのひとつめの機能、U-boot SPLです。
そしてやっとSSBLが動きます。
SSBL(second stage bootloader)は、FSBLが外部メモリ上に展開した情報を用いてUSB、イーサネット、ディスプレイ等の初期化を行います。
U-bootのふたつめの機能、U-bootです。
なぜこんな複雑な実装になっているかというのは単純な理由で、作りたいボードによって、外部メモリの実装が変わるからFSBLが必要で、また使う機能も変わるからSSBLが必要というわけです。FPGAの初期化でも似たイメージの処理をするので、それに近いでしょう。
U-bootには、この初期化を行わせてLinuxカーネルに渡すという特性上、ハードウェアレイヤーのコードを書かねばならないので、それに便利な関数やなんかが用意されています。
TF-A
TF-A(Trusted Firmware-A)を説明するためには、まずTrustZoneを説明する必要があります。
TrustZone
TrustZoneは、ARMマイコンの一部に内蔵されている対セキュリティ機能のひとつです。
そもそもマイコンに対する攻撃は、①ソフト的な攻撃②物理的な攻撃にわけられます。
TrustZoneは前者を想定した機能で、例えば勝手にファームを書き換えて装置を誤動作させようとか、ブートローダを改ざんして内部データを盗もうという輩への対応策です。
STMの公式セミナー動画がわかりやすかったのでこちらの例で説明します。
この例では、Flashメモリを悪意のあるものに張り替えて、呼び出されたプログラムから内部データを盗もうとしています。
しかし、ここでTrustZoneがSRAMへのアクセスを許可しない割り込みをかけたので、攻撃は失敗しました。
この実態は、TrustZoneが各通信を監視し、その場であってはいけない処理を認識すると遮断するというものです。
しかし、組み込みハードウェアの通信設計のパターンは無限大存在するので、すべて網羅することは困難となります。
TrustZoneのZoneとは、内部的に絶対保護したいSecureZoneと、あきらめてもいいNonSecureZoneに分け、アドレスフィルタリングを行い、SecureZoneに攻撃的な動作を検知した場合対応するというものなのです(かなり意訳がはいっていますが)
本題
TF-Aは、言ってしまえばTrustZoneの初期化を担当するFSBLです。U-Boot SPLと同じタイミングで呼び出され始め、外部メモリの初期化を始める時点でTrustな環境を構築するために各ペリフェラルを初期化して見張りを置いていきます。
MP1の場合、Cortex-AとM両方にこの処理が必要ですから、途中で分岐しているような図になっています。
OP-TEE
最後にOP-TEEです。TrustZone完全に理解したマンならなんとなく必要性がわかるものとなります。OP-TEEは、TrustZoneの信頼できる実行環境のことで(Open-Trusted Execution Environment)といいます。
OP-TEEは主にLinux側からハードウェアへの攻撃を阻止するための門番です。
基本的に、TrustZoneの世界ではLinuxはNonSecureとして解釈されます。これがSecureZoneに通信してハードウェアを動作させるわけですが、この時悪意のあるコードを混ぜられれば、さきほどと同様のリスクを背負います。
そこで、SecureZoneで別の門番OSを動作させ、Linuxからの通信を監視することを考えます。このOSがOP-TEEです。これによって、実際にハードウェアを動かすコードの実態がLinux側から不正に制御されることを防ぐことができます。
これをセキュアモニターといいます。
さっきのTF-Aと何が違うんという点ですが、OP-TEEは、この図でいうBL-32にあたります。(SecureZoneで動作する門番(Runtime))
BL-32にどのようなソフトを置くかは検討次第ですが、少なくとも前STMに採用されていたSP-MINというセキュアモニターは非推奨になり、いまはOP-TEEがスタンダードとなっています。
まとめ
いかがだったでしょうか。
なかなか国内でドキュメントや人口が増えない組み込みLinux分野がこれから盛り上がってくるといいなと思います。
注意
この解説の画像はST Wikiから活用させていただいています。
Discussion