feat: add local-staging FULLOTA2 flow

This commit is contained in:
chenjintao
2026-09-27 18:28:05 +08:00
parent b726fe192a
commit f607d8b9f7
7 changed files with 407 additions and 34 deletions
+58 -28
View File
@@ -12,22 +12,22 @@ OTA 所依赖的底层条件。
- 保持 `customer_app` 为 460 KiB;
- 将 APP 物理/XIP 地址固定为 `0x002C2000` / `0x802C2000`;
- 保留 SDK 已有的 `QUEC_APP_FULL_FOTA` 与 FULLOTA2 公共 API;
- 将现有 OTA 命令切换为最小本地暂存式 FULLOTA2;
- 为设备和构建产物加入唯一布局身份;
- 在构建前后执行 fail-closed 校验。
本基线暂不完成:
- 不把当前 `ql_fullfota_app_start()` 在线升级流程切换成 FULLOTA2;
- 不增加签名 manifest、SHA-256 下载校验、断点续传或后台灰度;
- 不提供 A/B APP 分区,也不承诺自动版本回滚;
- 不在线更新 kernel、分区表或 `customer_fs`。
这样安排是为了先冻结量产后难以改变的 Flash 布局,同时避免在生产前仓促替换业务 OTA
状态机。后续 FULLOTA2 逻辑只需通过 `customer_app` 版本迭代即可加入。
这版保留现有后端的 `version + url` 命令格式,只改变设备端执行方式。网络包会完整写入
`U:/FotaFile.bin`,通过 SDK FBF 校验并设置升级标志后才重启。
> 生产门禁:从本工厂基线开始量产,到 FULLOTA2 业务 APP 完成真机验收并正式发布之前,
> 后端必须暂停向生产设备下发 OTA 命令。当前 `ql_fullfota_app_start()` 入口仍是原有
> FULLOTA1 流程,新的分区基线不会自动消除其覆盖下载风险。
> 生产门禁:完成本文的真机 FULLOTA2 正向、失败注入和断电测试前,后端不得向生产设备
> 下发 OTA 命令。验收通过后可继续使用原协议下发;这条门禁针对测试状态,不再是因为
> 设备仍调用 FULLOTA1。
## 2. 固定身份与 Flash 布局
@@ -55,7 +55,7 @@ ota_ready_fs640
| fota_param | `0x003F7000` | `0x003FA000` | 12 KiB |
| factory_a | `0x003FA000` | `0x00400000` | 24 KiB |
`customer_app` 是单槽。FULLOTA2 能保护下载阶段:文件未下载完整、哈希错误或 FBF 校验
`customer_app` 是单槽。FULLOTA2 能保护下载阶段:文件未下载完整或 SDK FBF 校验
失败时不会设置升级标志;但 updater 开始擦写 `customer_app` 后,没有旧 APP 槽可供回滚。
## 3. 本次修改
@@ -90,6 +90,8 @@ ota_ready_fs640
- kernel 必须继续定义 `QUEC_APP_FULL_FOTA`;
- FULLOTA2 所需的 SDK API 必须存在;
- `app.bin + 8192` 不得超过 460 KiB。
- MQTT OTA 入口必须调用 `banban_fullfota2_start()`,不得回退到
`ql_fullfota_app_start()`。
可独立执行:
@@ -99,6 +101,34 @@ ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_ota_ready_baselin
--app-bin ql-application\threadx\build\app.bin
```
### 3.4 最小 FULLOTA2 执行路径
`ota_fullfota2.c` 使用独立任务完成同步 HTTP 下载。现有 MQTT 工作线程只负责参数校验、
保存目标版本、回复 `accepted` 和启动任务,不会承担长时间网络读取。
执行顺序:
1. 删除上一次残留的 `U:/FotaFile.bin`;
2. HTTP GET 必须返回 200 和非零 `Content-Length`;
3. FBF 必须大于 8 KiB 且不超过 460 KiB;
4. `U:` 空闲空间必须不小于 `Content-Length + 64 KiB`;
5. 设置内部 Flash 固定暂存路径,初始化 FOTA context;
6. 以 4096 字节缓冲顺序读取并调用 `ql_fota_image_write()`;
7. 实际接收字节数必须严格等于 `Content-Length`;
8. 调用 `ql_fota_image_flush()`,再用 `ql_stat()` 复核落盘文件长度;
9. 调用 `ql_fota_image_verify()` 校验 FBF 并设置升级标志;
10. deinit 后重启,由 updater 写入单槽 `customer_app`。
任一步在设置升级标志前失败,都会关闭 HTTP/FOTA context、删除暂存包、撤销固定升级模式、
清除 pending 状态并把 `fota_start` 恢复为 0。旧 APP 未被擦写,可继续运行并接受下一次命令。
旧实现会在 OTA 前删除服务器管理的提示音。fs640 基线有足够空间后,这版不再主动删除
业务资源;若空间不足则明确失败,不通过牺牲业务文件强行升级。
`http_aofei.c` 中未被当前业务调用的旧 HTTP 轮询 OTA 包装函数原本仍可进入 FULLOTA1,
本基线将其显式禁用。生产固件只有 MQTT `version + url` 命令可以启动 OTA,避免两个入口
使用不同的升级语义。
## 4. 构建和生产烧录
在 Windows SDK 构建环境中执行:
@@ -156,39 +186,39 @@ ASR1605/CRANELRH,但量产包必须由 `EG800AKCN_11LG` 配置生成。
4. 在写入 10%、50%、90% 时分别断电,重启后确认业务 APP 仍可运行并可清理残留文件。
5. 验证正常业务文件占用后,仍满足 `FBF size + 64 KiB` 空闲门槛。
### 5.4 最小 FULLOTA2 冒烟测试
### 5.4 最小 FULLOTA2 验收测试
正式量产前至少做一次 A 到 B 的本地暂存升级。B 可以只修改版本号:
1. 将 B 版 `customer_app.fbf` 顺序写入 `U:/FotaFile.bin`;
2. 调用 `ql_fota_fixed_internal_flash_upgrade(1)`;
3. 调用 `ql_fota_set_package_path()`、`ql_fota_init()`、`ql_fota_image_write()`、
`ql_fota_image_flush()` 和 `ql_fota_image_verify()`;
4. 重启,确认 updater 安装 B,且 `U:/FotaFile.bin` 被正确处理;
5. 确认 NVM、设备身份、业务配置和预置音频未损坏;
6. 下载损坏、截断和旧地址 FBF,确认均不能设置有效升级标志。
1. 后端按现有协议下发 B 版 `version + url`;
2. 串口必须看到 HTTP 长度、下载进度、FBF verify 成功和重启日志;
3. 重启后确认 updater 安装 B,且 `U:/FotaFile.bin` 被正确处理;
4. 确认 NVM、设备身份、业务配置和 14 个预置音频未损坏;
5. 分别测试 404、无 Content-Length、空间不足、传输中断、截断 FBF、损坏 FBF 和旧地址
FBF,确认旧 APP 保持可运行且下一次 OTA 可以重新开始;
6. 在下载 10%、50%、90% 时断电,确认旧 APP 启动后清理残留状态并可再次升级;
7. 在 `ql_fota_image_verify()` 成功后的 updater 擦写阶段断电,记录底层 updater 的实际恢复
行为;当前单槽设计不能承诺该阶段自动回滚。
## 6. 后续 FULLOTA2 业务实现
## 6. 后续 OTA 加固
后续 APP 版本建议按以下顺序加入,不再改变本基线分区:
后续 APP 版本建议按以下顺序加固,不再改变本基线分区:
1. 下载所有权收敛到单一 OTA task,其他任务只投递数据或事件。
2. 下载前停止对话、录音、播放和非必要 Flash 写入,保持 watchdog 正常喂狗。
3. 删除确认不再使用的旧暂存文件。
4. 检查 `free >= manifest.size + 65536`。
5. 从偏移 0 顺序写入 `U:/FotaFile.bin`;首版不要宣称支持断点续传。
6. 同时计算 SHA-256,并在下载完成后重新打开本地文件复核大小和 SHA-256。
7. 在设置升级标志前持久化 `PENDING_REBOOT` 状态。
8. 只有 `ql_fota_image_verify()` 成功后才重启。
9. 新 APP 启动后核对版本、release sequence 和 layout,再上报健康状态。
10. 失败时 deinit、删除残留文件、恢复业务,并上报明确错误码。
1. 后端增加 size、SHA-256、layout ID 和单调递增 release sequence;
2. 下载过程中计算 SHA-256,完成后重新打开本地文件复核;
3. manifest 增加数字签名、公钥轮换和允许的 HTTPS host;
4. 将单 pending 文件升级为双时隙、CRC 和递增 generation;
5. 增加下载进度、稳定失败码和升级阶段上报;
6. 增加按设备灰度、暂停和失败率熔断;
7. 若未来硬件 Flash 扩容,再评估真正 A/B APP。
建议使用两个带 CRC 和递增 generation 的状态文件,例如 `U:/ota_state.a`、
`U:/ota_state.b`。这能抵抗状态文件写入中断,但不构成固件 A/B 回滚。
## 7. 后端联调契约
现有 OTA 命令仅有 URL 和版本不足以可靠激活 FULLOTA2。建议后端增加:
最小 FULLOTA2 继续兼容现有 URL 和版本协议,不要求后端立即修改。完成首轮生产验收后,
建议后端增加:
```json
{