4.4.9.1. OTA 功能与介绍
概述与介绍
OTA :( Over-the-Air Technology,空中

在 OTA 的
广义 应用 中,可以 划分 为 云端 和 设备 端 两个 主要 组成部分。云端 部分 负责 处理 设备 的 升级 请求,包括 执行 升级 校验、下发 升级包 以及 收集 升级 结果 等 任务。而 设备 端则 主要 依赖 云端 下发 的 升级包,完成 系统软件( FOTA, Firmware Over-the-Air)或 应用程序( SOTA, Software Over-the-Air)的 更新 与 升级。 本文
旨在 提供 底层 设备 端 OTA 的 用户手册,详细 阐述 OTA 在 底层 系统软件 和 应用程序 升级 中 的 机制 及其 实现 方法,同时 提供 相关 的 开发 指导。需要 特别 指出 的 是,通过 OTA 升级 的 系统软件 与 应用程序,主要 是 指 更新 存储 在 外部 存储器(如 eMMC )中 的 数据。 OTA 的
对外 交付 物 主要 为 一套 API 及其 相应 的 实现 库(如 libupdate.so),该库 实现 了 底层 烧写 校验 等 关键 功能。上层 的 OTA 服务 架构 则 由 客户 方 实现,用以 对接 客户 的 云端 服务。在 成功 从 云端 下载 升级包 后, OTA 服务 会 通过 调用 libupdate.so 中 的 接口,实现 版本 的 升级 和校验 等 操作,从而 确保 设备 能够 顺利、安全 地 完成 软件 更新。
缩略语
| 缩略语 | 英文 |
中文 |
|---|---|---|
| SoC | System on Chip | 片上 |
| BL[x] | Boot Loader Stage [x] | 启动 |
| SPL | Secondary Program Loader | 二级 |
| GPT | GUID Partition Table | GUID 磁盘分区 |
| GUID | Globally Unique IDentifier | 全局 |
| RSA | RSA Algorithm | RSA 公开密钥 |
| eMMC | embedded Multi-Media Card | 嵌入式 |
系统分区表
外部
| 分区 |
属性 | 升级 |
举例 |
|---|---|---|---|
| 持久 |
参数 用户 |
单 |
ubootenv, misc, userdata |
| AB 分区 | 前缀 |
AB 分区 |
boot_a, boot_b |
| BAK 分区 | 前缀 |
BAK 分区 |
miniboot, miniboot_bak1 |
| 单 |
支持 OTA 升级 |
在 recovery 模式 |
hbre, system |
X5 标准 eMMC 分区表(单
分区 形式)
| 序号 | 分区 |
大小 | 文件系统 | 用途 |
|---|---|---|---|---|
| 1 | gpt | 20k | none | 通用 |
| 2 | mbr | 4k | none | 存放 bl2 加载 |
| 3 | miniboot | 1280k | none | bl2,bl31,bl32 |
| 4 | miniboot_bak1 | 1280k | none | bl2,bl31,bl32 |
| 5 | misc | 4k | none | 存储 slot ab 状态机 |
| 6 | uboot | 2m | none | Uboot |
| 7 | ubootenv | 256k | none | 储存 Uboot 环境变量 |
| 8 | vbmeta | 16k | none | 用于 |
| 9 | boot | 32m | none | Image.lz4 和 dtb |
| 10 | system | 250m | ext4 | 系统 |
| 11 | hbre | 200m | ext4 | HOBOT 工具 |
| 12 | app | 700m | ext4 | 测试用例 |
| 13 | private | 256k | ext4 | 存放 |
| 14 | userdata | - | ext4 | 存放 |
X5 标准 eMMC 分区表( AB 分区
形式)
| 序号 | 分区 |
大小 | 文件系统 | 用途 |
|---|---|---|---|---|
| 1 | gpt | 20k | none | 通用 |
| 2 | mbr | 4k | none | 存放 bl2 加载 |
| 3 | miniboot | 1280k | none | bl2,bl31,bl32 |
| 4 | miniboot_bak1 | 1280k | none | bl2,bl31,bl32 |
| 5 | misc | 4k | none | 存储 slot ab 状态机 |
| 6 | uboot_a | 2m | none | Uboot |
| 7 | uboot_b | 2m | none | Uboot |
| 8 | ubootenv | 256k | none | 储存 Uboot 环境变量 |
| 9 | vbmeta_a | 16k | none | 用于 |
| 10 | vbmeta_b | 16k | none | 用于 |
| 11 | boot_a | 32m | none | Image.lz4 和 dtb |
| 12 | boot_b | 32m | none | Image.lz4 和 dtb |
| 13 | system_a | 250m | ext4 | 系统 |
| 14 | system_b | 250m | ext4 | 系统 |
| 15 | hbre_a | 200m | ext4 | HOBOT 工具 |
| 16 | hbre_b | 200m | ext4 | HOBOT 工具 |
| 17 | app_a | 700m | ext4 | 测试用例 |
| 18 | app_b | 700m | ext4 | 测试用例 |
| 19 | private | 256k | ext4 | 存放 |
| 20 | userdata | - | ext4 | 存放 |
根据
在

目前,系统

功能详细介绍
OTA 无缝更新
AB 分区升级无缝更新
OTA 升级
AB 分区

每个 slot 的
struct slot_metadata {
// Slot priority with 15 meaning highest priority, 1 lowest
// priority and 0 the slot is unbootable.
uint8_t priority : 4;
// Number of times left attempting to boot this slot.
uint8_t tries_remaining : 3;
// 1 if this slot has booted successfully, 0 otherwise.
uint8_t successful_boot : 1;
// 1 if this slot is corrupted from a dm-verity corruption, 0 otherwise.
uint8_t verity_corrupted : 1;
// Reserved for further use.
uint8_t reserved : 7;
} __attribute__((packed));
在
active (活动
分区): 该 标识 为 排他性,指明 当前 操作 的 启动 分区。 bootloader 始终 优先选择 这个 分区 来 进行 系统 引导。 bootable(可
启动): 表示 该 分区 存在 一套 可 供 引导 的 系统文件,换言之,该 分区 具备 启动 必要 的 条件。 successful(启动
成功):表示 该 slot 的 系统 能 正常 启动。 unbootable(不可
启动):表示 该 分区 损坏 或 存在 其他 故障,无法 完成 启动 过程。在 系统升级 过程 中,该 状态 通常 会 被 标记,且 该 标记 的 效果 相当于 将 所有 上述 标记 清除;值得注意 的 是,只有 当 当前 分区 的 active 标记 被 设置 时, unbootable 的 标记 才 会 被 清除。
在
下图

状态1: 默认
状态, ab slot 都 可以 启动。 b 的 优先级 高于 a,默认 从 b 启动。
slot_a = {
.priority = 14, //14 或更低
.tries_remaining = 1,
.successful_boot = 1,
.verity_corrupted = 0,
.reserved = 0
};
slot_b = {
.priority = 15, //15
.tries_remaining = 1,
.successful_boot = 1,
.verity_corrupted = 0,
.reserved = 0
};
状态2 : 升级
状态(烧写 状态), slot a 不可 启动 且 boot success 为 0 。
slot_a = {
.priority = 14, //14 或更低
.tries_remaining = 0, //tries_remaining 为 0
.successful_boot = 0, //successful_boot 设置为 0
.verity_corrupted = 0,
.reserved = 0
};
slot_b = {
.priority = 15, //15
.tries_remaining = 1,
.successful_boot = 1,
.verity_corrupted = 0,
.reserved = 0
};
异常
状态3 : 烧写
成功,还 未 重启 时,这个 时候 将要 升级 的 slot a 设置 为 active 状态,并 将 当前 的 b slot 设置 为 非 active 状态(调整 优先级)
slot_a = {
.priority = 15, //15
.tries_remaining = 1, //tries_remaining 为 1
.successful_boot = 0, //successful_boot 为 0
.verity_corrupted = 0,
.reserved = 0
};
slot_b = {
.priority = 14, //14 或更低
.tries_remaining = 1,
.successful_boot = 1,
.verity_corrupted = 0,
.reserved = 0
};
异常
状态4 : 重启
验证 成功,将 slot a 设置 为 success boot 状态
slot_a = {
.priority = 15, //15
.tries_remaining = 1, //tries_remaining 为 1
.successful_boot = 1, //successful_boot 为 1
.verity_corrupted = 0,
.reserved = 0
};
slot_b = {
.priority = 14, //14 或更低
.tries_remaining = 1,
.successful_boot = 1,
.verity_corrupted = 0,
.reserved = 0
};
详细
BAK 升级
在
BAK 分区

升级
操作 仅 针对 主 分区 进行,确保 主 分区 内容 的 安全 和 完整。 主
分区 升级 成功 并 完成 验证 后,系统 会 将 内容 同步 至 备份 分区,以 保持数据 的 一致性 和 可 恢复性。 随后,设备
将 重启 并 重新 验证 主 分区 的 启动 状态。 若
验证 成功,主 分区 的 内容 将 再次 同步 到 BAK 分区,并 进行 md5 值 校验 以 确保 数据 的 完整性。若 验证 失败,则 系统 将 自动 切换 至 BAK 分区 以 保证 设备 的 可用性。
以下
开始(Start):
流程
从 开始 节点 启动。
升级
主 :分区(Upgrade main partition) 首先
对主 分区 进行 升级 操作,这 一步 是 将 新 的 固件 或 软件 写 入主 分区。升级 操作 仅 针对 主 分区 进行,确保 主 分区 内容 的 安全 和 完整。
主
分区 :升级 成功?(Main partition upgrade successful?) 检查
主 分区 的 升级 是否 成功。 如果
升级 失败(no),流程 结束。 如果
升级 成功(yes),则 继续 下 一步。
同步
到 :备份 分区(Sync to backup partition) 将
升级 后 的 主 分区 数据 同步 到 备份 分区。这 一步 是 为了 确保 备份 分区 有 最新 的 数据,以便 在 需要 时 可以 恢复。
重启
设备 :并 验证 启动 状态(Restart the device and verify its startup status) 重启
设备 以 应用 新 的 升级。 验证
设备 是否 能够 正常 启动。
主
分区 :启动 验证 成功?(Main partition startup verification successful?) 检查
设备 启动 后主 分区 是否 正常 工作。 如果
验证 失败(no),则 自动 切换 到 备份 分区,以 确保 系统 稳定 运行。 如果
验证 成功(yes),则 继续 下 一步。
同步
到 BAK 分区 :并 执行 MD5验证(Sync to BAK partition and perform MD5 verification) 将
数据 同步 到 BAK 分区,并 进行 MD5校验,以 确保 数据 的 完整性 和 一致性。
MD5验证
成功?(MD5 verification successful?) :检查 MD5校验
是否 通过。 如果
校验 失败(no),则 自动 切换 到 备份 分区,以 确保 系统 稳定 运行。 如果
校验 成功(yes),则 流程 继续。
升级
完成(Upgrade completed) :所有
步骤 成功 完成 后,升级 流程 结束,系统 处于 最新 状态。
GOLDEN 升级
支持
版本: NAND, eMMC; 配置
步骤(以 eMMC 版为例) 将
板级 配置文件 中 的 HR_RECOVERY_MODE变量赋值 为 “yes”,以便 使 系统 支持 recovery 模式; 
选用
支持 GOLDEN 升级 的 分区表 配置文件; 
升级
流程 重启
设备,进入 recovery 模式; reboot -m recovery -f
从 Server 下载
升级包 到 /tmp(内存)中; OTA 升级
应用(执行 方式 和 在 正常 模式 中 一致);
注意事项
GOLDEN 分区
的 升级 必须 在 recovery 模式 下 进行; recovery 模式
下 使用 的 是 initramfs,升级 时 对 /tmp 下 的 可用 空间 大小 有 要求(/tmp 可用 空间 一般 是 Kernel 下 可用内存 的 一半); NAND 版:/tmp 下
的 可用 空间 最低 要求 为:升级包 镜像 的 大小 + 程序运行 所 需 内存(约 50M); eMMC 版:/tmp 下
的 可用 空间 最低 要求 为:升级包 镜像 的 大小 + 程序运行 所 需 内存(约 60M);
分区烧写方式
本
全镜像升级
全量
镜像 指 的 是 提供 的 目标 分区 的 完整 镜像文件。在 执行 升级 时,该 镜像 将 被 直接 写入 到 外部 存储器 的 对应 分区 中。全 镜像 升级 确保 了 分区 的 完整性 和 一致性,能够 有效 地 恢复 分区 到 预期 的 状态,适用 于 需要 全面 更新 或 恢复 的 场景。通过 这种 方式,用户 可以 简化 升级 流程,减少 潜在 的 兼容性问题,提高 系统 的 稳定性 和 可靠性。
差分镜像升级(仅支持eMMC)
差分
升级 是 一种 基于 差异 的 升级 方式,通过 仅 传输 目标 分区 新旧 版本 之间 的 差异 部分(即 差异 包),而 不是 完整 的 镜像文件。在 执行 升级 时,设备 端会 接收 差异 包,并 将 其 应用 到 当前 分区 的 数据 上,从而 生成 新 版本 的 目标 分区。差分 升级 的 核心 优势 在于 显著 减少 传输数据 量,节省 带宽 和 升级 时间,特别 适合 网络 条件 有限 或 需要 频繁 升级 的 场景。但是,差分 升级 并 不会 节省 内存。由于 差异 包 需要 在 设备 端 进行 解压 和 应用,这一 过程 通常 需要 额外 的 内存 资源 来 存储 临时 数据 和 执行 复杂 的 算法 操作。因此,差分 升级 对 设备 的 内存 资源 要求 较 高,尤其 是 在 处理 较大 差异 包时,可能 会 占用 较 多 的 内存空间。 注意事项
差分
升级 的 分区 必须mount成 只读 模式(ro)。
OTA 安全包含措施
镜像防回滚保护
OTA 升级
OTA 升级包
中 的antirollback 版本号:通过 解析 镜像 头部 的antirollback 版本号 获取。 升级
设备 的antirollback 版本号:存储 于eFuse中。
镜像
目前
只有eMMC介质 下 的AB形式 的 分区 支持 镜像 防回 滚 保护; antirollback版本
只 支持 通过OTA升级 的 方式 更新; sec和nosec的antirollback版本
分别 最 多 支持65个 版本[0-64]的 迭代 其他
限制 及 实现 细节 请 参考secure部分软件 版本 说明防回 滚
开启 antirollback
当前
检查
分区表 配置文件 board_cfg/soc/x5-soc-debug-ab-gpt.json和board_cfg/soc/sub_config/miniboot.json中的 have_anti_ver属性,此属性 的 含义 是 此 分区 的 镜像 包中 是否 含有antirollback的 版本信息,需 确保miniboot、uboot、vbmeta分区 中 的 have_anti_ver属性为true,其他 分区 省略 该 属性 或 为false。目前 x5-soc-debug-ab-gpt.json默认已 开启 此 属性,若 客户 使用 自定义 分区表,需 确保 属性 已 开启。 { "Note": "Miniboot partition configuration information, please do not modify it.", "miniboot": { "bl2": { "size": "256k" }, "bl3x": { "size": "2m" }, "size": "2304k", "part_type": "BAK", "ota_is_update": true, "ota_update_mode": "image", "have_anti_ver": true } }
"uboot": { "part_type": "AB", "size": "2m", "ota_is_update": true, "ota_update_mode": "image", "have_anti_ver": true }, "vbmeta": { "part_type": "AB", "size": "16k", "ota_is_update": true, "ota_update_mode": "image", "have_anti_ver": true }
修改
device/horizon/x5/board_x5_evb_debug_config.mk配置文件:如果
是 由 客户 签名BL2的 芯片(X5 Customer root rsa key hash烧录 及 ),会使用 同时 开启secure和non-secure antirollback域 的 防回 滚 检查;如果 是 地瓜key签名 的BL2芯片,开启secure boot后,只 支持non-secure antirollback域 的 防回 滚 检查。对于 由 客户key签名BL2的 芯片,需要 设置 以下 变量: export HR_ENABLE_CUSTOMER_KEY="yes"
修改
HR_PART_CONF_FILENAME变量,由于只有eMMC介质 下 的AB形式 的 分区 支持 镜像 防回 滚 保护,所以 选用 x5-soc-debug-ab-gpt.json配置文件,若客户 自定义 分区 配置文件,需 确保 为AB分区; export HR_PART_CONF_FILENAME=${HR_BOARD_CONF_DIR}/x5-soc-debug-ab-gpt.json
修改
ANTIROLLBACK_SEC_UPDATE和ANTIROLLBACK_NOSEC_UPDATE的值(true/false),选择 是否 更新 相应eFuse中 的antirollback版本; 修改
ANTIROLLBACK_SEC_VER的值,配置miniboot.img的 版本号; 修改
ANTIROLLBACK_NOSEC_VER的值,同时 配置boot.img和vbmeta.img的 版本号; # antirollback version export ANTIROLLBACK_SEC_UPDATE="true" export ANTIROLLBACK_SEC_VER=0 export ANTIROLLBACK_NOSEC_UPDATE="true" export ANTIROLLBACK_NOSEC_VER=0
修改
uboot/configs/hobot_x5_auto_defconfig配置文件,添加CONFIG_X5_SUPPORT_CHECK_ROLLBACK=y,开启antirollback检查:CONFIG_X5_SUPPORT_CHECK_ROLLBACK=y
版本号获取
eFuse中antirollback版本
获取 方法 在
端侧 输入ota_tool -v命令,获取 当前 的antirollback版本(antirollback版本 更新 后 需 重启 才能 生效,否则 查询 到 的 还是 旧 版本号)。 root@buildroot:~# ota_tool -v OTA Library version is 1.0.1 system version is V1.1.0_20250617-1317 Nosecure antirollback version is 35 Secure antirollback version is 35
OTA 版本校验
OTA 系统
升级包
中 的 版本信息 存储 在 OTA 配置文件 data.json 中 的 sys_version 字 段 中。 当前
设备 的 版本号 则 通过 解析 位于 /etc/version 文件 来 获取。
当前
root@buildroot:~# cat /etc/version
LNX6.1.83_PL5.1_V1.0.16_20250107-1708
系统
注:若
分区校验
OTA 系统
OTA 升级包gpt.conf,其
mbr:20480:24575:2
miniboot:24576:2383871:2
miniboot_bak1:2383872:4743167:2
misc:4743168:4747263:2
uboot:4747264:6844415:2
ubootenv:6844416:7106559:2
vbmeta:7106560:7122943:2
boot:7122944:19705855:2
system:19705856:281849855:2
hbre:281849856:491565055:2
app:491565056:1225568255:2
private:1225568256:1225830399:2
userdata:1225830400:1278259199:2
gpt.conf 文件分区名称:起始位置:结束位置:分区ID,该
mbr:
起始
位置:20480 结束
位置:24575 大小:24575 - 20480 + 1 = 4096 字节
miniboot:
起始
位置:24576 结束
位置:2383871 大小:2383871 - 24576 + 1 = 2359296 字节
miniboot_bak1:
起始
位置:2383872 结束
位置:4743167 大小:4743167 - 2383872 + 1 = 2359296 字节
misc:
起始
位置:4743168 结束
位置:4747263 大小:4747263 - 4743168 + 1 = 4096 字节
uboot:
起始
位置:4747264 结束
位置:6844415 大小:6844415 - 4747264 + 1 = 2097152 字节
ubootenv:
起始
位置:6844416 结束
位置:7106559 大小:7106559 - 6844416 + 1 = 262144 字节
vbmeta:
起始
位置:7106560 结束
位置:7122943 大小:7122943 - 7106560 + 1 = 16384 字节
boot:
起始
位置:7122944 结束
位置:19705855 大小:19705855 - 7122944 + 1 = 12582912 字节
system:
起始
位置:19705856 结束
位置:281849855 大小:281849855 - 19705856 + 1 = 262943000 字节
hbre:
起始
位置:281849856 结束
位置:491565055 大小:491565055 - 281849856 + 1 = 209715200 字节
app:
起始
位置:491565056 结束
位置:1225568255 大小:1225568255 - 491565056 + 1 = 734002200 字节
private:
起始
位置:1225568256 结束
位置:1225830399 大小:1225830399 - 1225568256 + 1 = 262144 字节
userdata:
起始
位置:1225830400 结束
位置:1278259199 大小:1278259199 - 1225830400 + 1 = 52428800 字节
汇总
| 分区 |
起始 |
结束 |
大小 |
|---|---|---|---|
| mbr | 20480 | 24575 | 4 KB |
| miniboot | 24576 | 2383871 | 2304 KB |
| miniboot_bak1 | 2383872 | 4743167 | 2304 KB |
| misc | 4743168 | 4747263 | 4 KB |
| uboot | 4747264 | 6844415 | 2048 KB |
| ubootenv | 6844416 | 7106559 | 256 KB |
| vbmeta | 7106560 | 7122943 | 16 KB |
| boot | 7122944 | 19705855 | 12 MB |
| system | 19705856 | 281849855 | 250 MB |
| hbre | 281849856 | 491565055 | 200 MB |
| app | 491565056 | 1225568255 | 700 MB |
| private | 1225568256 | 1225830399 | 256 KB |
| userdata | 1225830400 | 1278259199 | 50 MB |
该
由于
OTA 升级流程
OTA 服务
从 云端 下载 升级包 并 进行 校验,如果 存在 OTA 分区,则 将 其 下载 至 /ota;否则 将 其 下载 至 /userdata。此时 还会 调用 otaInitLib 进行 动态 库 初始化。 通过
调用 otaRequestStart 发起 升级 请求。该 API 将 解压 升级包 中 的 ota_process 程序,并 创建 一个 子 进程 来 执行 该 程序 进行 实际 的 镜像 烧写。同时,该 API 还会 创建 文件 锁以 防止 同时 进行 多次 升级,并 创建 管道 文件( pipe)以便 于 与 ota_process 进行 通信。此外,还会 启动 一个 线程 定期 读取 管道( pipe),以 获取 实时 的 升级 进程、结果 及 分区 信息。 在子
进程 的 升级 阶段, OTA Service 可以 通过 调用 otaGetResult 获取 升级 结果、调用 otaGetProgress 获取 升级 进度、调用 otaGetUpdatingImageName 获取 正在 升级 的 镜像。 当 otaGetResult 返回 OTA_UPGRADE_SUCCESS 时,表示
镜像 烧写 成功,系统 将 进入 校验 阶段。 调用 otaSetPartition 将
下次 启动 的 分区 设置 为 对 向 分区,并 开始 重启 流程。 重启
后, OTA Service 通过 调用 otaGetOwnerFlag 获取 升级 的 owner,如果 owner 为 OTA Service,则 OTA Service 将 负责 此次 升级 的 校验,进入 校验 流程。 调用 otaCheckUpdate 获取
升级 结果。该 API 主要 会 检查 镜像 是否 完整 写入、是否 从 预期 的 AB slot、 BAK slot 启动。 调用 otaMarkOTASuccessful 标记
当前 分区 启动 成功。该 API 操作 AB 状态机,将 当前 slot 标记 为 boot_successful,确保 后续 将 从 该 slot 启动。如果 在 此 步骤 之前 发生 任何 重启,下次 将 从 旧版本 镜像 所在 的 slot 启动,代表 升级 失败。 调用 otaPartitionSync 进行 BAK 分区
同步,确保 BAK 分区 内容 与 主 分区 内容 一致。 最后,通过
调用 otaClearFlags 清除 升级 标记,完成 此次 升级 过程。
升级

OTA 打包端介绍
打包镜像
打包工具使用方法
可
./bd.sh otapackage help
以下
==================================================================================
\ // Welcome to the OTA Package Build System!
\// Working directory: /home/zxs/x5
//\
// \
==================================================================================
Available commands for bd.sh:
./bd.sh otapackage [image | image_diff | all | help] [path]
Support functions:
help Displays this help message.
image|all Generate an all_in_one.zip package.
image_diff [path] Generate a differential OTA package using the specified path.
Usage example:
./bd.sh otapackage image
./bd.sh otapackage all
./bd.sh otapackage image_diff /path/to/diff
./bd.sh otapackage help
==================================================================================
Note:
1. If no command is provided, the default action will be 'all'.
2. The [path] argument must be a valid file path for differential OTA.
==================================================================================
全镜像打包方法
在
./bd.sh otapackage
生成
ls out/product/ota_packages
all_in_one.signature all_in_one.zip
其中, all_in_one.zip 是 OTA 升级包, all_in_one.signature 是
差分镜像打包方法
差分
首次
差分 升级 准备 生成
旧 镜像 包:在 第一次 进行 差分 升级 时,需要 在 生成 新 镜像 前,通过 全 镜像 打包 方法 生成 旧 镜像 包,为了 便于 区分 可 手动 将 生成 的all_in_one.zip重命名 为all_in_one_old.zip。 ./bd.sh otapackage生成
新 镜像:旧 镜像 包 妥善 保存 后 编译 生成 新 的 镜像。 生成
差分 镜像 包:通过 一下 命令 可 生成 差分 镜像 包,同时 会 生成 全量 镜像 包,可 作为 下次 差分 升级 的 旧 镜像 包。 ./bd.sh otapackage image_diff ./out/product/ota_packages/all_in_one_old.zip
生成
的 OTA 包将 输出 到 以下 路径: out/product/ota_packages。在 该 目录 下,您 将 看到 四个 新 生成 的 文件: ls out/product/ota_packages all_in_one.signature all_in_one.zip all_in_one_inc.signature all_in_one_inc.zip
其中,all_in_one.zip是OTA全量
镜像 包,可 重命名 为all_in_one_old.zip作为 下 一次 差分 升级 的 旧 镜像 包。all_in_one_inc.zip是 差分 镜像 包,.signature是 相应 镜像 包 的 签名文件,签名 算法 采用 RSA 4096 SHA-256。
后续
差分 升级 从
第二次 差分 升级 开始,可以 使用 上次 差分 打包 时 生成 的 全量 镜像 包 作为 旧 镜像 包,无需 重复 生成 旧 镜像 包 操作。
签名密钥
签名build/tools/ota_tools/keys,包含
private_key.pem public_key.pem
其中, private_key.pem 是
如果
私钥
生成: openssl genrsa -out private_key.pem 4096
公钥
生成: openssl rsa -RSAPublicKey_out -in private_key.pem -out public_key.pem
替换
路径 build/tools/ota_tools/keys 下 的 private_key.pem 和 public_key.pem 执行
下列 命令 重新 编译 ./bd.sh ./bd.sh otapackage
注意: 打包
OTA 升级包介绍
升级包结构
Archive: all_in_one.zip
Length Date Time Name
--------- ---------- ----- ----
497 2025-01-09 14:30 gpt.conf
3030 2025-01-09 14:30 data.json
1310720 2025-01-09 17:39 miniboot.img
1210176 2025-01-09 21:04 uboot.img
16384 2025-01-09 17:40 vbmeta.img
33554432 2025-01-09 17:40 boot.img
262144000 2025-01-09 17:40 system.img
209190912 2025-01-09 21:09 hbre.img
734003200 2025-01-09 17:41 app.img
237520 2025-01-09 14:29 ota_process
--------- -------
1241670871 10 files
上述
| 文件 | 描述 |
|---|---|
| gpt.conf | 分区表 |
| data.json | OTA 配置文件 |
| *.img | 各 |
| ota_process | OTA 烧写 |
注意:OTA 包中应
OTA 配置文件
OTA 升级包
通用
| 配置 | 类型 | 功能 |
|---|---|---|
| sys_version | str | 系统软件 |
| update_partition | arr[str] | 升级 |
| partition_info | arr[obj] | 各 |
各
| 配置 | 类型 | 功能 |
|---|---|---|
| md5sum | arr[obj] | 各 |
| md5_scope | arr[obj] | 各 |
| medium | str | 所在 |
| part_type | str | 分区 |
| upgrade_method | str | 升级 |
| imgname | str | 镜像.img/.bin/.ubifs 为 |
以下
{
"sys_version": "LNX6.1.83_PL5.1_V1.0.16_20250111-1657",
"update_partition": [
"miniboot",
"uboot",
"vbmeta",
"boot",
"system",
"hbre",
"app"
],
"partition_info": {
"miniboot": {
"md5sum": {
"miniboot.img": "892e12d67ce2f6d8cf4fa77644fb329f"
},
"md5_scope": {
"miniboot.img": 2359296
},
"medium": "emmc",
"part_type": "BAK",
"upgrade_method": "image",
"imgname": "miniboot.img"
},
"uboot": {
"md5sum": {
"uboot.img": "2859118f26e787993551baa1eaf05b79"
},
"md5_scope": {
"uboot.img": 2097152
},
"medium": "emmc",
"part_type": "GOLDEN",
"upgrade_method": "image",
"imgname": "uboot.img"
},
"vbmeta": {
"md5sum": {
"vbmeta.img": "a81d6d6d7c3bd900ab1c6d172fc160c0"
},
"md5_scope": {
"vbmeta.img": 16384
},
"medium": "emmc",
"part_type": "GOLDEN",
"upgrade_method": "image",
"imgname": "vbmeta.img"
},
"boot": {
"md5sum": {
"boot.img": "a0e1065fd364f8d18198f099309b975c"
},
"md5_scope": {
"boot.img": 12582912
},
"medium": "emmc",
"part_type": "GOLDEN",
"upgrade_method": "image",
"imgname": "boot.img"
},
"system": {
"md5sum": {
"system.img": "92d66eb15695e0f29de1368703cf74b2"
},
"md5_scope": {
"system.img": 262144000
},
"medium": "emmc",
"part_type": "GOLDEN",
"upgrade_method": "image",
"imgname": "system.img"
},
"hbre": {
"md5sum": {
"hbre.img": "c7b4a3467667f2b61940aa8b5135b811"
},
"md5_scope": {
"hbre.img": 209715200
},
"medium": "emmc",
"part_type": "GOLDEN",
"upgrade_method": "image",
"imgname": "hbre.img"
},
"app": {
"md5sum": {
"app.img": "4303af014b1f269aa97eb0d91be8cb9f"
},
"md5_scope": {
"app.img": 734003200
},
"medium": "emmc",
"part_type": "GOLDEN",
"upgrade_method": "image",
"imgname": "app.img"
}
}
}
通过
OTA 升级端介绍
本
ota_tool 使用
ota_tool 是hbre/otaupdate/src/ota_tool 目录。
ota_tool Usage:
-v, --version get this library's version,
system version,
antirollback version
-b, --boot check ota update status when boot.
-s, --setpartition [partition] set A/B slot partition, 0--A; 1--B.
-g, --getpartition get A/B slot partition, 0--A; 1--B.
-p, --package [package_path] specify the path of package, the package paths can be relative or absolute, it's length must be smaller than 64 bytes.
-n, --noreboot request ota without reboot.
-c, --checksign signature check.
-i, --signature signature information file.
-h, --help Display this help screen.
使用 ota_tool 进行
参数
-h 用于
获取 帮助 信息。 -v 用于
获取 libupdate.so 版本、当前 系统软件 版本。 -b 启动
后 检查 升级 结果(系统启动 时会 自动 进行 此 检查,用户 无需 干预)。 -s 设置
下次 启动 A/B slot, 0 表示 A, 1 表示 B。 -g 获取
当前 A/B slot。 -p 指定
升级包。 -n 升级
成功 后 不 进行 自动 重启。 -c 启用
包 完整性 验证。 -i 指定
签名文件(必须 跟 在 -p 参数 后面)。
举例:
# 全量升级,不验证包完整性
ota_tool -p all_in_one.zip
# 全量升级,验证包完整性
ota_tool -c -p all_in_one.zip -i all_in_one.signature
# 差分升级,不验证包完整性
ota_tool -p all_in_one_inc.zip
# 差分升级,验证包完整性
ota_tool -c -p all_in_one_inc.zip -i all_in_one_inc.signature
ota_tool 实现
ota_tool 使用 C 语言hbre/otaupdate/src/ota_tool/otainterface.c。其
主
最后,调用 ota_update_all_img 函数
ota_update_all_img 实现
static int32_t ota_update_all_img(const char *zip_path)
{
int32_t progress = 0;
uint8_t slot = 0;
uint8_t next_slot = 0;
int32_t ret = 0;
ota_update_result_e result = 0;
char part_name[ARRAY_32] = { 0 };
ret = otaGetPartition(&slot);
if (ret < 0) {
return ret;
}
if (slot == 2) {
next_slot = 0;
printf("The slot [%d] to be burned\n", next_slot);
} else {
next_slot = 1 - slot;
printf("The slot [%d] to be burned\n", next_slot);
}
ret = otaInitLib();
if (ret < 0) {
printf("error:init failed!\n");
return ret;
}
ret = otaRequestStart(zip_path, OTA_TOOL);
if (ret < 0) {
printf("error: start ota update failed!\n");
ret = -1;
goto err;
}
while (otaGetResult() != OTA_UPGRADE_SUCCESS && otaGetResult() != OTA_UPGRADE_FAILED) {
progress = otaGetProgress();
result = otaGetResult();
otaGetUpdatingImageName(part_name, sizeof(part_name));
if (result == UPGRADE_FAILED) {
printf("error: ota update failed!\n");
ret = -1;
break;
}
OTA_show_Process_Bar(part_name, progress,
"OTA is upgrading ...");
usleep(100 * 1000);
}
err:
if (otaGetResult() == OTA_UPGRADE_SUCCESS) {
ret = otaSetPartition(next_slot);
if (ret < 0) {
printf("error: set partition failed!\n");
return ret;
}
if (g_is_reboot == true) {
printf("reboot system!\n");
ota_system_exe("reboot");
} else {
printf("ota update success and waiting for reboot!\n");
}
}
otaDeinitLib();
return ret;
}
调用
otaGetPartition获取当前 所在 ab slot。 调用
otaInitLib初始化 libupdate.so 库。调用
otaRequestStart函数并 传入 升级包 路径 及 所有者( OTA_TOOL),开始 执行 升级。 等待
otaGetResult返回结果 为 OTA_UPGRADE_SUCCESS或OTA_UPGRADE_FAILED。在此 过程 中,调用 otaGetProgress、otaGetResult、otaGetUpdatingImageName获取升级 进度、结果 和 正在 升级 的 镜像,并 通过 OTA_show_Process_Bar在控制台 打印 进度。 若
升级 结果 otaGetResult为OTA_UPGRADE_SUCCESS,则认为 升级 成功,调用 otaSetPartition设置 ab slot 到对 向 slot ,并 根据 配置 重启 系统。

ota_boot_check 实现
系统启动S99ota_update_check 服务,该ota_tool -b 进行ota_boot_check 流程。
int32_t ota_boot_check(void)
{
int32_t ret = 0;
enum ota_update_owner owner = 0;
if ((ret = otaInitLib()) != 0) {
printf("error: init failed!\n");
return ret;
}
if ((ret = otaGetOwnerFlag(&owner)) != 0) {
printf("error: Get owner flag failed!\n");
goto exit;
}
if (owner == NORMAL_BOOT) {
printf("Normal boot\n");
if ((ret = otaMarkOTASuccessful()) != 0) {
printf("error: mark boot success failed\n");
}
return ret;
}
if (owner != OTA_TOOL) {
printf("ota_tool is not owner, owner is [%d]\n", owner);
return otaDeinitLib();
}
if ((ret = otaCheckUpdate()) != 0) {
printf("error: boot check failed\n");
goto exit;
}
if ((ret = otaMarkOTASuccessful()) != 0) {
printf("error: mark boot success failed\n");
goto exit;
}
if ((ret = otaPartitionSync()) != 0) {
printf("error: partition sync failed\n");
goto exit;
}
exit:
otaClearFlags();
otaDeinitLib();
return ret;
}
调用
otaInitLib初始化库。 调用
otaGetOwnerFlag获取当前 的 升级 owner。 若 owner 为
NORMAL_BOOT,则调用 otaMarkOTASuccessful标记启动 成功,随后 退出。 若 owner 不
为 OTA_TOOL,正常退出,等待 其他 所有者 进行 重启 检测。 调用
otaCheckUpdate获取升级 结果。如果 结果 异常,调用 otaClearFlags清除 OTA 标记,终止 OTA 流程。调用
otaMarkOTASuccessful标记启动 成功。 调用
otaPartitionSync同步 AB 分区和 BAK 分区。如果 此 调用 返回 非零值,则 表示 AB、 BAK 分区 同步 失败,对应 的 分区 处于 不可 用 状态,需要 重新 进行 OTA 升级 以 修复 对应 的 AB 和 BAK 分区。
ota_boot_check 流程图
