多核启动
本节使用 i.MX6Q 的 SylixOS BSP 为例来介绍 SylixOS 多核启动流程。其主要流程如下图 SylixOS 多核启动流程。
多核启动相对于单核启动而言,主核的启动流程与单核启动大体相似。当执行完 startup.S 汇编后,调用的第一个 C 程序函数是 bspinit.c 文件里的 halPrimaryCpuMain 函数,这个函数与单核中的 bspinit 函数内容是一样的,最后都会调用 API_KernelPrimaryStart 函数。这些就不再赘述。
不同的情况在于,多核的情况下,主核在调用 API_KernelPrimaryStart 函数完成了自己资源初始化之后,会多做一步操作,即调用 _KernelBootSecondary 通知从核进行初始化操作。其函数原型如下:
#include <SylixOS.h>
static VOID _KernelBootSecondary (VOID);
这个函数没有返回值和参数,其仅仅会修改一个全局变量 _K_ulSecondaryHold 的值,标记其他核可以进行启动,然后通过 LW_SPINLOCK_NOTIFY 宏去通知其他核。
完成上述操作之后,主核就与单核启动一样,最后调用 _KernelPrimaryCoreStartup ,进入多任务状态。
与主核相同,从核启动也是从 startup.S 汇编代码开始。reset 检测到 CPU 不是主核时,会跳到从核 CPU 复位入口执行,同样会有关 cache,MMU 和分支预测等内容,但因为主核已经进行了堆栈初始化操作,因此从核的复位函数不会有堆栈的操作。从核复位完成之后会进入到 C 代码中运行。
与主核不同,从核运行的第一个 C 函数是 bspinit.c 里的 halSecondaryCpuMain 函数。halSecondaryCpuMain 函数一般只需调用从核系统内核入口函数 API_KernelSecondaryStart 即可。此函数原型如下:
#include <SylixOS.h>
VOID API_KernelSecondaryStart(PKERNEL_START_ROUTINE pStartHook);
函数原型分析:
- 此函数没有返回值。
- 参数 pStartHook 是从核的用户回调函数,用于初始化本CPU基本资源, 例如 MMU,CACHE,FPU 等。
API_KernelSecondaryStart 会循环等待上述的主核通知,即循环检测 _K_ulSecondaryHold 的值。当主核修改了这个值之后,从核才会继续执行操作。后续的操作就与主核类似,包括从核底层的初始化,用户回调,以及从核启动多任务。
imx6Q 的 BSP 里,从核的回调函数是 halSecondaryCpuInit,如下:
static VOID halSecondaryCpuInit (VOID)
{
/*
* 初始化 FPU 系统。
*/
API_KernelFpuSecondaryInit(ARM_MACHINE_A9, ARM_FPU_VFPv3);
API_VmmLibSecondaryInit(ARM_MACHINE_A9); /* 初始化 VMM 系统 */
API_CacheLibSecondaryInit(ARM_MACHINE_A9); /* 初始化 CACHE 系统 */
armGicCpuInit(LW_FALSE, 255); /* 初始化当前 CPU 使用 GIC 接口 */
halSmpCoreInit(); /* 初始化 SMP 核心 */
}
其主要是按顺序初始化目标系统从核的 VFP、MMU、CACHE、中断、SMP 核心内容。
当用户回调执行完成以后,从核会调用 _KernelSecondaryCoreStartup 函数进入多任务状态。这个函数的原型如下:
#include <SylixOS.h>
static VOID _KernelSecondaryCoreStartup (PLW_CLASS_CPU pcpuCur);
函数 _KernelSecondaryCoreStartup 原型分析:
- 此函数没有返回值。
- 参数 pcpuCur 是当前CPU的信息。
这个函数和主核调用的 KernelPrimaryCoreStartup 函数相比,_KernelSecondaryCoreStartup 内会有等待主核运行的操作,从核进入多任务模式前必须得到主核通知。在 SylixOS 里,不管是主核还是从核,当他们都进入多任务状态的时候,这两者属于对等关系,不分主从。此时,整个 SylixOS 多核系统启动完成。