固件版本块(格式 2)

相关仓库: - 网关产品仓:https://github.com/ffffxxxjjj/gateway - 参考仓 h743-hello:https://github.com/ffffxxxjjj/h743-hello - 签名打包服务 sign:https://github.com/ffffxxxjjj/sign

本文件是固件版本块的唯一规格。型号、固件名、版本、构建日期、 构建时间只从签名的包头/版本块里读,绝不从文件名解析; 文件名只当人的标签(下载、管理页认),设备根本不看文件名。

1. 位置与长度

2. 字段

偏移 长度 内容
[0..4] 4 魔数,ASCII FWVR
[4] 1 格式字节,必须为 2
[5..105] 100 型号,ASCII、零填充(如 stm32h743vi)
[105..205] 100 固件名,ASCII、零填充(如 gateway)
[205..211] 6 版本:major、minor、patch,3 个 u16 小端(依次在 205、207、209)
[211..219] 8 构建日期,8 位 ASCII YYYYMMDD
[219..225] 6 构建时间,6 位 ASCII HHMMSS(UTC,不是本地时间)
[225..228] 3 保留,必须为 0

3. 校验规则(与 sign-tool pack 一致)

sign-tool 读版本块时(read_version_block):

版本块格式 2 不要与 GWPK 包头格式 3 混淆:版本块是 固件 bin 里的身份块(格式 2);GWPK 包头是 sign-tool 打包后 的共用包头(格式 3,共 312 字节)。两者是两层东西。

4. 打包脚本怎么打版本块

网关应用的打包脚本(app/package.sh)流程:

  1. cargo build 编出 ELF;
  2. cargo objcopy -O binary 转成 bin,放 dist/,带版本号命名 (如 gateway-app-v0.1.0.bin、gateway-app-v0.1.0-release.bin);
  3. app/tools/stamp_version.py 把版本块打进 bin 偏移 0x1000:
  4. 型号固定 stm32h743vi,固件名固定 gateway;
  5. 版本取 app/Cargo.toml 的 version(如 0.1.0);
  6. 构建日期/时间取打包时的 UTC 时间(date -u),不是本地时间;
  7. 安全规则:目标区域全零才写;已等于要写的版本块则幂等跳过; 目标区域是其他内容(代码/数据,说明链接器没有留位)则 失败退出,绝不覆盖代码。

hello 的打包脚本没有打版本块这步(hello 的 bin 不需要被 sign-tool 打包);网关应用的打包脚本必须补这步,否则 sign-tool pack 读不到身份,无法打包。

5. 链接器怎么留位

应用的链接脚本(app/link.x,基于 cortex-m-rt 的 link.x 改) 在 Flash 布局里给版本块留了固定位置:

验证方法(沙盒/CI 可做,不用上板):

# 编应用(debug 或 release)
cargo build --target thumbv7em-none-eabihf
# 转 bin
cargo objcopy --bin gateway-app --target thumbv7em-none-eabihf -- -O binary /tmp/app.bin
# 查版本块位置(打戳前应全零,打戳后应为 FWVR + 格式 2)
python3 -c "
data = open('/tmp/app.bin', 'rb').read()
print('len', len(data))
print('region', data[0x1000:0x1000+16])
"

打戳前该区域必须全零(说明留位成功);若不是全零, 说明链接脚本没有留位,打包脚本会失败退出(安全规则), 不要绕过它强行写。

6. 例子

以下是一组测试向量里的版本块内容(仅作格式示例, 不是网关应用骨架的实际版本;骨架的实际版本取自其 Cargo.toml,构建日期/时间取打包时的 UTC 时间):

对应的版本块字节(前 16 字节):46 57 56 52 02(FWVR + 格式 2) 后跟型号、固件名(零填充)、版本(major=1、minor=2、patch=3, 依次为 01 00、02 00、03 00 小端,在 205、207、209)。