1 /** @file
2   SMI management.
3 
4   Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials are licensed and made available
6   under the terms and conditions of the BSD License which accompanies this
7   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 "PiSmmCore.h"
16 
17 //
18 // SMM_HANDLER - used for each SMM handler
19 //
20 
21 #define SMI_ENTRY_SIGNATURE  SIGNATURE_32('s','m','i','e')
22 
23  typedef struct {
24   UINTN       Signature;
25   LIST_ENTRY  AllEntries;  // All entries
26 
27   EFI_GUID    HandlerType; // Type of interrupt
28   LIST_ENTRY  SmiHandlers; // All handlers
29 } SMI_ENTRY;
30 
31 #define SMI_HANDLER_SIGNATURE  SIGNATURE_32('s','m','i','h')
32 
33  typedef struct {
34   UINTN                         Signature;
35   LIST_ENTRY                    Link;        // Link on SMI_ENTRY.SmiHandlers
36   EFI_SMM_HANDLER_ENTRY_POINT2  Handler;     // The smm handler's entry point
37   SMI_ENTRY                     *SmiEntry;
38 } SMI_HANDLER;
39 
40 LIST_ENTRY  mRootSmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiHandlerList);
41 LIST_ENTRY  mSmiEntryList       = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList);
42 
43 /**
44   Finds the SMI entry for the requested handler type.
45 
46   @param  HandlerType            The type of the interrupt
47   @param  Create                 Create a new entry if not found
48 
49   @return SMI entry
50 
51 **/
52 SMI_ENTRY  *
53 EFIAPI
SmmCoreFindSmiEntry(IN EFI_GUID * HandlerType,IN BOOLEAN Create)54 SmmCoreFindSmiEntry (
55   IN EFI_GUID  *HandlerType,
56   IN BOOLEAN   Create
57   )
58 {
59   LIST_ENTRY  *Link;
60   SMI_ENTRY   *Item;
61   SMI_ENTRY   *SmiEntry;
62 
63   //
64   // Search the SMI entry list for the matching GUID
65   //
66   SmiEntry = NULL;
67   for (Link = mSmiEntryList.ForwardLink;
68        Link != &mSmiEntryList;
69        Link = Link->ForwardLink) {
70 
71     Item = CR (Link, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE);
72     if (CompareGuid (&Item->HandlerType, HandlerType)) {
73       //
74       // This is the SMI entry
75       //
76       SmiEntry = Item;
77       break;
78     }
79   }
80 
81   //
82   // If the protocol entry was not found and Create is TRUE, then
83   // allocate a new entry
84   //
85   if ((SmiEntry == NULL) && Create) {
86     SmiEntry = AllocatePool (sizeof(SMI_ENTRY));
87     if (SmiEntry != NULL) {
88       //
89       // Initialize new SMI entry structure
90       //
91       SmiEntry->Signature = SMI_ENTRY_SIGNATURE;
92       CopyGuid ((VOID *)&SmiEntry->HandlerType, HandlerType);
93       InitializeListHead (&SmiEntry->SmiHandlers);
94 
95       //
96       // Add it to SMI entry list
97       //
98       InsertTailList (&mSmiEntryList, &SmiEntry->AllEntries);
99     }
100   }
101   return SmiEntry;
102 }
103 
104 /**
105   Manage SMI of a particular type.
106 
107   @param  HandlerType    Points to the handler type or NULL for root SMI handlers.
108   @param  Context        Points to an optional context buffer.
109   @param  CommBuffer     Points to the optional communication buffer.
110   @param  CommBufferSize Points to the size of the optional communication buffer.
111 
112   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING  Interrupt source was processed successfully but not quiesced.
113   @retval EFI_INTERRUPT_PENDING              One or more SMI sources could not be quiesced.
114   @retval EFI_NOT_FOUND                      Interrupt source was not handled or quiesced.
115   @retval EFI_SUCCESS                        Interrupt source was handled and quiesced.
116 
117 **/
118 EFI_STATUS
119 EFIAPI
SmiManage(IN CONST EFI_GUID * HandlerType,IN CONST VOID * Context OPTIONAL,IN OUT VOID * CommBuffer OPTIONAL,IN OUT UINTN * CommBufferSize OPTIONAL)120 SmiManage (
121   IN     CONST EFI_GUID  *HandlerType,
122   IN     CONST VOID      *Context         OPTIONAL,
123   IN OUT VOID            *CommBuffer      OPTIONAL,
124   IN OUT UINTN           *CommBufferSize  OPTIONAL
125   )
126 {
127   LIST_ENTRY   *Link;
128   LIST_ENTRY   *Head;
129   SMI_ENTRY    *SmiEntry;
130   SMI_HANDLER  *SmiHandler;
131   BOOLEAN      SuccessReturn;
132   EFI_STATUS   Status;
133 
134   Status = EFI_NOT_FOUND;
135   SuccessReturn = FALSE;
136   if (HandlerType == NULL) {
137     //
138     // Root SMI handler
139     //
140 
141     Head = &mRootSmiHandlerList;
142   } else {
143     //
144     // Non-root SMI handler
145     //
146     SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, FALSE);
147     if (SmiEntry == NULL) {
148       //
149       // There is no handler registered for this interrupt source
150       //
151       return Status;
152     }
153 
154     Head = &SmiEntry->SmiHandlers;
155   }
156 
157   for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
158     SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);
159 
160     Status = SmiHandler->Handler (
161                (EFI_HANDLE) SmiHandler,
162                Context,
163                CommBuffer,
164                CommBufferSize
165                );
166 
167     switch (Status) {
168     case EFI_INTERRUPT_PENDING:
169       //
170       // If a handler returns EFI_INTERRUPT_PENDING and HandlerType is not NULL then
171       // no additional handlers will be processed and EFI_INTERRUPT_PENDING will be returned.
172       //
173       if (HandlerType != NULL) {
174         return EFI_INTERRUPT_PENDING;
175       }
176       break;
177 
178     case EFI_SUCCESS:
179       //
180       // If at least one of the handlers returns EFI_SUCCESS then the function will return
181       // EFI_SUCCESS. If a handler returns EFI_SUCCESS and HandlerType is not NULL then no
182       // additional handlers will be processed.
183       //
184       if (HandlerType != NULL) {
185         return EFI_SUCCESS;
186       }
187       SuccessReturn = TRUE;
188       break;
189 
190     case EFI_WARN_INTERRUPT_SOURCE_QUIESCED:
191       //
192       // If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED
193       // then the function will return EFI_SUCCESS.
194       //
195       SuccessReturn = TRUE;
196       break;
197 
198     case EFI_WARN_INTERRUPT_SOURCE_PENDING:
199       //
200       // If all the handlers returned EFI_WARN_INTERRUPT_SOURCE_PENDING
201       // then EFI_WARN_INTERRUPT_SOURCE_PENDING will be returned.
202       //
203       break;
204 
205     default:
206       //
207       // Unexpected status code returned.
208       //
209       ASSERT (FALSE);
210       break;
211     }
212   }
213 
214   if (SuccessReturn) {
215     Status = EFI_SUCCESS;
216   }
217 
218   return Status;
219 }
220 
221 /**
222   Registers a handler to execute within SMM.
223 
224   @param  Handler        Handler service funtion pointer.
225   @param  HandlerType    Points to the handler type or NULL for root SMI handlers.
226   @param  DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function.
227 
228   @retval EFI_SUCCESS           Handler register success.
229   @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL.
230 
231 **/
232 EFI_STATUS
233 EFIAPI
SmiHandlerRegister(IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler,IN CONST EFI_GUID * HandlerType OPTIONAL,OUT EFI_HANDLE * DispatchHandle)234 SmiHandlerRegister (
235   IN  EFI_SMM_HANDLER_ENTRY_POINT2  Handler,
236   IN  CONST EFI_GUID                *HandlerType  OPTIONAL,
237   OUT EFI_HANDLE                    *DispatchHandle
238   )
239 {
240   SMI_HANDLER  *SmiHandler;
241   SMI_ENTRY    *SmiEntry;
242   LIST_ENTRY   *List;
243 
244   if (Handler == NULL || DispatchHandle == NULL) {
245     return EFI_INVALID_PARAMETER;
246   }
247 
248   SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER));
249   if (SmiHandler == NULL) {
250     return EFI_OUT_OF_RESOURCES;
251   }
252 
253   SmiHandler->Signature = SMI_HANDLER_SIGNATURE;
254   SmiHandler->Handler = Handler;
255 
256   if (HandlerType == NULL) {
257     //
258     // This is root SMI handler
259     //
260     SmiEntry = NULL;
261     List = &mRootSmiHandlerList;
262   } else {
263     //
264     // None root SMI handler
265     //
266     SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE);
267     if (SmiEntry == NULL) {
268       return EFI_OUT_OF_RESOURCES;
269     }
270 
271     List = &SmiEntry->SmiHandlers;
272   }
273 
274   SmiHandler->SmiEntry = SmiEntry;
275   InsertTailList (List, &SmiHandler->Link);
276 
277   *DispatchHandle = (EFI_HANDLE) SmiHandler;
278 
279   return EFI_SUCCESS;
280 }
281 
282 /**
283   Unregister a handler in SMM.
284 
285   @param  DispatchHandle  The handle that was specified when the handler was registered.
286 
287   @retval EFI_SUCCESS           Handler function was successfully unregistered.
288   @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle.
289 
290 **/
291 EFI_STATUS
292 EFIAPI
SmiHandlerUnRegister(IN EFI_HANDLE DispatchHandle)293 SmiHandlerUnRegister (
294   IN EFI_HANDLE  DispatchHandle
295   )
296 {
297   SMI_HANDLER  *SmiHandler;
298   SMI_ENTRY    *SmiEntry;
299 
300   SmiHandler = (SMI_HANDLER *) DispatchHandle;
301 
302   if (SmiHandler == NULL) {
303     return EFI_INVALID_PARAMETER;
304   }
305 
306   if (SmiHandler->Signature != SMI_HANDLER_SIGNATURE) {
307     return EFI_INVALID_PARAMETER;
308   }
309 
310   SmiEntry = SmiHandler->SmiEntry;
311 
312   RemoveEntryList (&SmiHandler->Link);
313   FreePool (SmiHandler);
314 
315   if (SmiEntry == NULL) {
316     //
317     // This is root SMI handler
318     //
319     return EFI_SUCCESS;
320   }
321 
322   if (IsListEmpty (&SmiEntry->SmiHandlers)) {
323     //
324     // No handler registered for this interrupt now, remove the SMI_ENTRY
325     //
326     RemoveEntryList (&SmiEntry->AllEntries);
327 
328     FreePool (SmiEntry);
329   }
330 
331   return EFI_SUCCESS;
332 }
333