OpenHarmony 轻量设备 Hi3861 芯片开发板启动流程分析
引言
OpenHarmony 作为一款万物互联的操作系统,覆盖了从嵌入式实时物联网操作系统到移动操作系统的全覆盖,其中内核包括 LiteOS-M,LiteOS-A 和 Linux。LiteOS-M 内核是面向 IoT 领域构建的轻量级物联网操作系统内核,主要面向没有 MMU 的处理器,架构如图 1-1 所示。
图 1-1 LiteOS-M 架构图
Hi3861 是一款高度集成的 2.4GHz SoC WiFi 芯片,采用高性能 32bit 微处理器,最大工作频率 160MHz,内嵌 SRAM 352KB、ROM 288KB、Flash 2MB。目前市面上的采用 LiteOS-M 的 OpenHarmony 开发板厂商有深开鸿、润和软件、小熊派,因为海思的 SDK 是以库文件的形式提供的,所以不同的 Hi3861 芯片开发板启动流程是一样的。
Hi3861 Boot 介绍
Boot 是操作系统启动之前的软件,通用叫法是 bootloader,Hi3861 的 boot 分为 4 部分:RomBoot、FlashBoot、LoaderBoot、 CommonBoot,如图 2-1 所示。
图 2-1 Hi3861 Boot 启动流程
● RomBoot 功能包括:加载 LoaderBoot 到 RAM,进一步利用 LoaderBoot 下载镜像到 Flash、烧写 EFUSE, 校验并引导 FlashBoot。FlashBoot 分为 AB 面,A 面校验成功直接启动,校验失败会去校验 B 面,B 面校验成功会修复 A 面再引导启动,否则复位重启。
● FlashBoot 功能包括:升级固件,校验并引导固件。
● LoaderBoot 功能包括:下载镜像到 Flash, 烧写 EFUSE(例如:安全启动/Flash 加密相关密钥等)。
● CommonBoot 为 Flashboot 与 LoaderBoot 共用的功能模块。
相关文件介绍
Hi3861 的 LiteOS-M 代码是 SDK 中以库文件的形式提供的,虽然我们无法看到源代码,但这不代表我们分析不了启动流程,我们可以从分析 map 文件和 asm 这两个文件入手。这两个文件都是编译链接工具生成的,其中 asm 文件是汇编程序源文件,可以查看函数之间的调用关系,map 文件里包括全局符号、函数地址及占用的空间和位置。map 和 asm 文件主要作用是当开发板崩溃时用于分析其崩溃的原因,我们分析函数跳转关系时并不需要知道太多汇编,只需要知道基本的跳转语句和赋值语句即可,这两个文件位于 out 目录下和操作系统固件平级的目录,如图 3-1。
图 3-1 Hi3861 asm 和 map 文件位置图
一个编译完成的固件通常有以下几部分:
1) RO 段包括只读代码段(code 段/.text 段)和常量段(RO Data 段/.constdata 段)。
2) RW 段(.data 段)指已被初始化成非 0 值的变量段。
3) ZI 段(.bss 段)指未被初始化或初始化为 0 的变量段。
我们源代码的函数和字符串常量都位于 text 段。
LiteOS-M 启动流程介绍
1) 嵌入式处理器和操作系统都具有类似的结构启动流程也大体相似,从芯片上电开始 Boot 把控制权交给操作系统,Hi3861 从 Boot 跳转到操作系统代码如下:
这部分是将该地址当函数作为跳转,因为 FlashBoot 和 kernel,是两套代码程序,他们之间没有依赖引用关系,但是他们在一个地址空间,所以直接地址跳转,这也是从 Boot 到 kernel 通用的跳转方式。
2) 芯片启动是从中断向量表的复位中断处理程序开始,接着把数据从 Flash 复制到 RAM、清空 bss 数据段、初始化时钟、跳转到 main 函数。我们通过查看 asm 文件的 main 函数,可以看出其中调用的函数如图 4-1 所示,从图 4-1 我们可得知调用的函数包括设置串口、校验版本号、配置板子、Kernel 初始化、应用初始化和操作系统的调度运转,其中 main 函数位于 liblitekernel_flash.a(main.o)文件中。
图 4-1 main 函数调用关系
LOS_KernelInit 是负责初始化内核数据结构的,如图 4-2 所示,主要函数有 OsMemSystemInit(内存初始化)、OsHwiInit(中断初始化)、OsTaskInit(任务初始化) ,这些过程主要目的是把内核相关的变量初始化,准备好全局信息,方便 API 函数去调用,API 函数调用必须在这些初始化完成后才可以。
3) 从 AppInit 开始脱离了 sdk,可以看到源代码了,AppInit 函数位于 libwifiiot_app.a(app_main.o)中,部分截图如图 4-3,源代码为 app_main.c,其中调用的函数包括获取 sdk 版本号,外设初始化,ipc 初始化,flash 分区,WiFi 初始化,tcp/ip 初始化,然后跳转到了 OpenHarmony 特有的函数 OHOS_Main。
OHOS_Main 位于 libwifiiot_app.a(ohos_main.o)中,源代码为 ohos_main.c,主要完成 OpenHarmony 系统相关和用户应用相关的调用,里边主要函数是 OHOS_SystemInit,如图 4-4,在其中调用了用户自己写的应用任务相关代码,如图 4-5,从而实现了在 LOS_start 之前把任务列表填好,这样才能保证用户任务或定时等功能参与了系统调度。
图 4-2 LOS_KernelInit 函数调用关系
图 4-3 app_main 函数调用关系
图 4-4 OHOS_Main 函数调用关系
图 4-5 OHOS_SystemInit 函数调用关系
用户应用的启动原理
1) 在图 4-5 中出现的函数 MODULE_INIT(run),就是调用最终调用用户程序的代码。
这是个宏定义,展开的调用关系 :\base\startup\bootstrap_lite\services\source\core_main.h 定义,从 MODULE_CALL、MODULE_BEGIN 、MODULE_END,最终调用的地址是__zinitcall_##name##_start,MODULE_INIT(run)调用的函数地址是__zinitcall_run_start。
通过查看链接文件得出__zinitcall_run_start 包含.zinitcall.run0.init),如图 5-1 所示。
图 5-1 __zinitcall_run_start 链接关系
查看 map 文件发现我们自己的应用程序文件就在.zinitcall.run2.init 中,如图 5-2 所示。
图 5-2 led_exapmle 文件在 map 中的位置
2) 从运行角度看启动中调用到了应用程序 led_exapmle,所谓位置为.zinitcall.run2.init,但我们在应用程序中的关联函数是 SYS_RUN(LedExampleEntry),SYS_RUN 的展开关系如图 5-3 所示,最终即是 zinitcall.run2.init,和程序运行时候的调用匹配在一起了。应用程序的调用关系就是编译链接阶段生成指定的段,初始化时调用指定段,这样实现了 LiteOS-M 的操作系统代码与应用程序代码的解耦。
图 5-3 SYS_RUN 的展开关系
总结
本文向大家讲述了在没有部分源代码的情况下,如何通过对 map 文件和 asm 文件的分析从而得出 Hi3861 芯片开发板 LiteOS-M 的启动流程。总体过程就是最小硬件系统的配置完成后,LOS_KernelInit 负责初始化系统到一个合适的状态,AppInit 调用 OpenHarmony 和应用相关代码,最后 LOS_Start 负责把操作系统运转起来。
评论