はじめに
AtomVM + ESP32 で遊んでいる最中、パーティションテーブル(partition table)を調整した直後から起動時クラッシュが継続する事象に遭遇しました。
調査の結果、boot.avm のフラッシュ領域が何らかの理由で全て 0xFF(消去済み)になっており、起動時に読み込むべき boot.avm が実質的に消えていることが原因でした。
対処として、パーティションテーブルで示される boot.avm のオフセットへ boot.avm を再書き込みしたところ、クラッシュは解消しました。
また、普段の書き込みで使用している idf.py flash だけでは、状況によって boot.avm が期待どおりに復元されない可能性があることが分かりました。原因不明の起動時クラッシュに遭遇した場合は、パーティションテーブルでオフセットを確認し、boot.avm 先頭ヘッダを簡易チェックする手順を作業後の習慣にしておくのが安全です。
※ 写真はイメージです
問題発生から解決までの流れ
- 日本語フォント導入を試したところ、アプリ領域(
main.avm)に収まらなくなった - 容量確保のためパーティションテーブルを一時的に調整したが、意図どおりに動かず元に戻した
- パーティションテーブルを戻した後も、起動時クラッシュが継続した
-
boot.avm領域の先頭を読み出すと0xFFで埋まっており、完全消去されていることを確認した -
boot.avmを正しいオフセットへ再書き込みし、クラッシュが解消した
AtomVM のフラッシュ配置
ESP32 上の AtomVM は、概ね以下のような配置になります(実際のオフセットはパーティションテーブルの設定による)。
- ブートローダ/パーティションテーブル/NVS・PHY_INIT など(起動に必要な基本領域)
-
factory(AtomVM 本体。atomvm-esp32.binが入る) -
boot.avm(AtomVM のコアライブラリ。起動時に最初に読み込まれる AVM pack) -
main.avm(アプリ本体。Erlang/Elixir の AVM pack)
詳細は AtomVM の資料を参照:
今回のパーティションテーブル
idf.py partition-table で、パーティションテーブルのオフセットが期待どおりになっていることを確認しました。AtomVM で Elixir アプリを作るときの標準のオフセットです。
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K,
factory,app,factory,0x10000,1792K,
boot.avm,data,phy,0x1d0000,512K,
main.avm,data,phy,0x250000,1M,
-
boot.avmは0x1d0000(512KB) -
main.avmは0x250000(1MB)
症状の確認
起動ログを見てみると boot.avm パーティションを正常に読み込めているように見える一方で、AVM pack としては不正扱いになっていました。
I (611) AtomVM: Starting AtomVM revision 0.7.0-dev+git.fde1fb3
I (611) sys: Loaded BEAM partition boot.avm at address 0x1d0000 (size=524288 bytes)
...
E (651) AtomVM: Invalid startup avmpack. size=524288
abort() was called at PC 0x42011407 on core 0
--- 0x42011407: app_main at .../src/platforms/esp32/main/main.c:99
boot.avm の先頭 64 バイトを確認したところ、全て 0xFF でした。
これは「その領域が消去されていて、実データが無い」ことを意味するそうです。
# boot.avm の開始オフセット(0x1d0000)から先頭 64 バイトだけ読み出してファイルに保存する
# - 0xFF 埋めなら「消去済みで実データなし」の可能性が高い
esptool.py -p /dev/ttyACM0 read_flash 0x1d0000 64 boot_head.bin
# 読み出した 64 バイトを 1 バイト単位で 16 進ダンプして目視確認する
# - 先頭に "#!/usr/bin/env AtomVM" や "FOR1" / "BEAM" などが見えれば正常なデータが入っている目安
xxd -g1 -l 64 boot_head.bin
00000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
00000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
00000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
00000030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
想定される原因
原因は特定できていませんが、今回の状況からは以下のいずれかが考えられます。
- 全消去(
idf.py erase-flash/esptool.py erase_flash)を実行し、その後boot.avmを戻す工程が無かった - パーティションテーブルの変更の実験中に、誤って
boot.avmを含む範囲を消去/上書きした - パーティションテーブルを一時的に変えた状態のオフセット情報で書き込み・消去が走り、復帰後の配置と不整合になった
復旧手順
パーティションテーブルで示される boot.avm のオフセット(ここでは 0x1d0000)へ boot.avm を再書き込みします。
BOOT_AVM="$HOME/atomvm/AtomVM/build/libs/esp32boot/elixir_esp32boot.avm"
# 念のため対象領域を消去(サイズは boot.avm パーティションの Size に合わせる)
esptool.py -p /dev/ttyACM0 erase_region 0x1d0000 0x80000
# boot.avm を所定オフセットへ書き込み
esptool.py -p /dev/ttyACM0 write_flash 0x1d0000 "$BOOT_AVM"
# 先頭 64 バイトを読み出して簡易チェック
esptool.py -p /dev/ttyACM0 read_flash 0x1d0000 64 boot_head.bin
xxd -g1 -l 64 boot_head.bin
復旧後は、先頭に #!/usr/bin/env AtomVM 相当の文字列や FOR1 / BEAM などが見える状態になりました(0xFF ではない)。
00000000: 23 21 2f 75 73 72 2f 62 69 6e 2f 65 6e 76 20 41 #!/usr/bin/env A
00000010: 74 6f 6d 56 4d 0a 00 00 00 00 08 2c 00 00 00 03 tomVM......,....
00000020: 00 00 00 00 65 73 70 33 32 69 6e 69 74 2e 62 65 ....esp32init.be
00000030: 61 6d 00 00 46 4f 52 31 00 00 08 10 42 45 41 4d am..FOR1....BEAM
おわりに
パーティションテーブルを触ったあとにクラッシュが止まらない場合はまず「boot.avm が消えている」を疑うと復旧が早まりそうです。
boot.avm の先頭 64 バイト確認する癖をつけることが得策のようです。
