1、概述
在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行的一段代码。
他的作用就是为操作系统内核准备好运行环境,比如初始化必要的设备硬件,建立内存映射图等。
bootloader不一定只有一个,有些操作系统有两级的bootloader,第一级bootloader和第二级bootloader分别完成不同的功能。
二级bootloader功能如下:
1)提供OTA升级运行环境
OTA的差分及压缩升级功能,需要一个与OS及APP隔离的运行环境,用于差分恢复运行区版本。该运行环境可以在一级bootloader中实现,但是一级bootloader通常是芯片或者模组厂商提供,而且很多芯片及模组厂商未提供bootloader的源码,无法保证可以在所有的一级bootlaoder中实现。
2)充分利用内存空间
在二级bootloader运行时,OS还未启动,二级bootloader可以使用全部的内存空间,用于差分解压算法等比较耗内存的操作。OS启动后,将整个系统内存重新初始化,也可以使用全部内存空间,二级bootloader使用了多少内存对OS无影响。这样可以达到内存的充分利用。
3)版本自动回滚
可以提供版本回滚功能,版本升级后,如果启动失败,可以通过二级bootloader自动回滚到升级前可正常运行的版本。
4)设备本地升级。
可以支持设备在不直接联网的场景下,通过本地升级功能,作为子设备升级。
2、二级bootloader总体框架
2.1、物理部署
如上图所示,二级bootloader介于一级bootloader与OS之间,与一级bootloader及OS共用FLASH空间,运行时独享系统RAM空间。
2.2、二级bootloader对外功能
1)UART驱动、FLASH驱动级Watchdog驱动,需要独立运行,不能对一级bootloader及OS产生符号依赖。可以对一级bootloader有功能依赖,不能对OS有功能依赖。
2)FLASH分区表中二级boot相关的配置需要与OS的FLASH分区表二级boot相关的配置完全一致。
3、二级bootloader详细设计方案
3.1、二级bootloader启动流程
3.1.1、二级bootloader加载启动
对于一级bootloader来说,二级bootloader与OS一样,是一个被引导启动的application,因此二级bootloader编译及启动方式与OS基本相同。
由于每个MCU,bootloader启动OS的方式不一样,因此二级bootloader被一级bootloader引导启动的方式也不尽相同。目前AliOS Things支持的MCU,通常有下面几种启动引导方式:
1)一级bootloader从固定地址跳转到二级bootloader的入口执行。目前,mk3060及developerkit等采用的这种方式。
2)一级bootloader从固定地址读取二级bootloader的入口函数地址,再跳转到二级bootloader入口执行。目前,mk3080采用的是这种启动方式。
3)有多个固定的FLASH启动地址,一级bootloader根据FLASH中的固定参数,决定从哪个FLASH地址启动。developerkit等STM32系列的MCU均是该方式启动。
注:mk3080的一级bootloader实际也有选择启动地址的过程,选择完后,再按照步骤2启动。
下面详述一下一级bootloader加载启动二级bootloader的流程。
二级bootloader加载启动流程
二级bootloader加载启动流程2
FLASH排列规则如下:
注:部分MCU是一级bootloader实现,一级bootloader会将二级bootloader的data段数据拷贝到RAM中。目前mk3060,是二级bootloader自己拷贝的data段数据;mk3080是一级bootloader加载二级bootloader前完成。
该流程,由一级bootloader完成,二级bootloader本身不需要操作,只需要按照MCU要求OS的编译链接方式生成bin文件即可。
3.1.2、二级bootloader内存初始化
将data段数据拷贝到RAM中对应地址
该操作,需要在FLASH中找到data段的起始地址和结束地址,以及在RAM中的起始地址和结束地址,然后将FLASH中的data数据拷贝到RAM中。
对于大部分CPU,data段的起始和结束地址,定义在链接脚本中,代码通过extern变量方式获取。少量MCU,如mk3080,需要按照特殊方式获取(3080的特殊方式,可以参加3080二级bootloader加载启动流程章节)。
bss段数据清0
该操作需要在RAM中找到bss段的起始地址和结束地址,然后将RAM中对应地址的数据清0。
所有的MCU,bss段的起始和结束地址,都可以直接定义在链接脚本中,代码通过extern变量方式获取。
动态内存管理
二级bootloader在运行OTA差分功能时,需要使用动态内存申请及释放功能。因此要求二级bootloader中支持动态内存管理。
二级bootloader中,使用的动态内存范围在二级bootloader的链接脚本中定义,从二级bootloader的bss段结束,到recovery栈空间开始的范围。内存管理使用与OS相同的动态内存管理算法。
3.2、二级bootloader引导启动OS流程
3.2.1、OS启动流程
二级bootloader引导启动OS的流程,基本与一级bootloader引导启动二级bootloader的流程相同。基本也分2种方式:固定地址跳转和从固定地址读取入口函数地址跳转,详见二级bootloader启动流程种的描述。
下面以mk3060和mk3080为列,详述一下二级bootloader加载启动OS的流程。
二级bootloader加载启动OS流程
注意:FLASH烧写,需要4K对齐,因此需要从0x1C000开始烧写。而OS的起始FLASH地址需要从0x1C01C开始,因此需要在OS的bin文件头部补充0x1C byte的填充数据。
二级bootloader加载启动OS流程
FLASH排列规则如下:
3.2.2、中断向量表
二级bootloader本身不处理中断,全程禁止中断运行(串口中断除外),但是在OS启动后,需要将中断送给OS。目前有几种实现方式,如下:
采用逐级中断向量表跳转方式,硬件产生中断后,先进入一级bootloader,一级bootloader转给二级bootloader,二级bootloader转给OS。
OS启动后,直接将中断注册给MCU SDK提供的中断机制。
硬件触发中断后,直接调用中断处理回调函数。
直接修改中断向量表入口地址
OS启动后,设置MCU硬件寄存器,将中断向量表地址设置为OS内部中断向量表地址。硬件产生中断后,直接进入OS的中断向量表。developerkit目前采用的这种方式。
3.3、二级bootloader运行流程
3.3.1、运行流程
说明:1)在recovery中,也会初始化串口。
2)一旦进入recovery流程,不能再返回启动OS的流程,需要重启后再进入。
3)一旦进入命令行模式,不能再返回启动OS的流程,需要重启后进入。
4)部分MCU,OS的启动入口地址是写死的,不需要动态获取。
3.4、命令行功能
命令行功能,运行开发者通过串口输入命令方式,查询版本信息、进行本地升级等操作。
3.4.1、进入流程
在判断是否需要进入命令行模式时,会持续读串口100ms,如果读到字符'w',就会进入命令行模式。
进入后,会打印二级bootloader版本号,以及命令提示信息。然后等待用户进一步输入串口命令。
3.4.2、支持命令
1)打印运行区版本号及备份区版本号。
2)Xmodem读写flash
3)Ymodem读写flash
4)USB升级
5)Canbus升级
6)版本回退到备区
7)reboot
说明,上面命令,支持配置项配置,通过配置项决定是否支持对应功能。
3.5、异常处理
二级bootloader中发生异常时,直接reset。
4、现有系统影响分析
4.1、FLASH空间
4.1.1、FLASH空间消耗增多
增加二级bootloader功能后,相对于没有实现二级bootloader以及差分升级的设备,会增加36k的FLASH消耗。对于已经实现差分升级的设备,会增加16k的FLASH消耗。
增加的FLASH消耗主要是由下面几方面导致:
1)增加了二级bootloader的处理逻辑,代码需要消耗FLASH空间。
2)增加了命令行处理功能,相关代码需要消耗FLASH空间。
3)增加双备份及回滚功能,相关代码需要消耗FLASH空间。
4)增加了任意时刻断点续传功能,增加了8K的备份数据区。
4.1.2、FLASH空间规划发生变化
增加二级boot及差分功能后,会导致FLASH空间规划发生变化。原因如下:
1)二级bootloader在FLASH中的位置,需要位于一级bootloader之后,OS之前。
2)增加了一个8K备份区,用于备份二级bootloader及差分参数,一级备份断点续传数据。
4.2、版本烧写方式
4.2.1、当前烧写方式
当前烧写方式归纳一下,存在下面2种烧写方式:
OS的bin文件单独烧写。
OS与bootloader的bin作为一个文件一起烧写。
4.2.2、增加二级bootloader后烧写方式
增加二级bootloader后,有下面3种方式:
可行方案:
1)对于一级bootloader可以烧写的设备
将二级bootloader与一级bootloader合并成一个bin文件,单独烧写。OS的bin文件单独烧写。
2)对于一级bootloader不可以烧写(或者不需要烧写)的设备
将二级bootloader单独烧写。OS的bin文件单独烧写。
4.3、启动时间
由于需要支持命令行模式,在每次启动时,会有100ms的等待按键输入的时间。导致每次启动会慢100ms。
5、移植说明
5.1、概述
二级bootloader实现,依赖UART驱动、FLASH驱动、WatchDog驱动,需要能够被一级bootloader启动,需要能够加载启动OS的bin。
5.2、启动引导
启动引导,需要实现下列功能:
1)能够被一级bootloader引导启动
2)能够找到OS的data段在flash中的位置,以及在RAM中的位置,启动OS前,将data段拷贝到RAM中。
2)能够找到OS的入口地址,启动OS。
3)能够将中断和异常转给OS。
5.3、UART驱动移植
UART驱动,需要提供下列功能:
5.4、FLASH驱动移植
FLASH驱动,需要提供下列功能:
5.5、WatchDog驱动移植
WatchDog驱动,需要提供下列内容:
5.6、内存管理
内存管理,主要需要提供动态管理内存的起始地址和结束地址,方式如下:
5.7、复位及延时
需提供如下接口: