1 /** @file
2 SMM MP service implementation
3 
4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "PiSmmCpuDxeSmm.h"
16 
17 //
18 // Slots for all MTRR( FIXED MTRR + VARIABLE MTRR + MTRR_LIB_IA32_MTRR_DEF_TYPE)
19 //
20 MTRR_SETTINGS                               gSmiMtrrs;
21 UINT64                                      gPhyMask;
22 SMM_DISPATCHER_MP_SYNC_DATA                 *mSmmMpSyncData = NULL;
23 UINTN                                       mSmmMpSyncDataSize;
24 SMM_CPU_SEMAPHORES                          mSmmCpuSemaphores;
25 UINTN                                       mSemaphoreSize;
26 SPIN_LOCK                                   *mPFLock = NULL;
27 SMM_CPU_SYNC_MODE                           mCpuSmmSyncMode;
28 
29 /**
30   Performs an atomic compare exchange operation to get semaphore.
31   The compare exchange operation must be performed using
32   MP safe mechanisms.
33 
34   @param      Sem        IN:  32-bit unsigned integer
35                          OUT: original integer - 1
36   @return     Original integer - 1
37 
38 **/
39 UINT32
WaitForSemaphore(IN OUT volatile UINT32 * Sem)40 WaitForSemaphore (
41   IN OUT  volatile UINT32           *Sem
42   )
43 {
44   UINT32                            Value;
45 
46   do {
47     Value = *Sem;
48   } while (Value == 0 ||
49            InterlockedCompareExchange32 (
50              (UINT32*)Sem,
51              Value,
52              Value - 1
53              ) != Value);
54   return Value - 1;
55 }
56 
57 
58 /**
59   Performs an atomic compare exchange operation to release semaphore.
60   The compare exchange operation must be performed using
61   MP safe mechanisms.
62 
63   @param      Sem        IN:  32-bit unsigned integer
64                          OUT: original integer + 1
65   @return     Original integer + 1
66 
67 **/
68 UINT32
ReleaseSemaphore(IN OUT volatile UINT32 * Sem)69 ReleaseSemaphore (
70   IN OUT  volatile UINT32           *Sem
71   )
72 {
73   UINT32                            Value;
74 
75   do {
76     Value = *Sem;
77   } while (Value + 1 != 0 &&
78            InterlockedCompareExchange32 (
79              (UINT32*)Sem,
80              Value,
81              Value + 1
82              ) != Value);
83   return Value + 1;
84 }
85 
86 /**
87   Performs an atomic compare exchange operation to lock semaphore.
88   The compare exchange operation must be performed using
89   MP safe mechanisms.
90 
91   @param      Sem        IN:  32-bit unsigned integer
92                          OUT: -1
93   @return     Original integer
94 
95 **/
96 UINT32
LockdownSemaphore(IN OUT volatile UINT32 * Sem)97 LockdownSemaphore (
98   IN OUT  volatile UINT32           *Sem
99   )
100 {
101   UINT32                            Value;
102 
103   do {
104     Value = *Sem;
105   } while (InterlockedCompareExchange32 (
106              (UINT32*)Sem,
107              Value, (UINT32)-1
108              ) != Value);
109   return Value;
110 }
111 
112 /**
113   Wait all APs to performs an atomic compare exchange operation to release semaphore.
114 
115   @param   NumberOfAPs      AP number
116 
117 **/
118 VOID
WaitForAllAPs(IN UINTN NumberOfAPs)119 WaitForAllAPs (
120   IN      UINTN                     NumberOfAPs
121   )
122 {
123   UINTN                             BspIndex;
124 
125   BspIndex = mSmmMpSyncData->BspIndex;
126   while (NumberOfAPs-- > 0) {
127     WaitForSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
128   }
129 }
130 
131 /**
132   Performs an atomic compare exchange operation to release semaphore
133   for each AP.
134 
135 **/
136 VOID
ReleaseAllAPs(VOID)137 ReleaseAllAPs (
138   VOID
139   )
140 {
141   UINTN                             Index;
142   UINTN                             BspIndex;
143 
144   BspIndex = mSmmMpSyncData->BspIndex;
145   for (Index = mMaxNumberOfCpus; Index-- > 0;) {
146     if (Index != BspIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {
147       ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run);
148     }
149   }
150 }
151 
152 /**
153   Checks if all CPUs (with certain exceptions) have checked in for this SMI run
154 
155   @param   Exceptions     CPU Arrival exception flags.
156 
157   @retval   TRUE  if all CPUs the have checked in.
158   @retval   FALSE  if at least one Normal AP hasn't checked in.
159 
160 **/
161 BOOLEAN
AllCpusInSmmWithExceptions(SMM_CPU_ARRIVAL_EXCEPTIONS Exceptions)162 AllCpusInSmmWithExceptions (
163   SMM_CPU_ARRIVAL_EXCEPTIONS  Exceptions
164   )
165 {
166   UINTN                             Index;
167   SMM_CPU_DATA_BLOCK                *CpuData;
168   EFI_PROCESSOR_INFORMATION         *ProcessorInfo;
169 
170   ASSERT (*mSmmMpSyncData->Counter <= mNumberOfCpus);
171 
172   if (*mSmmMpSyncData->Counter == mNumberOfCpus) {
173     return TRUE;
174   }
175 
176   CpuData = mSmmMpSyncData->CpuData;
177   ProcessorInfo = gSmmCpuPrivate->ProcessorInfo;
178   for (Index = mMaxNumberOfCpus; Index-- > 0;) {
179     if (!(*(CpuData[Index].Present)) && ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {
180       if (((Exceptions & ARRIVAL_EXCEPTION_DELAYED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmDelayed) != 0) {
181         continue;
182       }
183       if (((Exceptions & ARRIVAL_EXCEPTION_BLOCKED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmBlocked) != 0) {
184         continue;
185       }
186       if (((Exceptions & ARRIVAL_EXCEPTION_SMI_DISABLED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmEnable) != 0) {
187         continue;
188       }
189       return FALSE;
190     }
191   }
192 
193 
194   return TRUE;
195 }
196 
197 
198 /**
199   Given timeout constraint, wait for all APs to arrive, and insure when this function returns, no AP will execute normal mode code before
200   entering SMM, except SMI disabled APs.
201 
202 **/
203 VOID
SmmWaitForApArrival(VOID)204 SmmWaitForApArrival (
205   VOID
206   )
207 {
208   UINT64                            Timer;
209   UINTN                             Index;
210 
211   ASSERT (*mSmmMpSyncData->Counter <= mNumberOfCpus);
212 
213   //
214   // Platform implementor should choose a timeout value appropriately:
215   // - The timeout value should balance the SMM time constrains and the likelihood that delayed CPUs are excluded in the SMM run. Note
216   //   the SMI Handlers must ALWAYS take into account the cases that not all APs are available in an SMI run.
217   // - The timeout value must, in the case of 2nd timeout, be at least long enough to give time for all APs to receive the SMI IPI
218   //   and either enter SMM or buffer the SMI, to insure there is no CPU running normal mode code when SMI handling starts. This will
219   //   be TRUE even if a blocked CPU is brought out of the blocked state by a normal mode CPU (before the normal mode CPU received the
220   //   SMI IPI), because with a buffered SMI, and CPU will enter SMM immediately after it is brought out of the blocked state.
221   // - The timeout value must be longer than longest possible IO operation in the system
222   //
223 
224   //
225   // Sync with APs 1st timeout
226   //
227   for (Timer = StartSyncTimer ();
228        !IsSyncTimerTimeout (Timer) &&
229        !AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED );
230        ) {
231     CpuPause ();
232   }
233 
234   //
235   // Not all APs have arrived, so we need 2nd round of timeout. IPIs should be sent to ALL none present APs,
236   // because:
237   // a) Delayed AP may have just come out of the delayed state. Blocked AP may have just been brought out of blocked state by some AP running
238   //    normal mode code. These APs need to be guaranteed to have an SMI pending to insure that once they are out of delayed / blocked state, they
239   //    enter SMI immediately without executing instructions in normal mode. Note traditional flow requires there are no APs doing normal mode
240   //    work while SMI handling is on-going.
241   // b) As a consequence of SMI IPI sending, (spurious) SMI may occur after this SMM run.
242   // c) ** NOTE **: Use SMI disabling feature VERY CAREFULLY (if at all) for traditional flow, because a processor in SMI-disabled state
243   //    will execute normal mode code, which breaks the traditional SMI handlers' assumption that no APs are doing normal
244   //    mode work while SMI handling is on-going.
245   // d) We don't add code to check SMI disabling status to skip sending IPI to SMI disabled APs, because:
246   //    - In traditional flow, SMI disabling is discouraged.
247   //    - In relaxed flow, CheckApArrival() will check SMI disabling status before calling this function.
248   //    In both cases, adding SMI-disabling checking code increases overhead.
249   //
250   if (*mSmmMpSyncData->Counter < mNumberOfCpus) {
251     //
252     // Send SMI IPIs to bring outside processors in
253     //
254     for (Index = mMaxNumberOfCpus; Index-- > 0;) {
255       if (!(*(mSmmMpSyncData->CpuData[Index].Present)) && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {
256         SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId);
257       }
258     }
259 
260     //
261     // Sync with APs 2nd timeout.
262     //
263     for (Timer = StartSyncTimer ();
264          !IsSyncTimerTimeout (Timer) &&
265          !AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED );
266          ) {
267       CpuPause ();
268     }
269   }
270 
271   return;
272 }
273 
274 
275 /**
276   Replace OS MTRR's with SMI MTRR's.
277 
278   @param    CpuIndex             Processor Index
279 
280 **/
281 VOID
ReplaceOSMtrrs(IN UINTN CpuIndex)282 ReplaceOSMtrrs (
283   IN      UINTN                     CpuIndex
284   )
285 {
286   SmmCpuFeaturesDisableSmrr ();
287 
288   //
289   // Replace all MTRRs registers
290   //
291   MtrrSetAllMtrrs (&gSmiMtrrs);
292 }
293 
294 /**
295   SMI handler for BSP.
296 
297   @param     CpuIndex         BSP processor Index
298   @param     SyncMode         SMM MP sync mode
299 
300 **/
301 VOID
BSPHandler(IN UINTN CpuIndex,IN SMM_CPU_SYNC_MODE SyncMode)302 BSPHandler (
303   IN      UINTN                     CpuIndex,
304   IN      SMM_CPU_SYNC_MODE         SyncMode
305   )
306 {
307   UINTN                             Index;
308   MTRR_SETTINGS                     Mtrrs;
309   UINTN                             ApCount;
310   BOOLEAN                           ClearTopLevelSmiResult;
311   UINTN                             PresentCount;
312 
313   ASSERT (CpuIndex == mSmmMpSyncData->BspIndex);
314   ApCount = 0;
315 
316   //
317   // Flag BSP's presence
318   //
319   *mSmmMpSyncData->InsideSmm = TRUE;
320 
321   //
322   // Initialize Debug Agent to start source level debug in BSP handler
323   //
324   InitializeDebugAgent (DEBUG_AGENT_INIT_ENTER_SMI, NULL, NULL);
325 
326   //
327   // Mark this processor's presence
328   //
329   *(mSmmMpSyncData->CpuData[CpuIndex].Present) = TRUE;
330 
331   //
332   // Clear platform top level SMI status bit before calling SMI handlers. If
333   // we cleared it after SMI handlers are run, we would miss the SMI that
334   // occurs after SMI handlers are done and before SMI status bit is cleared.
335   //
336   ClearTopLevelSmiResult = ClearTopLevelSmiStatus();
337   ASSERT (ClearTopLevelSmiResult == TRUE);
338 
339   //
340   // Set running processor index
341   //
342   gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu = CpuIndex;
343 
344   //
345   // If Traditional Sync Mode or need to configure MTRRs: gather all available APs.
346   //
347   if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) {
348 
349     //
350     // Wait for APs to arrive
351     //
352     SmmWaitForApArrival();
353 
354     //
355     // Lock the counter down and retrieve the number of APs
356     //
357     *mSmmMpSyncData->AllCpusInSync = TRUE;
358     ApCount = LockdownSemaphore (mSmmMpSyncData->Counter) - 1;
359 
360     //
361     // Wait for all APs to get ready for programming MTRRs
362     //
363     WaitForAllAPs (ApCount);
364 
365     if (SmmCpuFeaturesNeedConfigureMtrrs()) {
366       //
367       // Signal all APs it's time for backup MTRRs
368       //
369       ReleaseAllAPs ();
370 
371       //
372       // WaitForSemaphore() may wait for ever if an AP happens to enter SMM at
373       // exactly this point. Please make sure PcdCpuSmmMaxSyncLoops has been set
374       // to a large enough value to avoid this situation.
375       // Note: For HT capable CPUs, threads within a core share the same set of MTRRs.
376       // We do the backup first and then set MTRR to avoid race condition for threads
377       // in the same core.
378       //
379       MtrrGetAllMtrrs(&Mtrrs);
380 
381       //
382       // Wait for all APs to complete their MTRR saving
383       //
384       WaitForAllAPs (ApCount);
385 
386       //
387       // Let all processors program SMM MTRRs together
388       //
389       ReleaseAllAPs ();
390 
391       //
392       // WaitForSemaphore() may wait for ever if an AP happens to enter SMM at
393       // exactly this point. Please make sure PcdCpuSmmMaxSyncLoops has been set
394       // to a large enough value to avoid this situation.
395       //
396       ReplaceOSMtrrs (CpuIndex);
397 
398       //
399       // Wait for all APs to complete their MTRR programming
400       //
401       WaitForAllAPs (ApCount);
402     }
403   }
404 
405   //
406   // The BUSY lock is initialized to Acquired state
407   //
408   AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy);
409 
410   //
411   // Perform the pre tasks
412   //
413   PerformPreTasks ();
414 
415   //
416   // Invoke SMM Foundation EntryPoint with the processor information context.
417   //
418   gSmmCpuPrivate->SmmCoreEntry (&gSmmCpuPrivate->SmmCoreEntryContext);
419 
420   //
421   // Make sure all APs have completed their pending none-block tasks
422   //
423   for (Index = mMaxNumberOfCpus; Index-- > 0;) {
424     if (Index != CpuIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {
425       AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);
426       ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy);
427     }
428   }
429 
430   //
431   // Perform the remaining tasks
432   //
433   PerformRemainingTasks ();
434 
435   //
436   // If Relaxed-AP Sync Mode: gather all available APs after BSP SMM handlers are done, and
437   // make those APs to exit SMI synchronously. APs which arrive later will be excluded and
438   // will run through freely.
439   //
440   if (SyncMode != SmmCpuSyncModeTradition && !SmmCpuFeaturesNeedConfigureMtrrs()) {
441 
442     //
443     // Lock the counter down and retrieve the number of APs
444     //
445     *mSmmMpSyncData->AllCpusInSync = TRUE;
446     ApCount = LockdownSemaphore (mSmmMpSyncData->Counter) - 1;
447     //
448     // Make sure all APs have their Present flag set
449     //
450     while (TRUE) {
451       PresentCount = 0;
452       for (Index = mMaxNumberOfCpus; Index-- > 0;) {
453         if (*(mSmmMpSyncData->CpuData[Index].Present)) {
454           PresentCount ++;
455         }
456       }
457       if (PresentCount > ApCount) {
458         break;
459       }
460     }
461   }
462 
463   //
464   // Notify all APs to exit
465   //
466   *mSmmMpSyncData->InsideSmm = FALSE;
467   ReleaseAllAPs ();
468 
469   //
470   // Wait for all APs to complete their pending tasks
471   //
472   WaitForAllAPs (ApCount);
473 
474   if (SmmCpuFeaturesNeedConfigureMtrrs()) {
475     //
476     // Signal APs to restore MTRRs
477     //
478     ReleaseAllAPs ();
479 
480     //
481     // Restore OS MTRRs
482     //
483     SmmCpuFeaturesReenableSmrr ();
484     MtrrSetAllMtrrs(&Mtrrs);
485 
486     //
487     // Wait for all APs to complete MTRR programming
488     //
489     WaitForAllAPs (ApCount);
490   }
491 
492   //
493   // Stop source level debug in BSP handler, the code below will not be
494   // debugged.
495   //
496   InitializeDebugAgent (DEBUG_AGENT_INIT_EXIT_SMI, NULL, NULL);
497 
498   //
499   // Signal APs to Reset states/semaphore for this processor
500   //
501   ReleaseAllAPs ();
502 
503   //
504   // Perform pending operations for hot-plug
505   //
506   SmmCpuUpdate ();
507 
508   //
509   // Clear the Present flag of BSP
510   //
511   *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE;
512 
513   //
514   // Gather APs to exit SMM synchronously. Note the Present flag is cleared by now but
515   // WaitForAllAps does not depend on the Present flag.
516   //
517   WaitForAllAPs (ApCount);
518 
519   //
520   // Reset BspIndex to -1, meaning BSP has not been elected.
521   //
522   if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) {
523     mSmmMpSyncData->BspIndex = (UINT32)-1;
524   }
525 
526   //
527   // Allow APs to check in from this point on
528   //
529   *mSmmMpSyncData->Counter = 0;
530   *mSmmMpSyncData->AllCpusInSync = FALSE;
531 }
532 
533 /**
534   SMI handler for AP.
535 
536   @param     CpuIndex         AP processor Index.
537   @param     ValidSmi         Indicates that current SMI is a valid SMI or not.
538   @param     SyncMode         SMM MP sync mode.
539 
540 **/
541 VOID
APHandler(IN UINTN CpuIndex,IN BOOLEAN ValidSmi,IN SMM_CPU_SYNC_MODE SyncMode)542 APHandler (
543   IN      UINTN                     CpuIndex,
544   IN      BOOLEAN                   ValidSmi,
545   IN      SMM_CPU_SYNC_MODE         SyncMode
546   )
547 {
548   UINT64                            Timer;
549   UINTN                             BspIndex;
550   MTRR_SETTINGS                     Mtrrs;
551 
552   //
553   // Timeout BSP
554   //
555   for (Timer = StartSyncTimer ();
556        !IsSyncTimerTimeout (Timer) &&
557        !(*mSmmMpSyncData->InsideSmm);
558        ) {
559     CpuPause ();
560   }
561 
562   if (!(*mSmmMpSyncData->InsideSmm)) {
563     //
564     // BSP timeout in the first round
565     //
566     if (mSmmMpSyncData->BspIndex != -1) {
567       //
568       // BSP Index is known
569       //
570       BspIndex = mSmmMpSyncData->BspIndex;
571       ASSERT (CpuIndex != BspIndex);
572 
573       //
574       // Send SMI IPI to bring BSP in
575       //
576       SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[BspIndex].ProcessorId);
577 
578       //
579       // Now clock BSP for the 2nd time
580       //
581       for (Timer = StartSyncTimer ();
582            !IsSyncTimerTimeout (Timer) &&
583            !(*mSmmMpSyncData->InsideSmm);
584            ) {
585         CpuPause ();
586       }
587 
588       if (!(*mSmmMpSyncData->InsideSmm)) {
589         //
590         // Give up since BSP is unable to enter SMM
591         // and signal the completion of this AP
592         WaitForSemaphore (mSmmMpSyncData->Counter);
593         return;
594       }
595     } else {
596       //
597       // Don't know BSP index. Give up without sending IPI to BSP.
598       //
599       WaitForSemaphore (mSmmMpSyncData->Counter);
600       return;
601     }
602   }
603 
604   //
605   // BSP is available
606   //
607   BspIndex = mSmmMpSyncData->BspIndex;
608   ASSERT (CpuIndex != BspIndex);
609 
610   //
611   // Mark this processor's presence
612   //
613   *(mSmmMpSyncData->CpuData[CpuIndex].Present) = TRUE;
614 
615   if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) {
616     //
617     // Notify BSP of arrival at this point
618     //
619     ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
620   }
621 
622   if (SmmCpuFeaturesNeedConfigureMtrrs()) {
623     //
624     // Wait for the signal from BSP to backup MTRRs
625     //
626     WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
627 
628     //
629     // Backup OS MTRRs
630     //
631     MtrrGetAllMtrrs(&Mtrrs);
632 
633     //
634     // Signal BSP the completion of this AP
635     //
636     ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
637 
638     //
639     // Wait for BSP's signal to program MTRRs
640     //
641     WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
642 
643     //
644     // Replace OS MTRRs with SMI MTRRs
645     //
646     ReplaceOSMtrrs (CpuIndex);
647 
648     //
649     // Signal BSP the completion of this AP
650     //
651     ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
652   }
653 
654   while (TRUE) {
655     //
656     // Wait for something to happen
657     //
658     WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
659 
660     //
661     // Check if BSP wants to exit SMM
662     //
663     if (!(*mSmmMpSyncData->InsideSmm)) {
664       break;
665     }
666 
667     //
668     // BUSY should be acquired by SmmStartupThisAp()
669     //
670     ASSERT (
671       !AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)
672       );
673 
674     //
675     // Invoke the scheduled procedure
676     //
677     (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) (
678       (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter
679       );
680 
681     //
682     // Release BUSY
683     //
684     ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
685   }
686 
687   if (SmmCpuFeaturesNeedConfigureMtrrs()) {
688     //
689     // Notify BSP the readiness of this AP to program MTRRs
690     //
691     ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
692 
693     //
694     // Wait for the signal from BSP to program MTRRs
695     //
696     WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
697 
698     //
699     // Restore OS MTRRs
700     //
701     SmmCpuFeaturesReenableSmrr ();
702     MtrrSetAllMtrrs(&Mtrrs);
703   }
704 
705   //
706   // Notify BSP the readiness of this AP to Reset states/semaphore for this processor
707   //
708   ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
709 
710   //
711   // Wait for the signal from BSP to Reset states/semaphore for this processor
712   //
713   WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
714 
715   //
716   // Reset states/semaphore for this processor
717   //
718   *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE;
719 
720   //
721   // Notify BSP the readiness of this AP to exit SMM
722   //
723   ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);
724 
725 }
726 
727 /**
728   Create 4G PageTable in SMRAM.
729 
730   @param[in]      Is32BitPageTable Whether the page table is 32-bit PAE
731   @return         PageTable Address
732 
733 **/
734 UINT32
Gen4GPageTable(IN BOOLEAN Is32BitPageTable)735 Gen4GPageTable (
736   IN      BOOLEAN                   Is32BitPageTable
737   )
738 {
739   VOID    *PageTable;
740   UINTN   Index;
741   UINT64  *Pte;
742   UINTN   PagesNeeded;
743   UINTN   Low2MBoundary;
744   UINTN   High2MBoundary;
745   UINTN   Pages;
746   UINTN   GuardPage;
747   UINT64  *Pdpte;
748   UINTN   PageIndex;
749   UINTN   PageAddress;
750 
751   Low2MBoundary = 0;
752   High2MBoundary = 0;
753   PagesNeeded = 0;
754   if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
755     //
756     // Add one more page for known good stack, then find the lower 2MB aligned address.
757     //
758     Low2MBoundary = (mSmmStackArrayBase + EFI_PAGE_SIZE) & ~(SIZE_2MB-1);
759     //
760     // Add two more pages for known good stack and stack guard page,
761     // then find the lower 2MB aligned address.
762     //
763     High2MBoundary = (mSmmStackArrayEnd - mSmmStackSize + EFI_PAGE_SIZE * 2) & ~(SIZE_2MB-1);
764     PagesNeeded = ((High2MBoundary - Low2MBoundary) / SIZE_2MB) + 1;
765   }
766   //
767   // Allocate the page table
768   //
769   PageTable = AllocatePageTableMemory (5 + PagesNeeded);
770   ASSERT (PageTable != NULL);
771 
772   PageTable = (VOID *)((UINTN)PageTable);
773   Pte = (UINT64*)PageTable;
774 
775   //
776   // Zero out all page table entries first
777   //
778   ZeroMem (Pte, EFI_PAGES_TO_SIZE (1));
779 
780   //
781   // Set Page Directory Pointers
782   //
783   for (Index = 0; Index < 4; Index++) {
784     Pte[Index] = (UINTN)PageTable + EFI_PAGE_SIZE * (Index + 1) + (Is32BitPageTable ? IA32_PAE_PDPTE_ATTRIBUTE_BITS : PAGE_ATTRIBUTE_BITS);
785   }
786   Pte += EFI_PAGE_SIZE / sizeof (*Pte);
787 
788   //
789   // Fill in Page Directory Entries
790   //
791   for (Index = 0; Index < EFI_PAGE_SIZE * 4 / sizeof (*Pte); Index++) {
792     Pte[Index] = (Index << 21) | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;
793   }
794 
795   if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
796     Pages = (UINTN)PageTable + EFI_PAGES_TO_SIZE (5);
797     GuardPage = mSmmStackArrayBase + EFI_PAGE_SIZE;
798     Pdpte = (UINT64*)PageTable;
799     for (PageIndex = Low2MBoundary; PageIndex <= High2MBoundary; PageIndex += SIZE_2MB) {
800       Pte = (UINT64*)(UINTN)(Pdpte[BitFieldRead32 ((UINT32)PageIndex, 30, 31)] & ~(EFI_PAGE_SIZE - 1));
801       Pte[BitFieldRead32 ((UINT32)PageIndex, 21, 29)] = (UINT64)Pages | PAGE_ATTRIBUTE_BITS;
802       //
803       // Fill in Page Table Entries
804       //
805       Pte = (UINT64*)Pages;
806       PageAddress = PageIndex;
807       for (Index = 0; Index < EFI_PAGE_SIZE / sizeof (*Pte); Index++) {
808         if (PageAddress == GuardPage) {
809           //
810           // Mark the guard page as non-present
811           //
812           Pte[Index] = PageAddress;
813           GuardPage += mSmmStackSize;
814           if (GuardPage > mSmmStackArrayEnd) {
815             GuardPage = 0;
816           }
817         } else {
818           Pte[Index] = PageAddress | PAGE_ATTRIBUTE_BITS;
819         }
820         PageAddress+= EFI_PAGE_SIZE;
821       }
822       Pages += EFI_PAGE_SIZE;
823     }
824   }
825 
826   return (UINT32)(UINTN)PageTable;
827 }
828 
829 /**
830   Set memory cache ability.
831 
832   @param    PageTable              PageTable Address
833   @param    Address                Memory Address to change cache ability
834   @param    Cacheability           Cache ability to set
835 
836 **/
837 VOID
SetCacheability(IN UINT64 * PageTable,IN UINTN Address,IN UINT8 Cacheability)838 SetCacheability (
839   IN      UINT64                    *PageTable,
840   IN      UINTN                     Address,
841   IN      UINT8                     Cacheability
842   )
843 {
844   UINTN   PTIndex;
845   VOID    *NewPageTableAddress;
846   UINT64  *NewPageTable;
847   UINTN   Index;
848 
849   ASSERT ((Address & EFI_PAGE_MASK) == 0);
850 
851   if (sizeof (UINTN) == sizeof (UINT64)) {
852     PTIndex = (UINTN)RShiftU64 (Address, 39) & 0x1ff;
853     ASSERT (PageTable[PTIndex] & IA32_PG_P);
854     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);
855   }
856 
857   PTIndex = (UINTN)RShiftU64 (Address, 30) & 0x1ff;
858   ASSERT (PageTable[PTIndex] & IA32_PG_P);
859   PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);
860 
861   //
862   // A perfect implementation should check the original cacheability with the
863   // one being set, and break a 2M page entry into pieces only when they
864   // disagreed.
865   //
866   PTIndex = (UINTN)RShiftU64 (Address, 21) & 0x1ff;
867   if ((PageTable[PTIndex] & IA32_PG_PS) != 0) {
868     //
869     // Allocate a page from SMRAM
870     //
871     NewPageTableAddress = AllocatePageTableMemory (1);
872     ASSERT (NewPageTableAddress != NULL);
873 
874     NewPageTable = (UINT64 *)NewPageTableAddress;
875 
876     for (Index = 0; Index < 0x200; Index++) {
877       NewPageTable[Index] = PageTable[PTIndex];
878       if ((NewPageTable[Index] & IA32_PG_PAT_2M) != 0) {
879         NewPageTable[Index] &= ~((UINT64)IA32_PG_PAT_2M);
880         NewPageTable[Index] |= (UINT64)IA32_PG_PAT_4K;
881       }
882       NewPageTable[Index] |= (UINT64)(Index << EFI_PAGE_SHIFT);
883     }
884 
885     PageTable[PTIndex] = ((UINTN)NewPageTableAddress & gPhyMask) | PAGE_ATTRIBUTE_BITS;
886   }
887 
888   ASSERT (PageTable[PTIndex] & IA32_PG_P);
889   PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);
890 
891   PTIndex = (UINTN)RShiftU64 (Address, 12) & 0x1ff;
892   ASSERT (PageTable[PTIndex] & IA32_PG_P);
893   PageTable[PTIndex] &= ~((UINT64)((IA32_PG_PAT_4K | IA32_PG_CD | IA32_PG_WT)));
894   PageTable[PTIndex] |= (UINT64)Cacheability;
895 }
896 
897 /**
898   Schedule a procedure to run on the specified CPU.
899 
900   @param[in]       Procedure                The address of the procedure to run
901   @param[in]       CpuIndex                 Target CPU Index
902   @param[in, out]  ProcArguments            The parameter to pass to the procedure
903   @param[in]       BlockingMode             Startup AP in blocking mode or not
904 
905   @retval EFI_INVALID_PARAMETER    CpuNumber not valid
906   @retval EFI_INVALID_PARAMETER    CpuNumber specifying BSP
907   @retval EFI_INVALID_PARAMETER    The AP specified by CpuNumber did not enter SMM
908   @retval EFI_INVALID_PARAMETER    The AP specified by CpuNumber is busy
909   @retval EFI_SUCCESS              The procedure has been successfully scheduled
910 
911 **/
912 EFI_STATUS
InternalSmmStartupThisAp(IN EFI_AP_PROCEDURE Procedure,IN UINTN CpuIndex,IN OUT VOID * ProcArguments OPTIONAL,IN BOOLEAN BlockingMode)913 InternalSmmStartupThisAp (
914   IN      EFI_AP_PROCEDURE          Procedure,
915   IN      UINTN                     CpuIndex,
916   IN OUT  VOID                      *ProcArguments OPTIONAL,
917   IN      BOOLEAN                   BlockingMode
918   )
919 {
920   if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) {
921     DEBUG((DEBUG_ERROR, "CpuIndex(%d) >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus(%d)\n", CpuIndex, gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus));
922     return EFI_INVALID_PARAMETER;
923   }
924   if (CpuIndex == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
925     DEBUG((DEBUG_ERROR, "CpuIndex(%d) == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu\n", CpuIndex));
926     return EFI_INVALID_PARAMETER;
927   }
928   if (!(*(mSmmMpSyncData->CpuData[CpuIndex].Present))) {
929     if (mSmmMpSyncData->EffectiveSyncMode == SmmCpuSyncModeTradition) {
930       DEBUG((DEBUG_ERROR, "!mSmmMpSyncData->CpuData[%d].Present\n", CpuIndex));
931     }
932     return EFI_INVALID_PARAMETER;
933   }
934   if (gSmmCpuPrivate->Operation[CpuIndex] == SmmCpuRemove) {
935     if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
936       DEBUG((DEBUG_ERROR, "gSmmCpuPrivate->Operation[%d] == SmmCpuRemove\n", CpuIndex));
937     }
938     return EFI_INVALID_PARAMETER;
939   }
940 
941   if (BlockingMode) {
942     AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
943   } else {
944     if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) {
945       DEBUG((DEBUG_ERROR, "mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex));
946       return EFI_INVALID_PARAMETER;
947     }
948   }
949 
950   mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;
951   mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments;
952   ReleaseSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
953 
954   if (BlockingMode) {
955     AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
956     ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
957   }
958   return EFI_SUCCESS;
959 }
960 
961 /**
962   Schedule a procedure to run on the specified CPU in blocking mode.
963 
964   @param[in]       Procedure                The address of the procedure to run
965   @param[in]       CpuIndex                 Target CPU Index
966   @param[in, out]  ProcArguments            The parameter to pass to the procedure
967 
968   @retval EFI_INVALID_PARAMETER    CpuNumber not valid
969   @retval EFI_INVALID_PARAMETER    CpuNumber specifying BSP
970   @retval EFI_INVALID_PARAMETER    The AP specified by CpuNumber did not enter SMM
971   @retval EFI_INVALID_PARAMETER    The AP specified by CpuNumber is busy
972   @retval EFI_SUCCESS              The procedure has been successfully scheduled
973 
974 **/
975 EFI_STATUS
976 EFIAPI
SmmBlockingStartupThisAp(IN EFI_AP_PROCEDURE Procedure,IN UINTN CpuIndex,IN OUT VOID * ProcArguments OPTIONAL)977 SmmBlockingStartupThisAp (
978   IN      EFI_AP_PROCEDURE          Procedure,
979   IN      UINTN                     CpuIndex,
980   IN OUT  VOID                      *ProcArguments OPTIONAL
981   )
982 {
983   return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, TRUE);
984 }
985 
986 /**
987   Schedule a procedure to run on the specified CPU.
988 
989   @param  Procedure                The address of the procedure to run
990   @param  CpuIndex                 Target CPU Index
991   @param  ProcArguments            The parameter to pass to the procedure
992 
993   @retval EFI_INVALID_PARAMETER    CpuNumber not valid
994   @retval EFI_INVALID_PARAMETER    CpuNumber specifying BSP
995   @retval EFI_INVALID_PARAMETER    The AP specified by CpuNumber did not enter SMM
996   @retval EFI_INVALID_PARAMETER    The AP specified by CpuNumber is busy
997   @retval EFI_SUCCESS              The procedure has been successfully scheduled
998 
999 **/
1000 EFI_STATUS
1001 EFIAPI
SmmStartupThisAp(IN EFI_AP_PROCEDURE Procedure,IN UINTN CpuIndex,IN OUT VOID * ProcArguments OPTIONAL)1002 SmmStartupThisAp (
1003   IN      EFI_AP_PROCEDURE          Procedure,
1004   IN      UINTN                     CpuIndex,
1005   IN OUT  VOID                      *ProcArguments OPTIONAL
1006   )
1007 {
1008   return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, FeaturePcdGet (PcdCpuSmmBlockStartupThisAp));
1009 }
1010 
1011 /**
1012   This function sets DR6 & DR7 according to SMM save state, before running SMM C code.
1013   They are useful when you want to enable hardware breakpoints in SMM without entry SMM mode.
1014 
1015   NOTE: It might not be appreciated in runtime since it might
1016         conflict with OS debugging facilities. Turn them off in RELEASE.
1017 
1018   @param    CpuIndex              CPU Index
1019 
1020 **/
1021 VOID
1022 EFIAPI
CpuSmmDebugEntry(IN UINTN CpuIndex)1023 CpuSmmDebugEntry (
1024   IN UINTN  CpuIndex
1025   )
1026 {
1027   SMRAM_SAVE_STATE_MAP *CpuSaveState;
1028 
1029   if (FeaturePcdGet (PcdCpuSmmDebug)) {
1030     ASSERT(CpuIndex < mMaxNumberOfCpus);
1031     CpuSaveState = (SMRAM_SAVE_STATE_MAP *)gSmmCpuPrivate->CpuSaveState[CpuIndex];
1032     if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {
1033       AsmWriteDr6 (CpuSaveState->x86._DR6);
1034       AsmWriteDr7 (CpuSaveState->x86._DR7);
1035     } else {
1036       AsmWriteDr6 ((UINTN)CpuSaveState->x64._DR6);
1037       AsmWriteDr7 ((UINTN)CpuSaveState->x64._DR7);
1038     }
1039   }
1040 }
1041 
1042 /**
1043   This function restores DR6 & DR7 to SMM save state.
1044 
1045   NOTE: It might not be appreciated in runtime since it might
1046         conflict with OS debugging facilities. Turn them off in RELEASE.
1047 
1048   @param    CpuIndex              CPU Index
1049 
1050 **/
1051 VOID
1052 EFIAPI
CpuSmmDebugExit(IN UINTN CpuIndex)1053 CpuSmmDebugExit (
1054   IN UINTN  CpuIndex
1055   )
1056 {
1057   SMRAM_SAVE_STATE_MAP *CpuSaveState;
1058 
1059   if (FeaturePcdGet (PcdCpuSmmDebug)) {
1060     ASSERT(CpuIndex < mMaxNumberOfCpus);
1061     CpuSaveState = (SMRAM_SAVE_STATE_MAP *)gSmmCpuPrivate->CpuSaveState[CpuIndex];
1062     if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {
1063       CpuSaveState->x86._DR7 = (UINT32)AsmReadDr7 ();
1064       CpuSaveState->x86._DR6 = (UINT32)AsmReadDr6 ();
1065     } else {
1066       CpuSaveState->x64._DR7 = AsmReadDr7 ();
1067       CpuSaveState->x64._DR6 = AsmReadDr6 ();
1068     }
1069   }
1070 }
1071 
1072 /**
1073   C function for SMI entry, each processor comes here upon SMI trigger.
1074 
1075   @param    CpuIndex              CPU Index
1076 
1077 **/
1078 VOID
1079 EFIAPI
SmiRendezvous(IN UINTN CpuIndex)1080 SmiRendezvous (
1081   IN      UINTN                     CpuIndex
1082   )
1083 {
1084   EFI_STATUS                     Status;
1085   BOOLEAN                        ValidSmi;
1086   BOOLEAN                        IsBsp;
1087   BOOLEAN                        BspInProgress;
1088   UINTN                          Index;
1089   UINTN                          Cr2;
1090 
1091   ASSERT(CpuIndex < mMaxNumberOfCpus);
1092 
1093   //
1094   // Save Cr2 because Page Fault exception in SMM may override its value
1095   //
1096   Cr2 = AsmReadCr2 ();
1097 
1098   //
1099   // Perform CPU specific entry hooks
1100   //
1101   SmmCpuFeaturesRendezvousEntry (CpuIndex);
1102 
1103   //
1104   // Determine if this is a valid SMI
1105   //
1106   ValidSmi = PlatformValidSmi();
1107 
1108   //
1109   // Determine if BSP has been already in progress. Note this must be checked after
1110   // ValidSmi because BSP may clear a valid SMI source after checking in.
1111   //
1112   BspInProgress = *mSmmMpSyncData->InsideSmm;
1113 
1114   if (!BspInProgress && !ValidSmi) {
1115     //
1116     // If we reach here, it means when we sampled the ValidSmi flag, SMI status had not
1117     // been cleared by BSP in a new SMI run (so we have a truly invalid SMI), or SMI
1118     // status had been cleared by BSP and an existing SMI run has almost ended. (Note
1119     // we sampled ValidSmi flag BEFORE judging BSP-in-progress status.) In both cases, there
1120     // is nothing we need to do.
1121     //
1122     goto Exit;
1123   } else {
1124     //
1125     // Signal presence of this processor
1126     //
1127     if (ReleaseSemaphore (mSmmMpSyncData->Counter) == 0) {
1128       //
1129       // BSP has already ended the synchronization, so QUIT!!!
1130       //
1131 
1132       //
1133       // Wait for BSP's signal to finish SMI
1134       //
1135       while (*mSmmMpSyncData->AllCpusInSync) {
1136         CpuPause ();
1137       }
1138       goto Exit;
1139     } else {
1140 
1141       //
1142       // The BUSY lock is initialized to Released state.
1143       // This needs to be done early enough to be ready for BSP's SmmStartupThisAp() call.
1144       // E.g., with Relaxed AP flow, SmmStartupThisAp() may be called immediately
1145       // after AP's present flag is detected.
1146       //
1147       InitializeSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
1148     }
1149 
1150     if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
1151       ActivateSmmProfile (CpuIndex);
1152     }
1153 
1154     if (BspInProgress) {
1155       //
1156       // BSP has been elected. Follow AP path, regardless of ValidSmi flag
1157       // as BSP may have cleared the SMI status
1158       //
1159       APHandler (CpuIndex, ValidSmi, mSmmMpSyncData->EffectiveSyncMode);
1160     } else {
1161       //
1162       // We have a valid SMI
1163       //
1164 
1165       //
1166       // Elect BSP
1167       //
1168       IsBsp = FALSE;
1169       if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) {
1170         if (!mSmmMpSyncData->SwitchBsp || mSmmMpSyncData->CandidateBsp[CpuIndex]) {
1171           //
1172           // Call platform hook to do BSP election
1173           //
1174           Status = PlatformSmmBspElection (&IsBsp);
1175           if (EFI_SUCCESS == Status) {
1176             //
1177             // Platform hook determines successfully
1178             //
1179             if (IsBsp) {
1180               mSmmMpSyncData->BspIndex = (UINT32)CpuIndex;
1181             }
1182           } else {
1183             //
1184             // Platform hook fails to determine, use default BSP election method
1185             //
1186             InterlockedCompareExchange32 (
1187               (UINT32*)&mSmmMpSyncData->BspIndex,
1188               (UINT32)-1,
1189               (UINT32)CpuIndex
1190               );
1191           }
1192         }
1193       }
1194 
1195       //
1196       // "mSmmMpSyncData->BspIndex == CpuIndex" means this is the BSP
1197       //
1198       if (mSmmMpSyncData->BspIndex == CpuIndex) {
1199 
1200         //
1201         // Clear last request for SwitchBsp.
1202         //
1203         if (mSmmMpSyncData->SwitchBsp) {
1204           mSmmMpSyncData->SwitchBsp = FALSE;
1205           for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
1206             mSmmMpSyncData->CandidateBsp[Index] = FALSE;
1207           }
1208         }
1209 
1210         if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
1211           SmmProfileRecordSmiNum ();
1212         }
1213 
1214         //
1215         // BSP Handler is always called with a ValidSmi == TRUE
1216         //
1217         BSPHandler (CpuIndex, mSmmMpSyncData->EffectiveSyncMode);
1218       } else {
1219         APHandler (CpuIndex, ValidSmi, mSmmMpSyncData->EffectiveSyncMode);
1220       }
1221     }
1222 
1223     ASSERT (*mSmmMpSyncData->CpuData[CpuIndex].Run == 0);
1224 
1225     //
1226     // Wait for BSP's signal to exit SMI
1227     //
1228     while (*mSmmMpSyncData->AllCpusInSync) {
1229       CpuPause ();
1230     }
1231   }
1232 
1233 Exit:
1234   SmmCpuFeaturesRendezvousExit (CpuIndex);
1235   //
1236   // Restore Cr2
1237   //
1238   AsmWriteCr2 (Cr2);
1239 }
1240 
1241 /**
1242   Allocate buffer for all semaphores and spin locks.
1243 
1244 **/
1245 VOID
InitializeSmmCpuSemaphores(VOID)1246 InitializeSmmCpuSemaphores (
1247   VOID
1248   )
1249 {
1250   UINTN                      ProcessorCount;
1251   UINTN                      TotalSize;
1252   UINTN                      GlobalSemaphoresSize;
1253   UINTN                      CpuSemaphoresSize;
1254   UINTN                      MsrSemahporeSize;
1255   UINTN                      SemaphoreSize;
1256   UINTN                      Pages;
1257   UINTN                      *SemaphoreBlock;
1258   UINTN                      SemaphoreAddr;
1259 
1260   SemaphoreSize   = GetSpinLockProperties ();
1261   ProcessorCount = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
1262   GlobalSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_GLOBAL) / sizeof (VOID *)) * SemaphoreSize;
1263   CpuSemaphoresSize    = (sizeof (SMM_CPU_SEMAPHORE_CPU) / sizeof (VOID *)) * ProcessorCount * SemaphoreSize;
1264   MsrSemahporeSize     = MSR_SPIN_LOCK_INIT_NUM * SemaphoreSize;
1265   TotalSize = GlobalSemaphoresSize + CpuSemaphoresSize + MsrSemahporeSize;
1266   DEBUG((EFI_D_INFO, "One Semaphore Size    = 0x%x\n", SemaphoreSize));
1267   DEBUG((EFI_D_INFO, "Total Semaphores Size = 0x%x\n", TotalSize));
1268   Pages = EFI_SIZE_TO_PAGES (TotalSize);
1269   SemaphoreBlock = AllocatePages (Pages);
1270   ASSERT (SemaphoreBlock != NULL);
1271   ZeroMem (SemaphoreBlock, TotalSize);
1272 
1273   SemaphoreAddr = (UINTN)SemaphoreBlock;
1274   mSmmCpuSemaphores.SemaphoreGlobal.Counter       = (UINT32 *)SemaphoreAddr;
1275   SemaphoreAddr += SemaphoreSize;
1276   mSmmCpuSemaphores.SemaphoreGlobal.InsideSmm     = (BOOLEAN *)SemaphoreAddr;
1277   SemaphoreAddr += SemaphoreSize;
1278   mSmmCpuSemaphores.SemaphoreGlobal.AllCpusInSync = (BOOLEAN *)SemaphoreAddr;
1279   SemaphoreAddr += SemaphoreSize;
1280   mSmmCpuSemaphores.SemaphoreGlobal.PFLock        = (SPIN_LOCK *)SemaphoreAddr;
1281   SemaphoreAddr += SemaphoreSize;
1282   mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock
1283                                                   = (SPIN_LOCK *)SemaphoreAddr;
1284   SemaphoreAddr += SemaphoreSize;
1285   mSmmCpuSemaphores.SemaphoreGlobal.MemoryMappedLock
1286                                                   = (SPIN_LOCK *)SemaphoreAddr;
1287 
1288   SemaphoreAddr = (UINTN)SemaphoreBlock + GlobalSemaphoresSize;
1289   mSmmCpuSemaphores.SemaphoreCpu.Busy    = (SPIN_LOCK *)SemaphoreAddr;
1290   SemaphoreAddr += ProcessorCount * SemaphoreSize;
1291   mSmmCpuSemaphores.SemaphoreCpu.Run     = (UINT32 *)SemaphoreAddr;
1292   SemaphoreAddr += ProcessorCount * SemaphoreSize;
1293   mSmmCpuSemaphores.SemaphoreCpu.Present = (BOOLEAN *)SemaphoreAddr;
1294 
1295   SemaphoreAddr = (UINTN)SemaphoreBlock + GlobalSemaphoresSize + CpuSemaphoresSize;
1296   mSmmCpuSemaphores.SemaphoreMsr.Msr              = (SPIN_LOCK *)SemaphoreAddr;
1297   mSmmCpuSemaphores.SemaphoreMsr.AvailableCounter =
1298         ((UINTN)SemaphoreBlock + Pages * SIZE_4KB - SemaphoreAddr) / SemaphoreSize;
1299   ASSERT (mSmmCpuSemaphores.SemaphoreMsr.AvailableCounter >= MSR_SPIN_LOCK_INIT_NUM);
1300 
1301   mPFLock                       = mSmmCpuSemaphores.SemaphoreGlobal.PFLock;
1302   mConfigSmmCodeAccessCheckLock = mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock;
1303   mMemoryMappedLock             = mSmmCpuSemaphores.SemaphoreGlobal.MemoryMappedLock;
1304 
1305   mSemaphoreSize = SemaphoreSize;
1306 }
1307 
1308 /**
1309   Initialize un-cacheable data.
1310 
1311 **/
1312 VOID
1313 EFIAPI
InitializeMpSyncData(VOID)1314 InitializeMpSyncData (
1315   VOID
1316   )
1317 {
1318   UINTN                      CpuIndex;
1319 
1320   if (mSmmMpSyncData != NULL) {
1321     //
1322     // mSmmMpSyncDataSize includes one structure of SMM_DISPATCHER_MP_SYNC_DATA, one
1323     // CpuData array of SMM_CPU_DATA_BLOCK and one CandidateBsp array of BOOLEAN.
1324     //
1325     ZeroMem (mSmmMpSyncData, mSmmMpSyncDataSize);
1326     mSmmMpSyncData->CpuData = (SMM_CPU_DATA_BLOCK *)((UINT8 *)mSmmMpSyncData + sizeof (SMM_DISPATCHER_MP_SYNC_DATA));
1327     mSmmMpSyncData->CandidateBsp = (BOOLEAN *)(mSmmMpSyncData->CpuData + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);
1328     if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) {
1329       //
1330       // Enable BSP election by setting BspIndex to -1
1331       //
1332       mSmmMpSyncData->BspIndex = (UINT32)-1;
1333     }
1334     mSmmMpSyncData->EffectiveSyncMode = mCpuSmmSyncMode;
1335 
1336     mSmmMpSyncData->Counter       = mSmmCpuSemaphores.SemaphoreGlobal.Counter;
1337     mSmmMpSyncData->InsideSmm     = mSmmCpuSemaphores.SemaphoreGlobal.InsideSmm;
1338     mSmmMpSyncData->AllCpusInSync = mSmmCpuSemaphores.SemaphoreGlobal.AllCpusInSync;
1339     ASSERT (mSmmMpSyncData->Counter != NULL && mSmmMpSyncData->InsideSmm != NULL &&
1340             mSmmMpSyncData->AllCpusInSync != NULL);
1341     *mSmmMpSyncData->Counter       = 0;
1342     *mSmmMpSyncData->InsideSmm     = FALSE;
1343     *mSmmMpSyncData->AllCpusInSync = FALSE;
1344 
1345     for (CpuIndex = 0; CpuIndex < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; CpuIndex ++) {
1346       mSmmMpSyncData->CpuData[CpuIndex].Busy    =
1347         (SPIN_LOCK *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Busy + mSemaphoreSize * CpuIndex);
1348       mSmmMpSyncData->CpuData[CpuIndex].Run     =
1349         (UINT32 *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Run + mSemaphoreSize * CpuIndex);
1350       mSmmMpSyncData->CpuData[CpuIndex].Present =
1351         (BOOLEAN *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Present + mSemaphoreSize * CpuIndex);
1352       *(mSmmMpSyncData->CpuData[CpuIndex].Busy)    = 0;
1353       *(mSmmMpSyncData->CpuData[CpuIndex].Run)     = 0;
1354       *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE;
1355     }
1356   }
1357 }
1358 
1359 /**
1360   Initialize global data for MP synchronization.
1361 
1362   @param Stacks       Base address of SMI stack buffer for all processors.
1363   @param StackSize    Stack size for each processor in SMM.
1364 
1365 **/
1366 UINT32
InitializeMpServiceData(IN VOID * Stacks,IN UINTN StackSize)1367 InitializeMpServiceData (
1368   IN VOID        *Stacks,
1369   IN UINTN       StackSize
1370   )
1371 {
1372   UINT32                    Cr3;
1373   UINTN                     Index;
1374   UINT8                     *GdtTssTables;
1375   UINTN                     GdtTableStepSize;
1376 
1377   //
1378   // Allocate memory for all locks and semaphores
1379   //
1380   InitializeSmmCpuSemaphores ();
1381 
1382   //
1383   // Initialize mSmmMpSyncData
1384   //
1385   mSmmMpSyncDataSize = sizeof (SMM_DISPATCHER_MP_SYNC_DATA) +
1386                        (sizeof (SMM_CPU_DATA_BLOCK) + sizeof (BOOLEAN)) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
1387   mSmmMpSyncData = (SMM_DISPATCHER_MP_SYNC_DATA*) AllocatePages (EFI_SIZE_TO_PAGES (mSmmMpSyncDataSize));
1388   ASSERT (mSmmMpSyncData != NULL);
1389   mCpuSmmSyncMode = (SMM_CPU_SYNC_MODE)PcdGet8 (PcdCpuSmmSyncMode);
1390   InitializeMpSyncData ();
1391 
1392   //
1393   // Initialize physical address mask
1394   // NOTE: Physical memory above virtual address limit is not supported !!!
1395   //
1396   AsmCpuid (0x80000008, (UINT32*)&Index, NULL, NULL, NULL);
1397   gPhyMask = LShiftU64 (1, (UINT8)Index) - 1;
1398   gPhyMask &= (1ull << 48) - EFI_PAGE_SIZE;
1399 
1400   //
1401   // Create page tables
1402   //
1403   Cr3 = SmmInitPageTable ();
1404 
1405   GdtTssTables = InitGdt (Cr3, &GdtTableStepSize);
1406 
1407   //
1408   // Install SMI handler for each CPU
1409   //
1410   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
1411     InstallSmiHandler (
1412       Index,
1413       (UINT32)mCpuHotPlugData.SmBase[Index],
1414       (VOID*)((UINTN)Stacks + (StackSize * Index)),
1415       StackSize,
1416       (UINTN)(GdtTssTables + GdtTableStepSize * Index),
1417       gcSmiGdtr.Limit + 1,
1418       gcSmiIdtr.Base,
1419       gcSmiIdtr.Limit + 1,
1420       Cr3
1421       );
1422   }
1423 
1424   //
1425   // Record current MTRR settings
1426   //
1427   ZeroMem (&gSmiMtrrs, sizeof (gSmiMtrrs));
1428   MtrrGetAllMtrrs (&gSmiMtrrs);
1429 
1430   return Cr3;
1431 }
1432 
1433 /**
1434 
1435   Register the SMM Foundation entry point.
1436 
1437   @param          This              Pointer to EFI_SMM_CONFIGURATION_PROTOCOL instance
1438   @param          SmmEntryPoint     SMM Foundation EntryPoint
1439 
1440   @retval         EFI_SUCCESS       Successfully to register SMM foundation entry point
1441 
1442 **/
1443 EFI_STATUS
1444 EFIAPI
RegisterSmmEntry(IN CONST EFI_SMM_CONFIGURATION_PROTOCOL * This,IN EFI_SMM_ENTRY_POINT SmmEntryPoint)1445 RegisterSmmEntry (
1446   IN CONST EFI_SMM_CONFIGURATION_PROTOCOL  *This,
1447   IN EFI_SMM_ENTRY_POINT                   SmmEntryPoint
1448   )
1449 {
1450   //
1451   // Record SMM Foundation EntryPoint, later invoke it on SMI entry vector.
1452   //
1453   gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint;
1454   return EFI_SUCCESS;
1455 }
1456