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