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