LoginSignup
1
1

More than 3 years have passed since last update.

FRDM-K64F上でZephyr OSのmcubootを試してみる

Posted at

はじめに

色々な環境のOTA(Firmware Update)を試しています。
今回はZephyr OSで使われているmcubootのOTA。
取りあえず付属のサンプルをFRDM-k64Fで動かしてみただけで、実はオンラインアップデートにもなってませんが、記録を残しておきます。

【今回の環境】

mcubootについて

mcubootの仕組みは以下に説明があります。
https://mcuboot.com/mcuboot/design.html
自分の理解のために一部ですが和訳しました。
Design document of mcuboot

上記にかなり細かく説明が書かれていますが、この説明だけではわからない点もあり、以下は実機で確認した内容も含みます。

全体的な仕組み

image.png

mcubootは、Firmware格納場所として2つのPartitionを想定します。通常はPrimary PartitionのFirmwareが、署名検証後に起動されます。更新用FirmwareがSecondary Partitionに格納されると、mcubootはSecondaryの署名を検証後、Scratch Partitionを使ってPrimary/SecondaryのFirmwareを交換し、PrimaryのFirmwareを起動します。交換後のFirmware起動が正常に行われなかった場合(交換後FirmwareによりImage OKがセットされなかった場合)、mcubootは次のブート時にPrimary/SecondaryのFirmwareを再交換し、Primaryを以前のFirmwareに戻します。

なお、上記図中のFlashのアドレスはFRDM-K64F用のサンプルで使われているもので、環境によって調整が必要です。
また、今回動かしたサンプルでは、Secondary Partitionへの書込みをFirmwareの機能では行わず、Primary Partitionと同様に直接書込みしてます。

Firmwareへの署名

Firmwareへの署名にはimgtool.pyを使います。署名後は、署名関連の情報がTLV(Type Length Value)形式でFirmwareの後ろに付与されます。今回動かしたサンプルでは以下の情報が付与されました。

項目 用途 長さ
TLV header MAGIC NumberとTLV領域長 4byte
SHA256 Firmwareのハッシュ値 Tag 2byte, Length 2byte, Value 32byte
KEYHASH 署名鍵に対応する公開鍵のハッシュ値 Tag 2byte, Length 2byte, Value 32byte
ECDSA256 Firmwareの署名値 Tag 2byte, Length 2byte, Value 72byte

Image Trailer

Primary/Secondary Partitionの末尾はImage Trailerと呼ばれ、mcubootが各Partitionの状態を管理するための領域として使われます。主な記録内容は以下の通り。

項目 用途
Swap status Firmware交換が中断した場合に備えて進行状況を記録
Image OK Firmwareが正しく起動できたことを示すフラグ
MAGIC Firmwareが格納されていることを示すMAGIC Number

Swap Type

Boot時にFirmware交換や戻しを行うかどうかは、Image Trailerの情報から以下のように判定されます(ドキュメントにはもっと細かく書かれていますが、要約すると恐らく以下のような感じ)。

判定順序 判定条件 Swap Type Swap Typeの意味 Boot動作
1 SecondaryのMAGICが正しく設定済 BOOT_SWAP_TYPE_TEST Secondaryに新たなFirmwaerがあるので要交換 Secondaryの署名を確認後、Primary/SecondaryのFirmwareを交換し、Primaryからの起動を試みる
2 PrimaryのMAGICが設定済だが、Image OKが未設定 BOOT_SWAP_TYPE_REVERT Firmware交換を行ったが正しく起動できなかったので戻しが必要 Primary/SecondaryのFirmwareを交換して元に戻したう上で、Primaryからの起動を試みる
3 それ以外 BOOT_SWAP_TYPE_NONE 通常状態 Primaryからの起動を試みる

Firmware更新方式のバリエーション

Mode 内容
Swapping Mode 通常の方式。Secondary Partitionに交換用Firmwareが存在する場合、ブートローダは次のブート時にSecondaryの署名を確認後、Primary/SecondaryのFirmwareを交換し、Primaryからの起動を試みる。
Overwriting Mode Firmwareの戻しを行わない方式。Secondary Partitionに交換用Firmwareが存在する場合、ブートローダは次のブート時にSecondaryの署名を確認後、PrimaryをSecondaryの内容で上書きし、Primaryからの起動を試みる。MCUBOOT_OVERWRITE_ONLYオプションで指定する。
Direct-xip Mode Firmwareの交換や上書きを行わず、Primary/Secondaryのうちバージョンの新しい方を直接起動する。
RAM-load Mode Firmwareの交換や上書きを行わず、Primary/Secondaryのうちバージョンの新しい方を、RAMにコピーしてから起動する。

実験準備(Zephyr環境の準備)

Zephyrのドキュメントに沿って用意していきます。
Getting Started Guide

1. Select and Update OS

開発環境のパッケージを最新化。

sudo apt update
sudo apt upgrade

2. Install dependencies

Zephyr OSのビルドに必要なパッケージを追加します。

sudo apt install --no-install-recommends git cmake ninja-build gperf \
  ccache dfu-util device-tree-compiler wget \
  python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \
  make gcc gcc-multilib g++-multilib libsdl2-dev

CMakeのバージョンチェック。3.13.1以上ならOK。

$ cmake --version
cmake version 3.16.3

3. Get Zephyr and install Python dependencies

Zephyrの管理ツールwestをインストール。

pip3 install --user -U west
echo 'export PATH=~/.local/bin:"$PATH"' >> ~/.bashrc
source ~/.bashrc

Zephyrのソースコードをダウンロード。

west init ~/zephyrproject
cd ~/zephyrproject
west update

ZephyrアプリをビルドするためのCMake環境を準備。

west zephyr-export

追加で必要なツールをインストール。

pip3 install --user -r ~/zephyrproject/zephyr/scripts/requirements.txt

4. Install a Toolchain

Zephyr SDK installerをダウンロード。

cd ~
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.11.4/zephyr-sdk-0.11.4-setup.run

Zephyr SDKをインストール。
Toolchainも一緒にインストールされます。

chmod +x zephyr-sdk-0.11.4-setup.run
./zephyr-sdk-0.11.4-setup.run -- -d ~/zephyr-sdk-0.11.4

udev ruleをインストール。

sudo cp ~/zephyr-sdk-0.11.4/sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d
sudo udevadm control --reload

5. Build the Blinky Sample

まずはZephyrのサンプルを動かしてみます。ボードとしてfrdm_k64fを指定してビルド。

cd ~/zephyrproject/zephyr
west build -p auto -b frdm_k64f samples/basic/blinky

6. Flash the Sample

フラッシュに書き込むと、FRDM-K64Fの緑のLEDが点滅しました。

west flash

ディレクトリ構成

以上で、Zephyr環境の準備ができました。
今回使うディレクトリは以下の構成になっています。

~/
 ├ zephyrproject
 │  ├ bootloader
 │  │  └ mcuboot
 │  │    └ samples
 │  │      └ zephyr
 │  └ zephyr
 │     └ samples
 │       └ basic
 │         └ blinky
 └ zephyr-sdk-0.11.4
    └ arm-zephyr-eabi

mcuboot sampleのビルドとテスト

上記の通り、mcubootはzephyrproject/bootloader/mcubootに、Zephyr用のサンプルはその下のsamples/zephyrにあります。
このサンプルの使い方は、同じディレクトリのMakefileの冒頭コメントに記載されてます。

ビルドとテスト

Makefileのコメントを参考に以下コマンドでビルドします。
なお、Makefile中で環境変数ZEPHYR_BASEを参照しているので、あらかじめZephyrの場所を定義しておきます。

export ZEPHYR_BASE=~/zephyrproject/zephyr
make BOARD=frdm_k64f all

ビルドが終わると、以下3つのファイルが作られました。

ファイル サイズ 内容
mcuboot.bin 0xB774 ブートローダ
signed-hello1.bin 0x3C70 最初のFirmware
signed-hello2.bin 0x60000 交換用Firmware(Image Trailer付き)

交換用Firmwareは、本来であれば、現行FirmwareによってSecondary Partitionに書き込まれた後、現行FirmwareがImage TrailerのMAGIC領域にMagic Numberを書き込むことによって交換されますが、このサンプルでは交換用FirmwareとMAGIC領域の書込みもコマンドで直接行います。そのため、signed-hello2.bin--padオプション付きで署名されており、Image Trailerを含むSecondary Partition全体を表現する大きなサイズ(0x60000 byte)になっています。

次に、まずブートローダを書き込んでみます。

make flash_boot

シリアルには以下のメッセージ。まだPrimary Partitionが空なのでエラーになります。

*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
[00:00:00.005,000] <inf> mcuboot: Starting bootloader
[00:00:00.006,000] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Boot source: primary slot
[00:00:00.010,000] <inf> mcuboot: Swap type: none
[00:00:00.010,000] <err> mcuboot: Unable to find bootable image

次に、最初のFirmwareをPrimary Partitionに書き込みます。

make flash_hello1

書込んだFirmwareが起動されました。

*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
[00:00:00.005,000] <inf> mcuboot: Starting bootloader
[00:00:00.006,000] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Boot source: primary slot
[00:00:00.010,000] <inf> mcuboot: Swap type: none
*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
Hello World from hello1 on frdm_k64f!

続いて、交換用FirmwareをSecondary Partitionに書き込みます。前述の通りサイズが大きいので、書込みには時間がかかります。

make flash_hello2

Swap TypeがBOOT_SWAP_TYPE_TESTに変わり、後から書き込んだFirmware(hello2)が起動されました。

*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
[00:00:00.005,000] <inf> mcuboot: Starting bootloader
[00:00:00.006,000] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Boot source: primary slot
[00:00:00.010,000] <inf> mcuboot: Swap type: test
*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
Hello World from hello2 on frdm_k64f!

しかし、このFirmwareにはImage OKをセットする処理は入っていないため、リブートするとSwap TypeはBOOT_SWAP_TYPE_REVERTとなり、Firmwareの戻しが行われます。

*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
[00:00:00.005,000] <inf> mcuboot: Starting bootloader
[00:00:00.006,000] <inf> mcuboot: Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Boot source: none
[00:00:00.006,000] <inf> mcuboot: Swap type: revert
*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
Hello World from hello1 on frdm_k64f!

最初のFirmware(hello1)に戻っていることがわかります。もう一度リブートすると、Swap Typeが通常状態のBOOT_SWAP_TYPE_NONEに戻っていることがわかります。

*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
[00:00:00.005,000] <inf> mcuboot: Starting bootloader
[00:00:00.006,000] <inf> mcuboot: Primary image: magic=good, swap_type=0x4, copy_done=0x1, image_ok=0x1
[00:00:00.006,000] <inf> mcuboot: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.006,000] <inf> mcuboot: Boot source: none
[00:00:00.006,000] <inf> mcuboot: Swap type: none
*** Booting Zephyr OS build zephyr-v2.4.0-1531-gf004410aa1a7  ***
Hello World from hello1 on frdm_k64f!

run-tests.shを使ったテスト

同じフォルダ内のrun-tests.shを実行すると、様々な条件でのテストが行えるようになっていました。

No Test Name テスト内容 hello1書込み後 hello2書込み後 リセット後
1 GOOD RSA hello1/hello2ともにRSA2048で署名 hello1が起動 hello2が起動 hello1が起動
2 GOOD ECDSA hello1/hello2ともにEDSA256で署名 hello1が起動 hello2が起動 hello1が起動
3 OVERWRITE Overwriting Modeでビルド hello1が起動 hello2が起動 hello2が起動
4 BAD RSA hello1はRSA2048で署名、hello2はECDSA256で署名 hello1が起動 hello1が起動 hello1が起動
5 BAD ECDSA hello1はECDSA256で署名、hello2はRSA2048で署名 hello1が起動 hello1が起動 hello1が起動
6 NO BOOTCHECK Primary Partitionの完全性チェックを行わないモードでビルド hello1が起動 hello1が起動 hello1が起動
7 WRONG RSA hello2を異なるRSA2048鍵で署名 hello1が起動 hello1が起動 hello1が起動
8 WRONG ECDSA hello2を異なるECDSA256鍵で署名 hello1が起動 hello1が起動 hello1が起動

なお、FRDM-K64Fで実行する場合、スクリプト中のpyocd erase --chipを実行するとエラーが出て動かなくなるので、この行(8ヶ所)をコメントアウトしておくと実行できます。

ひとまず今回の実験はここまで。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1