]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Smi.c
Add PI SMM IPL and PI SMM Core
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Smi.c
1 /** @file
2 SMI management.
3
4 Copyright (c) 2009 - 2010, 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_SUCCESS 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_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was not handled or quiesced.
115 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED 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 if (HandlerType == NULL) {
135 //
136 // Root SMI handler
137 //
138 Status = EFI_WARN_INTERRUPT_SOURCE_PENDING;
139
140 Head = &mRootSmiHandlerList;
141 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
142 SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);
143
144 Status = SmiHandler->Handler (
145 (EFI_HANDLE) SmiHandler,
146 Context,
147 CommBuffer,
148 CommBufferSize
149 );
150 if (Status == EFI_SUCCESS || Status == EFI_INTERRUPT_PENDING) {
151 return Status;
152 }
153 }
154 return Status;
155 }
156
157 //
158 // Non-root SMI handler
159 //
160 SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, FALSE);
161 if (SmiEntry == NULL) {
162 //
163 // There is no handler registered for this interrupt source
164 //
165 return EFI_WARN_INTERRUPT_SOURCE_PENDING;
166 }
167
168 InterruptQuiesced = FALSE;
169 Head = &SmiEntry->SmiHandlers;
170 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
171 SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);
172
173 Status = SmiHandler->Handler (
174 (EFI_HANDLE) SmiHandler,
175 Context,
176 CommBuffer,
177 CommBufferSize
178 );
179
180 switch (Status) {
181 case EFI_INTERRUPT_PENDING:
182 //
183 // If a handler returns EFI_INTERRUPT_PENDING, the interrupt could not be
184 // quiesced, then no additional handlers will be processed,
185 // and EFI_INTERRUPT_PENDING will be returned
186 //
187 return EFI_INTERRUPT_PENDING;
188
189 case EFI_SUCCESS:
190 //
191 // If handler return EFI_SUCCESS, the interrupt was handled and quiesced,
192 // no other handlers should still be called,
193 // and EFI_WARN_INTERRUPT_SOURCE_QUIESCED will be returned
194 //
195 return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
196
197 case EFI_WARN_INTERRUPT_SOURCE_QUIESCED:
198 //
199 // If at least one of the handlers report EFI_WARN_INTERRUPT_SOURCE_QUIESCED,
200 // then this function will return EFI_WARN_INTERRUPT_SOURCE_QUIESCED
201 //
202 InterruptQuiesced = TRUE;
203 break;
204
205 default:
206 break;
207 }
208 }
209
210 if (InterruptQuiesced) {
211 Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
212 } else {
213 //
214 // If no handler report EFI_WARN_INTERRUPT_SOURCE_QUIESCED, then this
215 // function will return EFI_INTERRUPT_PENDING
216 //
217 Status = EFI_INTERRUPT_PENDING;
218 }
219 return Status;
220 }
221
222 /**
223 Registers a handler to execute within SMM.
224
225 @param Handler Handler service funtion pointer.
226 @param HandlerType Points to the handler type or NULL for root SMI handlers.
227 @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function.
228
229 @retval EFI_SUCCESS Handler register success.
230 @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL.
231
232 **/
233 EFI_STATUS
234 EFIAPI
235 SmiHandlerRegister (
236 IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler,
237 IN CONST EFI_GUID *HandlerType OPTIONAL,
238 OUT EFI_HANDLE *DispatchHandle
239 )
240 {
241 SMI_HANDLER *SmiHandler;
242 SMI_ENTRY *SmiEntry;
243 LIST_ENTRY *List;
244
245 if (Handler == NULL || DispatchHandle == NULL) {
246 return EFI_INVALID_PARAMETER;
247 }
248
249 SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER));
250 if (SmiHandler == NULL) {
251 return EFI_OUT_OF_RESOURCES;
252 }
253
254 SmiHandler->Signature = SMI_HANDLER_SIGNATURE;
255 SmiHandler->Handler = Handler;
256
257 if (HandlerType == NULL) {
258 //
259 // This is root SMI handler
260 //
261 SmiEntry = NULL;
262 List = &mRootSmiHandlerList;
263 } else {
264 //
265 // None root SMI handler
266 //
267 SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE);
268 if (SmiEntry == NULL) {
269 return EFI_OUT_OF_RESOURCES;
270 }
271
272 List = &SmiEntry->SmiHandlers;
273 }
274
275 SmiHandler->SmiEntry = SmiEntry;
276 InsertTailList (List, &SmiHandler->Link);
277
278 *DispatchHandle = (EFI_HANDLE) SmiHandler;
279
280 return EFI_SUCCESS;
281 }
282
283 /**
284 Unregister a handler in SMM.
285
286 @param DispatchHandle The handle that was specified when the handler was registered.
287
288 @retval EFI_SUCCESS Handler function was successfully unregistered.
289 @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle.
290
291 **/
292 EFI_STATUS
293 EFIAPI
294 SmiHandlerUnRegister (
295 IN EFI_HANDLE DispatchHandle
296 )
297 {
298 SMI_HANDLER *SmiHandler;
299 SMI_ENTRY *SmiEntry;
300
301 SmiHandler = (SMI_HANDLER *) DispatchHandle;
302
303 if (SmiHandler == NULL) {
304 return EFI_INVALID_PARAMETER;
305 }
306
307 if (SmiHandler->Signature != SMI_HANDLER_SIGNATURE) {
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 }