1 /** @file
2 Implementation of SMM CPU Services Protocol.
3 
4 Copyright (c) 2011 - 2015, 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 // SMM CPU Service Protocol instance
19 //
20 EFI_SMM_CPU_SERVICE_PROTOCOL  mSmmCpuService = {
21   SmmGetProcessorInfo,
22   SmmSwitchBsp,
23   SmmAddProcessor,
24   SmmRemoveProcessor,
25   SmmWhoAmI,
26   SmmRegisterExceptionHandler
27 };
28 
29 /**
30   Get Package ID/Core ID/Thread ID of a processor.
31 
32   APIC ID must be an initial APIC ID.
33 
34   The algorithm below assumes the target system has symmetry across physical package boundaries
35   with respect to the number of logical processors per package, number of cores per package.
36 
37   @param  ApicId    APIC ID of the target logical processor.
38   @param  Location    Returns the processor location information.
39 **/
40 VOID
SmmGetProcessorLocation(IN UINT32 ApicId,OUT EFI_CPU_PHYSICAL_LOCATION * Location)41 SmmGetProcessorLocation (
42   IN UINT32 ApicId,
43   OUT EFI_CPU_PHYSICAL_LOCATION *Location
44   )
45 {
46   UINTN   ThreadBits;
47   UINTN   CoreBits;
48   UINT32  RegEax;
49   UINT32  RegEbx;
50   UINT32  RegEcx;
51   UINT32  RegEdx;
52   UINT32  MaxCpuIdIndex;
53   UINT32  SubIndex;
54   UINTN   LevelType;
55   UINT32  MaxLogicProcessorsPerPackage;
56   UINT32  MaxCoresPerPackage;
57   BOOLEAN TopologyLeafSupported;
58 
59   ASSERT (Location != NULL);
60 
61   ThreadBits            = 0;
62   CoreBits              = 0;
63   TopologyLeafSupported = FALSE;
64 
65   //
66   // Check if the processor is capable of supporting more than one logical processor.
67   //
68   AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
69   ASSERT ((RegEdx & BIT28) != 0);
70 
71   //
72   // Assume three-level mapping of APIC ID: Package:Core:SMT.
73   //
74 
75   //
76   // Get the max index of basic CPUID
77   //
78   AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL);
79 
80   //
81   // If the extended topology enumeration leaf is available, it
82   // is the preferred mechanism for enumerating topology.
83   //
84   if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {
85     AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, &RegEax, &RegEbx, &RegEcx, NULL);
86     //
87     // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for
88     // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not
89     // supported on that processor.
90     //
91     if ((RegEbx & 0xffff) != 0) {
92       TopologyLeafSupported = TRUE;
93 
94       //
95       // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract
96       // the SMT sub-field of x2APIC ID.
97       //
98       LevelType = (RegEcx >> 8) & 0xff;
99       ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT);
100       if ((RegEbx & 0xffff) > 1 ) {
101         ThreadBits = RegEax & 0x1f;
102       } else {
103         //
104         // HT is not supported
105         //
106         ThreadBits = 0;
107       }
108 
109       //
110       // Software must not assume any "level type" encoding
111       // value to be related to any sub-leaf index, except sub-leaf 0.
112       //
113       SubIndex = 1;
114       do {
115         AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, SubIndex, &RegEax, NULL, &RegEcx, NULL);
116         LevelType = (RegEcx >> 8) & 0xff;
117         if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) {
118           CoreBits = (RegEax & 0x1f) - ThreadBits;
119           break;
120         }
121         SubIndex++;
122       } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID);
123     }
124   }
125 
126   if (!TopologyLeafSupported) {
127     AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);
128     MaxLogicProcessorsPerPackage = (RegEbx >> 16) & 0xff;
129     if (MaxCpuIdIndex >= CPUID_CACHE_PARAMS) {
130       AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &RegEax, NULL, NULL, NULL);
131       MaxCoresPerPackage = (RegEax >> 26) + 1;
132     } else {
133       //
134       // Must be a single-core processor.
135       //
136       MaxCoresPerPackage = 1;
137     }
138 
139     ThreadBits = (UINTN) (HighBitSet32 (MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1);
140     CoreBits = (UINTN) (HighBitSet32 (MaxCoresPerPackage - 1) + 1);
141   }
142 
143   Location->Thread = ApicId & ~((-1) << ThreadBits);
144   Location->Core = (ApicId >> ThreadBits) & ~((-1) << CoreBits);
145   Location->Package = (ApicId >> (ThreadBits+ CoreBits));
146 }
147 
148 /**
149   Gets processor information on the requested processor at the instant this call is made.
150 
151   @param[in]  This                 A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
152   @param[in]  ProcessorNumber      The handle number of processor.
153   @param[out] ProcessorInfoBuffer  A pointer to the buffer where information for
154                                    the requested processor is deposited.
155 
156   @retval EFI_SUCCESS             Processor information was returned.
157   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
158   @retval EFI_INVALID_PARAMETER   ProcessorNumber is invalid.
159   @retval EFI_NOT_FOUND           The processor with the handle specified by
160                                   ProcessorNumber does not exist in the platform.
161 
162 **/
163 EFI_STATUS
164 EFIAPI
SmmGetProcessorInfo(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINTN ProcessorNumber,OUT EFI_PROCESSOR_INFORMATION * ProcessorInfoBuffer)165 SmmGetProcessorInfo (
166   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
167   IN       UINTN                        ProcessorNumber,
168   OUT      EFI_PROCESSOR_INFORMATION    *ProcessorInfoBuffer
169   )
170 {
171   //
172   // Check parameter
173   //
174   if (ProcessorNumber >= mMaxNumberOfCpus || ProcessorInfoBuffer == NULL) {
175     return EFI_INVALID_PARAMETER;
176   }
177 
178   if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
179     return EFI_NOT_FOUND;
180   }
181 
182   //
183   // Fill in processor information
184   //
185   CopyMem (ProcessorInfoBuffer, &gSmmCpuPrivate->ProcessorInfo[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
186   return EFI_SUCCESS;
187 }
188 
189 /**
190   This service switches the requested AP to be the BSP since the next SMI.
191 
192   @param[in] This             A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
193   @param[in] ProcessorNumber  The handle number of AP that is to become the new BSP.
194 
195   @retval EFI_SUCCESS             BSP will be switched in next SMI.
196   @retval EFI_UNSUPPORTED         Switching the BSP or a processor to be hot-removed is not supported.
197   @retval EFI_NOT_FOUND           The processor with the handle specified by ProcessorNumber does not exist.
198   @retval EFI_INVALID_PARAMETER   ProcessorNumber is invalid.
199 **/
200 EFI_STATUS
201 EFIAPI
SmmSwitchBsp(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINTN ProcessorNumber)202 SmmSwitchBsp (
203   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
204   IN       UINTN                        ProcessorNumber
205   )
206 {
207   //
208   // Check parameter
209   //
210   if (ProcessorNumber >= mMaxNumberOfCpus) {
211     return EFI_INVALID_PARAMETER;
212   }
213 
214   if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
215     return EFI_NOT_FOUND;
216   }
217 
218   if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone ||
219       gSmst->CurrentlyExecutingCpu == ProcessorNumber) {
220     return EFI_UNSUPPORTED;
221   }
222 
223   //
224   // Setting of the BSP for next SMI is pending until all SMI handlers are finished
225   //
226   gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuSwitchBsp;
227   return EFI_SUCCESS;
228 }
229 
230 /**
231   Notify that a processor was hot-added.
232 
233   @param[in] This                A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
234   @param[in] ProcessorId         Local APIC ID of the hot-added processor.
235   @param[out] ProcessorNumber    The handle number of the hot-added processor.
236 
237   @retval EFI_SUCCESS            The hot-addition of the specified processors was successfully notified.
238   @retval EFI_UNSUPPORTED        Hot addition of processor is not supported.
239   @retval EFI_NOT_FOUND          The processor with the handle specified by ProcessorNumber does not exist.
240   @retval EFI_INVALID_PARAMETER  ProcessorNumber is invalid.
241   @retval EFI_ALREADY_STARTED    The processor is already online in the system.
242 **/
243 EFI_STATUS
244 EFIAPI
SmmAddProcessor(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINT64 ProcessorId,OUT UINTN * ProcessorNumber)245 SmmAddProcessor (
246   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
247   IN       UINT64                        ProcessorId,
248   OUT      UINTN                         *ProcessorNumber
249   )
250 {
251   UINTN  Index;
252 
253   if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
254     return EFI_UNSUPPORTED;
255   }
256 
257   //
258   // Check parameter
259   //
260   if (ProcessorNumber == NULL || ProcessorId == INVALID_APIC_ID) {
261     return EFI_INVALID_PARAMETER;
262   }
263 
264   //
265   // Check if the processor already exists
266   //
267 
268   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
269     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ProcessorId) {
270       return EFI_ALREADY_STARTED;
271     }
272   }
273 
274   //
275   // Check CPU hot plug data. The CPU RAS handler should have created the mapping
276   // of the APIC ID to SMBASE.
277   //
278   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
279     if (mCpuHotPlugData.ApicId[Index] == ProcessorId &&
280         gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == INVALID_APIC_ID) {
281       gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = ProcessorId;
282       gSmmCpuPrivate->ProcessorInfo[Index].StatusFlag = 0;
283       SmmGetProcessorLocation ((UINT32)ProcessorId, &gSmmCpuPrivate->ProcessorInfo[Index].Location);
284 
285       *ProcessorNumber = Index;
286       gSmmCpuPrivate->Operation[Index] = SmmCpuAdd;
287       return EFI_SUCCESS;
288     }
289   }
290 
291   return EFI_INVALID_PARAMETER;
292 }
293 
294 /**
295   Notify that a processor was hot-removed.
296 
297   @param[in] This                A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
298   @param[in] ProcessorNumber     The handle number of the hot-added processor.
299 
300   @retval EFI_SUCCESS            The hot-removal of the specified processors was successfully notified.
301   @retval EFI_UNSUPPORTED        Hot removal of processor is not supported.
302   @retval EFI_UNSUPPORTED        Hot removal of BSP is not supported.
303   @retval EFI_UNSUPPORTED        Hot removal of a processor with pending hot-plug operation is not supported.
304   @retval EFI_INVALID_PARAMETER  ProcessorNumber is invalid.
305 **/
306 EFI_STATUS
307 EFIAPI
SmmRemoveProcessor(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINTN ProcessorNumber)308 SmmRemoveProcessor (
309   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
310   IN       UINTN                         ProcessorNumber
311   )
312 {
313   if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
314     return EFI_UNSUPPORTED;
315   }
316 
317   //
318   // Check parameter
319   //
320   if (ProcessorNumber >= mMaxNumberOfCpus ||
321       gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
322     return EFI_INVALID_PARAMETER;
323   }
324 
325   //
326   // Can't remove BSP
327   //
328   if (ProcessorNumber == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
329     return EFI_UNSUPPORTED;
330   }
331 
332   if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone) {
333     return EFI_UNSUPPORTED;
334   }
335 
336   gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId = INVALID_APIC_ID;
337   mCpuHotPlugData.ApicId[ProcessorNumber] = INVALID_APIC_ID;
338 
339   //
340   // Removal of the processor from the CPU list is pending until all SMI handlers are finished
341   //
342   gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuRemove;
343   return EFI_SUCCESS;
344 }
345 
346 /**
347   This return the handle number for the calling processor.
348 
349   @param[in] This                 A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
350   @param[out] ProcessorNumber      The handle number of currently executing processor.
351 
352   @retval EFI_SUCCESS             The current processor handle number was returned
353                                   in ProcessorNumber.
354   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
355 
356 **/
357 EFI_STATUS
358 EFIAPI
SmmWhoAmI(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,OUT UINTN * ProcessorNumber)359 SmmWhoAmI (
360   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
361   OUT      UINTN                        *ProcessorNumber
362   )
363 {
364   UINTN  Index;
365   UINT64 ApicId;
366 
367   //
368   // Check parameter
369   //
370   if (ProcessorNumber == NULL) {
371     return EFI_INVALID_PARAMETER;
372   }
373 
374   ApicId = GetApicId ();
375 
376   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
377     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) {
378       *ProcessorNumber = Index;
379       return EFI_SUCCESS;
380     }
381   }
382   //
383   // This should not happen
384   //
385   ASSERT (FALSE);
386   return EFI_NOT_FOUND;
387 }
388 
389 /**
390   Update the SMM CPU list per the pending operation.
391 
392   This function is called after return from SMI handlers.
393 **/
394 VOID
SmmCpuUpdate(VOID)395 SmmCpuUpdate (
396   VOID
397   )
398 {
399   UINTN   Index;
400 
401   //
402   // Handle pending BSP switch operations
403   //
404   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
405     if (gSmmCpuPrivate->Operation[Index] == SmmCpuSwitchBsp) {
406       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
407       mSmmMpSyncData->SwitchBsp = TRUE;
408       mSmmMpSyncData->CandidateBsp[Index] = TRUE;
409     }
410   }
411 
412   //
413   // Handle pending hot-add operations
414   //
415   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
416     if (gSmmCpuPrivate->Operation[Index] == SmmCpuAdd) {
417       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
418       mNumberOfCpus++;
419     }
420   }
421 
422   //
423   // Handle pending hot-remove operations
424   //
425   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
426     if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) {
427       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
428       mNumberOfCpus--;
429     }
430   }
431 }
432 
433 /**
434   Register exception handler.
435 
436   @param  This                  A pointer to the SMM_CPU_SERVICE_PROTOCOL instance.
437   @param  ExceptionType         Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and
438                                 the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL
439                                 of the UEFI 2.0 specification.
440   @param  InterruptHandler      A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER
441                                 that is called when a processor interrupt occurs.
442                                 If this parameter is NULL, then the handler will be uninstalled.
443 
444   @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
445   @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was previously installed.
446   @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed.
447   @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
448 
449 **/
450 EFI_STATUS
451 EFIAPI
SmmRegisterExceptionHandler(IN EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN EFI_EXCEPTION_TYPE ExceptionType,IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler)452 SmmRegisterExceptionHandler (
453     IN EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
454     IN EFI_EXCEPTION_TYPE            ExceptionType,
455     IN EFI_CPU_INTERRUPT_HANDLER     InterruptHandler
456     )
457 {
458   return RegisterCpuInterruptHandler (ExceptionType, InterruptHandler);
459 }
460 
461 /**
462   Initialize SMM CPU Services.
463 
464   It installs EFI SMM CPU Services Protocol.
465 
466   @param ImageHandle The firmware allocated handle for the EFI image.
467 
468   @retval EFI_SUCCESS    EFI SMM CPU Services Protocol was installed successfully.
469 **/
470 EFI_STATUS
InitializeSmmCpuServices(IN EFI_HANDLE Handle)471 InitializeSmmCpuServices (
472   IN EFI_HANDLE  Handle
473   )
474 {
475   EFI_STATUS Status;
476 
477   Status = gSmst->SmmInstallProtocolInterface (
478                     &Handle,
479                     &gEfiSmmCpuServiceProtocolGuid,
480                     EFI_NATIVE_INTERFACE,
481                     &mSmmCpuService
482                     );
483   ASSERT_EFI_ERROR (Status);
484   return Status;
485 }
486 
487