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 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 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 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 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