1 /** @file
2   SMM SwDispatch2 Protocol on SMM SwDispatch Protocol Thunk driver.
3 
4   Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
5 
6 
7   This program and the accompanying materials are licensed and made available under
8 
9   the terms and conditions of the BSD License that accompanies this distribution.
10 
11   The full text of the license may be found at
12 
13   http://opensource.org/licenses/bsd-license.php.
14 
15 
16 
17   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
18 
19   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 
21 
22 
23 
24 **/
25 
26 #include <PiDxe.h>
27 #include <FrameworkSmm.h>
28 
29 #include <Protocol/SmmSwDispatch2.h>
30 #include <Protocol/SmmSwDispatch.h>
31 #include <Protocol/SmmControl.h>
32 #include <Protocol/SmmCpu.h>
33 
34 #include <Library/UefiBootServicesTableLib.h>
35 #include <Library/UefiDriverEntryPoint.h>
36 #include <Library/SmmServicesTableLib.h>
37 #include <Library/BaseLib.h>
38 #include <Library/IoLib.h>
39 #include <Library/DebugLib.h>
40 
41 typedef struct {
42   LIST_ENTRY                     Link;
43   EFI_HANDLE                     DispatchHandle;
44   UINTN                          SwSmiInputValue;
45   UINTN                          DispatchFunction;
46 } EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT;
47 
48 /**
49   Register a child SMI source dispatch function for the specified software SMI.
50 
51   This service registers a function (DispatchFunction) which will be called when the software
52   SMI source specified by RegisterContext->SwSmiCpuIndex is detected. On return,
53   DispatchHandle contains a unique handle which may be used later to unregister the function
54   using UnRegister().
55 
56   @param[in]  This                  Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
57   @param[in]  DispatchFunction      Function to register for handler when the specified software
58                                     SMI is generated.
59   @param[in, out]  RegisterContext  Pointer to the dispatch function's context.
60                                     The caller fills this context in before calling
61                                     the register function to indicate to the register
62                                     function which Software SMI input value the
63                                     dispatch function should be invoked for.
64   @param[out] DispatchHandle        Handle generated by the dispatcher to track the
65                                     function instance.
66 
67   @retval EFI_SUCCESS            The dispatch function has been successfully
68                                  registered and the SMI source has been enabled.
69   @retval EFI_DEVICE_ERROR       The SW driver was unable to enable the SMI source.
70   @retval EFI_INVALID_PARAMETER  RegisterContext is invalid. The SW SMI input value
71                                  is not within valid range.
72   @retval EFI_OUT_OF_RESOURCES   There is not enough memory (system or SMM) to manage this
73                                  child.
74   @retval EFI_OUT_OF_RESOURCES   A unique software SMI value could not be assigned
75                                  for this dispatch.
76 **/
77 EFI_STATUS
78 EFIAPI
79 SmmSwDispatch2Register (
80   IN  CONST EFI_SMM_SW_DISPATCH2_PROTOCOL  *This,
81   IN        EFI_SMM_HANDLER_ENTRY_POINT2   DispatchFunction,
82   IN  OUT   EFI_SMM_SW_REGISTER_CONTEXT    *RegisterContext,
83   OUT       EFI_HANDLE                     *DispatchHandle
84   );
85 
86 /**
87   Unregister a child SMI source dispatch function for the specified software SMI.
88 
89   This service removes the handler associated with DispatchHandle so that it will no longer be
90   called in response to a software SMI.
91 
92   @param[in] This                Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
93   @param[in] DispatchHandle      Handle of dispatch function to deregister.
94 
95   @retval EFI_SUCCESS            The dispatch function has been successfully unregistered.
96   @retval EFI_INVALID_PARAMETER  The DispatchHandle was not valid.
97 **/
98 EFI_STATUS
99 EFIAPI
100 SmmSwDispatch2UnRegister (
101   IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL  *This,
102   IN       EFI_HANDLE                     DispatchHandle
103   );
104 
105 EFI_SMM_SW_DISPATCH2_PROTOCOL gSmmSwDispatch2 = {
106   SmmSwDispatch2Register,
107   SmmSwDispatch2UnRegister,
108   0 // MaximumSwiValue
109 };
110 
111 EFI_SMM_SW_DISPATCH_PROTOCOL  *mSmmSwDispatch;
112 UINT8                         mSmiTriggerRegister;
113 UINT8                         mSmiDataRegister;
114 
115 EFI_SMM_CPU_PROTOCOL          *mSmmCpuProtocol;
116 LIST_ENTRY                    mSmmSwDispatch2ThunkQueue = INITIALIZE_LIST_HEAD_VARIABLE (mSmmSwDispatch2ThunkQueue);
FindSmmSwDispatch2ContextBySwSmiInputValue(IN UINTN SwSmiInputValue)117 
118 /**
119   This function find SmmSwDispatch2Context by SwSmiInputValue.
120 
121   @param SwSmiInputValue The SwSmiInputValue to indentify the SmmSwDispatch2 context
122 
123   @return SmmSwDispatch2 context
124 **/
125 EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT *
126 FindSmmSwDispatch2ContextBySwSmiInputValue (
127   IN UINTN   SwSmiInputValue
128   )
129 {
130   LIST_ENTRY                            *Link;
131   EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT    *ThunkContext;
132 
133   for (Link = mSmmSwDispatch2ThunkQueue.ForwardLink;
134     Link != &mSmmSwDispatch2ThunkQueue;
135     Link = Link->ForwardLink) {
136     ThunkContext = BASE_CR (
137                      Link,
138                      EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT,
139                      Link
140                      );
141     if (ThunkContext->SwSmiInputValue == SwSmiInputValue) {
142       return ThunkContext;
143     }
144   }
145   return NULL;
146 }
FindSmmSwDispatch2ContextByDispatchHandle(IN EFI_HANDLE DispatchHandle)147 
148 /**
149   This function find SmmSwDispatch2Context by DispatchHandle.
150 
151   @param DispatchHandle The DispatchHandle to indentify the SmmSwDispatch2Thunk context
152 
153   @return SmmSwDispatch2Thunk context
154 **/
155 EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT *
156 FindSmmSwDispatch2ContextByDispatchHandle (
157   IN EFI_HANDLE   DispatchHandle
158   )
159 {
160   LIST_ENTRY                            *Link;
161   EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT    *ThunkContext;
162 
163   for (Link = mSmmSwDispatch2ThunkQueue.ForwardLink;
164        Link != &mSmmSwDispatch2ThunkQueue;
165        Link = Link->ForwardLink) {
166     ThunkContext = BASE_CR (
167                      Link,
168                      EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT,
169                      Link
170                      );
171     if (ThunkContext->DispatchHandle == DispatchHandle) {
172       return ThunkContext;
173     }
174   }
175   return NULL;
176 }
177 
178 /**
179   Framework dispatch function for a Software SMI handler.
180 
181   @param  DispatchHandle        The handle of this dispatch function.
182   @param  DispatchContext       The pointer to the dispatch function's context.
183                                 The SwSmiInputValue field is filled in
184                                 by the software dispatch driver prior to
FrameworkDispatchFunction(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SW_DISPATCH_CONTEXT * DispatchContext)185                                 invoking this dispatch function.
186                                 The dispatch function will only be called
187                                 for input values for which it is registered.
188 
189   @return None
190 
191 **/
192 VOID
193 EFIAPI
194 FrameworkDispatchFunction (
195   IN  EFI_HANDLE                    DispatchHandle,
196   IN  EFI_SMM_SW_DISPATCH_CONTEXT   *DispatchContext
197   )
198 {
199   EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT    *ThunkContext;
200   EFI_SMM_HANDLER_ENTRY_POINT2          DispatchFunction;
201   EFI_SMM_SW_REGISTER_CONTEXT           RegisterContext;
202   EFI_SMM_SW_CONTEXT                    SwContext;
203   UINTN                                 Size;
204   UINTN                                 Index;
205   EFI_SMM_SAVE_STATE_IO_INFO            IoInfo;
206   EFI_STATUS                            Status;
207 
208   //
209   // Search context
210   //
211   ThunkContext = FindSmmSwDispatch2ContextBySwSmiInputValue (DispatchContext->SwSmiInputValue);
212   ASSERT (ThunkContext != NULL);
213   if (ThunkContext == NULL) {
214     return ;
215   }
216 
217   //
218   // Construct new context
219   //
220   RegisterContext.SwSmiInputValue = DispatchContext->SwSmiInputValue;
221   Size = sizeof(SwContext);
222   SwContext.CommandPort = IoRead8 (mSmiTriggerRegister);
223   SwContext.DataPort    = IoRead8 (mSmiDataRegister);
224 
225   //
226   // Try to find which CPU trigger SWSMI
227   //
228   SwContext.SwSmiCpuIndex = 0;
229   for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
230     Status = mSmmCpuProtocol->ReadSaveState (
231                                 mSmmCpuProtocol,
232                                 sizeof(IoInfo),
233                                 EFI_SMM_SAVE_STATE_REGISTER_IO,
234                                 Index,
235                                 &IoInfo
236                                 );
237     if (EFI_ERROR (Status)) {
238       continue;
239     }
240     if (IoInfo.IoPort == mSmiTriggerRegister) {
241       //
242       // Great! Find it.
243       //
244       SwContext.SwSmiCpuIndex = Index;
245       break;
246     }
247   }
248 
249   //
250   // Dispatch
251   //
252   DispatchFunction = (EFI_SMM_HANDLER_ENTRY_POINT2)ThunkContext->DispatchFunction;
253   DispatchFunction (
254     DispatchHandle,
255     &RegisterContext,
256     &SwContext,
257     &Size
258     );
259   return ;
260 }
261 
262 /**
263   Register a child SMI source dispatch function for the specified software SMI.
264 
265   This service registers a function (DispatchFunction) which will be called when the software
266   SMI source specified by RegisterContext->SwSmiCpuIndex is detected. On return,
267   DispatchHandle contains a unique handle which may be used later to unregister the function
268   using UnRegister().
269 
270   @param[in]  This                  Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
271   @param[in]  DispatchFunction      Function to register for handler when the specified software
272                                     SMI is generated.
273   @param[in, out]  RegisterContext  Pointer to the dispatch function's context.
274                                     The caller fills this context in before calling
275                                     the register function to indicate to the register
276                                     function which Software SMI input value the
277                                     dispatch function should be invoked for.
278   @param[out] DispatchHandle        Handle generated by the dispatcher to track the
279                                     function instance.
280 
281   @retval EFI_SUCCESS            The dispatch function has been successfully
282                                  registered and the SMI source has been enabled.
283   @retval EFI_DEVICE_ERROR       The SW driver was unable to enable the SMI source.
SmmSwDispatch2Register(IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL * This,IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,IN OUT EFI_SMM_SW_REGISTER_CONTEXT * RegisterContext,OUT EFI_HANDLE * DispatchHandle)284   @retval EFI_INVALID_PARAMETER  RegisterContext is invalid. The SW SMI input value
285                                  is not within valid range.
286   @retval EFI_OUT_OF_RESOURCES   There is not enough memory (system or SMM) to manage this
287                                  child.
288   @retval EFI_OUT_OF_RESOURCES   A unique software SMI value could not be assigned
289                                  for this dispatch.
290 **/
291 EFI_STATUS
292 EFIAPI
293 SmmSwDispatch2Register (
294   IN  CONST EFI_SMM_SW_DISPATCH2_PROTOCOL  *This,
295   IN        EFI_SMM_HANDLER_ENTRY_POINT2   DispatchFunction,
296   IN  OUT   EFI_SMM_SW_REGISTER_CONTEXT    *RegisterContext,
297   OUT       EFI_HANDLE                     *DispatchHandle
298   )
299 {
300   EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT    *ThunkContext;
301   EFI_SMM_SW_DISPATCH_CONTEXT           DispatchContext;
302   EFI_STATUS                            Status;
303   UINTN                                 Index;
304 
305   if (RegisterContext->SwSmiInputValue == (UINTN)-1) {
306     //
307     // If SwSmiInputValue is set to (UINTN) -1 then a unique value will be assigned and returned in the structure.
308     //
309     Status = EFI_NOT_FOUND;
310     for (Index = 1; Index < gSmmSwDispatch2.MaximumSwiValue; Index++) {
311       DispatchContext.SwSmiInputValue = Index;
312       Status = mSmmSwDispatch->Register (
313                                  mSmmSwDispatch,
314                                  FrameworkDispatchFunction,
315                                  &DispatchContext,
316                                  DispatchHandle
317                                  );
318       if (!EFI_ERROR (Status)) {
319         RegisterContext->SwSmiInputValue = Index;
320         break;
321       }
322     }
323     if (RegisterContext->SwSmiInputValue == (UINTN)-1) {
324       return EFI_OUT_OF_RESOURCES;
325     }
326   } else {
327     DispatchContext.SwSmiInputValue = RegisterContext->SwSmiInputValue;
328     Status = mSmmSwDispatch->Register (
329                                mSmmSwDispatch,
330                                FrameworkDispatchFunction,
331                                &DispatchContext,
332                                DispatchHandle
333                                );
334   }
335   if (!EFI_ERROR (Status)) {
336     //
337     // Register
338     //
339     Status = gSmst->SmmAllocatePool (
340                       EfiRuntimeServicesData,
341                       sizeof(*ThunkContext),
342                       (VOID **)&ThunkContext
343                       );
344     ASSERT_EFI_ERROR (Status);
345     if (EFI_ERROR (Status)) {
346       mSmmSwDispatch->UnRegister (mSmmSwDispatch, *DispatchHandle);
347       return EFI_OUT_OF_RESOURCES;
348     }
349 
350     ThunkContext->SwSmiInputValue  = RegisterContext->SwSmiInputValue;
351     ThunkContext->DispatchFunction = (UINTN)DispatchFunction;
352     ThunkContext->DispatchHandle   = *DispatchHandle;
353     InsertTailList (&mSmmSwDispatch2ThunkQueue, &ThunkContext->Link);
354   }
355 
356   return Status;
357 }
358 
359 /**
360   Unregister a child SMI source dispatch function for the specified software SMI.
361 
362   This service removes the handler associated with DispatchHandle so that it will no longer be
363   called in response to a software SMI.
SmmSwDispatch2UnRegister(IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL * This,IN EFI_HANDLE DispatchHandle)364 
365   @param[in] This                Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
366   @param[in] DispatchHandle      Handle of dispatch function to deregister.
367 
368   @retval EFI_SUCCESS            The dispatch function has been successfully unregistered.
369   @retval EFI_INVALID_PARAMETER  The DispatchHandle was not valid.
370 **/
371 EFI_STATUS
372 EFIAPI
373 SmmSwDispatch2UnRegister (
374   IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL  *This,
375   IN       EFI_HANDLE                     DispatchHandle
376   )
377 {
378   EFI_SMM_SW_DISPATCH2_THUNK_CONTEXT    *ThunkContext;
379   EFI_STATUS                            Status;
380 
381   Status = mSmmSwDispatch->UnRegister (mSmmSwDispatch, DispatchHandle);
382   if (!EFI_ERROR (Status)) {
383     //
384     // Unregister
385     //
386     ThunkContext = FindSmmSwDispatch2ContextByDispatchHandle (DispatchHandle);
387     ASSERT (ThunkContext != NULL);
388     if (ThunkContext != NULL) {
389       RemoveEntryList (&ThunkContext->Link);
390       gSmst->SmmFreePool (ThunkContext);
391     }
392   }
393 
394   return Status;
395 }
396 
397 /**
398   Entry Point for this thunk driver.
SmmSwDispatch2ThunkMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)399 
400   @param[in] ImageHandle  Image handle of this driver.
401   @param[in] SystemTable  A Pointer to the EFI System Table.
402 
403   @retval EFI_SUCCESS  The entry point is executed successfully.
404   @retval other        Some error occurred when executing this entry point.
405 **/
406 EFI_STATUS
407 EFIAPI
408 SmmSwDispatch2ThunkMain (
409   IN EFI_HANDLE        ImageHandle,
410   IN EFI_SYSTEM_TABLE  *SystemTable
411   )
412 {
413   EFI_STATUS               Status;
414   EFI_SMM_CONTROL_PROTOCOL *SmmControl;
415   EFI_SMM_CONTROL_REGISTER RegisterInfo;
416 
417   //
418   // Locate Framework SMM SwDispatch Protocol
419   //
420   Status = gBS->LocateProtocol (
421                   &gEfiSmmSwDispatchProtocolGuid,
422                   NULL,
423                   (VOID **)&mSmmSwDispatch
424                   );
425   ASSERT_EFI_ERROR (Status);
426   gSmmSwDispatch2.MaximumSwiValue = mSmmSwDispatch->MaximumSwiValue;
427   if (gSmmSwDispatch2.MaximumSwiValue == 0x0) {
428     DEBUG ((EFI_D_ERROR, "BUGBUG: MaximumSwiValue is 0, work-around to make it 0xFF\n"));
429     gSmmSwDispatch2.MaximumSwiValue = 0xFF;
430   }
431 
432   //
433   // Locate Framework SMM Control Protocol
434   //
435   Status = gBS->LocateProtocol (
436                   &gEfiSmmControlProtocolGuid,
437                   NULL,
438                   (VOID **)&SmmControl
439                   );
440 
441   ASSERT_EFI_ERROR (Status);
442   Status = SmmControl->GetRegisterInfo (
443                          SmmControl,
444                          &RegisterInfo
445                          );
446   ASSERT_EFI_ERROR (Status);
447   mSmiTriggerRegister = RegisterInfo.SmiTriggerRegister;
448   mSmiDataRegister    = RegisterInfo.SmiDataRegister;
449 
450   //
451   // Locate PI SMM CPU protocol
452   //
453   Status = gSmst->SmmLocateProtocol (
454                     &gEfiSmmCpuProtocolGuid,
455                     NULL,
456                     (VOID **)&mSmmCpuProtocol
457                     );
458   ASSERT_EFI_ERROR (Status);
459 
460   //
461   // Publish PI SMM SwDispatch2 Protocol
462   //
463   ImageHandle = NULL;
464   Status = gSmst->SmmInstallProtocolInterface (
465                     &ImageHandle,
466                     &gEfiSmmSwDispatch2ProtocolGuid,
467                     EFI_NATIVE_INTERFACE,
468                     &gSmmSwDispatch2
469                     );
470   ASSERT_EFI_ERROR (Status);
471   return Status;
472 }
473 
474