VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 60.1 KB
Line 
1/** @file
2 Construct MP Services Protocol.
3
4 The MP Services Protocol provides a generalized way of performing following tasks:
5 - Retrieving information of multi-processor environment and MP-related status of
6 specific processors.
7 - Dispatching user-provided function to APs.
8 - Maintain MP-related processor status.
9
10 The MP Services Protocol must be produced on any system with more than one logical
11 processor.
12
13 The Protocol is available only during boot time.
14
15 MP Services Protocol is hardware-independent. Most of the logic of this protocol
16 is architecturally neutral. It abstracts the multi-processor environment and
17 status of processors, and provides interfaces to retrieve information, maintain,
18 and dispatch.
19
20 MP Services Protocol may be consumed by ACPI module. The ACPI module may use this
21 protocol to retrieve data that are needed for an MP platform and report them to OS.
22 MP Services Protocol may also be used to program and configure processors, such
23 as MTRR synchronization for memory space attributes setting in DXE Services.
24 MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
25 by taking advantage of the processing capabilities of the APs, for example, using
26 APs to help test system memory in parallel with other device initialization.
27 Diagnostics applications may also use this protocol for multi-processor.
28
29 Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
30 SPDX-License-Identifier: BSD-2-Clause-Patent
31
32**/
33
34#include <PiDxe.h>
35
36#include <Library/ArmLib.h>
37#include <Library/ArmMmuLib.h>
38#include <Library/ArmPlatformLib.h>
39#include <Library/ArmSmcLib.h>
40#include <Library/BaseMemoryLib.h>
41#include <Library/CacheMaintenanceLib.h>
42#include <Library/CpuExceptionHandlerLib.h>
43#include <Library/DebugLib.h>
44#include <Library/HobLib.h>
45#include <Library/MemoryAllocationLib.h>
46#include <Library/UefiBootServicesTableLib.h>
47#include <Library/UefiLib.h>
48#include <IndustryStandard/ArmStdSmc.h>
49#include <Ppi/ArmMpCoreInfo.h>
50#include <Protocol/LoadedImage.h>
51
52#include "MpServicesInternal.h"
53
54#define POLL_INTERVAL_US 50000
55
56STATIC CPU_MP_DATA mCpuMpData;
57STATIC BOOLEAN mNonBlockingModeAllowed;
58UINT64 *gApStacksBase;
59UINT64 *gProcessorIDs;
60CONST UINT64 gApStackSize = AP_STACK_SIZE;
61VOID *gTtbr0;
62UINTN gTcr;
63UINTN gMair;
64
65STATIC
66BOOLEAN
67IsCurrentProcessorBSP (
68 VOID
69 );
70
71/** Turns on the specified core using PSCI and executes the user-supplied
72 function that's been configured via a previous call to SetApProcedure.
73
74 @param ProcessorIndex The index of the core to turn on.
75
76 @retval EFI_SUCCESS Success.
77 @retval EFI_DEVICE_ERROR The processor could not be turned on.
78
79**/
80STATIC
81EFI_STATUS
82EFIAPI
83DispatchCpu (
84 IN UINTN ProcessorIndex
85 )
86{
87 ARM_SMC_ARGS Args;
88 EFI_STATUS Status;
89
90 Status = EFI_SUCCESS;
91
92 mCpuMpData.CpuData[ProcessorIndex].State = CpuStateBusy;
93
94 /* Turn the AP on */
95 if (sizeof (Args.Arg0) == sizeof (UINT32)) {
96 Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH32;
97 } else {
98 Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH64;
99 }
100
101 Args.Arg1 = gProcessorIDs[ProcessorIndex];
102 Args.Arg2 = (UINTN)ApEntryPoint;
103
104 ArmCallSmc (&Args);
105
106 if (Args.Arg0 == ARM_SMC_PSCI_RET_ALREADY_ON) {
107 Status = EFI_NOT_READY;
108 } else if (Args.Arg0 != ARM_SMC_PSCI_RET_SUCCESS) {
109 DEBUG ((DEBUG_ERROR, "PSCI_CPU_ON call failed: %d\n", Args.Arg0));
110 Status = EFI_DEVICE_ERROR;
111 }
112
113 return Status;
114}
115
116/** Returns whether the specified processor is the BSP.
117
118 @param[in] ProcessorIndex The index the processor to check.
119
120 @return TRUE if the processor is the BSP, FALSE otherwise.
121**/
122STATIC
123BOOLEAN
124IsProcessorBSP (
125 UINTN ProcessorIndex
126 )
127{
128 EFI_PROCESSOR_INFORMATION *CpuInfo;
129
130 CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
131
132 return (CpuInfo->StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
133}
134
135/** Get the Application Processors state.
136
137 @param[in] CpuData The pointer to CPU_AP_DATA of specified AP.
138
139 @return The AP status.
140**/
141CPU_STATE
142GetApState (
143 IN CPU_AP_DATA *CpuData
144 )
145{
146 return CpuData->State;
147}
148
149/** Configures the processor context with the user-supplied procedure and
150 argument.
151
152 @param CpuData The processor context.
153 @param Procedure The user-supplied procedure.
154 @param ProcedureArgument The user-supplied procedure argument.
155
156**/
157STATIC
158VOID
159SetApProcedure (
160 IN CPU_AP_DATA *CpuData,
161 IN EFI_AP_PROCEDURE Procedure,
162 IN VOID *ProcedureArgument
163 )
164{
165 ASSERT (CpuData != NULL);
166 ASSERT (Procedure != NULL);
167
168 CpuData->Parameter = ProcedureArgument;
169 CpuData->Procedure = Procedure;
170}
171
172/** Returns the index of the next processor that is blocked.
173
174 @param[out] NextNumber The index of the next blocked processor.
175
176 @retval EFI_SUCCESS Successfully found the next blocked processor.
177 @retval EFI_NOT_FOUND There are no blocked processors.
178
179**/
180STATIC
181EFI_STATUS
182GetNextBlockedNumber (
183 OUT UINTN *NextNumber
184 )
185{
186 UINTN Index;
187 CPU_STATE State;
188 CPU_AP_DATA *CpuData;
189
190 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
191 CpuData = &mCpuMpData.CpuData[Index];
192 if (IsProcessorBSP (Index)) {
193 // Skip BSP
194 continue;
195 }
196
197 State = CpuData->State;
198
199 if (State == CpuStateBlocked) {
200 *NextNumber = Index;
201 return EFI_SUCCESS;
202 }
203 }
204
205 return EFI_NOT_FOUND;
206}
207
208/** Stalls the BSP for the minimum of POLL_INTERVAL_US and Timeout.
209
210 @param[in] Timeout The time limit in microseconds remaining for
211 APs to return from Procedure.
212
213 @retval StallTime Time of execution stall.
214**/
215STATIC
216UINTN
217CalculateAndStallInterval (
218 IN UINTN Timeout
219 )
220{
221 UINTN StallTime;
222
223 if ((Timeout < POLL_INTERVAL_US) && (Timeout != 0)) {
224 StallTime = Timeout;
225 } else {
226 StallTime = POLL_INTERVAL_US;
227 }
228
229 gBS->Stall (StallTime);
230
231 return StallTime;
232}
233
234/**
235 This service retrieves the number of logical processor in the platform
236 and the number of those logical processors that are enabled on this boot.
237 This service may only be called from the BSP.
238
239 This function is used to retrieve the following information:
240 - The number of logical processors that are present in the system.
241 - The number of enabled logical processors in the system at the instant
242 this call is made.
243
244 Because MP Service Protocol provides services to enable and disable processors
245 dynamically, the number of enabled logical processors may vary during the
246 course of a boot session.
247
248 If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
249 If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
250 EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
251 is returned in NumberOfProcessors, the number of currently enabled processor
252 is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
253
254 @param[in] This A pointer to the
255 EFI_MP_SERVICES_PROTOCOL instance.
256 @param[out] NumberOfProcessors Pointer to the total number of logical
257 processors in the system, including
258 the BSP and disabled APs.
259 @param[out] NumberOfEnabledProcessors Pointer to the number of enabled
260 logical processors that exist in the
261 system, including the BSP.
262
263 @retval EFI_SUCCESS The number of logical processors and enabled
264 logical processors was retrieved.
265 @retval EFI_DEVICE_ERROR The calling processor is an AP.
266 @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL.
267 @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL.
268
269**/
270STATIC
271EFI_STATUS
272EFIAPI
273GetNumberOfProcessors (
274 IN EFI_MP_SERVICES_PROTOCOL *This,
275 OUT UINTN *NumberOfProcessors,
276 OUT UINTN *NumberOfEnabledProcessors
277 )
278{
279 if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
280 return EFI_INVALID_PARAMETER;
281 }
282
283 if (!IsCurrentProcessorBSP ()) {
284 return EFI_DEVICE_ERROR;
285 }
286
287 *NumberOfProcessors = mCpuMpData.NumberOfProcessors;
288 *NumberOfEnabledProcessors = mCpuMpData.NumberOfEnabledProcessors;
289 return EFI_SUCCESS;
290}
291
292/**
293 Gets detailed MP-related information on the requested processor at the
294 instant this call is made. This service may only be called from the BSP.
295
296 This service retrieves detailed MP-related information about any processor
297 on the platform. Note the following:
298 - The processor information may change during the course of a boot session.
299 - The information presented here is entirely MP related.
300
301 Information regarding the number of caches and their sizes, frequency of
302 operation, slot numbers is all considered platform-related information and is
303 not provided by this service.
304
305 @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
306 instance.
307 @param[in] ProcessorIndex The index of the processor.
308 @param[out] ProcessorInfoBuffer A pointer to the buffer where information
309 for the requested processor is deposited.
310
311 @retval EFI_SUCCESS Processor information was returned.
312 @retval EFI_DEVICE_ERROR The calling processor is an AP.
313 @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
314 @retval EFI_NOT_FOUND The processor with the handle specified by
315 ProcessorNumber does not exist in the platform.
316
317**/
318STATIC
319EFI_STATUS
320EFIAPI
321GetProcessorInfo (
322 IN EFI_MP_SERVICES_PROTOCOL *This,
323 IN UINTN ProcessorIndex,
324 OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer
325 )
326{
327 if (ProcessorInfoBuffer == NULL) {
328 return EFI_INVALID_PARAMETER;
329 }
330
331 if (!IsCurrentProcessorBSP ()) {
332 return EFI_DEVICE_ERROR;
333 }
334
335 ProcessorIndex &= ~CPU_V2_EXTENDED_TOPOLOGY;
336
337 if (ProcessorIndex >= mCpuMpData.NumberOfProcessors) {
338 return EFI_NOT_FOUND;
339 }
340
341 CopyMem (
342 ProcessorInfoBuffer,
343 &mCpuMpData.CpuData[ProcessorIndex].Info,
344 sizeof (EFI_PROCESSOR_INFORMATION)
345 );
346 return EFI_SUCCESS;
347}
348
349/**
350 This service executes a caller provided function on all enabled APs. APs can
351 run either simultaneously or one at a time in sequence. This service supports
352 both blocking and non-blocking requests. The non-blocking requests use EFI
353 events so the BSP can detect when the APs have finished. This service may only
354 be called from the BSP.
355
356 This function is used to dispatch all the enabled APs to the function
357 specified by Procedure. If any enabled AP is busy, then EFI_NOT_READY is
358 returned immediately and Procedure is not started on any AP.
359
360 If SingleThread is TRUE, all the enabled APs execute the function specified by
361 Procedure one by one, in ascending order of processor handle number.
362 Otherwise, all the enabled APs execute the function specified by Procedure
363 simultaneously.
364
365 If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
366 APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in
367 non-blocking mode, and the BSP returns from this service without waiting for
368 APs. If a non-blocking mode is requested after the UEFI Event
369 EFI_EVENT_GROUP_READY_TO_BOOT is signaled, then EFI_UNSUPPORTED must be
370 returned.
371
372 If the timeout specified by TimeoutInMicroseconds expires before all APs
373 return from Procedure, then Procedure on the failed APs is terminated.
374 All enabled APs are always available for further calls to
375 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
376 EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
377 content points to the list of processor handle numbers in which Procedure was
378 terminated.
379
380 Note: It is the responsibility of the consumer of the
381 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() to make sure that the nature of the
382 code that is executed on the BSP and the dispatched APs is well controlled.
383 The MP Services Protocol does not guarantee that the Procedure function is
384 MP-safe. Hence, the tasks that can be run in parallel are limited to certain
385 independent tasks and well-controlled exclusive code. EFI services and
386 protocols may not be called by APs unless otherwise specified.
387
388 In blocking execution mode, BSP waits until all APs finish or
389 TimeoutInMicroseconds expires.
390
391 In non-blocking execution mode, BSP is freed to return to the caller and then
392 proceed to the next task without having to wait for APs. The following
393 sequence needs to occur in a non-blocking execution mode:
394
395 -# The caller that intends to use this MP Services Protocol in non-blocking
396 mode creates WaitEvent by calling the EFI CreateEvent() service. The
397 caller invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter
398 WaitEvent is not NULL, then StartupAllAPs() executes in non-blocking
399 mode. It requests the function specified by Procedure to be started on
400 all the enabled APs, and releases the BSP to continue with other tasks.
401 -# The caller can use the CheckEvent() and WaitForEvent() services to check
402 the state of the WaitEvent created in step 1.
403 -# When the APs complete their task or TimeoutInMicroSecondss expires, the
404 MP Service signals WaitEvent by calling the EFI SignalEvent() function.
405 If FailedCpuList is not NULL, its content is available when WaitEvent is
406 signaled. If all APs returned from Procedure prior to the timeout, then
407 FailedCpuList is set to NULL. If not all APs return from Procedure before
408 the timeout, then FailedCpuList is filled in with the list of the failed
409 APs. The buffer is allocated by MP Service Protocol using AllocatePool().
410 It is the caller's responsibility to free the buffer with FreePool()
411 service.
412 -# This invocation of SignalEvent() function informs the caller that invoked
413 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs
414 completed the specified task or a timeout occurred. The contents of
415 FailedCpuList can be examined to determine which APs did not complete the
416 specified task prior to the timeout.
417
418 @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
419 instance.
420 @param[in] Procedure A pointer to the function to be run on
421 enabled APs of the system. See type
422 EFI_AP_PROCEDURE.
423 @param[in] SingleThread If TRUE, then all the enabled APs execute
424 the function specified by Procedure one by
425 one, in ascending order of processor
426 handle number. If FALSE, then all the
427 enabled APs execute the function specified
428 by Procedure simultaneously.
429 @param[in] WaitEvent The event created by the caller with
430 CreateEvent() service. If it is NULL,
431 then execute in blocking mode. BSP waits
432 until all APs finish or
433 TimeoutInMicroseconds expires. If it's
434 not NULL, then execute in non-blocking
435 mode. BSP requests the function specified
436 by Procedure to be started on all the
437 enabled APs, and go on executing
438 immediately. If all return from Procedure,
439 or TimeoutInMicroseconds expires, this
440 event is signaled. The BSP can use the
441 CheckEvent() or WaitForEvent()
442 services to check the state of event. Type
443 EFI_EVENT is defined in CreateEvent() in
444 the Unified Extensible Firmware Interface
445 Specification.
446 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds
447 for APs to return from Procedure, either
448 for blocking or non-blocking mode. Zero
449 means infinity. If the timeout expires
450 before all APs return from Procedure, then
451 Procedure on the failed APs is terminated.
452 All enabled APs are available for next
453 function assigned by
454 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
455 or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
456 If the timeout expires in blocking mode,
457 BSP returns EFI_TIMEOUT. If the timeout
458 expires in non-blocking mode, WaitEvent
459 is signaled with SignalEvent().
460 @param[in] ProcedureArgument The parameter passed into Procedure for
461 all APs.
462 @param[out] FailedCpuList If NULL, this parameter is ignored.
463 Otherwise, if all APs finish successfully,
464 then its content is set to NULL. If not
465 all APs finish before timeout expires,
466 then its content is set to address of the
467 buffer holding handle numbers of the
468 failed APs.
469 The buffer is allocated by MP Service
470 Protocol, and it's the caller's
471 responsibility to free the buffer with
472 FreePool() service.
473 In blocking mode, it is ready for
474 consumption when the call returns. In
475 non-blocking mode, it is ready when
476 WaitEvent is signaled. The list of failed
477 CPU is terminated by END_OF_CPU_LIST.
478
479 @retval EFI_SUCCESS In blocking mode, all APs have finished before
480 the timeout expired.
481 @retval EFI_SUCCESS In non-blocking mode, function has been
482 dispatched to all enabled APs.
483 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
484 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
485 signaled.
486 @retval EFI_DEVICE_ERROR Caller processor is AP.
487 @retval EFI_NOT_STARTED No enabled APs exist in the system.
488 @retval EFI_NOT_READY Any enabled APs are busy.
489 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
490 all enabled APs have finished.
491 @retval EFI_INVALID_PARAMETER Procedure is NULL.
492
493**/
494STATIC
495EFI_STATUS
496EFIAPI
497StartupAllAPs (
498 IN EFI_MP_SERVICES_PROTOCOL *This,
499 IN EFI_AP_PROCEDURE Procedure,
500 IN BOOLEAN SingleThread,
501 IN EFI_EVENT WaitEvent OPTIONAL,
502 IN UINTN TimeoutInMicroseconds,
503 IN VOID *ProcedureArgument OPTIONAL,
504 OUT UINTN **FailedCpuList OPTIONAL
505 )
506{
507 EFI_STATUS Status;
508
509 if (!IsCurrentProcessorBSP ()) {
510 return EFI_DEVICE_ERROR;
511 }
512
513 if ((mCpuMpData.NumberOfProcessors == 1) || (mCpuMpData.NumberOfEnabledProcessors == 1)) {
514 return EFI_NOT_STARTED;
515 }
516
517 if (Procedure == NULL) {
518 return EFI_INVALID_PARAMETER;
519 }
520
521 if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
522 return EFI_UNSUPPORTED;
523 }
524
525 if (FailedCpuList != NULL) {
526 mCpuMpData.FailedList = AllocateZeroPool (
527 (mCpuMpData.NumberOfProcessors + 1) *
528 sizeof (UINTN)
529 );
530 if (mCpuMpData.FailedList == NULL) {
531 return EFI_OUT_OF_RESOURCES;
532 }
533
534 SetMemN (
535 mCpuMpData.FailedList,
536 (mCpuMpData.NumberOfProcessors + 1) *
537 sizeof (UINTN),
538 END_OF_CPU_LIST
539 );
540 mCpuMpData.FailedListIndex = 0;
541 *FailedCpuList = mCpuMpData.FailedList;
542 }
543
544 StartupAllAPsPrepareState (SingleThread);
545
546 // If any enabled APs are busy (ignoring the BSP), return EFI_NOT_READY
547 if (mCpuMpData.StartCount != (mCpuMpData.NumberOfEnabledProcessors - 1)) {
548 return EFI_NOT_READY;
549 }
550
551 if (WaitEvent != NULL) {
552 Status = StartupAllAPsWithWaitEvent (
553 Procedure,
554 ProcedureArgument,
555 WaitEvent,
556 TimeoutInMicroseconds,
557 SingleThread,
558 FailedCpuList
559 );
560
561 if (EFI_ERROR (Status) && (FailedCpuList != NULL)) {
562 if (mCpuMpData.FailedListIndex == 0) {
563 FreePool (*FailedCpuList);
564 *FailedCpuList = NULL;
565 }
566 }
567 } else {
568 Status = StartupAllAPsNoWaitEvent (
569 Procedure,
570 ProcedureArgument,
571 TimeoutInMicroseconds,
572 SingleThread,
573 FailedCpuList
574 );
575
576 if (FailedCpuList != NULL) {
577 if (mCpuMpData.FailedListIndex == 0) {
578 FreePool (*FailedCpuList);
579 *FailedCpuList = NULL;
580 }
581 }
582 }
583
584 return Status;
585}
586
587/**
588 This service lets the caller get one enabled AP to execute a caller-provided
589 function. The caller can request the BSP to either wait for the completion
590 of the AP or just proceed with the next task by using the EFI event mechanism.
591 See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
592 execution support. This service may only be called from the BSP.
593
594 This function is used to dispatch one enabled AP to the function specified by
595 Procedure passing in the argument specified by ProcedureArgument. If WaitEvent
596 is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
597 TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
598 BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
599 is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
600 then EFI_UNSUPPORTED must be returned.
601
602 If the timeout specified by TimeoutInMicroseconds expires before the AP returns
603 from Procedure, then execution of Procedure by the AP is terminated. The AP is
604 available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
605 EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
606
607 @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
608 instance.
609 @param[in] Procedure A pointer to the function to be run on
610 enabled APs of the system. See type
611 EFI_AP_PROCEDURE.
612 @param[in] ProcessorNumber The handle number of the AP. The range is
613 from 0 to the total number of logical
614 processors minus 1. The total number of
615 logical processors can be retrieved by
616 EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
617 @param[in] WaitEvent The event created by the caller with CreateEvent()
618 service. If it is NULL, then execute in
619 blocking mode. BSP waits until all APs finish
620 or TimeoutInMicroseconds expires. If it's
621 not NULL, then execute in non-blocking mode.
622 BSP requests the function specified by
623 Procedure to be started on all the enabled
624 APs, and go on executing immediately. If
625 all return from Procedure or TimeoutInMicroseconds
626 expires, this event is signaled. The BSP
627 can use the CheckEvent() or WaitForEvent()
628 services to check the state of event. Type
629 EFI_EVENT is defined in CreateEvent() in
630 the Unified Extensible Firmware Interface
631 Specification.
632 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
633 APs to return from Procedure, either for
634 blocking or non-blocking mode. Zero means
635 infinity. If the timeout expires before
636 all APs return from Procedure, then Procedure
637 on the failed APs is terminated. All enabled
638 APs are available for next function assigned
639 by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
640 or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
641 If the timeout expires in blocking mode,
642 BSP returns EFI_TIMEOUT. If the timeout
643 expires in non-blocking mode, WaitEvent
644 is signaled with SignalEvent().
645 @param[in] ProcedureArgument The parameter passed into Procedure for
646 all APs.
647 @param[out] Finished If NULL, this parameter is ignored. In
648 blocking mode, this parameter is ignored.
649 In non-blocking mode, if AP returns from
650 Procedure before the timeout expires, its
651 content is set to TRUE. Otherwise, the
652 value is set to FALSE. The caller can
653 determine if the AP returned from Procedure
654 by evaluating this value.
655
656 @retval EFI_SUCCESS In blocking mode, specified AP finished before
657 the timeout expires.
658 @retval EFI_SUCCESS In non-blocking mode, the function has been
659 dispatched to specified AP.
660 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
661 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
662 signaled.
663 @retval EFI_DEVICE_ERROR The calling processor is an AP.
664 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
665 the specified AP has finished.
666 @retval EFI_NOT_READY The specified AP is busy.
667 @retval EFI_NOT_FOUND The processor with the handle specified by
668 ProcessorNumber does not exist.
669 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
670 @retval EFI_INVALID_PARAMETER Procedure is NULL.
671
672**/
673STATIC
674EFI_STATUS
675EFIAPI
676StartupThisAP (
677 IN EFI_MP_SERVICES_PROTOCOL *This,
678 IN EFI_AP_PROCEDURE Procedure,
679 IN UINTN ProcessorNumber,
680 IN EFI_EVENT WaitEvent OPTIONAL,
681 IN UINTN TimeoutInMicroseconds,
682 IN VOID *ProcedureArgument OPTIONAL,
683 OUT BOOLEAN *Finished OPTIONAL
684 )
685{
686 EFI_STATUS Status;
687 UINTN Timeout;
688 CPU_AP_DATA *CpuData;
689
690 if (!IsCurrentProcessorBSP ()) {
691 return EFI_DEVICE_ERROR;
692 }
693
694 if (Procedure == NULL) {
695 return EFI_INVALID_PARAMETER;
696 }
697
698 if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
699 return EFI_NOT_FOUND;
700 }
701
702 CpuData = &mCpuMpData.CpuData[ProcessorNumber];
703
704 if (IsProcessorBSP (ProcessorNumber)) {
705 return EFI_INVALID_PARAMETER;
706 }
707
708 if (!IsProcessorEnabled (ProcessorNumber)) {
709 return EFI_INVALID_PARAMETER;
710 }
711
712 if ((GetApState (CpuData) != CpuStateIdle) &&
713 (GetApState (CpuData) != CpuStateFinished))
714 {
715 return EFI_NOT_READY;
716 }
717
718 if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
719 return EFI_UNSUPPORTED;
720 }
721
722 Timeout = TimeoutInMicroseconds;
723
724 CpuData->Timeout = TimeoutInMicroseconds;
725 CpuData->TimeTaken = 0;
726 CpuData->TimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);
727
728 SetApProcedure (
729 CpuData,
730 Procedure,
731 ProcedureArgument
732 );
733
734 Status = DispatchCpu (ProcessorNumber);
735 if (EFI_ERROR (Status)) {
736 CpuData->State = CpuStateIdle;
737 return EFI_NOT_READY;
738 }
739
740 if (WaitEvent != NULL) {
741 // Non Blocking
742 if (Finished != NULL) {
743 CpuData->SingleApFinished = Finished;
744 *Finished = FALSE;
745 }
746
747 CpuData->WaitEvent = WaitEvent;
748 Status = gBS->SetTimer (
749 CpuData->CheckThisAPEvent,
750 TimerPeriodic,
751 POLL_INTERVAL_US
752 );
753
754 return EFI_SUCCESS;
755 }
756
757 // Blocking
758 while (TRUE) {
759 if (GetApState (CpuData) == CpuStateFinished) {
760 CpuData->State = CpuStateIdle;
761 break;
762 }
763
764 if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
765 return EFI_TIMEOUT;
766 }
767
768 Timeout -= CalculateAndStallInterval (Timeout);
769 }
770
771 return EFI_SUCCESS;
772}
773
774/**
775 This service switches the requested AP to be the BSP from that point onward.
776 This service changes the BSP for all purposes. This call can only be
777 performed by the current BSP.
778
779 This service switches the requested AP to be the BSP from that point onward.
780 This service changes the BSP for all purposes. The new BSP can take over the
781 execution of the old BSP and continue seamlessly from where the old one left
782 off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
783 is signaled.
784
785 If the BSP cannot be switched prior to the return from this service, then
786 EFI_UNSUPPORTED must be returned.
787
788 @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
789 @param[in] ProcessorNumber The handle number of AP that is to become the new
790 BSP. The range is from 0 to the total number of
791 logical processors minus 1. The total number of
792 logical processors can be retrieved by
793 EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
794 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
795 enabled AP. Otherwise, it will be disabled.
796
797 @retval EFI_SUCCESS BSP successfully switched.
798 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
799 this service returning.
800 @retval EFI_UNSUPPORTED Switching the BSP is not supported.
801 @retval EFI_SUCCESS The calling processor is an AP.
802 @retval EFI_NOT_FOUND The processor with the handle specified by
803 ProcessorNumber does not exist.
804 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
805 a disabled AP.
806 @retval EFI_NOT_READY The specified AP is busy.
807
808**/
809STATIC
810EFI_STATUS
811EFIAPI
812SwitchBSP (
813 IN EFI_MP_SERVICES_PROTOCOL *This,
814 IN UINTN ProcessorNumber,
815 IN BOOLEAN EnableOldBSP
816 )
817{
818 return EFI_UNSUPPORTED;
819}
820
821/**
822 This service lets the caller enable or disable an AP from this point onward.
823 This service may only be called from the BSP.
824
825 This service allows the caller enable or disable an AP from this point onward.
826 The caller can optionally specify the health status of the AP by Health. If
827 an AP is being disabled, then the state of the disabled AP is implementation
828 dependent. If an AP is enabled, then the implementation must guarantee that a
829 complete initialization sequence is performed on the AP, so the AP is in a state
830 that is compatible with an MP operating system. This service may not be supported
831 after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
832
833 If the enable or disable AP operation cannot be completed prior to the return
834 from this service, then EFI_UNSUPPORTED must be returned.
835
836 @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
837 @param[in] ProcessorNumber The handle number of AP that is to become the new
838 BSP. The range is from 0 to the total number of
839 logical processors minus 1. The total number of
840 logical processors can be retrieved by
841 EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
842 @param[in] EnableAP Specifies the new state for the processor for
843 enabled, FALSE for disabled.
844 @param[in] HealthFlag If not NULL, a pointer to a value that specifies
845 the new health status of the AP. This flag
846 corresponds to StatusFlag defined in
847 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
848 the PROCESSOR_HEALTH_STATUS_BIT is used. All other
849 bits are ignored. If it is NULL, this parameter
850 is ignored.
851
852 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
853 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
854 prior to this service returning.
855 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
856 @retval EFI_DEVICE_ERROR The calling processor is an AP.
857 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
858 does not exist.
859 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
860
861**/
862STATIC
863EFI_STATUS
864EFIAPI
865EnableDisableAP (
866 IN EFI_MP_SERVICES_PROTOCOL *This,
867 IN UINTN ProcessorNumber,
868 IN BOOLEAN EnableAP,
869 IN UINT32 *HealthFlag OPTIONAL
870 )
871{
872 UINTN StatusFlag;
873 CPU_AP_DATA *CpuData;
874
875 StatusFlag = mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag;
876 CpuData = &mCpuMpData.CpuData[ProcessorNumber];
877
878 if (!IsCurrentProcessorBSP ()) {
879 return EFI_DEVICE_ERROR;
880 }
881
882 if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
883 return EFI_NOT_FOUND;
884 }
885
886 if (IsProcessorBSP (ProcessorNumber)) {
887 return EFI_INVALID_PARAMETER;
888 }
889
890 if (GetApState (CpuData) != CpuStateIdle) {
891 return EFI_UNSUPPORTED;
892 }
893
894 if (EnableAP) {
895 if (!IsProcessorEnabled (ProcessorNumber)) {
896 mCpuMpData.NumberOfEnabledProcessors++;
897 }
898
899 StatusFlag |= PROCESSOR_ENABLED_BIT;
900 } else {
901 if (IsProcessorEnabled (ProcessorNumber) && !IsProcessorBSP (ProcessorNumber)) {
902 mCpuMpData.NumberOfEnabledProcessors--;
903 }
904
905 StatusFlag &= ~PROCESSOR_ENABLED_BIT;
906 }
907
908 if ((HealthFlag != NULL) && !IsProcessorBSP (ProcessorNumber)) {
909 StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
910 StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
911 }
912
913 mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag = StatusFlag;
914 return EFI_SUCCESS;
915}
916
917/**
918 This return the handle number for the calling processor. This service may be
919 called from the BSP and APs.
920
921 This service returns the processor handle number for the calling processor.
922 The returned value is in the range from 0 to the total number of logical
923 processors minus 1. The total number of logical processors can be retrieved
924 with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
925 called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
926 is returned. Otherwise, the current processors handle number is returned in
927 ProcessorNumber, and EFI_SUCCESS is returned.
928
929 @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
930 @param[out] ProcessorNumber The handle number of AP that is to become the new
931 BSP. The range is from 0 to the total number of
932 logical processors minus 1. The total number of
933 logical processors can be retrieved by
934 EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
935
936 @retval EFI_SUCCESS The current processor handle number was returned
937 in ProcessorNumber.
938 @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
939
940**/
941STATIC
942EFI_STATUS
943EFIAPI
944WhoAmI (
945 IN EFI_MP_SERVICES_PROTOCOL *This,
946 OUT UINTN *ProcessorNumber
947 )
948{
949 UINTN Index;
950 UINT64 ProcessorId;
951
952 if (ProcessorNumber == NULL) {
953 return EFI_INVALID_PARAMETER;
954 }
955
956 ProcessorId = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());
957 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
958 if (ProcessorId == gProcessorIDs[Index]) {
959 *ProcessorNumber = Index;
960 break;
961 }
962 }
963
964 return EFI_SUCCESS;
965}
966
967STATIC EFI_MP_SERVICES_PROTOCOL mMpServicesProtocol = {
968 GetNumberOfProcessors,
969 GetProcessorInfo,
970 StartupAllAPs,
971 StartupThisAP,
972 SwitchBSP,
973 EnableDisableAP,
974 WhoAmI
975};
976
977/** Adds the specified processor the list of failed processors.
978
979 @param ProcessorIndex The processor index to add.
980 @param ApState Processor state.
981
982**/
983STATIC
984VOID
985AddProcessorToFailedList (
986 UINTN ProcessorIndex,
987 CPU_STATE ApState
988 )
989{
990 UINTN Index;
991 BOOLEAN Found;
992
993 Found = FALSE;
994
995 if ((mCpuMpData.FailedList == NULL) ||
996 (ApState == CpuStateIdle) ||
997 (ApState == CpuStateFinished) ||
998 IsProcessorBSP (ProcessorIndex))
999 {
1000 return;
1001 }
1002
1003 // If we are retrying make sure we don't double count
1004 for (Index = 0; Index < mCpuMpData.FailedListIndex; Index++) {
1005 if (mCpuMpData.FailedList[Index] == ProcessorIndex) {
1006 Found = TRUE;
1007 break;
1008 }
1009 }
1010
1011 /* If the CPU isn't already in the FailedList, add it */
1012 if (!Found) {
1013 mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = ProcessorIndex;
1014 }
1015}
1016
1017/** Handles the StartupAllAPs case where the timeout has occurred.
1018
1019**/
1020STATIC
1021VOID
1022ProcessStartupAllAPsTimeout (
1023 VOID
1024 )
1025{
1026 CPU_AP_DATA *CpuData;
1027 UINTN Index;
1028
1029 if (mCpuMpData.FailedList == NULL) {
1030 return;
1031 }
1032
1033 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1034 CpuData = &mCpuMpData.CpuData[Index];
1035 if (IsProcessorBSP (Index)) {
1036 // Skip BSP
1037 continue;
1038 }
1039
1040 if (!IsProcessorEnabled (Index)) {
1041 // Skip Disabled processors
1042 continue;
1043 }
1044
1045 CpuData = &mCpuMpData.CpuData[Index];
1046 AddProcessorToFailedList (Index, GetApState (CpuData));
1047 }
1048}
1049
1050/** Updates the status of the APs.
1051
1052 @param[in] ProcessorIndex The index of the AP to update.
1053**/
1054STATIC
1055VOID
1056UpdateApStatus (
1057 IN UINTN ProcessorIndex
1058 )
1059{
1060 EFI_STATUS Status;
1061 CPU_AP_DATA *CpuData;
1062 CPU_AP_DATA *NextCpuData;
1063 CPU_STATE State;
1064 UINTN NextNumber;
1065
1066 CpuData = &mCpuMpData.CpuData[ProcessorIndex];
1067
1068 if (IsProcessorBSP (ProcessorIndex)) {
1069 // Skip BSP
1070 return;
1071 }
1072
1073 if (!IsProcessorEnabled (ProcessorIndex)) {
1074 // Skip Disabled processors
1075 return;
1076 }
1077
1078 State = GetApState (CpuData);
1079
1080 switch (State) {
1081 case CpuStateFinished:
1082 if (mCpuMpData.SingleThread) {
1083 Status = GetNextBlockedNumber (&NextNumber);
1084 if (!EFI_ERROR (Status)) {
1085 NextCpuData = &mCpuMpData.CpuData[NextNumber];
1086
1087 NextCpuData->State = CpuStateReady;
1088
1089 SetApProcedure (
1090 NextCpuData,
1091 mCpuMpData.Procedure,
1092 mCpuMpData.ProcedureArgument
1093 );
1094
1095 Status = DispatchCpu (NextNumber);
1096 if (!EFI_ERROR (Status)) {
1097 mCpuMpData.StartCount++;
1098 } else {
1099 AddProcessorToFailedList (NextNumber, NextCpuData->State);
1100 }
1101 }
1102 }
1103
1104 CpuData->State = CpuStateIdle;
1105 mCpuMpData.FinishCount++;
1106 break;
1107
1108 default:
1109 break;
1110 }
1111}
1112
1113/**
1114 If a timeout is specified in StartupAllAps(), a timer is set, which invokes
1115 this procedure periodically to check whether all APs have finished.
1116
1117 @param[in] Event The WaitEvent the user supplied.
1118 @param[in] Context The event context.
1119**/
1120STATIC
1121VOID
1122EFIAPI
1123CheckAllAPsStatus (
1124 IN EFI_EVENT Event,
1125 IN VOID *Context
1126 )
1127{
1128 EFI_STATUS Status;
1129 UINTN Index;
1130
1131 mCpuMpData.AllTimeTaken += POLL_INTERVAL_US;
1132
1133 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1134 UpdateApStatus (Index);
1135 }
1136
1137 if (mCpuMpData.AllTimeoutActive && (mCpuMpData.AllTimeTaken > mCpuMpData.AllTimeout)) {
1138 ProcessStartupAllAPsTimeout ();
1139
1140 // Force terminal exit
1141 mCpuMpData.FinishCount = mCpuMpData.StartCount;
1142 }
1143
1144 if (mCpuMpData.FinishCount != mCpuMpData.StartCount) {
1145 return;
1146 }
1147
1148 gBS->SetTimer (
1149 mCpuMpData.CheckAllAPsEvent,
1150 TimerCancel,
1151 0
1152 );
1153
1154 if (mCpuMpData.FailedListIndex == 0) {
1155 if (mCpuMpData.FailedList != NULL) {
1156 // Since we don't have the original `FailedCpuList`
1157 // pointer here to set to NULL, don't free the
1158 // memory.
1159 }
1160 }
1161
1162 Status = gBS->SignalEvent (mCpuMpData.AllWaitEvent);
1163 ASSERT_EFI_ERROR (Status);
1164 mCpuMpData.AllWaitEvent = NULL;
1165}
1166
1167/** Invoked periodically via a timer to check the state of the processor.
1168
1169 @param Event The event supplied by the timer expiration.
1170 @param Context The processor context.
1171
1172**/
1173STATIC
1174VOID
1175EFIAPI
1176CheckThisAPStatus (
1177 IN EFI_EVENT Event,
1178 IN VOID *Context
1179 )
1180{
1181 EFI_STATUS Status;
1182 CPU_AP_DATA *CpuData;
1183 CPU_STATE State;
1184
1185 CpuData = Context;
1186
1187 CpuData->TimeTaken += POLL_INTERVAL_US;
1188
1189 State = GetApState (CpuData);
1190
1191 if (State == CpuStateFinished) {
1192 Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);
1193 ASSERT_EFI_ERROR (Status);
1194
1195 if (CpuData->SingleApFinished != NULL) {
1196 *(CpuData->SingleApFinished) = TRUE;
1197 }
1198
1199 if (CpuData->WaitEvent != NULL) {
1200 Status = gBS->SignalEvent (CpuData->WaitEvent);
1201 ASSERT_EFI_ERROR (Status);
1202 }
1203
1204 CpuData->State = CpuStateIdle;
1205 }
1206
1207 if (CpuData->TimeoutActive && (CpuData->TimeTaken > CpuData->Timeout)) {
1208 Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);
1209 if (CpuData->WaitEvent != NULL) {
1210 Status = gBS->SignalEvent (CpuData->WaitEvent);
1211 ASSERT_EFI_ERROR (Status);
1212 CpuData->WaitEvent = NULL;
1213 }
1214 }
1215}
1216
1217/**
1218 This function is called by all processors (both BSP and AP) once and collects
1219 MP related data.
1220
1221 @param BSP TRUE if the processor is the BSP.
1222 @param Mpidr The MPIDR for the specified processor. This should be
1223 the full MPIDR and not only the affinity bits.
1224 @param ProcessorIndex The index of the processor.
1225
1226 @return EFI_SUCCESS if the data for the processor collected and filled in.
1227
1228**/
1229STATIC
1230EFI_STATUS
1231FillInProcessorInformation (
1232 IN BOOLEAN BSP,
1233 IN UINTN Mpidr,
1234 IN UINTN ProcessorIndex
1235 )
1236{
1237 EFI_PROCESSOR_INFORMATION *CpuInfo;
1238
1239 CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
1240
1241 CpuInfo->ProcessorId = GET_MPIDR_AFFINITY_BITS (Mpidr);
1242 CpuInfo->StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
1243
1244 if (BSP) {
1245 CpuInfo->StatusFlag |= PROCESSOR_AS_BSP_BIT;
1246 }
1247
1248 if ((Mpidr & MPIDR_MT_BIT) > 0) {
1249 CpuInfo->Location.Package = GET_MPIDR_AFF2 (Mpidr);
1250 CpuInfo->Location.Core = GET_MPIDR_AFF1 (Mpidr);
1251 CpuInfo->Location.Thread = GET_MPIDR_AFF0 (Mpidr);
1252
1253 CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF3 (Mpidr);
1254 CpuInfo->ExtendedInformation.Location2.Die = GET_MPIDR_AFF2 (Mpidr);
1255 CpuInfo->ExtendedInformation.Location2.Core = GET_MPIDR_AFF1 (Mpidr);
1256 CpuInfo->ExtendedInformation.Location2.Thread = GET_MPIDR_AFF0 (Mpidr);
1257 } else {
1258 CpuInfo->Location.Package = GET_MPIDR_AFF1 (Mpidr);
1259 CpuInfo->Location.Core = GET_MPIDR_AFF0 (Mpidr);
1260 CpuInfo->Location.Thread = 0;
1261
1262 CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF2 (Mpidr);
1263 CpuInfo->ExtendedInformation.Location2.Die = GET_MPIDR_AFF1 (Mpidr);
1264 CpuInfo->ExtendedInformation.Location2.Core = GET_MPIDR_AFF0 (Mpidr);
1265 CpuInfo->ExtendedInformation.Location2.Thread = 0;
1266 }
1267
1268 mCpuMpData.CpuData[ProcessorIndex].State = BSP ? CpuStateBusy : CpuStateIdle;
1269
1270 mCpuMpData.CpuData[ProcessorIndex].Procedure = NULL;
1271 mCpuMpData.CpuData[ProcessorIndex].Parameter = NULL;
1272
1273 return EFI_SUCCESS;
1274}
1275
1276/** Initializes the MP Services system data
1277
1278 @param NumberOfProcessors The number of processors, both BSP and AP.
1279 @param CoreInfo CPU information gathered earlier during boot.
1280
1281**/
1282STATIC
1283EFI_STATUS
1284MpServicesInitialize (
1285 IN UINTN NumberOfProcessors,
1286 IN CONST ARM_CORE_INFO *CoreInfo
1287 )
1288{
1289 EFI_STATUS Status;
1290 UINTN Index;
1291 EFI_EVENT ReadyToBootEvent;
1292 BOOLEAN IsBsp;
1293
1294 //
1295 // Clear the data structure area first.
1296 //
1297 ZeroMem (&mCpuMpData, sizeof (CPU_MP_DATA));
1298 //
1299 // First BSP fills and inits all known values, including its own records.
1300 //
1301 mCpuMpData.NumberOfProcessors = NumberOfProcessors;
1302 mCpuMpData.NumberOfEnabledProcessors = NumberOfProcessors;
1303
1304 mCpuMpData.CpuData = AllocateZeroPool (
1305 mCpuMpData.NumberOfProcessors * sizeof (CPU_AP_DATA)
1306 );
1307
1308 if (mCpuMpData.CpuData == NULL) {
1309 return EFI_OUT_OF_RESOURCES;
1310 }
1311
1312 /* Allocate one extra for the sentinel entry at the end */
1313 gProcessorIDs = AllocateZeroPool ((mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64));
1314 ASSERT (gProcessorIDs != NULL);
1315
1316 Status = gBS->CreateEvent (
1317 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1318 TPL_CALLBACK,
1319 CheckAllAPsStatus,
1320 NULL,
1321 &mCpuMpData.CheckAllAPsEvent
1322 );
1323 ASSERT_EFI_ERROR (Status);
1324
1325 gApStacksBase = AllocatePages (
1326 EFI_SIZE_TO_PAGES (
1327 mCpuMpData.NumberOfProcessors *
1328 gApStackSize
1329 )
1330 );
1331 ASSERT (gApStacksBase != NULL);
1332
1333 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1334 if (GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ()) == CoreInfo[Index].Mpidr) {
1335 IsBsp = TRUE;
1336 } else {
1337 IsBsp = FALSE;
1338 }
1339
1340 FillInProcessorInformation (IsBsp, CoreInfo[Index].Mpidr, Index);
1341
1342 gProcessorIDs[Index] = mCpuMpData.CpuData[Index].Info.ProcessorId;
1343
1344 Status = gBS->CreateEvent (
1345 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1346 TPL_CALLBACK,
1347 CheckThisAPStatus,
1348 (VOID *)&mCpuMpData.CpuData[Index],
1349 &mCpuMpData.CpuData[Index].CheckThisAPEvent
1350 );
1351 ASSERT_EFI_ERROR (Status);
1352 }
1353
1354 gProcessorIDs[Index] = MAX_UINT64;
1355
1356 gTcr = ArmGetTCR ();
1357 gMair = ArmGetMAIR ();
1358 gTtbr0 = ArmGetTTBR0BaseAddress ();
1359
1360 //
1361 // The global pointer variables as well as the gProcessorIDs array contents
1362 // are accessed by the other cores so we must clean them to the PoC
1363 //
1364 WriteBackDataCacheRange (&gProcessorIDs, sizeof (UINT64 *));
1365 WriteBackDataCacheRange (&gApStacksBase, sizeof (UINT64 *));
1366
1367 WriteBackDataCacheRange (
1368 gProcessorIDs,
1369 (mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64)
1370 );
1371
1372 mNonBlockingModeAllowed = TRUE;
1373
1374 Status = EfiCreateEventReadyToBootEx (
1375 TPL_CALLBACK,
1376 ReadyToBootSignaled,
1377 NULL,
1378 &ReadyToBootEvent
1379 );
1380 ASSERT_EFI_ERROR (Status);
1381
1382 return EFI_SUCCESS;
1383}
1384
1385/**
1386 Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
1387 signaled. After this point, non-blocking mode is no longer allowed.
1388
1389 @param Event Event whose notification function is being invoked.
1390 @param Context The pointer to the notification function's context,
1391 which is implementation-dependent.
1392
1393**/
1394STATIC
1395VOID
1396EFIAPI
1397ReadyToBootSignaled (
1398 IN EFI_EVENT Event,
1399 IN VOID *Context
1400 )
1401{
1402 mNonBlockingModeAllowed = FALSE;
1403}
1404
1405/** Initialize multi-processor support.
1406
1407 @param ImageHandle Image handle.
1408 @param SystemTable System table.
1409
1410 @return EFI_SUCCESS on success, or an error code.
1411
1412**/
1413EFI_STATUS
1414EFIAPI
1415ArmPsciMpServicesDxeInitialize (
1416 IN EFI_HANDLE ImageHandle,
1417 IN EFI_SYSTEM_TABLE *SystemTable
1418 )
1419{
1420 EFI_STATUS Status;
1421 EFI_HANDLE Handle;
1422 UINTN MaxCpus;
1423 EFI_LOADED_IMAGE_PROTOCOL *Image;
1424 EFI_HOB_GENERIC_HEADER *Hob;
1425 VOID *HobData;
1426 UINTN HobDataSize;
1427 CONST ARM_CORE_INFO *CoreInfo;
1428
1429 MaxCpus = 1;
1430
1431 Status = gBS->HandleProtocol (
1432 ImageHandle,
1433 &gEfiLoadedImageProtocolGuid,
1434 (VOID **)&Image
1435 );
1436 ASSERT_EFI_ERROR (Status);
1437
1438 //
1439 // Parts of the code in this driver may be executed by other cores running
1440 // with the MMU off so we need to ensure that everything is clean to the
1441 // point of coherency (PoC)
1442 //
1443 WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize);
1444
1445 Hob = GetFirstGuidHob (&gArmMpCoreInfoGuid);
1446 if (Hob != NULL) {
1447 HobData = GET_GUID_HOB_DATA (Hob);
1448 HobDataSize = GET_GUID_HOB_DATA_SIZE (Hob);
1449 CoreInfo = (ARM_CORE_INFO *)HobData;
1450 MaxCpus = HobDataSize / sizeof (ARM_CORE_INFO);
1451 }
1452
1453 if (MaxCpus == 1) {
1454 DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP system"));
1455 // We are not MP so nothing to do
1456 return EFI_NOT_FOUND;
1457 }
1458
1459 Status = MpServicesInitialize (MaxCpus, CoreInfo);
1460 if (Status != EFI_SUCCESS) {
1461 ASSERT_EFI_ERROR (Status);
1462 return Status;
1463 }
1464
1465 //
1466 // Now install the MP services protocol.
1467 //
1468 Handle = NULL;
1469 Status = gBS->InstallMultipleProtocolInterfaces (
1470 &Handle,
1471 &gEfiMpServiceProtocolGuid,
1472 &mMpServicesProtocol,
1473 NULL
1474 );
1475 ASSERT_EFI_ERROR (Status);
1476
1477 return Status;
1478}
1479
1480/** AP exception handler.
1481
1482 @param InterruptType The AArch64 CPU exception type.
1483 @param SystemContext System context.
1484
1485**/
1486STATIC
1487VOID
1488EFIAPI
1489ApExceptionHandler (
1490 IN CONST EFI_EXCEPTION_TYPE InterruptType,
1491 IN CONST EFI_SYSTEM_CONTEXT SystemContext
1492 )
1493{
1494 ARM_SMC_ARGS Args;
1495 UINT64 Mpidr;
1496 UINTN Index;
1497 UINTN ProcessorIndex;
1498
1499 Mpidr = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());
1500
1501 Index = 0;
1502 ProcessorIndex = MAX_UINT64;
1503
1504 do {
1505 if (gProcessorIDs[Index] == Mpidr) {
1506 ProcessorIndex = Index;
1507 break;
1508 }
1509
1510 Index++;
1511 } while (gProcessorIDs[Index] != MAX_UINT64);
1512
1513 if (ProcessorIndex != MAX_UINT64) {
1514 mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;
1515 ArmDataMemoryBarrier ();
1516 }
1517
1518 Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;
1519 ArmCallSmc (&Args);
1520
1521 /* Should never be reached */
1522 ASSERT (FALSE);
1523 CpuDeadLoop ();
1524}
1525
1526/** C entry-point for the AP.
1527 This function gets called from the assembly function ApEntryPoint.
1528
1529**/
1530VOID
1531ApProcedure (
1532 VOID
1533 )
1534{
1535 ARM_SMC_ARGS Args;
1536 EFI_AP_PROCEDURE UserApProcedure;
1537 VOID *UserApParameter;
1538 UINTN ProcessorIndex;
1539
1540 ProcessorIndex = 0;
1541
1542 WhoAmI (&mMpServicesProtocol, &ProcessorIndex);
1543
1544 /* Fetch the user-supplied procedure and parameter to execute */
1545 UserApProcedure = mCpuMpData.CpuData[ProcessorIndex].Procedure;
1546 UserApParameter = mCpuMpData.CpuData[ProcessorIndex].Parameter;
1547
1548 InitializeCpuExceptionHandlers (NULL);
1549 RegisterCpuInterruptHandler (EXCEPT_AARCH64_SYNCHRONOUS_EXCEPTIONS, ApExceptionHandler);
1550 RegisterCpuInterruptHandler (EXCEPT_AARCH64_IRQ, ApExceptionHandler);
1551 RegisterCpuInterruptHandler (EXCEPT_AARCH64_FIQ, ApExceptionHandler);
1552 RegisterCpuInterruptHandler (EXCEPT_AARCH64_SERROR, ApExceptionHandler);
1553
1554 UserApProcedure (UserApParameter);
1555
1556 mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;
1557
1558 ArmDataMemoryBarrier ();
1559
1560 /* Since we're finished with this AP, turn it off */
1561 Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;
1562 ArmCallSmc (&Args);
1563
1564 /* Should never be reached */
1565 ASSERT (FALSE);
1566 CpuDeadLoop ();
1567}
1568
1569/** Returns whether the processor executing this function is the BSP.
1570
1571 @return Whether the current processor is the BSP.
1572**/
1573STATIC
1574BOOLEAN
1575IsCurrentProcessorBSP (
1576 VOID
1577 )
1578{
1579 EFI_STATUS Status;
1580 UINTN ProcessorIndex;
1581
1582 Status = WhoAmI (&mMpServicesProtocol, &ProcessorIndex);
1583 if (EFI_ERROR (Status)) {
1584 ASSERT_EFI_ERROR (Status);
1585 return FALSE;
1586 }
1587
1588 return IsProcessorBSP (ProcessorIndex);
1589}
1590
1591/** Returns whether the specified processor is enabled.
1592
1593 @param[in] ProcessorIndex The index of the processor to check.
1594
1595 @return TRUE if the processor is enabled, FALSE otherwise.
1596**/
1597STATIC
1598BOOLEAN
1599IsProcessorEnabled (
1600 UINTN ProcessorIndex
1601 )
1602{
1603 EFI_PROCESSOR_INFORMATION *CpuInfo;
1604
1605 CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
1606
1607 return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) != 0;
1608}
1609
1610/** Sets up the state for the StartupAllAPs function.
1611
1612 @param SingleThread Whether the APs will execute sequentially.
1613
1614**/
1615STATIC
1616VOID
1617StartupAllAPsPrepareState (
1618 IN BOOLEAN SingleThread
1619 )
1620{
1621 UINTN Index;
1622 CPU_STATE APInitialState;
1623 CPU_AP_DATA *CpuData;
1624
1625 mCpuMpData.FinishCount = 0;
1626 mCpuMpData.StartCount = 0;
1627 mCpuMpData.SingleThread = SingleThread;
1628
1629 APInitialState = CpuStateReady;
1630
1631 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1632 CpuData = &mCpuMpData.CpuData[Index];
1633
1634 //
1635 // Get APs prepared, and put failing APs into FailedCpuList.
1636 // If "SingleThread", only 1 AP will put into ready state, other AP will be
1637 // put into ready state 1 by 1, until the previous 1 finished its task.
1638 // If not "SingleThread", all APs are put into ready state from the
1639 // beginning
1640 //
1641
1642 if (IsProcessorBSP (Index)) {
1643 // Skip BSP
1644 continue;
1645 }
1646
1647 if (!IsProcessorEnabled (Index)) {
1648 // Skip Disabled processors
1649 if (mCpuMpData.FailedList != NULL) {
1650 mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
1651 }
1652
1653 continue;
1654 }
1655
1656 // If any APs finished after timing out, reset state to Idle
1657 if (GetApState (CpuData) == CpuStateFinished) {
1658 CpuData->State = CpuStateIdle;
1659 }
1660
1661 if (GetApState (CpuData) != CpuStateIdle) {
1662 // Skip busy processors
1663 if (mCpuMpData.FailedList != NULL) {
1664 mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
1665 }
1666 }
1667
1668 CpuData->State = APInitialState;
1669
1670 mCpuMpData.StartCount++;
1671 if (SingleThread) {
1672 APInitialState = CpuStateBlocked;
1673 }
1674 }
1675}
1676
1677/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
1678
1679 @param Procedure The user-supplied procedure.
1680 @param ProcedureArgument The user-supplied procedure argument.
1681 @param WaitEvent The wait event to be signaled when the work is
1682 complete or a timeout has occurred.
1683 @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
1684 indicates an infinite timeout.
1685 @param SingleThread Whether the APs will execute sequentially.
1686 @param FailedCpuList User-supplied pointer for list of failed CPUs.
1687
1688 @return EFI_SUCCESS on success.
1689**/
1690STATIC
1691EFI_STATUS
1692StartupAllAPsWithWaitEvent (
1693 IN EFI_AP_PROCEDURE Procedure,
1694 IN VOID *ProcedureArgument,
1695 IN EFI_EVENT WaitEvent,
1696 IN UINTN TimeoutInMicroseconds,
1697 IN BOOLEAN SingleThread,
1698 IN UINTN **FailedCpuList
1699 )
1700{
1701 EFI_STATUS Status;
1702 UINTN Index;
1703 CPU_AP_DATA *CpuData;
1704
1705 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1706 CpuData = &mCpuMpData.CpuData[Index];
1707 if (IsProcessorBSP (Index)) {
1708 // Skip BSP
1709 continue;
1710 }
1711
1712 if (!IsProcessorEnabled (Index)) {
1713 // Skip Disabled processors
1714 continue;
1715 }
1716
1717 if (GetApState (CpuData) == CpuStateReady) {
1718 SetApProcedure (CpuData, Procedure, ProcedureArgument);
1719 if ((mCpuMpData.StartCount == 0) || !SingleThread) {
1720 Status = DispatchCpu (Index);
1721 if (EFI_ERROR (Status)) {
1722 AddProcessorToFailedList (Index, CpuData->State);
1723 break;
1724 }
1725 }
1726 }
1727 }
1728
1729 if (EFI_ERROR (Status)) {
1730 return EFI_NOT_READY;
1731 }
1732
1733 //
1734 // Save data into private data structure, and create timer to poll AP state
1735 // before exiting
1736 //
1737 mCpuMpData.Procedure = Procedure;
1738 mCpuMpData.ProcedureArgument = ProcedureArgument;
1739 mCpuMpData.AllWaitEvent = WaitEvent;
1740 mCpuMpData.AllTimeout = TimeoutInMicroseconds;
1741 mCpuMpData.AllTimeTaken = 0;
1742 mCpuMpData.AllTimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);
1743 Status = gBS->SetTimer (
1744 mCpuMpData.CheckAllAPsEvent,
1745 TimerPeriodic,
1746 POLL_INTERVAL_US
1747 );
1748
1749 return Status;
1750}
1751
1752/** Handles execution of StartupAllAPs when no wait event has been specified.
1753
1754 @param Procedure The user-supplied procedure.
1755 @param ProcedureArgument The user-supplied procedure argument.
1756 @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
1757 indicates an infinite timeout.
1758 @param SingleThread Whether the APs will execute sequentially.
1759 @param FailedCpuList User-supplied pointer for list of failed CPUs.
1760
1761 @return EFI_SUCCESS on success.
1762**/
1763STATIC
1764EFI_STATUS
1765StartupAllAPsNoWaitEvent (
1766 IN EFI_AP_PROCEDURE Procedure,
1767 IN VOID *ProcedureArgument,
1768 IN UINTN TimeoutInMicroseconds,
1769 IN BOOLEAN SingleThread,
1770 IN UINTN **FailedCpuList
1771 )
1772{
1773 EFI_STATUS Status;
1774 UINTN Index;
1775 UINTN NextIndex;
1776 UINTN Timeout;
1777 CPU_AP_DATA *CpuData;
1778 BOOLEAN DispatchError;
1779
1780 Timeout = TimeoutInMicroseconds;
1781 DispatchError = FALSE;
1782
1783 while (TRUE) {
1784 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1785 CpuData = &mCpuMpData.CpuData[Index];
1786 if (IsProcessorBSP (Index)) {
1787 // Skip BSP
1788 continue;
1789 }
1790
1791 if (!IsProcessorEnabled (Index)) {
1792 // Skip Disabled processors
1793 continue;
1794 }
1795
1796 switch (GetApState (CpuData)) {
1797 case CpuStateReady:
1798 SetApProcedure (CpuData, Procedure, ProcedureArgument);
1799 Status = DispatchCpu (Index);
1800 if (EFI_ERROR (Status)) {
1801 AddProcessorToFailedList (Index, CpuData->State);
1802 CpuData->State = CpuStateIdle;
1803 mCpuMpData.StartCount--;
1804 DispatchError = TRUE;
1805
1806 if (SingleThread) {
1807 // Dispatch the next available AP
1808 Status = GetNextBlockedNumber (&NextIndex);
1809 if (!EFI_ERROR (Status)) {
1810 mCpuMpData.CpuData[NextIndex].State = CpuStateReady;
1811 }
1812 }
1813 }
1814
1815 break;
1816
1817 case CpuStateFinished:
1818 mCpuMpData.FinishCount++;
1819 if (SingleThread) {
1820 Status = GetNextBlockedNumber (&NextIndex);
1821 if (!EFI_ERROR (Status)) {
1822 mCpuMpData.CpuData[NextIndex].State = CpuStateReady;
1823 }
1824 }
1825
1826 CpuData->State = CpuStateIdle;
1827 break;
1828
1829 default:
1830 break;
1831 }
1832 }
1833
1834 if (mCpuMpData.FinishCount == mCpuMpData.StartCount) {
1835 Status = EFI_SUCCESS;
1836 break;
1837 }
1838
1839 if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
1840 Status = EFI_TIMEOUT;
1841 break;
1842 }
1843
1844 Timeout -= CalculateAndStallInterval (Timeout);
1845 }
1846
1847 if (Status == EFI_TIMEOUT) {
1848 // Add any remaining CPUs to the FailedCpuList
1849 if (FailedCpuList != NULL) {
1850 for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
1851 AddProcessorToFailedList (Index, mCpuMpData.CpuData[Index].State);
1852 }
1853 }
1854 }
1855
1856 if (DispatchError) {
1857 Status = EFI_NOT_READY;
1858 }
1859
1860 return Status;
1861}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette