1 /** @file
2   CPU PEI Module installs CPU Multiple Processor PPI.
3 
4   Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "CpuMpPei.h"
10 
11 extern EDKII_PEI_MP_SERVICES2_PPI            mMpServices2Ppi;
12 
13 //
14 // CPU MP PPI to be installed
15 //
16 EFI_PEI_MP_SERVICES_PPI                mMpServicesPpi = {
17   PeiGetNumberOfProcessors,
18   PeiGetProcessorInfo,
19   PeiStartupAllAPs,
20   PeiStartupThisAP,
21   PeiSwitchBSP,
22   PeiEnableDisableAP,
23   PeiWhoAmI,
24 };
25 
26 EFI_PEI_PPI_DESCRIPTOR           mPeiCpuMpPpiList[] = {
27   {
28     EFI_PEI_PPI_DESCRIPTOR_PPI,
29     &gEdkiiPeiMpServices2PpiGuid,
30     &mMpServices2Ppi
31   },
32   {
33     (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
34     &gEfiPeiMpServicesPpiGuid,
35     &mMpServicesPpi
36   }
37 };
38 
39 /**
40   This service retrieves the number of logical processor in the platform
41   and the number of those logical processors that are enabled on this boot.
42   This service may only be called from the BSP.
43 
44   This function is used to retrieve the following information:
45     - The number of logical processors that are present in the system.
46     - The number of enabled logical processors in the system at the instant
47       this call is made.
48 
49   Because MP Service Ppi provides services to enable and disable processors
50   dynamically, the number of enabled logical processors may vary during the
51   course of a boot session.
52 
53   If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
54   If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
55   EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
56   is returned in NumberOfProcessors, the number of currently enabled processor
57   is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
58 
59   @param[in]  PeiServices         An indirect pointer to the PEI Services Table
60                                   published by the PEI Foundation.
61   @param[in]  This                Pointer to this instance of the PPI.
62   @param[out] NumberOfProcessors  Pointer to the total number of logical processors in
63                                   the system, including the BSP and disabled APs.
64   @param[out] NumberOfEnabledProcessors
65                                   Number of processors in the system that are enabled.
66 
67   @retval EFI_SUCCESS             The number of logical processors and enabled
68                                   logical processors was retrieved.
69   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
70   @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
71                                   NumberOfEnabledProcessors is NULL.
72 **/
73 EFI_STATUS
74 EFIAPI
PeiGetNumberOfProcessors(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,OUT UINTN * NumberOfProcessors,OUT UINTN * NumberOfEnabledProcessors)75 PeiGetNumberOfProcessors (
76   IN  CONST EFI_PEI_SERVICES    **PeiServices,
77   IN  EFI_PEI_MP_SERVICES_PPI   *This,
78   OUT UINTN                     *NumberOfProcessors,
79   OUT UINTN                     *NumberOfEnabledProcessors
80   )
81 {
82   if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
83     return EFI_INVALID_PARAMETER;
84   }
85 
86   return MpInitLibGetNumberOfProcessors (
87            NumberOfProcessors,
88            NumberOfEnabledProcessors
89            );
90 }
91 
92 /**
93   Gets detailed MP-related information on the requested processor at the
94   instant this call is made. This service may only be called from the BSP.
95 
96   This service retrieves detailed MP-related information about any processor
97   on the platform. Note the following:
98     - The processor information may change during the course of a boot session.
99     - The information presented here is entirely MP related.
100 
101   Information regarding the number of caches and their sizes, frequency of operation,
102   slot numbers is all considered platform-related information and is not provided
103   by this service.
104 
105   @param[in]  PeiServices         An indirect pointer to the PEI Services Table
106                                   published by the PEI Foundation.
107   @param[in]  This                Pointer to this instance of the PPI.
108   @param[in]  ProcessorNumber     Pointer to the total number of logical processors in
109                                   the system, including the BSP and disabled APs.
110   @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled.
111 
112   @retval EFI_SUCCESS             Processor information was returned.
113   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
114   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
115   @retval EFI_NOT_FOUND           The processor with the handle specified by
116                                   ProcessorNumber does not exist in the platform.
117 **/
118 EFI_STATUS
119 EFIAPI
PeiGetProcessorInfo(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,IN UINTN ProcessorNumber,OUT EFI_PROCESSOR_INFORMATION * ProcessorInfoBuffer)120 PeiGetProcessorInfo (
121   IN  CONST EFI_PEI_SERVICES     **PeiServices,
122   IN  EFI_PEI_MP_SERVICES_PPI    *This,
123   IN  UINTN                      ProcessorNumber,
124   OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
125   )
126 {
127   return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL);
128 }
129 
130 /**
131   This service executes a caller provided function on all enabled APs. APs can
132   run either simultaneously or one at a time in sequence. This service supports
133   both blocking requests only. This service may only
134   be called from the BSP.
135 
136   This function is used to dispatch all the enabled APs to the function specified
137   by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
138   immediately and Procedure is not started on any AP.
139 
140   If SingleThread is TRUE, all the enabled APs execute the function specified by
141   Procedure one by one, in ascending order of processor handle number. Otherwise,
142   all the enabled APs execute the function specified by Procedure simultaneously.
143 
144   If the timeout specified by TimeoutInMicroSeconds expires before all APs return
145   from Procedure, then Procedure on the failed APs is terminated. All enabled APs
146   are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
147   and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its
148   content points to the list of processor handle numbers in which Procedure was
149   terminated.
150 
151   Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
152   to make sure that the nature of the code that is executed on the BSP and the
153   dispatched APs is well controlled. The MP Services Ppi does not guarantee
154   that the Procedure function is MP-safe. Hence, the tasks that can be run in
155   parallel are limited to certain independent tasks and well-controlled exclusive
156   code. PEI services and Ppis may not be called by APs unless otherwise
157   specified.
158 
159   In blocking execution mode, BSP waits until all APs finish or
160   TimeoutInMicroSeconds expires.
161 
162   @param[in] PeiServices          An indirect pointer to the PEI Services Table
163                                   published by the PEI Foundation.
164   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
165   @param[in] Procedure            A pointer to the function to be run on enabled APs of
166                                   the system.
167   @param[in] SingleThread         If TRUE, then all the enabled APs execute the function
168                                   specified by Procedure one by one, in ascending order
169                                   of processor handle number. If FALSE, then all the
170                                   enabled APs execute the function specified by Procedure
171                                   simultaneously.
172   @param[in] TimeoutInMicroSeconds
173                                   Indicates the time limit in microseconds for APs to
174                                   return from Procedure, for blocking mode only. Zero
175                                   means infinity. If the timeout expires before all APs
176                                   return from Procedure, then Procedure on the failed APs
177                                   is terminated. All enabled APs are available for next
178                                   function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
179                                   or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the
180                                   timeout expires in blocking mode, BSP returns
181                                   EFI_TIMEOUT.
182   @param[in] ProcedureArgument    The parameter passed into Procedure for all APs.
183 
184   @retval EFI_SUCCESS             In blocking mode, all APs have finished before the
185                                   timeout expired.
186   @retval EFI_DEVICE_ERROR        Caller processor is AP.
187   @retval EFI_NOT_STARTED         No enabled APs exist in the system.
188   @retval EFI_NOT_READY           Any enabled APs are busy.
189   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before all
190                                   enabled APs have finished.
191   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
192 **/
193 EFI_STATUS
194 EFIAPI
PeiStartupAllAPs(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,IN EFI_AP_PROCEDURE Procedure,IN BOOLEAN SingleThread,IN UINTN TimeoutInMicroSeconds,IN VOID * ProcedureArgument OPTIONAL)195 PeiStartupAllAPs (
196   IN  CONST EFI_PEI_SERVICES    **PeiServices,
197   IN  EFI_PEI_MP_SERVICES_PPI   *This,
198   IN  EFI_AP_PROCEDURE          Procedure,
199   IN  BOOLEAN                   SingleThread,
200   IN  UINTN                     TimeoutInMicroSeconds,
201   IN  VOID                      *ProcedureArgument      OPTIONAL
202   )
203 {
204   return MpInitLibStartupAllAPs (
205            Procedure,
206            SingleThread,
207            NULL,
208            TimeoutInMicroSeconds,
209            ProcedureArgument,
210            NULL
211            );
212 }
213 
214 /**
215   This service lets the caller get one enabled AP to execute a caller-provided
216   function. The caller can request the BSP to wait for the completion
217   of the AP. This service may only be called from the BSP.
218 
219   This function is used to dispatch one enabled AP to the function specified by
220   Procedure passing in the argument specified by ProcedureArgument.
221   The execution is in blocking mode. The BSP waits until the AP finishes or
222   TimeoutInMicroSecondss expires.
223 
224   If the timeout specified by TimeoutInMicroseconds expires before the AP returns
225   from Procedure, then execution of Procedure by the AP is terminated. The AP is
226   available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and
227   EFI_PEI_MP_SERVICES_PPI.StartupThisAP().
228 
229   @param[in] PeiServices          An indirect pointer to the PEI Services Table
230                                   published by the PEI Foundation.
231   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
232   @param[in] Procedure            A pointer to the function to be run on enabled APs of
233                                   the system.
234   @param[in] ProcessorNumber      The handle number of the AP. The range is from 0 to the
235                                   total number of logical processors minus 1. The total
236                                   number of logical processors can be retrieved by
237                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
238   @param[in] TimeoutInMicroseconds
239                                   Indicates the time limit in microseconds for APs to
240                                   return from Procedure, for blocking mode only. Zero
241                                   means infinity. If the timeout expires before all APs
242                                   return from Procedure, then Procedure on the failed APs
243                                   is terminated. All enabled APs are available for next
244                                   function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
245                                   or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the
246                                   timeout expires in blocking mode, BSP returns
247                                   EFI_TIMEOUT.
248   @param[in] ProcedureArgument    The parameter passed into Procedure for all APs.
249 
250   @retval EFI_SUCCESS             In blocking mode, specified AP finished before the
251                                   timeout expires.
252   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
253   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before the
254                                   specified AP has finished.
255   @retval EFI_NOT_FOUND           The processor with the handle specified by
256                                   ProcessorNumber does not exist.
257   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
258   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
259 **/
260 EFI_STATUS
261 EFIAPI
PeiStartupThisAP(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,IN EFI_AP_PROCEDURE Procedure,IN UINTN ProcessorNumber,IN UINTN TimeoutInMicroseconds,IN VOID * ProcedureArgument OPTIONAL)262 PeiStartupThisAP (
263   IN  CONST EFI_PEI_SERVICES    **PeiServices,
264   IN  EFI_PEI_MP_SERVICES_PPI   *This,
265   IN  EFI_AP_PROCEDURE          Procedure,
266   IN  UINTN                     ProcessorNumber,
267   IN  UINTN                     TimeoutInMicroseconds,
268   IN  VOID                      *ProcedureArgument      OPTIONAL
269   )
270 {
271   return MpInitLibStartupThisAP (
272            Procedure,
273            ProcessorNumber,
274            NULL,
275            TimeoutInMicroseconds,
276            ProcedureArgument,
277            NULL
278            );
279 }
280 
281 /**
282   This service switches the requested AP to be the BSP from that point onward.
283   This service changes the BSP for all purposes.   This call can only be performed
284   by the current BSP.
285 
286   This service switches the requested AP to be the BSP from that point onward.
287   This service changes the BSP for all purposes. The new BSP can take over the
288   execution of the old BSP and continue seamlessly from where the old one left
289   off.
290 
291   If the BSP cannot be switched prior to the return from this service, then
292   EFI_UNSUPPORTED must be returned.
293 
294   @param[in] PeiServices          An indirect pointer to the PEI Services Table
295                                   published by the PEI Foundation.
296   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
297   @param[in] ProcessorNumber      The handle number of the AP. The range is from 0 to the
298                                   total number of logical processors minus 1. The total
299                                   number of logical processors can be retrieved by
300                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
301   @param[in] EnableOldBSP         If TRUE, then the old BSP will be listed as an enabled
302                                   AP. Otherwise, it will be disabled.
303 
304   @retval EFI_SUCCESS             BSP successfully switched.
305   @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to this
306                                   service returning.
307   @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
308   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
309   @retval EFI_NOT_FOUND           The processor with the handle specified by
310                                   ProcessorNumber does not exist.
311   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or a disabled
312                                   AP.
313   @retval EFI_NOT_READY           The specified AP is busy.
314 **/
315 EFI_STATUS
316 EFIAPI
PeiSwitchBSP(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,IN UINTN ProcessorNumber,IN BOOLEAN EnableOldBSP)317 PeiSwitchBSP (
318   IN  CONST EFI_PEI_SERVICES   **PeiServices,
319   IN  EFI_PEI_MP_SERVICES_PPI  *This,
320   IN  UINTN                    ProcessorNumber,
321   IN  BOOLEAN                  EnableOldBSP
322   )
323 {
324   return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP);
325 }
326 
327 /**
328   This service lets the caller enable or disable an AP from this point onward.
329   This service may only be called from the BSP.
330 
331   This service allows the caller enable or disable an AP from this point onward.
332   The caller can optionally specify the health status of the AP by Health. If
333   an AP is being disabled, then the state of the disabled AP is implementation
334   dependent. If an AP is enabled, then the implementation must guarantee that a
335   complete initialization sequence is performed on the AP, so the AP is in a state
336   that is compatible with an MP operating system.
337 
338   If the enable or disable AP operation cannot be completed prior to the return
339   from this service, then EFI_UNSUPPORTED must be returned.
340 
341   @param[in] PeiServices          An indirect pointer to the PEI Services Table
342                                   published by the PEI Foundation.
343   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
344   @param[in] ProcessorNumber      The handle number of the AP. The range is from 0 to the
345                                   total number of logical processors minus 1. The total
346                                   number of logical processors can be retrieved by
347                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
348   @param[in] EnableAP             Specifies the new state for the processor for enabled,
349                                   FALSE for disabled.
350   @param[in] HealthFlag           If not NULL, a pointer to a value that specifies the
351                                   new health status of the AP. This flag corresponds to
352                                   StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo().
353                                   Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other
354                                   bits are ignored. If it is NULL, this parameter is
355                                   ignored.
356 
357   @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
358   @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed prior
359                                   to this service returning.
360   @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
361   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
362   @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
363                                   does not exist.
364   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
365 **/
366 EFI_STATUS
367 EFIAPI
PeiEnableDisableAP(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,IN UINTN ProcessorNumber,IN BOOLEAN EnableAP,IN UINT32 * HealthFlag OPTIONAL)368 PeiEnableDisableAP (
369   IN  CONST EFI_PEI_SERVICES    **PeiServices,
370   IN  EFI_PEI_MP_SERVICES_PPI   *This,
371   IN  UINTN                     ProcessorNumber,
372   IN  BOOLEAN                   EnableAP,
373   IN  UINT32                    *HealthFlag OPTIONAL
374   )
375 {
376   return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag);
377 }
378 
379 /**
380   This return the handle number for the calling processor.  This service may be
381   called from the BSP and APs.
382 
383   This service returns the processor handle number for the calling processor.
384   The returned value is in the range from 0 to the total number of logical
385   processors minus 1. The total number of logical processors can be retrieved
386   with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be
387   called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
388   is returned. Otherwise, the current processors handle number is returned in
389   ProcessorNumber, and EFI_SUCCESS is returned.
390 
391   @param[in]  PeiServices         An indirect pointer to the PEI Services Table
392                                   published by the PEI Foundation.
393   @param[in]  This                A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
394   @param[out] ProcessorNumber     The handle number of the AP. The range is from 0 to the
395                                   total number of logical processors minus 1. The total
396                                   number of logical processors can be retrieved by
397                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
398 
399   @retval EFI_SUCCESS             The current processor handle number was returned in
400                                   ProcessorNumber.
401   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
402 **/
403 EFI_STATUS
404 EFIAPI
PeiWhoAmI(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_MP_SERVICES_PPI * This,OUT UINTN * ProcessorNumber)405 PeiWhoAmI (
406   IN  CONST EFI_PEI_SERVICES   **PeiServices,
407   IN  EFI_PEI_MP_SERVICES_PPI  *This,
408   OUT UINTN                    *ProcessorNumber
409   )
410 {
411   return MpInitLibWhoAmI (ProcessorNumber);
412 }
413 
414 /**
415   Get GDT register value.
416 
417   This function is mainly for AP purpose because AP may have different GDT
418   table than BSP.
419 
420   @param[in,out] Buffer  The pointer to private data buffer.
421 
422 **/
423 VOID
424 EFIAPI
GetGdtr(IN OUT VOID * Buffer)425 GetGdtr (
426   IN OUT VOID *Buffer
427   )
428 {
429   AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer);
430 }
431 
432 /**
433   Initializes CPU exceptions handlers for the sake of stack switch requirement.
434 
435   This function is a wrapper of InitializeCpuExceptionHandlersEx. It's mainly
436   for the sake of AP's init because of EFI_AP_PROCEDURE API requirement.
437 
438   @param[in,out] Buffer  The pointer to private data buffer.
439 
440 **/
441 VOID
442 EFIAPI
InitializeExceptionStackSwitchHandlers(IN OUT VOID * Buffer)443 InitializeExceptionStackSwitchHandlers (
444   IN OUT VOID *Buffer
445   )
446 {
447   CPU_EXCEPTION_INIT_DATA           *EssData;
448   IA32_DESCRIPTOR                   Idtr;
449   EFI_STATUS                        Status;
450 
451   EssData = Buffer;
452   //
453   // We don't plan to replace IDT table with a new one, but we should not assume
454   // the AP's IDT is the same as BSP's IDT either.
455   //
456   AsmReadIdtr (&Idtr);
457   EssData->Ia32.IdtTable = (VOID *)Idtr.Base;
458   EssData->Ia32.IdtTableSize = Idtr.Limit + 1;
459   Status = InitializeCpuExceptionHandlersEx (NULL, EssData);
460   ASSERT_EFI_ERROR (Status);
461 }
462 
463 /**
464   Initializes MP exceptions handlers for the sake of stack switch requirement.
465 
466   This function will allocate required resources required to setup stack switch
467   and pass them through CPU_EXCEPTION_INIT_DATA to each logic processor.
468 
469 **/
470 VOID
InitializeMpExceptionStackSwitchHandlers(VOID)471 InitializeMpExceptionStackSwitchHandlers (
472   VOID
473   )
474 {
475   EFI_STATUS                      Status;
476   UINTN                           Index;
477   UINTN                           Bsp;
478   UINTN                           ExceptionNumber;
479   UINTN                           OldGdtSize;
480   UINTN                           NewGdtSize;
481   UINTN                           NewStackSize;
482   IA32_DESCRIPTOR                 Gdtr;
483   CPU_EXCEPTION_INIT_DATA         EssData;
484   UINT8                           *GdtBuffer;
485   UINT8                           *StackTop;
486   UINTN                           NumberOfProcessors;
487 
488   if (!PcdGetBool (PcdCpuStackGuard)) {
489     return;
490   }
491 
492   MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL);
493   MpInitLibWhoAmI (&Bsp);
494 
495   ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);
496   NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;
497 
498   Status = PeiServicesAllocatePool (
499              NewStackSize * NumberOfProcessors,
500              (VOID **)&StackTop
501              );
502   ASSERT(StackTop != NULL);
503   if (EFI_ERROR (Status)) {
504     ASSERT_EFI_ERROR (Status);
505     return;
506   }
507   StackTop += NewStackSize  * NumberOfProcessors;
508 
509   //
510   // The default exception handlers must have been initialized. Let's just skip
511   // it in this method.
512   //
513   EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV;
514   EssData.Ia32.InitDefaultHandlers = FALSE;
515 
516   EssData.Ia32.StackSwitchExceptions = FixedPcdGetPtr(PcdCpuStackSwitchExceptionList);
517   EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber;
518   EssData.Ia32.KnownGoodStackSize = FixedPcdGet32(PcdCpuKnownGoodStackSize);
519 
520   //
521   // Initialize Gdtr to suppress incorrect compiler/analyzer warnings.
522   //
523   Gdtr.Base = 0;
524   Gdtr.Limit = 0;
525   for (Index = 0; Index < NumberOfProcessors; ++Index) {
526     //
527     // To support stack switch, we need to re-construct GDT but not IDT.
528     //
529     if (Index == Bsp) {
530       GetGdtr(&Gdtr);
531     } else {
532       //
533       // AP might have different size of GDT from BSP.
534       //
535       MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL);
536     }
537 
538     //
539     // X64 needs only one TSS of current task working for all exceptions
540     // because of its IST feature. IA32 needs one TSS for each exception
541     // in addition to current task. Since AP is not supposed to allocate
542     // memory, we have to do it in BSP. To simplify the code, we allocate
543     // memory for IA32 case to cover both IA32 and X64 exception stack
544     // switch.
545     //
546     // Layout of memory to allocate for each processor:
547     //    --------------------------------
548     //    |            Alignment         |  (just in case)
549     //    --------------------------------
550     //    |                              |
551     //    |        Original GDT          |
552     //    |                              |
553     //    --------------------------------
554     //    |    Current task descriptor   |
555     //    --------------------------------
556     //    |                              |
557     //    |  Exception task descriptors  |  X ExceptionNumber
558     //    |                              |
559     //    --------------------------------
560     //    |  Current task-state segment  |
561     //    --------------------------------
562     //    |                              |
563     //    | Exception task-state segment |  X ExceptionNumber
564     //    |                              |
565     //    --------------------------------
566     //
567     OldGdtSize = Gdtr.Limit + 1;
568     EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) *
569                                         (ExceptionNumber + 1);
570     EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) *
571                                     (ExceptionNumber + 1);
572     NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) +
573                  OldGdtSize +
574                  EssData.Ia32.ExceptionTssDescSize +
575                  EssData.Ia32.ExceptionTssSize;
576 
577     Status = PeiServicesAllocatePool (
578                NewGdtSize,
579                (VOID **)&GdtBuffer
580                );
581     ASSERT (GdtBuffer != NULL);
582     if (EFI_ERROR (Status)) {
583       ASSERT_EFI_ERROR (Status);
584       return;
585     }
586 
587     //
588     // Make sure GDT table alignment
589     //
590     EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof (IA32_TSS_DESCRIPTOR));
591     NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer);
592     EssData.Ia32.GdtTableSize = NewGdtSize;
593 
594     EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize);
595     EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize +
596                                  EssData.Ia32.ExceptionTssDescSize);
597 
598     EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop;
599     DEBUG ((DEBUG_INFO,
600             "Exception stack top[cpu%lu]: 0x%lX\n",
601             (UINT64)(UINTN)Index,
602             (UINT64)(UINTN)StackTop));
603 
604     if (Index == Bsp) {
605       InitializeExceptionStackSwitchHandlers (&EssData);
606     } else {
607       MpInitLibStartupThisAP (
608         InitializeExceptionStackSwitchHandlers,
609         Index,
610         NULL,
611         0,
612         (VOID *)&EssData,
613         NULL
614         );
615     }
616 
617     StackTop  -= NewStackSize;
618   }
619 }
620 
621 /**
622   Initializes MP and exceptions handlers.
623 
624   @param  PeiServices                The pointer to the PEI Services Table.
625 
626   @retval EFI_SUCCESS     MP was successfully initialized.
627   @retval others          Error occurred in MP initialization.
628 
629 **/
630 EFI_STATUS
InitializeCpuMpWorker(IN CONST EFI_PEI_SERVICES ** PeiServices)631 InitializeCpuMpWorker (
632   IN CONST EFI_PEI_SERVICES     **PeiServices
633   )
634 {
635   EFI_STATUS                      Status;
636   EFI_VECTOR_HANDOFF_INFO         *VectorInfo;
637   EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi;
638 
639   //
640   // Get Vector Hand-off Info PPI
641   //
642   VectorInfo = NULL;
643   Status = PeiServicesLocatePpi (
644              &gEfiVectorHandoffInfoPpiGuid,
645              0,
646              NULL,
647              (VOID **)&VectorHandoffInfoPpi
648              );
649   if (Status == EFI_SUCCESS) {
650     VectorInfo = VectorHandoffInfoPpi->Info;
651   }
652 
653   //
654   // Initialize default handlers
655   //
656   Status = InitializeCpuExceptionHandlers (VectorInfo);
657   if (EFI_ERROR (Status)) {
658     return Status;
659   }
660 
661   Status = MpInitLibInitialize ();
662   if (EFI_ERROR (Status)) {
663     return Status;
664   }
665 
666   //
667   // Special initialization for the sake of Stack Guard
668   //
669   InitializeMpExceptionStackSwitchHandlers ();
670 
671   //
672   // Update and publish CPU BIST information
673   //
674   CollectBistDataFromPpi (PeiServices);
675 
676   //
677   // Install CPU MP PPI
678   //
679   Status = PeiServicesInstallPpi(mPeiCpuMpPpiList);
680   ASSERT_EFI_ERROR (Status);
681 
682   return Status;
683 }
684 
685 /**
686   The Entry point of the MP CPU PEIM.
687 
688   This function will wakeup APs and collect CPU AP count and install the
689   Mp Service Ppi.
690 
691   @param  FileHandle    Handle of the file being invoked.
692   @param  PeiServices   Describes the list of possible PEI Services.
693 
694   @retval EFI_SUCCESS   MpServicePpi is installed successfully.
695 
696 **/
697 EFI_STATUS
698 EFIAPI
CpuMpPeimInit(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)699 CpuMpPeimInit (
700   IN       EFI_PEI_FILE_HANDLE  FileHandle,
701   IN CONST EFI_PEI_SERVICES     **PeiServices
702   )
703 {
704   EFI_STATUS           Status;
705 
706   //
707   // For the sake of special initialization needing to be done right after
708   // memory discovery.
709   //
710   Status = PeiServicesNotifyPpi (&mPostMemNotifyList[0]);
711   ASSERT_EFI_ERROR (Status);
712 
713   return Status;
714 }
715