From e42e94041f7c71a5e2e57154bd568f3c14fd6eec Mon Sep 17 00:00:00 2001 From: mdkinney Date: Thu, 25 Feb 2010 23:41:19 +0000 Subject: [PATCH] Add PI SMM IPL and PI SMM Core git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10094 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Core/PiSmmCore/Dependency.c | 371 ++++++ MdeModulePkg/Core/PiSmmCore/Dispatcher.c | 1174 +++++++++++++++++ MdeModulePkg/Core/PiSmmCore/Handle.c | 532 ++++++++ .../PiSmmCore/InstallConfigurationTable.c | 161 +++ MdeModulePkg/Core/PiSmmCore/Locate.c | 499 +++++++ MdeModulePkg/Core/PiSmmCore/Notify.c | 170 +++ MdeModulePkg/Core/PiSmmCore/Page.c | 318 +++++ MdeModulePkg/Core/PiSmmCore/PiSmmCore.c | 363 +++++ MdeModulePkg/Core/PiSmmCore/PiSmmCore.h | 718 ++++++++++ MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf | 68 + .../Core/PiSmmCore/PiSmmCorePrivateData.h | 105 ++ MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c | 1041 +++++++++++++++ MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf | 66 + MdeModulePkg/Core/PiSmmCore/Pool.c | 249 ++++ MdeModulePkg/Core/PiSmmCore/Smi.c | 333 +++++ 15 files changed, 6168 insertions(+) create mode 100644 MdeModulePkg/Core/PiSmmCore/Dependency.c create mode 100644 MdeModulePkg/Core/PiSmmCore/Dispatcher.c create mode 100644 MdeModulePkg/Core/PiSmmCore/Handle.c create mode 100644 MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c create mode 100644 MdeModulePkg/Core/PiSmmCore/Locate.c create mode 100644 MdeModulePkg/Core/PiSmmCore/Notify.c create mode 100644 MdeModulePkg/Core/PiSmmCore/Page.c create mode 100644 MdeModulePkg/Core/PiSmmCore/PiSmmCore.c create mode 100644 MdeModulePkg/Core/PiSmmCore/PiSmmCore.h create mode 100644 MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf create mode 100644 MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h create mode 100644 MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c create mode 100644 MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf create mode 100644 MdeModulePkg/Core/PiSmmCore/Pool.c create mode 100644 MdeModulePkg/Core/PiSmmCore/Smi.c diff --git a/MdeModulePkg/Core/PiSmmCore/Dependency.c b/MdeModulePkg/Core/PiSmmCore/Dependency.c new file mode 100644 index 0000000000..800892dc44 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Dependency.c @@ -0,0 +1,371 @@ +/** @file + SMM Driver Dispatcher Dependency Evaluator + + This routine evaluates a dependency expression (DEPENDENCY_EXPRESSION) to determine + if a driver can be scheduled for execution. The criteria for + schedulability is that the dependency expression is satisfied. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +/// +/// EFI_DEP_REPLACE_TRUE - Used to dynamically patch the dependecy expression +/// to save time. A EFI_DEP_PUSH is evauated one an +/// replaced with EFI_DEP_REPLACE_TRUE. If PI spec's Vol 2 +/// Driver Execution Environment Core Interface use 0xff +/// as new DEPEX opcode. EFI_DEP_REPLACE_TRUE should be +/// defined to a new value that is not conflicting with PI spec. +/// +#define EFI_DEP_REPLACE_TRUE 0xff + +/// +/// Define the initial size of the dependency expression evaluation stack +/// +#define DEPEX_STACK_SIZE_INCREMENT 0x1000 + +// +// Global stack used to evaluate dependency expressions +// +BOOLEAN *mDepexEvaluationStack = NULL; +BOOLEAN *mDepexEvaluationStackEnd = NULL; +BOOLEAN *mDepexEvaluationStackPointer = NULL; + +/** + Grow size of the Depex stack + + @retval EFI_SUCCESS Stack successfully growed. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +GrowDepexStack ( + VOID + ) +{ + BOOLEAN *NewStack; + UINTN Size; + + Size = DEPEX_STACK_SIZE_INCREMENT; + if (mDepexEvaluationStack != NULL) { + Size = Size + (mDepexEvaluationStackEnd - mDepexEvaluationStack); + } + + NewStack = AllocatePool (Size * sizeof (BOOLEAN)); + if (NewStack == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (mDepexEvaluationStack != NULL) { + // + // Copy to Old Stack to the New Stack + // + CopyMem ( + NewStack, + mDepexEvaluationStack, + (mDepexEvaluationStackEnd - mDepexEvaluationStack) * sizeof (BOOLEAN) + ); + + // + // Free The Old Stack + // + FreePool (mDepexEvaluationStack); + } + + // + // Make the Stack pointer point to the old data in the new stack + // + mDepexEvaluationStackPointer = NewStack + (mDepexEvaluationStackPointer - mDepexEvaluationStack); + mDepexEvaluationStack = NewStack; + mDepexEvaluationStackEnd = NewStack + Size; + + return EFI_SUCCESS; +} + +/** + Push an element onto the Boolean Stack. + + @param Value BOOLEAN to push. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PushBool ( + IN BOOLEAN Value + ) +{ + EFI_STATUS Status; + + // + // Check for a stack overflow condition + // + if (mDepexEvaluationStackPointer == mDepexEvaluationStackEnd) { + // + // Grow the stack + // + Status = GrowDepexStack (); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Push the item onto the stack + // + *mDepexEvaluationStackPointer = Value; + mDepexEvaluationStackPointer++; + + return EFI_SUCCESS; +} + +/** + Pop an element from the Boolean stack. + + @param Value BOOLEAN to pop. + + @retval EFI_SUCCESS The value was popped onto the stack. + @retval EFI_ACCESS_DENIED The pop operation underflowed the stack. + +**/ +EFI_STATUS +PopBool ( + OUT BOOLEAN *Value + ) +{ + // + // Check for a stack underflow condition + // + if (mDepexEvaluationStackPointer == mDepexEvaluationStack) { + return EFI_ACCESS_DENIED; + } + + // + // Pop the item off the stack + // + mDepexEvaluationStackPointer--; + *Value = *mDepexEvaluationStackPointer; + return EFI_SUCCESS; +} + +/** + This is the POSTFIX version of the dependency evaluator. This code does + not need to handle Before or After, as it is not valid to call this + routine in this case. The SOR is just ignored and is a nop in the grammer. + POSTFIX means all the math is done on top of the stack. + + @param DriverEntry DriverEntry element to update. + + @retval TRUE If driver is ready to run. + @retval FALSE If driver is not ready to run or some fatal error + was found. + +**/ +BOOLEAN +SmmIsSchedulable ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + EFI_STATUS Status; + UINT8 *Iterator; + BOOLEAN Operator; + BOOLEAN Operator2; + EFI_GUID DriverGuid; + VOID *Interface; + + Operator = FALSE; + Operator2 = FALSE; + + if (DriverEntry->After || DriverEntry->Before) { + // + // If Before or After Depex skip as SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter () + // processes them. + // + return FALSE; + } + + if (DriverEntry->Depex == NULL) { + // + // A NULL Depex means that the SMM driver is not built correctly. + // All SMM drivers must have a valid depex expressiion. + // + ASSERT (FALSE); + return FALSE; + } + + // + // Clean out memory leaks in Depex Boolean stack. Leaks are only caused by + // incorrectly formed DEPEX expressions + // + mDepexEvaluationStackPointer = mDepexEvaluationStack; + + + Iterator = DriverEntry->Depex; + + while (TRUE) { + // + // Check to see if we are attempting to fetch dependency expression instructions + // past the end of the dependency expression. + // + if (((UINTN)Iterator - (UINTN)DriverEntry->Depex) >= DriverEntry->DepexSize) { + return FALSE; + } + + // + // Look at the opcode of the dependency expression instruction. + // + switch (*Iterator) { + case EFI_DEP_BEFORE: + case EFI_DEP_AFTER: + // + // For a well-formed Dependency Expression, the code should never get here. + // The BEFORE and AFTER are processed prior to this routine's invocation. + // If the code flow arrives at this point, there was a BEFORE or AFTER + // that were not the first opcodes. + // + ASSERT (FALSE); + case EFI_DEP_SOR: + // + // These opcodes can only appear once as the first opcode. If it is found + // at any other location, then the dependency expression evaluates to FALSE + // + if (Iterator != DriverEntry->Depex) { + return FALSE; + } + // + // Otherwise, it is the first opcode and should be treated as a NOP. + // + break; + + case EFI_DEP_PUSH: + // + // Push operator is followed by a GUID. Test to see if the GUID protocol + // is installed and push the boolean result on the stack. + // + CopyMem (&DriverGuid, Iterator + 1, sizeof (EFI_GUID)); + + Status = SmmLocateProtocol (&DriverGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + // + // For SMM Driver, it may depend on uefi protocols + // + Status = gBS->LocateProtocol (&DriverGuid, NULL, &Interface); + } + + if (EFI_ERROR (Status)) { + Status = PushBool (FALSE); + } else { + *Iterator = EFI_DEP_REPLACE_TRUE; + Status = PushBool (TRUE); + } + if (EFI_ERROR (Status)) { + return FALSE; + } + + Iterator += sizeof (EFI_GUID); + break; + + case EFI_DEP_AND: + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = PopBool (&Operator2); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = PushBool ((BOOLEAN)(Operator && Operator2)); + if (EFI_ERROR (Status)) { + return FALSE; + } + break; + + case EFI_DEP_OR: + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = PopBool (&Operator2); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = PushBool ((BOOLEAN)(Operator || Operator2)); + if (EFI_ERROR (Status)) { + return FALSE; + } + break; + + case EFI_DEP_NOT: + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = PushBool ((BOOLEAN)(!Operator)); + if (EFI_ERROR (Status)) { + return FALSE; + } + break; + + case EFI_DEP_TRUE: + Status = PushBool (TRUE); + if (EFI_ERROR (Status)) { + return FALSE; + } + break; + + case EFI_DEP_FALSE: + Status = PushBool (FALSE); + if (EFI_ERROR (Status)) { + return FALSE; + } + break; + + case EFI_DEP_END: + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + return FALSE; + } + return Operator; + + case EFI_DEP_REPLACE_TRUE: + Status = PushBool (TRUE); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Iterator += sizeof (EFI_GUID); + break; + + default: + goto Done; + } + + // + // Skip over the Dependency Op Code we just processed in the switch. + // The math is done out of order, but it should not matter. That is + // we may add in the sizeof (EFI_GUID) before we account for the OP Code. + // This is not an issue, since we just need the correct end result. You + // need to be careful using Iterator in the loop as it's intermediate value + // may be strange. + // + Iterator++; + } + +Done: + return FALSE; +} diff --git a/MdeModulePkg/Core/PiSmmCore/Dispatcher.c b/MdeModulePkg/Core/PiSmmCore/Dispatcher.c new file mode 100644 index 0000000000..9e1a778900 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Dispatcher.c @@ -0,0 +1,1174 @@ +/** @file + SMM Driver Dispatcher. + + Step #1 - When a FV protocol is added to the system every driver in the FV + is added to the mDiscoveredList. The SOR, Before, and After Depex are + pre-processed as drivers are added to the mDiscoveredList. If an Apriori + file exists in the FV those drivers are addeded to the + mScheduledQueue. The mFvHandleList is used to make sure a + FV is only processed once. + + Step #2 - Dispatch. Remove driver from the mScheduledQueue and load and + start it. After mScheduledQueue is drained check the + mDiscoveredList to see if any item has a Depex that is ready to + be placed on the mScheduledQueue. + + Step #3 - Adding to the mScheduledQueue requires that you process Before + and After dependencies. This is done recursively as the call to add + to the mScheduledQueue checks for Before and recursively adds + all Befores. It then addes the item that was passed in and then + processess the After dependecies by recursively calling the routine. + + Dispatcher Rules: + The rules for the dispatcher are similar to the DXE dispatcher. + + The rules for DXE dispatcher are in chapter 10 of the DXE CIS. Figure 10-3 + is the state diagram for the DXE dispatcher + + Depex - Dependency Expresion. + SOR - Schedule On Request - Don't schedule if this bit is set. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +// +// SMM Dispatcher Data structures +// +#define KNOWN_HANDLE_SIGNATURE SIGNATURE_32('k','n','o','w') +typedef struct { + UINTN Signature; + LIST_ENTRY Link; // mFvHandleList + EFI_HANDLE Handle; +} KNOWN_HANDLE; + +// +// Function Prototypes +// + +/** + Insert InsertedDriverEntry onto the mScheduledQueue. To do this you + must add any driver with a before dependency on InsertedDriverEntry first. + You do this by recursively calling this routine. After all the Befores are + processed you can add InsertedDriverEntry to the mScheduledQueue. + Then you can add any driver with an After dependency on InsertedDriverEntry + by recursively calling this routine. + + @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue + +**/ +VOID +SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( + IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry + ); + +// +// The Driver List contains one copy of every driver that has been discovered. +// Items are never removed from the driver list. List of EFI_SMM_DRIVER_ENTRY +// +LIST_ENTRY mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList); + +// +// Queue of drivers that are ready to dispatch. This queue is a subset of the +// mDiscoveredList.list of EFI_SMM_DRIVER_ENTRY. +// +LIST_ENTRY mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue); + +// +// List of handles who's Fv's have been parsed and added to the mFwDriverList. +// +LIST_ENTRY mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList); + +// +// Flag for the SMM Dispacher. TRUE if dispatcher is execuing. +// +BOOLEAN gDispatcherRunning = FALSE; + +// +// Flag for the SMM Dispacher. TRUE if there is one or more SMM drivers ready to be dispatched +// +BOOLEAN gRequestDispatch = FALSE; + +// +// List of file types supported by dispatcher +// +EFI_FV_FILETYPE mSmmFileTypes[] = { + EFI_FV_FILETYPE_SMM, + EFI_FV_FILETYPE_COMBINED_SMM_DXE + // + // Note: DXE core will process the FV image file, so skip it in SMM core + // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE + // +}; + +typedef struct { + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH File; + EFI_DEVICE_PATH_PROTOCOL End; +} FV_FILEPATH_DEVICE_PATH; + +FV_FILEPATH_DEVICE_PATH mFvDevicePath; + +// +// DXE Architecture Protocols +// +EFI_SECURITY_ARCH_PROTOCOL *mSecurity = NULL; + +/** + Loads an EFI image into SMRAM. + + @param DriverEntry EFI_SMM_DRIVER_ENTRY instance + + @return EFI_STATUS + +**/ +EFI_STATUS +EFIAPI +SmmLoadImage ( + IN OUT EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + UINT32 AuthenticationStatus; + UINTN FilePathSize; + VOID *Buffer; + UINTN Size; + UINTN PageCount; + EFI_GUID *NameGuid; + EFI_STATUS Status; + EFI_STATUS SecurityStatus; + EFI_HANDLE DeviceHandle; + EFI_PHYSICAL_ADDRESS DstBuffer; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_DEVICE_PATH_PROTOCOL *OriginalFilePath; + EFI_DEVICE_PATH_PROTOCOL *HandleFilePath; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + Buffer = NULL; + Size = 0; + Fv = DriverEntry->Fv; + NameGuid = &DriverEntry->FileName; + FilePath = DriverEntry->FvFileDevicePath; + + OriginalFilePath = FilePath; + HandleFilePath = FilePath; + DeviceHandle = NULL; + SecurityStatus = EFI_SUCCESS; + Status = EFI_SUCCESS; + AuthenticationStatus = 0; + + // + // Try to get the image device handle by checking the match protocol. + // + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &HandleFilePath, &DeviceHandle); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // If the Security Architectural Protocol has not been located yet, then attempt to locate it + // + if (mSecurity == NULL) { + gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity); + } + + // + // Verify the Authentication Status through the Security Architectural Protocol + // + if ((mSecurity != NULL) && (OriginalFilePath != NULL)) { + SecurityStatus = mSecurity->FileAuthenticationState ( + mSecurity, + AuthenticationStatus, + OriginalFilePath + ); + if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) { + Status = SecurityStatus; + return Status; + } + } + + // + // Pull out just the file portion of the DevicePath for the LoadedImage FilePath + // + FilePath = OriginalFilePath; + Status = gBS->HandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath); + if (!EFI_ERROR (Status)) { + FilePathSize = GetDevicePathSize (HandleFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); + FilePath = (EFI_DEVICE_PATH_PROTOCOL *) (((UINT8 *)FilePath) + FilePathSize ); + } + + // + // Try reading PE32 section firstly + // + Status = Fv->ReadSection ( + Fv, + NameGuid, + EFI_SECTION_PE32, + 0, + &Buffer, + &Size, + &AuthenticationStatus + ); + + if (EFI_ERROR (Status)) { + // + // Try reading TE section secondly + // + Buffer = NULL; + Size = 0; + Status = Fv->ReadSection ( + Fv, + NameGuid, + EFI_SECTION_TE, + 0, + &Buffer, + &Size, + &AuthenticationStatus + ); + } + + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + return Status; + } + + // + // Initialize ImageContext + // + ImageContext.Handle = Buffer; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + return Status; + } + + PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment); + DstBuffer = (UINTN)(-1); + + Status = SmmAllocatePages ( + AllocateMaxAddress, + EfiRuntimeServicesCode, + PageCount, + &DstBuffer + ); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + return Status; + } + + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; + // + // Align buffer on section boundry + // + ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; + ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + + // + // Flush the instruction cache so the image data are written before we execute it + // + InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize); + + // + // Save Image EntryPoint in DriverEntry + // + DriverEntry->ImageEntryPoint = ImageContext.EntryPoint; + DriverEntry->ImageBuffer = DstBuffer; + DriverEntry->NumberOfPage = PageCount; + + // + // Allocate a Loaded Image Protocol in EfiBootServicesData + // + Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&DriverEntry->LoadedImage); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + + // + // Fill in the remaining fields of the Loaded Image Protocol instance. + // Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed. + // + DriverEntry->LoadedImage->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; + DriverEntry->LoadedImage->ParentHandle = gSmmCorePrivate->SmmIplImageHandle; + DriverEntry->LoadedImage->SystemTable = gST; + DriverEntry->LoadedImage->DeviceHandle = DeviceHandle; + + // + // Make an EfiBootServicesData buffer copy of FilePath + // + Status = gBS->AllocatePool (EfiBootServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->LoadedImage->FilePath); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + Status = gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + CopyMem (DriverEntry->LoadedImage->FilePath, FilePath, GetDevicePathSize (FilePath)); + + DriverEntry->LoadedImage->ImageBase = (VOID *)(UINTN)DriverEntry->ImageBuffer; + DriverEntry->LoadedImage->ImageSize = ImageContext.ImageSize; + DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode; + DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData; + + // + // Create a new image handle in the UEFI handle database for the SMM Driver + // + DriverEntry->ImageHandle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &DriverEntry->ImageHandle, + &gEfiLoadedImageProtocolGuid, DriverEntry->LoadedImage, + NULL + ); + + // + // Print the load address and the PDB file name if it is available + // + + DEBUG_CODE_BEGIN (); + + UINTN Index; + UINTN StartIndex; + CHAR8 EfiFileName[256]; + + + DEBUG ((DEBUG_INFO | DEBUG_LOAD, + "Loading driver at 0x%11p EntryPoint=0x%11p ", + (VOID *)(UINTN) ImageContext.ImageAddress, + FUNCTION_ENTRY_POINT (ImageContext.EntryPoint))); + + + // + // Print Module Name by Pdb file path. + // Windows and Unix style file path are all trimmed correctly. + // + if (ImageContext.PdbPointer != NULL) { + StartIndex = 0; + for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) { + if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) { + StartIndex = Index + 1; + } + } + // + // Copy the PDB file name to our temporary string, and replace .pdb with .efi + // The PDB file name is limited in the range of 0~255. + // If the length is bigger than 255, trim the redudant characters to avoid overflow in array boundary. + // + for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) { + EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex]; + if (EfiFileName[Index] == 0) { + EfiFileName[Index] = '.'; + } + if (EfiFileName[Index] == '.') { + EfiFileName[Index + 1] = 'e'; + EfiFileName[Index + 2] = 'f'; + EfiFileName[Index + 3] = 'i'; + EfiFileName[Index + 4] = 0; + break; + } + } + + if (Index == sizeof (EfiFileName) - 4) { + EfiFileName[Index] = 0; + } + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex])); + } + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n")); + + DEBUG_CODE_END (); + + // + // Free buffer allocated by Fv->ReadSection. + // + // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection + // used the UEFI Boot Services AllocatePool() function + // + Status = gBS->FreePool(Buffer); + return Status; +} + +/** + Preprocess dependency expression and update DriverEntry to reflect the + state of Before, After, and SOR dependencies. If DriverEntry->Before + or DriverEntry->After is set it will never be cleared. If SOR is set + it will be cleared by SmmSchedule(), and then the driver can be + dispatched. + + @param DriverEntry DriverEntry element to update . + + @retval EFI_SUCCESS It always works. + +**/ +EFI_STATUS +SmmPreProcessDepex ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + UINT8 *Iterator; + + Iterator = DriverEntry->Depex; + if (*Iterator == EFI_DEP_SOR) { + DriverEntry->Unrequested = TRUE; + } else { + DriverEntry->Dependent = TRUE; + } + + if (*Iterator == EFI_DEP_BEFORE) { + DriverEntry->Before = TRUE; + } else if (*Iterator == EFI_DEP_AFTER) { + DriverEntry->After = TRUE; + } + + if (DriverEntry->Before || DriverEntry->After) { + CopyMem (&DriverEntry->BeforeAfterGuid, Iterator + 1, sizeof (EFI_GUID)); + } + + return EFI_SUCCESS; +} + +/** + Read Depex and pre-process the Depex for Before and After. If Section Extraction + protocol returns an error via ReadSection defer the reading of the Depex. + + @param DriverEntry Driver to work on. + + @retval EFI_SUCCESS Depex read and preprossesed + @retval EFI_PROTOCOL_ERROR The section extraction protocol returned an error + and Depex reading needs to be retried. + @retval Error DEPEX not found. + +**/ +EFI_STATUS +SmmGetDepexSectionAndPreProccess ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + EFI_STATUS Status; + EFI_SECTION_TYPE SectionType; + UINT32 AuthenticationStatus; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + + Fv = DriverEntry->Fv; + + // + // Grab Depex info, it will never be free'ed. + // (Note: DriverEntry->Depex is in DXE memory) + // + SectionType = EFI_SECTION_SMM_DEPEX; + Status = Fv->ReadSection ( + DriverEntry->Fv, + &DriverEntry->FileName, + SectionType, + 0, + &DriverEntry->Depex, + (UINTN *)&DriverEntry->DepexSize, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_PROTOCOL_ERROR) { + // + // The section extraction protocol failed so set protocol error flag + // + DriverEntry->DepexProtocolError = TRUE; + } else { + // + // If no Depex assume depend on all architectural protocols + // + DriverEntry->Depex = NULL; + DriverEntry->Dependent = TRUE; + DriverEntry->DepexProtocolError = FALSE; + } + } else { + // + // Set Before, After, and Unrequested state information based on Depex + // Driver will be put in Dependent or Unrequested state + // + SmmPreProcessDepex (DriverEntry); + DriverEntry->DepexProtocolError = FALSE; + } + + return Status; +} + +/** + Check every driver and locate a matching one. If the driver is found, the Unrequested + state flag is cleared. + + @param FirmwareVolumeHandle The handle of the Firmware Volume that contains + the firmware file specified by DriverName. + @param DriverName The Driver name to put in the Dependent state. + + @retval EFI_SUCCESS The DriverName was found and it's SOR bit was + cleared + @retval EFI_NOT_FOUND The DriverName does not exist or it's SOR bit was + not set. + +**/ +EFI_STATUS +SmmSchedule ( + IN EFI_HANDLE FirmwareVolumeHandle, + IN EFI_GUID *DriverName + ) +{ + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + // + // Check every driver + // + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->FvHandle == FirmwareVolumeHandle && + DriverEntry->Unrequested && + CompareGuid (DriverName, &DriverEntry->FileName)) { + // + // Move the driver from the Unrequested to the Dependent state + // + DriverEntry->Unrequested = FALSE; + DriverEntry->Dependent = TRUE; + + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + +/** + This is the main Dispatcher for SMM and it exits when there are no more + drivers to run. Drain the mScheduledQueue and load and start a PE + image for each driver. Search the mDiscoveredList to see if any driver can + be placed on the mScheduledQueue. If no drivers are placed on the + mScheduledQueue exit the function. On exit it is assumed the Bds() + will be called, and when the Bds() exits the Dispatcher will be called + again. + + @retval EFI_ALREADY_STARTED The SMM Dispatcher is already running + @retval EFI_NOT_FOUND No SMM Drivers were dispatched + @retval EFI_SUCCESS One or more SMM Drivers were dispatched + +**/ +EFI_STATUS +SmmDispatcher ( + VOID + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + BOOLEAN ReadyToRun; + + if (!gRequestDispatch) { + return EFI_NOT_FOUND; + } + + if (gDispatcherRunning) { + // + // If the dispatcher is running don't let it be restarted. + // + return EFI_ALREADY_STARTED; + } + + gDispatcherRunning = TRUE; + + ReturnStatus = EFI_NOT_FOUND; + do { + // + // Drain the Scheduled Queue + // + while (!IsListEmpty (&mScheduledQueue)) { + DriverEntry = CR ( + mScheduledQueue.ForwardLink, + EFI_SMM_DRIVER_ENTRY, + ScheduledLink, + EFI_SMM_DRIVER_ENTRY_SIGNATURE + ); + + // + // Load the SMM Driver image into memory. If the Driver was transitioned from + // Untrused to Scheduled it would have already been loaded so we may need to + // skip the LoadImage + // + if (DriverEntry->ImageHandle == NULL) { + Status = SmmLoadImage (DriverEntry); + + // + // Update the driver state to reflect that it's been loaded + // + if (EFI_ERROR (Status)) { + + if (Status == EFI_SECURITY_VIOLATION) { + // + // Take driver from Scheduled to Untrused state + // + DriverEntry->Untrusted = TRUE; + } else { + // + // The SMM Driver could not be loaded, and do not attempt to load or start it again. + // Take driver from Scheduled to Initialized. + // + // This case include the Never Trusted state if EFI_ACCESS_DENIED is returned + // + DriverEntry->Initialized = TRUE; + } + + DriverEntry->Scheduled = FALSE; + RemoveEntryList (&DriverEntry->ScheduledLink); + + // + // If it's an error don't try the StartImage + // + continue; + } + } + + DriverEntry->Scheduled = FALSE; + DriverEntry->Initialized = TRUE; + RemoveEntryList (&DriverEntry->ScheduledLink); + + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_BEGIN, + &DriverEntry->ImageHandle, + sizeof (DriverEntry->ImageHandle) + ); + + // + // For each SMM driver, pass NULL as ImageHandle + // + Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint)(DriverEntry->ImageHandle, gST); + if (EFI_ERROR(Status)){ + SmmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage); + } + + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_END, + &DriverEntry->ImageHandle, + sizeof (DriverEntry->ImageHandle) + ); + + ReturnStatus = EFI_SUCCESS; + } + + // + // Search DriverList for items to place on Scheduled Queue + // + ReadyToRun = FALSE; + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + + if (DriverEntry->DepexProtocolError){ + // + // If Section Extraction Protocol did not let the Depex be read before retry the read + // + Status = SmmGetDepexSectionAndPreProccess (DriverEntry); + } + + if (DriverEntry->Dependent) { + if (SmmIsSchedulable (DriverEntry)) { + SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + ReadyToRun = TRUE; + } + } + } + } while (ReadyToRun); + + // + // If there is no more SMM driver to dispatch, stop the dispatch request + // + gRequestDispatch = FALSE; + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + + if (!DriverEntry->Initialized){ + // + // We have SMM driver pending to dispatch + // + gRequestDispatch = TRUE; + break; + } + } + + gDispatcherRunning = FALSE; + + return ReturnStatus; +} + +/** + Insert InsertedDriverEntry onto the mScheduledQueue. To do this you + must add any driver with a before dependency on InsertedDriverEntry first. + You do this by recursively calling this routine. After all the Befores are + processed you can add InsertedDriverEntry to the mScheduledQueue. + Then you can add any driver with an After dependency on InsertedDriverEntry + by recursively calling this routine. + + @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue + +**/ +VOID +SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( + IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry + ) +{ + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + // + // Process Before Dependency + // + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->Before && DriverEntry->Dependent) { + if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { + // + // Recursively process BEFORE + // + SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + } + } + } + + // + // Convert driver from Dependent to Scheduled state + // + + InsertedDriverEntry->Dependent = FALSE; + InsertedDriverEntry->Scheduled = TRUE; + InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink); + + + // + // Process After Dependency + // + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->After && DriverEntry->Dependent) { + if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { + // + // Recursively process AFTER + // + SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + } + } + } +} + +/** + Return TRUE if the Fv has been processed, FALSE if not. + + @param FvHandle The handle of a FV that's being tested + + @retval TRUE Fv protocol on FvHandle has been processed + @retval FALSE Fv protocol on FvHandle has not yet been + processed + +**/ +BOOLEAN +FvHasBeenProcessed ( + IN EFI_HANDLE FvHandle + ) +{ + LIST_ENTRY *Link; + KNOWN_HANDLE *KnownHandle; + + for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) { + KnownHandle = CR(Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE); + if (KnownHandle->Handle == FvHandle) { + return TRUE; + } + } + return FALSE; +} + +/** + Remember that Fv protocol on FvHandle has had it's drivers placed on the + mDiscoveredList. This fucntion adds entries on the mFvHandleList. Items are + never removed/freed from the mFvHandleList. + + @param FvHandle The handle of a FV that has been processed + +**/ +VOID +FvIsBeingProcesssed ( + IN EFI_HANDLE FvHandle + ) +{ + KNOWN_HANDLE *KnownHandle; + + KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE)); + ASSERT (KnownHandle != NULL); + + KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE; + KnownHandle->Handle = FvHandle; + InsertTailList (&mFvHandleList, &KnownHandle->Link); +} + +/** + Convert FvHandle and DriverName into an EFI device path + + @param Fv Fv protocol, needed to read Depex info out of + FLASH. + @param FvHandle Handle for Fv, needed in the + EFI_SMM_DRIVER_ENTRY so that the PE image can be + read out of the FV at a later time. + @param DriverName Name of driver to add to mDiscoveredList. + + @return Pointer to device path constructed from FvHandle and DriverName + +**/ +EFI_DEVICE_PATH_PROTOCOL * +SmmFvToDevicePath ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, + IN EFI_HANDLE FvHandle, + IN EFI_GUID *DriverName + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; + EFI_DEVICE_PATH_PROTOCOL *FileNameDevicePath; + + // + // Remember the device path of the FV + // + Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); + if (EFI_ERROR (Status)) { + FileNameDevicePath = NULL; + } else { + // + // Build a device path to the file in the FV to pass into gBS->LoadImage + // + EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, DriverName); + SetDevicePathEndNode (&mFvDevicePath.End); + + // + // Note: FileNameDevicePath is in DXE memory + // + FileNameDevicePath = AppendDevicePath ( + FvDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath + ); + } + return FileNameDevicePath; +} + +/** + Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry, + and initilize any state variables. Read the Depex from the FV and store it + in DriverEntry. Pre-process the Depex to set the SOR, Before and After state. + The Discovered list is never free'ed and contains booleans that represent the + other possible SMM driver states. + + @param Fv Fv protocol, needed to read Depex info out of + FLASH. + @param FvHandle Handle for Fv, needed in the + EFI_SMM_DRIVER_ENTRY so that the PE image can be + read out of the FV at a later time. + @param DriverName Name of driver to add to mDiscoveredList. + + @retval EFI_SUCCESS If driver was added to the mDiscoveredList. + @retval EFI_ALREADY_STARTED The driver has already been started. Only one + DriverName may be active in the system at any one + time. + +**/ +EFI_STATUS +SmmAddToDriverList ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, + IN EFI_HANDLE FvHandle, + IN EFI_GUID *DriverName + ) +{ + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + // + // Create the Driver Entry for the list. ZeroPool initializes lots of variables to + // NULL or FALSE. + // + DriverEntry = AllocateZeroPool (sizeof (EFI_SMM_DRIVER_ENTRY)); + ASSERT (DriverEntry != NULL); + + DriverEntry->Signature = EFI_SMM_DRIVER_ENTRY_SIGNATURE; + CopyGuid (&DriverEntry->FileName, DriverName); + DriverEntry->FvHandle = FvHandle; + DriverEntry->Fv = Fv; + DriverEntry->FvFileDevicePath = SmmFvToDevicePath (Fv, FvHandle, DriverName); + + SmmGetDepexSectionAndPreProccess (DriverEntry); + + InsertTailList (&mDiscoveredList, &DriverEntry->Link); + gRequestDispatch = TRUE; + + return EFI_SUCCESS; +} + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + Event notification that is fired every time a FV dispatch protocol is added. + More than one protocol may have been added when this event is fired, so you + must loop on SmmLocateHandle () to see how many protocols were added and + do the following to each FV: + If the Fv has already been processed, skip it. If the Fv has not been + processed then mark it as being processed, as we are about to process it. + Read the Fv and add any driver in the Fv to the mDiscoveredList.The + mDiscoveredList is never free'ed and contains variables that define + the other states the SMM driver transitions to.. + While you are at it read the A Priori file into memory. + Place drivers in the A Priori list onto the mScheduledQueue. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmDriverDispatchHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_STATUS GetNextFileStatus; + EFI_STATUS SecurityStatus; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; + EFI_HANDLE FvHandle; + EFI_GUID NameGuid; + UINTN Key; + EFI_FV_FILETYPE Type; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINTN Size; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + EFI_GUID *AprioriFile; + UINTN AprioriEntryCount; + UINTN Index; + LIST_ENTRY *Link; + UINT32 AuthenticationStatus; + UINTN SizeOfBuffer; + + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < HandleCount; Index++) { + FvHandle = HandleBuffer[Index]; + + if (FvHasBeenProcessed (FvHandle)) { + // + // This Fv has already been processed so lets skip it! + // + continue; + } + + // + // Since we are about to process this Fv mark it as processed. + // + FvIsBeingProcesssed (FvHandle); + + Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv); + if (EFI_ERROR (Status)) { + // + // FvHandle must have a Firmware Volume2 Protocol thus we should never get here. + // + ASSERT (FALSE); + continue; + } + + Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); + if (EFI_ERROR (Status)) { + // + // The Firmware volume doesn't have device path, can't be dispatched. + // + continue; + } + + // + // If the Security Architectural Protocol has not been located yet, then attempt to locate it + // + if (mSecurity == NULL) { + gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity); + } + + // + // Evaluate the authentication status of the Firmware Volume through + // Security Architectural Protocol + // + if (mSecurity != NULL) { + SecurityStatus = mSecurity->FileAuthenticationState ( + mSecurity, + 0, + FvDevicePath + ); + if (SecurityStatus != EFI_SUCCESS) { + // + // Security check failed. The firmware volume should not be used for any purpose. + // + continue; + } + } + + // + // Discover Drivers in FV and add them to the Discovered Driver List. + // Process EFI_FV_FILETYPE_SMM type and then EFI_FV_FILETYPE_COMBINED_SMM_DXE + // + for (Index = 0; Index < sizeof (mSmmFileTypes)/sizeof (EFI_FV_FILETYPE); Index++) { + // + // Initialize the search key + // + Key = 0; + do { + Type = mSmmFileTypes[Index]; + GetNextFileStatus = Fv->GetNextFile ( + Fv, + &Key, + &Type, + &NameGuid, + &Attributes, + &Size + ); + if (!EFI_ERROR (GetNextFileStatus)) { + SmmAddToDriverList (Fv, FvHandle, &NameGuid); + } + } while (!EFI_ERROR (GetNextFileStatus)); + } + + // + // Read the array of GUIDs from the Apriori file if it is present in the firmware volume + // (Note: AprioriFile is in DXE memory) + // + AprioriFile = NULL; + Status = Fv->ReadSection ( + Fv, + &gAprioriGuid, + EFI_SECTION_RAW, + 0, + (VOID **)&AprioriFile, + &SizeOfBuffer, + &AuthenticationStatus + ); + if (!EFI_ERROR (Status)) { + AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID); + } else { + AprioriEntryCount = 0; + } + + // + // Put drivers on Apriori List on the Scheduled queue. The Discovered List includes + // drivers not in the current FV and these must be skipped since the a priori list + // is only valid for the FV that it resided in. + // + + for (Index = 0; Index < AprioriEntryCount; Index++) { + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (CompareGuid (&DriverEntry->FileName, &AprioriFile[Index]) && + (FvHandle == DriverEntry->FvHandle)) { + DriverEntry->Dependent = FALSE; + DriverEntry->Scheduled = TRUE; + InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink); + break; + } + } + } + + // + // Free data allocated by Fv->ReadSection () + // + // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection + // used the UEFI Boot Services AllocatePool() function + // + gBS->FreePool (AprioriFile); + } + + // + // Execute the SMM Dispatcher on any newly discovered FVs and previously + // discovered SMM drivers that have been discovered but not dispatched. + // + return SmmDispatcher (); +} + +/** + Traverse the discovered list for any drivers that were discovered but not loaded + because the dependency experessions evaluated to false. + +**/ +VOID +SmmDisplayDiscoveredNotDispatched ( + VOID + ) +{ + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->Dependent) { + DEBUG ((DEBUG_LOAD, "SMM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName)); + } + } +} diff --git a/MdeModulePkg/Core/PiSmmCore/Handle.c b/MdeModulePkg/Core/PiSmmCore/Handle.c new file mode 100644 index 0000000000..7625c38692 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Handle.c @@ -0,0 +1,532 @@ +/** @file + SMM handle & protocol handling. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +// +// mProtocolDatabase - A list of all protocols in the system. (simple list for now) +// gHandleList - A list of all the handles in the system +// +LIST_ENTRY mProtocolDatabase = INITIALIZE_LIST_HEAD_VARIABLE (mProtocolDatabase); +LIST_ENTRY gHandleList = INITIALIZE_LIST_HEAD_VARIABLE (gHandleList); + +/** + Check whether a handle is a valid EFI_HANDLE + + @param UserHandle The handle to check + + @retval EFI_INVALID_PARAMETER The handle is NULL or not a valid EFI_HANDLE. + @retval EFI_SUCCESS The handle is valid EFI_HANDLE. + +**/ +EFI_STATUS +SmmValidateHandle ( + IN EFI_HANDLE UserHandle + ) +{ + IHANDLE *Handle; + + Handle = (IHANDLE *)UserHandle; + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + if (Handle->Signature != EFI_HANDLE_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +/** + Finds the protocol entry for the requested protocol. + + @param Protocol The ID of the protocol + @param Create Create a new entry if not found + + @return Protocol entry + +**/ +PROTOCOL_ENTRY * +SmmFindProtocolEntry ( + IN EFI_GUID *Protocol, + IN BOOLEAN Create + ) +{ + LIST_ENTRY *Link; + PROTOCOL_ENTRY *Item; + PROTOCOL_ENTRY *ProtEntry; + + // + // Search the database for the matching GUID + // + + ProtEntry = NULL; + for (Link = mProtocolDatabase.ForwardLink; + Link != &mProtocolDatabase; + Link = Link->ForwardLink) { + + Item = CR(Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE); + if (CompareGuid (&Item->ProtocolID, Protocol)) { + // + // This is the protocol entry + // + ProtEntry = Item; + break; + } + } + + // + // If the protocol entry was not found and Create is TRUE, then + // allocate a new entry + // + if ((ProtEntry == NULL) && Create) { + ProtEntry = AllocatePool (sizeof(PROTOCOL_ENTRY)); + if (ProtEntry != NULL) { + // + // Initialize new protocol entry structure + // + ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE; + CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol); + InitializeListHead (&ProtEntry->Protocols); + InitializeListHead (&ProtEntry->Notify); + + // + // Add it to protocol database + // + InsertTailList (&mProtocolDatabase, &ProtEntry->AllEntries); + } + } + return ProtEntry; +} + +/** + Finds the protocol instance for the requested handle and protocol. + Note: This function doesn't do parameters checking, it's caller's responsibility + to pass in valid parameters. + + @param Handle The handle to search the protocol on + @param Protocol GUID of the protocol + @param Interface The interface for the protocol being searched + + @return Protocol instance (NULL: Not found) + +**/ +PROTOCOL_INTERFACE * +SmmFindProtocolInterface ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_ENTRY *ProtEntry; + LIST_ENTRY *Link; + + Prot = NULL; + + // + // Lookup the protocol entry for this protocol ID + // + ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); + if (ProtEntry != NULL) { + // + // Look at each protocol interface for any matches + // + for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link=Link->ForwardLink) { + // + // If this protocol interface matches, remove it + // + Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE); + if (Prot->Interface == Interface && Prot->Protocol == ProtEntry) { + break; + } + Prot = NULL; + } + } + return Prot; +} + +/** + Wrapper function to SmmInstallProtocolInterfaceNotify. This is the public API which + Calls the private one which contains a BOOLEAN parameter for notifications + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + + @return Status code + +**/ +EFI_STATUS +EFIAPI +SmmInstallProtocolInterface ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface + ) +{ + return SmmInstallProtocolInterfaceNotify ( + UserHandle, + Protocol, + InterfaceType, + Interface, + TRUE + ); +} + +/** + Installs a protocol interface into the boot services environment. + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + @param Notify indicates whether notify the notification list + for this protocol + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate + @retval EFI_SUCCESS Protocol interface successfully installed + +**/ +EFI_STATUS +SmmInstallProtocolInterfaceNotify ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface, + IN BOOLEAN Notify + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_ENTRY *ProtEntry; + IHANDLE *Handle; + EFI_STATUS Status; + VOID *ExistingInterface; + + // + // returns EFI_INVALID_PARAMETER if InterfaceType is invalid. + // Also added check for invalid UserHandle and Protocol pointers. + // + if (UserHandle == NULL || Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (InterfaceType != EFI_NATIVE_INTERFACE) { + return EFI_INVALID_PARAMETER; + } + + // + // Print debug message + // + DEBUG((DEBUG_LOAD | DEBUG_INFO, "SmmInstallProtocolInterface: %g %p\n", Protocol, Interface)); + + Status = EFI_OUT_OF_RESOURCES; + Prot = NULL; + Handle = NULL; + + if (*UserHandle != NULL) { + Status = SmmHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface); + if (!EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Lookup the Protocol Entry for the requested protocol + // + ProtEntry = SmmFindProtocolEntry (Protocol, TRUE); + if (ProtEntry == NULL) { + goto Done; + } + + // + // Allocate a new protocol interface structure + // + Prot = AllocateZeroPool (sizeof(PROTOCOL_INTERFACE)); + if (Prot == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // If caller didn't supply a handle, allocate a new one + // + Handle = (IHANDLE *)*UserHandle; + if (Handle == NULL) { + Handle = AllocateZeroPool (sizeof(IHANDLE)); + if (Handle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Initialize new handler structure + // + Handle->Signature = EFI_HANDLE_SIGNATURE; + InitializeListHead (&Handle->Protocols); + + // + // Add this handle to the list global list of all handles + // in the system + // + InsertTailList (&gHandleList, &Handle->AllHandles); + } + + Status = SmmValidateHandle (Handle); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Each interface that is added must be unique + // + ASSERT (SmmFindProtocolInterface (Handle, Protocol, Interface) == NULL); + + // + // Initialize the protocol interface structure + // + Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE; + Prot->Handle = Handle; + Prot->Protocol = ProtEntry; + Prot->Interface = Interface; + + // + // Add this protocol interface to the head of the supported + // protocol list for this handle + // + InsertHeadList (&Handle->Protocols, &Prot->Link); + + // + // Add this protocol interface to the tail of the + // protocol entry + // + InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol); + + // + // Notify the notification list for this protocol + // + if (Notify) { + SmmNotifyProtocol (Prot); + } + Status = EFI_SUCCESS; + +Done: + if (!EFI_ERROR (Status)) { + // + // Return the new handle back to the caller + // + *UserHandle = Handle; + } else { + // + // There was an error, clean up + // + if (Prot != NULL) { + FreePool (Prot); + } + } + return Status; +} + +/** + Uninstalls all instances of a protocol:interfacer from a handle. + If the last protocol interface is remove from the handle, the + handle is freed. + + @param UserHandle The handle to remove the protocol handler from + @param Protocol The protocol, of protocol:interface, to remove + @param Interface The interface, of protocol:interface, to remove + + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_SUCCESS Protocol interface successfully uninstalled. + +**/ +EFI_STATUS +EFIAPI +SmmUninstallProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + EFI_STATUS Status; + IHANDLE *Handle; + PROTOCOL_INTERFACE *Prot; + + // + // Check that Protocol is valid + // + if (Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check that UserHandle is a valid handle + // + Status = SmmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check that Protocol exists on UserHandle, and Interface matches the interface in the database + // + Prot = SmmFindProtocolInterface (UserHandle, Protocol, Interface); + if (Prot == NULL) { + return EFI_NOT_FOUND; + } + + // + // Remove the protocol interface from the protocol + // + Status = EFI_NOT_FOUND; + Handle = (IHANDLE *)UserHandle; + Prot = SmmRemoveInterfaceFromProtocol (Handle, Protocol, Interface); + + if (Prot != NULL) { + // + // Remove the protocol interface from the handle + // + RemoveEntryList (&Prot->Link); + + // + // Free the memory + // + Prot->Signature = 0; + FreePool (Prot); + Status = EFI_SUCCESS; + } + + // + // If there are no more handlers for the handle, free the handle + // + if (IsListEmpty (&Handle->Protocols)) { + Handle->Signature = 0; + RemoveEntryList (&Handle->AllHandles); + FreePool (Handle); + } + return Status; +} + +/** + Locate a certain GUID protocol interface in a Handle's protocols. + + @param UserHandle The handle to obtain the protocol interface on + @param Protocol The GUID of the protocol + + @return The requested protocol interface for the handle + +**/ +PROTOCOL_INTERFACE * +SmmGetProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol + ) +{ + EFI_STATUS Status; + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_INTERFACE *Prot; + IHANDLE *Handle; + LIST_ENTRY *Link; + + Status = SmmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return NULL; + } + + Handle = (IHANDLE *)UserHandle; + + // + // Look at each protocol interface for a match + // + for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link = Link->ForwardLink) { + Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE); + ProtEntry = Prot->Protocol; + if (CompareGuid (&ProtEntry->ProtocolID, Protocol)) { + return Prot; + } + } + return NULL; +} + +/** + Queries a handle to determine if it supports a specified protocol. + + @param UserHandle The handle being queried. + @param Protocol The published unique identifier of the protocol. + @param Interface Supplies the address where a pointer to the + corresponding Protocol Interface is returned. + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the specified protocol. + @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE.. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER Interface is NULL. + +**/ +EFI_STATUS +EFIAPI +SmmHandleProtocol ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + PROTOCOL_INTERFACE *Prot; + + // + // Check for invalid Protocol + // + if (Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check for invalid Interface + // + if (Interface == NULL) { + return EFI_INVALID_PARAMETER; + } else { + *Interface = NULL; + } + + // + // Check for invalid UserHandle + // + Status = SmmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Look at each protocol interface for a match + // + Prot = SmmGetProtocolInterface (UserHandle, Protocol); + if (Prot == NULL) { + return EFI_UNSUPPORTED; + } + + // + // This is the protocol interface entry for this protocol + // + *Interface = Prot->Interface; + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c b/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c new file mode 100644 index 0000000000..9623fae7f2 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c @@ -0,0 +1,161 @@ +/** @file + System Management System Table Services SmmInstallConfigurationTable service + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +#define CONFIG_TABLE_SIZE_INCREASED 0x10 + +UINTN mSmmSystemTableAllocateSize = 0; + +/** + The SmmInstallConfigurationTable() function is used to maintain the list + of configuration tables that are stored in the System Management System + Table. The list is stored as an array of (GUID, Pointer) pairs. The list + must be allocated from pool memory with PoolType set to EfiRuntimeServicesData. + + @param SystemTable A pointer to the SMM System Table (SMST). + @param Guid A pointer to the GUID for the entry to add, update, or remove. + @param Table A pointer to the buffer of the table to add. + @param TableSize The size of the table to install. + + @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. + @retval EFI_INVALID_PARAMETER Guid is not valid. + @retval EFI_NOT_FOUND An attempt was made to delete a non-existent entry. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. + +**/ +EFI_STATUS +EFIAPI +SmmInstallConfigurationTable ( + IN CONST EFI_SMM_SYSTEM_TABLE2 *SystemTable, + IN CONST EFI_GUID *Guid, + IN VOID *Table, + IN UINTN TableSize + ) +{ + UINTN Index; + EFI_CONFIGURATION_TABLE *ConfigurationTable; + + // + // If Guid is NULL, then this operation cannot be performed + // + if (Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConfigurationTable = gSmmCoreSmst.SmmConfigurationTable; + + // + // Search all the table for an entry that matches Guid + // + for (Index = 0; Index < gSmmCoreSmst.NumberOfTableEntries; Index++) { + if (CompareGuid (Guid, &(ConfigurationTable[Index].VendorGuid))) { + break; + } + } + + if (Index < gSmmCoreSmst.NumberOfTableEntries) { + // + // A match was found, so this is either a modify or a delete operation + // + if (Table != NULL) { + // + // If Table is not NULL, then this is a modify operation. + // Modify the table enty and return. + // + ConfigurationTable[Index].VendorTable = Table; + return EFI_SUCCESS; + } + + // + // A match was found and Table is NULL, so this is a delete operation. + // + gSmmCoreSmst.NumberOfTableEntries--; + + // + // Copy over deleted entry + // + CopyMem ( + &(ConfigurationTable[Index]), + &(ConfigurationTable[Index + 1]), + (gSmmCoreSmst.NumberOfTableEntries - Index) * sizeof (EFI_CONFIGURATION_TABLE) + ); + + } else { + // + // No matching GUIDs were found, so this is an add operation. + // + if (Table == NULL) { + // + // If Table is NULL on an add operation, then return an error. + // + return EFI_NOT_FOUND; + } + + // + // Assume that Index == gSmmCoreSmst.NumberOfTableEntries + // + if ((Index * sizeof (EFI_CONFIGURATION_TABLE)) >= mSmmSystemTableAllocateSize) { + // + // Allocate a table with one additional entry. + // + mSmmSystemTableAllocateSize += (CONFIG_TABLE_SIZE_INCREASED * sizeof (EFI_CONFIGURATION_TABLE)); + ConfigurationTable = AllocatePool (mSmmSystemTableAllocateSize); + if (ConfigurationTable == NULL) { + // + // If a new table could not be allocated, then return an error. + // + return EFI_OUT_OF_RESOURCES; + } + + if (gSmmCoreSmst.SmmConfigurationTable != NULL) { + // + // Copy the old table to the new table. + // + CopyMem ( + ConfigurationTable, + gSmmCoreSmst.SmmConfigurationTable, + Index * sizeof (EFI_CONFIGURATION_TABLE) + ); + + // + // Free Old Table + // + FreePool (gSmmCoreSmst.SmmConfigurationTable); + } + + // + // Update System Table + // + gSmmCoreSmst.SmmConfigurationTable = ConfigurationTable; + } + + // + // Fill in the new entry + // + CopyGuid ((VOID *)&ConfigurationTable[Index].VendorGuid, Guid); + ConfigurationTable[Index].VendorTable = Table; + + // + // This is an add operation, so increment the number of table entries + // + gSmmCoreSmst.NumberOfTableEntries++; + } + + // + // CRC-32 field is ignorable for SMM System Table and should be set to zero + // + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Core/PiSmmCore/Locate.c b/MdeModulePkg/Core/PiSmmCore/Locate.c new file mode 100644 index 0000000000..585d3c8253 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Locate.c @@ -0,0 +1,499 @@ +/** @file + Locate handle functions + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +// +// ProtocolRequest - Last LocateHandle request ID +// +UINTN mEfiLocateHandleRequest = 0; + +// +// Internal prototypes +// + +typedef struct { + EFI_GUID *Protocol; + VOID *SearchKey; + LIST_ENTRY *Position; + PROTOCOL_ENTRY *ProtEntry; +} LOCATE_POSITION; + +typedef +IHANDLE * +(* CORE_GET_NEXT) ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ); + +/** + Routine to get the next Handle, when you are searching for all handles. + + @param Position Information about which Handle to seach for. + @param Interface Return the interface structure for the matching + protocol. + + @return An pointer to IHANDLE if the next Position is not the end of the list. + Otherwise,NULL is returned. + +**/ +IHANDLE * +SmmGetNextLocateAllHandles ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ) +{ + IHANDLE *Handle; + + // + // Next handle + // + Position->Position = Position->Position->ForwardLink; + + // + // If not at the end of the list, get the handle + // + Handle = NULL; + *Interface = NULL; + if (Position->Position != &gHandleList) { + Handle = CR (Position->Position, IHANDLE, AllHandles, EFI_HANDLE_SIGNATURE); + } + return Handle; +} + +/** + Routine to get the next Handle, when you are searching for register protocol + notifies. + + @param Position Information about which Handle to seach for. + @param Interface Return the interface structure for the matching + protocol. + + @return An pointer to IHANDLE if the next Position is not the end of the list. + Otherwise,NULL is returned. + +**/ +IHANDLE * +SmmGetNextLocateByRegisterNotify ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ) +{ + IHANDLE *Handle; + PROTOCOL_NOTIFY *ProtNotify; + PROTOCOL_INTERFACE *Prot; + LIST_ENTRY *Link; + + Handle = NULL; + *Interface = NULL; + ProtNotify = Position->SearchKey; + + // + // If this is the first request, get the next handle + // + if (ProtNotify != NULL) { + ASSERT(ProtNotify->Signature == PROTOCOL_NOTIFY_SIGNATURE); + Position->SearchKey = NULL; + + // + // If not at the end of the list, get the next handle + // + Link = ProtNotify->Position->ForwardLink; + if (Link != &ProtNotify->Protocol->Protocols) { + Prot = CR (Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE); + Handle = Prot->Handle; + *Interface = Prot->Interface; + } + } + return Handle; +} + +/** + Routine to get the next Handle, when you are searching for a given protocol. + + @param Position Information about which Handle to seach for. + @param Interface Return the interface structure for the matching + protocol. + + @return An pointer to IHANDLE if the next Position is not the end of the list. + Otherwise,NULL is returned. + +**/ +IHANDLE * +SmmGetNextLocateByProtocol ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ) +{ + IHANDLE *Handle; + LIST_ENTRY *Link; + PROTOCOL_INTERFACE *Prot; + + Handle = NULL; + *Interface = NULL; + for (; ;) { + // + // Next entry + // + Link = Position->Position->ForwardLink; + Position->Position = Link; + + // + // If not at the end, return the handle + // + if (Link == &Position->ProtEntry->Protocols) { + Handle = NULL; + break; + } + + // + // Get the handle + // + Prot = CR(Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE); + Handle = Prot->Handle; + *Interface = Prot->Interface; + + // + // If this handle has not been returned this request, then + // return it now + // + if (Handle->LocateRequest != mEfiLocateHandleRequest) { + Handle->LocateRequest = mEfiLocateHandleRequest; + break; + } + } + return Handle; +} + +/** + Return the first Protocol Interface that matches the Protocol GUID. If + Registration is pasased in return a Protocol Instance that was just add + to the system. If Retistration is NULL return the first Protocol Interface + you find. + + @param Protocol The protocol to search for + @param Registration Optional Registration Key returned from + RegisterProtocolNotify() + @param Interface Return the Protocol interface (instance). + + @retval EFI_SUCCESS If a valid Interface is returned + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_NOT_FOUND Protocol interface not found + +**/ +EFI_STATUS +EFIAPI +SmmLocateProtocol ( + IN EFI_GUID *Protocol, + IN VOID *Registration OPTIONAL, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + LOCATE_POSITION Position; + PROTOCOL_NOTIFY *ProtNotify; + IHANDLE *Handle; + + if (Interface == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Protocol == NULL) { + return EFI_NOT_FOUND; + } + + *Interface = NULL; + Status = EFI_SUCCESS; + + // + // Set initial position + // + Position.Protocol = Protocol; + Position.SearchKey = Registration; + Position.Position = &gHandleList; + + mEfiLocateHandleRequest += 1; + + if (Registration == NULL) { + // + // Look up the protocol entry and set the head pointer + // + Position.ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); + if (Position.ProtEntry == NULL) { + return EFI_NOT_FOUND; + } + Position.Position = &Position.ProtEntry->Protocols; + + Handle = SmmGetNextLocateByProtocol (&Position, Interface); + } else { + Handle = SmmGetNextLocateByRegisterNotify (&Position, Interface); + } + + if (Handle == NULL) { + Status = EFI_NOT_FOUND; + } else if (Registration != NULL) { + // + // If this is a search by register notify and a handle was + // returned, update the register notification position + // + ProtNotify = Registration; + ProtNotify->Position = ProtNotify->Position->ForwardLink; + } + + return Status; +} + +/** + Locates the requested handle(s) and returns them in Buffer. + + @param SearchType The type of search to perform to locate the + handles + @param Protocol The protocol to search for + @param SearchKey Dependant on SearchType + @param BufferSize On input the size of Buffer. On output the + size of data returned. + @param Buffer The buffer to return the results in + + @retval EFI_BUFFER_TOO_SMALL Buffer too small, required buffer size is + returned in BufferSize. + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully found the requested handle(s) and + returns them in Buffer. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandle ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *BufferSize, + OUT EFI_HANDLE *Buffer + ) +{ + EFI_STATUS Status; + LOCATE_POSITION Position; + PROTOCOL_NOTIFY *ProtNotify; + CORE_GET_NEXT GetNext; + UINTN ResultSize; + IHANDLE *Handle; + IHANDLE **ResultBuffer; + VOID *Interface; + + if (BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((*BufferSize > 0) && (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + GetNext = NULL; + + // + // Set initial position + // + Position.Protocol = Protocol; + Position.SearchKey = SearchKey; + Position.Position = &gHandleList; + + ResultSize = 0; + ResultBuffer = (IHANDLE **) Buffer; + Status = EFI_SUCCESS; + + // + // Get the search function based on type + // + switch (SearchType) { + case AllHandles: + GetNext = SmmGetNextLocateAllHandles; + break; + + case ByRegisterNotify: + // + // Must have SearchKey for locate ByRegisterNotify + // + if (SearchKey == NULL) { + Status = EFI_INVALID_PARAMETER; + break; + } + GetNext = SmmGetNextLocateByRegisterNotify; + break; + + case ByProtocol: + GetNext = SmmGetNextLocateByProtocol; + if (Protocol == NULL) { + Status = EFI_INVALID_PARAMETER; + break; + } + // + // Look up the protocol entry and set the head pointer + // + Position.ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); + if (Position.ProtEntry == NULL) { + Status = EFI_NOT_FOUND; + break; + } + Position.Position = &Position.ProtEntry->Protocols; + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Enumerate out the matching handles + // + mEfiLocateHandleRequest += 1; + for (; ;) { + // + // Get the next handle. If no more handles, stop + // + Handle = GetNext (&Position, &Interface); + if (NULL == Handle) { + break; + } + + // + // Increase the resulting buffer size, and if this handle + // fits return it + // + ResultSize += sizeof(Handle); + if (ResultSize <= *BufferSize) { + *ResultBuffer = Handle; + ResultBuffer += 1; + } + } + + // + // If the result is a zero length buffer, then there were no + // matching handles + // + if (ResultSize == 0) { + Status = EFI_NOT_FOUND; + } else { + // + // Return the resulting buffer size. If it's larger than what + // was passed, then set the error code + // + if (ResultSize > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ResultSize; + + if (SearchType == ByRegisterNotify && !EFI_ERROR(Status)) { + // + // If this is a search by register notify and a handle was + // returned, update the register notification position + // + ProtNotify = SearchKey; + ProtNotify->Position = ProtNotify->Position->ForwardLink; + } + } + + return Status; +} + +/** + Function returns an array of handles that support the requested protocol + in a buffer allocated from pool. This is a version of SmmLocateHandle() + that allocates a buffer for the caller. + + @param SearchType Specifies which handle(s) are to be returned. + @param Protocol Provides the protocol to search by. This + parameter is only valid for SearchType + ByProtocol. + @param SearchKey Supplies the search key depending on the + SearchType. + @param NumberHandles The number of handles returned in Buffer. + @param Buffer A pointer to the buffer to return the requested + array of handles that support Protocol. + + @retval EFI_SUCCESS The result array of handles was returned. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the + matching results. + @retval EFI_INVALID_PARAMETER One or more paramters are not valid. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandleBuffer ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + if (NumberHandles == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + *NumberHandles = 0; + *Buffer = NULL; + Status = SmmLocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + // + // LocateHandleBuffer() returns incorrect status code if SearchType is + // invalid. + // + // Add code to correctly handle expected errors from SmmLocateHandle(). + // + if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) { + if (Status != EFI_INVALID_PARAMETER) { + Status = EFI_NOT_FOUND; + } + return Status; + } + + *Buffer = AllocatePool (BufferSize); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = SmmLocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + + *NumberHandles = BufferSize / sizeof(EFI_HANDLE); + if (EFI_ERROR(Status)) { + *NumberHandles = 0; + } + + return Status; +} diff --git a/MdeModulePkg/Core/PiSmmCore/Notify.c b/MdeModulePkg/Core/PiSmmCore/Notify.c new file mode 100644 index 0000000000..8654f6e459 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Notify.c @@ -0,0 +1,170 @@ +/** @file + Support functions for UEFI protocol notification infrastructure. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +/** + Signal event for every protocol in protocol entry. + + @param Prot Protocol interface + +**/ +VOID +SmmNotifyProtocol ( + IN PROTOCOL_INTERFACE *Prot + ) +{ + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_NOTIFY *ProtNotify; + LIST_ENTRY *Link; + + ProtEntry = Prot->Protocol; + for (Link=ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link=Link->ForwardLink) { + ProtNotify = CR(Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE); + ProtNotify->Function (&ProtEntry->ProtocolID, Prot->Interface, Prot->Handle); + } +} + +/** + Removes Protocol from the protocol list (but not the handle list). + + @param Handle The handle to remove protocol on. + @param Protocol GUID of the protocol to be moved + @param Interface The interface of the protocol + + @return Protocol Entry + +**/ +PROTOCOL_INTERFACE * +SmmRemoveInterfaceFromProtocol ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_NOTIFY *ProtNotify; + PROTOCOL_ENTRY *ProtEntry; + LIST_ENTRY *Link; + + Prot = SmmFindProtocolInterface (Handle, Protocol, Interface); + if (Prot != NULL) { + + ProtEntry = Prot->Protocol; + + // + // If there's a protocol notify location pointing to this entry, back it up one + // + for(Link = ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link=Link->ForwardLink) { + ProtNotify = CR(Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE); + + if (ProtNotify->Position == &Prot->ByProtocol) { + ProtNotify->Position = Prot->ByProtocol.BackLink; + } + } + + // + // Remove the protocol interface entry + // + RemoveEntryList (&Prot->ByProtocol); + } + + return Prot; +} + +/** + Add a new protocol notification record for the request protocol. + + @param Protocol The requested protocol to add the notify + registration + @param Function Points to the notification function + @param Registration Returns the registration record + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully returned the registration record + that has been added + +**/ +EFI_STATUS +EFIAPI +SmmRegisterProtocolNotify ( + IN CONST EFI_GUID *Protocol, + IN EFI_SMM_NOTIFY_FN Function, + OUT VOID **Registration + ) +{ + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_NOTIFY *ProtNotify; + LIST_ENTRY *Link; + EFI_STATUS Status; + + if ((Protocol == NULL) || (Function == NULL) || (Registration == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ProtNotify = NULL; + + // + // Get the protocol entry to add the notification too + // + ProtEntry = SmmFindProtocolEntry ((EFI_GUID *) Protocol, TRUE); + if (ProtEntry != NULL) { + // + // Find whether notification already exist + // + for (Link = ProtEntry->Notify.ForwardLink; + Link != &ProtEntry->Notify; + Link = Link->ForwardLink) { + + ProtNotify = CR(Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE); + if (CompareGuid (&ProtNotify->Protocol->ProtocolID, Protocol) && + (ProtNotify->Function == Function)) { + + // + // Notification already exist + // + *Registration = ProtNotify; + + return EFI_SUCCESS; + } + } + + // + // Allocate a new notification record + // + ProtNotify = AllocatePool (sizeof(PROTOCOL_NOTIFY)); + if (ProtNotify != NULL) { + ProtNotify->Signature = PROTOCOL_NOTIFY_SIGNATURE; + ProtNotify->Protocol = ProtEntry; + ProtNotify->Function = Function; + // + // Start at the ending + // + ProtNotify->Position = ProtEntry->Protocols.BackLink; + + InsertTailList (&ProtEntry->Notify, &ProtNotify->Link); + } + } + + // + // Done. If we have a protocol notify entry, then return it. + // Otherwise, we must have run out of resources trying to add one + // + Status = EFI_OUT_OF_RESOURCES; + if (ProtNotify != NULL) { + *Registration = ProtNotify; + Status = EFI_SUCCESS; + } + return Status; +} diff --git a/MdeModulePkg/Core/PiSmmCore/Page.c b/MdeModulePkg/Core/PiSmmCore/Page.c new file mode 100644 index 0000000000..ec4dd4fcb8 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Page.c @@ -0,0 +1,318 @@ +/** @file + SMM Memory page management functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +#define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT) + +typedef struct { + LIST_ENTRY Link; + UINTN NumberOfPages; +} FREE_PAGE_LIST; + +LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap); + +/** + Internal Function. Allocate n pages from given free page node. + + @param Pages The free page node. + @param NumberOfPages Number of pages to be allocated. + @param MaxAddress Request to allocate memory below this address. + + @return Memory address of allocated pages. + +**/ +UINTN +InternalAllocPagesOnOneNode ( + IN OUT FREE_PAGE_LIST *Pages, + IN UINTN NumberOfPages, + IN UINTN MaxAddress + ) +{ + UINTN Top; + UINTN Bottom; + FREE_PAGE_LIST *Node; + + Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages); + if (Top > Pages->NumberOfPages) { + Top = Pages->NumberOfPages; + } + Bottom = Top - NumberOfPages; + + if (Top < Pages->NumberOfPages) { + Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top)); + Node->NumberOfPages = Pages->NumberOfPages - Top; + InsertHeadList (&Pages->Link, &Node->Link); + } + + if (Bottom > 0) { + Pages->NumberOfPages = Bottom; + } else { + RemoveEntryList (&Pages->Link); + } + + return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom); +} + +/** + Internal Function. Allocate n pages from free page list below MaxAddress. + + @param FreePageList The free page node. + @param NumberOfPages Number of pages to be allocated. + @param MaxAddress Request to allocate memory below this address. + + @return Memory address of allocated pages. + +**/ +UINTN +InternalAllocMaxAddress ( + IN OUT LIST_ENTRY *FreePageList, + IN UINTN NumberOfPages, + IN UINTN MaxAddress + ) +{ + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + + for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + if (Pages->NumberOfPages >= NumberOfPages && + (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) { + return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress); + } + } + return (UINTN)(-1); +} + +/** + Internal Function. Allocate n pages from free page list at given address. + + @param FreePageList The free page node. + @param NumberOfPages Number of pages to be allocated. + @param MaxAddress Request to allocate memory below this address. + + @return Memory address of allocated pages. + +**/ +UINTN +InternalAllocAddress ( + IN OUT LIST_ENTRY *FreePageList, + IN UINTN NumberOfPages, + IN UINTN Address + ) +{ + UINTN EndAddress; + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + + if ((Address & EFI_PAGE_MASK) != 0) { + return ~Address; + } + + EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages); + for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + if ((UINTN)Pages <= Address) { + if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) { + break; + } + return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress); + } + } + return ~Address; +} + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform. + @param MemoryType The type of memory to turn the allocated pages + into. + @param NumberOfPages The number of pages to allocate. + @param Memory A pointer to receive the base allocated memory + address. + + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ) +{ + UINTN RequestedAddress; + + if (MemoryType != EfiRuntimeServicesCode && + MemoryType != EfiRuntimeServicesData) { + return EFI_INVALID_PARAMETER; + } + + if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) { + return EFI_OUT_OF_RESOURCES; + } + + // + // We don't track memory type in SMM + // + RequestedAddress = (UINTN)*Memory; + switch (Type) { + case AllocateAnyPages: + RequestedAddress = (UINTN)(-1); + case AllocateMaxAddress: + *Memory = InternalAllocMaxAddress ( + &mSmmMemoryMap, + NumberOfPages, + RequestedAddress + ); + if (*Memory == (UINTN)-1) { + return EFI_OUT_OF_RESOURCES; + } + break; + case AllocateAddress: + *Memory = InternalAllocAddress ( + &mSmmMemoryMap, + NumberOfPages, + RequestedAddress + ); + if (*Memory != RequestedAddress) { + return EFI_NOT_FOUND; + } + break; + default: + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +/** + Internal Function. Merge two adjacent nodes. + + @param First The first of two nodes to merge. + + @return Pointer to node after merge (if success) or pointer to next node (if fail). + +**/ +FREE_PAGE_LIST * +InternalMergeNodes ( + IN FREE_PAGE_LIST *First + ) +{ + FREE_PAGE_LIST *Next; + + Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link); + ASSERT ( + TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages); + + if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) { + First->NumberOfPages += Next->NumberOfPages; + RemoveEntryList (&Next->Link); + Next = First; + } + return Next; +} + +/** + Frees previous allocated pages. + + @param Memory Base address of memory being freed. + @param NumberOfPages The number of pages to free. + + @retval EFI_NOT_FOUND Could not find the entry that covers the range. + @retval EFI_INVALID_PARAMETER Address not aligned. + @return EFI_SUCCESS Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +SmmFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + + if ((Memory & EFI_PAGE_MASK) != 0) { + return EFI_INVALID_PARAMETER; + } + + Pages = NULL; + Node = mSmmMemoryMap.ForwardLink; + while (Node != &mSmmMemoryMap) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + if (Memory < (UINTN)Pages) { + break; + } + Node = Node->ForwardLink; + } + + if (Node != &mSmmMemoryMap && + Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) { + return EFI_INVALID_PARAMETER; + } + + if (Node->BackLink != &mSmmMemoryMap) { + Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link); + if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) { + return EFI_INVALID_PARAMETER; + } + } + + Pages = (FREE_PAGE_LIST*)(UINTN)Memory; + Pages->NumberOfPages = NumberOfPages; + InsertTailList (Node, &Pages->Link); + + if (Pages->Link.BackLink != &mSmmMemoryMap) { + Pages = InternalMergeNodes ( + BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link) + ); + } + + if (Node != &mSmmMemoryMap) { + InternalMergeNodes (Pages); + } + + return EFI_SUCCESS; +} + +/** + Add free SMRAM region for use by memory service. + + @param MemBase Base address of memory region. + @param MemLength Length of the memory region. + @param Type Memory type. + @param Attributes Memory region state. + +**/ +VOID +SmmAddMemoryRegion ( + IN EFI_PHYSICAL_ADDRESS MemBase, + IN UINT64 MemLength, + IN EFI_MEMORY_TYPE Type, + IN UINT64 Attributes + ) +{ + UINTN AlignedMemBase; + + AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + MemLength -= AlignedMemBase - MemBase; + SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength)); +} diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c new file mode 100644 index 0000000000..0a50b5b70e --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c @@ -0,0 +1,363 @@ +/** @file + SMM Core Main Entry Point + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +// +// Physical pointer to private structure shared between SMM IPL and the SMM Core +// +SMM_CORE_PRIVATE_DATA *gSmmCorePrivate; + +// +// SMM Core global variable for SMM System Table. Only accessed as a physical structure in SMRAM. +// +EFI_SMM_SYSTEM_TABLE2 gSmmCoreSmst = { + { + SMM_SMST_SIGNATURE, + EFI_SMM_SYSTEM_TABLE2_REVISION, + sizeof (gSmmCoreSmst.Hdr) + }, + NULL, // SmmFirmwareVendor + 0, // SmmFirmwareRevision + SmmInstallConfigurationTable, + { + { + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5, // SmmMemRead + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5 // SmmMemWrite + }, + { + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5, // SmmIoRead + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5 // SmmIoWrite + } + }, + SmmAllocatePool, + SmmFreePool, + SmmAllocatePages, + SmmFreePages, + NULL, // SmmStartupThisAp + 0, // CurrentlyExecutingCpu + 0, // NumberOfCpus + NULL, // CpuSaveStateSize + NULL, // CpuSaveState + 0, // NumberOfTableEntries + NULL, // SmmConfigurationTable + SmmInstallProtocolInterface, + SmmUninstallProtocolInterface, + SmmHandleProtocol, + SmmRegisterProtocolNotify, + SmmLocateHandle, + SmmLocateProtocol, + SmiManage, + SmiHandlerRegister, + SmiHandlerUnRegister +}; + +// +// Flag to determine if the platform has performed a legacy boot. +// If this flag is TRUE, then the runtime code and runtime data associated with the +// SMM IPL are converted to free memory, so the SMM COre must guarantee that is +// does not touch of the code/data associated with the SMM IPL if this flag is TRUE. +// +BOOLEAN mInLegacyBoot = FALSE; + +// +// Table of SMI Handlers that are registered by the SMM Core when it is initialized +// +SMM_CORE_SMI_HANDLERS mSmmCoreSmiHandlers[] = { + { SmmDriverDispatchHandler, &gEfiEventDxeDispatchGuid, NULL, TRUE }, + { SmmReadyToLockHandler, &gEfiDxeSmmReadyToLockProtocolGuid, NULL, FALSE }, + { SmmLegacyBootHandler, &gEfiEventLegacyBootGuid, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +/** + Place holder function until all the SMM System Table Service are available. + + Note: This function is only used by SMRAM invocation. It is never used by DXE invocation. + + @param Arg1 Undefined + @param Arg2 Undefined + @param Arg3 Undefined + @param Arg4 Undefined + @param Arg5 Undefined + + @return EFI_NOT_AVAILABLE_YET + +**/ +EFI_STATUS +EFIAPI +SmmEfiNotAvailableYetArg5 ( + UINTN Arg1, + UINTN Arg2, + UINTN Arg3, + UINTN Arg4, + UINTN Arg5 + ) +{ + // + // This function should never be executed. If it does, then the architectural protocols + // have not been designed correctly. + // + return EFI_NOT_AVAILABLE_YET; +} + +/** + Software SMI handler that is called when a Legacy Boot event is signalled. The SMM + Core uses this signal to know that a Legacy Boot has been performed and that + gSmmCorePrivate that is shared between the UEFI and SMM execution environments can + not be accessed from SMM anymore since that structure is considered free memory by + a legacy OS. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmLegacyBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + mInLegacyBoot = TRUE; + return EFI_SUCCESS; +} + +/** + Software SMI handler that is called when the DxeSmmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signalled. This function unregisters the + Software SMIs that are nor required after SMRAM is locked and installs the + SMM Ready To Lock Protocol so SMM Drivers are informed that SMRAM is about + to be locked. It also verifies the the SMM CPU I/O 2 Protocol has been installed + and NULLs gBS and gST because they can not longer be used after SMRAM is locked. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmReadyToLockHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_HANDLE SmmHandle; + VOID *Interface; + + // + // Unregister SMI Handlers that are no required after the SMM driver dispatch is stopped + // + for (Index = 0; mSmmCoreSmiHandlers[Index].HandlerType != NULL; Index++) { + if (mSmmCoreSmiHandlers[Index].UnRegister) { + SmiHandlerUnRegister (mSmmCoreSmiHandlers[Index].DispatchHandle); + } + } + + // + // Install SMM Ready to lock protocol + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEfiSmmReadyToLockProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + // + // Make sure SMM CPU I/O 2 Procol has been installed into the handle database + // + Status = SmmLocateProtocol (&gEfiSmmCpuIo2ProtocolGuid, NULL, &Interface); + + // + // Print a message on a debug build if the SMM CPU I/O 2 Protocol is not installed + // + DEBUG_CODE_BEGIN (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "\nSMM: SmmCpuIo Arch Protocol not present!!\n")); + } + DEBUG_CODE_END (); + + // + // Assert if the CPU I/O 2 Protocol is not installed + // + ASSERT_EFI_ERROR (Status); + + // + // Display any drivers that were not dispatched because dependency expression + // evaluated to false if this is a debug build + // + DEBUG_CODE_BEGIN (); + SmmDisplayDiscoveredNotDispatched (); + DEBUG_CODE_END (); + + // + // Not allowed to use gST or gBS after lock + // + gST = NULL; + gBS = NULL; + + return Status; +} + +/** + The main entry point to SMM Foundation. + + Note: This function is only used by SMRAM invocation. It is never used by DXE invocation. + + @param SmmEntryContext Processor information and functionality + needed by SMM Foundation. + +**/ +VOID +EFIAPI +SmmEntryPoint ( + IN CONST EFI_SMM_ENTRY_CONTEXT *SmmEntryContext +) +{ + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *CommunicateHeader; + BOOLEAN OldInSmm; + + // + // Update SMST using the context + // + CopyMem (&gSmmCoreSmst.SmmStartupThisAp, SmmEntryContext, sizeof (EFI_SMM_ENTRY_CONTEXT)); + + // + // If a legacy boot has occured, then make sure gSmmCorePrivate is not accessed + // + if (mInLegacyBoot) { + // + // Asynchronous SMI + // + SmiManage (NULL, NULL, NULL, NULL); + return; + } + + // + // Save current InSmm state and set InSmm state to TRUE, it will be used by SmmBase2 protocol + // + OldInSmm = gSmmCorePrivate->InSmm; + gSmmCorePrivate->InSmm = TRUE; + + // + // Check to see if this is a Synchronous SMI sent through the SMM Communication + // Protocol or an Asynchronous SMI + // + if (gSmmCorePrivate->CommunicationBuffer != NULL) { + // + // Synchronous SMI for SMM Core or request from Communicate protocol + // + CommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)gSmmCorePrivate->CommunicationBuffer; + *gSmmCorePrivate->BufferSize -= OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + Status = SmiManage ( + &CommunicateHeader->HeaderGuid, + NULL, + CommunicateHeader->Data, + gSmmCorePrivate->BufferSize + ); + + // + // Update CommunicationBuffer, BufferSize and ReturnStatus + // Communicate service finished, reset the pointer to CommBuffer to NULL + // + *gSmmCorePrivate->BufferSize += OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + gSmmCorePrivate->CommunicationBuffer = NULL; + gSmmCorePrivate->ReturnStatus = (Status == EFI_WARN_INTERRUPT_SOURCE_QUIESCED) ? EFI_SUCCESS : EFI_NOT_FOUND; + } else { + // + // Asynchronous SMI + // + SmiManage (NULL, NULL, NULL, NULL); + } + + // + // Restore original InSmm state as we are going to leave SMM + // + gSmmCorePrivate->InSmm = OldInSmm; +} + +/** + The Entry Point for SMM Core + + Install DXE Protocols and reload SMM Core into SMRAM and register SMM Core + EntryPoint on the SMI vector. + + Note: This function is called for both DXE invocation and SMRAM invocation. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +SmmMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + + // + // Get SMM Core Private context passed in from SMM IPL in ImageHandle. + // + gSmmCorePrivate = (SMM_CORE_PRIVATE_DATA *)ImageHandle; + + // + // Fill in SMRAM physical address for the SMM Services Table and the SMM Entry Point. + // + gSmmCorePrivate->Smst = &gSmmCoreSmst; + gSmmCorePrivate->SmmEntryPoint = SmmEntryPoint; + + // + // Initialize memory service using free SMRAM + // + SmmInitializeMemoryServices (gSmmCorePrivate->SmramRangeCount, gSmmCorePrivate->SmramRanges); + + // + // Register all SMI Handlers required by the SMM Core + // + for (Index = 0; mSmmCoreSmiHandlers[Index].HandlerType != NULL; Index++) { + Status = SmiHandlerRegister ( + mSmmCoreSmiHandlers[Index].Handler, + mSmmCoreSmiHandlers[Index].HandlerType, + &mSmmCoreSmiHandlers[Index].DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h new file mode 100644 index 0000000000..2926f90d35 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h @@ -0,0 +1,718 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by SmmCore module. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _SMM_CORE_H_ +#define _SMM_CORE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PiSmmCorePrivateData.h" + +// +// Used to build a table of SMI Handlers that the SMM Core registers +// +typedef struct { + EFI_SMM_HANDLER_ENTRY_POINT2 Handler; + EFI_GUID *HandlerType; + EFI_HANDLE DispatchHandle; + BOOLEAN UnRegister; +} SMM_CORE_SMI_HANDLERS; + +// +// Structure for recording the state of an SMM Driver +// +#define EFI_SMM_DRIVER_ENTRY_SIGNATURE SIGNATURE_32('s', 'd','r','v') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; // mDriverList + + LIST_ENTRY ScheduledLink; // mScheduledQueue + + EFI_HANDLE FvHandle; + EFI_GUID FileName; + EFI_DEVICE_PATH_PROTOCOL *FvFileDevicePath; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + + VOID *Depex; + UINTN DepexSize; + + BOOLEAN Before; + BOOLEAN After; + EFI_GUID BeforeAfterGuid; + + BOOLEAN Dependent; + BOOLEAN Unrequested; + BOOLEAN Scheduled; + BOOLEAN Untrusted; + BOOLEAN Initialized; + BOOLEAN DepexProtocolError; + + EFI_HANDLE ImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + // + // Image EntryPoint in SMRAM + // + PHYSICAL_ADDRESS ImageEntryPoint; + // + // Image Buffer in SMRAM + // + PHYSICAL_ADDRESS ImageBuffer; + // + // Image Page Number + // + UINTN NumberOfPage; +} EFI_SMM_DRIVER_ENTRY; + +#define EFI_HANDLE_SIGNATURE SIGNATURE_32('h','n','d','l') + +/// +/// IHANDLE - contains a list of protocol handles +/// +typedef struct { + UINTN Signature; + /// All handles list of IHANDLE + LIST_ENTRY AllHandles; + /// List of PROTOCOL_INTERFACE's for this handle + LIST_ENTRY Protocols; + UINTN LocateRequest; +} IHANDLE; + +#define ASSERT_IS_HANDLE(a) ASSERT((a)->Signature == EFI_HANDLE_SIGNATURE) + +#define PROTOCOL_ENTRY_SIGNATURE SIGNATURE_32('p','r','t','e') + +/// +/// PROTOCOL_ENTRY - each different protocol has 1 entry in the protocol +/// database. Each handler that supports this protocol is listed, along +/// with a list of registered notifies. +/// +typedef struct { + UINTN Signature; + /// Link Entry inserted to mProtocolDatabase + LIST_ENTRY AllEntries; + /// ID of the protocol + EFI_GUID ProtocolID; + /// All protocol interfaces + LIST_ENTRY Protocols; + /// Registerd notification handlers + LIST_ENTRY Notify; +} PROTOCOL_ENTRY; + +#define PROTOCOL_INTERFACE_SIGNATURE SIGNATURE_32('p','i','f','c') + +/// +/// PROTOCOL_INTERFACE - each protocol installed on a handle is tracked +/// with a protocol interface structure +/// +typedef struct { + UINTN Signature; + /// Link on IHANDLE.Protocols + LIST_ENTRY Link; + /// Back pointer + IHANDLE *Handle; + /// Link on PROTOCOL_ENTRY.Protocols + LIST_ENTRY ByProtocol; + /// The protocol ID + PROTOCOL_ENTRY *Protocol; + /// The interface value + VOID *Interface; +} PROTOCOL_INTERFACE; + +#define PROTOCOL_NOTIFY_SIGNATURE SIGNATURE_32('p','r','t','n') + +/// +/// PROTOCOL_NOTIFY - used for each register notification for a protocol +/// +typedef struct { + UINTN Signature; + PROTOCOL_ENTRY *Protocol; + /// All notifications for this protocol + LIST_ENTRY Link; + /// Notification function + EFI_SMM_NOTIFY_FN Function; + /// Last position notified + LIST_ENTRY *Position; +} PROTOCOL_NOTIFY; + +// +// SMM Core Global Variables +// +extern SMM_CORE_PRIVATE_DATA *gSmmCorePrivate; +extern EFI_SMM_SYSTEM_TABLE2 gSmmCoreSmst; +extern LIST_ENTRY gHandleList; + +/** + Called to initialize the memory service. + + @param SmramRangeCount Number of SMRAM Regions + @param SmramRanges Pointer to SMRAM Descriptors + +**/ +VOID +SmmInitializeMemoryServices ( + IN UINTN SmramRangeCount, + IN EFI_SMRAM_DESCRIPTOR *SmramRanges + ); + +/** + The SmmInstallConfigurationTable() function is used to maintain the list + of configuration tables that are stored in the System Management System + Table. The list is stored as an array of (GUID, Pointer) pairs. The list + must be allocated from pool memory with PoolType set to EfiRuntimeServicesData. + + @param SystemTable A pointer to the SMM System Table (SMST). + @param Guid A pointer to the GUID for the entry to add, update, or remove. + @param Table A pointer to the buffer of the table to add. + @param TableSize The size of the table to install. + + @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. + @retval EFI_INVALID_PARAMETER Guid is not valid. + @retval EFI_NOT_FOUND An attempt was made to delete a non-existent entry. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. + +**/ +EFI_STATUS +EFIAPI +SmmInstallConfigurationTable ( + IN CONST EFI_SMM_SYSTEM_TABLE2 *SystemTable, + IN CONST EFI_GUID *Guid, + IN VOID *Table, + IN UINTN TableSize + ); + +/** + Wrapper function to SmmInstallProtocolInterfaceNotify. This is the public API which + Calls the private one which contains a BOOLEAN parameter for notifications + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + + @return Status code + +**/ +EFI_STATUS +EFIAPI +SmmInstallProtocolInterface ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface + ); + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform + @param MemoryType The type of memory to turn the allocated pages + into + @param NumberOfPages The number of pages to allocate + @param Memory A pointer to receive the base allocated memory + address + + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ); + +/** + Frees previous allocated pages. + + @param Memory Base address of memory being freed + @param NumberOfPages The number of pages to free + + @retval EFI_NOT_FOUND Could not find the entry that covers the range + @retval EFI_INVALID_PARAMETER Address not aligned + @return EFI_SUCCESS Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +SmmFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Allocate pool of a particular type. + + @param PoolType Type of pool to allocate + @param Size The amount of pool to allocate + @param Buffer The address to return a pointer to the allocated + pool + + @retval EFI_INVALID_PARAMETER PoolType not valid + @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. + @retval EFI_SUCCESS Pool successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ); + +/** + Frees pool. + + @param Buffer The allocated pool entry to free + + @retval EFI_INVALID_PARAMETER Buffer is not a valid value. + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +EFIAPI +SmmFreePool ( + IN VOID *Buffer + ); + +/** + Installs a protocol interface into the boot services environment. + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + @param Notify indicates whether notify the notification list + for this protocol + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate + @retval EFI_SUCCESS Protocol interface successfully installed + +**/ +EFI_STATUS +SmmInstallProtocolInterfaceNotify ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface, + IN BOOLEAN Notify + ); + +/** + Uninstalls all instances of a protocol:interfacer from a handle. + If the last protocol interface is remove from the handle, the + handle is freed. + + @param UserHandle The handle to remove the protocol handler from + @param Protocol The protocol, of protocol:interface, to remove + @param Interface The interface, of protocol:interface, to remove + + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_SUCCESS Protocol interface successfully uninstalled. + +**/ +EFI_STATUS +EFIAPI +SmmUninstallProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + Queries a handle to determine if it supports a specified protocol. + + @param UserHandle The handle being queried. + @param Protocol The published unique identifier of the protocol. + @param Interface Supplies the address where a pointer to the + corresponding Protocol Interface is returned. + + @return The requested protocol interface for the handle + +**/ +EFI_STATUS +EFIAPI +SmmHandleProtocol ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + OUT VOID **Interface + ); + +/** + Add a new protocol notification record for the request protocol. + + @param Protocol The requested protocol to add the notify + registration + @param Function Points to the notification function + @param Registration Returns the registration record + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully returned the registration record + that has been added + +**/ +EFI_STATUS +EFIAPI +SmmRegisterProtocolNotify ( + IN CONST EFI_GUID *Protocol, + IN EFI_SMM_NOTIFY_FN Function, + OUT VOID **Registration + ); + +/** + Locates the requested handle(s) and returns them in Buffer. + + @param SearchType The type of search to perform to locate the + handles + @param Protocol The protocol to search for + @param SearchKey Dependant on SearchType + @param BufferSize On input the size of Buffer. On output the + size of data returned. + @param Buffer The buffer to return the results in + + @retval EFI_BUFFER_TOO_SMALL Buffer too small, required buffer size is + returned in BufferSize. + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully found the requested handle(s) and + returns them in Buffer. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandle ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *BufferSize, + OUT EFI_HANDLE *Buffer + ); + +/** + Return the first Protocol Interface that matches the Protocol GUID. If + Registration is pasased in return a Protocol Instance that was just add + to the system. If Retistration is NULL return the first Protocol Interface + you find. + + @param Protocol The protocol to search for + @param Registration Optional Registration Key returned from + RegisterProtocolNotify() + @param Interface Return the Protocol interface (instance). + + @retval EFI_SUCCESS If a valid Interface is returned + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_NOT_FOUND Protocol interface not found + +**/ +EFI_STATUS +EFIAPI +SmmLocateProtocol ( + IN EFI_GUID *Protocol, + IN VOID *Registration OPTIONAL, + OUT VOID **Interface + ); + +/** + Manage SMI of a particular type. + + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param Context Points to an optional context buffer. + @param CommBuffer Points to the optional communication buffer. + @param CommBufferSize Points to the size of the optional communication buffer. + + @retval EFI_SUCCESS Interrupt source was processed successfully but not quiesced. + @retval EFI_INTERRUPT_PENDING One or more SMI sources could not be quiesced. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was not handled or quiesced. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED Interrupt source was handled and quiesced. + +**/ +EFI_STATUS +EFIAPI +SmiManage ( + IN CONST EFI_GUID *HandlerType, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + Registers a handler to execute within SMM. + + @param Handler Handler service funtion pointer. + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function. + + @retval EFI_SUCCESS Handler register success. + @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerRegister ( + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN CONST EFI_GUID *HandlerType OPTIONAL, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a handler in SMM. + + @param DispatchHandle The handle that was specified when the handler was registered. + + @retval EFI_SUCCESS Handler function was successfully unregistered. + @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerUnRegister ( + IN EFI_HANDLE DispatchHandle + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmDriverDispatchHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmLegacyBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmReadyToLockHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + Place holder function until all the SMM System Table Service are available. + + @param Arg1 Undefined + @param Arg2 Undefined + @param Arg3 Undefined + @param Arg4 Undefined + @param Arg5 Undefined + + @return EFI_NOT_AVAILABLE_YET + +**/ +EFI_STATUS +EFIAPI +SmmEfiNotAvailableYetArg5 ( + UINTN Arg1, + UINTN Arg2, + UINTN Arg3, + UINTN Arg4, + UINTN Arg5 + ); + +// +//Functions used during debug buils +// + +/** + Traverse the discovered list for any drivers that were discovered but not loaded + because the dependency experessions evaluated to false. + +**/ +VOID +SmmDisplayDiscoveredNotDispatched ( + VOID + ); + +/** + Add free SMRAM region for use by memory service. + + @param MemBase Base address of memory region. + @param MemLength Length of the memory region. + @param Type Memory type. + @param Attributes Memory region state. + +**/ +VOID +SmmAddMemoryRegion ( + IN EFI_PHYSICAL_ADDRESS MemBase, + IN UINT64 MemLength, + IN EFI_MEMORY_TYPE Type, + IN UINT64 Attributes + ); + +/** + Finds the protocol entry for the requested protocol. + + @param Protocol The ID of the protocol + @param Create Create a new entry if not found + + @return Protocol entry + +**/ +PROTOCOL_ENTRY * +SmmFindProtocolEntry ( + IN EFI_GUID *Protocol, + IN BOOLEAN Create + ); + +/** + Signal event for every protocol in protocol entry. + + @param Prot Protocol interface + +**/ +VOID +SmmNotifyProtocol ( + IN PROTOCOL_INTERFACE *Prot + ); + +/** + Finds the protocol instance for the requested handle and protocol. + Note: This function doesn't do parameters checking, it's caller's responsibility + to pass in valid parameters. + + @param Handle The handle to search the protocol on + @param Protocol GUID of the protocol + @param Interface The interface for the protocol being searched + + @return Protocol instance (NULL: Not found) + +**/ +PROTOCOL_INTERFACE * +SmmFindProtocolInterface ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + Removes Protocol from the protocol list (but not the handle list). + + @param Handle The handle to remove protocol on. + @param Protocol GUID of the protocol to be moved + @param Interface The interface of the protocol + + @return Protocol Entry + +**/ +PROTOCOL_INTERFACE * +SmmRemoveInterfaceFromProtocol ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + This is the POSTFIX version of the dependency evaluator. This code does + not need to handle Before or After, as it is not valid to call this + routine in this case. The SOR is just ignored and is a nop in the grammer. + POSTFIX means all the math is done on top of the stack. + + @param DriverEntry DriverEntry element to update. + + @retval TRUE If driver is ready to run. + @retval FALSE If driver is not ready to run or some fatal error + was found. + +**/ +BOOLEAN +SmmIsSchedulable ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ); + +#endif diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf new file mode 100644 index 0000000000..5f38065c0c --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf @@ -0,0 +1,68 @@ +## @file +# This module provide an SMM CIS compliant implementation of SMM Core. +# +# Copyright (c) 2009 - 2010, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmCore + FILE_GUID = E94F54CD-81EB-47ed-AEC3-856F5DC157A9 + MODULE_TYPE = SMM_CORE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmMain + +# VALID_ARCHITECTURES = IA32 X64 + +[Sources] + PiSmmCore.c + PiSmmCore.h + PiSmmCorePrivateData.h + Page.c + Pool.c + Handle.c + Locate.c + Notify.c + Dependency.c + Dispatcher.c + Smi.c + InstallConfigurationTable.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + PeCoffLib + CacheMaintenanceLib + DebugLib + ReportStatusCodeLib + DevicePathLib + UefiLib + UefiBootServicesTableLib + MemoryAllocationLib + +[Protocols] + gEfiDxeSmmReadyToLockProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSmmReadyToLockProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiSmmCpuIo2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiFirmwareVolume2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSecurityArchProtocolGuid # PROTOCOL SIMETIMES_CONSUMED + gEfiLoadedImageProtocolGuid # PROTOCOL SOMETIMES_PRODUCED + gEfiDevicePathProtocolGuid # PROTOCOL SOMETIMES_CONSUMED + +[Guids] + gAprioriGuid # ALWAYS_CONSUMED + gEfiEventDxeDispatchGuid # ALWAYS_CONSUMED + gEfiEventLegacyBootGuid # ALWAYS_CONSUMED diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h b/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h new file mode 100644 index 0000000000..ce007015a1 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h @@ -0,0 +1,105 @@ +/** @file + The internal header file that declared a data structure that is shared + between the SMM IPL and the SMM Core. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PI_SMM_CORE_PRIVATE_DATA_H_ +#define _PI_SMM_CORE_PRIVATE_DATA_H_ + +/// +/// Signature for the private structure shared between the SMM IPL and the SMM Core +/// +#define SMM_CORE_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('s', 'm', 'm', 'c') + +/// +/// Private structure that is used to share information between the SMM IPL and +/// the SMM Core. This structure is allocated from memory of type EfiRuntimeServicesData. +/// Since runtime memory types are converted to available memory when a legacy boot +/// is performed, the SMM Core must access any fields of this structure if a legacy +/// boot is performed. As a result, the SMM IPL must create an event notification +/// for the Legacy Boot event and notify the SMM Core that a legacy boot is being +/// performed. The SMM Core can then use this information to filter accesses to +/// thos structure. +/// +typedef struct { + UINTN Signature; + + /// + /// The ImageHandle passed into the entry point of the SMM IPL. This ImageHandle + /// is used by the SMM Core to fill in the ParentImageHandle field of the Loaded + /// Image Protocol for each SMM Driver that is dispatched by the SMM Core. + /// + EFI_HANDLE SmmIplImageHandle; + + /// + /// The number of SMRAM ranges passed from the SMM IPL to the SMM Core. The SMM + /// Core uses these ranges of SMRAM to initialize the SMM Core memory manager. + /// + UINTN SmramRangeCount; + + /// + /// A table of SMRAM ranges passed from the SMM IPL to the SMM Core. The SMM + /// Core uses these ranges of SMRAM to initialize the SMM Core memory manager. + /// + EFI_SMRAM_DESCRIPTOR *SmramRanges; + + /// + /// The SMM Foundation Entry Point. The SMM Core fills in this field when the + /// SMM Core is initialized. The SMM IPL is responsbile for registering this entry + /// point with the SMM Configuration Protocol. The SMM Configuration Protocol may + /// not be available at the time the SMM IPL and SMM Core are started, so the SMM IPL + /// sets up a protocol notification on the SMM Configuration Protocol and registers + /// the SMM Foundation Entry Point as soon as the SMM Configuration Protocol is + /// available. + /// + EFI_SMM_ENTRY_POINT SmmEntryPoint; + + /// + /// Boolean flag set to TRUE while an SMI is being processed by the SMM Core. + /// + BOOLEAN SmmEntryPointRegistered; + + /// + /// Boolean flag set to TRUE while an SMI is being processed by the SMM Core. + /// + BOOLEAN InSmm; + + /// + /// This field is set by the SMM Core then the SMM Core is initialized. This field is + /// used by the SMM Base 2 Protocol and SMM Communication Protocol implementations in + /// the SMM IPL. + /// + EFI_SMM_SYSTEM_TABLE2 *Smst; + + /// + /// This field is used by the SMM Communicatioon Protocol to pass a buffer into + /// a software SMI handler and for the software SMI handler to pass a buffer back to + /// the caller of the SMM Communication Protocol. + /// + VOID *CommunicationBuffer; + + /// + /// This field is used by the SMM Communicatioon Protocol to pass the size of a buffer, + /// in bytes, into a software SMI handler and for the software SMI handler to pass the + /// size, in bytes, of a buffer back to the caller of the SMM Communication Protocol. + /// + UINTN *BufferSize; + + /// + /// This field is used by the SMM Communication Protocol to pass the return status from + /// a software SMI handler back to the caller of the SMM Communication Protocol. + /// + EFI_STATUS ReturnStatus; +} SMM_CORE_PRIVATE_DATA; + +#endif diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c b/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c new file mode 100644 index 0000000000..c3c2afaec8 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c @@ -0,0 +1,1041 @@ +/** @file + SMM IPL that produces SMM related runtime protocols and load the SMM Core into SMRAM + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PiSmmCorePrivateData.h" + +// +// Function prototypes from produced protocols +// + +/** + Indicate whether the driver is currently executing in the SMM Initialization phase. + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param InSmram Pointer to a Boolean which, on return, indicates that the driver is currently executing + inside of SMRAM (TRUE) or outside of SMRAM (FALSE). + + @retval EFI_INVALID_PARAMETER InSmram was NULL. + @retval EFI_SUCCESS The call returned successfully. + +**/ +EFI_STATUS +EFIAPI +SmmBase2InSmram ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT BOOLEAN *InSmram + ); + +/** + Retrieves the location of the System Management System Table (SMST). + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param Smst On return, points to a pointer to the System Management Service Table (SMST). + + @retval EFI_INVALID_PARAMETER Smst or This was invalid. + @retval EFI_SUCCESS The memory was returned to the system. + @retval EFI_UNSUPPORTED Not in SMM. + +**/ +EFI_STATUS +EFIAPI +SmmBase2GetSmstLocation ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT EFI_SMM_SYSTEM_TABLE2 **Smst + ); + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered + UEFI service. This function is part of the SMM Communication Protocol that may + be called in physical mode prior to SetVirtualAddressMap() and in virtual mode + after SetVirtualAddressMap(). + + @param[in] This The EFI_SMM_COMMUNICATION_PROTOCOL instance. + @param[in out] CommBuffer A pointer to the buffer to convey into SMRAM. + @param[in out] CommSize The size of the data buffer being passed in.On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. +**/ +EFI_STATUS +EFIAPI +SmmCommunicationCommunicate ( + IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommSize + ); + +/** + Event notification that is fired every time a gEfiSmmConfigurationProtocol installs. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplSmmConfigurationEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Event notification that is fired every time a DxeSmmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signalled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplReadyToLockEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Event notification that is fired when DxeDispatch Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplGuidedEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmIplSetVirtualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// Data structure used to declare a table of protocol notifications and event +// notifications required by the SMM IPL +// +typedef struct { + BOOLEAN Protocol; + BOOLEAN CloseOnLock; + EFI_GUID *Guid; + EFI_EVENT_NOTIFY NotifyFunction; + VOID *NotifyContext; + EFI_EVENT Event; +} SMM_IPL_EVENT_NOTIFICATION; + +// +// Handle to install the SMM Base2 Protocol and the SMM Communication Protocol +// +EFI_HANDLE mSmmIplHandle = NULL; + +// +// SMM Base 2 Protocol instance +// +EFI_SMM_BASE2_PROTOCOL mSmmBase2 = { + SmmBase2InSmram, + SmmBase2GetSmstLocation +}; + +// +// SMM Communication Protocol instance +// +EFI_SMM_COMMUNICATION_PROTOCOL mSmmCommunication = { + SmmCommunicationCommunicate +}; + +// +// SMM Core Private Data structure that contains the data shared between +// the SMM IPL and the SMM Core. +// +SMM_CORE_PRIVATE_DATA mSmmCorePrivateData = { + SMM_CORE_PRIVATE_DATA_SIGNATURE, // Signature + NULL, // SmmIplImageHandle + 0, // SmramRangeCount + NULL, // SmramRanges + NULL, // SmmEntryPoint + FALSE, // SmmEntryPointRegistered + FALSE, // InSmm + NULL, // Smst + 0, // BufferSize + NULL, // CommunicationBuffer + EFI_SUCCESS // ReturnStatus +}; + +// +// Global pointer used to access mSmmCorePrivateData from outside and inside SMM +// +SMM_CORE_PRIVATE_DATA *gSmmCorePrivate = &mSmmCorePrivateData; + +// +// SMM IPL global variables +// +EFI_SMM_CONTROL2_PROTOCOL *mSmmControl2; +EFI_SMM_ACCESS2_PROTOCOL *mSmmAccess; +EFI_SMRAM_DESCRIPTOR *mCurrentSmramRange; +BOOLEAN mSmmLocked = FALSE; + +// +// Table of Protocol notification and GUIDed Event notifications that the SMM IPL requires +// +SMM_IPL_EVENT_NOTIFICATION mSmmIplEvents[] = { + // + // Declare protocol notification on the SMM Configuration protocol. When this notification is etablished, + // the associated event is immediately signalled, so the notification function will be executed and the + // SMM Configuration Protocol will be found if it is already in the handle database. + // + { TRUE, FALSE, &gEfiSmmConfigurationProtocolGuid, SmmIplSmmConfigurationEventNotify, &gEfiSmmConfigurationProtocolGuid, NULL }, + // + // Declare protocl notification on DxeSmmReadyToLock protocols. When this notification is etablished, + // the associated event is immediately signalled, so the notification function will be executed and the + // DXE SMM Ready To Lock Protocol will be found if it is already in the handle database. + // + { TRUE, TRUE, &gEfiDxeSmmReadyToLockProtocolGuid, SmmIplReadyToLockEventNotify, &gEfiDxeSmmReadyToLockProtocolGuid, NULL }, + // + // Declare event notification on the DXE Dispatch Event Group. This event is signaled by the DXE Core + // each time the DXE Core dispatcher has completed its work. When this event is signalled, the SMM Core + // if notified, so the SMM Core can dispatch SMM drivers. + // + { FALSE, TRUE, &gEfiEventDxeDispatchGuid, SmmIplGuidedEventNotify, &gEfiEventDxeDispatchGuid, NULL }, + // + // Declare event notification on Ready To Boot Event Group. This is an extra event notification that is + // used to make sure SMRAM is locked before any boot options are processed. + // + { FALSE, TRUE, &gEfiEventReadyToBootGuid, SmmIplReadyToLockEventNotify, &gEfiEventReadyToBootGuid, NULL }, + // + // Declare event notification on Legacy Boot Event Group. This is used to inform the SMM Core that the platform + // is performing a legacy boot operation, and that the UEFI environment is no longer available and the SMM Core + // must guarantee that it does not access any UEFI related structures outside of SMRAM. + // + { FALSE, FALSE, &gEfiEventLegacyBootGuid, SmmIplGuidedEventNotify, &gEfiEventLegacyBootGuid, NULL }, + // + // Declare event notification on SetVirtualAddressMap() Event Group. This is used to convert gSmmCorePrivate + // and mSmmControl2 from physical addresses to virtual addresses. + // + { FALSE, FALSE, &gEfiEventVirtualAddressChangeGuid, SmmIplSetVirtualAddressNotify, NULL, NULL }, + // + // Terminate the table of event notifications + // + { FALSE, FALSE, NULL, NULL, NULL, NULL } +}; + +/** + Indicate whether the driver is currently executing in the SMM Initialization phase. + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param InSmram Pointer to a Boolean which, on return, indicates that the driver is currently executing + inside of SMRAM (TRUE) or outside of SMRAM (FALSE). + + @retval EFI_INVALID_PARAMETER InSmram was NULL. + @retval EFI_SUCCESS The call returned successfully. + +**/ +EFI_STATUS +EFIAPI +SmmBase2InSmram ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT BOOLEAN *InSmram + ) +{ + if (InSmram == NULL) { + return EFI_INVALID_PARAMETER; + } + + *InSmram = gSmmCorePrivate->InSmm; + + return EFI_SUCCESS; +} + +/** + Retrieves the location of the System Management System Table (SMST). + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param Smst On return, points to a pointer to the System Management Service Table (SMST). + + @retval EFI_INVALID_PARAMETER Smst or This was invalid. + @retval EFI_SUCCESS The memory was returned to the system. + @retval EFI_UNSUPPORTED Not in SMM. + +**/ +EFI_STATUS +EFIAPI +SmmBase2GetSmstLocation ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT EFI_SMM_SYSTEM_TABLE2 **Smst + ) +{ + if ((This == NULL) ||(Smst == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!gSmmCorePrivate->InSmm) { + return EFI_UNSUPPORTED; + } + + *Smst = gSmmCorePrivate->Smst; + + return EFI_SUCCESS; +} + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered + UEFI service. This function is part of the SMM Communication Protocol that may + be called in physical mode prior to SetVirtualAddressMap() and in virtual mode + after SetVirtualAddressMap(). + + @param[in] This The EFI_SMM_COMMUNICATION_PROTOCOL instance. + @param[in out] CommBuffer A pointer to the buffer to convey into SMRAM. + @param[in out] CommSize The size of the data buffer being passed in.On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. +**/ +EFI_STATUS +EFIAPI +SmmCommunicationCommunicate ( + IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommSize + ) +{ + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *CommunicateHeader; + BOOLEAN OldInSmm; + + // + // Check parameters + // + if ((CommBuffer == NULL) || (CommSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // If not already in SMM, then generate a Software SMI + // + if (!gSmmCorePrivate->InSmm && gSmmCorePrivate->SmmEntryPointRegistered) { + // + // Put arguments for Software SMI in gSmmCorePrivate + // + gSmmCorePrivate->CommunicationBuffer = CommBuffer; + gSmmCorePrivate->BufferSize = CommSize; + + // + // Generate Software SMI + // + Status = mSmmControl2->Trigger (mSmmControl2, NULL, NULL, FALSE, 0); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Return status from software SMI + // + return gSmmCorePrivate->ReturnStatus; + } + + // + // If we are in SMM, then the execution mode must be physical, which means that + // OS established virtual addresses can not be used. If SetVirtualAddressMap() + // has been called, then a direct invocation of the Software SMI is not + // not allowed so return EFI_INVALID_PARAMETER. + // + if (EfiGoneVirtual()) { + return EFI_INVALID_PARAMETER; + } + + // + // Save current InSmm state and set InSmm state to TRUE + // + OldInSmm = gSmmCorePrivate->InSmm; + gSmmCorePrivate->InSmm = TRUE; + + // + // Already in SMM and before SetVirtualAddressMap(), so call SmiManage() directly. + // + CommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)CommBuffer; + *CommSize -= OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + Status = gSmmCorePrivate->Smst->SmiManage ( + &CommunicateHeader->HeaderGuid, + NULL, + CommunicateHeader->Data, + CommSize + ); + + // + // Update CommunicationBuffer, BufferSize and ReturnStatus + // Communicate service finished, reset the pointer to CommBuffer to NULL + // + *CommSize += OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + + // + // Restore original InSmm state + // + gSmmCorePrivate->InSmm = OldInSmm; + + return (Status == EFI_WARN_INTERRUPT_SOURCE_QUIESCED) ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +/** + Event notification that is fired when DxeDispatch Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplGuidedEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_SMM_COMMUNICATE_HEADER CommunicateHeader; + UINTN Size; + + // + // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure + // + CopyGuid (&CommunicateHeader.HeaderGuid, (EFI_GUID *)Context); + CommunicateHeader.MessageLength = 1; + CommunicateHeader.Data[0] = 0; + + // + // Generate the Software SMI and return the result + // + Size = sizeof (CommunicateHeader); + SmmCommunicationCommunicate (&mSmmCommunication, &CommunicateHeader, &Size); +} + +/** + Event notification that is fired every time a gEfiSmmConfigurationProtocol installs. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplSmmConfigurationEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration; + + // + // Make sure this notification is for this handler + // + Status = gBS->LocateProtocol (Context, NULL, (VOID **)&SmmConfiguration); + if (EFI_ERROR (Status)) { + return; + } + + // + // Register the SMM Entry Point provided by the SMM Core with the SMM COnfiguration protocol + // + Status = SmmConfiguration->RegisterSmmEntry (SmmConfiguration, gSmmCorePrivate->SmmEntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Set flag to indicate that the SM< Entry Point has been registered which + // means that SMIs are now fully operational. + // + gSmmCorePrivate->SmmEntryPointRegistered = TRUE; + + // + // Print debug message showing SMM Core entry point address. + // + DEBUG ((DEBUG_INFO, "SMM IPL registered SMM Entry Point address %p\n", (VOID *)(UINTN)gSmmCorePrivate->SmmEntryPoint)); + + // + // Attempt to reset SMRAM cacheability to UC + // + Status = gDS->SetMemorySpaceAttributes( + mCurrentSmramRange->CpuStart, + mCurrentSmramRange->PhysicalSize, + EFI_MEMORY_UC + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "SMM IPL failed to reset SMRAM window to EFI_MEMORY_UC\n")); + } + + // + // Close all SMRAM ranges to protect SMRAM + // + Status = mSmmAccess->Close (mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Print debug message that the SMRAM window is now closed. + // + DEBUG ((DEBUG_INFO, "SMM IPL closed SMRAM window\n")); +} + +/** + Event notification that is fired every time a DxeSmmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signalled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplReadyToLockEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *Interface; + UINTN Index; + + // + // See if we are already locked + // + if (mSmmLocked) { + return; + } + + // + // Make sure this notification is for this handler + // + if (CompareGuid ((EFI_GUID *)Context, &gEfiDxeSmmReadyToLockProtocolGuid)) { + Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + return; + } + } else { + // + // If SMM is not locked yet and we got here from gEfiEventReadyToBootGuid being + // signalled, then gEfiDxeSmmReadyToLockProtocolGuid was not installed as expected. + // Print a warning on debug builds. + // + DEBUG ((DEBUG_WARN, "SMM IPL! DXE SMM Ready To Lock Protocol not installed before Ready To Boot signal\n")); + } + + // + // Lock the SMRAM (Note: Locking SMRAM may not be supported on all platforms) + // + mSmmAccess->Lock (mSmmAccess); + + // + // Close protocol and event notification events that do not apply after the + // DXE SMM Ready To Lock Protocol has been installed or the Ready To Boot + // event has been signalled. + // + for (Index = 0; mSmmIplEvents[Index].NotifyFunction != NULL; Index++) { + if (mSmmIplEvents[Index].CloseOnLock) { + gBS->CloseEvent (mSmmIplEvents[Index].Event); + } + } + + // + // Inform SMM Core that the DxeSmmReadyToLock protocol was installed + // + SmmIplGuidedEventNotify (Event, (VOID *)&gEfiDxeSmmReadyToLockProtocolGuid); + + // + // Print debug message that the SMRAM window is now locked. + // + DEBUG ((DEBUG_INFO, "SMM IPL locked SMRAM window\n")); + + // + // Set flag so this operation will not be performed again + // + mSmmLocked = TRUE; +} + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmIplSetVirtualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EfiConvertPointer (0x0, (VOID **)&mSmmControl2); +} + +/** + Searches all Firmware Volumes for the first file matching FileType and SectionType and returns the section data. + + @param FileType FileType to search for within any of the firmware volumes in the platform. + @param SectionType SectionType to search for within any of the matching FileTypes in the firmware volumes in the platform. + @param SourceSize Return the size of the returned section data.. + + @retval != NULL Pointer to the allocated buffer containing the section data. + @retval NULL Section data was not found. + +**/ +VOID * +GetSectionInAnyFv ( + IN EFI_FV_FILETYPE FileType, + IN EFI_SECTION_TYPE SectionType, + OUT UINTN *SourceSize + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + UINTN Key; + EFI_GUID NameGuid; + EFI_FV_FILE_ATTRIBUTES Attributes; + VOID *SourceBuffer; + UINT32 AuthenticationStatus; + + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiFirmwareVolume2ProtocolGuid, + (VOID **)&Fv + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Use Firmware Volume 2 Protocol to search for a file of type FileType + // + Key = 0; + Status = Fv->GetNextFile (Fv, &Key, &FileType, &NameGuid, &Attributes, SourceSize); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Use Firmware Volume 2 Protocol to read a section of type SectionType + // + SourceBuffer = NULL; + Status = Fv->ReadSection (Fv, &NameGuid, SectionType, 0, &SourceBuffer, SourceSize, &AuthenticationStatus); + if (!EFI_ERROR (Status)) { + FreePool (HandleBuffer); + return SourceBuffer; + } + } + + FreePool(HandleBuffer); + + return NULL; +} + +/** + Load the SMM Core image into SMRAM and executes the SMM Core from SMRAM. + + @param[in] SmramRange Descriptor for the range of SMRAM to reload the + currently executing image. + @param[in] Context Context to pass into SMM Core + + @return EFI_STATUS + +**/ +EFI_STATUS +ExecuteSmmCoreFromSmram ( + IN EFI_SMRAM_DESCRIPTOR *SmramRange, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *SourceBuffer; + UINTN SourceSize; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + UINTN PageCount; + EFI_PHYSICAL_ADDRESS DestinationBuffer; + EFI_IMAGE_ENTRY_POINT EntryPoint; + + // + // Search all Firmware Volumes for a PE/COFF image in a file of type SMM_CORE + // + SourceBuffer = GetSectionInAnyFv (EFI_FV_FILETYPE_SMM_CORE, EFI_SECTION_PE32, &SourceSize); + if (SourceBuffer == NULL) { + return EFI_NOT_FOUND; + } + + // + // Initilize ImageContext + // + ImageContext.Handle = SourceBuffer; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR + // specified by SmramRange + // + PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment); + + ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0); + ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount)); + + SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount); + DestinationBuffer = SmramRange->CpuStart + SmramRange->PhysicalSize; + + // + // Align buffer on section boundry + // + ImageContext.ImageAddress = DestinationBuffer; + ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; + ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); + + // + // Print debug message showing SMM Core load address. + // + DEBUG ((DEBUG_INFO, "SMM IPL loading SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.ImageAddress)); + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (!EFI_ERROR (Status)) { + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (!EFI_ERROR (Status)) { + // + // Flush the instruction cache so the image data are written before we execute it + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + + // + // Print debug message showing SMM Core entry point address. + // + DEBUG ((DEBUG_INFO, "SMM IPL calling SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.EntryPoint)); + + // + // Execute image + // + EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)ImageContext.EntryPoint; + Status = EntryPoint ((EFI_HANDLE)Context, gST); + } + } + + // + // If the load operation, relocate operation, or the image execution return an + // error, then free memory allocated from the EFI_SRAM_DESCRIPTOR specified by + // SmramRange + // + if (EFI_ERROR (Status)) { + SmramRange->PhysicalSize += EFI_PAGES_TO_SIZE (PageCount); + } + + // + // Always free memory allocted by GetFileBufferByFilePath () + // + FreePool (SourceBuffer); + + return Status; +} + +/** + The Entry Point for SMM IPL + + Load SMM Core into SMRAM, register SMM Core entry point for SMIs, install + SMM Base 2 Protocol and SMM Communication Protocol, and register for the + critical events required to coordinate between DXE and SMM environments. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +SmmIplEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration; + UINTN Size; + UINTN Index; + EFI_SMM_RESERVED_SMRAM_REGION *SmramResRegion; + UINT64 MaxSize; + VOID *Registration; + + // + // Fill in the image handle of the SMM IPL so the SMM Core can use this as the + // ParentImageHandle field of the Load Image Protocol for all SMM Drivers loaded + // by the SMM Core + // + mSmmCorePrivateData.SmmIplImageHandle = ImageHandle; + + // + // Get SMM Access Protocol + // + Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Get SMM Control2 Protocol + // + Status = gBS->LocateProtocol (&gEfiSmmControl2ProtocolGuid, NULL, (VOID **)&mSmmControl2); + ASSERT_EFI_ERROR (Status); + + // + // Get SMM Configuration Protocol if it is present + // + SmmConfiguration = NULL; + Status = gBS->LocateProtocol (&gEfiSmmConfigurationProtocolGuid, NULL, (VOID **) &SmmConfiguration); + + // + // Get SMRAM information + // + Size = 0; + Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, NULL); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + gSmmCorePrivate->SmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size); + ASSERT (gSmmCorePrivate->SmramRanges != NULL); + + Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, gSmmCorePrivate->SmramRanges); + ASSERT_EFI_ERROR (Status); + + gSmmCorePrivate->SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR); + + // + // Open all SMRAM ranges + // + Status = mSmmAccess->Open (mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Print debug message that the SMRAM window is now open. + // + DEBUG ((DEBUG_INFO, "SMM IPL opened SMRAM window\n")); + + // + // Subtract SMRAM any reserved SMRAM regions. + // + if (SmmConfiguration != NULL) { + SmramResRegion = SmmConfiguration->SmramReservedRegions; + while (SmramResRegion->SmramReservedSize != 0) { + for (Index = 0; Index < gSmmCorePrivate->SmramRangeCount; Index ++) { + if ((SmramResRegion->SmramReservedStart >= gSmmCorePrivate->SmramRanges[Index].CpuStart) && \ + ((SmramResRegion->SmramReservedStart + SmramResRegion->SmramReservedSize) <= \ + (gSmmCorePrivate->SmramRanges[Index].CpuStart + gSmmCorePrivate->SmramRanges[Index].PhysicalSize))) { + // + // This range has reserved area, calculate the left free size + // + gSmmCorePrivate->SmramRanges[Index].PhysicalSize = SmramResRegion->SmramReservedStart - gSmmCorePrivate->SmramRanges[Index].CpuStart; + } + } + SmramResRegion++; + } + } + + // + // Find the largest SMRAM range between 1MB and 4GB that is at least 1MB in size + // + mCurrentSmramRange = NULL; + for (Index = 0, MaxSize = SIZE_1MB; Index < gSmmCorePrivate->SmramRangeCount; Index++) { + if (gSmmCorePrivate->SmramRanges[Index].CpuStart >= BASE_1MB) { + if ((gSmmCorePrivate->SmramRanges[Index].CpuStart + gSmmCorePrivate->SmramRanges[Index].PhysicalSize) <= BASE_4GB) { + if (gSmmCorePrivate->SmramRanges[Index].PhysicalSize >= MaxSize) { + MaxSize = gSmmCorePrivate->SmramRanges[Index].PhysicalSize; + mCurrentSmramRange = &gSmmCorePrivate->SmramRanges[Index]; + } + } + } + } + + if (mCurrentSmramRange != NULL) { + // + // Print debug message showing SMRAM window that will be used by SMM IPL and SMM Core + // + DEBUG ((DEBUG_INFO, "SMM IPL found SMRAM window %p - %p\n", + (VOID *)(UINTN)mCurrentSmramRange->CpuStart, + (VOID *)(UINTN)(mCurrentSmramRange->CpuStart + mCurrentSmramRange->PhysicalSize - 1) + )); + + // + // Attempt to set SMRAM cacheability to WB + // + Status = gDS->SetMemorySpaceAttributes( + mCurrentSmramRange->CpuStart, + mCurrentSmramRange->PhysicalSize, + EFI_MEMORY_WB + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "SMM IPL failed to set SMRAM window to EFI_MEMORY_WB\n")); + } + + // + // Load SMM Core into SMRAM and execute it from SMRAM + // + Status = ExecuteSmmCoreFromSmram (mCurrentSmramRange, gSmmCorePrivate); + if (EFI_ERROR (Status)) { + // + // Print error message that the SMM Core failed to be loaded and executed. + // + DEBUG ((DEBUG_ERROR, "SMM IPL could not load and execute SMM Core from SMRAM\n")); + + // + // Attempt to reset SMRAM cacheability to UC + // + Status = gDS->SetMemorySpaceAttributes( + mCurrentSmramRange->CpuStart, + mCurrentSmramRange->PhysicalSize, + EFI_MEMORY_UC + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "SMM IPL failed to reset SMRAM window to EFI_MEMORY_UC\n")); + } + } + } else { + // + // Print error message that there are not enough SMRAM resources to load the SMM Core. + // + DEBUG ((DEBUG_ERROR, "SMM IPL could not find a large enough SMRAM region to load SMM Core\n")); + } + + // + // If the SMM Core could not be loaded then close SMRAM window, free allocated + // resources, and return an error so SMM IPL will be unloaded. + // + if (mCurrentSmramRange == NULL || EFI_ERROR (Status)) { + // + // Close all SMRAM ranges + // + Status = mSmmAccess->Close (mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Print debug message that the SMRAM window is now closed. + // + DEBUG ((DEBUG_INFO, "SMM IPL closed SMRAM window\n")); + + // + // Free all allocated resources + // + FreePool (gSmmCorePrivate->SmramRanges); + + return EFI_UNSUPPORTED; + } + + // + // Install SMM Base2 Protocol and SMM Communication Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mSmmIplHandle, + &gEfiSmmBase2ProtocolGuid, &mSmmBase2, + &gEfiSmmCommunicationProtocolGuid, &mSmmCommunication, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Create the set of protocol and event notififcations that the SMM IPL requires + // + for (Index = 0; mSmmIplEvents[Index].NotifyFunction != NULL; Index++) { + if (mSmmIplEvents[Index].Protocol) { + mSmmIplEvents[Index].Event = EfiCreateProtocolNotifyEvent ( + mSmmIplEvents[Index].Guid, + TPL_CALLBACK, + mSmmIplEvents[Index].NotifyFunction, + mSmmIplEvents[Index].NotifyContext, + &Registration + ); + } else { + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + mSmmIplEvents[Index].NotifyFunction, + mSmmIplEvents[Index].NotifyContext, + mSmmIplEvents[Index].Guid, + &mSmmIplEvents[Index].Event + ); + ASSERT_EFI_ERROR (Status); + } + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf b/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf new file mode 100644 index 0000000000..d724c495f0 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf @@ -0,0 +1,66 @@ +## @file +# This module provide an SMM CIS compliant implementation of SMM IPL. +# +# Copyright (c) 2009 - 2010, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmIpl + FILE_GUID = 2FA2A6DA-11D5-4dc3-999A-749648B03C56 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmIplEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + PiSmmIpl.c + PiSmmCorePrivateData.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + PeCoffLib + CacheMaintenanceLib + MemoryAllocationLib + DebugLib + UefiBootServicesTableLib + DxeServicesTableLib + UefiLib + UefiRuntimeLib + +[Protocols] + gEfiSmmBase2ProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiSmmCommunicationProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiSmmAccess2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSmmConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSmmControl2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDxeSmmReadyToLockProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiFirmwareVolume2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + +[Guids] + gEfiEventDxeDispatchGuid # ALWAYS_CONSUMED + gEfiEventReadyToBootGuid # ALWAYS_CONSUMED + gEfiEventLegacyBootGuid # ALWAYS_CONSUMED + gEfiEventVirtualAddressChangeGuid # ALWAYS_CONSUMED + +[Depex] + gEfiSmmAccess2ProtocolGuid AND gEfiSmmControl2ProtocolGuid diff --git a/MdeModulePkg/Core/PiSmmCore/Pool.c b/MdeModulePkg/Core/PiSmmCore/Pool.c new file mode 100644 index 0000000000..9e86d93e18 --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Pool.c @@ -0,0 +1,249 @@ +/** @file + SMM Memory pool management functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +// +// MIN_POOL_SHIFT must not be less than 5 +// +#define MIN_POOL_SHIFT 6 +#define MIN_POOL_SIZE (1 << MIN_POOL_SHIFT) + +// +// MAX_POOL_SHIFT must not be less than EFI_PAGE_SHIFT - 1 +// +#define MAX_POOL_SHIFT (EFI_PAGE_SHIFT - 1) +#define MAX_POOL_SIZE (1 << MAX_POOL_SHIFT) + +// +// MAX_POOL_INDEX are calculated by maximum and minimum pool sizes +// +#define MAX_POOL_INDEX (MAX_POOL_SHIFT - MIN_POOL_SHIFT + 1) + +typedef struct { + UINTN Size; + BOOLEAN Available; +} POOL_HEADER; + +typedef struct { + POOL_HEADER Header; + LIST_ENTRY Link; +} FREE_POOL_HEADER; + +LIST_ENTRY mSmmPoolLists[MAX_POOL_INDEX]; + +/** + Called to initialize the memory service. + + @param SmramRangeCount Number of SMRAM Regions + @param SmramRanges Pointer to SMRAM Descriptors + +**/ +VOID +SmmInitializeMemoryServices ( + IN UINTN SmramRangeCount, + IN EFI_SMRAM_DESCRIPTOR *SmramRanges + ) +{ + UINTN Index; + + // + // Initialize Pool list + // + for (Index = sizeof (mSmmPoolLists) / sizeof (*mSmmPoolLists); Index > 0;) { + InitializeListHead (&mSmmPoolLists[--Index]); + } + + // + // Initialize free SMRAM regions + // + for (Index = 0; Index < SmramRangeCount; Index++) { + SmmAddMemoryRegion ( + SmramRanges[Index].CpuStart, + SmramRanges[Index].PhysicalSize, + EfiConventionalMemory, + SmramRanges[Index].RegionState + ); + } +} + +/** + Internal Function. Allocate a pool by specified PoolIndex. + + @param PoolIndex Index which indicate the Pool size. + @param FreePoolHdr The returned Free pool. + + @retval EFI_OUT_OF_RESOURCES Allocation failed. + @retval EFI_SUCCESS Pool successfully allocated. + +**/ +EFI_STATUS +InternalAllocPoolByIndex ( + IN UINTN PoolIndex, + OUT FREE_POOL_HEADER **FreePoolHdr + ) +{ + EFI_STATUS Status; + FREE_POOL_HEADER *Hdr; + + Status = EFI_SUCCESS; + if (PoolIndex == MAX_POOL_INDEX) { + Hdr = (FREE_POOL_HEADER *)AllocatePages (EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1)); + if (Hdr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + } else if (!IsListEmpty (&mSmmPoolLists[PoolIndex])) { + Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[PoolIndex]), FREE_POOL_HEADER, Link); + RemoveEntryList (&Hdr->Link); + } else { + Status = InternalAllocPoolByIndex (PoolIndex + 1, &Hdr); + if (!EFI_ERROR (Status)) { + Hdr->Header.Size >>= 1; + Hdr->Header.Available = TRUE; + InsertHeadList (&mSmmPoolLists[PoolIndex], &Hdr->Link); + Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size); + } + } + + if (!EFI_ERROR (Status)) { + Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex; + Hdr->Header.Available = FALSE; + } + + *FreePoolHdr = Hdr; + return Status; +} + +/** + Internal Function. Free a pool by specified PoolIndex. + + @param FreePoolHdr The pool to free. + + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +InternalFreePoolByIndex ( + IN FREE_POOL_HEADER *FreePoolHdr + ) +{ + UINTN PoolIndex; + + ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0); + ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0); + ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE); + + PoolIndex = HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT; + FreePoolHdr->Header.Available = TRUE; + InsertHeadList (&mSmmPoolLists[PoolIndex], &FreePoolHdr->Link); + return EFI_SUCCESS; +} + +/** + Allocate pool of a particular type. + + @param PoolType Type of pool to allocate. + @param Size The amount of pool to allocate. + @param Buffer The address to return a pointer to the allocated + pool. + + @retval EFI_INVALID_PARAMETER PoolType not valid. + @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. + @retval EFI_SUCCESS Pool successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + POOL_HEADER *PoolHdr; + FREE_POOL_HEADER *FreePoolHdr; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + UINTN PoolIndex; + + if (PoolType != EfiRuntimeServicesCode && + PoolType != EfiRuntimeServicesData) { + return EFI_INVALID_PARAMETER; + } + + if (Size == 0) { + *Buffer = NULL; + return EFI_SUCCESS; + } + + Size += sizeof (*PoolHdr); + if (Size > MAX_POOL_SIZE) { + Size = EFI_SIZE_TO_PAGES (Size); + Status = SmmAllocatePages (AllocateAnyPages, PoolType, Size, &Address); + if (EFI_ERROR (Status)) { + return Status; + } + + PoolHdr = (POOL_HEADER*)(UINTN)Address; + PoolHdr->Size = EFI_PAGES_TO_SIZE (Size); + PoolHdr->Available = FALSE; + *Buffer = PoolHdr + 1; + return Status; + } + + Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT; + PoolIndex = HighBitSet32 ((UINT32)Size); + if ((Size & (Size - 1)) != 0) { + PoolIndex++; + } + + Status = InternalAllocPoolByIndex (PoolIndex, &FreePoolHdr); + *Buffer = &FreePoolHdr->Header + 1; + return Status; +} + +/** + Frees pool. + + @param Buffer The allocated pool entry to free. + + @retval EFI_INVALID_PARAMETER Buffer is not a valid value. + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +EFIAPI +SmmFreePool ( + IN VOID *Buffer + ) +{ + FREE_POOL_HEADER *FreePoolHdr; + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1); + ASSERT (!FreePoolHdr->Header.Available); + + if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) { + ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0); + ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0); + return SmmFreePages ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr, + EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size) + ); + } + return InternalFreePoolByIndex (FreePoolHdr); +} diff --git a/MdeModulePkg/Core/PiSmmCore/Smi.c b/MdeModulePkg/Core/PiSmmCore/Smi.c new file mode 100644 index 0000000000..ccf6c0de2c --- /dev/null +++ b/MdeModulePkg/Core/PiSmmCore/Smi.c @@ -0,0 +1,333 @@ +/** @file + SMI management. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiSmmCore.h" + +// +// SMM_HANDLER - used for each SMM handler +// + +#define SMI_ENTRY_SIGNATURE SIGNATURE_32('s','m','i','e') + + typedef struct { + UINTN Signature; + LIST_ENTRY AllEntries; // All entries + + EFI_GUID HandlerType; // Type of interrupt + LIST_ENTRY SmiHandlers; // All handlers +} SMI_ENTRY; + +#define SMI_HANDLER_SIGNATURE SIGNATURE_32('s','m','i','h') + + typedef struct { + UINTN Signature; + LIST_ENTRY Link; // Link on SMI_ENTRY.SmiHandlers + EFI_SMM_HANDLER_ENTRY_POINT2 Handler; // The smm handler's entry point + SMI_ENTRY *SmiEntry; +} SMI_HANDLER; + +LIST_ENTRY mRootSmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiHandlerList); +LIST_ENTRY mSmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList); + +/** + Finds the SMI entry for the requested handler type. + + @param HandlerType The type of the interrupt + @param Create Create a new entry if not found + + @return SMI entry + +**/ +SMI_ENTRY * +EFIAPI +SmmCoreFindSmiEntry ( + IN EFI_GUID *HandlerType, + IN BOOLEAN Create + ) +{ + LIST_ENTRY *Link; + SMI_ENTRY *Item; + SMI_ENTRY *SmiEntry; + + // + // Search the SMI entry list for the matching GUID + // + SmiEntry = NULL; + for (Link = mSmiEntryList.ForwardLink; + Link != &mSmiEntryList; + Link = Link->ForwardLink) { + + Item = CR (Link, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + if (CompareGuid (&Item->HandlerType, HandlerType)) { + // + // This is the SMI entry + // + SmiEntry = Item; + break; + } + } + + // + // If the protocol entry was not found and Create is TRUE, then + // allocate a new entry + // + if ((SmiEntry == NULL) && Create) { + SmiEntry = AllocatePool (sizeof(SMI_ENTRY)); + if (SmiEntry != NULL) { + // + // Initialize new SMI entry structure + // + SmiEntry->Signature = SMI_ENTRY_SIGNATURE; + CopyGuid ((VOID *)&SmiEntry->HandlerType, HandlerType); + InitializeListHead (&SmiEntry->SmiHandlers); + + // + // Add it to SMI entry list + // + InsertTailList (&mSmiEntryList, &SmiEntry->AllEntries); + } + } + return SmiEntry; +} + +/** + Manage SMI of a particular type. + + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param Context Points to an optional context buffer. + @param CommBuffer Points to the optional communication buffer. + @param CommBufferSize Points to the size of the optional communication buffer. + + @retval EFI_SUCCESS Interrupt source was processed successfully but not quiesced. + @retval EFI_INTERRUPT_PENDING One or more SMI sources could not be quiesced. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was not handled or quiesced. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED Interrupt source was handled and quiesced. + +**/ +EFI_STATUS +EFIAPI +SmiManage ( + IN CONST EFI_GUID *HandlerType, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *Head; + SMI_ENTRY *SmiEntry; + SMI_HANDLER *SmiHandler; + BOOLEAN InterruptQuiesced; + EFI_STATUS Status; + + if (HandlerType == NULL) { + // + // Root SMI handler + // + Status = EFI_WARN_INTERRUPT_SOURCE_PENDING; + + Head = &mRootSmiHandlerList; + for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { + SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + + Status = SmiHandler->Handler ( + (EFI_HANDLE) SmiHandler, + Context, + CommBuffer, + CommBufferSize + ); + if (Status == EFI_SUCCESS || Status == EFI_INTERRUPT_PENDING) { + return Status; + } + } + return Status; + } + + // + // Non-root SMI handler + // + SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, FALSE); + if (SmiEntry == NULL) { + // + // There is no handler registered for this interrupt source + // + return EFI_WARN_INTERRUPT_SOURCE_PENDING; + } + + InterruptQuiesced = FALSE; + Head = &SmiEntry->SmiHandlers; + for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { + SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + + Status = SmiHandler->Handler ( + (EFI_HANDLE) SmiHandler, + Context, + CommBuffer, + CommBufferSize + ); + + switch (Status) { + case EFI_INTERRUPT_PENDING: + // + // If a handler returns EFI_INTERRUPT_PENDING, the interrupt could not be + // quiesced, then no additional handlers will be processed, + // and EFI_INTERRUPT_PENDING will be returned + // + return EFI_INTERRUPT_PENDING; + + case EFI_SUCCESS: + // + // If handler return EFI_SUCCESS, the interrupt was handled and quiesced, + // no other handlers should still be called, + // and EFI_WARN_INTERRUPT_SOURCE_QUIESCED will be returned + // + return EFI_WARN_INTERRUPT_SOURCE_QUIESCED; + + case EFI_WARN_INTERRUPT_SOURCE_QUIESCED: + // + // If at least one of the handlers report EFI_WARN_INTERRUPT_SOURCE_QUIESCED, + // then this function will return EFI_WARN_INTERRUPT_SOURCE_QUIESCED + // + InterruptQuiesced = TRUE; + break; + + default: + break; + } + } + + if (InterruptQuiesced) { + Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED; + } else { + // + // If no handler report EFI_WARN_INTERRUPT_SOURCE_QUIESCED, then this + // function will return EFI_INTERRUPT_PENDING + // + Status = EFI_INTERRUPT_PENDING; + } + return Status; +} + +/** + Registers a handler to execute within SMM. + + @param Handler Handler service funtion pointer. + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function. + + @retval EFI_SUCCESS Handler register success. + @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerRegister ( + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN CONST EFI_GUID *HandlerType OPTIONAL, + OUT EFI_HANDLE *DispatchHandle + ) +{ + SMI_HANDLER *SmiHandler; + SMI_ENTRY *SmiEntry; + LIST_ENTRY *List; + + if (Handler == NULL || DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER)); + if (SmiHandler == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SmiHandler->Signature = SMI_HANDLER_SIGNATURE; + SmiHandler->Handler = Handler; + + if (HandlerType == NULL) { + // + // This is root SMI handler + // + SmiEntry = NULL; + List = &mRootSmiHandlerList; + } else { + // + // None root SMI handler + // + SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE); + if (SmiEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + List = &SmiEntry->SmiHandlers; + } + + SmiHandler->SmiEntry = SmiEntry; + InsertTailList (List, &SmiHandler->Link); + + *DispatchHandle = (EFI_HANDLE) SmiHandler; + + return EFI_SUCCESS; +} + +/** + Unregister a handler in SMM. + + @param DispatchHandle The handle that was specified when the handler was registered. + + @retval EFI_SUCCESS Handler function was successfully unregistered. + @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerUnRegister ( + IN EFI_HANDLE DispatchHandle + ) +{ + SMI_HANDLER *SmiHandler; + SMI_ENTRY *SmiEntry; + + SmiHandler = (SMI_HANDLER *) DispatchHandle; + + if (SmiHandler == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (SmiHandler->Signature != SMI_HANDLER_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + SmiEntry = SmiHandler->SmiEntry; + + RemoveEntryList (&SmiHandler->Link); + FreePool (SmiHandler); + + if (SmiEntry == NULL) { + // + // This is root SMI handler + // + return EFI_SUCCESS; + } + + if (IsListEmpty (&SmiEntry->SmiHandlers)) { + // + // No handler registered for this interrupt now, remove the SMI_ENTRY + // + RemoveEntryList (&SmiEntry->AllEntries); + + FreePool (SmiEntry); + } + + return EFI_SUCCESS; +} -- 2.39.2