nRF52840のDFUでSlot1がpermanentになり詰んだ話 - ダウングレードによるデバイスロック問題
この記事はLLM(GitHub Copilot)によって作成されました。
nRF Connect SDK 2.9で開発したnRF52840のファームウェアで、MCUboot/MCUmgrを使ったDFU(Device Firmware Update)中に、デバイスが完全にロックされてそれ以上DFUできなくなるという深刻なバグに遭遇しました。この問題は既にオープンソースコミュニティでも報告されていますが、ダウングレード特有のケースは明示的に議論されていないため、本記事で詳細を共有します。
発生した問題
症状: デバイスの完全なロック
バージョンが小さいファームウェアをDFUすると、デバイスが完全にロックされ、それ以上DFUできなくなるという問題が発生しました。
具体的な流れ:
1. Primary Slot: v1.0.2.2 (実行中)
2. DFU実行: v1.0.1.1 をアップロード
3. 再起動
4. MCUbootがバージョンチェック: 1.0.2.2 > 1.0.1.1 → スワップ拒否
5. Primary Slot: v1.0.2.2 のまま起動
6. Secondary Slot: v1.0.1.1 が pending 状態で残る
7. 次のDFUを試みる
8. MCUmgr: "There is no free slot to place the image" エラー
9. デッドロック状態 → DFU不可能
再現条件
以下の3つの条件が揃うと必ず発生します:
-
ダウングレード防止が有効
CONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER=y
-
バージョンが小さいイメージをDFU
- Primary: 1.0.2.2
- DFU: 1.0.1.1 (ダウングレード)
-
PERMANENT modeで書き込み (nRF Connect appのデフォルト)
- Upgrade Mode: “Confirm only”
- Secondary slotに
image_ok=SET
が設定される
エラーメッセージ
nRF Connect app:
- "SMP Command Error: There is no free slot to place the image"
mcumgr CLI:
- "Error: 6" (image erase失敗)
- Upload: 0.00% で停止
根本原因: MCUbootとMCUmgrの判断基準のミスマッチ
この問題の本質は、**MCUbootとMCUmgrの設計上の「すき間」**にあります。
┌─────────────────────────────────────┐
│ MCUmgr (DFU制御) │
│ - Secondary slotに空きがあるかチェック │
│ - バージョン比較はしない │
│ - pending フラグがあれば「使用中」判定 │
└─────────────┬───────────────────────┘
│ ギャップ!
▼
┌─────────────────────────────────────┐
│ MCUboot (起動時のスワップ制御) │
│ - バージョン比較を実行 │
│ - ダウングレード検出 → スワップ拒否 │
│ - Secondary slotはそのまま残る │
└─────────────────────────────────────┘
問題: MCUmgrは「Secondary slotにイメージがある」と判断するが、MCUbootは「スワップしない」と判断。結果、スワップされないイメージがSecondary slotに永久に残ります。
MCUmgrのスロット判定ロジック
bool is_slot_free(int slot) {
if (slot == 1) {
// Secondary slotをチェック
if (magic == BOOT_MAGIC_GOOD && pending) {
return false; // 使用中と判定
}
return true; // 空きスロット
}
}
盲点: MCUbootがそのイメージを実際にスワップするかはチェックしない!
これは既知の問題
Apache mynewt-mcumgr Issue #157 (2021年11月)
URL: https://github.com/apache/mynewt-mcumgr/issues/157
タイトル: “img_mgmt_erase is too strict, won’t allow erasing pending secondary slot”
状況: Open(未解決)
MCUbootがスワップを拒否し、Secondary slotにpendingイメージが残る問題として報告されています。報告者は “We are now stuck with a device that can only be recovered using a debugger” と述べており、今回のケースと同様の深刻な状況です。
Zephyr Issue #58103 (2023年5月)
URL: https://github.com/zephyrproject-rtos/zephyr/issues/58103
タイトル: “mcuboot/mcumgr: board not upgradable after ‘Not enough free space to run swap upgrade’”
状況: Closed(PR #64586でマージ - 部分的解決)
Fabio Baltieri (Zephyr開発者) の評価:
“Seems pretty serious to me, may lead to devices lost and non upgradable in the field.”
優先度はpriority: medium
バグとして扱われていますが、実際の影響は非常に深刻です。
症状:
$ mcumgr image list
image=0 slot=1
flags: pending # スワップされないイメージ
$ mcumgr image erase
Error: 6 # 消去拒否
$ mcumgr image upload zephyr.signed.bin
0 B / 213.04 KiB [---] 0.00% # 停止
今回のケースとの違い
既存報告 | 今回のケース | 共通点 |
---|---|---|
原因: イメージサイズ大 | 原因: バージョンダウングレード | MCUbootがスワップ拒否 |
swap領域不足 | ダウングレード防止 | Secondary pendingイメージが残る |
Error: 6 | “no free slot” | 新規DFU不可能 |
重要な発見: ダウングレード特有のケースは明示的に報告されていない可能性が高いです。理由として、開発環境ではCONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER
を無効化していることが多く、通常の運用ではバージョンを常に上げるため発生しにくいことが挙げられます。
解決方法
即座の回復(現状復帰)
方法1: JLinkでSecondary slot消去(最も確実)
# Secondary Slot全体を消去
nrfjprog --erasepage 0x85000-0xfe000
# または Image Trailerのみ消去
nrfjprog --erasepage 0xfde00-0xfe000
# リセット
nrfjprog --reset
効果: Secondary slotが物理的に消去され、MCUmgrが「空き」と認識します。
方法2: 完全消去
nrfjprog --eraseall
nrfjprog --program merged.hex --verify
nrfjprog --reset
方法3: Shellコマンド(利用可能な場合)
mcuboot erase 2
根本的な対策(再発防止)
対策1: 開発時のバージョン管理ルール(推奨)
開発中は常に BUILD 番号を上げる:
v1.0.1.1 → v1.0.2.2 → v1.0.2.3 → v1.0.2.4 ...
絶対にダウングレードしない!
対策2: ダウングレード防止を無効化(開発時のみ)
prj.conf
:
# 開発時はビルド番号を無視
# CONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER=y
CONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER=n
注意: 本番環境では必ず有効化すること(セキュリティ上重要)
対策3: TEST modeをデフォルトにする
nRF Connect appの設定:
Upgrade Mode: Test only (Confirm onlyではなく)
効果:
image_ok=UNSET
で書き込み- スワップされなければ自動的にREVERT
- Secondary slotが自動的にクリアされる
影響範囲
深刻度
Fabio Baltieri (Zephyr開発者) の評価:
“Seems pretty serious to me, may lead to devices lost and non upgradable in the field.”
影響を受けるシステム
- MCUboot + MCUmgr を使用するすべてのシステム
- ダウングレード防止が有効な環境
- nRF52/nRF53/nRF91シリーズなどNordic製品
- Zephyr RTOS採用の組み込みシステム
特に危険なケース
-
フィールド配備済みデバイス
- JLinkアクセス不可
- リモートDFUのみ
- 完全にロックされる可能性
-
量産後のデバイス
- デバッグポート無効化済み
- 物理的な回復手段なし
-
クリティカルシステム
- ダウンタイム許容不可
- 緊急修正が必要なケース
Zephyr/MCUbootプロジェクトの対応状況
PR #64586 (2023年)
- ビルド時チェック強化: イメージサイズ超過時にビルドエラー
- 部分的なerase許可: 特定条件下でpendingイメージの消去を許可
現在の状況(2025年)
- ✅ イメージサイズ問題は改善
- ❌ ダウングレードによるロックは未解決
- ⚠️ 回避策: JLinkでの強制消去のみ
推奨される運用
開発環境:
CONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER=n
# + バージョンを常に上げる運用
本番環境:
CONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER=y
# + 厳格なバージョン管理
# + TEST mode でのDFU
# + 確認後に手動confirm
まとめ
nRF52840でのDFUトラブルは、MCUbootとMCUmgrの設計上の「すき間」により、スワップされないイメージがSecondary slotに残り続け、新規DFUが完全にブロックされるという問題でした。
この問題は2021年から報告されている既知のバグですが、イメージサイズケースは部分的に解決されたものの、ダウングレードケースは未解決のままです。
開発環境ではCONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER=n
を使用し、本番環境では厳格なバージョン管理とTEST modeでのDFUを組み合わせることを推奨します。
特にフィールド配備済みデバイスでこの問題が発生すると、物理的なデバッグアクセスなしでは回復不可能になるため、運用設計時には十分な注意が必要です。
参考リンク
- Apache mynewt-mcumgr Issue #157: https://github.com/apache/mynewt-mcumgr/issues/157
- Zephyr Issue #58103: https://github.com/zephyrproject-rtos/zephyr/issues/58103
- Nordic DevZone: https://devzone.nordicsemi.com/f/nordic-q-a/78984/big-primary-image-prevents-fota
- MCUboot Documentation: https://docs.mcuboot.com/
- Zephyr MCUmgr: https://docs.zephyrproject.org/latest/services/device_mgmt/mcumgr.html