• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Construct MP Services Protocol on top of the EMU Thread protocol.
3   This code makes APs show up in the emulator. PcdEmuApCount is the
4   number of APs the emulator should produce.
5 
6   The MP Services Protocol provides a generalized way of performing following tasks:
7     - Retrieving information of multi-processor environment and MP-related status of
8       specific processors.
9     - Dispatching user-provided function to APs.
10     - Maintain MP-related processor status.
11 
12   The MP Services Protocol must be produced on any system with more than one logical
13   processor.
14 
15   The Protocol is available only during boot time.
16 
17   MP Services Protocol is hardware-independent. Most of the logic of this protocol
18   is architecturally neutral. It abstracts the multi-processor environment and
19   status of processors, and provides interfaces to retrieve information, maintain,
20   and dispatch.
21 
22   MP Services Protocol may be consumed by ACPI module. The ACPI module may use this
23   protocol to retrieve data that are needed for an MP platform and report them to OS.
24   MP Services Protocol may also be used to program and configure processors, such
25   as MTRR synchronization for memory space attributes setting in DXE Services.
26   MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
27   by taking advantage of the processing capabilities of the APs, for example, using
28   APs to help test system memory in parallel with other device initialization.
29   Diagnostics applications may also use this protocol for multi-processor.
30 
31 Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
32 Portitions Copyright (c) 2011, Apple Inc. All rights reserved.
33 This program and the accompanying materials are licensed and made available under
34 the terms and conditions of the BSD License that accompanies this distribution.
35 The full text of the license may be found at
36 http://opensource.org/licenses/bsd-license.php.
37 
38 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
39 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
40 
41 
42 **/
43 
44 #include "CpuDriver.h"
45 
46 
47 MP_SYSTEM_DATA                gMPSystem;
48 EMU_THREAD_THUNK_PROTOCOL     *gThread = NULL;
49 EFI_EVENT                     gReadToBootEvent;
50 BOOLEAN                       gReadToBoot = FALSE;
51 UINTN                         gPollInterval;
52 
53 
54 BOOLEAN
IsBSP(VOID)55 IsBSP (
56   VOID
57   )
58 {
59   EFI_STATUS  Status;
60   UINTN       ProcessorNumber;
61 
62   Status = CpuMpServicesWhoAmI (&mMpServicesTemplate, &ProcessorNumber);
63   if (EFI_ERROR (Status)) {
64     return FALSE;
65   }
66 
67   return (gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
68 }
69 
70 
71 VOID
SetApProcedure(IN PROCESSOR_DATA_BLOCK * Processor,IN EFI_AP_PROCEDURE Procedure,IN VOID * ProcedureArgument)72 SetApProcedure (
73   IN   PROCESSOR_DATA_BLOCK  *Processor,
74   IN   EFI_AP_PROCEDURE      Procedure,
75   IN   VOID                  *ProcedureArgument
76   )
77 {
78   gThread->MutexLock (Processor->ProcedureLock);
79   Processor->Parameter  = ProcedureArgument;
80   Processor->Procedure  = Procedure;
81   gThread->MutexUnlock (Processor->ProcedureLock);
82 }
83 
84 
85 EFI_STATUS
GetNextBlockedNumber(OUT UINTN * NextNumber)86 GetNextBlockedNumber (
87   OUT UINTN                               *NextNumber
88   )
89 {
90   UINTN                 Number;
91   PROCESSOR_STATE       ProcessorState;
92   PROCESSOR_DATA_BLOCK  *Data;
93 
94   for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
95     Data = &gMPSystem.ProcessorData[Number];
96     if ((Data->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
97       // Skip BSP
98       continue;
99     }
100 
101     gThread->MutexLock (Data->StateLock);
102     ProcessorState = Data->State;
103     gThread->MutexUnlock (Data->StateLock);
104 
105     if (ProcessorState == CPU_STATE_BLOCKED) {
106       *NextNumber = Number;
107       return EFI_SUCCESS;
108     }
109   }
110 
111   return EFI_NOT_FOUND;
112 }
113 
114 /**
115  * Calculated and stalled the interval time by BSP to check whether
116  * the APs have finished.
117  *
118  * @param[in]  Timeout    The time limit in microseconds for
119  *                        APs to return from Procedure.
120  *
121  * @retval     StallTime  Time of execution stall.
122 **/
123 UINTN
CalculateAndStallInterval(IN UINTN Timeout)124 CalculateAndStallInterval (
125   IN UINTN                  Timeout
126   )
127 {
128   UINTN                 StallTime;
129 
130   if (Timeout < gPollInterval && Timeout != 0) {
131     StallTime = Timeout;
132   } else {
133     StallTime = gPollInterval;
134   }
135   gBS->Stall (StallTime);
136 
137   return StallTime;
138 }
139 
140 /**
141   This service retrieves the number of logical processor in the platform
142   and the number of those logical processors that are enabled on this boot.
143   This service may only be called from the BSP.
144 
145   This function is used to retrieve the following information:
146     - The number of logical processors that are present in the system.
147     - The number of enabled logical processors in the system at the instant
148       this call is made.
149 
150   Because MP Service Protocol provides services to enable and disable processors
151   dynamically, the number of enabled logical processors may vary during the
152   course of a boot session.
153 
154   If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
155   If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
156   EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
157   is returned in NumberOfProcessors, the number of currently enabled processor
158   is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
159 
160   @param[in]  This                        A pointer to the EFI_MP_SERVICES_PROTOCOL
161                                           instance.
162   @param[out] NumberOfProcessors          Pointer to the total number of logical
163                                           processors in the system, including the BSP
164                                           and disabled APs.
165   @param[out] NumberOfEnabledProcessors   Pointer to the number of enabled logical
166                                           processors that exist in system, including
167                                           the BSP.
168 
169   @retval EFI_SUCCESS             The number of logical processors and enabled
170                                   logical processors was retrieved.
171   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
172   @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
173   @retval EFI_INVALID_PARAMETER   NumberOfEnabledProcessors is NULL.
174 
175 **/
176 EFI_STATUS
177 EFIAPI
CpuMpServicesGetNumberOfProcessors(IN EFI_MP_SERVICES_PROTOCOL * This,OUT UINTN * NumberOfProcessors,OUT UINTN * NumberOfEnabledProcessors)178 CpuMpServicesGetNumberOfProcessors (
179   IN  EFI_MP_SERVICES_PROTOCOL  *This,
180   OUT UINTN                     *NumberOfProcessors,
181   OUT UINTN                     *NumberOfEnabledProcessors
182   )
183 {
184   if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
185     return EFI_INVALID_PARAMETER;
186   }
187 
188   if (!IsBSP ()) {
189     return EFI_DEVICE_ERROR;
190   }
191 
192   *NumberOfProcessors        = gMPSystem.NumberOfProcessors;
193   *NumberOfEnabledProcessors = gMPSystem.NumberOfEnabledProcessors;
194   return EFI_SUCCESS;
195 }
196 
197 
198 
199 /**
200   Gets detailed MP-related information on the requested processor at the
201   instant this call is made. This service may only be called from the BSP.
202 
203   This service retrieves detailed MP-related information about any processor
204   on the platform. Note the following:
205     - The processor information may change during the course of a boot session.
206     - The information presented here is entirely MP related.
207 
208   Information regarding the number of caches and their sizes, frequency of operation,
209   slot numbers is all considered platform-related information and is not provided
210   by this service.
211 
212   @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
213                                     instance.
214   @param[in]  ProcessorNumber       The handle number of processor.
215   @param[out] ProcessorInfoBuffer   A pointer to the buffer where information for
216                                     the requested processor is deposited.
217 
218   @retval EFI_SUCCESS             Processor information was returned.
219   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
220   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
221   @retval EFI_NOT_FOUND           The processor with the handle specified by
222                                   ProcessorNumber does not exist in the platform.
223 
224 **/
225 EFI_STATUS
226 EFIAPI
CpuMpServicesGetProcessorInfo(IN EFI_MP_SERVICES_PROTOCOL * This,IN UINTN ProcessorNumber,OUT EFI_PROCESSOR_INFORMATION * ProcessorInfoBuffer)227 CpuMpServicesGetProcessorInfo (
228   IN  EFI_MP_SERVICES_PROTOCOL   *This,
229   IN  UINTN                      ProcessorNumber,
230   OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
231   )
232 {
233   if (ProcessorInfoBuffer == NULL) {
234     return EFI_INVALID_PARAMETER;
235   }
236 
237   if (!IsBSP ()) {
238     return EFI_DEVICE_ERROR;
239   }
240 
241   if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
242     return EFI_NOT_FOUND;
243   }
244 
245   CopyMem (ProcessorInfoBuffer, &gMPSystem.ProcessorData[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
246   return EFI_SUCCESS;
247 }
248 
249 
250 /**
251   This service executes a caller provided function on all enabled APs. APs can
252   run either simultaneously or one at a time in sequence. This service supports
253   both blocking and non-blocking requests. The non-blocking requests use EFI
254   events so the BSP can detect when the APs have finished. This service may only
255   be called from the BSP.
256 
257   This function is used to dispatch all the enabled APs to the function specified
258   by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
259   immediately and Procedure is not started on any AP.
260 
261   If SingleThread is TRUE, all the enabled APs execute the function specified by
262   Procedure one by one, in ascending order of processor handle number. Otherwise,
263   all the enabled APs execute the function specified by Procedure simultaneously.
264 
265   If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
266   APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
267   mode, and the BSP returns from this service without waiting for APs. If a
268   non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
269   is signaled, then EFI_UNSUPPORTED must be returned.
270 
271   If the timeout specified by TimeoutInMicroseconds expires before all APs return
272   from Procedure, then Procedure on the failed APs is terminated. All enabled APs
273   are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
274   and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
275   content points to the list of processor handle numbers in which Procedure was
276   terminated.
277 
278   Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
279   to make sure that the nature of the code that is executed on the BSP and the
280   dispatched APs is well controlled. The MP Services Protocol does not guarantee
281   that the Procedure function is MP-safe. Hence, the tasks that can be run in
282   parallel are limited to certain independent tasks and well-controlled exclusive
283   code. EFI services and protocols may not be called by APs unless otherwise
284   specified.
285 
286   In blocking execution mode, BSP waits until all APs finish or
287   TimeoutInMicroseconds expires.
288 
289   In non-blocking execution mode, BSP is freed to return to the caller and then
290   proceed to the next task without having to wait for APs. The following
291   sequence needs to occur in a non-blocking execution mode:
292 
293     -# The caller that intends to use this MP Services Protocol in non-blocking
294        mode creates WaitEvent by calling the EFI CreateEvent() service.  The caller
295        invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
296        is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
297        the function specified by Procedure to be started on all the enabled APs,
298        and releases the BSP to continue with other tasks.
299     -# The caller can use the CheckEvent() and WaitForEvent() services to check
300        the state of the WaitEvent created in step 1.
301     -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
302        Service signals WaitEvent by calling the EFI SignalEvent() function. If
303        FailedCpuList is not NULL, its content is available when WaitEvent is
304        signaled. If all APs returned from Procedure prior to the timeout, then
305        FailedCpuList is set to NULL. If not all APs return from Procedure before
306        the timeout, then FailedCpuList is filled in with the list of the failed
307        APs. The buffer is allocated by MP Service Protocol using AllocatePool().
308        It is the caller's responsibility to free the buffer with FreePool() service.
309     -# This invocation of SignalEvent() function informs the caller that invoked
310        EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
311        the specified task or a timeout occurred. The contents of FailedCpuList
312        can be examined to determine which APs did not complete the specified task
313        prior to the timeout.
314 
315   @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
316                                       instance.
317   @param[in]  Procedure               A pointer to the function to be run on
318                                       enabled APs of the system. See type
319                                       EFI_AP_PROCEDURE.
320   @param[in]  SingleThread            If TRUE, then all the enabled APs execute
321                                       the function specified by Procedure one by
322                                       one, in ascending order of processor handle
323                                       number.  If FALSE, then all the enabled APs
324                                       execute the function specified by Procedure
325                                       simultaneously.
326   @param[in]  WaitEvent               The event created by the caller with CreateEvent()
327                                       service.  If it is NULL, then execute in
328                                       blocking mode. BSP waits until all APs finish
329                                       or TimeoutInMicroseconds expires.  If it's
330                                       not NULL, then execute in non-blocking mode.
331                                       BSP requests the function specified by
332                                       Procedure to be started on all the enabled
333                                       APs, and go on executing immediately. If
334                                       all return from Procedure, or TimeoutInMicroseconds
335                                       expires, this event is signaled. The BSP
336                                       can use the CheckEvent() or WaitForEvent()
337                                       services to check the state of event.  Type
338                                       EFI_EVENT is defined in CreateEvent() in
339                                       the Unified Extensible Firmware Interface
340                                       Specification.
341   @param[in]  TimeoutInMicrosecsond   Indicates the time limit in microseconds for
342                                       APs to return from Procedure, either for
343                                       blocking or non-blocking mode. Zero means
344                                       infinity.  If the timeout expires before
345                                       all APs return from Procedure, then Procedure
346                                       on the failed APs is terminated. All enabled
347                                       APs are available for next function assigned
348                                       by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
349                                       or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
350                                       If the timeout expires in blocking mode,
351                                       BSP returns EFI_TIMEOUT.  If the timeout
352                                       expires in non-blocking mode, WaitEvent
353                                       is signaled with SignalEvent().
354   @param[in]  ProcedureArgument       The parameter passed into Procedure for
355                                       all APs.
356   @param[out] FailedCpuList           If NULL, this parameter is ignored. Otherwise,
357                                       if all APs finish successfully, then its
358                                       content is set to NULL. If not all APs
359                                       finish before timeout expires, then its
360                                       content is set to address of the buffer
361                                       holding handle numbers of the failed APs.
362                                       The buffer is allocated by MP Service Protocol,
363                                       and it's the caller's responsibility to
364                                       free the buffer with FreePool() service.
365                                       In blocking mode, it is ready for consumption
366                                       when the call returns. In non-blocking mode,
367                                       it is ready when WaitEvent is signaled.  The
368                                       list of failed CPU is terminated by
369                                       END_OF_CPU_LIST.
370 
371   @retval EFI_SUCCESS             In blocking mode, all APs have finished before
372                                   the timeout expired.
373   @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched
374                                   to all enabled APs.
375   @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
376                                   UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
377                                   signaled.
378   @retval EFI_DEVICE_ERROR        Caller processor is AP.
379   @retval EFI_NOT_STARTED         No enabled APs exist in the system.
380   @retval EFI_NOT_READY           Any enabled APs are busy.
381   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
382                                   all enabled APs have finished.
383   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
384 
385 **/
386 EFI_STATUS
387 EFIAPI
CpuMpServicesStartupAllAps(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)388 CpuMpServicesStartupAllAps (
389   IN  EFI_MP_SERVICES_PROTOCOL  *This,
390   IN  EFI_AP_PROCEDURE          Procedure,
391   IN  BOOLEAN                   SingleThread,
392   IN  EFI_EVENT                 WaitEvent               OPTIONAL,
393   IN  UINTN                     TimeoutInMicroseconds,
394   IN  VOID                      *ProcedureArgument      OPTIONAL,
395   OUT UINTN                     **FailedCpuList         OPTIONAL
396   )
397 {
398   EFI_STATUS            Status;
399   PROCESSOR_DATA_BLOCK  *ProcessorData;
400   UINTN                 Number;
401   UINTN                 NextNumber;
402   PROCESSOR_STATE       APInitialState;
403   PROCESSOR_STATE       ProcessorState;
404   UINTN                 Timeout;
405 
406 
407   if (!IsBSP ()) {
408     return EFI_DEVICE_ERROR;
409   }
410 
411   if (gMPSystem.NumberOfProcessors == 1) {
412     return EFI_NOT_STARTED;
413   }
414 
415   if (Procedure == NULL) {
416     return EFI_INVALID_PARAMETER;
417   }
418 
419   if ((WaitEvent != NULL)  && gReadToBoot) {
420     return EFI_UNSUPPORTED;
421   }
422 
423   for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
424     ProcessorData = &gMPSystem.ProcessorData[Number];
425     if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
426       // Skip BSP
427       continue;
428     }
429 
430     if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
431       // Skip Disabled processors
432       continue;
433     }
434     gThread->MutexLock(ProcessorData->StateLock);
435     if (ProcessorData->State != CPU_STATE_IDLE) {
436       gThread->MutexUnlock (ProcessorData->StateLock);
437       return EFI_NOT_READY;
438     }
439     gThread->MutexUnlock(ProcessorData->StateLock);
440   }
441 
442   if (FailedCpuList != NULL) {
443     gMPSystem.FailedList = AllocatePool ((gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN));
444     if (gMPSystem.FailedList == NULL) {
445       return EFI_OUT_OF_RESOURCES;
446     }
447     SetMemN (gMPSystem.FailedList, (gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN), END_OF_CPU_LIST);
448     gMPSystem.FailedListIndex = 0;
449     *FailedCpuList = gMPSystem.FailedList;
450   }
451 
452   Timeout = TimeoutInMicroseconds;
453 
454   ProcessorData               = NULL;
455 
456   gMPSystem.FinishCount   = 0;
457   gMPSystem.StartCount    = 0;
458   gMPSystem.SingleThread  = SingleThread;
459   APInitialState          = CPU_STATE_READY;
460 
461   for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
462     ProcessorData = &gMPSystem.ProcessorData[Number];
463 
464     if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
465       // Skip BSP
466       continue;
467     }
468 
469     if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
470       // Skip Disabled processors
471       gMPSystem.FailedList[gMPSystem.FailedListIndex++] = Number;
472       continue;
473     }
474 
475     //
476     // Get APs prepared, and put failing APs into FailedCpuList
477     // if "SingleThread", only 1 AP will put to ready state, other AP will be put to ready
478     // state 1 by 1, until the previous 1 finished its task
479     // if not "SingleThread", all APs are put to ready state from the beginning
480     //
481     gThread->MutexLock(ProcessorData->StateLock);
482     ASSERT (ProcessorData->State == CPU_STATE_IDLE);
483     ProcessorData->State = APInitialState;
484     gThread->MutexUnlock (ProcessorData->StateLock);
485 
486     gMPSystem.StartCount++;
487     if (SingleThread) {
488       APInitialState = CPU_STATE_BLOCKED;
489     }
490   }
491 
492   if (WaitEvent != NULL) {
493     for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
494       ProcessorData = &gMPSystem.ProcessorData[Number];
495       if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
496        // Skip BSP
497         continue;
498       }
499 
500       if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
501         // Skip Disabled processors
502         continue;
503       }
504 
505       gThread->MutexLock (ProcessorData->StateLock);
506       ProcessorState = ProcessorData->State;
507       gThread->MutexUnlock (ProcessorData->StateLock);
508 
509       if (ProcessorState == CPU_STATE_READY) {
510         SetApProcedure (ProcessorData, Procedure, ProcedureArgument);
511       }
512     }
513 
514     //
515     // Save data into private data structure, and create timer to poll AP state before exiting
516     //
517     gMPSystem.Procedure         = Procedure;
518     gMPSystem.ProcedureArgument = ProcedureArgument;
519     gMPSystem.WaitEvent         = WaitEvent;
520     gMPSystem.Timeout           = TimeoutInMicroseconds;
521     gMPSystem.TimeoutActive     = (BOOLEAN)(TimeoutInMicroseconds != 0);
522     Status = gBS->SetTimer (
523                     gMPSystem.CheckAllAPsEvent,
524                     TimerPeriodic,
525                     gPollInterval
526                     );
527     return Status;
528 
529   }
530 
531   while (TRUE) {
532     for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
533       ProcessorData = &gMPSystem.ProcessorData[Number];
534       if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
535        // Skip BSP
536         continue;
537       }
538 
539       if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
540         // Skip Disabled processors
541         continue;
542       }
543 
544       gThread->MutexLock (ProcessorData->StateLock);
545       ProcessorState = ProcessorData->State;
546       gThread->MutexUnlock (ProcessorData->StateLock);
547 
548       switch (ProcessorState) {
549       case CPU_STATE_READY:
550         SetApProcedure (ProcessorData, Procedure, ProcedureArgument);
551         break;
552 
553       case CPU_STATE_FINISHED:
554         gMPSystem.FinishCount++;
555         if (SingleThread) {
556           Status = GetNextBlockedNumber (&NextNumber);
557           if (!EFI_ERROR (Status)) {
558             gThread->MutexLock (gMPSystem.ProcessorData[NextNumber].StateLock);
559             gMPSystem.ProcessorData[NextNumber].State = CPU_STATE_READY;
560             gThread->MutexUnlock (gMPSystem.ProcessorData[NextNumber].StateLock);
561           }
562         }
563 
564         gThread->MutexLock (ProcessorData->StateLock);
565         ProcessorData->State = CPU_STATE_IDLE;
566         gThread->MutexUnlock (ProcessorData->StateLock);
567 
568         break;
569 
570       default:
571         break;
572       }
573     }
574 
575     if (gMPSystem.FinishCount == gMPSystem.StartCount) {
576       Status = EFI_SUCCESS;
577       goto Done;
578     }
579 
580     if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
581       Status = EFI_TIMEOUT;
582       goto Done;
583     }
584 
585     Timeout -= CalculateAndStallInterval (Timeout);
586   }
587 
588 Done:
589   if (FailedCpuList != NULL) {
590     if (gMPSystem.FailedListIndex == 0) {
591       FreePool (*FailedCpuList);
592       *FailedCpuList = NULL;
593     }
594   }
595 
596   return EFI_SUCCESS;
597 }
598 
599 
600 /**
601   This service lets the caller get one enabled AP to execute a caller-provided
602   function. The caller can request the BSP to either wait for the completion
603   of the AP or just proceed with the next task by using the EFI event mechanism.
604   See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
605   execution support.  This service may only be called from the BSP.
606 
607   This function is used to dispatch one enabled AP to the function specified by
608   Procedure passing in the argument specified by ProcedureArgument.  If WaitEvent
609   is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
610   TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
611   BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
612   is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
613   then EFI_UNSUPPORTED must be returned.
614 
615   If the timeout specified by TimeoutInMicroseconds expires before the AP returns
616   from Procedure, then execution of Procedure by the AP is terminated. The AP is
617   available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
618   EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
619 
620   @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
621                                       instance.
622   @param[in]  Procedure               A pointer to the function to be run on
623                                       enabled APs of the system. See type
624                                       EFI_AP_PROCEDURE.
625   @param[in]  ProcessorNumber         The handle number of the AP. The range is
626                                       from 0 to the total number of logical
627                                       processors minus 1. The total number of
628                                       logical processors can be retrieved by
629                                       EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
630   @param[in]  WaitEvent               The event created by the caller with CreateEvent()
631                                       service.  If it is NULL, then execute in
632                                       blocking mode. BSP waits until all APs finish
633                                       or TimeoutInMicroseconds expires.  If it's
634                                       not NULL, then execute in non-blocking mode.
635                                       BSP requests the function specified by
636                                       Procedure to be started on all the enabled
637                                       APs, and go on executing immediately. If
638                                       all return from Procedure or TimeoutInMicroseconds
639                                       expires, this event is signaled. The BSP
640                                       can use the CheckEvent() or WaitForEvent()
641                                       services to check the state of event.  Type
642                                       EFI_EVENT is defined in CreateEvent() in
643                                       the Unified Extensible Firmware Interface
644                                       Specification.
645   @param[in]  TimeoutInMicrosecsond   Indicates the time limit in microseconds for
646                                       APs to return from Procedure, either for
647                                       blocking or non-blocking mode. Zero means
648                                       infinity.  If the timeout expires before
649                                       all APs return from Procedure, then Procedure
650                                       on the failed APs is terminated. All enabled
651                                       APs are available for next function assigned
652                                       by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
653                                       or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
654                                       If the timeout expires in blocking mode,
655                                       BSP returns EFI_TIMEOUT.  If the timeout
656                                       expires in non-blocking mode, WaitEvent
657                                       is signaled with SignalEvent().
658   @param[in]  ProcedureArgument       The parameter passed into Procedure for
659                                       all APs.
660   @param[out] Finished                If NULL, this parameter is ignored.  In
661                                       blocking mode, this parameter is ignored.
662                                       In non-blocking mode, if AP returns from
663                                       Procedure before the timeout expires, its
664                                       content is set to TRUE. Otherwise, the
665                                       value is set to FALSE. The caller can
666                                       determine if the AP returned from Procedure
667                                       by evaluating this value.
668 
669   @retval EFI_SUCCESS             In blocking mode, specified AP finished before
670                                   the timeout expires.
671   @retval EFI_SUCCESS             In non-blocking mode, the function has been
672                                   dispatched to specified AP.
673   @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
674                                   UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
675                                   signaled.
676   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
677   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
678                                   the specified AP has finished.
679   @retval EFI_NOT_READY           The specified AP is busy.
680   @retval EFI_NOT_FOUND           The processor with the handle specified by
681                                   ProcessorNumber does not exist.
682   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
683   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
684 
685 **/
686 EFI_STATUS
687 EFIAPI
CpuMpServicesStartupThisAP(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)688 CpuMpServicesStartupThisAP (
689   IN  EFI_MP_SERVICES_PROTOCOL  *This,
690   IN  EFI_AP_PROCEDURE          Procedure,
691   IN  UINTN                     ProcessorNumber,
692   IN  EFI_EVENT                 WaitEvent               OPTIONAL,
693   IN  UINTN                     TimeoutInMicroseconds,
694   IN  VOID                      *ProcedureArgument      OPTIONAL,
695   OUT BOOLEAN                   *Finished               OPTIONAL
696   )
697 {
698   UINTN            Timeout;
699 
700   if (!IsBSP ()) {
701     return EFI_DEVICE_ERROR;
702   }
703 
704   if (Procedure == NULL) {
705     return EFI_INVALID_PARAMETER;
706   }
707 
708   if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
709     return EFI_NOT_FOUND;
710   }
711 
712   if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
713     return EFI_INVALID_PARAMETER;
714   }
715 
716   if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
717     return EFI_INVALID_PARAMETER;
718   }
719 
720   gThread->MutexLock(gMPSystem.ProcessorData[ProcessorNumber].StateLock);
721   if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
722     gThread->MutexUnlock(gMPSystem.ProcessorData[ProcessorNumber].StateLock);
723     return EFI_NOT_READY;
724   }
725   gThread->MutexUnlock(gMPSystem.ProcessorData[ProcessorNumber].StateLock);
726 
727   if ((WaitEvent != NULL)  && gReadToBoot) {
728     return EFI_UNSUPPORTED;
729   }
730 
731   Timeout = TimeoutInMicroseconds;
732 
733   gMPSystem.StartCount   = 1;
734   gMPSystem.FinishCount  = 0;
735 
736   SetApProcedure (&gMPSystem.ProcessorData[ProcessorNumber], Procedure, ProcedureArgument);
737 
738   if (WaitEvent != NULL) {
739     // Non Blocking
740     gMPSystem.WaitEvent = WaitEvent;
741     gBS->SetTimer (
742            gMPSystem.ProcessorData[ProcessorNumber].CheckThisAPEvent,
743            TimerPeriodic,
744            gPollInterval
745            );
746     return EFI_SUCCESS;
747   }
748 
749   // Blocking
750   while (TRUE) {
751     gThread->MutexLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
752     if (gMPSystem.ProcessorData[ProcessorNumber].State == CPU_STATE_FINISHED) {
753       gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;
754       gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
755       break;
756     }
757 
758     gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
759 
760     if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
761       return EFI_TIMEOUT;
762     }
763 
764     Timeout -= CalculateAndStallInterval (Timeout);
765   }
766 
767   return EFI_SUCCESS;
768 
769 }
770 
771 
772 /**
773   This service switches the requested AP to be the BSP from that point onward.
774   This service changes the BSP for all purposes.   This call can only be performed
775   by the current BSP.
776 
777   This service switches the requested AP to be the BSP from that point onward.
778   This service changes the BSP for all purposes. The new BSP can take over the
779   execution of the old BSP and continue seamlessly from where the old one left
780   off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
781   is signaled.
782 
783   If the BSP cannot be switched prior to the return from this service, then
784   EFI_UNSUPPORTED must be returned.
785 
786   @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
787   @param[in] ProcessorNumber   The handle number of AP that is to become the new
788                                BSP. The range is from 0 to the total number of
789                                logical processors minus 1. The total number of
790                                logical processors can be retrieved by
791                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
792   @param[in] EnableOldBSP      If TRUE, then the old BSP will be listed as an
793                                enabled AP. Otherwise, it will be disabled.
794 
795   @retval EFI_SUCCESS             BSP successfully switched.
796   @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
797                                   this service returning.
798   @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
799   @retval EFI_SUCCESS             The calling processor is an AP.
800   @retval EFI_NOT_FOUND           The processor with the handle specified by
801                                   ProcessorNumber does not exist.
802   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
803                                   a disabled AP.
804   @retval EFI_NOT_READY           The specified AP is busy.
805 
806 **/
807 EFI_STATUS
808 EFIAPI
CpuMpServicesSwitchBSP(IN EFI_MP_SERVICES_PROTOCOL * This,IN UINTN ProcessorNumber,IN BOOLEAN EnableOldBSP)809 CpuMpServicesSwitchBSP (
810   IN EFI_MP_SERVICES_PROTOCOL  *This,
811   IN  UINTN                    ProcessorNumber,
812   IN  BOOLEAN                  EnableOldBSP
813   )
814 {
815   UINTN   Index;
816 
817   if (!IsBSP ()) {
818     return EFI_DEVICE_ERROR;
819   }
820 
821   if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
822     return EFI_NOT_FOUND;
823   }
824 
825   if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
826     return EFI_INVALID_PARAMETER;
827   }
828 
829   if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
830     return EFI_INVALID_PARAMETER;
831   }
832 
833   for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
834     if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
835       break;
836     }
837   }
838   ASSERT (Index != gMPSystem.NumberOfProcessors);
839 
840   gThread->MutexLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
841   if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
842     gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
843     return EFI_NOT_READY;
844   }
845   gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
846 
847   // Skip for now as we need switch a bunch of stack stuff around and it's complex
848   // May not be worth it?
849   return EFI_NOT_READY;
850 }
851 
852 
853 /**
854   This service lets the caller enable or disable an AP from this point onward.
855   This service may only be called from the BSP.
856 
857   This service allows the caller enable or disable an AP from this point onward.
858   The caller can optionally specify the health status of the AP by Health. If
859   an AP is being disabled, then the state of the disabled AP is implementation
860   dependent. If an AP is enabled, then the implementation must guarantee that a
861   complete initialization sequence is performed on the AP, so the AP is in a state
862   that is compatible with an MP operating system. This service may not be supported
863   after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
864 
865   If the enable or disable AP operation cannot be completed prior to the return
866   from this service, then EFI_UNSUPPORTED must be returned.
867 
868   @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
869   @param[in] ProcessorNumber   The handle number of AP that is to become the new
870                                BSP. The range is from 0 to the total number of
871                                logical processors minus 1. The total number of
872                                logical processors can be retrieved by
873                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
874   @param[in] EnableAP          Specifies the new state for the processor for
875                                enabled, FALSE for disabled.
876   @param[in] HealthFlag        If not NULL, a pointer to a value that specifies
877                                the new health status of the AP. This flag
878                                corresponds to StatusFlag defined in
879                                EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
880                                the PROCESSOR_HEALTH_STATUS_BIT is used. All other
881                                bits are ignored.  If it is NULL, this parameter
882                                is ignored.
883 
884   @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
885   @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed
886                                   prior to this service returning.
887   @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
888   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
889   @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
890                                   does not exist.
891   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
892 
893 **/
894 EFI_STATUS
895 EFIAPI
CpuMpServicesEnableDisableAP(IN EFI_MP_SERVICES_PROTOCOL * This,IN UINTN ProcessorNumber,IN BOOLEAN EnableAP,IN UINT32 * HealthFlag OPTIONAL)896 CpuMpServicesEnableDisableAP (
897   IN  EFI_MP_SERVICES_PROTOCOL  *This,
898   IN  UINTN                     ProcessorNumber,
899   IN  BOOLEAN                   EnableAP,
900   IN  UINT32                    *HealthFlag OPTIONAL
901   )
902 {
903   if (!IsBSP ()) {
904     return EFI_DEVICE_ERROR;
905   }
906 
907   if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
908     return EFI_NOT_FOUND;
909   }
910 
911   if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
912     return EFI_INVALID_PARAMETER;
913   }
914 
915   gThread->MutexLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
916   if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
917     gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
918     return EFI_UNSUPPORTED;
919   }
920   gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
921 
922   if (EnableAP) {
923     if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0 ) {
924       gMPSystem.NumberOfEnabledProcessors++;
925     }
926     gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_ENABLED_BIT;
927   } else {
928     if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == PROCESSOR_ENABLED_BIT ) {
929       gMPSystem.NumberOfEnabledProcessors--;
930     }
931     gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_ENABLED_BIT;
932   }
933 
934   if (HealthFlag != NULL) {
935     gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
936     gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
937   }
938 
939   return EFI_SUCCESS;
940 }
941 
942 
943 /**
944   This return the handle number for the calling processor.  This service may be
945   called from the BSP and APs.
946 
947   This service returns the processor handle number for the calling processor.
948   The returned value is in the range from 0 to the total number of logical
949   processors minus 1. The total number of logical processors can be retrieved
950   with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
951   called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
952   is returned. Otherwise, the current processors handle number is returned in
953   ProcessorNumber, and EFI_SUCCESS is returned.
954 
955   @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
956   @param[in] ProcessorNumber   The handle number of AP that is to become the new
957                                BSP. The range is from 0 to the total number of
958                                logical processors minus 1. The total number of
959                                logical processors can be retrieved by
960                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
961 
962   @retval EFI_SUCCESS             The current processor handle number was returned
963                                   in ProcessorNumber.
964   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
965 
966 **/
967 EFI_STATUS
968 EFIAPI
CpuMpServicesWhoAmI(IN EFI_MP_SERVICES_PROTOCOL * This,OUT UINTN * ProcessorNumber)969 CpuMpServicesWhoAmI (
970   IN EFI_MP_SERVICES_PROTOCOL  *This,
971   OUT UINTN                    *ProcessorNumber
972   )
973 {
974   UINTN   Index;
975   UINT64  ProcessorId;
976 
977   if (ProcessorNumber == NULL) {
978     return EFI_INVALID_PARAMETER;
979   }
980 
981   ProcessorId = gThread->Self ();
982   for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
983     if (gMPSystem.ProcessorData[Index].Info.ProcessorId == ProcessorId) {
984       break;
985     }
986   }
987 
988   *ProcessorNumber = Index;
989   return EFI_SUCCESS;
990 }
991 
992 
993 
994 EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
995   CpuMpServicesGetNumberOfProcessors,
996   CpuMpServicesGetProcessorInfo,
997   CpuMpServicesStartupAllAps,
998   CpuMpServicesStartupThisAP,
999   CpuMpServicesSwitchBSP,
1000   CpuMpServicesEnableDisableAP,
1001   CpuMpServicesWhoAmI
1002 };
1003 
1004 
1005 
1006 /*++
1007   If timeout occurs in StartupAllAps(), a timer is set, which invokes this
1008   procedure periodically to check whether all APs have finished.
1009 
1010 
1011 --*/
1012 VOID
1013 EFIAPI
CpuCheckAllAPsStatus(IN EFI_EVENT Event,IN VOID * Context)1014 CpuCheckAllAPsStatus (
1015   IN  EFI_EVENT   Event,
1016   IN  VOID        *Context
1017   )
1018 {
1019   UINTN                 ProcessorNumber;
1020   UINTN                 NextNumber;
1021   PROCESSOR_DATA_BLOCK  *ProcessorData;
1022   PROCESSOR_DATA_BLOCK  *NextData;
1023   EFI_STATUS            Status;
1024   PROCESSOR_STATE       ProcessorState;
1025   UINTN                 Cpu;
1026   BOOLEAN               Found;
1027 
1028   if (gMPSystem.TimeoutActive) {
1029     gMPSystem.Timeout -= CalculateAndStallInterval (gMPSystem.Timeout);
1030   }
1031 
1032   for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; ProcessorNumber++) {
1033     ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
1034     if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
1035      // Skip BSP
1036       continue;
1037     }
1038 
1039     if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
1040       // Skip Disabled processors
1041       continue;
1042     }
1043 
1044     // This is an Interrupt Service routine.
1045     // This can grab a lock that is held in a non-interrupt
1046     // context. Meaning deadlock. Which is a bad thing.
1047     // So, try lock it. If we can get it, cool, do our thing.
1048     // otherwise, just dump out & try again on the next iteration.
1049     Status = gThread->MutexTryLock (ProcessorData->StateLock);
1050     if (EFI_ERROR(Status)) {
1051       return;
1052     }
1053     ProcessorState = ProcessorData->State;
1054     gThread->MutexUnlock (ProcessorData->StateLock);
1055 
1056     switch (ProcessorState) {
1057     case CPU_STATE_FINISHED:
1058       if (gMPSystem.SingleThread) {
1059         Status = GetNextBlockedNumber (&NextNumber);
1060         if (!EFI_ERROR (Status)) {
1061           NextData = &gMPSystem.ProcessorData[NextNumber];
1062 
1063           gThread->MutexLock (NextData->StateLock);
1064           NextData->State = CPU_STATE_READY;
1065           gThread->MutexUnlock (NextData->StateLock);
1066 
1067           SetApProcedure (NextData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);
1068         }
1069       }
1070 
1071       gThread->MutexLock (ProcessorData->StateLock);
1072       ProcessorData->State = CPU_STATE_IDLE;
1073       gThread->MutexUnlock (ProcessorData->StateLock);
1074       gMPSystem.FinishCount++;
1075       break;
1076 
1077     default:
1078       break;
1079     }
1080   }
1081 
1082   if (gMPSystem.TimeoutActive && gMPSystem.Timeout == 0) {
1083     //
1084     // Timeout
1085     //
1086     if (gMPSystem.FailedList != NULL) {
1087       for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; ProcessorNumber++) {
1088         ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
1089         if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
1090          // Skip BSP
1091           continue;
1092         }
1093 
1094         if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
1095           // Skip Disabled processors
1096           continue;
1097         }
1098 
1099         // Mark the
1100         Status = gThread->MutexTryLock (ProcessorData->StateLock);
1101         if (EFI_ERROR(Status)) {
1102           return;
1103         }
1104         ProcessorState = ProcessorData->State;
1105         gThread->MutexUnlock (ProcessorData->StateLock);
1106 
1107         if (ProcessorState != CPU_STATE_IDLE) {
1108           // If we are retrying make sure we don't double count
1109           for (Cpu = 0, Found = FALSE; Cpu < gMPSystem.NumberOfProcessors; Cpu++) {
1110             if (gMPSystem.FailedList[Cpu] == END_OF_CPU_LIST) {
1111               break;
1112             }
1113             if (gMPSystem.FailedList[ProcessorNumber] == Cpu) {
1114               Found = TRUE;
1115               break;
1116             }
1117           }
1118           if (!Found) {
1119             gMPSystem.FailedList[gMPSystem.FailedListIndex++] = Cpu;
1120           }
1121         }
1122       }
1123     }
1124     // Force terminal exit
1125     gMPSystem.FinishCount = gMPSystem.StartCount;
1126   }
1127 
1128   if (gMPSystem.FinishCount != gMPSystem.StartCount) {
1129     return;
1130   }
1131 
1132   gBS->SetTimer (
1133          gMPSystem.CheckAllAPsEvent,
1134          TimerCancel,
1135          0
1136          );
1137 
1138   if (gMPSystem.FailedListIndex == 0) {
1139     if (gMPSystem.FailedList != NULL) {
1140       FreePool (gMPSystem.FailedList);
1141       gMPSystem.FailedList = NULL;
1142     }
1143   }
1144 
1145   Status = gBS->SignalEvent (gMPSystem.WaitEvent);
1146 
1147   return ;
1148 }
1149 
1150 VOID
1151 EFIAPI
CpuCheckThisAPStatus(IN EFI_EVENT Event,IN VOID * Context)1152 CpuCheckThisAPStatus (
1153   IN  EFI_EVENT                           Event,
1154   IN  VOID                                *Context
1155   )
1156 {
1157   EFI_STATUS            Status;
1158   PROCESSOR_DATA_BLOCK  *ProcessorData;
1159   PROCESSOR_STATE       ProcessorState;
1160 
1161   ProcessorData = (PROCESSOR_DATA_BLOCK *) Context;
1162 
1163   //
1164   // This is an Interrupt Service routine.
1165   // that can grab a lock that is held in a non-interrupt
1166   // context. Meaning deadlock. Which is a badddd thing.
1167   // So, try lock it. If we can get it, cool, do our thing.
1168   // otherwise, just dump out & try again on the next iteration.
1169   //
1170   Status = gThread->MutexTryLock (ProcessorData->StateLock);
1171   if (EFI_ERROR(Status)) {
1172     return;
1173   }
1174   ProcessorState = ProcessorData->State;
1175   gThread->MutexUnlock (ProcessorData->StateLock);
1176 
1177   if (ProcessorState == CPU_STATE_FINISHED) {
1178     Status = gBS->SetTimer (ProcessorData->CheckThisAPEvent, TimerCancel, 0);
1179     ASSERT_EFI_ERROR (Status);
1180 
1181     Status = gBS->SignalEvent (gMPSystem.WaitEvent);
1182     ASSERT_EFI_ERROR (Status);
1183 
1184     gThread->MutexLock (ProcessorData->StateLock);
1185     ProcessorData->State = CPU_STATE_IDLE;
1186     gThread->MutexUnlock (ProcessorData->StateLock);
1187   }
1188 
1189   return ;
1190 }
1191 
1192 
1193 /*++
1194   This function is called by all processors (both BSP and AP) once and collects MP related data
1195 
1196   MPSystemData  - Pointer to the data structure containing MP related data
1197   BSP           - TRUE if the CPU is BSP
1198 
1199   EFI_SUCCESS   - Data for the processor collected and filled in
1200 
1201 --*/
1202 EFI_STATUS
FillInProcessorInformation(IN BOOLEAN BSP,IN UINTN ProcessorNumber)1203 FillInProcessorInformation (
1204   IN     BOOLEAN              BSP,
1205   IN     UINTN                ProcessorNumber
1206   )
1207 {
1208   gMPSystem.ProcessorData[ProcessorNumber].Info.ProcessorId  = gThread->Self ();
1209   gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag   = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
1210   if (BSP) {
1211     gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_AS_BSP_BIT;
1212   }
1213 
1214   gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Package = (UINT32) ProcessorNumber;
1215   gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Core    = 0;
1216   gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Thread  = 0;
1217   gMPSystem.ProcessorData[ProcessorNumber].State = BSP ? CPU_STATE_BUSY : CPU_STATE_IDLE;
1218 
1219   gMPSystem.ProcessorData[ProcessorNumber].Procedure        = NULL;
1220   gMPSystem.ProcessorData[ProcessorNumber].Parameter        = NULL;
1221   gMPSystem.ProcessorData[ProcessorNumber].StateLock        = gThread->MutexInit ();
1222   gMPSystem.ProcessorData[ProcessorNumber].ProcedureLock    = gThread->MutexInit ();
1223 
1224   return EFI_SUCCESS;
1225 }
1226 
1227 VOID *
1228 EFIAPI
CpuDriverApIdolLoop(VOID * Context)1229 CpuDriverApIdolLoop (
1230   VOID  *Context
1231   )
1232 {
1233   EFI_AP_PROCEDURE      Procedure;
1234   VOID                  *Parameter;
1235   UINTN                 ProcessorNumber;
1236   PROCESSOR_DATA_BLOCK  *ProcessorData;
1237 
1238   ProcessorNumber = (UINTN)Context;
1239   ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
1240 
1241   ProcessorData->Info.ProcessorId = gThread->Self ();
1242 
1243   while (TRUE) {
1244     //
1245     // Make a local copy on the stack to be extra safe
1246     //
1247     gThread->MutexLock (ProcessorData->ProcedureLock);
1248     Procedure = ProcessorData->Procedure;
1249     Parameter = ProcessorData->Parameter;
1250     gThread->MutexUnlock (ProcessorData->ProcedureLock);
1251 
1252     if (Procedure != NULL) {
1253       gThread->MutexLock (ProcessorData->StateLock);
1254       ProcessorData->State = CPU_STATE_BUSY;
1255       gThread->MutexUnlock (ProcessorData->StateLock);
1256 
1257       Procedure (Parameter);
1258 
1259       gThread->MutexLock (ProcessorData->ProcedureLock);
1260       ProcessorData->Procedure = NULL;
1261       gThread->MutexUnlock (ProcessorData->ProcedureLock);
1262 
1263       gThread->MutexLock (ProcessorData->StateLock);
1264       ProcessorData->State = CPU_STATE_FINISHED;
1265       gThread->MutexUnlock (ProcessorData->StateLock);
1266     }
1267 
1268     // Poll 5 times a seconds, 200ms
1269     // Don't want to burn too many system resources doing nothing.
1270     gEmuThunk->Sleep (200 * 1000);
1271   }
1272 
1273   return 0;
1274 }
1275 
1276 
1277 EFI_STATUS
InitializeMpSystemData(IN UINTN NumberOfProcessors)1278 InitializeMpSystemData (
1279   IN   UINTN     NumberOfProcessors
1280   )
1281 {
1282   EFI_STATUS              Status;
1283   UINTN                   Index;
1284 
1285 
1286   //
1287   // Clear the data structure area first.
1288   //
1289   ZeroMem (&gMPSystem, sizeof (MP_SYSTEM_DATA));
1290 
1291   //
1292   // First BSP fills and inits all known values, including it's own records.
1293   //
1294   gMPSystem.NumberOfProcessors         = NumberOfProcessors;
1295   gMPSystem.NumberOfEnabledProcessors  = NumberOfProcessors;
1296 
1297   gMPSystem.ProcessorData = AllocateZeroPool (gMPSystem.NumberOfProcessors * sizeof (PROCESSOR_DATA_BLOCK));
1298   ASSERT (gMPSystem.ProcessorData != NULL);
1299 
1300   FillInProcessorInformation (TRUE, 0);
1301 
1302   Status = gBS->CreateEvent (
1303                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
1304                   TPL_CALLBACK,
1305                   CpuCheckAllAPsStatus,
1306                   NULL,
1307                   &gMPSystem.CheckAllAPsEvent
1308                   );
1309   ASSERT_EFI_ERROR (Status);
1310 
1311 
1312   for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
1313     if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
1314      // Skip BSP
1315       continue;
1316     }
1317 
1318     FillInProcessorInformation (FALSE, Index);
1319 
1320     Status = gThread->CreateThread (
1321                         (VOID *)&gMPSystem.ProcessorData[Index].Info.ProcessorId,
1322                         NULL,
1323                         CpuDriverApIdolLoop,
1324                         (VOID *)Index
1325                         );
1326 
1327 
1328     Status = gBS->CreateEvent (
1329                          EVT_TIMER | EVT_NOTIFY_SIGNAL,
1330                          TPL_CALLBACK,
1331                          CpuCheckThisAPStatus,
1332                          (VOID *)  &gMPSystem.ProcessorData[Index],
1333                          &gMPSystem.ProcessorData[Index].CheckThisAPEvent
1334                          );
1335   }
1336 
1337   return EFI_SUCCESS;
1338 }
1339 
1340 
1341 
1342 /**
1343   Invoke a notification event
1344 
1345   @param  Event                 Event whose notification function is being invoked.
1346   @param  Context               The pointer to the notification function's context,
1347                                 which is implementation-dependent.
1348 
1349 **/
1350 VOID
1351 EFIAPI
CpuReadToBootFunction(IN EFI_EVENT Event,IN VOID * Context)1352 CpuReadToBootFunction (
1353   IN  EFI_EVENT                Event,
1354   IN  VOID                     *Context
1355   )
1356 {
1357   gReadToBoot = TRUE;
1358 }
1359 
1360 
1361 
1362 EFI_STATUS
CpuMpServicesInit(OUT UINTN * MaxCpus)1363 CpuMpServicesInit (
1364   OUT UINTN *MaxCpus
1365   )
1366 {
1367   EFI_STATUS              Status;
1368   EFI_HANDLE              Handle;
1369   EMU_IO_THUNK_PROTOCOL   *IoThunk;
1370 
1371   *MaxCpus = 1; // BSP
1372   IoThunk = GetIoThunkInstance (&gEmuThreadThunkProtocolGuid, 0);
1373   if (IoThunk != NULL) {
1374     Status = IoThunk->Open (IoThunk);
1375     if (!EFI_ERROR (Status)) {
1376       if (IoThunk->ConfigString != NULL) {
1377         *MaxCpus += StrDecimalToUintn (IoThunk->ConfigString);
1378         gThread = IoThunk->Interface;
1379       }
1380     }
1381   }
1382 
1383   if (*MaxCpus == 1) {
1384     // We are not MP so nothing to do
1385     return EFI_SUCCESS;
1386   }
1387 
1388   gPollInterval = (UINTN) PcdGet64 (PcdEmuMpServicesPollingInterval);
1389 
1390   Status  = InitializeMpSystemData (*MaxCpus);
1391   if (EFI_ERROR (Status)) {
1392     return Status;
1393   }
1394 
1395   Status = EfiCreateEventReadyToBootEx (TPL_CALLBACK, CpuReadToBootFunction, NULL, &gReadToBootEvent);
1396   ASSERT_EFI_ERROR (Status);
1397 
1398   //
1399   // Now install the MP services protocol.
1400   //
1401   Handle = NULL;
1402   Status = gBS->InstallMultipleProtocolInterfaces (
1403                   &Handle,
1404                   &gEfiMpServiceProtocolGuid,   &mMpServicesTemplate,
1405                   NULL
1406                   );
1407   return Status;
1408 }
1409 
1410 
1411