[openharmony]liteos-a系统初始化框架分析
系统启动流程
对于Hi3516dv300来说,系统上电之后是先启动一个uboot.bin;uboot.bin做一些初始化再转到内核调用;内核的入口为reset_vector,在连接文件liteos_llvm.ld中有描述,后面的启动如下图
image.png
image.png
注意:openharmony3.0lts的los_config.c放在了kernel/platform/目录,而不是kernel/common了,还有其他一些代码也有改动,需要注意
OsInitCall
通过看OsMain接口源码,很多初始化并不是直接调用内核各模块的初始化接口,而是调用一个统一的接口:OsInitCall如下图(如果分析过linux系统的初始化的话,这个跟linux的驱动初始化很像,参考连接
OsInitCall源码如下:
VOID OsInitCall(const UINT32 level)
{
if (level >= LOS_INIT_LEVEL_FINISH) {
return;
}
InitLevelCall("Kernel", level, g_kernInitLevelList);
}
看源码是根据传入的初始化level值调用InitLevelCall接口初始化具体的level下的模块,代码如下(这里把不相关的代码删除了):
/**
* It is recommended that each startup framework encapsulate a layer of its own calling interface.
*/
STATIC VOID InitLevelCall(const CHAR *name, const UINT32 level, struct ModuleInitInfo *initLevelList[])
{
struct ModuleInitInfo *module = NULL;
if (ArchCurrCpuid() == 0) {
g_initCurrentLevel = level;
g_initCurrentModule = initLevelList[level];
} else {
while (g_initCurrentLevel < level) {
}
}
do {
LOS_SpinLock(&g_initLock);
if (g_initCurrentModule >= initLevelList[level + 1]) {
LOS_SpinUnlock(&g_initLock);
break;
}
module = (struct ModuleInitInfo *)g_initCurrentModule;
g_initCurrentModule++;
LOS_SpinUnlock(&g_initLock);
if (module->hook != NULL) {
module->hook();
}
} while (1);
if (level >= LOS_INIT_LEVEL_KMOD_TASK) {
LOS_AtomicInc(&g_initCount);
while ((LOS_AtomicRead(&g_initCount) % LOSCFG_KERNEL_CORE_NUM) != 0) {
}
}
}
从代码实现分析,第三个参数是一个列表,代表的是所有需要初始化的内核模块信息,而初始化入口在module->hook中;对于同一level的模块可能有多个,所以这里使用了一个do/while循环。
从传入的参数看第三个参数是一个全局变量g_kernInitLevelList,这个变量是通过一个宏初始化的,如下
/**
* Register kernel init level labels.
*/
OS_INIT_LEVEL_REG(kernel, 10, g_kernInitLevelList);
这个宏的具体展开在下一节再详细说明,这里再说一下初始化level这个属性,从代码中看OpenHarmony的liteos-a系统中对系统模块初始化的级别定义了10个(0~9),如下
/**
* Kernel Module Init Level
*/
#define LOS_INIT_LEVEL_EARLIEST 0
#define LOS_INIT_LEVEL_ARCH_EARLY 1
#define LOS_INIT_LEVEL_PLATFORM_EARLY 2
#define LOS_INIT_LEVEL_KMOD_PREVM 3
#define LOS_INIT_LEVEL_VM_COMPLETE 4
#define LOS_INIT_LEVEL_ARCH 5
#define LOS_INIT_LEVEL_PLATFORM 6
#define LOS_INIT_LEVEL_KMOD_BASIC 7
#define LOS_INIT_LEVEL_KMOD_EXTENDED 8
#define LOS_INIT_LEVEL_KMOD_TASK 9
#define LOS_INIT_LEVEL_FINISH 10
从OsMain函数中调用情况看,就是按这个level顺序来调用OsInitCall初始化各模块的
内核模块初始化接口列表
g_kernInitLevelList的初始化是通过一个宏来处理的,如下
OS_INIT_LEVEL_REG(kernel, 10, g_kernInitLevelList);
经过展开之后,代码如下:
extern struct ModuleInitInfo __kernel_init_level_0;
extern struct ModuleInitInfo __kernel_init_level_1;
extern struct ModuleInitInfo __kernel_init_level_2;
extern struct ModuleInitInfo __kernel_init_level_3;
extern struct ModuleInitInfo __kernel_init_level_4;
extern struct ModuleInitInfo __kernel_init_level_5;
extern struct ModuleInitInfo __kernel_init_level_6;
extern struct ModuleInitInfo __kernel_init_level_7;
extern struct ModuleInitInfo __kernel_init_level_8;
extern struct ModuleInitInfo __kernel_init_level_9;
extern struct ModuleInitInfo __kernel_init_level_10;
STATIC struct ModuleInitInfo *g_kernInitLevelList[] = {
&__kernel_init_level_0,
&__kernel_init_level_1,
&__kernel_init_level_2,
&__kernel_init_level_3,
&__kernel_init_level_4,
&__kernel_init_level_5,
&__kernel_init_level_6,
&__kernel_init_level_7,
&__kernel_init_level_8,
&__kernel_init_level_9,
&__kernel_init_level_10,
}
其中__kernel_init_level_x的定义在连接脚本liteos_llvm.ld中,是在连接的时候才确定的值。
.rodata : ALIGN(0x1000) {
__rodata_start = .;
__kernel_init_level_0 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.0.*)));
__kernel_init_level_1 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.1.*)));
__kernel_init_level_2 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.2.*)));
__kernel_init_level_3 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.3.*)));
__kernel_init_level_4 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.4.*)));
__kernel_init_level_5 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.5.*)));
__kernel_init_level_6 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.6.*)));
__kernel_init_level_7 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.7.*)));
__kernel_init_level_8 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.8.*)));
__kernel_init_level_9 = ABSOLUTE(.);
KEEP(*( SORT (.rodata.init.kernel.9.*)));
__kernel_init_level_10 = ABSOLUTE(.);
*(.rodata .rodata.* .gnu.linkonce.r.*)
__exc_table_start = .;
KEEP(*(__exc_table))
__exc_table_end = .;
} > ram
编译之后查看OHOS_Image.map文件,可以看到这些定义之间存放了对应level下的内核模块入口,level0下有三个内核模块、level1~3无模块、level4有一个模块等等,如下:
4057a000 4057a000 0 1 __rodata_start = .
4057a000 4057a000 0 1 __kernel_init_level_0 = ABSOLUTE ( . )
4057a000 4057a000 4 4 lto.tmp:(.rodata.init.kernel.0.HisiSmpInit)
4057a000 4057a000 4 1 ModuleInitInfo_HisiSmpInit
4057a004 4057a004 4 4 lto.tmp:(.rodata.init.kernel.0.OsDmesgInit)
4057a004 4057a004 4 1 ModuleInitInfo_OsDmesgInit
4057a008 4057a008 4 4 lto.tmp:(.rodata.init.kernel.0.OsLkLoggerInit)
4057a008 4057a008 4 1 ModuleInitInfo_OsLkLoggerInit
4057a00c 4057a00c 0 1 __kernel_init_level_1 = ABSOLUTE ( . )
4057a00c 4057a00c 0 1 __kernel_init_level_2 = ABSOLUTE ( . )
4057a00c 4057a00c 0 1 __kernel_init_level_3 = ABSOLUTE ( . )
4057a00c 4057a00c 0 1 __kernel_init_level_4 = ABSOLUTE ( . )
4057a00c 4057a00c 4 4 lto.tmp:(.rodata.init.kernel.4.ShmInit)
4057a00c 4057a00c 4 1 ModuleInitInfo_ShmInit
4057a010 4057a010 0 1 __kernel_init_level_5 = ABSOLUTE ( . )
4057a010 4057a010 0 1 __kernel_init_level_6 = ABSOLUTE ( . )
4057a010 4057a010 4 4 lto.tmp:(.rodata.init.kernel.6.OsBsdInit)
4057a010 4057a010 4 1 ModuleInitInfo_OsBsdInit
4057a014 4057a014 0 1 __kernel_init_level_7 = ABSOLUTE ( . )
4057a014 4057a014 4 4 lto.tmp:(.rodata.init.kernel.7.los_vfs_init)
4057a014 4057a014 4 1 ModuleInitInfo_los_vfs_init
4057a018 4057a018 0 1 __kernel_init_level_8 = ABSOLUTE ( . )
4057a018 4057a018 4 4 lto.tmp:(.rodata.init.kernel.8.HieventInit)
4057a018 4057a018 4 1 ModuleInitInfo_HieventInit
4057a01c 4057a01c 4 4 lto.tmp:(.rodata.init.kernel.8.OsCpupInit)
4057a01c 4057a01c 4 1 ModuleInitInfo_OsCpupInit
4057a020 4057a020 4 4 lto.tmp:(.rodata.init.kernel.8.OsFutexInit)
4057a020 4057a020 4 1 ModuleInitInfo_OsFutexInit
4057a024 4057a024 4 4 lto.tmp:(.rodata.init.kernel.8.OsHiDumperDriverInit)
4057a024 4057a024 4 1 ModuleInitInfo_OsHiDumperDriverInit
4057a028 4057a028 4 4 lto.tmp:(.rodata.init.kernel.8.OsHiLogDriverInit)
4057a028 4057a028 4 1 ModuleInitInfo_OsHiLogDriverInit
4057a02c 4057a02c 4 4 lto.tmp:(.rodata.init.kernel.8.OsLiteIpcInit)
4057a02c 4057a02c 4 1 ModuleInitInfo_OsLiteIpcInit
4057a030 4057a030 4 4 lto.tmp:(.rodata.init.kernel.8.OsPmInit)
4057a030 4057a030 4 1 ModuleInitInfo_OsPmInit
4057a034 4057a034 4 4 lto.tmp:(.rodata.init.kernel.8.OsSysWorkQueueInit)
4057a034 4057a034 4 1 ModuleInitInfo_OsSysWorkQueueInit
4057a038 4057a038 4 4 lto.tmp:(.rodata.init.kernel.8.OsSyscallHandleInit)
4057a038 4057a038 4 1 ModuleInitInfo_OsSyscallHandleInit
4057a03c 4057a03c 4 4 lto.tmp:(.rodata.init.kernel.8.OsVdsoInit)
4057a03c 4057a03c 4 1 ModuleInitInfo_OsVdsoInit
4057a040 4057a040 4 4 lto.tmp:(.rodata.init.kernel.8.ProcFsInit)
4057a040 4057a040 4 1 ModuleInitInfo_ProcFsInit
4057a044 4057a044 4 4 lto.tmp:(.rodata.init.kernel.8.pipe_init)
4057a044 4057a044 4 1 ModuleInitInfo_pipe_init
4057a048 4057a048 0 1 __kernel_init_level_9 = ABSOLUTE ( . )
4057a048 4057a048 4 4 lto.tmp:(.rodata.init.kernel.9.OomTaskInit)
4057a048 4057a048 4 1 ModuleInitInfo_OomTaskInit
4057a04c 4057a04c 4 4 lto.tmp:(.rodata.init.kernel.9.OsCpupGuardCreator)
4057a04c 4057a04c 4 1 ModuleInitInfo_OsCpupGuardCreator
4057a050 4057a050 4 4 lto.tmp:(.rodata.init.kernel.9.OsMpInit)
4057a050 4057a050 4 1 ModuleInitInfo_OsMpInit
4057a054 4057a054 4 4 lto.tmp:(.rodata.init.kernel.9.OsResourceFreeTaskCreate)
4057a054 4057a054 4 1 ModuleInitInfo_OsResourceFreeTaskCreate
4057a058 4057a058 4 4 lto.tmp:(.rodata.init.kernel.9.OsSystemInit)
4057a058 4057a058 4 1 ModuleInitInfo_OsSystemInit
4057a05c 4057a05c 0 1 __kernel_init_level_10 = ABSOLUTE ( . )
4057a05c 4057a05c d3d9d 1 <internal>:(.rodata)
这些内核模块是怎么连接到这些对应地址的呢?下一节内容再详细讨论
内核模块注册到系统初始化框架
类似linux内核模块的注册,在liteos-a系统中使用了一个宏LOS_MODULE_INIT来注册,系统中调用如下:
image.png
宏定义源码在los_init.h中,如下:
/**
* @ingroup los_init
* @brief Register a startup module to the startup process.
*
* @par Description:
* This API is used to register a startup module to the startup process.
*
* @attention
* <ul>
* <li>Register a new module in the boot process of the kernel as part of the kernel capability component.</li>
* <li>In the startup framework, within the same _level, the startup sequence is sorted by
* the registered function name </li>
* <li>If the registration is not accompanied by the startup process after calling this interface,
* try to add -u_hook to liteos_tables_ldflags.mk </li>
* </ul>
*
* @param _hook [IN] Type #UINT32 (*)(VOID) Register function.
* @param _level [IN] Type #UINT32 Init level in the kernel.
*
* @retval None
* @par Dependency:
* <ul><li>los_init.h: the header file that contains the API declaration.</li></ul>
* @see
*/
#define LOS_MODULE_INIT(_hook, _level) OS_INIT_HOOK_REG(kernel, _hook, _level)
以OsSystemInit为例进行展开
定义如下:
LOS_MODULE_INIT(OsSystemInit, LOS_INIT_LEVEL_KMOD_TASK);
展开后如下
STATIC const struct ModuleInitInfo ModuleInitInfo_OsSystemInit __attribute__((section(".rodata.init.kernel.9.OsSystemInit))) __attribute__((aligned(alignof(struct ModuleInitInfo)))) =
{
.hook = (UINT32 (*)(VOID))&OsSystemInit,
#ifdef LOS_INIT_DEBUG
.name = "OsSystemInit",
#else
#endif
}
这里最主要的就是定义了结构体中的.hook变量,指向模块的初始化入口;然后就可以在启动的时候InitLevelCall中调用了。这样对应内核模块就初始化了
题外话
Liteos-m属于嵌入式RTOS,应用于soc的rom/ram比较小的场景,基本上不涉及内核态、用户态的分离;
liteos-a其实就是基于liteos-m增加了MMU/MPU等特性,可以实现用户态、内核态分离;并且很多用法参考了linux系统(像这里的内核模块初始化框架)
可以参考《深度探索嵌入式操作系统》这本书来学习
还有一个鸿蒙研究站的网站对liteos-a内核代码做了注释,可以学习