Skip to main content

x2600h_linux_mmc0_AB分区ota

一 OTA 原理和介质介绍

1.1 OTA简介和实现方法

​ 君正OTA简单来说分为三个步骤。

  • 第一步:将需要更新的升级固件放到OTA升级服务器上。

  • 第二步:通过网络将升级固件传输到设备上。

  • 第三步:设备利用得到的OTA升级固件更新到自身的设备上,然后重启切换到更新好的系统上,从而完成OTA整个流程。

    君正基于Linux 系统,目前OTA 升级的策略是采用双系统备份。也就是一台设备上同时含有两套系统。每次只运行在其中一套系统上。其中共用一个uboot,有两份kernel和rootfs文件系统。这样不管OTA升级是否成功,都可以保证当前的系统是正常的。

1.2 OTA 存储介质

​ 君正OTA目前支持的存储介质包含两种:mtd 和 mmc两种。其中mtd包含:nand 和 nor,本教程主要为mmc介质的OTA升级介绍。

二 软硬件环境

halley7: x2600h + mmc0, uart0_pe

job/linux/x2600 作为当前系统的编译源码(简称A系统,使用以太网MAC连网下载,ip:10.4.3.2)

job/linux/testcode/build 作为待升级的系统的编译源码(简称B系统,,使用以太网MAC连网下载,ip:10.4.3.2)

window11 安装的虚拟机virtualbox ubuntu18.04 64bit(ip:10.4.3.4)作为服务器。

三 A系统设置

编译配置:x2600h_mmc0_5.10_defconfig

3.1 uboot 添加支持 ota 功能

# 确定当前uboot编译路径及配置文件
xy@vb:~/x2600/build$ cat configs/x2600h_mmc0_5.10_defconfig | grep uboot
APP_uboot_toolchain_dir=../tools/toolchains/mips-gcc720-glibc229
APP_uboot_dir=../bootloader/uboot-x2000
APP_uboot_config=x2600h_base_xImage_msc0
# 修改uboot的配置文件
xy@vb:~/x2600/build$ vim ../bootloader/uboot-x2000/boards.cfg

在uboot对应的编译配置boards.cfg中将SPL_OS_BOOT改为SPL_OS_OTA_BOOT:

# 修改后的配置文件如下:
x2600h_base_xImage_msc0 mips xburst2 x2600_base ingenic x2600 x2600_base:SPL_JZMMC_SUPPORT,ENV_IS_IN_MMC,GPT_CREATOR,JZ_MMC_MSC0,SPL_PARAMS_FIXER,SPL_OS_OTA_BOOT,GPT_TAB_BUILT_IN,X2600H_DDR,SYS_UART_INDEX=0,LPJ="11935744",BOOT_FAST_FIXED

3.2 设置A系统的ota相关

1

其中“当前系统的ota版本号”要比想要升级的系统的ota版本号低:

2

从上往下,每项宏的配置对应的功能如下所示:

1:Storage medium(存储介质)(APP_ota_updater_storage_medium):
//板子的存储介质,包含两种类型,nand和mmc,可以根据自己的设备存储介质选择不同的类型

2:当前系统的ota版本号(每次编译新的ota版本时都应该递增此值)(APP_ota_updater_version)
//当前ota的版本号,会写入文件系统的 /etc/ota_info 'ota_version=N'
该版本号是指打包升级包的版本号,如果你需要制作版本号为3,该处需要修改为3

3:ota 升级服务器的地址(App_ota_updater_site)
//ota的服务器地址路径,会写入文件系统的 /etc/ota_info 'ota_site=...'

4:ota 分包大小(App_ota_updater_block_size)
//ota分包大小,制作ota镜像时会将ota镜像按此大小分包

5:ota kernel 镜像路径(App_ota_updater_kernel_img_path)
//ota kernel镜像的绝对路径,制作ota镜像时需要

6:ota rootfs 镜像路径(App_ota_updater_rootfs_img_path)
//ota rootfs镜像的绝对路径,制作ota镜像时需要

君正ota采用的是只读系统,同时我们设置了一个data分区,该分区是可读可写分区,在烧录时在分区表中声明此分区,并在IConfigTool中勾选自动挂载脚本,便会自动挂载到/usr/data下。

​ 在IConfigTool中做如下设置:

3

ota需要联网,本例选择以太网。也可以使用wifi或者4G,客户可以根据硬件设计自行实现联网:

4

配置后保存:

5

3.3 生成A系统的镜像

xy@vb:~/x2600/build$ make x2600h_mmc0_5.10_defconfig
xy@vb:~/x2600/build$ make
xy@vb:~/x2600/build$ ll output/
总用量 9356
-rw-r--r-- 1 xy xy 4603904 7月 22 16:55 rootfs.squashfs
-rw-rw-r-- 1 xy xy 215392 7月 22 17:00 u-boot-with-spl-mbr-gpt.bin
-rw-rw-r-- 1 xy xy 4743232 7月 22 16:55 xImage

四 B系统设置

4.1 uboot 添加支持 ota 功能

# 确定当前uboot编译路径及配置文件
xy@vb:~/testcode/build$ cat configs/x2600h_mmc0_5.10_defconfig | grep uboot
APP_uboot_toolchain_dir=../tools/toolchains/mips-gcc720-glibc229
APP_uboot_dir=../bootloader/uboot-x2000
APP_uboot_config=x2600h_base_xImage_msc0
# 修改uboot的配置文件
xy@vb:~/testcode/build$ vim ../bootloader/uboot-x2000/boards.cfg

在uboot对应的编译配置boards.cfg中将SPL_OS_BOOT改为SPL_OS_OTA_BOOT:

# 修改后的配置文件如下:
x2600h_base_xImage_msc0 mips xburst2 x2600_base ingenic x2600 x2600_base:SPL_JZMMC_SUPPORT,ENV_IS_IN_MMC,GPT_CREATOR,JZ_MMC_MSC0,SPL_PARAMS_FIXER,SPL_OS_OTA_BOOT,GPT_TAB_BUILT_IN,X2600H_DDR,SYS_UART_INDEX=0,LPJ="11935744",BOOT_FAST_FIXED

4.2 设置B系统的ota相关

6

其中“当前系统的ota版本号”要比A系统的ota版本号高,可以不连续:

7

从上往下,每项宏的配置对应的功能如下所示:

1:Storage medium(存储介质)(APP_ota_updater_storage_medium):
//板子的存储介质,包含两种类型,nand和mmc,可以根据自己的设备存储介质选择不同的类型

2:当前系统的ota版本号(每次编译新的ota版本时都应该递增此值)(APP_ota_updater_version)
//当前ota的版本号,会写入文件系统的 /etc/ota_info 'ota_version=N'
该版本号是指打包升级包的版本号,如果你需要制作版本号为3,该处需要修改为3

3:ota 升级服务器的地址(App_ota_updater_site)
//ota的服务器地址路径,会写入文件系统的 /etc/ota_info 'ota_site=...'

4:ota 分包大小(App_ota_updater_block_size)
//ota分包大小,制作ota镜像时会将ota镜像按此大小分包

5:ota kernel 镜像路径(App_ota_updater_kernel_img_path)
//ota kernel镜像的绝对路径,制作ota镜像时需要

6:ota rootfs 镜像路径(App_ota_updater_rootfs_img_path)
//ota rootfs镜像的绝对路径,制作ota镜像时需要

君正ota采用的是只读系统,同时我们设置了一个data分区,该分区是可读可写分区,在烧录时在分区表中声明此分区,并在IConfigTool中勾选自动挂载脚本,便会自动挂载到/usr/data下。

​ 在IConfigTool中做如下设置:

3

ota需要联网,本例选择以太网。也可以使用wifi或者4G,客户可以根据硬件设计自行实现联网:

4

配置后保存:

8

4.3 生成B系统的镜像

xy@vb:~/testcode/build$ make x2600h_mmc0_5.10_defconfig
xy@vb:~/testcode/build$ make
xy@vb:~/testcode/build$ ll output/
总用量 9356
-rw-r--r-- 1 xy xy 4603904 7月 22 17:59 rootfs.squashfs
-rw-rw-r-- 1 xy xy 215392 7月 22 17:59 u-boot-with-spl-mbr-gpt.bin
-rw-rw-r-- 1 xy xy 4743232 7月 22 17:59 xImage
# 生成ota分包
xy@vb:~/testcode/build$ make ota_img

注意: 在编译ota_img 之前必须得先保证编译好buildroot镜像⽂件与kernel镜像⽂件

在 "⼯程/build"⽬录下编译ota升级镜像,会在 "⼯程/build/output/ota" ⽬录下⽣成ota镜像⽂件:

9

其中 ota_update.in⽂件

build$ cat output/ota/ota_update.in

10关键字 ota_version:做版本检查,避免忙中出错和实际版本不一致 关键字 img_type:定义升级的内容类型:kernel rootfs 关键字 img_name:升级的文件名,在升级包目录的文件名前缀 关键字img_size:升级文件的总大小,因为升级文件会被拆分成多个文件,默认按1Mbyte拆分 关键字img_md5:升级文件的md5sum值

五 服务器搭建(仅供参考)

君正不提供服务器,客户需自己解决,以下服务器搭建仅供测试使用。ota的升级文件在设备端,能被wget命令获取即可。

5.1 安装 apache2 服务器,⽤于测试ota升级

安装命令: sudo apt-get install apache2

5.2 启动 apache2 服务器

启动命令: service apache2 start

电脑端放置OTA升级固件的⽬录:/var/www/html/ 。

注:如果设备端⽆法访问apache2服务器,有可能是防⽕墙的问题。
可以关掉电脑端的防⽕墙再进⾏测试,命令如下:
暂时关闭防⽕墙:
systemctl stop firewalld
# 新建服务器目录:
mkdir -p /var/www/html/ota/x2600h_halley7
cd /var/www/html/ota/x2600h_halley7

5.3 OTA服务器文件部署

本例中,将版本升级至1,我的pc机ip地址是10.4.3.4,所以地址:http://10.4.3.4/ota/x2600h_halley7/ 用于存放ota相关的文件,对应于服务器本机的路径为:/var/www/html/ota/x2600h_halley7。

该目录下的文件需要自己添加:

cd /var/www/html/ota/x2600h_halley7/

ls
ota_config.in #定义当前最新版本的ota版本
ota_v1 #版本为1对应的ota升级文件夹

新建ota_config.in ,该文件内容如下:

/*
关键字 current_version ⽤于定义当前最新的ota的版本
当current_version=1时,对应于⽬录ota_v1/存放ota的升级信息和⽂件
*/

current_version=1

新建ota_v'N'/⽬录('N'是数字,这里添加ota_v1),需要自己手动添加,并将编译好的ota升级镜像文件复制到该文件夹

mkdir ota_v1

cp /home/zxy/job/linux/testcode/build/output/ota/* /var/www/html/ota/x2600h_halley7/ota_v1/ -arf

⼿动创建 ota_v1.ok 到 /var/www/html/ota/x2600h_halley7/ota_v1/ ⽂件夹下,最终确定 ota_v1版本发布:

touch ota_v1.ok 

ota_v1.ok (1是版本号 ota_v'N'.ok 且该文件的内容为空即可) 此文件是做 ota 的最后一步确认,制作 ota镜像的时候不会有这个文件产生,这个文件需要客户自己手动创建生成,以确定此版本的ota验证是成功可行的,可以提供给外部的设备端进行下载更新。设备端如果检测到此文件才会启动ota升级流程。

所以/var/www/html/ota/x2600h_halley7/ota_v1文件夹的内容如下:

xy@vb:/var/www/html/ota/x2600h_halley7/ota_v1$ ls -lh
总用量 9.0M
-rw-rw-r-- 1 xy xy 165 722 18:00 ota_md5_rootfs.squashfs.6b33065f294a7a301959c678bebcceab
-rw-rw-r-- 1 xy xy 165 722 18:00 ota_md5_xImage.9094c162ab420558f08d32eb8f28ba6e
-rw-rw-r-- 1 xy xy 206 722 18:00 ota_update.in
-rw-rw-r-- 1 xy xy 0 722 17:55 ota_v1.ok
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 rootfs.squashfs.0000.6b33065f294a7a301959c678bebcceab
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 rootfs.squashfs.0001.5761023b8d428600f8911e9176bcb1ce
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 rootfs.squashfs.0002.2f4376f48a9e9c7902050bd02096138c
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 rootfs.squashfs.0003.fb960d4e0f996603b08a2edf95bbd00a
-rw-rw-r-- 1 xy xy 400K 722 18:00 rootfs.squashfs.0004.cfb6893b6e6d82cf4c00b4db08e6a35b
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 xImage.0000.9094c162ab420558f08d32eb8f28ba6e
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 xImage.0001.4ee5730cbd24c225c5b7fcb628a24752
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 xImage.0002.1443c97e94f8c38d8d6a09d1574fe8e1
-rw-rw-r-- 1 xy xy 1.0M 722 18:00 xImage.0003.bedb8b25467b301fabfa276c5921c12f
-rw-rw-r-- 1 xy xy 537K 722 18:00 xImage.0004.69139ea10e0577082266ea0758095a82

ota_v1/ota_update.in 里面放的是版本号为1的升级包,其中ota_version=1,该值跟第4.2章节中的设置相对应。

五 烧录

其中 kernel和kernel2 烧录的是同一个kernel固件, rootfs 和 rootfs2 烧录的是同一个rootfs固件。第一次烧录两份文件系统是为了后面ota升级备用使用的。每次板子都会只运行在正常的文件系统上面,保证板子能够正常运行。

ota 分区烧录的文件可以为一个空文件empty。

11

可以根据用户需要修改uboot中gpt分区表信息,注意分区信息要与烧录工具中的POLICY一致,设置如下:

12

uboot中gpt分区文件的位置:

13

# 打开分区表
xy@vb:~/x2600/bootloader/uboot-x2000$ vim board/ingenic/x2600_base/partitions.tab

14

注:此处uboot中gpt分区信息要与烧录工具中的POLICY一致!!!

14

14

六 升级流程

6.1 升级脚本

# 烧录后保证能ping 通10.4.3.4(服务器)
ifconfig eth0 10.4.3.2 netmask 255.255.255.0 up
ping 10.4.3.4
# ota的配置在文件 "/etc/ota_info" 中,ota_info 配置文件由前面make ota_img命令生成
cat /etc/ota_info

ota_version=0
ota_site=http://10.4.3.4/ota/x2600h_halley7

ota的升级脚本文件:

14

# 执行ota脚本开始升级
sh /etc/ota_bin/network_ota_update.sh

14

# 升级之后reboot重启,启动的就是升级后的系统
reboot
# 再次查看当前系统版本号,此时已经切换到升级后的系统了
cat /etc/ota_info
ota_version=1
ota_site=http://10.4.3.4/ota/x2600h_halley7

network_ota_update.sh 脚本的升级过程如下:

1 检查是否设置过下次启动项(检查文件是否存在/tmp/ota_boot_set),如未设置过,则继续
2 下载测试服务器的 ota_config.in文件,解析当前ota版本号
3 获取/etc/ota_info 中的版本号并与服务器ota版本号做对比,如果服务器更高,则继续
4 下载测试服务器 ota_v1/ota_v1.ok 文件,如果无法下载,说明当前v1版本还没有准备好,则退出ota升级流程
5 下载测试服务器 ota_v1/ota_update.in 文件
6 获取 ota_version=字段,如果和当前版本不相等,则退出ota升级流程
7 获取img_type img_name img_size img_md5 字段,并且验证和保存
ota_kernel ota_kernel_name ota_kernel_size ota_kernel_md5
ota_rootfs ota_rootfs_name ota_rootfs_size ota_rootfs_md5
8 获取需要升级的kernel rootfs分区,检查kernel分区大小,检查rootfs 分区大小是否满足升级包要求
9 开始升级kernel
10 启动升级脚本 /etc/ota_bin/ota_update_kernel.sh
11 调用download_ota_img 函数下载 xImage 相关文件,校验 md5值
12 去掉文件 md5 后缀,生成 xImage.000N,再生成 xImage.000N.ok 通知升级脚本读取 xImage.000N数据
13 继续下载新的 xImage.000N+1.md5文件,等待xImage.000N.done产生,并删除 xImage.000N
14 继续11的步骤,直到xImage 相关文件下载完成
15 等待xImage.ok 生成,表示升级脚本成功,或者 xImage.failed 表示升级脚本失败
16 按前面的步骤升级rootfs
17 如果升级成功,则设置启动分区为刚才升级的分区,返回值0
18 如果什么都没有升级,也没有出错,则返回值2,中间任意步骤出错,返回1

6.2 设置ota启动分区

若升级成功会自动设置启动分区为升级的分区,若想设置回来可以设置ota分区的内容,uboot会检查ota分区的内容,以决定启动哪个分区:

#查看ota分区对应的设备名
fdisk -l #查看分区表
cat /proc/partitions #查看分区对应的设备

#写入启动分区名 ota:kernel2 或者 ota:kernel
echo ota:kernel2 > /dev/mmcblk0p5

#查看当前的启动分区名
dd if=/dev/mmcblk0p5 bs=1 count=256

七 ota 补充说明

7.1 ota_bin 目录

# ls /etc/ota_bin/

#ota升级过程均使用脚本编写,都有中文注释

# 升级主程序 供开发者调用

local_ota_update.sh
ota 本地升级主程序,探测是否需要 ota 升级,以及本地读取 ota 镜像执行 ota 升级流程

network_ota_update.sh
ota 网络升级主程序,探测是否需要 ota 升级,以及下载 ota 镜像执行 ota 升级流程

network_main_os_update_recovery.sh
ota recovery方式进行ota升级的主程序,主系统 中用来探测是否需要 ota 升级,下载 recovery 镜像以及升级 recovery 系统

network_recovery_update_main_os.sh
ota recovery方式进行ota升级的主程序,recovery系统 中用来下载 主系统 镜像以及升级 主系统

# 功能性程序与函数

ota_local_method.sh
ota 升级程序的设备端方法和升级流程回调的实现

ota_update_kernel.sh
ota kernel的升级方法

ota_update_rootfs_squashfs.sh
ota rootfs.squashfs 的升级方法

ota_update_rtos_bin.sh
ota rtos.bin 的升级方法

ota_utils.sh
ota 用到的实用函数集

ota_img_data_provider.sh
ota 流式升级方式的数据提供者
被ota_update_kernel.sh ota_update_rootfs_squashfs.sh 调用

main_os_info_check.sh
ota 检查 主系统 的升级信息可用性
被network_main_os_update_recovery.sh 调用

recovery_version_utils.sh
ota 用于检测recovery版本信息的工具脚本
被network_main_os_update_recovery.sh 调用

7.2 ota 文件组织策略

编译ota升级镜像时,kernel和buildroot的镜像文件被拆分成多份,分成多个文件,依次追加md5值,并且保存md5值用做文件校验,这样的好处是可以确保下载的文件不会因为服务器缓存的问题而导致的包之间不一致的问题。即使文件名碰巧重合,设备端也可以通过后缀的md5值进行校验

以kernel(xImage)为例子,将 xImage(大小 4743232)拆分成5个文件:

xImage.0000.9094c162ab420558f08d32eb8f28ba6e
xImage.0001.4ee5730cbd24c225c5b7fcb628a24752
xImage.0002.1443c97e94f8c38d8d6a09d1574fe8e1
xImage.0003.bedb8b25467b301fabfa276c5921c12f
xImage.0004.69139ea10e0577082266ea0758095a82

对xImage 以及 xImage.000 求 md5sum,关键字 img_md5 保存 xImage.md5sum,ota_md5_xImage 保存 xImage.000的md5sum 用于下载后设备校验。

所有的xImage 相关文件被依次追加前一个xImage包文件的md5值作为后缀:

1 ota_md5_xImage -> ota_md5_xImage."xImage.md5"
2 xImage.0000 -> xImage.0000."xImage.md5"
3 xImage.0001 -> xImage.0001."xImage.0000.md5"
4 xImage.0002 -> xImage.0002."xImage.0001.md5"
5 xImage.0003 -> xImage.0003."xImage.0002.md5"
5 xImage.0004 -> xImage.0004."xImage.0003.md5"

7.3 ota 分区策略

更新设备分区的时候必须保证当前被更新的分区不被占用,所以rootfs分区的程序不能更新rootfs分区的内容,只能去更新另外一个rootfs分区。

从安全的角度上也是一样,如果当前能更新程序所在的分区,比如 rootfs 打包在kernel中的情况,但是遇到掉电/死机/重启等不正常的时候,再一次启动分区的内容不是完整的,所以设备就会无法启动。

所以必须为ota升级程序划分专门的kernel分区和rootfs分区,另外uboot不能进行升级,一旦升级失败机器将无法启动。

目前采用的策略是双分区备份策略,即rootfs的升级程序升级kernel2,rootfs2,反之rootfs2升级kernel,rootfs。升级完成之后改变当前的启动分区为被升级的分区并且重启,启动分区的信息保存在“ota”分区中。

八 ota 常见问题汇总

8.1 ota升级完成无法进入到新的系统?

​ 可以检查设备端的固件uboot是否支持ota功能。具体可以参考上面 4.1 uboot 添加支持 ota 功能。

8.2 设备端无法通过wget获取到升级服务器上的文件?

可以检查电脑端的防火墙是否关闭。如果是局域网,要检查你设备端的网络和你的升级服务器上的网络是否在同一个网段。

8.3 遇到region .sram' overflowed by 144 bytes的问题

如果遇到region .sram' overflowed by 144 bytes的问题,可以对spl部分进行裁剪,如关闭打印信息

# 文件bootloader/uboot-x2000/include/configs/x2600_base.h中
# 禁用此标志(直接删除也可以):
/* #define CONFIG_SPL_SERIAL_SUPPORT */