1.概述
本文探讨的OTA升级仅针对运行linux系统的域控制器,升级方式为FOTA,探究升级文件从OTA服务器下载到域控中以后,如何将升级文件安装到存储系统。
为安全起见,支持FOTA升级的存储区域必须支持AB分区设计,每个分区中储存能独立运行的程序,确保即使系统升级失败,也能通过回滚机制切换到原来的分区,保证系统的连续稳定运行。
说几个概念:
- 静默升级
系统正常运行时就可以执行OTA功能, 具备静默升级的功能后, 系统可以在运行时随时接收来自云端升级包并升级并在下次启动时切换。
- 无缝切换
当升级完成后,系统复位重启可以直接进入新系统,无需再启动阶段在进行安装等待。
- 回滚支持
升级安装过程被打断、新系统有问题无法运行、需要切换到旧系统时,都需要支持能够回滚到原来的系统。
- 安全启动
在 OTA 之后, 系统需要支持安全引导, 以确保在启动期间加载正确的升级包, 如果没有安全的引导过程, 系统可能使用未经授权或损坏的风险代码。
2.应用领域
主机厂推出OTA功能,可以有如下的应用:
- 功能扩展
主机厂通过OTA升级为车辆添加新功能,不断提升车辆的智能化水平,同时对现有功能进行优化,提升车辆的性能和用户体验,例如,优化电池和电机控制算法以提高续航里程,优化自动驾驶系统的算法不断提升驾驶能力。
- 故障修复
主机厂可随时修复由于软件异常导致的车辆故障,避免批量召回造成的重大损失,并且主机厂可以通过预测性诊断技术,预测潜在故障并提前推送修复程序。
- 个性化定制
用户可以根据自己的需求和喜好对车辆进行个性化设置,如调整座椅加热、空调温度、驾驶模式,通过OTA升级更换车辆的主题和界面样式,使车辆符合用户的个性化需求。
3.技术细节
要整体上理解OTA,就得先从存储介质开始,才能理解整个系统。
3.1 GPT分区表
GPT是一种数据结构,用于结构化磁盘、eMMC、Flash等存储介质,为用户提供看待存储介质和使用存储介质的方式和视角。
LBA是看待存储介质颗粒度的属性,一般为512字节。
GPT 是一种现代分区表类型,用于替代 MBR,解决MBR最大可寻址存储空间为2 TiB的问题,在需要管理大容量硬盘时适用,GPT 不受分区数量和大小的限制。
分区表头数据结构
点击图片可查看完整电子表格
分区表项数据结构
点击图片可查看完整电子表格
分区表读取
在linux系统,可以使用dd命令直接读取磁盘等存储介质的原始数据,使用如下命令,读取eMMC中前10个LBA中的数据,可以看出实际写入分区表的数值。
Bash |
然后用二进制工具查看读出来的原始数据如下:
写入分区表
linux下,使用如下命令为磁盘等介质进行分区:
Bash |
嵌入式系统中,对Flash和eMMC等存储介质的使用,没有linux等OS的协助,一般是直接访问存储介质的,所以能看到最纯粹的磁盘数据,根据项目需要,可以自定义使用存储介质的区块,GPT分区表也不用一定要放在磁盘的最开始的区块,根据项目需要设计好存储介质的数据结构,然后完全由启动阶段的Bootloader来处理介质内的数据。
3.2 OTA时的分区操作
槽是一个逻辑概念,是一组同类型分区的集合,如所有A分区的集合,称为A槽。
启动槽是当前系统运行的槽,目标槽是用于升级的槽。
OTA时,始终升级的是对端的非激活分区,升级的安装过程可分解为如下步骤:
- 升级前,读取目标存储设备, 解析分区表数据结构,判断当前系统处于哪个槽;
- 对启动槽写入Successful 属性,用于表明当前槽启动成功, 用于将来的回滚;
- 对目标槽清空 bootable 属性, 防止升级过程中断电, 先标明目标槽没有启动能力;
- 用 OTA 获取的数据包对目标槽进行 OTA 升级, 擦写其对应的 GPT 分区;
- 如果升级成功, 设置目标槽的 Actives 属性、 Bootable 属性,并且配置 Retry_Count 属性为 3或者5, 清除目标槽的 Successful 属性, 清除当前槽的 Actives 属性。
- 重启系统, 系统下次启动时从包含 Actvie 属性的目标槽启动。
Flash一般用MCU来刷写,eMMC一般用MPU来刷写,由bootloader来决定启动哪个槽。
4.实践应用
下面以实际案例,分析一下系统的存储分区是如何变化的,案例中包含一个异构SoC,内部分为MCU和MPU,Flash和eMMC分别存储MCU和MPU的系统文件,OTA由MPU中的OTA程序实现,系统启动由MCU的bootloader实现,MCU和MPU均可访问Flash和eMMC。
上文提到的Entry属性,包括Active 属性, Bootable 属性, Successful 属性, Retrycnt 属性 ,Active 属性用于标志槽的优先级,bootloader优先选择Active 属性=1的槽启动,Bootable 属性标志槽可否可启动,Successful 属性标志槽是否成功启动过,Retrycnt 属性标志槽可尝试启动的次数。
4.1 分区属性变化过程
首次烧写后
在域控产品出厂前,供应商会用烧录工具直接对Flash和eMMC进行烧写,包括对分区表和镜像文件的烧写,烧写后,分区Entry属性如下:
1.所有属性标志均为0;
2.Bootloader从A槽启动后,设置A槽的Active=1,RetryCount=3。
出厂烧写工具对分区表的属性写入
A槽 | Active=0 | Successful=0 | RetryCnt=0 | |
B槽 | Active=0 | Successful=0 | RetryCnt=0 |
Bootloader首次对分区表的属性写入
A槽 | Active=1 | Successful=0 | RetryCnt=3 | 从A槽启动 |
B槽 | Active=0 | Successful=0 | RetryCnt=0 |
首次进入系统后
当系统首次烧录完成,进入系统后,系统应用程序会设置A槽的Success=1,此时,分区Entry属性如下:
A槽 | Active=1 | Successful=1 | RetryCnt=3 | 从A槽启动 |
B槽 | Active=0 | Successful=x | RetryCnt=x |
系统回滚
每次系统重新运行后,Bootloader需要根据槽的Entry属性运行回滚逻辑,具体如下:
1.可以直接从A槽启动
A槽 | Active=1 | Successful=1 | RetryCnt=3 | 从A槽启动 |
B槽 | Active=0 | Successful=0 | RetryCnt=0 |
2.尝试从A槽启动
当从A槽启动,但是系统没成功启动,此时A槽的Successful=0,Bootloader仍旧从A槽尝试启动,每次都会把RetryCnt减1,如果成功启动,系统应用置Successful=1,下次Bootloader运行时,检测到Successful=1后,重新将RetryCnt=3。
A槽 | Active=1 | Successful=0 | RetryCnt=3 | 从A槽启动 |
B槽 | Active=0 | Successful=0 | RetryCnt=0 |
3.回滚到B槽进行启动
如果A槽的RetryCnt减到0,但是仍旧Successful=0,那么判定A槽无法启动系统,将切换到B槽进行启动,此时Bootloader将设置分区的Entry属性为如下:
A槽 | Active=0 | Successful=0 | RetryCnt=0 | |
B槽 | Active=1 | Successful=0 | RetryCnt=3 | 从B槽启动 |
4.2 Bootloader和应用对分区属性的设置
芯片启动后,首先进入Bootloader阶段,此阶段将读取存储系统的分区属性,根据属性值进行逻辑判断,并确定进入哪个分区,详见流程图。
5.总结
本文总结了OTA升级过程中,域控SoC是如何对其存储设备进行分区数据管理的,详细分析了GPT分区表的技术细节,以及分区表项的属性设置,以及芯片的Bootloader如何进行分区回滚的方案。
嵌入式开发的实操,已在github公开,需要学习研究的自取。
https://github.com/sydyg/Vehicle_Soft_Class.git