1 /** @file
2   MP initialize support functions for PEI phase.
3 
4   Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "MpLib.h"
10 #include <Library/PeiServicesLib.h>
11 #include <Guid/S3SmmInitDone.h>
12 
13 /**
14   S3 SMM Init Done notification function.
15 
16   @param  PeiServices      Indirect reference to the PEI Services Table.
17   @param  NotifyDesc       Address of the notification descriptor data structure.
18   @param  InvokePpi        Address of the PPI that was invoked.
19 
20   @retval EFI_SUCCESS      The function completes successfully.
21 
22 **/
23 EFI_STATUS
24 EFIAPI
25 NotifyOnS3SmmInitDonePpi (
26   IN  EFI_PEI_SERVICES                              **PeiServices,
27   IN  EFI_PEI_NOTIFY_DESCRIPTOR                     *NotifyDesc,
28   IN  VOID                                          *InvokePpi
29   );
30 
31 
32 //
33 // Global function
34 //
35 EFI_PEI_NOTIFY_DESCRIPTOR        mS3SmmInitDoneNotifyDesc = {
36   EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
37   &gEdkiiS3SmmInitDoneGuid,
38   NotifyOnS3SmmInitDonePpi
39 };
40 
41 /**
42   S3 SMM Init Done notification function.
43 
44   @param  PeiServices      Indirect reference to the PEI Services Table.
45   @param  NotifyDesc       Address of the notification descriptor data structure.
46   @param  InvokePpi        Address of the PPI that was invoked.
47 
48   @retval EFI_SUCCESS      The function completes successfully.
49 
50 **/
51 EFI_STATUS
52 EFIAPI
NotifyOnS3SmmInitDonePpi(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_NOTIFY_DESCRIPTOR * NotifyDesc,IN VOID * InvokePpi)53 NotifyOnS3SmmInitDonePpi (
54   IN  EFI_PEI_SERVICES                              **PeiServices,
55   IN  EFI_PEI_NOTIFY_DESCRIPTOR                     *NotifyDesc,
56   IN  VOID                                          *InvokePpi
57   )
58 {
59   CPU_MP_DATA     *CpuMpData;
60 
61   CpuMpData = GetCpuMpData ();
62 
63   //
64   // PiSmmCpuDxeSmm driver hardcode change the loop mode to HLT mode.
65   // So in this notify function, code need to check the current loop
66   // mode, if it is not HLT mode, code need to change loop mode back
67   // to the original mode.
68   //
69   if (CpuMpData->ApLoopMode != ApInHltLoop) {
70     CpuMpData->WakeUpByInitSipiSipi = TRUE;
71   }
72 
73   return EFI_SUCCESS;
74 }
75 
76 
77 /**
78   Enable Debug Agent to support source debugging on AP function.
79 
80 **/
81 VOID
EnableDebugAgent(VOID)82 EnableDebugAgent (
83   VOID
84   )
85 {
86 }
87 
88 /**
89   Get pointer to CPU MP Data structure.
90   For BSP, the pointer is retrieved from HOB.
91   For AP, the structure is just after IDT.
92 
93   @return  The pointer to CPU MP Data structure.
94 **/
95 CPU_MP_DATA *
GetCpuMpData(VOID)96 GetCpuMpData (
97   VOID
98   )
99 {
100   CPU_MP_DATA                  *CpuMpData;
101   MSR_IA32_APIC_BASE_REGISTER  ApicBaseMsr;
102   IA32_DESCRIPTOR              Idtr;
103 
104   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
105   if (ApicBaseMsr.Bits.BSP == 1) {
106     CpuMpData = GetCpuMpDataFromGuidedHob ();
107     ASSERT (CpuMpData != NULL);
108   } else {
109     AsmReadIdtr (&Idtr);
110     CpuMpData = (CPU_MP_DATA *) (Idtr.Base + Idtr.Limit + 1);
111   }
112   return CpuMpData;
113 }
114 
115 /**
116   Save the pointer to CPU MP Data structure.
117 
118   @param[in] CpuMpData  The pointer to CPU MP Data structure will be saved.
119 **/
120 VOID
SaveCpuMpData(IN CPU_MP_DATA * CpuMpData)121 SaveCpuMpData (
122   IN CPU_MP_DATA   *CpuMpData
123   )
124 {
125   UINT64           Data64;
126   //
127   // Build location of CPU MP DATA buffer in HOB
128   //
129   Data64 = (UINT64) (UINTN) CpuMpData;
130   BuildGuidDataHob (
131     &mCpuInitMpLibHobGuid,
132     (VOID *) &Data64,
133     sizeof (UINT64)
134     );
135 }
136 
137 /**
138   Check if AP wakeup buffer is overlapped with existing allocated buffer.
139 
140   @param[in]  WakeupBufferStart     AP wakeup buffer start address.
141   @param[in]  WakeupBufferEnd       AP wakeup buffer end address.
142 
143   @retval  TRUE       There is overlap.
144   @retval  FALSE      There is no overlap.
145 **/
146 BOOLEAN
CheckOverlapWithAllocatedBuffer(IN UINT64 WakeupBufferStart,IN UINT64 WakeupBufferEnd)147 CheckOverlapWithAllocatedBuffer (
148   IN UINT64               WakeupBufferStart,
149   IN UINT64               WakeupBufferEnd
150   )
151 {
152   EFI_PEI_HOB_POINTERS      Hob;
153   EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
154   BOOLEAN                   Overlapped;
155   UINT64                    MemoryStart;
156   UINT64                    MemoryEnd;
157 
158   Overlapped = FALSE;
159   //
160   // Get the HOB list for processing
161   //
162   Hob.Raw = GetHobList ();
163   //
164   // Collect memory ranges
165   //
166   while (!END_OF_HOB_LIST (Hob)) {
167     if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
168       MemoryHob   = Hob.MemoryAllocation;
169       MemoryStart = MemoryHob->AllocDescriptor.MemoryBaseAddress;
170       MemoryEnd   = MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength;
171       if (!((WakeupBufferStart >= MemoryEnd) || (WakeupBufferEnd <= MemoryStart))) {
172         Overlapped = TRUE;
173         break;
174       }
175     }
176     Hob.Raw = GET_NEXT_HOB (Hob);
177   }
178   return Overlapped;
179 }
180 
181 /**
182   Get available system memory below 1MB by specified size.
183 
184   @param[in] WakeupBufferSize   Wakeup buffer size required
185 
186   @retval other   Return wakeup buffer address below 1MB.
187   @retval -1      Cannot find free memory below 1MB.
188 **/
189 UINTN
GetWakeupBuffer(IN UINTN WakeupBufferSize)190 GetWakeupBuffer (
191   IN UINTN                WakeupBufferSize
192   )
193 {
194   EFI_PEI_HOB_POINTERS    Hob;
195   UINT64                  WakeupBufferStart;
196   UINT64                  WakeupBufferEnd;
197 
198   WakeupBufferSize = (WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1);
199 
200   //
201   // Get the HOB list for processing
202   //
203   Hob.Raw = GetHobList ();
204 
205   //
206   // Collect memory ranges
207   //
208   while (!END_OF_HOB_LIST (Hob)) {
209     if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
210       if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&
211           (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
212           ((Hob.ResourceDescriptor->ResourceAttribute &
213             (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
214              EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
215              EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
216              )) == 0)
217            ) {
218         //
219         // Need memory under 1MB to be collected here
220         //
221         WakeupBufferEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;
222         if (WakeupBufferEnd > BASE_1MB) {
223           //
224           // Wakeup buffer should be under 1MB
225           //
226           WakeupBufferEnd = BASE_1MB;
227         }
228         while (WakeupBufferEnd > WakeupBufferSize) {
229           //
230           // Wakeup buffer should be aligned on 4KB
231           //
232           WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);
233           if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {
234             break;
235           }
236           if (CheckOverlapWithAllocatedBuffer (WakeupBufferStart, WakeupBufferEnd)) {
237             //
238             // If this range is overlapped with existing allocated buffer, skip it
239             // and find the next range
240             //
241             WakeupBufferEnd -= WakeupBufferSize;
242             continue;
243           }
244           DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",
245                                WakeupBufferStart, WakeupBufferSize));
246           return (UINTN)WakeupBufferStart;
247         }
248       }
249     }
250     //
251     // Find the next HOB
252     //
253     Hob.Raw = GET_NEXT_HOB (Hob);
254   }
255 
256   return (UINTN) -1;
257 }
258 
259 /**
260   Get available EfiBootServicesCode memory below 4GB by specified size.
261 
262   This buffer is required to safely transfer AP from real address mode to
263   protected mode or long mode, due to the fact that the buffer returned by
264   GetWakeupBuffer() may be marked as non-executable.
265 
266   @param[in] BufferSize   Wakeup transition buffer size.
267 
268   @retval other   Return wakeup transition buffer address below 4GB.
269   @retval 0       Cannot find free memory below 4GB.
270 **/
271 UINTN
GetModeTransitionBuffer(IN UINTN BufferSize)272 GetModeTransitionBuffer (
273   IN UINTN                BufferSize
274   )
275 {
276   //
277   // PEI phase doesn't need to do such transition. So simply return 0.
278   //
279   return 0;
280 }
281 
282 /**
283   Checks APs status and updates APs status if needed.
284 
285 **/
286 VOID
CheckAndUpdateApsStatus(VOID)287 CheckAndUpdateApsStatus (
288   VOID
289   )
290 {
291 }
292 
293 /**
294   Initialize global data for MP support.
295 
296   @param[in] CpuMpData  The pointer to CPU MP Data structure.
297 **/
298 VOID
InitMpGlobalData(IN CPU_MP_DATA * CpuMpData)299 InitMpGlobalData (
300   IN CPU_MP_DATA               *CpuMpData
301   )
302 {
303   EFI_STATUS  Status;
304 
305   SaveCpuMpData (CpuMpData);
306 
307   ///
308   /// Install Notify
309   ///
310   Status = PeiServicesNotifyPpi (&mS3SmmInitDoneNotifyDesc);
311   ASSERT_EFI_ERROR (Status);
312 }
313 
314 /**
315   This service executes a caller provided function on all enabled APs.
316 
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]  TimeoutInMicroseconds   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 MpInitLibStartupAllAPs() or
349                                       MPInitLibStartupThisAP().
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 Initialization
363                                       library, 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_UNSUPPORTED         WaitEvent is not NULL if non-blocking mode is not
379                                   supported.
380   @retval EFI_DEVICE_ERROR        Caller processor is AP.
381   @retval EFI_NOT_STARTED         No enabled APs exist in the system.
382   @retval EFI_NOT_READY           Any enabled APs are busy.
383   @retval EFI_NOT_READY           MP Initialize Library is not initialized.
384   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
385                                   all enabled APs have finished.
386   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
387 
388 **/
389 EFI_STATUS
390 EFIAPI
MpInitLibStartupAllAPs(IN EFI_AP_PROCEDURE Procedure,IN BOOLEAN SingleThread,IN EFI_EVENT WaitEvent OPTIONAL,IN UINTN TimeoutInMicroseconds,IN VOID * ProcedureArgument OPTIONAL,OUT UINTN ** FailedCpuList OPTIONAL)391 MpInitLibStartupAllAPs (
392   IN  EFI_AP_PROCEDURE          Procedure,
393   IN  BOOLEAN                   SingleThread,
394   IN  EFI_EVENT                 WaitEvent               OPTIONAL,
395   IN  UINTN                     TimeoutInMicroseconds,
396   IN  VOID                      *ProcedureArgument      OPTIONAL,
397   OUT UINTN                     **FailedCpuList         OPTIONAL
398   )
399 {
400   if (WaitEvent != NULL) {
401     return EFI_UNSUPPORTED;
402   }
403 
404   return StartupAllAPsWorker (
405            Procedure,
406            SingleThread,
407            NULL,
408            TimeoutInMicroseconds,
409            ProcedureArgument,
410            FailedCpuList
411            );
412 }
413 
414 /**
415   This service lets the caller get one enabled AP to execute a caller-provided
416   function.
417 
418   @param[in]  Procedure               A pointer to the function to be run on the
419                                       designated AP of the system. See type
420                                       EFI_AP_PROCEDURE.
421   @param[in]  ProcessorNumber         The handle number of the AP. The range is
422                                       from 0 to the total number of logical
423                                       processors minus 1. The total number of
424                                       logical processors can be retrieved by
425                                       MpInitLibGetNumberOfProcessors().
426   @param[in]  WaitEvent               The event created by the caller with CreateEvent()
427                                       service.  If it is NULL, then execute in
428                                       blocking mode. BSP waits until this AP finish
429                                       or TimeoutInMicroSeconds expires.  If it's
430                                       not NULL, then execute in non-blocking mode.
431                                       BSP requests the function specified by
432                                       Procedure to be started on this AP,
433                                       and go on executing immediately. If this AP
434                                       return from Procedure or TimeoutInMicroSeconds
435                                       expires, this event is signaled. The BSP
436                                       can use the CheckEvent() or WaitForEvent()
437                                       services to check the state of event.  Type
438                                       EFI_EVENT is defined in CreateEvent() in
439                                       the Unified Extensible Firmware Interface
440                                       Specification.
441   @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for
442                                       this AP to finish this Procedure, either for
443                                       blocking or non-blocking mode. Zero means
444                                       infinity.  If the timeout expires before
445                                       this AP returns from Procedure, then Procedure
446                                       on the AP is terminated. The
447                                       AP is available for next function assigned
448                                       by MpInitLibStartupAllAPs() or
449                                       MpInitLibStartupThisAP().
450                                       If the timeout expires in blocking mode,
451                                       BSP returns EFI_TIMEOUT.  If the timeout
452                                       expires in non-blocking mode, WaitEvent
453                                       is signaled with SignalEvent().
454   @param[in]  ProcedureArgument       The parameter passed into Procedure on the
455                                       specified AP.
456   @param[out] Finished                If NULL, this parameter is ignored.  In
457                                       blocking mode, this parameter is ignored.
458                                       In non-blocking mode, if AP returns from
459                                       Procedure before the timeout expires, its
460                                       content is set to TRUE. Otherwise, the
461                                       value is set to FALSE. The caller can
462                                       determine if the AP returned from Procedure
463                                       by evaluating this value.
464 
465   @retval EFI_SUCCESS             In blocking mode, specified AP finished before
466                                   the timeout expires.
467   @retval EFI_SUCCESS             In non-blocking mode, the function has been
468                                   dispatched to specified AP.
469   @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
470                                   UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
471                                   signaled.
472   @retval EFI_UNSUPPORTED         WaitEvent is not NULL if non-blocking mode is not
473                                   supported.
474   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
475   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
476                                   the specified AP has finished.
477   @retval EFI_NOT_READY           The specified AP is busy.
478   @retval EFI_NOT_READY           MP Initialize Library is not initialized.
479   @retval EFI_NOT_FOUND           The processor with the handle specified by
480                                   ProcessorNumber does not exist.
481   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
482   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
483 
484 **/
485 EFI_STATUS
486 EFIAPI
MpInitLibStartupThisAP(IN EFI_AP_PROCEDURE Procedure,IN UINTN ProcessorNumber,IN EFI_EVENT WaitEvent OPTIONAL,IN UINTN TimeoutInMicroseconds,IN VOID * ProcedureArgument OPTIONAL,OUT BOOLEAN * Finished OPTIONAL)487 MpInitLibStartupThisAP (
488   IN  EFI_AP_PROCEDURE          Procedure,
489   IN  UINTN                     ProcessorNumber,
490   IN  EFI_EVENT                 WaitEvent               OPTIONAL,
491   IN  UINTN                     TimeoutInMicroseconds,
492   IN  VOID                      *ProcedureArgument      OPTIONAL,
493   OUT BOOLEAN                   *Finished               OPTIONAL
494   )
495 {
496   if (WaitEvent != NULL) {
497     return EFI_UNSUPPORTED;
498   }
499 
500   return StartupThisAPWorker (
501            Procedure,
502            ProcessorNumber,
503            NULL,
504            TimeoutInMicroseconds,
505            ProcedureArgument,
506            Finished
507            );
508 }
509 
510 /**
511   This service switches the requested AP to be the BSP from that point onward.
512   This service changes the BSP for all purposes. This call can only be performed
513   by the current BSP.
514 
515   @param[in] ProcessorNumber   The handle number of AP that is to become the new
516                                BSP. The range is from 0 to the total number of
517                                logical processors minus 1. The total number of
518                                logical processors can be retrieved by
519                                MpInitLibGetNumberOfProcessors().
520   @param[in] EnableOldBSP      If TRUE, then the old BSP will be listed as an
521                                enabled AP. Otherwise, it will be disabled.
522 
523   @retval EFI_SUCCESS             BSP successfully switched.
524   @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
525                                   this service returning.
526   @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
527   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
528   @retval EFI_NOT_FOUND           The processor with the handle specified by
529                                   ProcessorNumber does not exist.
530   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
531                                   a disabled AP.
532   @retval EFI_NOT_READY           The specified AP is busy.
533   @retval EFI_NOT_READY           MP Initialize Library is not initialized.
534 
535 **/
536 EFI_STATUS
537 EFIAPI
MpInitLibSwitchBSP(IN UINTN ProcessorNumber,IN BOOLEAN EnableOldBSP)538 MpInitLibSwitchBSP (
539   IN UINTN                     ProcessorNumber,
540   IN  BOOLEAN                  EnableOldBSP
541   )
542 {
543   return SwitchBSPWorker (ProcessorNumber, EnableOldBSP);
544 }
545 
546 /**
547   This service lets the caller enable or disable an AP from this point onward.
548   This service may only be called from the BSP.
549 
550   @param[in] ProcessorNumber   The handle number of AP.
551                                The range is from 0 to the total number of
552                                logical processors minus 1. The total number of
553                                logical processors can be retrieved by
554                                MpInitLibGetNumberOfProcessors().
555   @param[in] EnableAP          Specifies the new state for the processor for
556                                enabled, FALSE for disabled.
557   @param[in] HealthFlag        If not NULL, a pointer to a value that specifies
558                                the new health status of the AP. This flag
559                                corresponds to StatusFlag defined in
560                                EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
561                                the PROCESSOR_HEALTH_STATUS_BIT is used. All other
562                                bits are ignored.  If it is NULL, this parameter
563                                is ignored.
564 
565   @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
566   @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed
567                                   prior to this service returning.
568   @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
569   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
570   @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
571                                   does not exist.
572   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
573   @retval EFI_NOT_READY           MP Initialize Library is not initialized.
574 
575 **/
576 EFI_STATUS
577 EFIAPI
MpInitLibEnableDisableAP(IN UINTN ProcessorNumber,IN BOOLEAN EnableAP,IN UINT32 * HealthFlag OPTIONAL)578 MpInitLibEnableDisableAP (
579   IN  UINTN                     ProcessorNumber,
580   IN  BOOLEAN                   EnableAP,
581   IN  UINT32                    *HealthFlag OPTIONAL
582   )
583 {
584   return EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);
585 }
586 
587 
588