]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
UefiCpuPkg/MpInitLib: Rename EndOfPeiFlag to SaveRestoreFlag
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / PeiMpLib.c
1 /** @file
2 MP initialize support functions for PEI phase.
3
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "MpLib.h"
16 #include <Ppi/EndOfPeiPhase.h>
17 #include <Library/PeiServicesLib.h>
18
19 //
20 // Global PEI notify function descriptor on EndofPei event
21 //
22 GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_NOTIFY_DESCRIPTOR mMpInitLibNotifyList = {
23 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
24 &gEfiEndOfPeiSignalPpiGuid,
25 CpuMpEndOfPeiCallback
26 };
27
28 /**
29 Get pointer to CPU MP Data structure.
30
31 @return The pointer to CPU MP Data structure.
32 **/
33 CPU_MP_DATA *
34 GetCpuMpData (
35 VOID
36 )
37 {
38 CPU_MP_DATA *CpuMpData;
39
40 CpuMpData = GetCpuMpDataFromGuidedHob ();
41 ASSERT (CpuMpData != NULL);
42 return CpuMpData;
43 }
44
45 /**
46 Save the pointer to CPU MP Data structure.
47
48 @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
49 **/
50 VOID
51 SaveCpuMpData (
52 IN CPU_MP_DATA *CpuMpData
53 )
54 {
55 UINT64 Data64;
56 //
57 // Build location of CPU MP DATA buffer in HOB
58 //
59 Data64 = (UINT64) (UINTN) CpuMpData;
60 BuildGuidDataHob (
61 &mCpuInitMpLibHobGuid,
62 (VOID *) &Data64,
63 sizeof (UINT64)
64 );
65 }
66
67 /**
68 Get available system memory below 1MB by specified size.
69
70 @param[in] PeiCpuMpData Pointer to PEI CPU MP Data
71 **/
72 VOID
73 BackupAndPrepareWakeupBuffer(
74 IN CPU_MP_DATA *CpuMpData
75 )
76 {
77 CopyMem (
78 (VOID *) CpuMpData->BackupBuffer,
79 (VOID *) CpuMpData->WakeupBuffer,
80 CpuMpData->BackupBufferSize
81 );
82 CopyMem (
83 (VOID *) CpuMpData->WakeupBuffer,
84 (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
85 CpuMpData->AddressMap.RendezvousFunnelSize
86 );
87 }
88
89 /**
90 Restore wakeup buffer data.
91
92 @param[in] PeiCpuMpData Pointer to PEI CPU MP Data
93 **/
94 VOID
95 RestoreWakeupBuffer(
96 IN CPU_MP_DATA *CpuMpData
97 )
98 {
99 CopyMem (
100 (VOID *) CpuMpData->WakeupBuffer,
101 (VOID *) CpuMpData->BackupBuffer,
102 CpuMpData->BackupBufferSize
103 );
104 }
105
106 /**
107 Notify function on End Of PEI PPI.
108
109 On S3 boot, this function will restore wakeup buffer data.
110 On normal boot, this function will flag wakeup buffer to be un-used type.
111
112 @param[in] PeiServices The pointer to the PEI Services Table.
113 @param[in] NotifyDescriptor Address of the notification descriptor data structure.
114 @param[in] Ppi Address of the PPI that was installed.
115
116 @retval EFI_SUCCESS When everything is OK.
117 **/
118 EFI_STATUS
119 EFIAPI
120 CpuMpEndOfPeiCallback (
121 IN EFI_PEI_SERVICES **PeiServices,
122 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
123 IN VOID *Ppi
124 )
125 {
126 EFI_STATUS Status;
127 EFI_BOOT_MODE BootMode;
128 CPU_MP_DATA *CpuMpData;
129 EFI_PEI_HOB_POINTERS Hob;
130 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
131
132 DEBUG ((DEBUG_INFO, "PeiMpInitLib: CpuMpEndOfPeiCallback () invoked\n"));
133
134 Status = PeiServicesGetBootMode (&BootMode);
135 ASSERT_EFI_ERROR (Status);
136
137 CpuMpData = GetCpuMpData ();
138 if (BootMode != BOOT_ON_S3_RESUME) {
139 //
140 // Get the HOB list for processing
141 //
142 Hob.Raw = GetHobList ();
143 //
144 // Collect memory ranges
145 //
146 while (!END_OF_HOB_LIST (Hob)) {
147 if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
148 MemoryHob = Hob.MemoryAllocation;
149 if (MemoryHob->AllocDescriptor.MemoryBaseAddress == CpuMpData->WakeupBuffer) {
150 //
151 // Flag this HOB type to un-used
152 //
153 GET_HOB_TYPE (Hob) = EFI_HOB_TYPE_UNUSED;
154 break;
155 }
156 }
157 Hob.Raw = GET_NEXT_HOB (Hob);
158 }
159 } else {
160 CpuMpData->SaveRestoreFlag = TRUE;
161 RestoreWakeupBuffer (CpuMpData);
162 }
163 return EFI_SUCCESS;
164 }
165
166 /**
167 Check if AP wakeup buffer is overlapped with existing allocated buffer.
168
169 @param[in] WakeupBufferStart AP wakeup buffer start address.
170 @param[in] WakeupBufferEnd AP wakeup buffer end address.
171
172 @retval TRUE There is overlap.
173 @retval FALSE There is no overlap.
174 **/
175 BOOLEAN
176 CheckOverlapWithAllocatedBuffer (
177 IN UINTN WakeupBufferStart,
178 IN UINTN WakeupBufferEnd
179 )
180 {
181 EFI_PEI_HOB_POINTERS Hob;
182 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
183 BOOLEAN Overlapped;
184 UINTN MemoryStart;
185 UINTN MemoryEnd;
186
187 Overlapped = FALSE;
188 //
189 // Get the HOB list for processing
190 //
191 Hob.Raw = GetHobList ();
192 //
193 // Collect memory ranges
194 //
195 while (!END_OF_HOB_LIST (Hob)) {
196 if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
197 MemoryHob = Hob.MemoryAllocation;
198 MemoryStart = (UINTN) MemoryHob->AllocDescriptor.MemoryBaseAddress;
199 MemoryEnd = (UINTN) (MemoryHob->AllocDescriptor.MemoryBaseAddress +
200 MemoryHob->AllocDescriptor.MemoryLength);
201 if (!((WakeupBufferStart >= MemoryEnd) || (WakeupBufferEnd <= MemoryStart))) {
202 Overlapped = TRUE;
203 break;
204 }
205 }
206 Hob.Raw = GET_NEXT_HOB (Hob);
207 }
208 return Overlapped;
209 }
210
211 /**
212 Get available system memory below 1MB by specified size.
213
214 @param[in] WakeupBufferSize Wakeup buffer size required
215
216 @retval other Return wakeup buffer address below 1MB.
217 @retval -1 Cannot find free memory below 1MB.
218 **/
219 UINTN
220 GetWakeupBuffer (
221 IN UINTN WakeupBufferSize
222 )
223 {
224 EFI_PEI_HOB_POINTERS Hob;
225 UINTN WakeupBufferStart;
226 UINTN WakeupBufferEnd;
227
228 WakeupBufferSize = (WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1);
229
230 //
231 // Get the HOB list for processing
232 //
233 Hob.Raw = GetHobList ();
234
235 //
236 // Collect memory ranges
237 //
238 while (!END_OF_HOB_LIST (Hob)) {
239 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
240 if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&
241 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
242 ((Hob.ResourceDescriptor->ResourceAttribute &
243 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
244 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
245 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
246 )) == 0)
247 ) {
248 //
249 // Need memory under 1MB to be collected here
250 //
251 WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength);
252 if (WakeupBufferEnd > BASE_1MB) {
253 //
254 // Wakeup buffer should be under 1MB
255 //
256 WakeupBufferEnd = BASE_1MB;
257 }
258 while (WakeupBufferEnd > WakeupBufferSize) {
259 //
260 // Wakeup buffer should be aligned on 4KB
261 //
262 WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);
263 if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {
264 break;
265 }
266 if (CheckOverlapWithAllocatedBuffer (WakeupBufferStart, WakeupBufferEnd)) {
267 //
268 // If this range is overlapped with existing allocated buffer, skip it
269 // and find the next range
270 //
271 WakeupBufferEnd -= WakeupBufferSize;
272 continue;
273 }
274 DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",
275 WakeupBufferStart, WakeupBufferSize));
276 //
277 // Create a memory allocation HOB.
278 //
279 BuildMemoryAllocationHob (
280 WakeupBufferStart,
281 WakeupBufferSize,
282 EfiBootServicesData
283 );
284 return WakeupBufferStart;
285 }
286 }
287 }
288 //
289 // Find the next HOB
290 //
291 Hob.Raw = GET_NEXT_HOB (Hob);
292 }
293
294 return (UINTN) -1;
295 }
296
297 /**
298 Allocate reset vector buffer.
299
300 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
301 **/
302 VOID
303 AllocateResetVector (
304 IN OUT CPU_MP_DATA *CpuMpData
305 )
306 {
307 UINTN ApResetVectorSize;
308
309 if (CpuMpData->WakeupBuffer == (UINTN) -1) {
310 ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
311 sizeof (MP_CPU_EXCHANGE_INFO);
312
313 CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize);
314 CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
315 (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
316 BackupAndPrepareWakeupBuffer (CpuMpData);
317 }
318
319 if (CpuMpData->SaveRestoreFlag) {
320 BackupAndPrepareWakeupBuffer (CpuMpData);
321 }
322 }
323
324 /**
325 Free AP reset vector buffer.
326
327 @param[in] CpuMpData The pointer to CPU MP Data structure.
328 **/
329 VOID
330 FreeResetVector (
331 IN CPU_MP_DATA *CpuMpData
332 )
333 {
334 if (CpuMpData->SaveRestoreFlag) {
335 RestoreWakeupBuffer (CpuMpData);
336 }
337 }
338
339 /**
340 Checks APs status and updates APs status if needed.
341
342 **/
343 VOID
344 CheckAndUpdateApsStatus (
345 VOID
346 )
347 {
348 }
349
350 /**
351 Initialize global data for MP support.
352
353 @param[in] CpuMpData The pointer to CPU MP Data structure.
354 **/
355 VOID
356 InitMpGlobalData (
357 IN CPU_MP_DATA *CpuMpData
358 )
359 {
360 EFI_STATUS Status;
361
362 SaveCpuMpData (CpuMpData);
363 //
364 // Register an event for EndOfPei
365 //
366 Status = PeiServicesNotifyPpi (&mMpInitLibNotifyList);
367 ASSERT_EFI_ERROR (Status);
368 }
369
370 /**
371 This service executes a caller provided function on all enabled APs.
372
373 @param[in] Procedure A pointer to the function to be run on
374 enabled APs of the system. See type
375 EFI_AP_PROCEDURE.
376 @param[in] SingleThread If TRUE, then all the enabled APs execute
377 the function specified by Procedure one by
378 one, in ascending order of processor handle
379 number. If FALSE, then all the enabled APs
380 execute the function specified by Procedure
381 simultaneously.
382 @param[in] WaitEvent The event created by the caller with CreateEvent()
383 service. If it is NULL, then execute in
384 blocking mode. BSP waits until all APs finish
385 or TimeoutInMicroSeconds expires. If it's
386 not NULL, then execute in non-blocking mode.
387 BSP requests the function specified by
388 Procedure to be started on all the enabled
389 APs, and go on executing immediately. If
390 all return from Procedure, or TimeoutInMicroSeconds
391 expires, this event is signaled. The BSP
392 can use the CheckEvent() or WaitForEvent()
393 services to check the state of event. Type
394 EFI_EVENT is defined in CreateEvent() in
395 the Unified Extensible Firmware Interface
396 Specification.
397 @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for
398 APs to return from Procedure, either for
399 blocking or non-blocking mode. Zero means
400 infinity. If the timeout expires before
401 all APs return from Procedure, then Procedure
402 on the failed APs is terminated. All enabled
403 APs are available for next function assigned
404 by MpInitLibStartupAllAPs() or
405 MPInitLibStartupThisAP().
406 If the timeout expires in blocking mode,
407 BSP returns EFI_TIMEOUT. If the timeout
408 expires in non-blocking mode, WaitEvent
409 is signaled with SignalEvent().
410 @param[in] ProcedureArgument The parameter passed into Procedure for
411 all APs.
412 @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
413 if all APs finish successfully, then its
414 content is set to NULL. If not all APs
415 finish before timeout expires, then its
416 content is set to address of the buffer
417 holding handle numbers of the failed APs.
418 The buffer is allocated by MP Initialization
419 library, and it's the caller's responsibility to
420 free the buffer with FreePool() service.
421 In blocking mode, it is ready for consumption
422 when the call returns. In non-blocking mode,
423 it is ready when WaitEvent is signaled. The
424 list of failed CPU is terminated by
425 END_OF_CPU_LIST.
426
427 @retval EFI_SUCCESS In blocking mode, all APs have finished before
428 the timeout expired.
429 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
430 to all enabled APs.
431 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
432 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
433 signaled.
434 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
435 supported.
436 @retval EFI_DEVICE_ERROR Caller processor is AP.
437 @retval EFI_NOT_STARTED No enabled APs exist in the system.
438 @retval EFI_NOT_READY Any enabled APs are busy.
439 @retval EFI_NOT_READY MP Initialize Library is not initialized.
440 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
441 all enabled APs have finished.
442 @retval EFI_INVALID_PARAMETER Procedure is NULL.
443
444 **/
445 EFI_STATUS
446 EFIAPI
447 MpInitLibStartupAllAPs (
448 IN EFI_AP_PROCEDURE Procedure,
449 IN BOOLEAN SingleThread,
450 IN EFI_EVENT WaitEvent OPTIONAL,
451 IN UINTN TimeoutInMicroseconds,
452 IN VOID *ProcedureArgument OPTIONAL,
453 OUT UINTN **FailedCpuList OPTIONAL
454 )
455 {
456 if (WaitEvent != NULL) {
457 return EFI_UNSUPPORTED;
458 }
459
460 return StartupAllAPsWorker (
461 Procedure,
462 SingleThread,
463 NULL,
464 TimeoutInMicroseconds,
465 ProcedureArgument,
466 FailedCpuList
467 );
468 }
469
470 /**
471 This service lets the caller get one enabled AP to execute a caller-provided
472 function.
473
474 @param[in] Procedure A pointer to the function to be run on the
475 designated AP of the system. See type
476 EFI_AP_PROCEDURE.
477 @param[in] ProcessorNumber The handle number of the AP. The range is
478 from 0 to the total number of logical
479 processors minus 1. The total number of
480 logical processors can be retrieved by
481 MpInitLibGetNumberOfProcessors().
482 @param[in] WaitEvent The event created by the caller with CreateEvent()
483 service. If it is NULL, then execute in
484 blocking mode. BSP waits until this AP finish
485 or TimeoutInMicroSeconds expires. If it's
486 not NULL, then execute in non-blocking mode.
487 BSP requests the function specified by
488 Procedure to be started on this AP,
489 and go on executing immediately. If this AP
490 return from Procedure or TimeoutInMicroSeconds
491 expires, this event is signaled. The BSP
492 can use the CheckEvent() or WaitForEvent()
493 services to check the state of event. Type
494 EFI_EVENT is defined in CreateEvent() in
495 the Unified Extensible Firmware Interface
496 Specification.
497 @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for
498 this AP to finish this Procedure, either for
499 blocking or non-blocking mode. Zero means
500 infinity. If the timeout expires before
501 this AP returns from Procedure, then Procedure
502 on the AP is terminated. The
503 AP is available for next function assigned
504 by MpInitLibStartupAllAPs() or
505 MpInitLibStartupThisAP().
506 If the timeout expires in blocking mode,
507 BSP returns EFI_TIMEOUT. If the timeout
508 expires in non-blocking mode, WaitEvent
509 is signaled with SignalEvent().
510 @param[in] ProcedureArgument The parameter passed into Procedure on the
511 specified AP.
512 @param[out] Finished If NULL, this parameter is ignored. In
513 blocking mode, this parameter is ignored.
514 In non-blocking mode, if AP returns from
515 Procedure before the timeout expires, its
516 content is set to TRUE. Otherwise, the
517 value is set to FALSE. The caller can
518 determine if the AP returned from Procedure
519 by evaluating this value.
520
521 @retval EFI_SUCCESS In blocking mode, specified AP finished before
522 the timeout expires.
523 @retval EFI_SUCCESS In non-blocking mode, the function has been
524 dispatched to specified AP.
525 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
526 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
527 signaled.
528 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
529 supported.
530 @retval EFI_DEVICE_ERROR The calling processor is an AP.
531 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
532 the specified AP has finished.
533 @retval EFI_NOT_READY The specified AP is busy.
534 @retval EFI_NOT_READY MP Initialize Library is not initialized.
535 @retval EFI_NOT_FOUND The processor with the handle specified by
536 ProcessorNumber does not exist.
537 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
538 @retval EFI_INVALID_PARAMETER Procedure is NULL.
539
540 **/
541 EFI_STATUS
542 EFIAPI
543 MpInitLibStartupThisAP (
544 IN EFI_AP_PROCEDURE Procedure,
545 IN UINTN ProcessorNumber,
546 IN EFI_EVENT WaitEvent OPTIONAL,
547 IN UINTN TimeoutInMicroseconds,
548 IN VOID *ProcedureArgument OPTIONAL,
549 OUT BOOLEAN *Finished OPTIONAL
550 )
551 {
552 if (WaitEvent != NULL) {
553 return EFI_UNSUPPORTED;
554 }
555
556 return StartupThisAPWorker (
557 Procedure,
558 ProcessorNumber,
559 NULL,
560 TimeoutInMicroseconds,
561 ProcedureArgument,
562 Finished
563 );
564 }
565
566 /**
567 This service switches the requested AP to be the BSP from that point onward.
568 This service changes the BSP for all purposes. This call can only be performed
569 by the current BSP.
570
571 @param[in] ProcessorNumber The handle number of AP that is to become the new
572 BSP. The range is from 0 to the total number of
573 logical processors minus 1. The total number of
574 logical processors can be retrieved by
575 MpInitLibGetNumberOfProcessors().
576 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
577 enabled AP. Otherwise, it will be disabled.
578
579 @retval EFI_SUCCESS BSP successfully switched.
580 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
581 this service returning.
582 @retval EFI_UNSUPPORTED Switching the BSP is not supported.
583 @retval EFI_DEVICE_ERROR The calling processor is an AP.
584 @retval EFI_NOT_FOUND The processor with the handle specified by
585 ProcessorNumber does not exist.
586 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
587 a disabled AP.
588 @retval EFI_NOT_READY The specified AP is busy.
589 @retval EFI_NOT_READY MP Initialize Library is not initialized.
590
591 **/
592 EFI_STATUS
593 EFIAPI
594 MpInitLibSwitchBSP (
595 IN UINTN ProcessorNumber,
596 IN BOOLEAN EnableOldBSP
597 )
598 {
599 return SwitchBSPWorker (ProcessorNumber, EnableOldBSP);
600 }
601
602 /**
603 This service lets the caller enable or disable an AP from this point onward.
604 This service may only be called from the BSP.
605
606 @param[in] ProcessorNumber The handle number of AP.
607 The range is from 0 to the total number of
608 logical processors minus 1. The total number of
609 logical processors can be retrieved by
610 MpInitLibGetNumberOfProcessors().
611 @param[in] EnableAP Specifies the new state for the processor for
612 enabled, FALSE for disabled.
613 @param[in] HealthFlag If not NULL, a pointer to a value that specifies
614 the new health status of the AP. This flag
615 corresponds to StatusFlag defined in
616 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
617 the PROCESSOR_HEALTH_STATUS_BIT is used. All other
618 bits are ignored. If it is NULL, this parameter
619 is ignored.
620
621 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
622 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
623 prior to this service returning.
624 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
625 @retval EFI_DEVICE_ERROR The calling processor is an AP.
626 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
627 does not exist.
628 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
629 @retval EFI_NOT_READY MP Initialize Library is not initialized.
630
631 **/
632 EFI_STATUS
633 EFIAPI
634 MpInitLibEnableDisableAP (
635 IN UINTN ProcessorNumber,
636 IN BOOLEAN EnableAP,
637 IN UINT32 *HealthFlag OPTIONAL
638 )
639 {
640 return EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);
641 }
642
643