1# OTA升级 2 3## 概述 4 5OTA(Over the Air)提供对设备远程升级的能力,可以让您的设备(如IP摄像头等),轻松支持远程升级能力。目前轻量和小型系统仅支持全量包升级,暂不支持差分包升级。全量包升级是将新系统全部内容做成升级包,进行升级;差分包升级是将新老系统的差异内容做成升级包,进行升级。 6 7## 约束与限制 8 9- 支持基于Hi3861/Hi3518EV300/Hi3516DV300芯片的开源套件。 10 11- 对Hi3518EV300/Hi3516DV300开源套件,设备需要支持SD卡(VFAT格式)。 12 >  **说明:** 13 > 生成升级包需要在linux系统下面执行。 14 15 16## 生成公私钥对 17 181. 准备工作:在Windows PC 上,下载安装[OpenSSL工具](http://slproweb.com/products/Win32OpenSSL.html),并配置环境变量。 19 202. 使用OpenSSL工具生成公私钥对。 21 223. 请妥善保管私钥文件,在升级包制作过程中将私钥文件作为制作命令的参数带入,用于升级包签名,公钥用于升级时对升级包进行签名校验,公钥的放置如下: 23 轻量和小型系统将生成的公钥内容预置在代码中,需要厂商实现 HotaHalGetPubKey 这个接口来获取公钥。标准系统需要将生产的公钥放在 ./device/hisilicon/hi3516dv300/build/updater_config/signing_cert.crt 这个文件中。 24 254. 对使用 Hi3518EV300/Hi3516DV300 套件的轻量和小型系统,在上一步的基础上,还需用public_arr.txt里面的全部内容替换uboot模块device\hisilicon\third_party\uboot\u-boot-2020.01\product\hiupdate\verify\update_public_key.c 中的g_pub_key中的全部内容。 26 示例,uboot模块的公钥: 27 28 ``` 29 static unsigned char g_pub_key[PUBKEY_LEN] = { 30 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 31 0x00, 0xBF, 0xAA, 0xA5, 0xB3, 0xC2, 0x78, 0x5E, 32 } 33 ``` 34 35 36## 生成升级包 37 38 39### 轻量与小型系统升级包制作 40 411. 创建目标版本(target_package)文件夹,文件格式如下: 42 ``` 43 target_package 44 ├── OTA.tag 45 ├── config 46 ├── {component_1} 47 ├── {component_2} 48 ├── ...... 49 ├── {component_N} 50 └── updater_config 51 └── updater_specified_config.xml 52 ``` 53 542. 将待升级的组件,包括镜像文件(例如:rootfs.img等)等放入目标版本文件夹的根目录下,代替上结构中的{component_N}部分。 55 563. 填写“update_config”文件夹中的“updater_specified_config.xml”组件配置文件。 57 组件配置文件“updater_specified_config.xml”,格式如下: 58 59 ``` 60 <?xml version="1.0"?> 61 <package> 62 <head name="Component header information"> 63 <info fileVersion="01" prdID="hisi" softVersion="OpenHarmony x.x" date="202x.xx.xx" time="xx:xx:xx">head info</info> 64 </head> 65 <group name="Component information"> 66 <component compAddr="ota_tag" compId="27" resType="5" compType="0" compVer="1.0">./OTA.tag</component> 67 <component compAddr="config" compId="23" resType="5" compType="0" compVer="1.0">./config</component> 68 <component compAddr="bootloader" compId="24" resType="5" compType="0" compVer="1.0">./u-boot-xxxx.bin</component> 69 </group> 70 </package> 71 ``` 72 73 **表1** 组件配置文件节点说明 74 75 | 信息类别 | 节点名称 | 节点标签 | 是否必填 | 内容说明 | 76 | -------- | -------- | -------- | -------- | -------- | 77 | 头信息(head节点) | info节点 | / | 必填 | 该节点内容配置为:head info。 | 78 | | | fileVersion | 必填 | 保留字段,内容不影响升级包生成。 | 79 | | | prdID | 必填 | 保留字段,内容不影响升级包生成。 | 80 | | | softVersion | 必填 | 软件版本号,即升级包版本号,版本必须在“VERSION.mbn”范围内,否则无法生产升级。 | 81 | | | date | 必填 | 升级包制作日期,保留字段,不影响升级包生成。 | 82 | | | time | 必填 | 升级包制作时间,保留字段,不影响升级包生成。 | 83 | 组件信息(group节点) | component节点 | / | 必填 | 该节点内容配置为:要打入升级包的组件/镜像文件的路径,默认为版本包根路径。 | 84 | | | compAddr | 必填 | 该组件所对应的分区名称,例如:system、vendor等。 | 85 | | | compId | 必填 | 组件Id,不同组件Id不重复。 | 86 | | | resType | 必填 | 保留字段,不影响升级包生成。 | 87 | | | compType | 必填 | 处理方式全量/差分,配置镜像处理方式的,0为全量处理、1为差分处理。 | 88 89 >  **说明:** 90 > 对轻量系统/小型系统,不支持做差分升级,component标签中,属性compType值,不能配为‘1’,必须全部配置为‘0’。 91 > 92 > 对轻量系统/小型系统,不支持变分区升级包的制作。 93 944. 创建“OTA.tag文件”,内容为OTA升级包的魔数,固定如下: 95 ``` 96 package_type:ota1234567890qwertw 97 ``` 98 995. 创建“config文件”,内容为设置bootargs以及bootcmd的信息。 100 例如配置如下: 101 102 ``` 103 setenv bootargs 'mem=128M console=ttyAMA0,115200 root=/dev/mmcblk0p3 rw rootfstype=ext4 rootwait blkdevparts=mmcblk0:1M 104 (u-boot.bin),9M(kernel.bin),50M(rootfs_ext4.img),50M(userfs.img)' setenv bootcmd 'mmc read 0x0 0x82000000 0x800 0x4800;bootm 0x82000000' 105 ``` 106 1076. 执行升级包制作命令。 108 ``` 109 python build_update.py ./target_package/ ./output_package/ -pk ./rsa_private_key3072.pem -nz -nl2x 110 ``` 111 112 - ./target_package/:指定target_package路径。 113 - ./output_package/:指定升级包输出路径。 114 - -pk ./rsa_private_key3072.pem:指定私钥路径。 115 - -nz:打开not zip模式开关 116 - -nl2:打开非“标准系统”模式开关 117 118 119### 标准系统升级包制作 120 1211. 创建目标版本(target_package)文件夹,文件格式如下: 122 123 ``` 124 target_package 125 ├── {component_1} 126 ├── {component_2} 127 ├── ...... 128 ├── {component_N} 129 └── updater_config 130 ├── BOARD.list 131 ├── VERSION.mbn 132 └── updater_specified_config.xml 133 ``` 134 1352. 将待升级的组件,包括镜像文件(例如:system.img等)等放入目标版本文件夹的根目录下,代替上结构中的{component_N}部分。 136 1373. 填写“update_config”文件夹中的组件配置文件。 138 1394. 配置“update_config”文件夹中当前升级包支持的产品list:**BOARD.list**。 140 141 例如配置如下: 142 143 ``` 144 HI3516 145 HI3518 146 ``` 147 1485. 配置“update_config”文件夹中当前升级包所支持的版本范围:**VERSION.mbn**。 149 150 版本名称格式:Hi3516DV300-eng 10 QP1A.XXXXXX.{大版本号(6位)}.XXX{小版本号(3位)}。 151 152 例如:Hi3516DV300-eng 10 QP1A.190711.020。名称中“190711”为大版本号,“020”为小版本号。 153 154 例如配置如下: 155 156 ``` 157 Hi3516DV300-eng 10 QP1A.190711.001 158 Hi3516DV300-eng 10 QP1A.190711.020 159 Hi3518DV300-eng 10 QP1A.190711.021 160 ``` 161 1626. 针对增量(差分)升级包,还需要准备一个源版本(source_package),文件内容格式与目标版本(target_package)相同,需要打包成zip格式,即为:source_package.zip。 163 1647. 针对变分区升级包,还需要提供分区表文件“partition_file.xml”,partition_file.xml配置节点说明如下,可通过-pf参数指定。 165 166 分区表会随镜像一起生成,格式如下: 167 168 ``` 169 <?xml version="1.0" encoding="GB2312" ?> 170 <Partition_Info> 171 <Part Sel="1" PartitionName="镜像名称1" FlashType="flash磁盘类型" FileSystem="文件系统类型" Start="该分区起始地址" Length="该分区大小" SelectFile="实际镜像所在路径"/> 172 <Part Sel="1" PartitionName="镜像名称2" FlashType="flash磁盘类型" FileSystem="文件系统类型" Start="该分区起始地址" Length="该分区大小" SelectFile="实际镜像所在路径"/> 173 </Partition_Info> 174 ``` 175 176 **表2** 分区表Part标签说明 177 178 | 标签名称 | 标签说明 | 179 | -------- | -------- | 180 | Sel | 该分区是否生效,1表明生效,0表明不生效。 | 181 | PartitionName | 分区名称,例如:fastboot、boot等。 | 182 | FlashType | flash磁盘类型,例如emmc、ufs等。 | 183 | FileSystem | 文件系统类型,例如ext3/4、f2fs等,也可能为none。 | 184 | Start | 分区起始位置,所有分区最起始为0,单位为兆(M)。 | 185 | Length | 分区占用长度,单位为兆(M)。 | 186 | SelectFile | 实际镜像或文件所在路径。 | 187 1888. 执行升级包制作命令。 189 190 **全量升级包** 191 192 命令如下: 193 194 ``` 195 python build_update.py ./target_package/ ./output_package/ -pk ./rsa_private_key3072.pem 196 ``` 197 198 - ./target_package/:指定target_package路径。 199 - ./output_package/:指定升级包输出路径。 200 - -pk ./rsa_private_key3072.pem:指定私钥文件路径。 201 202 **增量(差分)升级包** 203 204 命令如下: 205 206 ``` 207 python build_update.py ./target_package/ ./output_package/ -s ./source_package.zip -pk ./rsa_private_key3072.pem 208 ``` 209 210 - ./target_package/:指定target_package路径。 211 - ./output_package/:指定升级包输出路径。 212 - -s ./source_package.zip:指定“source_package.zip”路径,当存在镜像需要进行差分处理时,必须使用-s参数指定source版本包。 213 - -pk ./rsa_private_key3072.pem:指定私钥文件路径。 214 215 **变分区升级包** 216 217 命令如下: 218 219 ``` 220 python build_update.py ./target_package/ ./output_package/ -pk ./rsa_private_key3072.pem -pf ./partition_file.xml 221 ``` 222 223 - ./target_package/:指定target_package路径。 224 - ./output_package/:指定升级包路径。 225 - -pk ./rsa_private_key3072.pem:指定私钥文件路径。 226 - -pf ./partition_file.xml:指定分区表文件路径。 227 228 229## 上传升级包 230 231将升级包上传到厂商的OTA服务器。 232 233 234## 下载升级包 235 2361. 厂商应用从OTA服务器下载升级包。 237 2382. 对Hi3518EV300/Hi3516DV300开源套件,需要插入SD卡(容量>100MBytes)。 239 240 241## 厂商应用集成OTA能力 242 2431. 轻量与小型系统 244 245 - 调用OTA模块的动态库libhota.so,对应头文件位于:base\update\ota_lite\interfaces\kits\hota_partition.h&hota_updater.h。 246 - libhota.so对应的源码路径为base\update\ota_lite\frameworks\source。 247 - API的使用方法,见本文“API应用场景”和API文档的OTA接口章节。 248 - 如果需要适配开发板,请参考HAL层头文件:base\update\ota_lite\hals\hal_hota_board.h。 249 2502. 标准系统请参考[JS参考规范](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-update.md)指导中的升级接口参考规范。 251 252 253## API应用场景-默认场景 254 255升级包是按照上文“生成公私钥对”和“生成升级包”章节制作的。 256 257 258### 开发指导 259 2601. 应用侧通过下载,获取当前设备升级包后,调用HotaInit接口初始化OTA模块。 261 2622. 调用HotaWrite接口传入升级包数据流,接口内部实现校验、解析及写入升级数据流。 263 2643. 写入完成后,调用HotaRestart接口重启系统,升级过程中,使用HotaCancel接口可以取消升级。 265 266 267### 示例代码 268 269使用OpenHarmony的“升级包格式和校验方法“进行升级。 270``` 271int main(int argc, char **argv) 272{ 273 printf("this is update print!\r\n"); 274 if (HotaInit(NULL, NULL) < 0) { 275 printf("ota update init fail!\r\n"); 276 return -1; 277 } 278 int fd = open(OTA_PKG_FILE, O_RDWR, S_IRUSR | S_IWUSR); 279 if (fd < 0) { 280 printf("file open failed, fd = %d\r\n", fd); 281 (void)HotaCancel(); 282 return -1; 283 } 284 int offset = 0; 285 int fileLen = lseek(fd, 0, SEEK_END); 286 int leftLen = fileLen; 287 while (leftLen > 0) { 288 if (lseek(fd, offset, SEEK_SET) < 0) { 289 close(fd); 290 printf("lseek fail!\r\n"); 291 (void)HotaCancel(); 292 return -1; 293 } 294 int tmpLen = leftLen >= READ_BUF_LEN ? READ_BUF_LEN : leftLen; 295 (void)memset_s(g_readBuf, READ_BUF_LEN, 0, READ_BUF_LEN); 296 if (read(fd, g_readBuf, tmpLen) < 0) { 297 close(fd); 298 printf("read fail!\r\n"); 299 (void)HotaCancel(); 300 return -1; 301 } 302 if (HotaWrite((unsigned char *)g_readBuf, offset, tmpLen) != 0) { 303 printf("ota write fail!\r\n"); 304 close(fd); 305 (void)HotaCancel(); 306 return -1; 307 } 308 offset += READ_BUF_LEN; 309 leftLen -= tmpLen; 310 } 311 close(fd); 312 printf("ota write finish!\r\n"); 313 printf("device will reboot in 10s...\r\n"); 314 sleep(10); 315 (void)HotaRestart(); 316 return 0; 317} 318``` 319 320 321## API应用场景-定制场景 322 323升级包不是按照上文“生成公私钥对”和“生成升级包”章节制作的,是通过其它方式制作的。 324 325 326### 开发指导 327 3281. 应用侧通过下载,获取当前设备升级包后,调用HotaInit接口初始化。 329 3302. 使用HotaSetPackageType接口设置NOT_USE_DEFAULT_PKG,使用"定制"流程。 331 3323. 调用HotaWrite接口传入升级包数据流,写入设备。 333 3344. 写入完成后,调用HotaRead接口读取数据,厂商可以自行校验升级包。 335 3365. 调用HotaSetBootSettings设置启动标记,在重启后需要进入uboot模式时使用(可选)。 337 3386. 调用HotaRestart接口,进行重启,升级过程中,使用HotaCancel接口可以取消升级。 339 340 341### 示例代码 342 343使用非OpenHarmony的“升级包格式和校验方法“进行升级。 344``` 345int main(int argc, char **argv) 346{ 347 printf("this is update print!\r\n"); 348 if (HotaInit(NULL, NULL) < 0) { 349 printf("ota update init fail!\r\n"); 350 (void)HotaCancel(); 351 return -1; 352 } 353 (void)HotaSetPackageType(NOT_USE_DEFAULT_PKG); 354 int fd = open(OTA_PKG_FILE, O_RDWR, S_IRUSR | S_IWUSR); 355 if (fd < 0) { 356 printf("file open failed, fd = %d\r\n", fd); 357 (void)HotaCancel(); 358 return -1; 359 } 360 int offset = 0; 361 int fileLen = lseek(fd, 0, SEEK_END); 362 int leftLen = fileLen; 363 while (leftLen > 0) { 364 if (lseek(fd, offset, SEEK_SET) < 0) { 365 close(fd); 366 printf("lseek fail!\r\n"); 367 (void)HotaCancel(); 368 return -1; 369 } 370 int tmpLen = leftLen >= READ_BUF_LEN ? READ_BUF_LEN : leftLen; 371 (void)memset_s(g_readBuf, READ_BUF_LEN, 0, READ_BUF_LEN); 372 if (read(fd, g_readBuf, tmpLen) < 0) { 373 close(fd); 374 printf("read fail!\r\n"); 375 (void)HotaCancel(); 376 return -1; 377 } 378 if (HotaWrite((unsigned char *)g_readBuf, offset, tmpLen) != 0) { 379 printf("ota write fail!\r\n"); 380 close(fd); 381 (void)HotaCancel(); 382 return -1; 383 } 384 offset += READ_BUF_LEN; 385 leftLen -= tmpLen; 386 } 387 close(fd); 388 printf("ota write finish!\r\n"); 389 leftLen = fileLen; 390 while (leftLen > 0) { 391 int tmpLen = leftLen >= READ_BUF_LEN ? READ_BUF_LEN : leftLen; 392 (void)memset_s(g_readBuf, READ_BUF_LEN, 0, READ_BUF_LEN); 393 if (HotaRead(offset, READ_BUF_LEN, (unsigned char *)g_readBuf) != 0) {} 394 printf("ota write fail!\r\n"); 395 (void)HotaCancel(); 396 return -1; 397 } 398 /* do your verify and parse */ 399 offset += READ_BUF_LEN; 400 leftLen -= tmpLen; 401 } 402 /* set your boot settings */ 403 (void)HotaSetBootSettings(); 404 printf("device will reboot in 10s...\r\n"); 405 sleep(10); 406 (void)HotaRestart(); 407 return 0; 408} 409``` 410 411 412## 系统升级 413 414厂商应用调用OTA模块的API,OTA模块执行升级包的签名验证、版本防回滚、烧写落盘功能,升级完成后自动重启系统。 415 416对于使用Hi3518EV300/Hi3516DV300开源套件的轻量和小型系统,在需要实现防回滚功能的版本中,需要增加LOCAL_VERSION的值,如"ohos default 1.0"->"ohos default 1.1",LOCAL_VERSION在device\hisilicon\third_party\uboot\u-boot-2020.01\product\hiupdate\ota_update\ota_local_info.c中。 417 418示例,增加版本号。 419``` 420const char *get_local_version(void) 421{ 422#if defined(CONFIG_TARGET_HI3516EV200) || \ 423 defined(CONFIG_TARGET_HI3516DV300) || \ 424 defined(CONFIG_TARGET_HI3518EV300) 425#define LOCAL_VERSION "ohos default 1.0" /* increase: default release version */ 426``` 427