实现bspLib.c中多核相关接口
在bspLib.c中有3个接口是需要我们来实现的,另外为了配合多核启动,我们还需要另外再自己实现几个接口以和bspInit.c来一起将多核启动起来。
1. bspMpInt
这个接口主要是向某一个核发送一个核间中断,核间中断IPI(Inter-Processor Interrupts)是多核之间进行通信的一种方法。发起方cpu核首先将消息写到一块事先约定好的共享内存中,然后发起核间中断,产生核间中断的cpu核在核间中断服务程序中读取该内存,以获得发起方通知的消息。
至于如何产生核间中断则需要看具体的中断控制器了,另外还有一个问题就是核间中断的中断号是多少?在arm架构中,每个核的核间中断号就是本核的ID,也就是cpu0的核间中断号是0,cpu1的是1,依此类推。
一般的多核控制器都有向多个核同时发送核间中断的功能,具体的参考控制器的手册和相应的Linux源码:
/********************************************************************************************************* ** 函数名称: bspMpInt ** 功能描述: 产生一个核间中断 ** 输 入 : ulCPUId 目标 CPU ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ VOID bspMpInt (ULONG ulCPUId) { /* * TODO: 加入你的处理代码 */ armGicSoftwareIntSend(ulCPUId, GIC_SW_INT_OPTION_USE_TARGET_LIST, 1 << ulCPUId); }
2. bspCpuUp
这个接口功能是启动一个从核,SylixOS下的多核启动流程大致如下图所示:
主核为了让从核启动后和自己看到的数据是一致的,在唤醒从核启动之前需要将DCache回写并失效,同时还要关闭本核中断,防止在中断处理中对内存访问从而改变Cache中的数据。接着就是具体的从核唤醒操作了,这是和具体的处理器相关的,从核启动后主核需要等待从核初始化完成后才能接着去唤醒下一个cpu核,在SylixOS中一般是通过轮询一个全局变量的值是否改变来实现的:
static volatile BOOL cpuStartDone[LW_CFG_MAX_PROCESSORS]; /********************************************************************************************************* ** 函数名称: bspCpuUp ** 功能描述: 启动一个 CPU ** 输 入 : ulCPUId 目标 CPU ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ VOID bspCpuUp (ULONG ulCPUId) { /* * TODO: 加入你的处理代码, 如果没有, 请保留下面的调试信息 */ INTREG iregInterLevel; iregInterLevel = KN_INT_DISABLE(); API_CacheClear(DATA_CACHE, LW_NULL, (size_t)~0); /* * TODO: 唤醒从核启动 */ cpuEnable(ulCPUId); while (!cpuStartDone[ulCPUId]); KN_INT_ENABLE(iregInterLevel); }
cpuStartDone 中的值默认都是0,在从核启动完成后会将相应核对应的变量置1,这样主核就知道从核已经启动完成了,就继续去启动下一个cpu核,这个循环控制是在bspInit.c 中做的。
当所有核都启动完成后还需要进行DCache的同步,这在SylixOS下是通过API_CacheBarrier 接口来实现的,这个会在下面小节讲解。
3. bspCpuDown
这个接口功能就是关闭一个cpu核,不过在SylixOS中一般cpu核启动后就不会再动态去关闭它了,所以这个接口使用默认实现即可。
4. bspCpuUpDone
在第2小节我们知道从核启动后需要告知主核它已经启动完成了,这是通过将全局变量置1来实现的:
VOID bspCpuUpDone (VOID) { cpuStartDone[LW_CPU_GET_CUR_ID()] = LW_TRUE; KN_SMP_MB(); }
这个接口是我们自己定义的,并不是SylixOS内核要求的,并且这个接口会在bspInit.c 中被调用,这个我们将在下一章节看到。
5. bspCpuUpSync
从第二小节的图中我们可以知道SylixOS所有核启动完成后需要进行DCache同步操作,这是通过API_CacheBarrier 接口实现的,这个接口可以让所有核同步执行某个回调函数,然后再同步等待所有核都执行完这个回调函数再继续运行:
static VOID bspCpuCacheSync (VOID) { API_CacheClear(DATA_CACHE, LW_NULL, (size_t)~0); } VOID bspCpuUpSync (VOID) { INT i; LW_CLASS_CPUSET cpuset; LW_CPU_ZERO(&cpuset); LW_CPU_FOREACH (i) { LW_CPU_SET(i, &cpuset); } API_CacheBarrier(bspCpuCacheSync, LW_NULL, sizeof(cpuset), &cpuset); }
API_CacheClear 接口将当前cpu核的DCache先回写再失效,这样每个核在API_CacheBarrier 执行完之后DCache中的数据是一致的。
bspCpuUpSync 接口同样也是我们自己实现的,不是SylixOS内核要求的,这个接口同样会在bspInit.c中被主核和从核调用。
评论