1 /** @file
2 Produces PI MP Services Protocol on top of Framework MP Services Protocol.
3 
4 Intel's Framework MP Services Protocol is replaced by EFI_MP_SERVICES_PROTOCOL in PI 1.1.
5 This module produces PI MP Services Protocol on top of Framework MP Services Protocol.
6 
7 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution.  The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12 
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 Module Name:
16 
17 **/
18 
19 #include "MpServicesOnFrameworkMpServicesThunk.h"
20 
21 EFI_HANDLE                          mHandle = NULL;
22 MP_SYSTEM_DATA                      mMPSystemData;
23 EFI_PHYSICAL_ADDRESS                mStartupVector;
24 MP_CPU_EXCHANGE_INFO                *mExchangeInfo;
25 BOOLEAN                             mStopCheckAPsStatus = FALSE;
26 UINTN                               mNumberOfProcessors;
27 EFI_GENERIC_MEMORY_TEST_PROTOCOL    *mGenMemoryTest;
28 
29 FRAMEWORK_EFI_MP_SERVICES_PROTOCOL  *mFrameworkMpService;
30 EFI_MP_SERVICES_PROTOCOL            mMpService = {
31   GetNumberOfProcessors,
32   GetProcessorInfo,
33   StartupAllAPs,
34   StartupThisAP,
35   SwitchBSP,
36   EnableDisableAP,
37   WhoAmI
38 };
39 
40 
41 /**
42   Implementation of GetNumberOfProcessors() service of MP Services Protocol.
43 
44   This service retrieves the number of logical processor in the platform
45   and the number of those logical processors that are enabled on this boot.
46   This service may only be called from the BSP.
47 
48   @param  This                       A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
49   @param  NumberOfProcessors         Pointer to the total number of logical processors in the system,
50                                      including the BSP and disabled APs.
51   @param  NumberOfEnabledProcessors  Pointer to the number of enabled logical processors that exist
52                                      in system, including the BSP.
53 
54   @retval EFI_SUCCESS	             Number of logical processors and enabled logical processors retrieved..
55   @retval EFI_DEVICE_ERROR           Caller processor is AP.
56   @retval EFI_INVALID_PARAMETER      NumberOfProcessors is NULL
57   @retval EFI_INVALID_PARAMETER      NumberOfEnabledProcessors is NULL
58 
59 **/
60 EFI_STATUS
61 EFIAPI
GetNumberOfProcessors(IN EFI_MP_SERVICES_PROTOCOL * This,OUT UINTN * NumberOfProcessors,OUT UINTN * NumberOfEnabledProcessors)62 GetNumberOfProcessors (
63   IN  EFI_MP_SERVICES_PROTOCOL     *This,
64   OUT UINTN                        *NumberOfProcessors,
65   OUT UINTN                        *NumberOfEnabledProcessors
66   )
67 {
68   EFI_STATUS      Status;
69   UINTN           CallerNumber;
70 
71   //
72   // Check whether caller processor is BSP
73   //
74   WhoAmI (This, &CallerNumber);
75   if (CallerNumber != GetBspNumber ()) {
76     return EFI_DEVICE_ERROR;
77   }
78 
79   //
80   // Check parameter NumberOfProcessors
81   //
82   if (NumberOfProcessors == NULL) {
83     return EFI_INVALID_PARAMETER;
84   }
85 
86   //
87   // Check parameter NumberOfEnabledProcessors
88   //
89   if (NumberOfEnabledProcessors == NULL) {
90     return EFI_INVALID_PARAMETER;
91   }
92 
93   Status = mFrameworkMpService->GetGeneralMPInfo (
94                                   mFrameworkMpService,
95                                   NumberOfProcessors,
96                                   NULL,
97                                   NumberOfEnabledProcessors,
98                                   NULL,
99                                   NULL
100                                   );
101   ASSERT_EFI_ERROR (Status);
102 
103   return EFI_SUCCESS;
104 }
105 
106 /**
107   Implementation of GetNumberOfProcessors() service of MP Services Protocol.
108 
109   Gets detailed MP-related information on the requested processor at the
110   instant this call is made. This service may only be called from the BSP.
111 
112   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
113   @param  ProcessorNumber       The handle number of processor.
114   @param  ProcessorInfoBuffer   A pointer to the buffer where information for the requested processor is deposited.
115 
116   @retval EFI_SUCCESS           Processor information successfully returned.
117   @retval EFI_DEVICE_ERROR      Caller processor is AP.
118   @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL
119   @retval EFI_NOT_FOUND         Processor with the handle specified by ProcessorNumber does not exist.
120 
121 **/
122 EFI_STATUS
123 EFIAPI
GetProcessorInfo(IN EFI_MP_SERVICES_PROTOCOL * This,IN UINTN ProcessorNumber,OUT EFI_PROCESSOR_INFORMATION * ProcessorInfoBuffer)124 GetProcessorInfo (
125   IN       EFI_MP_SERVICES_PROTOCOL     *This,
126   IN       UINTN                        ProcessorNumber,
127   OUT      EFI_PROCESSOR_INFORMATION    *ProcessorInfoBuffer
128   )
129 {
130   EFI_STATUS                 Status;
131   UINTN                      CallerNumber;
132   UINTN                      BufferSize;
133   EFI_MP_PROC_CONTEXT        ProcessorContextBuffer;
134 
135   //
136   // Check whether caller processor is BSP
137   //
138   WhoAmI (This, &CallerNumber);
139   if (CallerNumber != GetBspNumber ()) {
140     return EFI_DEVICE_ERROR;
141   }
142 
143   //
144   // Check parameter ProcessorInfoBuffer
145   //
146   if (ProcessorInfoBuffer == NULL) {
147     return EFI_INVALID_PARAMETER;
148   }
149 
150   //
151   // Check whether processor with the handle specified by ProcessorNumber exists
152   //
153   if (ProcessorNumber >= mNumberOfProcessors) {
154     return EFI_NOT_FOUND;
155   }
156 
157   BufferSize = sizeof (EFI_MP_PROC_CONTEXT);
158   Status = mFrameworkMpService->GetProcessorContext (
159                                   mFrameworkMpService,
160                                   ProcessorNumber,
161                                   &BufferSize,
162                                   &ProcessorContextBuffer
163                                   );
164   ASSERT_EFI_ERROR (Status);
165 
166   ProcessorInfoBuffer->ProcessorId = (UINT64) ProcessorContextBuffer.ApicID;
167 
168   //
169   // Get Status Flag of specified processor
170   //
171   ProcessorInfoBuffer->StatusFlag = 0;
172 
173   if (ProcessorContextBuffer.Enabled) {
174     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;
175   }
176 
177   if (ProcessorContextBuffer.Designation == EfiCpuBSP) {
178     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;
179   }
180 
181   if (ProcessorContextBuffer.Health.Flags.Uint32 == 0) {
182     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT;
183   }
184 
185   ProcessorInfoBuffer->Location.Package = (UINT32) ProcessorContextBuffer.PackageNumber;
186   ProcessorInfoBuffer->Location.Core    = (UINT32) ProcessorContextBuffer.NumberOfCores;
187   ProcessorInfoBuffer->Location.Thread  = (UINT32) ProcessorContextBuffer.NumberOfThreads;
188 
189   return EFI_SUCCESS;
190 }
191 
192 /**
193   Implementation of StartupAllAPs() service of MP Services Protocol.
194 
195   This service lets the caller get all enabled APs to execute a caller-provided function.
196   This service may only be called from the BSP.
197 
198   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
199   @param  Procedure             A pointer to the function to be run on enabled APs of the system.
200   @param  SingleThread          Indicates whether to execute the function simultaneously or one by one..
201   @param  WaitEvent             The event created by the caller.
202                                 If it is NULL, then execute in blocking mode.
203                                 If it is not NULL, then execute in non-blocking mode.
204   @param  TimeoutInMicroSeconds The time limit in microseconds for this AP to finish the function.
205                                 Zero means infinity.
206   @param  ProcedureArgument     Pointer to the optional parameter of the assigned function.
207   @param  FailedCpuList         The list of processor numbers that fail to finish the function before
208                                 TimeoutInMicrosecsond expires.
209 
210   @retval EFI_SUCCESS           In blocking mode, all APs have finished before the timeout expired.
211   @retval EFI_SUCCESS           In non-blocking mode, function has been dispatched to all enabled APs.
212   @retval EFI_DEVICE_ERROR      Caller processor is AP.
213   @retval EFI_NOT_STARTED       No enabled AP exists in the system.
214   @retval EFI_NOT_READY         Any enabled AP is busy.
215   @retval EFI_TIMEOUT           In blocking mode, The timeout expired before all enabled APs have finished.
216   @retval EFI_INVALID_PARAMETER Procedure is NULL.
217 
218 **/
219 EFI_STATUS
220 EFIAPI
StartupAllAPs(IN EFI_MP_SERVICES_PROTOCOL * This,IN EFI_AP_PROCEDURE Procedure,IN BOOLEAN SingleThread,IN EFI_EVENT WaitEvent OPTIONAL,IN UINTN TimeoutInMicroSeconds,IN VOID * ProcedureArgument OPTIONAL,OUT UINTN ** FailedCpuList OPTIONAL)221 StartupAllAPs (
222   IN  EFI_MP_SERVICES_PROTOCOL   *This,
223   IN  EFI_AP_PROCEDURE           Procedure,
224   IN  BOOLEAN                    SingleThread,
225   IN  EFI_EVENT                  WaitEvent             OPTIONAL,
226   IN  UINTN                      TimeoutInMicroSeconds,
227   IN  VOID                       *ProcedureArgument    OPTIONAL,
228   OUT UINTN                      **FailedCpuList       OPTIONAL
229   )
230 {
231   EFI_STATUS      Status;
232   UINTN           ProcessorNumber;
233   CPU_DATA_BLOCK  *CpuData;
234   BOOLEAN         Blocking;
235   UINTN           BspNumber;
236 
237   if (FailedCpuList != NULL) {
238     *FailedCpuList = NULL;
239   }
240 
241   //
242   // Check whether caller processor is BSP
243   //
244   BspNumber = GetBspNumber ();
245   WhoAmI (This, &ProcessorNumber);
246   if (ProcessorNumber != BspNumber) {
247     return EFI_DEVICE_ERROR;
248   }
249 
250   //
251   // Check parameter Procedure
252   //
253   if (Procedure == NULL) {
254     return EFI_INVALID_PARAMETER;
255   }
256 
257   //
258   // Temporarily suppress CheckAPsStatus()
259   //
260   mStopCheckAPsStatus = TRUE;
261 
262   //
263   // Check whether all enabled APs are idle.
264   // If any enabled AP is not idle, return EFI_NOT_READY.
265   //
266   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
267 
268     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
269 
270     mMPSystemData.CpuList[ProcessorNumber] = FALSE;
271     if (ProcessorNumber != BspNumber) {
272       if (CpuData->State != CpuStateDisabled) {
273         if (CpuData->State != CpuStateIdle) {
274           mStopCheckAPsStatus = FALSE;
275           return EFI_NOT_READY;
276         } else {
277           //
278           // Mark this processor as responsible for current calling.
279           //
280           mMPSystemData.CpuList[ProcessorNumber] = TRUE;
281         }
282       }
283     }
284   }
285 
286   mMPSystemData.FinishCount = 0;
287   mMPSystemData.StartCount  = 0;
288   Blocking                  = FALSE;
289   //
290   // Go through all enabled APs to wakeup them for Procedure.
291   // If in Single Thread mode, then only one AP is woken up, and others are waiting.
292   //
293   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
294 
295     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
296     //
297     // Check whether this processor is responsible for current calling.
298     //
299     if (mMPSystemData.CpuList[ProcessorNumber]) {
300 
301       mMPSystemData.StartCount++;
302 
303       AcquireSpinLock (&CpuData->CpuDataLock);
304       CpuData->State = CpuStateReady;
305       ReleaseSpinLock (&CpuData->CpuDataLock);
306 
307       if (!Blocking) {
308         WakeUpAp (
309           ProcessorNumber,
310           Procedure,
311           ProcedureArgument
312           );
313       }
314 
315       if (SingleThread) {
316         Blocking = TRUE;
317       }
318     }
319   }
320 
321   //
322   // If no enabled AP exists, return EFI_NOT_STARTED.
323   //
324   if (mMPSystemData.StartCount == 0) {
325     mStopCheckAPsStatus = FALSE;
326     return EFI_NOT_STARTED;
327   }
328 
329   //
330   // If WaitEvent is not NULL, execute in non-blocking mode.
331   // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS.
332   // CheckAPsStatus() will check completion and timeout periodically.
333   //
334   mMPSystemData.Procedure      = Procedure;
335   mMPSystemData.ProcArguments  = ProcedureArgument;
336   mMPSystemData.SingleThread   = SingleThread;
337   mMPSystemData.FailedCpuList  = FailedCpuList;
338   mMPSystemData.ExpectedTime   = CalculateTimeout (TimeoutInMicroSeconds, &mMPSystemData.CurrentTime);
339   mMPSystemData.WaitEvent      = WaitEvent;
340 
341   //
342   // Allow CheckAPsStatus()
343   //
344   mStopCheckAPsStatus = FALSE;
345 
346   if (WaitEvent != NULL) {
347     return EFI_SUCCESS;
348   }
349 
350   //
351   // If WaitEvent is NULL, execute in blocking mode.
352   // BSP checks APs'state until all APs finish or TimeoutInMicrosecsond expires.
353   //
354   do {
355     Status = CheckAllAPs ();
356   } while (Status == EFI_NOT_READY);
357 
358   return Status;
359 }
360 
361 /**
362   Implementation of StartupThisAP() service of MP Services Protocol.
363 
364   This service lets the caller get one enabled AP to execute a caller-provided function.
365   This service may only be called from the BSP.
366 
367   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
368   @param  Procedure             A pointer to the function to be run on the designated AP.
369   @param  ProcessorNumber       The handle number of AP..
370   @param  WaitEvent             The event created by the caller.
371                                 If it is NULL, then execute in blocking mode.
372                                 If it is not NULL, then execute in non-blocking mode.
373   @param  TimeoutInMicroseconds The time limit in microseconds for this AP to finish the function.
374                                 Zero means infinity.
375   @param  ProcedureArgument     Pointer to the optional parameter of the assigned function.
376   @param  Finished              Indicates whether AP has finished assigned function.
377                                 In blocking mode, it is ignored.
378 
379   @retval EFI_SUCCESS           In blocking mode, specified AP has finished before the timeout expires.
380   @retval EFI_SUCCESS           In non-blocking mode, function has been dispatched to specified AP.
381   @retval EFI_DEVICE_ERROR      Caller processor is AP.
382   @retval EFI_TIMEOUT           In blocking mode, the timeout expires before specified AP has finished.
383   @retval EFI_NOT_READY         Specified AP is busy.
384   @retval EFI_NOT_FOUND         Processor with the handle specified by ProcessorNumber does not exist.
385   @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
386   @retval EFI_INVALID_PARAMETER Procedure is NULL.
387 
388 **/
389 EFI_STATUS
390 EFIAPI
StartupThisAP(IN EFI_MP_SERVICES_PROTOCOL * This,IN EFI_AP_PROCEDURE Procedure,IN UINTN ProcessorNumber,IN EFI_EVENT WaitEvent OPTIONAL,IN UINTN TimeoutInMicroseconds,IN VOID * ProcedureArgument OPTIONAL,OUT BOOLEAN * Finished OPTIONAL)391 StartupThisAP (
392   IN  EFI_MP_SERVICES_PROTOCOL   *This,
393   IN  EFI_AP_PROCEDURE           Procedure,
394   IN  UINTN                      ProcessorNumber,
395   IN  EFI_EVENT                  WaitEvent OPTIONAL,
396   IN  UINTN                      TimeoutInMicroseconds,
397   IN  VOID                       *ProcedureArgument OPTIONAL,
398   OUT BOOLEAN                    *Finished OPTIONAL
399   )
400 {
401   CPU_DATA_BLOCK  *CpuData;
402   UINTN           CallerNumber;
403   EFI_STATUS      Status;
404   UINTN           BspNumber;
405 
406   if (Finished != NULL) {
407     *Finished = TRUE;
408   }
409 
410   //
411   // Check whether caller processor is BSP
412   //
413   BspNumber = GetBspNumber ();
414   WhoAmI (This, &CallerNumber);
415   if (CallerNumber != BspNumber) {
416     return EFI_DEVICE_ERROR;
417   }
418 
419   //
420   // Check whether processor with the handle specified by ProcessorNumber exists
421   //
422   if (ProcessorNumber >= mNumberOfProcessors) {
423     return EFI_NOT_FOUND;
424   }
425 
426   //
427   // Check whether specified processor is BSP
428   //
429   if (ProcessorNumber == BspNumber) {
430     return EFI_INVALID_PARAMETER;
431   }
432 
433   //
434   // Check parameter Procedure
435   //
436   if (Procedure == NULL) {
437     return EFI_INVALID_PARAMETER;
438   }
439 
440   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
441 
442   //
443   // Temporarily suppress CheckAPsStatus()
444   //
445   mStopCheckAPsStatus = TRUE;
446 
447   //
448   // Check whether specified AP is disabled
449   //
450   if (CpuData->State == CpuStateDisabled) {
451     mStopCheckAPsStatus = FALSE;
452     return EFI_INVALID_PARAMETER;
453   }
454 
455   //
456   // Check whether specified AP is busy
457   //
458   if (CpuData->State != CpuStateIdle) {
459     mStopCheckAPsStatus = FALSE;
460     return EFI_NOT_READY;
461   }
462 
463   //
464   // Wakeup specified AP for Procedure.
465   //
466   AcquireSpinLock (&CpuData->CpuDataLock);
467   CpuData->State = CpuStateReady;
468   ReleaseSpinLock (&CpuData->CpuDataLock);
469 
470   WakeUpAp (
471     ProcessorNumber,
472     Procedure,
473     ProcedureArgument
474     );
475 
476   //
477   // If WaitEvent is not NULL, execute in non-blocking mode.
478   // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS.
479   // CheckAPsStatus() will check completion and timeout periodically.
480   //
481   CpuData->WaitEvent      = WaitEvent;
482   CpuData->Finished       = Finished;
483   CpuData->ExpectedTime   = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime);
484 
485   //
486   // Allow CheckAPsStatus()
487   //
488   mStopCheckAPsStatus = FALSE;
489 
490   if (WaitEvent != NULL) {
491     return EFI_SUCCESS;
492   }
493 
494   //
495   // If WaitEvent is NULL, execute in blocking mode.
496   // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires.
497   //
498   do {
499     Status = CheckThisAP (ProcessorNumber);
500   } while (Status == EFI_NOT_READY);
501 
502   return Status;
503 }
504 
505 /**
506   Implementation of SwitchBSP() service of MP Services Protocol.
507 
508   This service switches the requested AP to be the BSP from that point onward.
509   This service may only be called from the current BSP.
510 
511   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
512   @param  ProcessorNumber       The handle number of processor.
513   @param  EnableOldBSP          Whether to enable or disable the original BSP.
514 
515   @retval EFI_SUCCESS           BSP successfully switched.
516   @retval EFI_DEVICE_ERROR      Caller processor is AP.
517   @retval EFI_NOT_FOUND         Processor with the handle specified by ProcessorNumber does not exist.
518   @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
519   @retval EFI_NOT_READY         Specified AP is busy.
520 
521 **/
522 EFI_STATUS
523 EFIAPI
SwitchBSP(IN EFI_MP_SERVICES_PROTOCOL * This,IN UINTN ProcessorNumber,IN BOOLEAN EnableOldBSP)524 SwitchBSP (
525   IN  EFI_MP_SERVICES_PROTOCOL            *This,
526   IN  UINTN                               ProcessorNumber,
527   IN  BOOLEAN                             EnableOldBSP
528   )
529 {
530   EFI_STATUS            Status;
531   CPU_DATA_BLOCK        *CpuData;
532   UINTN                 CallerNumber;
533   UINTN                 BspNumber;
534   UINTN                 ApicBase;
535   UINT32                CurrentTimerValue;
536   UINT32                CurrentTimerRegister;
537   UINT32                CurrentTimerDivide;
538   UINT64                CurrentTscValue;
539   BOOLEAN               OldInterruptState;
540 
541   //
542   // Check whether caller processor is BSP
543   //
544   BspNumber = GetBspNumber ();
545   WhoAmI (This, &CallerNumber);
546   if (CallerNumber != BspNumber) {
547     return EFI_DEVICE_ERROR;
548   }
549 
550   //
551   // Check whether processor with the handle specified by ProcessorNumber exists
552   //
553   if (ProcessorNumber >= mNumberOfProcessors) {
554     return EFI_NOT_FOUND;
555   }
556 
557   //
558   // Check whether specified processor is BSP
559   //
560   if (ProcessorNumber == BspNumber) {
561     return EFI_INVALID_PARAMETER;
562   }
563 
564   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
565 
566   //
567   // Check whether specified AP is disabled
568   //
569   if (CpuData->State == CpuStateDisabled) {
570     return EFI_INVALID_PARAMETER;
571   }
572 
573   //
574   // Check whether specified AP is busy
575   //
576   if (CpuData->State != CpuStateIdle) {
577     return EFI_NOT_READY;
578   }
579 
580   //
581   // Save and disable interrupt.
582   //
583   OldInterruptState = SaveAndDisableInterrupts ();
584 
585   //
586   // Record the current local APIC timer setting of BSP
587   //
588   ApicBase = (UINTN)AsmMsrBitFieldRead64 (MSR_IA32_APIC_BASE, 12, 35) << 12;
589   CurrentTimerValue     = MmioRead32 (ApicBase + APIC_REGISTER_TIMER_COUNT);
590   CurrentTimerRegister  = MmioRead32 (ApicBase + APIC_REGISTER_LVT_TIMER);
591   CurrentTimerDivide    = MmioRead32 (ApicBase + APIC_REGISTER_TIMER_DIVIDE);
592   //
593   // Set mask bit (BIT 16) of LVT Timer Register to disable its interrupt
594   //
595   MmioBitFieldWrite32 (ApicBase + APIC_REGISTER_LVT_TIMER, 16, 16, 1);
596 
597   //
598   // Record the current TSC value
599   //
600   CurrentTscValue = AsmReadTsc ();
601 
602   Status = mFrameworkMpService->SwitchBSP (
603                                   mFrameworkMpService,
604                                   ProcessorNumber,
605                                   EnableOldBSP
606                                   );
607   ASSERT_EFI_ERROR (Status);
608 
609   //
610   // Restore TSC value
611   //
612   AsmWriteMsr64 (MSR_IA32_TIME_STAMP_COUNTER, CurrentTscValue);
613 
614   //
615   // Restore local APIC timer setting to new BSP
616   //
617   MmioWrite32 (ApicBase + APIC_REGISTER_TIMER_DIVIDE, CurrentTimerDivide);
618   MmioWrite32 (ApicBase + APIC_REGISTER_TIMER_INIT_COUNT, CurrentTimerValue);
619   MmioWrite32 (ApicBase + APIC_REGISTER_LVT_TIMER, CurrentTimerRegister);
620 
621   //
622   // Restore interrupt state.
623   //
624   SetInterruptState (OldInterruptState);
625 
626   ChangeCpuState (BspNumber, EnableOldBSP);
627 
628   return EFI_SUCCESS;
629 }
630 
631 /**
632   Implementation of EnableDisableAP() service of MP Services Protocol.
633 
634   This service lets the caller enable or disable an AP.
635   This service may only be called from the BSP.
636 
637   @param  This                   A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
638   @param  ProcessorNumber        The handle number of processor.
639   @param  EnableAP               Indicates whether the newstate of the AP is enabled or disabled.
640   @param  HealthFlag             Indicates new health state of the AP..
641 
642   @retval EFI_SUCCESS            AP successfully enabled or disabled.
643   @retval EFI_DEVICE_ERROR       Caller processor is AP.
644   @retval EFI_NOT_FOUND          Processor with the handle specified by ProcessorNumber does not exist.
645   @retval EFI_INVALID_PARAMETERS ProcessorNumber specifies the BSP.
646 
647 **/
648 EFI_STATUS
649 EFIAPI
EnableDisableAP(IN EFI_MP_SERVICES_PROTOCOL * This,IN UINTN ProcessorNumber,IN BOOLEAN EnableAP,IN UINT32 * HealthFlag OPTIONAL)650 EnableDisableAP (
651   IN  EFI_MP_SERVICES_PROTOCOL            *This,
652   IN  UINTN                               ProcessorNumber,
653   IN  BOOLEAN                             EnableAP,
654   IN  UINT32                              *HealthFlag OPTIONAL
655   )
656 {
657   EFI_STATUS      Status;
658   UINTN           CallerNumber;
659   EFI_MP_HEALTH   HealthState;
660   EFI_MP_HEALTH   *HealthStatePointer;
661   UINTN           BspNumber;
662 
663   //
664   // Check whether caller processor is BSP
665   //
666   BspNumber = GetBspNumber ();
667   WhoAmI (This, &CallerNumber);
668   if (CallerNumber != BspNumber) {
669     return EFI_DEVICE_ERROR;
670   }
671 
672   //
673   // Check whether processor with the handle specified by ProcessorNumber exists
674   //
675   if (ProcessorNumber >= mNumberOfProcessors) {
676     return EFI_NOT_FOUND;
677   }
678 
679   //
680   // Check whether specified processor is BSP
681   //
682   if (ProcessorNumber == BspNumber) {
683     return EFI_INVALID_PARAMETER;
684   }
685 
686   if (HealthFlag == NULL) {
687     HealthStatePointer = NULL;
688   } else {
689     if ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) == 0) {
690       HealthState.Flags.Uint32 = 1;
691     } else {
692       HealthState.Flags.Uint32 = 0;
693     }
694     HealthState.TestStatus = 0;
695 
696     HealthStatePointer = &HealthState;
697   }
698 
699   Status = mFrameworkMpService->EnableDisableAP (
700                                   mFrameworkMpService,
701                                   ProcessorNumber,
702                                   EnableAP,
703                                   HealthStatePointer
704                                   );
705   ASSERT_EFI_ERROR (Status);
706 
707   ChangeCpuState (ProcessorNumber, EnableAP);
708 
709   return EFI_SUCCESS;
710 }
711 
712 /**
713   Implementation of WhoAmI() service of MP Services Protocol.
714 
715   This service lets the caller processor get its handle number.
716   This service may be called from the BSP and APs.
717 
718   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
719   @param  ProcessorNumber       Pointer to the handle number of AP.
720 
721   @retval EFI_SUCCESS           Processor number successfully returned.
722   @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL
723 
724 **/
725 EFI_STATUS
726 EFIAPI
WhoAmI(IN EFI_MP_SERVICES_PROTOCOL * This,OUT UINTN * ProcessorNumber)727 WhoAmI (
728   IN  EFI_MP_SERVICES_PROTOCOL            *This,
729   OUT UINTN                               *ProcessorNumber
730   )
731 {
732   EFI_STATUS  Status;
733 
734   if (ProcessorNumber == NULL) {
735     return EFI_INVALID_PARAMETER;
736   }
737 
738   Status = mFrameworkMpService->WhoAmI (
739                                   mFrameworkMpService,
740                                   ProcessorNumber
741                                   );
742   ASSERT_EFI_ERROR (Status);
743 
744   return EFI_SUCCESS;
745 }
746 
747 /**
748   Checks APs' status periodically.
749 
750   This function is triggerred by timer perodically to check the
751   state of APs for StartupAllAPs() and StartupThisAP() executed
752   in non-blocking mode.
753 
754   @param  Event    Event triggered.
755   @param  Context  Parameter passed with the event.
756 
757 **/
758 VOID
759 EFIAPI
CheckAPsStatus(IN EFI_EVENT Event,IN VOID * Context)760 CheckAPsStatus (
761   IN  EFI_EVENT                           Event,
762   IN  VOID                                *Context
763   )
764 {
765   UINTN           ProcessorNumber;
766   CPU_DATA_BLOCK  *CpuData;
767   EFI_STATUS      Status;
768 
769   //
770   // If CheckAPsStatus() is stopped, then return immediately.
771   //
772   if (mStopCheckAPsStatus) {
773     return;
774   }
775 
776   //
777   // First, check whether pending StartupAllAPs() exists.
778   //
779   if (mMPSystemData.WaitEvent != NULL) {
780 
781     Status = CheckAllAPs ();
782     //
783     // If all APs finish for StartupAllAPs(), signal the WaitEvent for it..
784     //
785     if (Status != EFI_NOT_READY) {
786       Status = gBS->SignalEvent (mMPSystemData.WaitEvent);
787       mMPSystemData.WaitEvent = NULL;
788     }
789   }
790 
791   //
792   // Second, check whether pending StartupThisAPs() callings exist.
793   //
794   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
795 
796     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
797 
798     if (CpuData->WaitEvent == NULL) {
799       continue;
800     }
801 
802     Status = CheckThisAP (ProcessorNumber);
803 
804     if (Status != EFI_NOT_READY) {
805       gBS->SignalEvent (CpuData->WaitEvent);
806       CpuData->WaitEvent = NULL;
807     }
808   }
809   return ;
810 }
811 
812 /**
813   Checks status of all APs.
814 
815   This function checks whether all APs have finished task assigned by StartupAllAPs(),
816   and whether timeout expires.
817 
818   @retval EFI_SUCCESS           All APs have finished task assigned by StartupAllAPs().
819   @retval EFI_TIMEOUT           The timeout expires.
820   @retval EFI_NOT_READY         APs have not finished task and timeout has not expired.
821 
822 **/
823 EFI_STATUS
CheckAllAPs(VOID)824 CheckAllAPs (
825   VOID
826   )
827 {
828   UINTN           ProcessorNumber;
829   UINTN           NextProcessorNumber;
830   UINTN           ListIndex;
831   EFI_STATUS      Status;
832   CPU_STATE       CpuState;
833   CPU_DATA_BLOCK  *CpuData;
834 
835   NextProcessorNumber = 0;
836 
837   //
838   // Go through all APs that are responsible for the StartupAllAPs().
839   //
840   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
841     if (!mMPSystemData.CpuList[ProcessorNumber]) {
842       continue;
843     }
844 
845     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
846 
847     //
848     //  Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
849     //  Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
850     //  value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
851     //
852     AcquireSpinLock (&CpuData->CpuDataLock);
853     CpuState = CpuData->State;
854     ReleaseSpinLock (&CpuData->CpuDataLock);
855 
856     if (CpuState == CpuStateFinished) {
857       mMPSystemData.FinishCount++;
858       mMPSystemData.CpuList[ProcessorNumber] = FALSE;
859 
860       AcquireSpinLock (&CpuData->CpuDataLock);
861       CpuData->State = CpuStateIdle;
862       ReleaseSpinLock (&CpuData->CpuDataLock);
863 
864       //
865       // If in Single Thread mode, then search for the next waiting AP for execution.
866       //
867       if (mMPSystemData.SingleThread) {
868         Status = GetNextWaitingProcessorNumber (&NextProcessorNumber);
869 
870         if (!EFI_ERROR (Status)) {
871           WakeUpAp (
872             NextProcessorNumber,
873             mMPSystemData.Procedure,
874             mMPSystemData.ProcArguments
875             );
876         }
877       }
878     }
879   }
880 
881   //
882   // If all APs finish, return EFI_SUCCESS.
883   //
884   if (mMPSystemData.FinishCount == mMPSystemData.StartCount) {
885     return EFI_SUCCESS;
886   }
887 
888   //
889   // If timeout expires, report timeout.
890   //
891   if (CheckTimeout (&mMPSystemData.CurrentTime, &mMPSystemData.TotalTime, mMPSystemData.ExpectedTime)) {
892     //
893     // If FailedCpuList is not NULL, record all failed APs in it.
894     //
895     if (mMPSystemData.FailedCpuList != NULL) {
896       *mMPSystemData.FailedCpuList = AllocatePool ((mMPSystemData.StartCount - mMPSystemData.FinishCount + 1) * sizeof(UINTN));
897       ASSERT (*mMPSystemData.FailedCpuList != NULL);
898     }
899     ListIndex = 0;
900 
901     for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
902       //
903       // Check whether this processor is responsible for StartupAllAPs().
904       //
905       if (mMPSystemData.CpuList[ProcessorNumber]) {
906         //
907         // Reset failed APs to idle state
908         //
909         ResetProcessorToIdleState (ProcessorNumber);
910         mMPSystemData.CpuList[ProcessorNumber] = FALSE;
911         if (mMPSystemData.FailedCpuList != NULL) {
912           (*mMPSystemData.FailedCpuList)[ListIndex++] = ProcessorNumber;
913         }
914       }
915     }
916     if (mMPSystemData.FailedCpuList != NULL) {
917       (*mMPSystemData.FailedCpuList)[ListIndex] = END_OF_CPU_LIST;
918     }
919     return EFI_TIMEOUT;
920   }
921   return EFI_NOT_READY;
922 }
923 
924 /**
925   Checks status of specified AP.
926 
927   This function checks whether specified AP has finished task assigned by StartupThisAP(),
928   and whether timeout expires.
929 
930   @param  ProcessorNumber       The handle number of processor.
931 
932   @retval EFI_SUCCESS           Specified AP has finished task assigned by StartupThisAPs().
933   @retval EFI_TIMEOUT           The timeout expires.
934   @retval EFI_NOT_READY         Specified AP has not finished task and timeout has not expired.
935 
936 **/
937 EFI_STATUS
CheckThisAP(UINTN ProcessorNumber)938 CheckThisAP (
939   UINTN  ProcessorNumber
940   )
941 {
942   CPU_DATA_BLOCK  *CpuData;
943   CPU_STATE       CpuState;
944 
945   ASSERT (ProcessorNumber < mNumberOfProcessors);
946   ASSERT (ProcessorNumber < MAX_CPU_NUMBER);
947 
948   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
949 
950   //
951   //  Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
952   //  Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
953   //  value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
954   //
955   AcquireSpinLock (&CpuData->CpuDataLock);
956   CpuState = CpuData->State;
957   ReleaseSpinLock (&CpuData->CpuDataLock);
958 
959   //
960   // If the APs finishes for StartupThisAP(), return EFI_SUCCESS.
961   //
962   if (CpuState == CpuStateFinished) {
963 
964     AcquireSpinLock (&CpuData->CpuDataLock);
965     CpuData->State = CpuStateIdle;
966     ReleaseSpinLock (&CpuData->CpuDataLock);
967 
968     if (CpuData->Finished != NULL) {
969       *(CpuData->Finished) = TRUE;
970     }
971     return EFI_SUCCESS;
972   } else {
973     //
974     // If timeout expires for StartupThisAP(), report timeout.
975     //
976     if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) {
977 
978       if (CpuData->Finished != NULL) {
979         *(CpuData->Finished) = FALSE;
980       }
981       //
982       // Reset failed AP to idle state
983       //
984       ResetProcessorToIdleState (ProcessorNumber);
985 
986       return EFI_TIMEOUT;
987     }
988   }
989   return EFI_NOT_READY;
990 }
991 
992 /**
993   Calculate timeout value and return the current performance counter value.
994 
995   Calculate the number of performance counter ticks required for a timeout.
996   If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
997   as infinity.
998 
999   @param  TimeoutInMicroseconds   Timeout value in microseconds.
1000   @param  CurrentTime             Returns the current value of the performance counter.
1001 
1002   @return Expected timestamp counter for timeout.
1003           If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
1004           as infinity.
1005 
1006 **/
1007 UINT64
CalculateTimeout(IN UINTN TimeoutInMicroseconds,OUT UINT64 * CurrentTime)1008 CalculateTimeout (
1009   IN  UINTN   TimeoutInMicroseconds,
1010   OUT UINT64  *CurrentTime
1011   )
1012 {
1013   //
1014   // Read the current value of the performance counter
1015   //
1016   *CurrentTime = GetPerformanceCounter ();
1017 
1018   //
1019   // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
1020   // as infinity.
1021   //
1022   if (TimeoutInMicroseconds == 0) {
1023     return 0;
1024   }
1025 
1026   //
1027   // GetPerformanceCounterProperties () returns the timestamp counter's frequency
1028   // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide
1029   // it by 1,000,000, to get the number of ticks for the timeout value.
1030   //
1031   return DivU64x32 (
1032            MultU64x64 (
1033              GetPerformanceCounterProperties (NULL, NULL),
1034              TimeoutInMicroseconds
1035              ),
1036            1000000
1037            );
1038 }
1039 
1040 /**
1041   Checks whether timeout expires.
1042 
1043   Check whether the number of ellapsed performance counter ticks required for a timeout condition
1044   has been reached.  If Timeout is zero, which means infinity, return value is always FALSE.
1045 
1046   @param  PreviousTime         On input, the value of the performance counter when it was last read.
1047                                On output, the current value of the performance counter
1048   @param  TotalTime            The total amount of ellapsed time in performance counter ticks.
1049   @param  Timeout              The number of performance counter ticks required to reach a timeout condition.
1050 
1051   @retval TRUE                 A timeout condition has been reached.
1052   @retval FALSE                A timeout condition has not been reached.
1053 
1054 **/
1055 BOOLEAN
CheckTimeout(IN OUT UINT64 * PreviousTime,IN UINT64 * TotalTime,IN UINT64 Timeout)1056 CheckTimeout (
1057   IN OUT UINT64  *PreviousTime,
1058   IN     UINT64  *TotalTime,
1059   IN     UINT64  Timeout
1060   )
1061 {
1062   UINT64  Start;
1063   UINT64  End;
1064   UINT64  CurrentTime;
1065   INT64   Delta;
1066   INT64   Cycle;
1067 
1068   if (Timeout == 0) {
1069     return FALSE;
1070   }
1071   GetPerformanceCounterProperties (&Start, &End);
1072   Cycle = End - Start;
1073   if (Cycle < 0) {
1074     Cycle = -Cycle;
1075   }
1076   Cycle++;
1077   CurrentTime = GetPerformanceCounter();
1078   Delta = (INT64) (CurrentTime - *PreviousTime);
1079   if (Start > End) {
1080     Delta = -Delta;
1081   }
1082   if (Delta < 0) {
1083     Delta += Cycle;
1084   }
1085   *TotalTime += Delta;
1086   *PreviousTime = CurrentTime;
1087   if (*TotalTime > Timeout) {
1088     return TRUE;
1089   }
1090   return FALSE;
1091 }
1092 
1093 /**
1094   Searches for the next waiting AP.
1095 
1096   Search for the next AP that is put in waiting state by single-threaded StartupAllAPs().
1097 
1098   @param  NextProcessorNumber  Pointer to the processor number of the next waiting AP.
1099 
1100   @retval EFI_SUCCESS          The next waiting AP has been found.
1101   @retval EFI_NOT_FOUND        No waiting AP exists.
1102 
1103 **/
1104 EFI_STATUS
GetNextWaitingProcessorNumber(OUT UINTN * NextProcessorNumber)1105 GetNextWaitingProcessorNumber (
1106   OUT UINTN                               *NextProcessorNumber
1107   )
1108 {
1109   UINTN           ProcessorNumber;
1110 
1111   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
1112 
1113     if (mMPSystemData.CpuList[ProcessorNumber]) {
1114       *NextProcessorNumber = ProcessorNumber;
1115       return EFI_SUCCESS;
1116     }
1117   }
1118 
1119   return EFI_NOT_FOUND;
1120 }
1121 
1122 
1123 /**
1124   Wrapper function for all procedures assigned to AP.
1125 
1126   Wrapper function for all procedures assigned to AP via MP service protocol.
1127   It controls states of AP and invokes assigned precedure.
1128 
1129 **/
1130 VOID
ApProcWrapper(VOID)1131 ApProcWrapper (
1132   VOID
1133   )
1134 {
1135   EFI_AP_PROCEDURE      Procedure;
1136   VOID                  *Parameter;
1137   UINTN                 ProcessorNumber;
1138   CPU_DATA_BLOCK        *CpuData;
1139 
1140   //
1141   // Program virtual wire mode for AP, since it will be lost after AP wake up
1142   //
1143   ProgramVirtualWireMode ();
1144   DisableLvtInterrupts ();
1145 
1146   //
1147   // Initialize Debug Agent to support source level debug on AP code.
1148   //
1149   InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);
1150 
1151   WhoAmI (&mMpService, &ProcessorNumber);
1152   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
1153 
1154   AcquireSpinLock (&CpuData->CpuDataLock);
1155   CpuData->State = CpuStateBusy;
1156   ReleaseSpinLock (&CpuData->CpuDataLock);
1157 
1158   //
1159   // Now let us check it out.
1160   //
1161   AcquireSpinLock (&CpuData->CpuDataLock);
1162   Procedure = CpuData->Procedure;
1163   Parameter = CpuData->Parameter;
1164   ReleaseSpinLock (&CpuData->CpuDataLock);
1165 
1166   if (Procedure != NULL) {
1167 
1168     Procedure (Parameter);
1169 
1170     //
1171     // if BSP is switched to AP, it continue execute from here, but it carries register state
1172     // of the old AP, so need to reload CpuData (might be stored in a register after compiler
1173     // optimization) to make sure it points to the right data
1174     //
1175     WhoAmI (&mMpService, &ProcessorNumber);
1176     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
1177 
1178     AcquireSpinLock (&CpuData->CpuDataLock);
1179     CpuData->Procedure = NULL;
1180     ReleaseSpinLock (&CpuData->CpuDataLock);
1181   }
1182 
1183   AcquireSpinLock (&CpuData->CpuDataLock);
1184   CpuData->State = CpuStateFinished;
1185   ReleaseSpinLock (&CpuData->CpuDataLock);
1186 }
1187 
1188 /**
1189   Function to wake up a specified AP and assign procedure to it.
1190 
1191   @param  ProcessorNumber  Handle number of the specified processor.
1192   @param  Procedure        Procedure to assign.
1193   @param  ProcArguments    Argument for Procedure.
1194 
1195 **/
1196 VOID
WakeUpAp(IN UINTN ProcessorNumber,IN EFI_AP_PROCEDURE Procedure,IN VOID * ProcArguments)1197 WakeUpAp (
1198   IN   UINTN                 ProcessorNumber,
1199   IN   EFI_AP_PROCEDURE      Procedure,
1200   IN   VOID                  *ProcArguments
1201   )
1202 {
1203   EFI_STATUS                   Status;
1204   CPU_DATA_BLOCK               *CpuData;
1205   EFI_PROCESSOR_INFORMATION    ProcessorInfoBuffer;
1206 
1207   ASSERT (ProcessorNumber < mNumberOfProcessors);
1208   ASSERT (ProcessorNumber < MAX_CPU_NUMBER);
1209 
1210   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
1211 
1212   AcquireSpinLock (&CpuData->CpuDataLock);
1213   CpuData->Parameter  = ProcArguments;
1214   CpuData->Procedure  = Procedure;
1215   ReleaseSpinLock (&CpuData->CpuDataLock);
1216 
1217   Status = GetProcessorInfo (
1218              &mMpService,
1219              ProcessorNumber,
1220              &ProcessorInfoBuffer
1221              );
1222   ASSERT_EFI_ERROR (Status);
1223 
1224   mExchangeInfo->ApFunction = (VOID *) (UINTN) ApProcWrapper;
1225   mExchangeInfo->ProcessorNumber[ProcessorInfoBuffer.ProcessorId] = (UINT32) ProcessorNumber;
1226   SendInitSipiSipi (
1227     (UINT32) ProcessorInfoBuffer.ProcessorId,
1228     (UINT32) (UINTN) mStartupVector
1229     );
1230 }
1231 
1232 /**
1233   Terminate AP's task and set it to idle state.
1234 
1235   This function terminates AP's task due to timeout by sending INIT-SIPI,
1236   and sends it to idle state.
1237 
1238   @param  ProcessorNumber  Handle number of the specified processor.
1239 
1240 **/
1241 VOID
ResetProcessorToIdleState(UINTN ProcessorNumber)1242 ResetProcessorToIdleState (
1243   UINTN      ProcessorNumber
1244   )
1245 {
1246   EFI_STATUS                   Status;
1247   CPU_DATA_BLOCK               *CpuData;
1248   EFI_PROCESSOR_INFORMATION    ProcessorInfoBuffer;
1249 
1250   Status = GetProcessorInfo (
1251              &mMpService,
1252              ProcessorNumber,
1253              &ProcessorInfoBuffer
1254              );
1255   ASSERT_EFI_ERROR (Status);
1256 
1257   mExchangeInfo->ApFunction = NULL;
1258   mExchangeInfo->ProcessorNumber[ProcessorInfoBuffer.ProcessorId] = (UINT32) ProcessorNumber;
1259   SendInitSipiSipi (
1260     (UINT32) ProcessorInfoBuffer.ProcessorId,
1261     (UINT32) (UINTN) mStartupVector
1262     );
1263 
1264   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
1265 
1266   AcquireSpinLock (&CpuData->CpuDataLock);
1267   CpuData->State = CpuStateIdle;
1268   ReleaseSpinLock (&CpuData->CpuDataLock);
1269 }
1270 
1271 /**
1272   Worker function of EnableDisableAP ()
1273 
1274   Worker function of EnableDisableAP (). Changes state of specified processor.
1275 
1276   @param  ProcessorNumber Processor number of specified AP.
1277   @param  NewState        Desired state of the specified AP.
1278 
1279   @retval EFI_SUCCESS     AP's state successfully changed.
1280 
1281 **/
1282 EFI_STATUS
ChangeCpuState(IN UINTN ProcessorNumber,IN BOOLEAN NewState)1283 ChangeCpuState (
1284   IN     UINTN                      ProcessorNumber,
1285   IN     BOOLEAN                    NewState
1286   )
1287 {
1288   CPU_DATA_BLOCK  *CpuData;
1289 
1290   ASSERT (ProcessorNumber < mNumberOfProcessors);
1291   ASSERT (ProcessorNumber < MAX_CPU_NUMBER);
1292 
1293   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
1294 
1295   if (!NewState) {
1296     AcquireSpinLock (&CpuData->CpuDataLock);
1297     CpuData->State = CpuStateDisabled;
1298     ReleaseSpinLock (&CpuData->CpuDataLock);
1299   } else {
1300     AcquireSpinLock (&CpuData->CpuDataLock);
1301     CpuData->State = CpuStateIdle;
1302     ReleaseSpinLock (&CpuData->CpuDataLock);
1303   }
1304 
1305   return EFI_SUCCESS;
1306 }
1307 
1308 /**
1309   Test memory region of EfiGcdMemoryTypeReserved.
1310 
1311   @param  Length           The length of memory region to test.
1312 
1313   @retval EFI_SUCCESS      The memory region passes test.
1314   @retval EFI_NOT_FOUND    The memory region is not reserved memory.
1315   @retval EFI_DEVICE_ERROR The memory fails on test.
1316 
1317 **/
1318 EFI_STATUS
TestReservedMemory(UINTN Length)1319 TestReservedMemory (
1320   UINTN  Length
1321   )
1322 {
1323   EFI_STATUS                       Status;
1324   EFI_GCD_MEMORY_SPACE_DESCRIPTOR  Descriptor;
1325   EFI_PHYSICAL_ADDRESS             Address;
1326   UINTN                            LengthCovered;
1327   UINTN                            RemainingLength;
1328 
1329   //
1330   // Walk through the memory descriptors covering the memory range.
1331   //
1332   Address         = mStartupVector;
1333   RemainingLength = Length;
1334   while (Address < mStartupVector + Length) {
1335     Status = gDS->GetMemorySpaceDescriptor(
1336                     Address,
1337                     &Descriptor
1338                     );
1339     if (EFI_ERROR (Status)) {
1340       return EFI_NOT_FOUND;
1341     }
1342 
1343     if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
1344       return EFI_NOT_FOUND;
1345     }
1346     //
1347     // Calculated the length of the intersected range.
1348     //
1349     LengthCovered = (UINTN) (Descriptor.BaseAddress + Descriptor.Length - Address);
1350     if (LengthCovered > RemainingLength) {
1351       LengthCovered = RemainingLength;
1352     }
1353 
1354     Status = mGenMemoryTest->CompatibleRangeTest (
1355                                mGenMemoryTest,
1356                                Address,
1357                                LengthCovered
1358                                );
1359     if (EFI_ERROR (Status)) {
1360       return EFI_DEVICE_ERROR;
1361     }
1362 
1363     Address         += LengthCovered;
1364     RemainingLength -= LengthCovered;
1365   }
1366 
1367   return EFI_SUCCESS;
1368 }
1369 
1370 /**
1371   Allocates startup vector for APs.
1372 
1373   This function allocates Startup vector for APs.
1374 
1375   @param  Size  The size of startup vector.
1376 
1377 **/
1378 VOID
AllocateStartupVector(UINTN Size)1379 AllocateStartupVector (
1380   UINTN   Size
1381   )
1382 {
1383   EFI_STATUS                            Status;
1384 
1385   Status = gBS->LocateProtocol (
1386                   &gEfiGenericMemTestProtocolGuid,
1387                   NULL,
1388                   (VOID **) &mGenMemoryTest
1389                   );
1390   if (EFI_ERROR (Status)) {
1391     mGenMemoryTest = NULL;
1392   }
1393 
1394   for (mStartupVector = 0x7F000; mStartupVector >= 0x2000; mStartupVector -= EFI_PAGE_SIZE) {
1395     if (mGenMemoryTest != NULL) {
1396       //
1397       // Test memory if it is EfiGcdMemoryTypeReserved.
1398       //
1399       Status = TestReservedMemory (EFI_SIZE_TO_PAGES (Size) * EFI_PAGE_SIZE);
1400       if (Status == EFI_DEVICE_ERROR) {
1401         continue;
1402       }
1403     }
1404 
1405     Status = gBS->AllocatePages (
1406                     AllocateAddress,
1407                     EfiBootServicesCode,
1408                     EFI_SIZE_TO_PAGES (Size),
1409                     &mStartupVector
1410                     );
1411 
1412     if (!EFI_ERROR (Status)) {
1413       break;
1414     }
1415   }
1416 
1417   ASSERT_EFI_ERROR (Status);
1418 }
1419 
1420 /**
1421   Prepares Startup Vector for APs.
1422 
1423   This function prepares Startup Vector for APs.
1424 
1425 **/
1426 VOID
PrepareAPStartupVector(VOID)1427 PrepareAPStartupVector (
1428   VOID
1429   )
1430 {
1431   MP_ASSEMBLY_ADDRESS_MAP   AddressMap;
1432   IA32_DESCRIPTOR           GdtrForBSP;
1433   IA32_DESCRIPTOR           IdtrForBSP;
1434   EFI_PHYSICAL_ADDRESS      GdtForAP;
1435   EFI_PHYSICAL_ADDRESS      IdtForAP;
1436   EFI_STATUS                Status;
1437 
1438   //
1439   // Get the address map of startup code for AP,
1440   // including code size, and offset of long jump instructions to redirect.
1441   //
1442   AsmGetAddressMap (&AddressMap);
1443 
1444   //
1445   // Allocate a 4K-aligned region under 1M for startup vector for AP.
1446   // The region contains AP startup code and exchange data between BSP and AP.
1447   //
1448   AllocateStartupVector (AddressMap.Size + sizeof (MP_CPU_EXCHANGE_INFO));
1449 
1450   //
1451   // Copy AP startup code to startup vector, and then redirect the long jump
1452   // instructions for mode switching.
1453   //
1454   CopyMem ((VOID *) (UINTN) mStartupVector, AddressMap.RendezvousFunnelAddress, AddressMap.Size);
1455   *(UINT32 *) (UINTN) (mStartupVector + AddressMap.FlatJumpOffset + 3) = (UINT32) (mStartupVector + AddressMap.PModeEntryOffset);
1456   //
1457   // For IA32 mode, LongJumpOffset is filled with zero. If non-zero, then we are in X64 mode, so further redirect for long mode switch.
1458   //
1459   if (AddressMap.LongJumpOffset != 0) {
1460     *(UINT32 *) (UINTN) (mStartupVector + AddressMap.LongJumpOffset + 2) = (UINT32) (mStartupVector + AddressMap.LModeEntryOffset);
1461   }
1462 
1463   //
1464   // Get the start address of exchange data between BSP and AP.
1465   //
1466   mExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (mStartupVector + AddressMap.Size);
1467 
1468   ZeroMem ((VOID *) mExchangeInfo, sizeof (MP_CPU_EXCHANGE_INFO));
1469 
1470   mExchangeInfo->StackStart  = AllocatePages (EFI_SIZE_TO_PAGES (mNumberOfProcessors * AP_STACK_SIZE));
1471   mExchangeInfo->StackSize  = AP_STACK_SIZE;
1472 
1473   AsmReadGdtr (&GdtrForBSP);
1474   AsmReadIdtr (&IdtrForBSP);
1475 
1476   //
1477   // Allocate memory under 4G to hold GDT for APs
1478   //
1479   GdtForAP = 0xffffffff;
1480   Status   = gBS->AllocatePages (
1481                     AllocateMaxAddress,
1482                     EfiBootServicesData,
1483                     EFI_SIZE_TO_PAGES ((GdtrForBSP.Limit + 1) + (IdtrForBSP.Limit + 1)),
1484                     &GdtForAP
1485                     );
1486   ASSERT_EFI_ERROR (Status);
1487 
1488   IdtForAP = (UINTN) GdtForAP + GdtrForBSP.Limit + 1;
1489 
1490   CopyMem ((VOID *) (UINTN) GdtForAP, (VOID *) GdtrForBSP.Base, GdtrForBSP.Limit + 1);
1491   CopyMem ((VOID *) (UINTN) IdtForAP, (VOID *) IdtrForBSP.Base, IdtrForBSP.Limit + 1);
1492 
1493   mExchangeInfo->GdtrProfile.Base  = (UINTN) GdtForAP;
1494   mExchangeInfo->GdtrProfile.Limit = GdtrForBSP.Limit;
1495   mExchangeInfo->IdtrProfile.Base  = (UINTN) IdtForAP;
1496   mExchangeInfo->IdtrProfile.Limit = IdtrForBSP.Limit;
1497 
1498   mExchangeInfo->BufferStart = (UINT32) mStartupVector;
1499   mExchangeInfo->Cr3         = (UINT32) (AsmReadCr3 ());
1500 }
1501 
1502 /**
1503   Prepares memory region for processor configuration.
1504 
1505   This function prepares memory region for processor configuration.
1506 
1507 **/
1508 VOID
PrepareMemoryForConfiguration(VOID)1509 PrepareMemoryForConfiguration (
1510   VOID
1511   )
1512 {
1513   UINTN                Index;
1514 
1515   //
1516   // Initialize Spin Locks for system
1517   //
1518   InitializeSpinLock (&mMPSystemData.APSerializeLock);
1519   for (Index = 0; Index < MAX_CPU_NUMBER; Index++) {
1520     InitializeSpinLock (&mMPSystemData.CpuData[Index].CpuDataLock);
1521   }
1522 
1523   PrepareAPStartupVector ();
1524 }
1525 
1526 /**
1527   Gets the processor number of BSP.
1528 
1529   @return  The processor number of BSP.
1530 
1531 **/
1532 UINTN
GetBspNumber(VOID)1533 GetBspNumber (
1534   VOID
1535   )
1536 {
1537   UINTN                      ProcessorNumber;
1538   EFI_MP_PROC_CONTEXT        ProcessorContextBuffer;
1539   EFI_STATUS                 Status;
1540   UINTN                      BufferSize;
1541 
1542   BufferSize = sizeof (EFI_MP_PROC_CONTEXT);
1543 
1544   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
1545     Status = mFrameworkMpService->GetProcessorContext (
1546                                     mFrameworkMpService,
1547                                     ProcessorNumber,
1548                                     &BufferSize,
1549                                     &ProcessorContextBuffer
1550                                     );
1551     ASSERT_EFI_ERROR (Status);
1552 
1553     if (ProcessorContextBuffer.Designation == EfiCpuBSP) {
1554       break;
1555     }
1556   }
1557   ASSERT (ProcessorNumber < mNumberOfProcessors);
1558 
1559   return ProcessorNumber;
1560 }
1561 
1562 /**
1563   Entrypoint of MP Services Protocol thunk driver.
1564 
1565   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
1566   @param[in] SystemTable    A pointer to the EFI System Table.
1567 
1568   @retval EFI_SUCCESS       The entry point is executed successfully.
1569 
1570 **/
1571 EFI_STATUS
1572 EFIAPI
InitializeMpServicesProtocol(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1573 InitializeMpServicesProtocol (
1574   IN EFI_HANDLE        ImageHandle,
1575   IN EFI_SYSTEM_TABLE  *SystemTable
1576   )
1577 {
1578   EFI_STATUS Status;
1579 
1580   //
1581   // Locates Framework version MP Services Protocol
1582   //
1583   Status = gBS->LocateProtocol (
1584                   &gFrameworkEfiMpServiceProtocolGuid,
1585                   NULL,
1586                   (VOID **) &mFrameworkMpService
1587                   );
1588   ASSERT_EFI_ERROR (Status);
1589 
1590   Status = mFrameworkMpService->GetGeneralMPInfo (
1591                                   mFrameworkMpService,
1592                                   &mNumberOfProcessors,
1593                                   NULL,
1594                                   NULL,
1595                                   NULL,
1596                                   NULL
1597                                   );
1598   ASSERT_EFI_ERROR (Status);
1599   ASSERT (mNumberOfProcessors < MAX_CPU_NUMBER);
1600 
1601   PrepareMemoryForConfiguration ();
1602 
1603   //
1604   // Create timer event to check AP state for non-blocking execution.
1605   //
1606   Status = gBS->CreateEvent (
1607                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
1608                   TPL_CALLBACK,
1609                   CheckAPsStatus,
1610                   NULL,
1611                   &mMPSystemData.CheckAPsEvent
1612                   );
1613   ASSERT_EFI_ERROR (Status);
1614 
1615   //
1616   // Now install the MP services protocol.
1617   //
1618   Status = gBS->InstallProtocolInterface (
1619                   &mHandle,
1620                   &gEfiMpServiceProtocolGuid,
1621                   EFI_NATIVE_INTERFACE,
1622                   &mMpService
1623                   );
1624   ASSERT_EFI_ERROR (Status);
1625 
1626   //
1627   // Launch the timer event to check AP state.
1628   //
1629   Status = gBS->SetTimer (
1630                   mMPSystemData.CheckAPsEvent,
1631                   TimerPeriodic,
1632                   100000
1633                   );
1634   ASSERT_EFI_ERROR (Status);
1635 
1636   return EFI_SUCCESS;
1637 }
1638