來源:公眾號【魚鷹談單片機】
作者:魚鷹Osprey
ID :emOsprey
大家好,我是魚鷹。今天寫 邪修如何升級 boot?(標題有誤,應該是 app)的第二篇,之前這篇文章是通過直接在代碼里面塞入另一個設備的 App 的代碼,完成對設備的升級,這種方法可以在前期快速驗證設備與設備之間的通路,而不需要考慮固件如何下載的問題。

這個方法好是好,就是每次更新固件,都得更新代碼,稍微有點麻煩。而且還會占用設備 A 本身的代碼區空間(上圖有點問題,設備 B APP其實只是設備 A APP 的一個數組,沒有專門的區域)。
今天魚鷹來個更高效的,只要能接入調試器就可以使用,方便快捷,使用腳本就能用。
這個方法就是使用 gdb 完成固件的注入,然后通過設備 A 的通信代碼,完成設備B的固件數據傳輸。
類似下面這種。

關鍵指令:
restore device_b_app.bin binary 0x20008000 # 加載固件到 ram,注意這個區域應該是沒有任何被任何功能使用的。call Upgrade_Device_B(0x20008000, 16384) # 調用設備 A 的升級函數
大概的自動化腳本(可能有問題,需要自行修改,或者讓 AI 處理)
# =====================================================================# GDB 自動化固件下載與設備升級腳本# =====================================================================# 1. 連接調試器并復位設備 Afile build/zephyr/zephyr.elftarget remote localhost:50000monitor resetmonitor halt# 2. 配置區域:指定加載地址與固件文件名set $TARGET_ADDR = 0x2400c400set $BIN_FILE = "device_b_app.bin"# 3. 動態獲取文件大小 (兼容 Linux / macOS)# 通過 shell 執行命令,將文件大小寫入臨時 gdb 腳本中,再由 gdb 導入shell if [ -f "$BIN_FILE" ]; then echo "set \$FIRMWARE_SIZE = $(stat -c %s "$BIN_FILE" 2>/dev/null || stat -f %z "$BIN_FILE")" > .tmp_size.gdb; else echo "set \$FIRMWARE_SIZE = 0" > .tmp_size.gdb; fisource .tmp_size.gdbshell rm -f .tmp_size.gdb# 4. 檢查文件大小是否有效if $FIRMWARE_SIZE == 0echo \n【錯誤】找不到固件文件,或者文件大小為 0 字節!退出腳本。\nquitend# 打印讀取到的配置信息echo \n========================================\necho >>> 配置就緒:\nprintf "固件文件: %s\n", $BIN_FILEprintf "目標 RAM 地址: 0x%X\n", $TARGET_ADDRprintf "自動捕獲大小: %d 字節 (約 %d KB)\n", $FIRMWARE_SIZE, $FIRMWARE_SIZE / 1024echo ========================================\n# 5. 下載 bin 文件到設備 A 的 RAMecho \n>>> 步驟 1: 正在下載固件到設備 A 的 RAM...\nrestore device_b_app.bin binary $TARGET_ADDR# 6. 調用設備 A 的 C 函數,傳入變量參數echo \n>>> 步驟 2: 正在調用 Upgrade_Device_B 執行升級...\ncall Upgrade_Device_B($TARGET_ADDR, $FIRMWARE_SIZE)# 7. 檢查并處理返回值 ($ 自動保存了最后一次 call 的結果)echo \n>>> 步驟 3: 檢查設備 B 的升級返回值...\nif $ == 0echo \n【升級成功】設備 B 已順利完成更新,返回值: 0\nelseecho \n【升級失敗】設備 B 升級中斷或報錯!\nprintf "設備 A 返回的錯誤碼 (R0): %d (0x%X)\n\n", $, $end
如果 gdb 支持Python,大小計算可以換成:
# 直接在 GDB 中用 Python 獲取大小并賦值給 GDB 變量python import os; gdb.set_convenience_variable('FIRMWARE_SIZE', os.path.getsize('device_b_app.bin'))
arm-none-eabi-gdb -x auto_upgrade.gdb # 調用腳本一般情況下,如果 ram 足夠大,直接放到ram就行,然后 gdb 調用設備 A 的函數完成升級,如果ram 不夠大,可以通過openocd下載到固定區域,或者使用分片傳輸。
這個方式其實有點像下載器算法了。不過更靈活,不需要制作算法,只是利用你早已經寫好的傳輸協議。
而且這個思想不僅可以用于固件升級,也可以對外部flash進行操作。
需要注意的是,操作完成后,記得重啟設備,不然有可能運行不正常。另外 gdb 調用函數需要需要盡可能簡單,不能有太多依賴條件,比如操作系統之類的,因為它的本質是在當前 PC 下強行讓 CPU 運行你的函數,如果有太多依賴,就可能存在問題。