1 /** @file
2   SMM Base Protocol on SMM Base2 Protocol Thunk driver.
3 
4   This driver co-operates with SMM Base Helper SMM driver to provide SMM Base Protocol
5   based on SMM Base2 Protocol.
6 
7   This thunk driver is expected to be loaded before PI SMM IPL driver so that
8   SMM BASE Protocol can be published immediately after SMM Base2 Protocol is installed to
9   make SMM Base Protocol.InSmm() as early as possible.
10 
11   Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
12   This program and the accompanying materials
13   are licensed and made available under the terms and conditions of the BSD License
14   which accompanies this distribution.  The full text of the license may be found at
15   http://opensource.org/licenses/bsd-license.php
16 
17   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
18   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 
20 **/
21 
22 #include <PiDxe.h>
23 #include <FrameworkSmm.h>
24 
25 #include <Protocol/SmmBase2.h>
26 #include <Protocol/SmmCommunication.h>
27 #include <Protocol/SmmBaseHelperReady.h>
28 
29 #include <Guid/SmmBaseThunkCommunication.h>
30 #include <Guid/EventGroup.h>
31 
32 #include <Library/DebugLib.h>
33 #include <Library/UefiBootServicesTableLib.h>
34 #include <Library/UefiDriverEntryPoint.h>
35 #include <Library/UefiLib.h>
36 #include <Library/UefiRuntimeLib.h>
37 
38 SMMBASETHUNK_COMMUNICATION_DATA  mCommunicationData = {
39   EFI_SMM_BASE_THUNK_COMMUNICATION_GUID,
40   sizeof (SMMBASE_FUNCTION_DATA)
41 };
42 
43 EFI_HANDLE                         mSmmBaseHandle = NULL;
44 EFI_SMM_BASE2_PROTOCOL             *mSmmBase2 = NULL;
45 EFI_SMM_COMMUNICATION_PROTOCOL     *mSmmCommunication = NULL;
46 EFI_SMM_BASE_HELPER_READY_PROTOCOL *mSmmBaseHelperReady = NULL;
47 BOOLEAN                            mAtRuntime = FALSE;
48 
49 /**
50   Determine if in SMM mode.
51 
52   @retval TRUE   In SMM mode.
53   @retval FALSE  Not in SMM mode.
54 **/
55 BOOLEAN
IsInSmm(VOID)56 IsInSmm (
57   VOID
58   )
59 {
60   EFI_STATUS Status;
61   BOOLEAN    InSmm;
62 
63   Status = mSmmBase2->InSmm (mSmmBase2, &InSmm);
64   ASSERT_EFI_ERROR (Status);
65   return InSmm;
66 }
67 
68 /**
69   Invoke services provided by SMM Base Helper SMM driver.
70 **/
71 VOID
SmmBaseHelperService(VOID)72 SmmBaseHelperService (
73   VOID
74   )
75 {
76   UINTN DataSize;
77 
78   mCommunicationData.FunctionData.Status = EFI_UNSUPPORTED;
79   mCommunicationData.FunctionData.SmmBaseImageHandle = mSmmBaseHandle;
80 
81   if ((mCommunicationData.FunctionData.Function != SmmBaseFunctionCommunicate) && IsInSmm()) {
82     ///
83     /// If in SMM mode, directly call services in SMM Base Helper.
84     ///
85     DataSize = (UINTN)(sizeof (SMMBASE_FUNCTION_DATA));
86     mSmmBaseHelperReady->ServiceEntry (
87                            NULL,
88                            NULL,
89                            &mCommunicationData.FunctionData,
90                            &DataSize
91                            );
92   } else {
93     ///
94     /// Call services in SMM Base Helper via SMM Communication Protocol.
95     ///
96     DataSize = (UINTN)(sizeof (mCommunicationData));
97     mSmmCommunication->Communicate (
98                          mSmmCommunication,
99                          &mCommunicationData,
100                          &DataSize
101                          );
102   }
103 }
104 
105 /**
106   Register a given driver into SMRAM. This is the equivalent of performing
107   the LoadImage/StartImage into System Management Mode.
108 
109   @param[in]   This                  Protocol instance pointer.
110   @param[in]   FilePath              Location of the image to be installed as the handler.
111   @param[in]   SourceBuffer          Optional source buffer in case the image file
112                                      is in memory.
113   @param[in]   SourceSize            Size of the source image file, if in memory.
114   @param[out]  ImageHandle           The handle that the base driver uses to decode
115                                      the handler. Unique among SMM handlers only,
116                                      not unique across DXE/EFI.
117   @param[in]   LegacyIA32Binary      An optional parameter specifying that the associated
118                                      file is a real-mode IA-32 binary.
119 
120   @retval      EFI_SUCCESS           The operation was successful.
121   @retval      EFI_OUT_OF_RESOURCES  There were no additional SMRAM resources to load the handler
122   @retval      EFI_UNSUPPORTED       This platform does not support 16-bit handlers.
123   @retval      EFI_UNSUPPORTED       Platform is in runtime.
124   @retval      EFI_INVALID_PARAMETER The handlers was not the correct image type
125 **/
126 EFI_STATUS
127 EFIAPI
SmmBaseRegister(IN EFI_SMM_BASE_PROTOCOL * This,IN EFI_DEVICE_PATH_PROTOCOL * FilePath,IN VOID * SourceBuffer,IN UINTN SourceSize,OUT EFI_HANDLE * ImageHandle,IN BOOLEAN LegacyIA32Binary)128 SmmBaseRegister (
129   IN      EFI_SMM_BASE_PROTOCOL     *This,
130   IN      EFI_DEVICE_PATH_PROTOCOL  *FilePath,
131   IN      VOID                      *SourceBuffer,
132   IN      UINTN                     SourceSize,
133   OUT     EFI_HANDLE                *ImageHandle,
134   IN      BOOLEAN                   LegacyIA32Binary
135   )
136 {
137   if (mAtRuntime || LegacyIA32Binary) {
138     return EFI_UNSUPPORTED;
139   }
140 
141   mCommunicationData.FunctionData.Function = SmmBaseFunctionRegister;
142   mCommunicationData.FunctionData.Args.Register.FilePath = FilePath;
143   mCommunicationData.FunctionData.Args.Register.SourceBuffer = SourceBuffer;
144   mCommunicationData.FunctionData.Args.Register.SourceSize = SourceSize;
145   mCommunicationData.FunctionData.Args.Register.ImageHandle = ImageHandle;
146   mCommunicationData.FunctionData.Args.Register.LegacyIA32Binary = LegacyIA32Binary;
147 
148   SmmBaseHelperService ();
149   return mCommunicationData.FunctionData.Status;
150 }
151 
152 /**
153   Removes a handler from execution within SMRAM.  This is the equivalent of performing
154   the UnloadImage in System Management Mode.
155 
156   @param[in]  This                  Protocol instance pointer.
157   @param[in]  ImageHandle           The handler to be removed.
158 
159   @retval     EFI_SUCCESS           The operation was successful
160   @retval     EFI_INVALID_PARAMETER The handler did not exist
161   @retval     EFI_UNSUPPORTED       Platform is in runtime.
162 **/
163 EFI_STATUS
164 EFIAPI
SmmBaseUnregister(IN EFI_SMM_BASE_PROTOCOL * This,IN EFI_HANDLE ImageHandle)165 SmmBaseUnregister (
166   IN      EFI_SMM_BASE_PROTOCOL     *This,
167   IN      EFI_HANDLE                ImageHandle
168   )
169 {
170   if (mAtRuntime) {
171     return EFI_UNSUPPORTED;
172   }
173 
174   mCommunicationData.FunctionData.Function = SmmBaseFunctionUnregister;
175   mCommunicationData.FunctionData.Args.UnRegister.ImageHandle = ImageHandle;
176 
177   SmmBaseHelperService ();
178   return mCommunicationData.FunctionData.Status;
179 }
180 
181 /**
182   The SMM Inter-module Communicate Service Communicate() function
183   provides a service to send/receive messages from a registered
184   EFI service.  The BASE protocol driver is responsible for doing
185   any of the copies such that the data lives in boot-service-accessible RAM.
186 
187   @param[in]      This                  Protocol instance pointer.
188   @param[in]      ImageHandle           The handle of the registered driver.
189   @param[in, out]  CommunicationBuffer   Pointer to the buffer to convey into SMRAM.
190   @param[in, out]  BufferSize            The size of the data buffer being passed in.
191                                         On exit, the size of data being returned.
192                                         Zero if the handler does not wish to reply with any data.
193 
194   @retval         EFI_SUCCESS           The message was successfully posted
195   @retval         EFI_INVALID_PARAMETER The buffer was NULL
196 **/
197 EFI_STATUS
198 EFIAPI
SmmBaseCommunicate(IN EFI_SMM_BASE_PROTOCOL * This,IN EFI_HANDLE ImageHandle,IN OUT VOID * CommunicationBuffer,IN OUT UINTN * BufferSize)199 SmmBaseCommunicate (
200   IN      EFI_SMM_BASE_PROTOCOL     *This,
201   IN      EFI_HANDLE                ImageHandle,
202   IN OUT  VOID                      *CommunicationBuffer,
203   IN OUT  UINTN                     *BufferSize
204   )
205 {
206   ///
207   /// Note this is a runtime interface
208   ///
209 
210   if (CommunicationBuffer == NULL || BufferSize == NULL) {
211     return EFI_INVALID_PARAMETER;
212   }
213 
214   mCommunicationData.FunctionData.Function = SmmBaseFunctionCommunicate;
215   mCommunicationData.FunctionData.Args.Communicate.ImageHandle = ImageHandle;
216   mCommunicationData.FunctionData.Args.Communicate.CommunicationBuffer = CommunicationBuffer;
217   mCommunicationData.FunctionData.Args.Communicate.SourceSize = BufferSize;
218 
219   SmmBaseHelperService ();
220   return mCommunicationData.FunctionData.Status;
221 }
222 
223 /**
224   Register a callback to execute within SMM.
225   This allows receipt of messages created with EFI_SMM_BASE_PROTOCOL.Communicate().
226 
227   @param[in]  This                  Protocol instance pointer.
228   @param[in]  SmmImageHandle        Handle of the callback service.
229   @param[in]  CallbackAddress       Address of the callback service.
230   @param[in]  MakeLast              If present, will stipulate that the handler is posted to
231                                     be executed last in the dispatch table.
232   @param[in]  FloatingPointSave     An optional parameter that informs the
233                                     EFI_SMM_ACCESS_PROTOCOL Driver core if it needs to save
234                                     the floating point register state. If any handler
235                                     require this, the state will be saved for all handlers.
236 
237   @retval     EFI_SUCCESS           The operation was successful
238   @retval     EFI_OUT_OF_RESOURCES  Not enough space in the dispatch queue
239   @retval     EFI_UNSUPPORTED       Platform is in runtime.
240   @retval     EFI_UNSUPPORTED       The caller is not in SMM.
241 **/
242 EFI_STATUS
243 EFIAPI
SmmBaseRegisterCallback(IN EFI_SMM_BASE_PROTOCOL * This,IN EFI_HANDLE SmmImageHandle,IN EFI_SMM_CALLBACK_ENTRY_POINT CallbackAddress,IN BOOLEAN MakeLast,IN BOOLEAN FloatingPointSave)244 SmmBaseRegisterCallback (
245   IN      EFI_SMM_BASE_PROTOCOL         *This,
246   IN      EFI_HANDLE                    SmmImageHandle,
247   IN      EFI_SMM_CALLBACK_ENTRY_POINT  CallbackAddress,
248   IN      BOOLEAN                       MakeLast,
249   IN      BOOLEAN                       FloatingPointSave
250   )
251 {
252   if (!IsInSmm()) {
253     return EFI_UNSUPPORTED;
254   }
255 
256   mCommunicationData.FunctionData.Function = SmmBaseFunctionRegisterCallback;
257   mCommunicationData.FunctionData.Args.RegisterCallback.SmmImageHandle = SmmImageHandle;
258   mCommunicationData.FunctionData.Args.RegisterCallback.CallbackAddress = CallbackAddress;
259   mCommunicationData.FunctionData.Args.RegisterCallback.MakeLast = MakeLast;
260   mCommunicationData.FunctionData.Args.RegisterCallback.FloatingPointSave = FloatingPointSave;
261 
262   SmmBaseHelperService();
263   return mCommunicationData.FunctionData.Status;
264 }
265 
266 /**
267   This routine tells caller if execution context is SMM or not.
268 
269   @param[in]   This                   Protocol instance pointer.
270   @param[out]  InSmm                  Whether the caller is inside SMM for IA-32
271                                       or servicing a PMI for the Itanium processor
272                                       family.
273 
274   @retval      EFI_SUCCESS            The operation was successful
275   @retval      EFI_INVALID_PARAMETER  InSmm was NULL.
276 **/
277 EFI_STATUS
278 EFIAPI
SmmBaseInSmm(IN EFI_SMM_BASE_PROTOCOL * This,OUT BOOLEAN * InSmm)279 SmmBaseInSmm (
280   IN      EFI_SMM_BASE_PROTOCOL     *This,
281   OUT     BOOLEAN                   *InSmm
282   )
283 {
284   return mSmmBase2->InSmm (mSmmBase2, InSmm);
285 }
286 
287 /**
288   The SmmAllocatePool() function allocates a memory region of Size bytes from memory of
289   type PoolType and returns the address of the allocated memory in the location referenced
290   by Buffer.  This function allocates pages from EFI SMRAM Memory as needed to grow the
291   requested pool type.  All allocations are eight-byte aligned.
292 
293   @param[in]   This                  Protocol instance pointer.
294   @param[in]   PoolType              The type of pool to allocate.
295                                      The only supported type is EfiRuntimeServicesData;
296                                      the interface will internally map this runtime request to
297                                      SMRAM for IA-32 and leave as this type for the Itanium
298                                      processor family. Other types can be ignored.
299   @param[in]   Size                  The number of bytes to allocate from the pool.
300   @param[out]  Buffer                A pointer to a pointer to the allocated buffer if the call
301                                      succeeds; undefined otherwise.
302 
303   @retval      EFI_SUCCESS           The requested number of bytes was allocated.
304   @retval      EFI_OUT_OF_RESOURCES  The pool requested could not be allocated.
305   @retval      EFI_UNSUPPORTED       Platform is in runtime.
306 **/
307 EFI_STATUS
308 EFIAPI
SmmBaseSmmAllocatePool(IN EFI_SMM_BASE_PROTOCOL * This,IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)309 SmmBaseSmmAllocatePool (
310   IN      EFI_SMM_BASE_PROTOCOL     *This,
311   IN      EFI_MEMORY_TYPE           PoolType,
312   IN      UINTN                     Size,
313   OUT     VOID                      **Buffer
314   )
315 {
316   if (mAtRuntime) {
317     return EFI_UNSUPPORTED;
318   }
319 
320   mCommunicationData.FunctionData.Function = SmmBaseFunctionAllocatePool;
321   mCommunicationData.FunctionData.Args.AllocatePool.PoolType = PoolType;
322   mCommunicationData.FunctionData.Args.AllocatePool.Size = Size;
323   mCommunicationData.FunctionData.Args.AllocatePool.Buffer = Buffer;
324 
325   SmmBaseHelperService ();
326   return mCommunicationData.FunctionData.Status;
327 }
328 
329 /**
330   The SmmFreePool() function returns the memory specified by Buffer to the system.
331   On return, the memory's type is EFI SMRAM Memory.  The Buffer that is freed must
332   have been allocated by SmmAllocatePool().
333 
334   @param[in]  This                  Protocol instance pointer.
335   @param[in]  Buffer                Pointer to the buffer allocation.
336 
337   @retval     EFI_SUCCESS           The memory was returned to the system.
338   @retval     EFI_INVALID_PARAMETER Buffer was invalid.
339   @retval     EFI_UNSUPPORTED       Platform is in runtime.
340 **/
341 EFI_STATUS
342 EFIAPI
SmmBaseSmmFreePool(IN EFI_SMM_BASE_PROTOCOL * This,IN VOID * Buffer)343 SmmBaseSmmFreePool (
344   IN      EFI_SMM_BASE_PROTOCOL     *This,
345   IN      VOID                      *Buffer
346   )
347 {
348   if (mAtRuntime) {
349     return EFI_UNSUPPORTED;
350   }
351 
352   mCommunicationData.FunctionData.Function = SmmBaseFunctionFreePool;
353   mCommunicationData.FunctionData.Args.FreePool.Buffer = Buffer;
354 
355   SmmBaseHelperService ();
356   return mCommunicationData.FunctionData.Status;
357 }
358 
359 /**
360   The GetSmstLocation() function returns the location of the System Management
361   Service Table.  The use of the API is such that a driver can discover the
362   location of the SMST in its entry point and then cache it in some driver
363   global variable so that the SMST can be invoked in subsequent callbacks.
364 
365   @param[in]  This                  Protocol instance pointer.
366   @param[out] Smst                  Pointer to the SMST.
367 
368   @retval     EFI_SUCCESS           The operation was successful
369   @retval     EFI_INVALID_PARAMETER Smst was invalid.
370   @retval     EFI_UNSUPPORTED       Not in SMM.
371 **/
372 EFI_STATUS
373 EFIAPI
SmmBaseGetSmstLocation(IN EFI_SMM_BASE_PROTOCOL * This,OUT EFI_SMM_SYSTEM_TABLE ** Smst)374 SmmBaseGetSmstLocation (
375   IN      EFI_SMM_BASE_PROTOCOL     *This,
376   OUT     EFI_SMM_SYSTEM_TABLE      **Smst
377   )
378 {
379   if (!IsInSmm ()) {
380     return EFI_UNSUPPORTED;
381   }
382 
383   if (Smst == NULL) {
384     return EFI_INVALID_PARAMETER;
385   }
386 
387   *Smst = mSmmBaseHelperReady->FrameworkSmst;
388   return EFI_SUCCESS;
389 }
390 
391 /**
392   Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
393 
394   This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
395   It convers pointer to new virtual address.
396 
397   @param  Event        Event whose notification function is being invoked
398   @param  Context      Pointer to the notification function's context
399 **/
400 VOID
401 EFIAPI
SmmBaseAddressChangeEvent(IN EFI_EVENT Event,IN VOID * Context)402 SmmBaseAddressChangeEvent (
403   IN EFI_EVENT        Event,
404   IN VOID             *Context
405   )
406 {
407   EfiConvertPointer (0x0, (VOID **) &mSmmCommunication);
408 }
409 
410 ///
411 /// SMM Base Protocol instance
412 ///
413 EFI_SMM_BASE_PROTOCOL  mSmmBase = {
414   SmmBaseRegister,
415   SmmBaseUnregister,
416   SmmBaseCommunicate,
417   SmmBaseRegisterCallback,
418   SmmBaseInSmm,
419   SmmBaseSmmAllocatePool,
420   SmmBaseSmmFreePool,
421   SmmBaseGetSmstLocation
422 };
423 
424 /**
425   Notification function on Exit Boot Services Event.
426 
427   This function sets a flag indicating it is in Runtime phase.
428 
429   @param  Event        Event whose notification function is being invoked
430   @param  Context      Pointer to the notification function's context
431 **/
432 VOID
433 EFIAPI
SmmBaseExitBootServicesEventNotify(IN EFI_EVENT Event,IN VOID * Context)434 SmmBaseExitBootServicesEventNotify (
435   IN EFI_EVENT  Event,
436   IN VOID       *Context
437   )
438 {
439   mAtRuntime = TRUE;
440 }
441 
442 /**
443   Entry Point for SMM Base Protocol on SMM Base2 Protocol Thunk driver.
444 
445   @param[in] ImageHandle  Image handle of this driver.
446   @param[in] SystemTable  A Pointer to the EFI System Table.
447 
448   @retval EFI_SUCCESS  The entry point is executed successfully.
449 **/
450 EFI_STATUS
451 EFIAPI
SmmBaseThunkMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)452 SmmBaseThunkMain (
453   IN EFI_HANDLE        ImageHandle,
454   IN EFI_SYSTEM_TABLE  *SystemTable
455   )
456 {
457   EFI_STATUS  Status;
458   EFI_EVENT   Event;
459 
460   mSmmBaseHandle = ImageHandle;
461 
462   //
463   // Assume only one instance of SMM Base2 Protocol in the system
464   // Locate SMM Base2 Protocol
465   //
466   Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **) &mSmmBase2);
467   ASSERT_EFI_ERROR (Status);
468 
469   //
470   // Assume only one instance of SMM Communication Protocol in the system
471   // Locate SMM Communication Protocol
472   //
473   Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication);
474   ASSERT_EFI_ERROR (Status);
475 
476   //
477   // Assume only one instance of SMM Base Helper Ready Protocol in the system
478   // Locate SMM Base Helper Ready Protocol
479   //
480   Status = gBS->LocateProtocol (&gEfiSmmBaseHelperReadyProtocolGuid, NULL, (VOID **) &mSmmBaseHelperReady);
481   ASSERT_EFI_ERROR (Status);
482 
483   //
484   // Create event notification on Exit Boot Services event.
485   //
486   Status = gBS->CreateEventEx (
487                   EVT_NOTIFY_SIGNAL,
488                   TPL_NOTIFY,
489                   SmmBaseExitBootServicesEventNotify,
490                   NULL,
491                   &gEfiEventExitBootServicesGuid,
492                   &Event
493                   );
494   ASSERT_EFI_ERROR (Status);
495 
496   //
497   // Create event on SetVirtualAddressMap() to convert mSmmCommunication from a physical address to a virtual address
498   //
499   Status = gBS->CreateEventEx (
500                   EVT_NOTIFY_SIGNAL,
501                   TPL_NOTIFY,
502                   SmmBaseAddressChangeEvent,
503                   NULL,
504                   &gEfiEventVirtualAddressChangeGuid,
505                   &Event
506                   );
507   ASSERT_EFI_ERROR (Status);
508 
509   //
510   // Publish Framework SMM BASE Protocol immediately after SMM Base2 Protocol is installed to
511   // make SMM Base Protocol.InSmm() available as early as possible.
512   //
513   Status = gBS->InstallMultipleProtocolInterfaces (
514                   &mSmmBaseHandle,
515                   &gEfiSmmBaseProtocolGuid, &mSmmBase,
516                   NULL
517                   );
518   ASSERT_EFI_ERROR (Status);
519 
520   return EFI_SUCCESS;
521 }
522