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