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