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