]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Smi.c
Update SmiManager() to invoke all root SMI handlers to following PI Spec.
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Smi.c
1 /** @file
2 SMI management.
3
4 Copyright (c) 2009 - 2012, 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
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
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 InterruptQuiesced;
132 EFI_STATUS Status;
133
134 Status = EFI_NOT_FOUND;
135 InterruptQuiesced = 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 then no additional handlers
171 // 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 a handler returns EFI_SUCCESS then no additional handlers will be processed.
181 // then the function will return EFI_SUCCESS.
182 //
183 if (HandlerType != NULL) {
184 return EFI_SUCCESS;
185 }
186 break;
187
188 case EFI_WARN_INTERRUPT_SOURCE_QUIESCED:
189 //
190 // If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED
191 // then the function will return EFI_SUCCESS.
192 //
193 InterruptQuiesced = TRUE;
194 break;
195
196 case EFI_WARN_INTERRUPT_SOURCE_PENDING:
197 //
198 // If all the handlers returned EFI_WARN_INTERRUPT_SOURCE_PENDING
199 // then EFI_WARN_INTERRUPT_SOURCE_PENDING will be returned.
200 //
201 break;
202
203 default:
204 //
205 // Unexpected status code returned.
206 //
207 ASSERT (FALSE);
208 break;
209 }
210 }
211
212 if (InterruptQuiesced) {
213 Status = EFI_SUCCESS;
214 }
215
216 return Status;
217 }
218
219 /**
220 Registers a handler to execute within SMM.
221
222 @param Handler Handler service funtion pointer.
223 @param HandlerType Points to the handler type or NULL for root SMI handlers.
224 @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function.
225
226 @retval EFI_SUCCESS Handler register success.
227 @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL.
228
229 **/
230 EFI_STATUS
231 EFIAPI
232 SmiHandlerRegister (
233 IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler,
234 IN CONST EFI_GUID *HandlerType OPTIONAL,
235 OUT EFI_HANDLE *DispatchHandle
236 )
237 {
238 SMI_HANDLER *SmiHandler;
239 SMI_ENTRY *SmiEntry;
240 LIST_ENTRY *List;
241
242 if (Handler == NULL || DispatchHandle == NULL) {
243 return EFI_INVALID_PARAMETER;
244 }
245
246 SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER));
247 if (SmiHandler == NULL) {
248 return EFI_OUT_OF_RESOURCES;
249 }
250
251 SmiHandler->Signature = SMI_HANDLER_SIGNATURE;
252 SmiHandler->Handler = Handler;
253
254 if (HandlerType == NULL) {
255 //
256 // This is root SMI handler
257 //
258 SmiEntry = NULL;
259 List = &mRootSmiHandlerList;
260 } else {
261 //
262 // None root SMI handler
263 //
264 SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE);
265 if (SmiEntry == NULL) {
266 return EFI_OUT_OF_RESOURCES;
267 }
268
269 List = &SmiEntry->SmiHandlers;
270 }
271
272 SmiHandler->SmiEntry = SmiEntry;
273 InsertTailList (List, &SmiHandler->Link);
274
275 *DispatchHandle = (EFI_HANDLE) SmiHandler;
276
277 return EFI_SUCCESS;
278 }
279
280 /**
281 Unregister a handler in SMM.
282
283 @param DispatchHandle The handle that was specified when the handler was registered.
284
285 @retval EFI_SUCCESS Handler function was successfully unregistered.
286 @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle.
287
288 **/
289 EFI_STATUS
290 EFIAPI
291 SmiHandlerUnRegister (
292 IN EFI_HANDLE DispatchHandle
293 )
294 {
295 SMI_HANDLER *SmiHandler;
296 SMI_ENTRY *SmiEntry;
297
298 SmiHandler = (SMI_HANDLER *) DispatchHandle;
299
300 if (SmiHandler == NULL) {
301 return EFI_INVALID_PARAMETER;
302 }
303
304 if (SmiHandler->Signature != SMI_HANDLER_SIGNATURE) {
305 return EFI_INVALID_PARAMETER;
306 }
307
308 SmiEntry = SmiHandler->SmiEntry;
309
310 RemoveEntryList (&SmiHandler->Link);
311 FreePool (SmiHandler);
312
313 if (SmiEntry == NULL) {
314 //
315 // This is root SMI handler
316 //
317 return EFI_SUCCESS;
318 }
319
320 if (IsListEmpty (&SmiEntry->SmiHandlers)) {
321 //
322 // No handler registered for this interrupt now, remove the SMI_ENTRY
323 //
324 RemoveEntryList (&SmiEntry->AllEntries);
325
326 FreePool (SmiEntry);
327 }
328
329 return EFI_SUCCESS;
330 }