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   Gets processor information on the requested processor at the instant this call is made.
31 
32   @param[in]  This                 A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
33   @param[in]  ProcessorNumber      The handle number of processor.
34   @param[out] ProcessorInfoBuffer  A pointer to the buffer where information for
35                                    the requested processor is deposited.
36 
37   @retval EFI_SUCCESS             Processor information was returned.
38   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
39   @retval EFI_INVALID_PARAMETER   ProcessorNumber is invalid.
40   @retval EFI_NOT_FOUND           The processor with the handle specified by
41                                   ProcessorNumber does not exist in the platform.
42 
43 **/
44 EFI_STATUS
45 EFIAPI
SmmGetProcessorInfo(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINTN ProcessorNumber,OUT EFI_PROCESSOR_INFORMATION * ProcessorInfoBuffer)46 SmmGetProcessorInfo (
47   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
48   IN       UINTN                        ProcessorNumber,
49   OUT      EFI_PROCESSOR_INFORMATION    *ProcessorInfoBuffer
50   )
51 {
52   //
53   // Check parameter
54   //
55   if (ProcessorNumber >= mMaxNumberOfCpus || ProcessorInfoBuffer == NULL) {
56     return EFI_INVALID_PARAMETER;
57   }
58 
59   if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
60     return EFI_NOT_FOUND;
61   }
62 
63   //
64   // Fill in processor information
65   //
66   CopyMem (ProcessorInfoBuffer, &gSmmCpuPrivate->ProcessorInfo[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
67   return EFI_SUCCESS;
68 }
69 
70 /**
71   This service switches the requested AP to be the BSP since the next SMI.
72 
73   @param[in] This             A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
74   @param[in] ProcessorNumber  The handle number of AP that is to become the new BSP.
75 
76   @retval EFI_SUCCESS             BSP will be switched in next SMI.
77   @retval EFI_UNSUPPORTED         Switching the BSP or a processor to be hot-removed is not supported.
78   @retval EFI_NOT_FOUND           The processor with the handle specified by ProcessorNumber does not exist.
79   @retval EFI_INVALID_PARAMETER   ProcessorNumber is invalid.
80 **/
81 EFI_STATUS
82 EFIAPI
SmmSwitchBsp(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINTN ProcessorNumber)83 SmmSwitchBsp (
84   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
85   IN       UINTN                        ProcessorNumber
86   )
87 {
88   //
89   // Check parameter
90   //
91   if (ProcessorNumber >= mMaxNumberOfCpus) {
92     return EFI_INVALID_PARAMETER;
93   }
94 
95   if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
96     return EFI_NOT_FOUND;
97   }
98 
99   if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone ||
100       gSmst->CurrentlyExecutingCpu == ProcessorNumber) {
101     return EFI_UNSUPPORTED;
102   }
103 
104   //
105   // Setting of the BSP for next SMI is pending until all SMI handlers are finished
106   //
107   gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuSwitchBsp;
108   return EFI_SUCCESS;
109 }
110 
111 /**
112   Notify that a processor was hot-added.
113 
114   @param[in] This                A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
115   @param[in] ProcessorId         Local APIC ID of the hot-added processor.
116   @param[out] ProcessorNumber    The handle number of the hot-added processor.
117 
118   @retval EFI_SUCCESS            The hot-addition of the specified processors was successfully notified.
119   @retval EFI_UNSUPPORTED        Hot addition of processor is not supported.
120   @retval EFI_NOT_FOUND          The processor with the handle specified by ProcessorNumber does not exist.
121   @retval EFI_INVALID_PARAMETER  ProcessorNumber is invalid.
122   @retval EFI_ALREADY_STARTED    The processor is already online in the system.
123 **/
124 EFI_STATUS
125 EFIAPI
SmmAddProcessor(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINT64 ProcessorId,OUT UINTN * ProcessorNumber)126 SmmAddProcessor (
127   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
128   IN       UINT64                        ProcessorId,
129   OUT      UINTN                         *ProcessorNumber
130   )
131 {
132   UINTN  Index;
133 
134   if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
135     return EFI_UNSUPPORTED;
136   }
137 
138   //
139   // Check parameter
140   //
141   if (ProcessorNumber == NULL || ProcessorId == INVALID_APIC_ID) {
142     return EFI_INVALID_PARAMETER;
143   }
144 
145   //
146   // Check if the processor already exists
147   //
148 
149   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
150     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ProcessorId) {
151       return EFI_ALREADY_STARTED;
152     }
153   }
154 
155   //
156   // Check CPU hot plug data. The CPU RAS handler should have created the mapping
157   // of the APIC ID to SMBASE.
158   //
159   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
160     if (mCpuHotPlugData.ApicId[Index] == ProcessorId &&
161         gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == INVALID_APIC_ID) {
162       gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = ProcessorId;
163       gSmmCpuPrivate->ProcessorInfo[Index].StatusFlag = 0;
164       GetProcessorLocationByApicId (
165         (UINT32)ProcessorId,
166         &gSmmCpuPrivate->ProcessorInfo[Index].Location.Package,
167         &gSmmCpuPrivate->ProcessorInfo[Index].Location.Core,
168         &gSmmCpuPrivate->ProcessorInfo[Index].Location.Thread
169         );
170 
171       *ProcessorNumber = Index;
172       gSmmCpuPrivate->Operation[Index] = SmmCpuAdd;
173       return EFI_SUCCESS;
174     }
175   }
176 
177   return EFI_INVALID_PARAMETER;
178 }
179 
180 /**
181   Notify that a processor was hot-removed.
182 
183   @param[in] This                A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
184   @param[in] ProcessorNumber     The handle number of the hot-added processor.
185 
186   @retval EFI_SUCCESS            The hot-removal of the specified processors was successfully notified.
187   @retval EFI_UNSUPPORTED        Hot removal of processor is not supported.
188   @retval EFI_UNSUPPORTED        Hot removal of BSP is not supported.
189   @retval EFI_UNSUPPORTED        Hot removal of a processor with pending hot-plug operation is not supported.
190   @retval EFI_INVALID_PARAMETER  ProcessorNumber is invalid.
191 **/
192 EFI_STATUS
193 EFIAPI
SmmRemoveProcessor(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN UINTN ProcessorNumber)194 SmmRemoveProcessor (
195   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
196   IN       UINTN                         ProcessorNumber
197   )
198 {
199   if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
200     return EFI_UNSUPPORTED;
201   }
202 
203   //
204   // Check parameter
205   //
206   if (ProcessorNumber >= mMaxNumberOfCpus ||
207       gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
208     return EFI_INVALID_PARAMETER;
209   }
210 
211   //
212   // Can't remove BSP
213   //
214   if (ProcessorNumber == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
215     return EFI_UNSUPPORTED;
216   }
217 
218   if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone) {
219     return EFI_UNSUPPORTED;
220   }
221 
222   gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId = INVALID_APIC_ID;
223   mCpuHotPlugData.ApicId[ProcessorNumber] = INVALID_APIC_ID;
224 
225   //
226   // Removal of the processor from the CPU list is pending until all SMI handlers are finished
227   //
228   gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuRemove;
229   return EFI_SUCCESS;
230 }
231 
232 /**
233   This return the handle number for the calling processor.
234 
235   @param[in] This                 A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
236   @param[out] ProcessorNumber      The handle number of currently executing processor.
237 
238   @retval EFI_SUCCESS             The current processor handle number was returned
239                                   in ProcessorNumber.
240   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
241 
242 **/
243 EFI_STATUS
244 EFIAPI
SmmWhoAmI(IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL * This,OUT UINTN * ProcessorNumber)245 SmmWhoAmI (
246   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
247   OUT      UINTN                        *ProcessorNumber
248   )
249 {
250   UINTN  Index;
251   UINT64 ApicId;
252 
253   //
254   // Check parameter
255   //
256   if (ProcessorNumber == NULL) {
257     return EFI_INVALID_PARAMETER;
258   }
259 
260   ApicId = GetApicId ();
261 
262   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
263     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) {
264       *ProcessorNumber = Index;
265       return EFI_SUCCESS;
266     }
267   }
268   //
269   // This should not happen
270   //
271   ASSERT (FALSE);
272   return EFI_NOT_FOUND;
273 }
274 
275 /**
276   Update the SMM CPU list per the pending operation.
277 
278   This function is called after return from SMI handlers.
279 **/
280 VOID
SmmCpuUpdate(VOID)281 SmmCpuUpdate (
282   VOID
283   )
284 {
285   UINTN   Index;
286 
287   //
288   // Handle pending BSP switch operations
289   //
290   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
291     if (gSmmCpuPrivate->Operation[Index] == SmmCpuSwitchBsp) {
292       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
293       mSmmMpSyncData->SwitchBsp = TRUE;
294       mSmmMpSyncData->CandidateBsp[Index] = TRUE;
295     }
296   }
297 
298   //
299   // Handle pending hot-add operations
300   //
301   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
302     if (gSmmCpuPrivate->Operation[Index] == SmmCpuAdd) {
303       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
304       mNumberOfCpus++;
305     }
306   }
307 
308   //
309   // Handle pending hot-remove operations
310   //
311   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
312     if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) {
313       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
314       mNumberOfCpus--;
315     }
316   }
317 }
318 
319 /**
320   Register exception handler.
321 
322   @param  This                  A pointer to the SMM_CPU_SERVICE_PROTOCOL instance.
323   @param  ExceptionType         Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and
324                                 the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL
325                                 of the UEFI 2.0 specification.
326   @param  InterruptHandler      A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER
327                                 that is called when a processor interrupt occurs.
328                                 If this parameter is NULL, then the handler will be uninstalled.
329 
330   @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
331   @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was previously installed.
332   @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed.
333   @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
334 
335 **/
336 EFI_STATUS
337 EFIAPI
SmmRegisterExceptionHandler(IN EFI_SMM_CPU_SERVICE_PROTOCOL * This,IN EFI_EXCEPTION_TYPE ExceptionType,IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler)338 SmmRegisterExceptionHandler (
339     IN EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
340     IN EFI_EXCEPTION_TYPE            ExceptionType,
341     IN EFI_CPU_INTERRUPT_HANDLER     InterruptHandler
342     )
343 {
344   return RegisterCpuInterruptHandler (ExceptionType, InterruptHandler);
345 }
346 
347 /**
348   Initialize SMM CPU Services.
349 
350   It installs EFI SMM CPU Services Protocol.
351 
352   @param ImageHandle The firmware allocated handle for the EFI image.
353 
354   @retval EFI_SUCCESS    EFI SMM CPU Services Protocol was installed successfully.
355 **/
356 EFI_STATUS
InitializeSmmCpuServices(IN EFI_HANDLE Handle)357 InitializeSmmCpuServices (
358   IN EFI_HANDLE  Handle
359   )
360 {
361   EFI_STATUS Status;
362 
363   Status = gSmst->SmmInstallProtocolInterface (
364                     &Handle,
365                     &gEfiSmmCpuServiceProtocolGuid,
366                     EFI_NATIVE_INTERFACE,
367                     &mSmmCpuService
368                     );
369   ASSERT_EFI_ERROR (Status);
370   return Status;
371 }
372 
373