多核Mp逻辑分析
有关龙芯处理器的新接口规范ACPI_3.0是如下定义固件下多核启动跳转到内核下的多核运行:
需要知道的基本问题
- MailBox,芯片内的广播,用于各个处理器核间共享数据,可以互相访问的Mail,芯片内以一个寄存器来表述.
- Mp,Multi-Core.
- SlaveCore,被动运行的Core,通过其他核通知其跳转.
上电处理器核如何运行?
- 首先,针对龙芯处理器来说,当处理器上电,所有的核都会同一时刻通过spi去flash去取指令和数据,在此刻内存没有被初始化.
- 然后,每个Core此刻看到和运行的指令是一致的,由于没有写回共享内存或flash数据,所以互补影响,只是简单的配置自身的一些芯片级Config.
- 之后,SlaveCore去轮寻等待被主核调度,即轮寻MailBox,在龙芯平台下,针对3.0接口规范下有两次被主核调度:
调度流程图
- 以上为从核的上电流程,而对应MailBox里的信息,例:函数入口地址,参数,内存块的传递是主核来添写,借此来调度从核,每一次跳转是为了需要从核完成一个特定的功能:
例:第一次跳转是为了获取从核的有关信息与从核的个数,第二次跳转是为了将主核Boot到内核
Mp是如何详细被调度的?
- 调度过程到底是为了完成哪些操作?
代码片段:
496 /**
497 MP Initialize Library initialization.
498
499 This service will allocate AP reset vector and wakeup all APs to do APs
500 initialization.
501
502 This service must be invoked before all other MP Initialize Library
503 service are invoked.
504
505 @retval EFI_SUCCESS MP initialization succeeds.
506 @retval Others MP initialization fails.
507
508 **/
509 EFI_STATUS
510 EFIAPI
511 MpInitLibInitialize (
512 VOID
513 )
514 {
515 CPU_MP_DATA *OldCpuMpData;
516 CPU_INFO_IN_HOB *CpuInfoInHob;
517 UINT32 MaxLogicalProcessorNumber;
518 UINTN BufferSize;
519 VOID *MpBuffer;
520 CPU_MP_DATA *CpuMpData;
521 UINTN Index;
522
523 OldCpuMpData = GetCpuMpDataFromGuidedHob ();
524 if (OldCpuMpData == NULL) {
525 MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
526 } else {
527 MaxLogicalProcessorNumber = OldCpuMpData->CpuCount;
528 }
529 ASSERT (MaxLogicalProcessorNumber != 0);
530
533 BufferSize = 0;
514 {
515 CPU_MP_DATA *OldCpuMpData;
516 CPU_INFO_IN_HOB *CpuInfoInHob;
517 UINT32 MaxLogicalProcessorNumber;
518 UINTN BufferSize;
519 VOID *MpBuffer;
520 CPU_MP_DATA *CpuMpData;
521 UINTN Index;
522
523 OldCpuMpData = GetCpuMpDataFromGuidedHob ();
524 if (OldCpuMpData == NULL) {
525 MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
526 } else {
527 MaxLogicalProcessorNumber = OldCpuMpData->CpuCount;
528 }
529 ASSERT (MaxLogicalProcessorNumber != 0);
530
533 BufferSize = 0;
534 BufferSize += sizeof (CPU_MP_DATA);
535 BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber;
536 MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
537 ASSERT (MpBuffer != NULL);
538 ZeroMem (MpBuffer, BufferSize);
539
541 CpuMpData = (CPU_MP_DATA *) MpBuffer;
542 CpuMpData->CpuCount = 1;
543 CpuMpData->BspNumber = 0;
544 CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1);
545 CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);
546 InitializeSpinLock(&CpuMpData->MpLock);
547 //
548 // Set BSP basic information
549 //
550 InitializeApData (CpuMpData, 0, 0);
552
553 if (OldCpuMpData == NULL) {
554 if (MaxLogicalProcessorNumber > 1) {
555 //
556 // Wakeup all APs and calculate the processor count in system
557 //
558 CollectProcessorCount (CpuMpData);
559 }
560 } else {
561 //
562 // APs have been wakeup before, just get the CPU Information
563 // from HOB
564 //
565 CpuMpData->CpuCount = OldCpuMpData->CpuCount;
566 CpuMpData->BspNumber = OldCpuMpData->BspNumber;
567 CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;
568 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
569 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
570 InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock);
571 CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;
572 }
573 if (MaxLogicalProcessorNumber > 1) {
574 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
575 SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);
576 }
577 }
578 }
579
581 //
582 // Initialize global data for MP support
583 //
584 InitMpGlobalData (CpuMpData);
585
586 return EFI_SUCCESS;
587 }
先来看下CPU_MP_DATA的定义:
108 //
109 // CPU MP Data save in memory
110 //
111 struct _CPU_MP_DATA {
112 UINT64 CpuInfoInHob;
113 UINT32 CpuCount;
114 UINT32 BspNumber;
115 //
116 // The above fields data will be passed from PEI to DXE
117 // Please make sure the fields offset same in the different
118 // architecture.
119 //
120 SPIN_LOCK MpLock;
121
122 volatile UINT32 FinishedCount;
123 BOOLEAN *Finished;
124 UINT64 ExpectedTime;
125 UINT64 CurrentTime;
126 UINT64 TotalTime;
127
128 CPU_AP_DATA *CpuData;
129 }
这个结构贯穿整个Mp结构,从核填写的数据也是写在这个结构里,然后由主核整理集合,下面仔细分析逻辑:
- GetCpuMpDataFromGuidedHob:查询是否已经存在一块Hob内存提供了Mp的资源,如果存在将使用改内存中的处理器核数等进行初始化.
- 我们这个默认不存在,所以初始化了逻辑上最大支持的处理器数量 PcdCpuMaxLogicalProcessorNumber=64
- 分配内存: MpData, ApData, + (MaxProcessNum * CPU_INFO_IN_HOB)…
- 初始化MpData, 因为主核是一直狂奔,所以首先给自身core加入了Mp中,CpuCount=1.
结构布局: CpuMpData->CpuData = (CPU_AP_DATA *)(CpuMpData + 1); CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);- InitializeApData : 初始化ApData, 这里只的是单核的数据,主核狂奔所以这个首先将自身的Cpu信息填写: ApicId及自身的状态etc…
- CollectProcessorCount: 主核已经将自身信息填写好,剩余就是第一次通过MailBox调度从核进行各自初始化各自的数据,
- 例: 主核为Core0,从核为Core1,从核被调度后,首先进行了几个操作,关中断,占用CpuMpData的内存,将该数据里的CpuCount自增加1(将自身核填写到Mp),依旧按照主核的逻辑,从核自己去填写自己的ApData
- 然后,拿到Mp的从核完成自身操作后,将结束标志填写到CpuMpData->FinishedCount,然后等待被内核进行第二次调度进入内核Kernel.
- 主核Pause等待所有从核都进行完7,8操作.
- 主核等待机制: 完成条件:
-
- 主核设置了超时时间,如果从核还未填写好Ap以及Mp信息,主核将不再等待. 这里超时机制设计到了时钟问题,暂时不说,后期详聊,
-
- 主核设置了最大支持核数, MaxProcessNum , 如果获取到FinshedCount将超过最大支持核数也不再等待.
-
- 例: 最近在调32个核的龙芯服务器, 就已此为例. 默认最大支持64核,当最后一个Core31填写完MpData->CpuCount = 32, InitAp(ApicId=31,…ApState.Idle)etc…,MpData->FinshedCount = 32, 标志着所有核第一次调度初始化完成,从核将轮寻等待主核去内核进行调度,主核将继续狂奔.
-
- 最终不忘将Mp的数据保存起来,供其他服务使用,比如ACPI需要知道启动了多少个核,才能允许内核BootCore的个数.
到这里,Mp的逻辑进行了简单的梳理,希望共同学习,有所收获,本人是小白一枚,在这里祝愿龙芯越来越好