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