在bspInit.c中添加多核启动功能
在本章节中,我们将添加从核启动需要的初始化代码,同样的在主核的处理过程中也需要添加代码来配合多核功能。
1. halModeInit
这个接口是主核和从核进入C函数接口第一个调用的函数,之前单核启动时这个接口中主要就是设置了中断向量表的基址,这里我们还要加上使能ACR.SMP这个寄存器位,这在第十九章节我们介绍过。在SylixOS下这是通过armAuxControlFeatureEnable 接口来实现的:
static VOID halModeInit (VOID) { /* * TODO: 加入你的处理代码, 但建议不作处理 */ armHighVectorDisable(); armVectorBaseAddrSet(BSP_CFG_RAM_BASE); armAuxControlFeatureEnable(AUX_CTRL_A7_SMP); /* 使能多核 Cache */ }
2. halEnableCpuIpiInt
通过上一章节我们知道cpu核是通过核间中断来进行通信的,所以在每个核的启动过程中需要设置核间中断,在SylixOS中安装核间中断向量是通过API_InterVectorIpi 这个接口来实现的:
static VOID halEnableCpuIpiInt (VOID) { ULONG ulCPUId = LW_CPU_GET_CUR_ID(); API_InterVectorIpi(ulCPUId, ulCPUId); /* 安装 IPI 向量 */ armGicIntVecterEnable(ulCPUId, LW_FALSE, 0, 1 << ulCPUId); /* 使能 IPI 中断 */ }
安装完核间中断向量后再通过中断控制器提供的接口来使能各个核对应的核间中断号,如上面代码所示。在主核的启动过程中,我们需要在usrStartup 函数中调用halEnableCpuIpiInt 这个接口:
static VOID usrStartup (VOID) { LW_CLASS_THREADATTR threadattr; /* * 注意, 不要修改该初始化顺序 (必须先初始化 vmm 才能正确的初始化 cache, * 网络需要其他资源必须最后初始化) */ halIdleInit(); #if LW_CFG_CPU_FPU_EN > 0 halFpuInit(); #endif /* LW_CFG_CPU_FPU_EN > 0 */ #if LW_CFG_RTC_EN > 0 halTimeInit(); #endif /* LW_CFG_RTC_EN > 0 */ #if LW_CFG_VMM_EN > 0 halVmmInit(); #endif /* LW_CFG_VMM_EN > 0 */ #if LW_CFG_CACHE_EN > 0 halCacheInit(); #endif /* LW_CFG_CACHE_EN > 0 */ halEnableCpuIpiInt(); API_ThreadAttrBuild(&threadattr, __LW_THREAD_BOOT_STK_SIZE, LW_PRIO_CRITICAL, LW_OPTION_THREAD_STK_CHK, LW_NULL); API_ThreadCreate("t_boot", (PTHREAD_START_ROUTINE)halBootThread, &threadattr, LW_NULL); /* Create boot thread */ }
3. API_CpuUp
主核除了上述的两个地方需要修改之外,还需要在halBootThread 这个接口中添加启动多核的逻辑代码,这段代码是加在创建t_main 线程之前的,因为当主核执行到创建t_main这里已经说明系统内核基本初始化完成了,可以开始唤醒从核启动了。
主核唤醒从核是通过循环调用API_CpuUp 这个接口来实现的,这个API会最终调用我们之前在bspLib.c中实现的bspCpuUp 这个接口:
ULONG ulCPUId; for (ulCPUId = 1; ulCPUId < LW_NCPUS; ulCPUId++) { /* 启动其它 CPU */ API_CpuUp(ulCPUId); } bspCpuUpSync();
当上面代码中的for循环退出时表示所有核已经启动完成,这是调用bspCpuUpSync来等待其他核进行DCache同步。LW_NCPUS这个值代表系统中cpu的个数,这个数值是通过系统启动参数ncpus 来控制的,所以我们还需要将这个参数修改为全志R16上cpu核的实际个数,也就是4:
API_KernelStartParam("ncpus=4 kdlog=no kderror=yes kfpu=no heapchk=yes " "sldepcache=yes hz=1000 hhz=1000 " "rfsmap=/:/dev/ram");
到这里为止,主核上需要添加修改的功能都已经完成了,接下来需要实现从核的启动初始化接口。
4. halSecondaryCpuMain
之前在startup.S中我们将从核的C入口跳转到了halSecondaryCpuMain 这个接口,所以我们需要实现这个接口:
INT halSecondaryCpuMain (VOID) { halModeInit(); API_KernelSecondaryStart(halSecondaryCpuInit); /* Secondary CPU 启动内核 */ return (0); /* 不会执行到这里 */ }
首先和主核一样调用halModeInit 来设置中断向量表基址和使能Cache共享功能,我们知道主核在启动的过程中是调用API_KernelStart 接口来初始化内核组件,然后调用usrStartup 回调函数来初始化MMU和Cache等功能的。类似的,从核启动时是通过API_KernelSecondaryStart 接口来初始化内核组件,然后通过halSecondaryCpuInit 回调函数来初始化从核的MMU和Cache等功能。
5. halSecondaryCpuInit
这个接口中做的事情类似于主核,主要就是FPU、MMU、Cache、中断控制器和中断向量的初始化:
static VOID halSecondaryCpuInit (VOID) { API_KernelFpuSecondaryInit(ARM_MACHINE_A7, ARM_FPU_VFPv4); /* 初始化 FPU 系统 */ API_VmmLibSecondaryInit(ARM_MACHINE_A7); /* 初始化 VMM 系统 */ API_CacheLibSecondaryInit(ARM_MACHINE_A7); /* 初始化 CACHE 系统 */ armGicCpuInit(LW_FALSE, 255); /* 初始化当前 CPU 使用 GIC 接口*/ API_VmmMmuEnable(); API_CacheEnable(INSTRUCTION_CACHE); /* 使能 I-Cache */ API_CacheEnable(DATA_CACHE); /* 使能 D-Cache */ halEnableCpuIpiInt(); bspCpuUpDone(); /* 通知其它 CPU 本核心启动完成 */ bspCpuUpSync(); /* 等待 CPU 启动同步 */ }
当上述工作都做完后表示从核已经启动完成了,然后通过bspCpuUpDone 接口通知主核当前核启动完成,最后调用bspCpuUpSync 接口来等待其他核来一起同步DCache。
注意:从核虽然也初始化了MMU,但是不会再次去初始化页表,而是使用主核启动时初始化的页表,也就是在SylixOS下,所有核共用一张页表。
到这里为止,多核启动需要的设置就全部完成了。
评论