2.5 镜像分割
镜像分割机制将一个目标分成两个独立的镜像:一个能够执行镜像升级;另一个镜像包含应用代码。通过将升级功能隔离到一个单独的镜像中,应用程序在支持无线升级的同时而无需将flash空间用于网络协议栈和管理代码。
2.5.1 概念
Mynewt支持3个镜像设置:
设置 | 描述 |
---|---|
Single | 一个大的镜像,不只是升级 |
Unified | 两个单独的镜像 |
Split | 内核在镜像槽0,应用在镜像槽1 |
每一个设置都有其平衡,独立设置将保证能够最大限度使用flash空间,但是却不允许升级。统一设置允许完整的备份,以便上传镜像有异常,在每个镜像中有大量的冗余,限制了应用程序可用的flash数量。通过分割设置则介于这两个选项之间。
在探索分割设置之前,对Mynewt的引导顺序有一个基础的了解将会有比较大的帮助。
- 启动顺序——Single
独立设置下,没有bootloader。从而,镜像从地址0开始。硬件直接从镜像代码开始。由于没有bootloader,不能交换镜像,故而此种模式下不能支持升级。
- 启动顺序——Unified
在统一设置下,bootloader放置到地址0。启动时,优先启动bootloader,bootloader将从两个镜像中决定启动哪个镜像。
- 启动顺序——Split
分割设置与以上两种设置不同,一个目标并不包含一个完整的镜像。目标被分为两个分离的镜像:一个加载程序,一个应用程序。
- Loader:Mynewt OS;网络协议栈用于连接,如BLE协议栈;任何需要用于升级镜像的组件。
- Application:Mynewt中不需要用于镜像升级的部分;应用特定代码。
loader镜像出于三个用途:
- 二阶bootloader,启动时跳转到应用程序镜像;
- 镜像升级服务器:用户可以升级一个新的加载器+应用,即使当前的应用程序镜像并没有运行;
- 功能容器:应用程序镜像可以直接访问loader镜像中的所有代码,保证整个程序功能完整。
从bootloader的角度看,loader镜像与普通镜像是相同的。Loader镜像的不同点在于,其启动顺序有改变:启动Mynewt OS之后,如果镜像槽1有应用将会跳转到应用程序镜像。
2.5.2 示例
本节将以dwm1001来进行说明,参考flash map(hw/bsp/dwm1001/bsp.yml),合计512KB:
名称 | 偏移 | 大小(kB) |
---|---|---|
Bootloader | 0x00000000 | 16 |
Reboot log | 0x00004000 | 16 |
Image slot 0 | 0x00008000 | 232 |
Image slot 1 | 0x00042000 | 232 |
Image SCRATCH | 0x0007C000 | 4 |
Flash文件系统NFFS | 0x0007D000 | 12 |
当目标构建后,可以通过newt size target-name命令来查看代码大小。
$newt size dwm1001_boot
Size of Application Image: app
FLASH RAM
25 216 *fill*
42 0 apps_boot.a
2385 4004 boot_bootutil.a
1220 0 crypto_mbedtls.a
558 508 hw_bsp_dwm1001.a
44 0 hw_cmsis-core.a
272 0 hw_drivers_uart_uart_hal.a
342 0 hw_hal.a
3210 128 hw_mcu_nordic_nrf52xxx.a
498 120 kernel_os.a
434 32 libc_baselibc.a
472 128 sys_flash_map.a
316 12 sys_mfg.a
6 4 sys_sysinit.a
72 0 dwm1001_boot-sysinit-app.a
objsize
text data bss dec hex filename
9896 60 4660 14616 3918 /home/me/work/mynewt-dw1000-apps/bin/targets/dwm1001_boot/app/apps/boot/boot.elf
由于是一个简单应用,boot镜像大小约9KB,而一个镜像槽大小为232KB,还是有比较大的可用空间。
当然,这只是单纯的应用程序,对于镜像分割设置,需要在target中设置loader变量,应用使用apps/splitty,该应用为一个分割示例应用,可以与slinky结合构成一个应用。
newt target create split-dwm1001
newt target set split-dwm1001 \
loader=@apache-mynewt-core/apps/bleprph \
app=@apache-mynewt-core/apps/splitty \
bsp=@mynewt-dw1000-core/hw/bsp/dwm1001 \
build_profile=optimized \
syscfg=BLE_LL_CFG_FEAT_LE_ENCRYPTION=0:BLE_SM_LEGACY=0
构建此目标。(若直接使用官方的包,bsp.part2linkerscript: “hw/bsp/dwm1001/split-nrf52-thingy.ld” 需要更改为 split-nrf52.ld,否则将会出错,此问题已经提交若已经修正,则无需修改)
构建好之后,查看镜像的大小:
$newt size split-dwm1001
Size of Application Image: app
FLASH RAM
38 14778 *fill*
580 1456 apps_splitty.a
20 0 boot_split_app.a
172 432 hw_bsp_dwm1001.a
110 32 mgmt_newtmgr_transport_nmgr_shell.a
5168 786 sys_shell.a
98 0 split-dwm1001-sysinit-app.a
objsize
text data bss dec hex filename
6152 4 17080 23236 5ac4 /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/app/apps/splitty/splitty.elf
Size of Loader Image: loader
FLASH RAM
282 282 *fill*
72 0 split-dwm1001-sysinit-app.a
90 0 split-dwm1001-sysinit-loader.a
... ...
objsize
text data bss dec hex filename
126780 924 13884 141588 22914 /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/loader/apps/bleprph/bleprph.elf
可以看到生成的目标分为了两个部分,一个为应用镜像app,另一个为Loader镜像loader,整个镜像loader部分为127KB左右,而应用程序镜像仅6KB左右。
$newt create-image split-dwm1001 1.0.0
$newt load split-dwm1001 0
Loading app image into slot 1
Loading loader image into slot 0
创建镜像,并将分割后的镜像部署到DWM1001上,可以看到加载过程:将app镜像加载到slot 2,loader镜像加载到slot 1.
2.5.3 镜像升级
Unified模式镜像升级
- 上传新镜像到slot 1,(newtmgr image upload filename)
- 告诉设备在下次启动时测试镜像(newtmgr image test image-hash)
- 重启设备(newtmgr reset)
- 镜像可用,将镜像确认替换(newtmgr image confirm)
分割模式镜像升级
使用分割模式后,镜像的升级流程将会有些复杂。因为需要更新两个镜像(app和loader),而不能仅仅更新一个,流程如下:
禁止split功能,以便slot 1中的应用镜像失效(newtmgr image test current-loader-hash)
重启设备(newtmgr reset)
说明:由于loader中包含了newtmgr相关功能,依然正常启动
确认以上修改(newtmgr image confirm)
上传新的loader到slot 1(newtmgr image upload new-loader)
告诉设备使用新的loader启动(newtmgr image test new-loader-hash)
重启设备(newtmgr reset)
说明:此时重启后,并没有应用程序,若新loader有问题,也是可以以之前的loader启动,依然可以更新、上传代码,不会”成砖“
确认以上loader(newtmgr image confirm)
说明:确认之后,新loader在slot 0中,旧的loader在slot 1
上传新的应用到slot 1(newtmgr image upload new-app)
下次启动时测试新的应用(newtmgr image test new-application-hash)
重启设备(newtmgr reset)
确认应用程序更改(newtmgr image confirm)
当流程完成后,可以使用image list进行确认镜像状态。
理论上,若在项目构建中loader部分未更新,是否可只用更新应用程序部分代码?即执行8/9/10/11,待验证。
2.5.4 补充
Loader项目
官方提供了一下两个应用程序作为loader,可以参考这个两个应用编写自己的loader应用。
- @apache-mynewt-core/apps/slinky
- @apache-mynewt-core/apps/bleprph
分割应用项目
以下两个应用程序被用作分割应用,若需要构建自己的分割应用程序,可以此为参考。需要注意,在slinky中即可以作为loader镜像,也可以作为应用镜像。
- @apache-mynewt-core/apps/slinky
- @apache-mynewt-core/apps/splitty
镜像分割原理
首先,newt将应用与loader分开构建,以确保它们是正确的,从而生成elf文件(两个文件),通过该文件newt可以知道每个部分使用的符号。
然后,newt以两种方式收集应用和loader所使用的symbols。它从.elf文件中收集一组符号,同时也从应用的.a文件中收集所有可能的symbols。
然后,收集Newt构建时两个应用程序公用的一整套包,确保在这些包中使用的符号都是匹配的。(注意:由于一些特性和#ifdefs,两个包有可能具有不相同的symbols,这种情况下,newt将会产生错误,建立分割镜像失败,不会建立)
通过这些包中共享的符号列表,newt创建了两个应用程序。接下来newt重新链接loader,以确保包含了loader应用中用到的所有符号(强制链接器从.elf文件中包含)。
newt建立了一个特殊loader.elf副本,仅仅包含了使用的符号。
最终,newt将这些应用程序连接起来,在镜像连接过程中,使用特殊loader.elf中的符号替换通用.a文件中的符号。
$newt create-image split-dwm1001 1.0.0
Compiling bin/targets/split-dwm1001/generated/src/split-dwm1001-sysflash.c
Compiling bin/targets/split-dwm1001/generated/src/split-dwm1001-sysinit-app.c
Compiling bin/targets/split-dwm1001/generated/src/split-dwm1001-sysinit-loader.c
Archiving split-dwm1001-sysinit-app.a #应用静态链接库文件
Linking /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/app/apps/splitty/splitty_tmp.elf #临时.elf副本
Compiling bin/targets/split-dwm1001/generated/src/split-dwm1001-sysinit-app.c
Compiling bin/targets/split-dwm1001/generated/src/split-dwm1001-sysinit-loader.c
Compiling bin/targets/split-dwm1001/generated/src/split-dwm1001-sysflash.c
Archiving split-dwm1001-sysinit-loader.a #loader静态链接库文件
Linking /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/loader/apps/bleprph/bleprph_tmp.elf #临时.elf副本
Linking /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/loader/apps/bleprph/bleprph.elf
#生成最终的img文件
Generating ROM elf
Linking /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/app/apps/splitty/splitty.elf
App image succesfully generated: /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/loader/apps/bleprph/bleprph.img
App image succesfully generated: /home/ronios/work/mynewt-dw1000-apps/bin/targets/split-dwm1001/app/apps/splitty/splitty.img