结论:AGNOS 的 A/B 系统更新是一条「拉取 staging → 标非活动槽不可启动 → 流式解压并解 sparse → 写分区与哈希校验 → abctl 切槽 → 重启后标记成功」的链。校验分两层:system 等非全量校验分区只比较尾部 64 字节 marker,其余分区重算 SHA-256。因此 marker 是性能优化,不是加密签名或全量介质自检。本文为固定源码快照的静态分析,不是可在车辆上照做的刷机手册。
平台辅助函数:设备路径、GPIO 与功放
Paths 按 PC/AGNOS 选择日志、持久化、下载缓存和共享内存路径;LOG_ROOT、COMMA_CACHE、OPENPILOT_PREFIX 可影响部分路径。GPIO 统一列出 panda、GNSS、蜂窝、相机、蜂鸣器和 IMU 中断 GPIO 编号;注释明确某些 _RST_N 名称与实际高有效复位相反,不能按名字推电平。功放侧 Amplifier._set_configs() 对 MAX98089 执行 I2C 读改写,初始化时先全局关闭、配置 DAC/I2S/扬声器、再开启;Amplifier.set_configs() 最多 15 次重试并递增等待,仅捕获 OSError,全部失败返回 False。
省电策略 HardwareComma.set_power_save() 在驻车时关闭功放、大核下线并调整 CPU governor/IRQ,恢复时重新初始化功放;它紧耦合 SoC sysfs 与 IRQ 名称。台架功率工具 read_power()/sample_power() 从固定 I2C hwmon 节点读取瓦特,按 123 Hz 定时采样,这是测量工具,不是 hardwared 中的 PowerMonitoring 驻车电量估计器。
| 组件 | 源码入口 | 行为与移植边界 |
|---|---|---|
| 路径选择 | Paths |
按 PC/AGNOS 选择日志、持久化、下载缓存与共享内存路径。 |
| 引脚表 | GPIO |
列出各外设 GPIO 编号;_RST_N 名称与实际复位极性可能相反。 |
| 功放配置 | Amplifier._set_configs() |
MAX98089 I2C 读改写;失败返回 False,调用方未检查布尔值。 |
| 省电策略 | HardwareComma.set_power_save() |
驻车关功放、大核下线并调整 governor/IRQ。 |
| 台架功率工具 | read_power()/sample_power() |
按 123 Hz 读取 I2C hwmon 瓦特,非驻车电量估计器。 |
A/B 更新调用链
updated.handle_agnos_update() 只在 AGNOS 版本变化时触发,清除 staging 的一致标记后刷目标 slot;updated 由 manager 作为驻车进程运行。launch_chffrplus.sh 则在启动时先验证/切换已下载的目标系统,或循环调用设备更新器。清单在 common/hardware/comma/agnos.json,system/hardware/comma/agnos.json 是指向它的符号链接,故两处路径并非两份独立配置。
选择槽时 get_target_slot_number() 读取 abctl --boot_slot,选择另一槽;get_partition_path() 按清单 has_ab 拼分区标签。下载解压由 StreamingDecompressor.read() 流式处理 HTTP/XZ,同时计算解压后字节的 SHA-256;unsparsify() 解析 Android sparse 的 raw/fill/don’t-care 三类 chunk。分区写入 flash_partition() 先快检;非全量分区先清尾部 hash 标记,再由 extract_compressed_image() 写设备、核对 raw/hash/大小,成功后回写标记。重试与切槽上,flash_agnos_update() 先 --set_unbootable,仅下载类异常最多重试 10 次;swap() 清 marker 后调用 abctl --set_active。
| 阶段 | 核心函数与契约 |
|---|---|
| 选择槽与路径 | get_target_slot_number() 读 abctl --boot_slot 选另一槽;get_partition_path() 按 has_ab 拼标签。 |
| 下载解压 | StreamingDecompressor.read() 流式 HTTP/XZ 解压并计算解压后 SHA-256。 |
| 稀疏镜像 | unsparsify() 解析 Android sparse 的 raw/fill/don’t-care chunk。 |
| 分区写入 | flash_partition() 快检、清尾标记、写设备并核对 raw/hash/大小、回写标记。 |
| 校验策略 | verify_partition() 全量校验分区重算 SHA-256;其他分区只比较末尾 64 字节 marker。 |
| 重试与切槽 | flash_agnos_update() 先 --set_unbootable,下载类异常最多重试 10 次;swap() 清 marker 后 abctl --set_active。 |
清单中的 hash 指解压后的稀疏输入流,hash_raw 指写入的原始分区数据;对于非稀疏分区两者相同。system 是 sparse 且采用尾部 hash 标记,其余本快照清单分区采用全量哈希。
静态发现的故障边界
unsparsify() 遇到 don’t-care chunk 只 yield b"",extract_compressed_image() 对空字节不推进输出文件位置;用 1 个 4096 字节 don’t-care 块的合成 sparse 输入验证过该函数只输出 0 字节。若发布镜像实际包含此类 chunk,写入布局/大小不会保留空洞,并可能因大小或哈希校验失败——但当前未下载真实镜像,不能断言发布镜像触发了该路径。
swap() 在 abctl 返回不含成功标记的输出时无限循环,源码中没有退避或截止时间;若命令直接抛异常则退出。隔离探针还显示:前两次刷写抛 RequestException、第三次成功时,同一分区被调用三次、前两次各睡 10 秒;刷写抛非下载类 ValueError 时第一次异常直接传播,不进入十次重试;abctl --set_unbootable 假返回码为 1 时仍继续调用刷写,因为源码调用 subprocess.run() 后未检查 returncode。
verify_partition() 对非全量校验分区只信任尾部 marker,无法检测 marker 写成之后发生的实体损坏;隔离探针验证了「载荷已变而尾部仍放预期哈希字符串」时非全量 verify_partition() 返回真、强制全量检查返回假。test_manifest() 只检查 URL 的 HEAD 可达性、内容类型和非稀疏分区的两个 hash 字段相等,不覆盖 sparse 解包、分区写入、切槽或电源中断。
常见问题
AGNOS 更新时断电会怎样?
源码在写入前先 abctl --set_unbootable 标记目标槽不可启动,写入后由 swap() 用 abctl --set_active 切槽,新系统启动后再以 abctl --set_success 标记成功。本文只做静态分析,未做断电注入实板验证;插件化更新服务应把「镜像校验通过」「目标槽可启动」「切槽完成」作为分离状态回执。
为什么有些分区只比较 64 字节尾部标记?
因为 verify_partition() 对 full_check 分区重算指定大小的 SHA-256,其他分区只比较末尾 64 字节 marker,并不每次重读分区实体。这个 marker 是性能优化,无法检测写标记之后的实体损坏。
sparse 镜像里的 don’t-care 块会被保留吗?
源码里 unsparsify() 遇到 don’t-care chunk 只 yield b"",而 extract_compressed_image() 对空字节不推进输出位置;隔离探针用合成输入验证了它只输出 0 字节,所以空洞不会按原布局保留。
A/B 切槽失败会重试吗?
源码中 swap() 在 abctl 输出不含成功标记时无限循环,没有退避或截止时间;隔离探针连续尝试三次、中间无 sleep()。新平台应对切槽失败设置有界重试、告警和恢复策略。
延伸阅读
- Ed25519 签名加 96 字节事务记录:让 MCU 升级不怕中途断电 —— 另一类「A/B 槽 + 断电回滚」的设计取舍。
- updater 的 staging 与激活:先落盘再切换的两段式更新 —— staging 一致标记在更新链里的位置。
- EdgeDeploy:BLE 与 USB 两条通道怎么把签名包送到 ESP32-S3 —— 镜像传输通道与断点续传。
- 设备状态是怎么汇总的:从各子系统到 deviceState 的一条链 —— 平台组件如何向上汇报硬件状态。
参考资料
- 项目 02 ——
openpilot-research/AGNOS_PLATFORM_UPDATE_DEEP_DIVE.md(平台辅助组件、A/B 安装与校验策略、sparse/XZ 边界)。 - 项目 02 —— 同目录探针记录:四项更新控制流、五项 sparse/校验、四项 XZ 流隔离探针结论。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复