diff --git a/StandaloneMmPkg/Core/Dependency.c b/StandaloneMmPkg/Core/Dependency.c new file mode 100644 index 0000000000..365007d8ae --- /dev/null +++ b/StandaloneMmPkg/Core/Dependency.c @@ -0,0 +1,389 @@ +/** @file + MM 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.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +/// +/// EFI_DEP_REPLACE_TRUE - Used to dynamically patch the dependency expression +/// to save time. A EFI_DEP_PUSH is evaluated 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. 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 +MmIsSchedulable ( + IN EFI_MM_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 MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter () + // processes them. + // + return FALSE; + } + + DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); + + if (DriverEntry->Depex == NULL) { + // + // A NULL Depex means that the MM driver is not built correctly. + // All MM drivers must have a valid depex expressiion. + // + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Depex is empty)\n")); + 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) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Attempt to fetch past end of depex)\n")); + 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. + // + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected BEFORE or AFTER opcode)\n")); + ASSERT (FALSE); + + 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 = MmLocateProtocol (&DriverGuid, NULL, &Interface); + if (EFI_ERROR (Status) && (mEfiSystemTable != NULL)) { + // + // For MM Driver, it may depend on uefi protocols + // + Status = mEfiSystemTable->BootServices->LocateProtocol (&DriverGuid, NULL, &Interface); + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " PUSH GUID(%g) = FALSE\n", &DriverGuid)); + Status = PushBool (FALSE); + } else { + DEBUG ((DEBUG_DISPATCH, " PUSH GUID(%g) = TRUE\n", &DriverGuid)); + *Iterator = EFI_DEP_REPLACE_TRUE; + Status = PushBool (TRUE); + } + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Iterator += sizeof (EFI_GUID); + break; + + case EFI_DEP_AND: + DEBUG ((DEBUG_DISPATCH, " AND\n")); + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Status = PopBool (&Operator2); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Status = PushBool ((BOOLEAN)(Operator && Operator2)); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + break; + + case EFI_DEP_OR: + DEBUG ((DEBUG_DISPATCH, " OR\n")); + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Status = PopBool (&Operator2); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Status = PushBool ((BOOLEAN)(Operator || Operator2)); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + break; + + case EFI_DEP_NOT: + DEBUG ((DEBUG_DISPATCH, " NOT\n")); + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Status = PushBool ((BOOLEAN)(!Operator)); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + break; + + case EFI_DEP_TRUE: + DEBUG ((DEBUG_DISPATCH, " TRUE\n")); + Status = PushBool (TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + break; + + case EFI_DEP_FALSE: + DEBUG ((DEBUG_DISPATCH, " FALSE\n")); + Status = PushBool (FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + break; + + case EFI_DEP_END: + DEBUG ((DEBUG_DISPATCH, " END\n")); + Status = PopBool (&Operator); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + DEBUG ((DEBUG_DISPATCH, " RESULT = %a\n", Operator ? "TRUE" : "FALSE")); + return Operator; + + case EFI_DEP_REPLACE_TRUE: + CopyMem (&DriverGuid, Iterator + 1, sizeof (EFI_GUID)); + DEBUG ((DEBUG_DISPATCH, " PUSH GUID(%g) = TRUE\n", &DriverGuid)); + Status = PushBool (TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unexpected error)\n")); + return FALSE; + } + + Iterator += sizeof (EFI_GUID); + break; + + default: + DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE (Unknown opcode)\n")); + 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/StandaloneMmPkg/Core/Dispatcher.c b/StandaloneMmPkg/Core/Dispatcher.c new file mode 100644 index 0000000000..8d009b4f80 --- /dev/null +++ b/StandaloneMmPkg/Core/Dispatcher.c @@ -0,0 +1,1071 @@ +/** @file + MM Driver Dispatcher. + + Step #1 - When a FV protocol is added to the system every driver in the FV + is added to the mDiscoveredList. The 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. + + Copyright (c) 2014, Hewlett-Packard Development Company, L.P. + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +// +// MM 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 +// + +EFI_STATUS +MmCoreFfsFindMmDriver ( + IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader + ); + +/** + 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 +MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( + IN EFI_MM_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_MM_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_MM_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 MM Dispacher. TRUE if dispatcher is execuing. +// +BOOLEAN gDispatcherRunning = FALSE; + +// +// Flag for the MM Dispacher. TRUE if there is one or more MM drivers ready to be dispatched +// +BOOLEAN gRequestDispatch = FALSE; + +// +// The global variable is defined for Loading modules at fixed address feature to track the MM code +// memory range usage. It is a bit mapped array in which every bit indicates the correspoding +// memory page available or not. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT64 *mMmCodeMemoryRangeUsageBitMap=NULL; + +/** + To check memory usage bit map array to figure out if the memory range in which the image will be loaded + is available or not. If memory range is avaliable, the function will mark the correponding bits to 1 + which indicates the memory range is used. The function is only invoked when load modules at fixed address + feature is enabled. + + @param ImageBase The base addres the image will be loaded at. + @param ImageSize The size of the image + + @retval EFI_SUCCESS The memory range the image will be loaded in is available + @retval EFI_NOT_FOUND The memory range the image will be loaded in is not available +**/ +EFI_STATUS +CheckAndMarkFixLoadingMemoryUsageBitMap ( + IN EFI_PHYSICAL_ADDRESS ImageBase, + IN UINTN ImageSize + ) +{ + UINT32 MmCodePageNumber; + UINT64 MmCodeSize; + EFI_PHYSICAL_ADDRESS MmCodeBase; + UINTN BaseOffsetPageNumber; + UINTN TopOffsetPageNumber; + UINTN Index; + + // + // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressMmCodePageNumber + // + MmCodePageNumber = 0; + MmCodeSize = EFI_PAGES_TO_SIZE (MmCodePageNumber); + MmCodeBase = gLoadModuleAtFixAddressMmramBase; + + // + // If the memory usage bit map is not initialized, do it. Every bit in the array + // indicate the status of the corresponding memory page, available or not + // + if (mMmCodeMemoryRangeUsageBitMap == NULL) { + mMmCodeMemoryRangeUsageBitMap = AllocateZeroPool (((MmCodePageNumber / 64) + 1) * sizeof (UINT64)); + } + + // + // If the Dxe code memory range is not allocated or the bit map array allocation failed, return EFI_NOT_FOUND + // + if (mMmCodeMemoryRangeUsageBitMap == NULL) { + return EFI_NOT_FOUND; + } + + // + // see if the memory range for loading the image is in the MM code range. + // + if (MmCodeBase + MmCodeSize < ImageBase + ImageSize || MmCodeBase > ImageBase) { + return EFI_NOT_FOUND; + } + + // + // Test if the memory is avalaible or not. + // + BaseOffsetPageNumber = (UINTN)EFI_SIZE_TO_PAGES ((UINT32)(ImageBase - MmCodeBase)); + TopOffsetPageNumber = (UINTN)EFI_SIZE_TO_PAGES ((UINT32)(ImageBase + ImageSize - MmCodeBase)); + for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) { + if ((mMmCodeMemoryRangeUsageBitMap[Index / 64] & LShiftU64 (1, (Index % 64))) != 0) { + // + // This page is already used. + // + return EFI_NOT_FOUND; + } + } + + // + // Being here means the memory range is available. So mark the bits for the memory range + // + for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) { + mMmCodeMemoryRangeUsageBitMap[Index / 64] |= LShiftU64 (1, (Index % 64)); + } + return EFI_SUCCESS; +} + +/** + Get the fixed loading address from image header assigned by build tool. This function only be called + when Loading module at Fixed address feature enabled. + + @param ImageContext Pointer to the image context structure that describes the PE/COFF + image that needs to be examined by this function. + @retval EFI_SUCCESS An fixed loading address is assigned to this image by build tools . + @retval EFI_NOT_FOUND The image has no assigned fixed loadding address. + +**/ +EFI_STATUS +GetPeCoffImageFixLoadingAssignedAddress( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + UINTN SectionHeaderOffset; + EFI_STATUS Status; + EFI_IMAGE_SECTION_HEADER SectionHeader; + EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr; + EFI_PHYSICAL_ADDRESS FixLoadingAddress; + UINT16 Index; + UINTN Size; + UINT16 NumberOfSections; + UINT64 ValueInSectionHeader; + + FixLoadingAddress = 0; + Status = EFI_NOT_FOUND; + + // + // Get PeHeader pointer + // + ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8* )ImageContext->Handle + ImageContext->PeCoffHeaderOffset); + SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + + ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader; + NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections; + + // + // Get base address from the first section header that doesn't point to code section. + // + for (Index = 0; Index < NumberOfSections; Index++) { + // + // Read section header from file + // + Size = sizeof (EFI_IMAGE_SECTION_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + SectionHeaderOffset, + &Size, + &SectionHeader + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EFI_NOT_FOUND; + + if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) { + // + // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields + // in the first section header that doesn't point to code section in image header. So there + // is an assumption that when the feature is enabled, if a module with a loading address + // assigned by tools, the PointerToRelocations & PointerToLineNumbers fields should not be + // Zero, or else, these 2 fields should be set to Zero + // + ValueInSectionHeader = ReadUnaligned64 ((UINT64*)&SectionHeader.PointerToRelocations); + if (ValueInSectionHeader != 0) { + // + // Found first section header that doesn't point to code section in which build tool saves the + // offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields + // + FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressMmramBase + (INT64)ValueInSectionHeader); + // + // Check if the memory range is available. + // + Status = CheckAndMarkFixLoadingMemoryUsageBitMap (FixLoadingAddress, (UINTN)(ImageContext->ImageSize + ImageContext->SectionAlignment)); + if (!EFI_ERROR(Status)) { + // + // The assigned address is valid. Return the specified loading address + // + ImageContext->ImageAddress = FixLoadingAddress; + } + } + break; + } + SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); + } + DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r\n", + FixLoadingAddress, Status)); + return Status; +} +/** + Loads an EFI image into SMRAM. + + @param DriverEntry EFI_MM_DRIVER_ENTRY instance + + @return EFI_STATUS + +**/ +EFI_STATUS +EFIAPI +MmLoadImage ( + IN OUT EFI_MM_DRIVER_ENTRY *DriverEntry + ) +{ + VOID *Buffer; + UINTN PageCount; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS DstBuffer; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + DEBUG ((DEBUG_INFO, "MmLoadImage - %g\n", &DriverEntry->FileName)); + + Buffer = AllocateCopyPool (DriverEntry->Pe32DataSize, DriverEntry->Pe32Data); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_SUCCESS; + + // + // 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) { + MmFreePool (Buffer); + } + return Status; + } + + PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); + DstBuffer = (UINTN)(-1); + + Status = MmAllocatePages ( + AllocateMaxAddress, + EfiRuntimeServicesCode, + PageCount, + &DstBuffer + ); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + MmFreePool (Buffer); + } + return Status; + } + + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; + + // + // Align buffer on section boundry + // + ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; + ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1)); + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + MmFreePool (Buffer); + } + MmFreePages (DstBuffer, PageCount); + return Status; + } + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + MmFreePool (Buffer); + } + MmFreePages (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; + + if (mEfiSystemTable != NULL) { + Status = mEfiSystemTable->BootServices->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_LOADED_IMAGE_PROTOCOL), + (VOID **)&DriverEntry->LoadedImage + ); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + MmFreePool (Buffer); + } + MmFreePages (DstBuffer, PageCount); + return Status; + } + + ZeroMem (DriverEntry->LoadedImage, sizeof (EFI_LOADED_IMAGE_PROTOCOL)); + // + // 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 = NULL; + DriverEntry->LoadedImage->SystemTable = mEfiSystemTable; + DriverEntry->LoadedImage->DeviceHandle = NULL; + DriverEntry->LoadedImage->FilePath = NULL; + + 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 MM Driver + // + DriverEntry->ImageHandle = NULL; + Status = mEfiSystemTable->BootServices->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 MM 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)); + } + 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 + // + MmFreePool (Buffer); + return Status; +} + +/** + Preprocess dependency expression and update DriverEntry to reflect the + state of Before and After dependencies. If DriverEntry->Before + or DriverEntry->After is set it will never be cleared. + + @param DriverEntry DriverEntry element to update . + + @retval EFI_SUCCESS It always works. + +**/ +EFI_STATUS +MmPreProcessDepex ( + IN EFI_MM_DRIVER_ENTRY *DriverEntry + ) +{ + UINT8 *Iterator; + + Iterator = DriverEntry->Depex; + 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 +MmGetDepexSectionAndPreProccess ( + IN EFI_MM_DRIVER_ENTRY *DriverEntry + ) +{ + EFI_STATUS Status; + + // + // Data already read + // + if (DriverEntry->Depex == NULL) { + Status = EFI_NOT_FOUND; + } else { + Status = EFI_SUCCESS; + } + 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 and After state information based on Depex + // Driver will be put in Dependent state + // + MmPreProcessDepex (DriverEntry); + DriverEntry->DepexProtocolError = FALSE; + } + + return Status; +} + +/** + This is the main Dispatcher for MM 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. + + @retval EFI_SUCCESS All of the MM Drivers that could be dispatched + have been run and the MM Entry Point has been + registered. + @retval EFI_NOT_READY The MM Driver that registered the MM Entry Point + was just dispatched. + @retval EFI_NOT_FOUND There are no MM Drivers available to be dispatched. + @retval EFI_ALREADY_STARTED The MM Dispatcher is already running + +**/ +EFI_STATUS +MmDispatcher ( + VOID + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + EFI_MM_DRIVER_ENTRY *DriverEntry; + BOOLEAN ReadyToRun; + BOOLEAN PreviousMmEntryPointRegistered; + + DEBUG ((DEBUG_INFO, "MmDispatcher\n")); + + if (!gRequestDispatch) { + DEBUG ((DEBUG_INFO, " !gRequestDispatch\n")); + return EFI_NOT_FOUND; + } + + if (gDispatcherRunning) { + DEBUG ((DEBUG_INFO, " gDispatcherRunning\n")); + // + // If the dispatcher is running don't let it be restarted. + // + return EFI_ALREADY_STARTED; + } + + gDispatcherRunning = TRUE; + + do { + // + // Drain the Scheduled Queue + // + DEBUG ((DEBUG_INFO, " Drain the Scheduled Queue\n")); + while (!IsListEmpty (&mScheduledQueue)) { + DriverEntry = CR ( + mScheduledQueue.ForwardLink, + EFI_MM_DRIVER_ENTRY, + ScheduledLink, + EFI_MM_DRIVER_ENTRY_SIGNATURE + ); + DEBUG ((DEBUG_INFO, " DriverEntry (Scheduled) - %g\n", &DriverEntry->FileName)); + + // + // Load the MM 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 = MmLoadImage (DriverEntry); + + // + // Update the driver state to reflect that it's been loaded + // + if (EFI_ERROR (Status)) { + // + // The MM Driver could not be loaded, and do not attempt to load or start it again. + // Take driver from Scheduled to Initialized. + // + 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); + + // + // Cache state of MmEntryPointRegistered before calling entry point + // + PreviousMmEntryPointRegistered = gMmCorePrivate->MmEntryPointRegistered; + + // + // For each MM driver, pass NULL as ImageHandle + // + if (mEfiSystemTable == NULL) { + DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Standalone Mode)\n", DriverEntry->ImageEntryPoint)); + Status = ((MM_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (DriverEntry->ImageHandle, &gMmCoreMmst); + } else { + DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Tradition Mode)\n", DriverEntry->ImageEntryPoint)); + Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) ( + DriverEntry->ImageHandle, + mEfiSystemTable + ); + } + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "StartImage Status - %r\n", Status)); + MmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage); + } + + if (!PreviousMmEntryPointRegistered && gMmCorePrivate->MmEntryPointRegistered) { + // + // Return immediately if the MM Entry Point was registered by the MM + // Driver that was just dispatched. The MM IPL will reinvoke the MM + // Core Dispatcher. This is required so MM Mode may be enabled as soon + // as all the dependent MM Drivers for MM Mode have been dispatched. + // Once the MM Entry Point has been registered, then MM Mode will be + // used. + // + gRequestDispatch = TRUE; + gDispatcherRunning = FALSE; + return EFI_NOT_READY; + } + } + + // + // Search DriverList for items to place on Scheduled Queue + // + DEBUG ((DEBUG_INFO, " Search DriverList for items to place on Scheduled Queue\n")); + ReadyToRun = FALSE; + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE); + DEBUG ((DEBUG_INFO, " DriverEntry (Discovered) - %g\n", &DriverEntry->FileName)); + + if (DriverEntry->DepexProtocolError) { + // + // If Section Extraction Protocol did not let the Depex be read before retry the read + // + Status = MmGetDepexSectionAndPreProccess (DriverEntry); + } + + if (DriverEntry->Dependent) { + if (MmIsSchedulable (DriverEntry)) { + MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + ReadyToRun = TRUE; + } + } + } + } while (ReadyToRun); + + // + // If there is no more MM driver to dispatch, stop the dispatch request + // + DEBUG ((DEBUG_INFO, " no more MM driver to dispatch, stop the dispatch request\n")); + gRequestDispatch = FALSE; + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE); + DEBUG ((DEBUG_INFO, " DriverEntry (Discovered) - %g\n", &DriverEntry->FileName)); + + if (!DriverEntry->Initialized) { + // + // We have MM driver pending to dispatch + // + gRequestDispatch = TRUE; + break; + } + } + + gDispatcherRunning = FALSE; + + return EFI_SUCCESS; +} + +/** + 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 +MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( + IN EFI_MM_DRIVER_ENTRY *InsertedDriverEntry + ) +{ + LIST_ENTRY *Link; + EFI_MM_DRIVER_ENTRY *DriverEntry; + + // + // Process Before Dependency + // + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->Before && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) { + DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); + DEBUG ((DEBUG_DISPATCH, " BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid)); + if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { + // + // Recursively process BEFORE + // + DEBUG ((DEBUG_DISPATCH, "TRUE\n END\n RESULT = TRUE\n")); + MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + } else { + DEBUG ((DEBUG_DISPATCH, "FALSE\n END\n RESULT = FALSE\n")); + } + } + } + + // + // 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_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->After && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) { + DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); + DEBUG ((DEBUG_DISPATCH, " AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid)); + if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { + // + // Recursively process AFTER + // + DEBUG ((DEBUG_DISPATCH, "TRUE\n END\n RESULT = TRUE\n")); + MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + } else { + DEBUG ((DEBUG_DISPATCH, "FALSE\n END\n RESULT = FALSE\n")); + } + } + } +} + +/** + 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; + + DEBUG ((DEBUG_INFO, "FvIsBeingProcesssed - 0x%08x\n", FvHandle)); + + KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE)); + ASSERT (KnownHandle != NULL); + + KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE; + KnownHandle->Handle = FvHandle; + InsertTailList (&mFvHandleList, &KnownHandle->Link); +} + +/** + 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 Before and After state. + The Discovered list is never free'ed and contains booleans that represent the + other possible MM driver states. + + @param Fv Fv protocol, needed to read Depex info out of + FLASH. + @param FvHandle Handle for Fv, needed in the + EFI_MM_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 +MmAddToDriverList ( + IN EFI_HANDLE FvHandle, + IN VOID *Pe32Data, + IN UINTN Pe32DataSize, + IN VOID *Depex, + IN UINTN DepexSize, + IN EFI_GUID *DriverName + ) +{ + EFI_MM_DRIVER_ENTRY *DriverEntry; + + DEBUG ((DEBUG_INFO, "MmAddToDriverList - %g (0x%08x)\n", DriverName, Pe32Data)); + + // + // Create the Driver Entry for the list. ZeroPool initializes lots of variables to + // NULL or FALSE. + // + DriverEntry = AllocateZeroPool (sizeof (EFI_MM_DRIVER_ENTRY)); + ASSERT (DriverEntry != NULL); + + DriverEntry->Signature = EFI_MM_DRIVER_ENTRY_SIGNATURE; + CopyGuid (&DriverEntry->FileName, DriverName); + DriverEntry->FvHandle = FvHandle; + DriverEntry->Pe32Data = Pe32Data; + DriverEntry->Pe32DataSize = Pe32DataSize; + DriverEntry->Depex = Depex; + DriverEntry->DepexSize = DepexSize; + + MmGetDepexSectionAndPreProccess (DriverEntry); + + InsertTailList (&mDiscoveredList, &DriverEntry->Link); + gRequestDispatch = TRUE; + + return EFI_SUCCESS; +} + +/** + This function is the main entry point for an MM 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 MmLocateHandle () 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 MM 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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmDriverDispatchHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "MmDriverDispatchHandler\n")); + + // + // Execute the MM Dispatcher on any newly discovered FVs and previously + // discovered MM drivers that have been discovered but not dispatched. + // + Status = MmDispatcher (); + + // + // Check to see if CommBuffer and CommBufferSize are valid + // + if (CommBuffer != NULL && CommBufferSize != NULL) { + if (*CommBufferSize > 0) { + if (Status == EFI_NOT_READY) { + // + // If a the MM Core Entry Point was just registered, then set flag to + // request the MM Dispatcher to be restarted. + // + *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_RESTART; + } else if (!EFI_ERROR (Status)) { + // + // Set the flag to show that the MM Dispatcher executed without errors + // + *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_SUCCESS; + } else { + // + // Set the flag to show that the MM Dispatcher encountered an error + // + *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_ERROR; + } + } + } + + return EFI_SUCCESS; +} + +/** + This function is the main entry point for an MM 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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmFvDispatchHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_FV_DISPATCH_DATA *CommunicationFvDispatchData; + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + + DEBUG ((DEBUG_INFO, "MmFvDispatchHandler\n")); + + CommunicationFvDispatchData = CommBuffer; + + DEBUG ((DEBUG_INFO, " Dispatch - 0x%016lx - 0x%016lx\n", CommunicationFvDispatchData->Address, + CommunicationFvDispatchData->Size)); + + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)CommunicationFvDispatchData->Address; + + MmCoreFfsFindMmDriver (FwVolHeader); + + // + // Execute the MM Dispatcher on any newly discovered FVs and previously + // discovered MM drivers that have been discovered but not dispatched. + // + Status = MmDispatcher (); + + return Status; +} + +/** + Traverse the discovered list for any drivers that were discovered but not loaded + because the dependency experessions evaluated to false. + +**/ +VOID +MmDisplayDiscoveredNotDispatched ( + VOID + ) +{ + LIST_ENTRY *Link; + EFI_MM_DRIVER_ENTRY *DriverEntry; + + for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->Dependent) { + DEBUG ((DEBUG_LOAD, "MM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName)); + } + } +} diff --git a/StandaloneMmPkg/Core/FwVol.c b/StandaloneMmPkg/Core/FwVol.c new file mode 100644 index 0000000000..5abf98c247 --- /dev/null +++ b/StandaloneMmPkg/Core/FwVol.c @@ -0,0 +1,104 @@ +/**@file + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" +#include + +// +// List of file types supported by dispatcher +// +EFI_FV_FILETYPE mMmFileTypes[] = { + EFI_FV_FILETYPE_MM, + 0xE, //EFI_FV_FILETYPE_MM_STANDALONE, + // + // Note: DXE core will process the FV image file, so skip it in MM core + // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE + // +}; + +EFI_STATUS +MmAddToDriverList ( + IN EFI_HANDLE FvHandle, + IN VOID *Pe32Data, + IN UINTN Pe32DataSize, + IN VOID *Depex, + IN UINTN DepexSize, + IN EFI_GUID *DriverName + ); + +BOOLEAN +FvHasBeenProcessed ( + IN EFI_HANDLE FvHandle + ); + +VOID +FvIsBeingProcesssed ( + IN EFI_HANDLE FvHandle + ); + +EFI_STATUS +MmCoreFfsFindMmDriver ( + IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader + ) +/*++ + +Routine Description: + Given the pointer to the Firmware Volume Header find the + MM driver and return it's PE32 image. + +Arguments: + FwVolHeader - Pointer to memory mapped FV + +Returns: + other - Failure + +--*/ +{ + EFI_STATUS Status; + EFI_STATUS DepexStatus; + EFI_FFS_FILE_HEADER *FileHeader; + EFI_FV_FILETYPE FileType; + VOID *Pe32Data; + UINTN Pe32DataSize; + VOID *Depex; + UINTN DepexSize; + UINTN Index; + + DEBUG ((DEBUG_INFO, "MmCoreFfsFindMmDriver - 0x%x\n", FwVolHeader)); + + if (FvHasBeenProcessed (FwVolHeader)) { + return EFI_SUCCESS; + } + + FvIsBeingProcesssed (FwVolHeader); + + for (Index = 0; Index < sizeof (mMmFileTypes) / sizeof (mMmFileTypes[0]); Index++) { + DEBUG ((DEBUG_INFO, "Check MmFileTypes - 0x%x\n", mMmFileTypes[Index])); + FileType = mMmFileTypes[Index]; + FileHeader = NULL; + do { + Status = FfsFindNextFile (FileType, FwVolHeader, &FileHeader); + if (!EFI_ERROR (Status)) { + Status = FfsFindSectionData (EFI_SECTION_PE32, FileHeader, &Pe32Data, &Pe32DataSize); + DEBUG ((DEBUG_INFO, "Find PE data - 0x%x\n", Pe32Data)); + DepexStatus = FfsFindSectionData (EFI_SECTION_MM_DEPEX, FileHeader, &Depex, &DepexSize); + if (!EFI_ERROR (DepexStatus)) { + MmAddToDriverList (FwVolHeader, Pe32Data, Pe32DataSize, Depex, DepexSize, &FileHeader->Name); + } + } + } while (!EFI_ERROR (Status)); + } + + return Status; +} diff --git a/StandaloneMmPkg/Core/Handle.c b/StandaloneMmPkg/Core/Handle.c new file mode 100644 index 0000000000..70d59fe15d --- /dev/null +++ b/StandaloneMmPkg/Core/Handle.c @@ -0,0 +1,533 @@ +/** @file + SMM handle & protocol handling. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.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 +MmValidateHandle ( + 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 * +MmFindProtocolEntry ( + 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 * +MmFindProtocolInterface ( + 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 = MmFindProtocolEntry (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 MmInstallProtocolInterfaceNotify. 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 +MmInstallProtocolInterface ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface + ) +{ + return MmInstallProtocolInterfaceNotify ( + 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 +MmInstallProtocolInterfaceNotify ( + 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, "MmInstallProtocolInterface: %g %p\n", Protocol, Interface)); + + Status = EFI_OUT_OF_RESOURCES; + Prot = NULL; + Handle = NULL; + + if (*UserHandle != NULL) { + Status = MmHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface); + if (!EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Lookup the Protocol Entry for the requested protocol + // + ProtEntry = MmFindProtocolEntry (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 = MmValidateHandle (Handle); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Each interface that is added must be unique + // + ASSERT (MmFindProtocolInterface (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) { + MmNotifyProtocol (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 +MmUninstallProtocolInterface ( + 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 = MmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check that Protocol exists on UserHandle, and Interface matches the interface in the database + // + Prot = MmFindProtocolInterface (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 = MmRemoveInterfaceFromProtocol (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 * +MmGetProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol + ) +{ + EFI_STATUS Status; + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_INTERFACE *Prot; + IHANDLE *Handle; + LIST_ENTRY *Link; + + Status = MmValidateHandle (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 +MmHandleProtocol ( + 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 = MmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Look at each protocol interface for a match + // + Prot = MmGetProtocolInterface (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/StandaloneMmPkg/Core/InstallConfigurationTable.c b/StandaloneMmPkg/Core/InstallConfigurationTable.c new file mode 100644 index 0000000000..2392be9958 --- /dev/null +++ b/StandaloneMmPkg/Core/InstallConfigurationTable.c @@ -0,0 +1,178 @@ +/** @file + System Management System Table Services MmInstallConfigurationTable service + + Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +#define CONFIG_TABLE_SIZE_INCREASED 0x10 + +UINTN mMmSystemTableAllocateSize = 0; + +/** + The MmInstallConfigurationTable() 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 +MmInstallConfigurationTable ( + IN CONST EFI_MM_SYSTEM_TABLE *SystemTable, + IN CONST EFI_GUID *Guid, + IN VOID *Table, + IN UINTN TableSize + ) +{ + UINTN Index; + EFI_CONFIGURATION_TABLE *ConfigurationTable; + EFI_CONFIGURATION_TABLE *OldTable; + + // + // If Guid is NULL, then this operation cannot be performed + // + if (Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConfigurationTable = gMmCoreMmst.MmConfigurationTable; + + // + // Search all the table for an entry that matches Guid + // + for (Index = 0; Index < gMmCoreMmst.NumberOfTableEntries; Index++) { + if (CompareGuid (Guid, &(ConfigurationTable[Index].VendorGuid))) { + break; + } + } + + if (Index < gMmCoreMmst.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 entry and return. + // + ConfigurationTable[Index].VendorTable = Table; + return EFI_SUCCESS; + } + + // + // A match was found and Table is NULL, so this is a delete operation. + // + gMmCoreMmst.NumberOfTableEntries--; + + // + // Copy over deleted entry + // + CopyMem ( + &(ConfigurationTable[Index]), + &(ConfigurationTable[Index + 1]), + (gMmCoreMmst.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 == gMmCoreMmst.NumberOfTableEntries + // + if ((Index * sizeof (EFI_CONFIGURATION_TABLE)) >= mMmSystemTableAllocateSize) { + // + // Allocate a table with one additional entry. + // + mMmSystemTableAllocateSize += (CONFIG_TABLE_SIZE_INCREASED * sizeof (EFI_CONFIGURATION_TABLE)); + ConfigurationTable = AllocatePool (mMmSystemTableAllocateSize); + if (ConfigurationTable == NULL) { + // + // If a new table could not be allocated, then return an error. + // + return EFI_OUT_OF_RESOURCES; + } + + if (gMmCoreMmst.MmConfigurationTable != NULL) { + // + // Copy the old table to the new table. + // + CopyMem ( + ConfigurationTable, + gMmCoreMmst.MmConfigurationTable, + Index * sizeof (EFI_CONFIGURATION_TABLE) + ); + + // + // Record the old table pointer. + // + OldTable = gMmCoreMmst.MmConfigurationTable; + + // + // As the MmInstallConfigurationTable() may be re-entered by FreePool() in + // its calling stack, updating System table to the new table pointer must + // be done before calling FreePool() to free the old table. + // It can make sure the gMmCoreMmst.MmConfigurationTable point to the new + // table and avoid the errors of use-after-free to the old table by the + // reenter of MmInstallConfigurationTable() in FreePool()'s calling stack. + // + gMmCoreMmst.MmConfigurationTable = ConfigurationTable; + + // + // Free the old table after updating System Table to the new table pointer. + // + FreePool (OldTable); + } else { + // + // Update System Table + // + gMmCoreMmst.MmConfigurationTable = 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 + // + gMmCoreMmst.NumberOfTableEntries++; + } + + // + // CRC-32 field is ignorable for SMM System Table and should be set to zero + // + + return EFI_SUCCESS; +} diff --git a/StandaloneMmPkg/Core/Locate.c b/StandaloneMmPkg/Core/Locate.c new file mode 100644 index 0000000000..9129718723 --- /dev/null +++ b/StandaloneMmPkg/Core/Locate.c @@ -0,0 +1,496 @@ +/** @file + Locate handle functions + + Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.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 * +MmGetNextLocateAllHandles ( + 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 * +MmGetNextLocateByRegisterNotify ( + 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 * +MmGetNextLocateByProtocol ( + 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 +MmLocateProtocol ( + 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) || (Protocol == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *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 = MmFindProtocolEntry (Protocol, FALSE); + if (Position.ProtEntry == NULL) { + return EFI_NOT_FOUND; + } + Position.Position = &Position.ProtEntry->Protocols; + + Handle = MmGetNextLocateByProtocol (&Position, Interface); + } else { + Handle = MmGetNextLocateByRegisterNotify (&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 +MmLocateHandle ( + 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 = MmGetNextLocateAllHandles; + break; + + case ByRegisterNotify: + GetNext = MmGetNextLocateByRegisterNotify; + // + // Must have SearchKey for locate ByRegisterNotify + // + if (SearchKey == NULL) { + Status = EFI_INVALID_PARAMETER; + } + break; + + case ByProtocol: + GetNext = MmGetNextLocateByProtocol; + if (Protocol == NULL) { + Status = EFI_INVALID_PARAMETER; + break; + } + // + // Look up the protocol entry and set the head pointer + // + Position.ProtEntry = MmFindProtocolEntry (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)) { + ASSERT (SearchKey != NULL); + // + // 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 MmLocateHandle() + 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 parameters are not valid. + +**/ +EFI_STATUS +EFIAPI +MmLocateHandleBuffer ( + 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 = MmLocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + // + // LocateHandleBuffer() returns incorrect status code if SearchType is + // invalid. + // + // Add code to correctly handle expected errors from MmLocateHandle(). + // + 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 = MmLocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + + *NumberHandles = BufferSize / sizeof(EFI_HANDLE); + if (EFI_ERROR (Status)) { + *NumberHandles = 0; + } + + return Status; +} diff --git a/StandaloneMmPkg/Core/Mmi.c b/StandaloneMmPkg/Core/Mmi.c new file mode 100644 index 0000000000..7193ebcd15 --- /dev/null +++ b/StandaloneMmPkg/Core/Mmi.c @@ -0,0 +1,337 @@ +/** @file + MMI management. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +// +// MM_HANDLER_STATE_NOTIFIER +// + +// +// MM_HANDLER - used for each MM handler +// + +#define MMI_ENTRY_SIGNATURE SIGNATURE_32('m','m','i','e') + +typedef struct { + UINTN Signature; + LIST_ENTRY AllEntries; // All entries + + EFI_GUID HandlerType; // Type of interrupt + LIST_ENTRY MmiHandlers; // All handlers +} MMI_ENTRY; + +#define MMI_HANDLER_SIGNATURE SIGNATURE_32('m','m','i','h') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; // Link on MMI_ENTRY.MmiHandlers + EFI_MM_HANDLER_ENTRY_POINT Handler; // The mm handler's entry point + MMI_ENTRY *MmiEntry; +} MMI_HANDLER; + +LIST_ENTRY mRootMmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootMmiHandlerList); +LIST_ENTRY mMmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mMmiEntryList); + +/** + Finds the MMI entry for the requested handler type. + + @param HandlerType The type of the interrupt + @param Create Create a new entry if not found + + @return MMI entry + +**/ +MMI_ENTRY * +EFIAPI +MmCoreFindMmiEntry ( + IN EFI_GUID *HandlerType, + IN BOOLEAN Create + ) +{ + LIST_ENTRY *Link; + MMI_ENTRY *Item; + MMI_ENTRY *MmiEntry; + + // + // Search the MMI entry list for the matching GUID + // + MmiEntry = NULL; + for (Link = mMmiEntryList.ForwardLink; + Link != &mMmiEntryList; + Link = Link->ForwardLink) { + + Item = CR (Link, MMI_ENTRY, AllEntries, MMI_ENTRY_SIGNATURE); + if (CompareGuid (&Item->HandlerType, HandlerType)) { + // + // This is the MMI entry + // + MmiEntry = Item; + break; + } + } + + // + // If the protocol entry was not found and Create is TRUE, then + // allocate a new entry + // + if ((MmiEntry == NULL) && Create) { + MmiEntry = AllocatePool (sizeof (MMI_ENTRY)); + if (MmiEntry != NULL) { + // + // Initialize new MMI entry structure + // + MmiEntry->Signature = MMI_ENTRY_SIGNATURE; + CopyGuid ((VOID *)&MmiEntry->HandlerType, HandlerType); + InitializeListHead (&MmiEntry->MmiHandlers); + + // + // Add it to MMI entry list + // + InsertTailList (&mMmiEntryList, &MmiEntry->AllEntries); + } + } + return MmiEntry; +} + +/** + Manage MMI of a particular type. + + @param HandlerType Points to the handler type or NULL for root MMI 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_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was processed successfully but not quiesced. + @retval EFI_INTERRUPT_PENDING One or more MMI sources could not be quiesced. + @retval EFI_NOT_FOUND Interrupt source was not handled or quiesced. + @retval EFI_SUCCESS Interrupt source was handled and quiesced. + +**/ +EFI_STATUS +EFIAPI +MmiManage ( + 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; + MMI_ENTRY *MmiEntry; + MMI_HANDLER *MmiHandler; + BOOLEAN SuccessReturn; + EFI_STATUS Status; + + Status = EFI_NOT_FOUND; + SuccessReturn = FALSE; + if (HandlerType == NULL) { + // + // Root MMI handler + // + + Head = &mRootMmiHandlerList; + } else { + // + // Non-root MMI handler + // + MmiEntry = MmCoreFindMmiEntry ((EFI_GUID *) HandlerType, FALSE); + if (MmiEntry == NULL) { + // + // There is no handler registered for this interrupt source + // + return Status; + } + + Head = &MmiEntry->MmiHandlers; + } + + for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { + MmiHandler = CR (Link, MMI_HANDLER, Link, MMI_HANDLER_SIGNATURE); + + Status = MmiHandler->Handler ( + (EFI_HANDLE) MmiHandler, + Context, + CommBuffer, + CommBufferSize + ); + + switch (Status) { + case EFI_INTERRUPT_PENDING: + // + // If a handler returns EFI_INTERRUPT_PENDING and HandlerType is not NULL then + // no additional handlers will be processed and EFI_INTERRUPT_PENDING will be returned. + // + if (HandlerType != NULL) { + return EFI_INTERRUPT_PENDING; + } + break; + + case EFI_SUCCESS: + // + // If at least one of the handlers returns EFI_SUCCESS then the function will return + // EFI_SUCCESS. If a handler returns EFI_SUCCESS and HandlerType is not NULL then no + // additional handlers will be processed. + // + if (HandlerType != NULL) { + return EFI_SUCCESS; + } + SuccessReturn = TRUE; + break; + + case EFI_WARN_INTERRUPT_SOURCE_QUIESCED: + // + // If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED + // then the function will return EFI_SUCCESS. + // + SuccessReturn = TRUE; + break; + + case EFI_WARN_INTERRUPT_SOURCE_PENDING: + // + // If all the handlers returned EFI_WARN_INTERRUPT_SOURCE_PENDING + // then EFI_WARN_INTERRUPT_SOURCE_PENDING will be returned. + // + break; + + default: + // + // Unexpected status code returned. + // + ASSERT (FALSE); + break; + } + } + + if (SuccessReturn) { + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Registers a handler to execute within MM. + + @param Handler Handler service funtion pointer. + @param HandlerType Points to the handler type or NULL for root MMI 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 +MmiHandlerRegister ( + IN EFI_MM_HANDLER_ENTRY_POINT Handler, + IN CONST EFI_GUID *HandlerType OPTIONAL, + OUT EFI_HANDLE *DispatchHandle + ) +{ + MMI_HANDLER *MmiHandler; + MMI_ENTRY *MmiEntry; + LIST_ENTRY *List; + + if (Handler == NULL || DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + MmiHandler = AllocateZeroPool (sizeof (MMI_HANDLER)); + if (MmiHandler == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MmiHandler->Signature = MMI_HANDLER_SIGNATURE; + MmiHandler->Handler = Handler; + + if (HandlerType == NULL) { + // + // This is root MMI handler + // + MmiEntry = NULL; + List = &mRootMmiHandlerList; + } else { + // + // None root MMI handler + // + MmiEntry = MmCoreFindMmiEntry ((EFI_GUID *) HandlerType, TRUE); + if (MmiEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + List = &MmiEntry->MmiHandlers; + } + + MmiHandler->MmiEntry = MmiEntry; + InsertTailList (List, &MmiHandler->Link); + + *DispatchHandle = (EFI_HANDLE) MmiHandler; + + return EFI_SUCCESS; +} + +/** + Unregister a handler in MM. + + @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 +MmiHandlerUnRegister ( + IN EFI_HANDLE DispatchHandle + ) +{ + MMI_HANDLER *MmiHandler; + MMI_ENTRY *MmiEntry; + + MmiHandler = (MMI_HANDLER *) DispatchHandle; + + if (MmiHandler == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MmiHandler->Signature != MMI_HANDLER_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + MmiEntry = MmiHandler->MmiEntry; + + RemoveEntryList (&MmiHandler->Link); + FreePool (MmiHandler); + + if (MmiEntry == NULL) { + // + // This is root MMI handler + // + return EFI_SUCCESS; + } + + if (IsListEmpty (&MmiEntry->MmiHandlers)) { + // + // No handler registered for this interrupt now, remove the MMI_ENTRY + // + RemoveEntryList (&MmiEntry->AllEntries); + + FreePool (MmiEntry); + } + + return EFI_SUCCESS; +} diff --git a/StandaloneMmPkg/Core/Notify.c b/StandaloneMmPkg/Core/Notify.c new file mode 100644 index 0000000000..57a03fed5b --- /dev/null +++ b/StandaloneMmPkg/Core/Notify.c @@ -0,0 +1,203 @@ +/** @file + Support functions for UEFI protocol notification infrastructure. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +/** + Signal event for every protocol in protocol entry. + + @param Prot Protocol interface + +**/ +VOID +MmNotifyProtocol ( + 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 * +MmRemoveInterfaceFromProtocol ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_NOTIFY *ProtNotify; + PROTOCOL_ENTRY *ProtEntry; + LIST_ENTRY *Link; + + Prot = MmFindProtocolInterface (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_SUCCESS Successfully returned the registration record + that has been added or unhooked + @retval EFI_INVALID_PARAMETER Protocol is NULL or Registration is NULL + @retval EFI_OUT_OF_RESOURCES Not enough memory resource to finish the request + @retval EFI_NOT_FOUND If the registration is not found when Function == NULL + +**/ +EFI_STATUS +EFIAPI +MmRegisterProtocolNotify ( + IN CONST EFI_GUID *Protocol, + IN EFI_MM_NOTIFY_FN Function, + OUT VOID **Registration + ) +{ + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_NOTIFY *ProtNotify; + LIST_ENTRY *Link; + EFI_STATUS Status; + + if (Protocol == NULL || Registration == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Function == NULL) { + // + // Get the protocol entry per Protocol + // + ProtEntry = MmFindProtocolEntry ((EFI_GUID *) Protocol, FALSE); + if (ProtEntry != NULL) { + ProtNotify = (PROTOCOL_NOTIFY * )*Registration; + for (Link = ProtEntry->Notify.ForwardLink; + Link != &ProtEntry->Notify; + Link = Link->ForwardLink) { + // + // Compare the notification record + // + if (ProtNotify == (CR (Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE))) { + // + // If Registration is an existing registration, then unhook it + // + ProtNotify->Signature = 0; + RemoveEntryList (&ProtNotify->Link); + FreePool (ProtNotify); + return EFI_SUCCESS; + } + } + } + // + // If the registration is not found + // + return EFI_NOT_FOUND; + } + + ProtNotify = NULL; + + // + // Get the protocol entry to add the notification too + // + ProtEntry = MmFindProtocolEntry ((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/StandaloneMmPkg/Core/Page.c b/StandaloneMmPkg/Core/Page.c new file mode 100644 index 0000000000..8250f8c729 --- /dev/null +++ b/StandaloneMmPkg/Core/Page.c @@ -0,0 +1,384 @@ +/** @file + MM Memory page management functions. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +#define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ + ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size))) + +#define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT) + +LIST_ENTRY mMmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mMmMemoryMap); + +UINTN mMapKey; + +/** + 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 +MmInternalAllocatePages ( + 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 MM + // + RequestedAddress = (UINTN)*Memory; + switch (Type) { + case AllocateAnyPages: + RequestedAddress = (UINTN)(-1); + case AllocateMaxAddress: + *Memory = InternalAllocMaxAddress ( + &mMmMemoryMap, + NumberOfPages, + RequestedAddress + ); + if (*Memory == (UINTN)-1) { + return EFI_OUT_OF_RESOURCES; + } + break; + case AllocateAddress: + *Memory = InternalAllocAddress ( + &mMmMemoryMap, + NumberOfPages, + RequestedAddress + ); + if (*Memory != RequestedAddress) { + return EFI_NOT_FOUND; + } + break; + default: + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +/** + 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 +MmAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ) +{ + EFI_STATUS Status; + + Status = MmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory); + return Status; +} + +/** + 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 +MmInternalFreePages ( + 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 = mMmMemoryMap.ForwardLink; + while (Node != &mMmMemoryMap) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + if (Memory < (UINTN)Pages) { + break; + } + Node = Node->ForwardLink; + } + + if (Node != &mMmMemoryMap && + Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) { + return EFI_INVALID_PARAMETER; + } + + if (Node->BackLink != &mMmMemoryMap) { + 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 != &mMmMemoryMap) { + Pages = InternalMergeNodes ( + BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link) + ); + } + + if (Node != &mMmMemoryMap) { + InternalMergeNodes (Pages); + } + + return EFI_SUCCESS; +} + +/** + 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 +MmFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_STATUS Status; + + Status = MmInternalFreePages (Memory, NumberOfPages); + return Status; +} + +/** + Add free MMRAM 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 +MmAddMemoryRegion ( + IN EFI_PHYSICAL_ADDRESS MemBase, + IN UINT64 MemLength, + IN EFI_MEMORY_TYPE Type, + IN UINT64 Attributes + ) +{ + UINTN AlignedMemBase; + + // + // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization + // + if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { + return; + } + + // + // Align range on an EFI_PAGE_SIZE boundary + // + AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + MemLength -= AlignedMemBase - MemBase; + MmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength)); +} diff --git a/StandaloneMmPkg/Core/Pool.c b/StandaloneMmPkg/Core/Pool.c new file mode 100644 index 0000000000..ce6cfcb9a4 --- /dev/null +++ b/StandaloneMmPkg/Core/Pool.c @@ -0,0 +1,293 @@ +/** @file + SMM Memory pool management functions. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +LIST_ENTRY mMmPoolLists[MAX_POOL_INDEX]; +// +// To cache the MMRAM base since when Loading modules At fixed address feature is enabled, +// all module is assigned an offset relative the MMRAM base in build time. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressMmramBase = 0; + +/** + Called to initialize the memory service. + + @param MmramRangeCount Number of MMRAM Regions + @param MmramRanges Pointer to MMRAM Descriptors + +**/ +VOID +MmInitializeMemoryServices ( + IN UINTN MmramRangeCount, + IN EFI_MMRAM_DESCRIPTOR *MmramRanges + ) +{ + UINTN Index; + + // + // Initialize Pool list + // + for (Index = sizeof (mMmPoolLists) / sizeof (*mMmPoolLists); Index > 0;) { + InitializeListHead (&mMmPoolLists[--Index]); + } + + + // + // Initialize free MMRAM regions + // + for (Index = 0; Index < MmramRangeCount; Index++) { + // + // BUGBUG: Add legacy MMRAM region is buggy. + // + if (MmramRanges[Index].CpuStart < BASE_1MB) { + continue; + } + DEBUG ((DEBUG_INFO, "MmAddMemoryRegion %d : 0x%016lx - 0x%016lx\n", + Index, MmramRanges[Index].CpuStart, MmramRanges[Index].PhysicalSize)); + MmAddMemoryRegion ( + MmramRanges[Index].CpuStart, + MmramRanges[Index].PhysicalSize, + EfiConventionalMemory, + MmramRanges[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; + EFI_PHYSICAL_ADDRESS Address; + + ASSERT (PoolIndex <= MAX_POOL_INDEX); + Status = EFI_SUCCESS; + Hdr = NULL; + if (PoolIndex == MAX_POOL_INDEX) { + Status = MmInternalAllocatePages ( + AllocateAnyPages, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), + &Address + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + Hdr = (FREE_POOL_HEADER *) (UINTN) Address; + } else if (!IsListEmpty (&mMmPoolLists[PoolIndex])) { + Hdr = BASE_CR (GetFirstNode (&mMmPoolLists[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 (&mMmPoolLists[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 = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT); + FreePoolHdr->Header.Available = TRUE; + ASSERT (PoolIndex < MAX_POOL_INDEX); + InsertHeadList (&mMmPoolLists[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 +MmInternalAllocatePool ( + 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; + } + + Size += sizeof (*PoolHdr); + if (Size > MAX_POOL_SIZE) { + Size = EFI_SIZE_TO_PAGES (Size); + Status = MmInternalAllocatePages (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 = (UINTN) HighBitSet32 ((UINT32)Size); + if ((Size & (Size - 1)) != 0) { + PoolIndex++; + } + + Status = InternalAllocPoolByIndex (PoolIndex, &FreePoolHdr); + if (!EFI_ERROR (Status)) { + *Buffer = &FreePoolHdr->Header + 1; + } + return Status; +} + +/** + 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 +MmAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + + Status = MmInternalAllocatePool (PoolType, Size, Buffer); + 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 +MmInternalFreePool ( + 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 MmInternalFreePages ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr, + EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size) + ); + } + return InternalFreePoolByIndex (FreePoolHdr); +} + +/** + 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 +MmFreePool ( + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + + Status = MmInternalFreePool (Buffer); + return Status; +} diff --git a/StandaloneMmPkg/Core/StandaloneMmCore.c b/StandaloneMmPkg/Core/StandaloneMmCore.c new file mode 100644 index 0000000000..74432320bf --- /dev/null +++ b/StandaloneMmPkg/Core/StandaloneMmCore.c @@ -0,0 +1,712 @@ +/** @file + MM Core Main Entry Point + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 "StandaloneMmCore.h" + +EFI_STATUS +MmCoreFfsFindMmDriver ( + IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader + ); + +EFI_STATUS +MmDispatcher ( + VOID + ); + +// +// Globals used to initialize the protocol +// +EFI_HANDLE mMmCpuHandle = NULL; + +// +// Physical pointer to private structure shared between MM IPL and the MM Core +// +MM_CORE_PRIVATE_DATA *gMmCorePrivate; + +// +// MM Core global variable for MM System Table. Only accessed as a physical structure in MMRAM. +// +EFI_MM_SYSTEM_TABLE gMmCoreMmst = { + + // The table header for the MMST. + { + MM_MMST_SIGNATURE, + EFI_MM_SYSTEM_TABLE_REVISION, + sizeof (gMmCoreMmst.Hdr) + }, + // MmFirmwareVendor + NULL, + // MmFirmwareRevision + 0, + // MmInstallConfigurationTable + MmInstallConfigurationTable, + // I/O Service + { + { + (EFI_MM_CPU_IO) MmEfiNotAvailableYetArg5, // MmMemRead + (EFI_MM_CPU_IO) MmEfiNotAvailableYetArg5 // MmMemWrite + }, + { + (EFI_MM_CPU_IO) MmEfiNotAvailableYetArg5, // MmIoRead + (EFI_MM_CPU_IO) MmEfiNotAvailableYetArg5 // MmIoWrite + } + }, + // Runtime memory services + MmAllocatePool, + MmFreePool, + MmAllocatePages, + MmFreePages, + // MP service + NULL, // MmStartupThisAp + 0, // CurrentlyExecutingCpu + 0, // NumberOfCpus + NULL, // CpuSaveStateSize + NULL, // CpuSaveState + 0, // NumberOfTableEntries + NULL, // MmConfigurationTable + MmInstallProtocolInterface, + MmUninstallProtocolInterface, + MmHandleProtocol, + MmRegisterProtocolNotify, + MmLocateHandle, + MmLocateProtocol, + MmiManage, + MmiHandlerRegister, + MmiHandlerUnRegister +}; + +// +// 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 +// MM IPL are converted to free memory, so the MM Core must guarantee that is +// does not touch of the code/data associated with the MM IPL if this flag is TRUE. +// +BOOLEAN mInLegacyBoot = FALSE; + +// +// Table of MMI Handlers that are registered by the MM Core when it is initialized +// +MM_CORE_MMI_HANDLERS mMmCoreMmiHandlers[] = { + { MmFvDispatchHandler, &gMmFvDispatchGuid, NULL, TRUE }, + { MmDriverDispatchHandler, &gEfiEventDxeDispatchGuid, NULL, TRUE }, + { MmReadyToLockHandler, &gEfiDxeMmReadyToLockProtocolGuid, NULL, TRUE }, + { MmEndOfDxeHandler, &gEfiEndOfDxeEventGroupGuid, NULL, FALSE }, + { MmLegacyBootHandler, &gEfiEventLegacyBootGuid, NULL, FALSE }, + { MmExitBootServiceHandler,&gEfiEventExitBootServicesGuid, NULL, FALSE }, + { MmReadyToBootHandler, &gEfiEventReadyToBootGuid, NULL, FALSE }, + { NULL, NULL, NULL, FALSE }, +}; + +EFI_SYSTEM_TABLE *mEfiSystemTable; +UINTN mMmramRangeCount; +EFI_MMRAM_DESCRIPTOR *mMmramRanges; + +/** + Place holder function until all the MM System Table Service are available. + + Note: This function is only used by MMRAM 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 +MmEfiNotAvailableYetArg5 ( + 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 MMI handler that is called when a Legacy Boot event is signaled. The MM + Core uses this signal to know that a Legacy Boot has been performed and that + gMmCorePrivate that is shared between the UEFI and MM execution environments can + not be accessed from MM anymore since that structure is considered free memory by + a legacy OS. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmLegacyBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_HANDLE MmHandle; + EFI_STATUS Status = EFI_SUCCESS; + + if (!mInLegacyBoot) { + MmHandle = NULL; + Status = MmInstallProtocolInterface ( + &MmHandle, + &gEfiEventLegacyBootGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + } + mInLegacyBoot = TRUE; + return Status; +} + +/** + Software MMI handler that is called when a ExitBoot Service event is signaled. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmExitBootServiceHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_HANDLE MmHandle; + EFI_STATUS Status = EFI_SUCCESS; + STATIC BOOLEAN mInExitBootServices = FALSE; + + if (!mInExitBootServices) { + MmHandle = NULL; + Status = MmInstallProtocolInterface ( + &MmHandle, + &gEfiEventExitBootServicesGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + } + mInExitBootServices = TRUE; + return Status; +} + +/** + Software MMI handler that is called when a ExitBoot Service event is signaled. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmReadyToBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_HANDLE MmHandle; + EFI_STATUS Status = EFI_SUCCESS; + STATIC BOOLEAN mInReadyToBoot = FALSE; + + if (!mInReadyToBoot) { + MmHandle = NULL; + Status = MmInstallProtocolInterface ( + &MmHandle, + &gEfiEventReadyToBootGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + } + mInReadyToBoot = TRUE; + return Status; +} + +/** + Software MMI handler that is called when the DxeMmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signaled. This function unregisters the + Software SMIs that are nor required after MMRAM is locked and installs the + MM Ready To Lock Protocol so MM Drivers are informed that MMRAM is about + to be locked. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmReadyToLockHandler ( + 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 MmHandle; + + DEBUG ((DEBUG_INFO, "MmReadyToLockHandler\n")); + + // + // Unregister MMI Handlers that are no longer required after the MM driver dispatch is stopped + // + for (Index = 0; mMmCoreMmiHandlers[Index].HandlerType != NULL; Index++) { + if (mMmCoreMmiHandlers[Index].UnRegister) { + MmiHandlerUnRegister (mMmCoreMmiHandlers[Index].DispatchHandle); + } + } + + // + // Install MM Ready to lock protocol + // + MmHandle = NULL; + Status = MmInstallProtocolInterface ( + &MmHandle, + &gEfiMmReadyToLockProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + // + // Make sure MM CPU I/O 2 Protocol has been installed into the handle database + // + //Status = MmLocateProtocol (&EFI_MM_CPU_IO_PROTOCOL_GUID, NULL, &Interface); + + // + // Print a message on a debug build if the MM CPU I/O 2 Protocol is not installed + // + //if (EFI_ERROR (Status)) { + //DEBUG ((DEBUG_ERROR, "\nSMM: SmmCpuIo Arch Protocol not present!!\n")); + //} + + + // + // 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 + // + //MmDisplayDiscoveredNotDispatched (); + + return Status; +} + +/** + Software MMI handler that is called when the EndOfDxe event is signaled. + This function installs the MM EndOfDxe Protocol so MM Drivers are informed that + platform code will invoke 3rd part code. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmEndOfDxeHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE MmHandle; + + DEBUG ((DEBUG_INFO, "MmEndOfDxeHandler\n")); + // + // Install MM EndOfDxe protocol + // + MmHandle = NULL; + Status = MmInstallProtocolInterface ( + &MmHandle, + &gEfiMmEndOfDxeProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + return Status; +} + + + +/** + The main entry point to MM Foundation. + + Note: This function is only used by MMRAM invocation. It is never used by DXE invocation. + + @param MmEntryContext Processor information and functionality + needed by MM Foundation. + +**/ +VOID +EFIAPI +MmEntryPoint ( + IN CONST EFI_MM_ENTRY_CONTEXT *MmEntryContext +) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_HEADER *CommunicateHeader; + BOOLEAN InLegacyBoot; + + DEBUG ((DEBUG_INFO, "MmEntryPoint ...\n")); + + // + // Update MMST using the context + // + CopyMem (&gMmCoreMmst.MmStartupThisAp, MmEntryContext, sizeof (EFI_MM_ENTRY_CONTEXT)); + + // + // Call platform hook before Mm Dispatch + // + //PlatformHookBeforeMmDispatch (); + + // + // If a legacy boot has occured, then make sure gMmCorePrivate is not accessed + // + InLegacyBoot = mInLegacyBoot; + if (!InLegacyBoot) { + // + // TBD: Mark the InMm flag as TRUE + // + gMmCorePrivate->InMm = TRUE; + + // + // Check to see if this is a Synchronous MMI sent through the MM Communication + // Protocol or an Asynchronous MMI + // + if (gMmCorePrivate->CommunicationBuffer != 0) { + // + // Synchronous MMI for MM Core or request from Communicate protocol + // + if (!MmIsBufferOutsideMmValid ((UINTN)gMmCorePrivate->CommunicationBuffer, gMmCorePrivate->BufferSize)) { + // + // If CommunicationBuffer is not in valid address scope, return EFI_INVALID_PARAMETER + // + gMmCorePrivate->CommunicationBuffer = 0; + gMmCorePrivate->ReturnStatus = EFI_INVALID_PARAMETER; + } else { + CommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)(UINTN)gMmCorePrivate->CommunicationBuffer; + gMmCorePrivate->BufferSize -= OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data); + Status = MmiManage ( + &CommunicateHeader->HeaderGuid, + NULL, + CommunicateHeader->Data, + (UINTN *)&gMmCorePrivate->BufferSize + ); + // + // Update CommunicationBuffer, BufferSize and ReturnStatus + // Communicate service finished, reset the pointer to CommBuffer to NULL + // + gMmCorePrivate->BufferSize += OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data); + gMmCorePrivate->CommunicationBuffer = 0; + gMmCorePrivate->ReturnStatus = (Status == EFI_SUCCESS) ? EFI_SUCCESS : EFI_NOT_FOUND; + } + } + } + + // + // Process Asynchronous MMI sources + // + MmiManage (NULL, NULL, NULL, NULL); + + // + // TBD: Do not use private data structure ? + // + + // + // If a legacy boot has occured, then make sure gMmCorePrivate is not accessed + // + if (!InLegacyBoot) { + // + // Clear the InMm flag as we are going to leave MM + // + gMmCorePrivate->InMm = FALSE; + } + + DEBUG ((DEBUG_INFO, "MmEntryPoint Done\n")); +} + +EFI_STATUS +EFIAPI +MmConfigurationMmNotify ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_MM_CONFIGURATION_PROTOCOL *MmConfiguration; + + DEBUG ((DEBUG_INFO, "MmConfigurationMmNotify(%g) - %x\n", Protocol, Interface)); + + MmConfiguration = Interface; + + // + // Register the MM Entry Point provided by the MM Core with the MM COnfiguration protocol + // + Status = MmConfiguration->RegisterMmEntry (MmConfiguration, (EFI_MM_ENTRY_POINT)(UINTN)gMmCorePrivate->MmEntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Set flag to indicate that the MM Entry Point has been registered which + // means that MMIs are now fully operational. + // + gMmCorePrivate->MmEntryPointRegistered = TRUE; + + // + // Print debug message showing MM Core entry point address. + // + DEBUG ((DEBUG_INFO, "MM Core registered MM Entry Point address %p\n", (VOID *)(UINTN)gMmCorePrivate->MmEntryPoint)); + return EFI_SUCCESS; +} + +UINTN +GetHobListSize ( + IN VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + ASSERT (HobStart != NULL); + + Hob.Raw = (UINT8 *) HobStart; + while (!END_OF_HOB_LIST (Hob)) { + Hob.Raw = GET_NEXT_HOB (Hob); + } + // + // Need plus END_OF_HOB_LIST + // + return (UINTN)Hob.Raw - (UINTN)HobStart + sizeof (EFI_HOB_GENERIC_HEADER); +} + +/** + The Entry Point for MM Core + + Install DXE Protocols and reload MM Core into MMRAM and register MM Core + EntryPoint on the MMI vector. + + Note: This function is called for both DXE invocation and MMRAM 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 +StandaloneMmMain ( + IN VOID *HobStart + ) +{ + EFI_STATUS Status; + UINTN Index; + VOID *MmHobStart; + UINTN HobSize; + VOID *Registration; + EFI_HOB_GUID_TYPE *GuidHob; + MM_CORE_DATA_HOB_DATA *DataInHob; + EFI_HOB_GUID_TYPE *MmramRangesHob; + EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *MmramRangesHobData; + EFI_MMRAM_DESCRIPTOR *MmramRanges; + UINT32 MmramRangeCount; + EFI_HOB_FIRMWARE_VOLUME *BfvHob; + + ProcessLibraryConstructorList (HobStart, &gMmCoreMmst); + + DEBUG ((DEBUG_INFO, "MmMain - 0x%x\n", HobStart)); + + // + // Determine if the caller has passed a reference to a MM_CORE_PRIVATE_DATA + // structure in the Hoblist. This choice will govern how boot information is + // extracted later. + // + GuidHob = GetNextGuidHob (&gMmCoreDataHobGuid, HobStart); + if (GuidHob == NULL) { + // + // Allocate and zero memory for a MM_CORE_PRIVATE_DATA table and then + // initialise it + // + gMmCorePrivate = (MM_CORE_PRIVATE_DATA *) AllocateRuntimePages(EFI_SIZE_TO_PAGES(sizeof (MM_CORE_PRIVATE_DATA))); + SetMem ((VOID *)(UINTN)gMmCorePrivate, sizeof (MM_CORE_PRIVATE_DATA), 0); + gMmCorePrivate->Signature = MM_CORE_PRIVATE_DATA_SIGNATURE; + gMmCorePrivate->MmEntryPointRegistered = FALSE; + gMmCorePrivate->InMm = FALSE; + gMmCorePrivate->ReturnStatus = EFI_SUCCESS; + + // + // Extract the MMRAM ranges from the MMRAM descriptor HOB + // + MmramRangesHob = GetNextGuidHob (&gEfiMmPeiMmramMemoryReserveGuid, HobStart); + if (MmramRangesHob == NULL) + return EFI_UNSUPPORTED; + + MmramRangesHobData = GET_GUID_HOB_DATA (MmramRangesHob); + ASSERT (MmramRangesHobData != NULL); + MmramRanges = MmramRangesHobData->Descriptor; + MmramRangeCount = MmramRangesHobData->NumberOfMmReservedRegions; + ASSERT (MmramRanges); + ASSERT (MmramRangeCount); + + // + // Copy the MMRAM ranges into MM_CORE_PRIVATE_DATA table just in case any + // code relies on them being present there + // + gMmCorePrivate->MmramRangeCount = MmramRangeCount; + gMmCorePrivate->MmramRanges = + (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (MmramRangeCount * sizeof (EFI_MMRAM_DESCRIPTOR)); + ASSERT (gMmCorePrivate->MmramRanges != 0); + CopyMem ( + (VOID *)(UINTN)gMmCorePrivate->MmramRanges, + MmramRanges, + MmramRangeCount * sizeof (EFI_MMRAM_DESCRIPTOR) + ); + } else { + DataInHob = GET_GUID_HOB_DATA (GuidHob); + gMmCorePrivate = (MM_CORE_PRIVATE_DATA *)(UINTN)DataInHob->Address; + MmramRanges = (EFI_MMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges; + MmramRangeCount = gMmCorePrivate->MmramRangeCount; + } + + // + // Print the MMRAM ranges passed by the caller + // + DEBUG ((DEBUG_INFO, "MmramRangeCount - 0x%x\n", MmramRangeCount)); + for (Index = 0; Index < MmramRangeCount; Index++) { + DEBUG ((DEBUG_INFO, "MmramRanges[%d]: 0x%016lx - 0x%lx\n", Index, + MmramRanges[Index].CpuStart, + MmramRanges[Index].PhysicalSize)); + } + + // + // Copy the MMRAM ranges into private MMRAM + // + mMmramRangeCount = MmramRangeCount; + DEBUG ((DEBUG_INFO, "mMmramRangeCount - 0x%x\n", mMmramRangeCount)); + mMmramRanges = AllocatePool (mMmramRangeCount * sizeof (EFI_MMRAM_DESCRIPTOR)); + DEBUG ((DEBUG_INFO, "mMmramRanges - 0x%x\n", mMmramRanges)); + ASSERT (mMmramRanges != NULL); + CopyMem (mMmramRanges, (VOID *)(UINTN)MmramRanges, mMmramRangeCount * sizeof (EFI_MMRAM_DESCRIPTOR)); + + // + // Get Boot Firmware Volume address from the BFV Hob + // + BfvHob = GetFirstHob (EFI_HOB_TYPE_FV); + if (BfvHob != NULL) { + DEBUG ((DEBUG_INFO, "BFV address - 0x%x\n", BfvHob->BaseAddress)); + DEBUG ((DEBUG_INFO, "BFV size - 0x%x\n", BfvHob->Length)); + gMmCorePrivate->StandaloneBfvAddress = BfvHob->BaseAddress; + } + + gMmCorePrivate->Mmst = (EFI_PHYSICAL_ADDRESS)(UINTN)&gMmCoreMmst; + gMmCorePrivate->MmEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)MmEntryPoint; + + // + // No need to initialize memory service. + // It is done in constructor of StandaloneMmCoreMemoryAllocationLib(), + // so that the library linked with StandaloneMmCore can use AllocatePool() in constuctor. + // + + DEBUG ((DEBUG_INFO, "MmInstallConfigurationTable For HobList\n")); + // + // Install HobList + // + HobSize = GetHobListSize (HobStart); + DEBUG ((DEBUG_INFO, "HobSize - 0x%x\n", HobSize)); + MmHobStart = AllocatePool (HobSize); + DEBUG ((DEBUG_INFO, "MmHobStart - 0x%x\n", MmHobStart)); + ASSERT (MmHobStart != NULL); + CopyMem (MmHobStart, HobStart, HobSize); + Status = MmInstallConfigurationTable (&gMmCoreMmst, &gEfiHobListGuid, MmHobStart, HobSize); + ASSERT_EFI_ERROR (Status); + + // + // Register notification for EFI_MM_CONFIGURATION_PROTOCOL registration and + // use it to register the MM Foundation entrypoint + // + DEBUG ((DEBUG_INFO, "MmRegisterProtocolNotify - MmConfigurationMmProtocol\n")); + Status = MmRegisterProtocolNotify ( + &gEfiMmConfigurationProtocolGuid, + MmConfigurationMmNotify, + &Registration + ); + ASSERT_EFI_ERROR (Status); + + // + // Dispatch standalone BFV + // + DEBUG ((DEBUG_INFO, "Mm Dispatch StandaloneBfvAddress - 0x%08x\n", gMmCorePrivate->StandaloneBfvAddress)); + if (gMmCorePrivate->StandaloneBfvAddress != 0) { + MmCoreFfsFindMmDriver ((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)gMmCorePrivate->StandaloneBfvAddress); + MmDispatcher (); + } + + // + // Register all handlers in the core table + // + for (Index = 0; mMmCoreMmiHandlers[Index].HandlerType != NULL; Index++) { + Status = MmiHandlerRegister ( + mMmCoreMmiHandlers[Index].Handler, + mMmCoreMmiHandlers[Index].HandlerType, + &mMmCoreMmiHandlers[Index].DispatchHandle + ); + DEBUG ((DEBUG_INFO, "MmiHandlerRegister - GUID %g - Status %d\n", mMmCoreMmiHandlers[Index].HandlerType, Status)); + } + + DEBUG ((DEBUG_INFO, "MmMain Done!\n")); + + return EFI_SUCCESS; +} diff --git a/StandaloneMmPkg/Core/StandaloneMmCore.h b/StandaloneMmPkg/Core/StandaloneMmCore.h new file mode 100644 index 0000000000..0d20bcaa6b --- /dev/null +++ b/StandaloneMmPkg/Core/StandaloneMmCore.h @@ -0,0 +1,903 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by MmCore module. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 _MM_CORE_H_ +#define _MM_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 +#include + +#include +#include + +#include "StandaloneMmCorePrivateData.h" + +// +// Used to build a table of MMI Handlers that the MM Core registers +// +typedef struct { + EFI_MM_HANDLER_ENTRY_POINT Handler; + EFI_GUID *HandlerType; + EFI_HANDLE DispatchHandle; + BOOLEAN UnRegister; +} MM_CORE_MMI_HANDLERS; + +// +// Structure for recording the state of an MM Driver +// +#define EFI_MM_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; + VOID *Pe32Data; + UINTN Pe32DataSize; + + VOID *Depex; + UINTN DepexSize; + + BOOLEAN Before; + BOOLEAN After; + EFI_GUID BeforeAfterGuid; + + BOOLEAN Dependent; + BOOLEAN Scheduled; + BOOLEAN Initialized; + BOOLEAN DepexProtocolError; + + EFI_HANDLE ImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + // + // Image EntryPoint in MMRAM + // + PHYSICAL_ADDRESS ImageEntryPoint; + // + // Image Buffer in MMRAM + // + PHYSICAL_ADDRESS ImageBuffer; + // + // Image Page Number + // + UINTN NumberOfPage; +} EFI_MM_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_MM_NOTIFY_FN Function; + /// Last position notified + LIST_ENTRY *Position; +} PROTOCOL_NOTIFY; + +// +// MM Core Global Variables +// +extern MM_CORE_PRIVATE_DATA *gMmCorePrivate; +extern EFI_MM_SYSTEM_TABLE gMmCoreMmst; +extern LIST_ENTRY gHandleList; +extern EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressMmramBase; + +/** + Called to initialize the memory service. + + @param MmramRangeCount Number of MMRAM Regions + @param MmramRanges Pointer to MMRAM Descriptors + +**/ +VOID +MmInitializeMemoryServices ( + IN UINTN MmramRangeCount, + IN EFI_MMRAM_DESCRIPTOR *MmramRanges + ); + +/** + The MmInstallConfigurationTable() 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 MM 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 +MmInstallConfigurationTable ( + IN CONST EFI_MM_SYSTEM_TABLE *SystemTable, + IN CONST EFI_GUID *Guid, + IN VOID *Table, + IN UINTN TableSize + ); + +/** + Wrapper function to MmInstallProtocolInterfaceNotify. 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 +MmInstallProtocolInterface ( + 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 +MmAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ); + +/** + 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 +MmInternalAllocatePages ( + 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, Address is zero or NumberOfPages is zero. + @return EFI_SUCCESS Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +MmFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + 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, Address is zero or NumberOfPages is zero. + @return EFI_SUCCESS Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +MmInternalFreePages ( + 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 +MmAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ); + +/** + 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 +MmInternalAllocatePool ( + 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 +MmFreePool ( + IN 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 +MmInternalFreePool ( + 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 +MmInstallProtocolInterfaceNotify ( + 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 +MmUninstallProtocolInterface ( + 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 +MmHandleProtocol ( + 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 +MmRegisterProtocolNotify ( + IN CONST EFI_GUID *Protocol, + IN EFI_MM_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 +MmLocateHandle ( + 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 +MmLocateProtocol ( + IN EFI_GUID *Protocol, + IN VOID *Registration OPTIONAL, + OUT VOID **Interface + ); + +/** + Manage MMI of a particular type. + + @param HandlerType Points to the handler type or NULL for root MMI 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 MMI 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 +MmiManage ( + 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 MM. + + @param Handler Handler service funtion pointer. + @param HandlerType Points to the handler type or NULL for root MMI 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 +MmiHandlerRegister ( + IN EFI_MM_HANDLER_ENTRY_POINT Handler, + IN CONST EFI_GUID *HandlerType OPTIONAL, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a handler in MM. + + @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 +MmiHandlerUnRegister ( + IN EFI_HANDLE DispatchHandle + ); + +/** + This function is the main entry point for an MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmDriverDispatchHandler ( + 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 MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmFvDispatchHandler ( + 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 MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmLegacyBootHandler ( + 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 MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmExitBootServiceHandler ( + 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 MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmReadyToBootHandler ( + 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 MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmReadyToLockHandler ( + 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 MM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by MmiHandlerRegister(). + @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-MM environment into an MM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +MmEndOfDxeHandler ( + 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 MM 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 +MmEfiNotAvailableYetArg5 ( + 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 expressions evaluated to false. + +**/ +VOID +MmDisplayDiscoveredNotDispatched ( + VOID + ); + +/** + Add free MMRAM 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 +MmAddMemoryRegion ( + 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 * +MmFindProtocolEntry ( + IN EFI_GUID *Protocol, + IN BOOLEAN Create + ); + +/** + Signal event for every protocol in protocol entry. + + @param Prot Protocol interface + +**/ +VOID +MmNotifyProtocol ( + 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 * +MmFindProtocolInterface ( + 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 * +MmRemoveInterfaceFromProtocol ( + 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. 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 +MmIsSchedulable ( + IN EFI_MM_DRIVER_ENTRY *DriverEntry + ); + +/** + Dump MMRAM information. + +**/ +VOID +DumpMmramInfo ( + VOID + ); + +extern UINTN mMmramRangeCount; +extern EFI_MMRAM_DESCRIPTOR *mMmramRanges; +extern EFI_SYSTEM_TABLE *mEfiSystemTable; + +#endif diff --git a/StandaloneMmPkg/Core/StandaloneMmCore.inf b/StandaloneMmPkg/Core/StandaloneMmCore.inf new file mode 100644 index 0000000000..ff2b8b9cef --- /dev/null +++ b/StandaloneMmPkg/Core/StandaloneMmCore.inf @@ -0,0 +1,80 @@ +## @file +# This module provide an SMM CIS compliant implementation of SMM Core. +# +# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+# Copyright (c) 2016 - 2018, ARM Limited. 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 = 0x0001001A + BASE_NAME = StandaloneMmCore + FILE_GUID = 6E14B6FD-3600-4DD6-A17A-206B3B6DCE16 + MODULE_TYPE = MM_CORE_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + ENTRY_POINT = StandaloneMmMain + +# VALID_ARCHITECTURES = IA32 X64 AARCH64 + +[Sources] + StandaloneMmCore.c + StandaloneMmCore.h + StandaloneMmCorePrivateData.h + Page.c + Pool.c + Handle.c + Locate.c + Notify.c + Dependency.c + Dispatcher.c + Mmi.c + InstallConfigurationTable.c + FwVol.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + StandaloneMmPkg/StandaloneMmPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + CacheMaintenanceLib + DebugLib + FvLib + HobLib + MemoryAllocationLib + MemLib + PeCoffLib + ReportStatusCodeLib + StandaloneMmCoreEntryPoint + +[Protocols] + gEfiDxeMmReadyToLockProtocolGuid ## UNDEFINED # SmiHandlerRegister + gEfiMmReadyToLockProtocolGuid ## PRODUCES + gEfiMmEndOfDxeProtocolGuid ## PRODUCES + gEfiLoadedImageProtocolGuid ## PRODUCES + gEfiMmConfigurationProtocolGuid ## CONSUMES + +[Guids] + gAprioriGuid ## SOMETIMES_CONSUMES ## File + gEfiEventDxeDispatchGuid ## PRODUCES ## GUID # SmiHandlerRegister + gEfiEndOfDxeEventGroupGuid ## PRODUCES ## GUID # SmiHandlerRegister + ## SOMETIMES_CONSUMES ## GUID # Locate protocol + ## SOMETIMES_PRODUCES ## GUID # SmiHandlerRegister + gEdkiiMemoryProfileGuid + gZeroGuid ## SOMETIMES_CONSUMES ## GUID + gEfiHobListGuid + gMmCoreDataHobGuid + gMmFvDispatchGuid + gEfiEventLegacyBootGuid + gEfiEventExitBootServicesGuid + gEfiEventReadyToBootGuid diff --git a/StandaloneMmPkg/Core/StandaloneMmCorePrivateData.h b/StandaloneMmPkg/Core/StandaloneMmCorePrivateData.h new file mode 100644 index 0000000000..d9598937cf --- /dev/null +++ b/StandaloneMmPkg/Core/StandaloneMmCorePrivateData.h @@ -0,0 +1,66 @@ +/** @file + The internal header file that declared a data structure that is shared + between the MM IPL and the MM Core. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, ARM Limited. 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 _STANDALONE_MM_CORE_PRIVATE_DATA_H_ +#define _STANDALONE_MM_CORE_PRIVATE_DATA_H_ + +#include + +// +// Page management +// + +typedef struct { + LIST_ENTRY Link; + UINTN NumberOfPages; +} FREE_PAGE_LIST; + +extern LIST_ENTRY mMmMemoryMap; + +// +// Pool management +// + +// +// 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; + +extern LIST_ENTRY mMmPoolLists[MAX_POOL_INDEX]; + +#endif diff --git a/StandaloneMmPkg/Include/Guid/MmFvDispatch.h b/StandaloneMmPkg/Include/Guid/MmFvDispatch.h new file mode 100644 index 0000000000..d141d40d8d --- /dev/null +++ b/StandaloneMmPkg/Include/Guid/MmFvDispatch.h @@ -0,0 +1,39 @@ +/** @file + GUIDs for MM Event. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.
+ +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that 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 __MM_FV_DISPATCH_H__ +#define __MM_FV_DISPATCH_H__ + +#define MM_FV_DISPATCH_GUID \ + { 0xb65694cc, 0x9e3, 0x4c3b, { 0xb5, 0xcd, 0x5, 0xf4, 0x4d, 0x3c, 0xdb, 0xff }} + +extern EFI_GUID gMmFvDispatchGuid; + +#pragma pack(1) + +typedef struct { + EFI_PHYSICAL_ADDRESS Address; + UINT64 Size; +} EFI_MM_COMMUNICATE_FV_DISPATCH_DATA; + +typedef struct { + EFI_GUID HeaderGuid; + UINTN MessageLength; + EFI_MM_COMMUNICATE_FV_DISPATCH_DATA Data; +} EFI_MM_COMMUNICATE_FV_DISPATCH; +#pragma pack() + +#endif diff --git a/StandaloneMmPkg/Include/Library/StandaloneMmCoreEntryPoint.h b/StandaloneMmPkg/Include/Library/StandaloneMmCoreEntryPoint.h new file mode 100644 index 0000000000..f53094bfc3 --- /dev/null +++ b/StandaloneMmPkg/Include/Library/StandaloneMmCoreEntryPoint.h @@ -0,0 +1,101 @@ +/** @file + Module entry point library for STANDALONE MM core. + +Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+Copyright (c) 2016 - 2018, ARM Limited. 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 __MODULE_ENTRY_POINT_H__ +#define __MODULE_ENTRY_POINT_H__ + +/// +/// Global variable that contains a pointer to the Hob List passed into the STANDALONE MM Core entry point. +/// +extern VOID *gHobList; + + +/** + The entry point of PE/COFF Image for the STANDALONE MM Core. + + This function is the entry point for the STANDALONE MM Core. This function is required to call + ProcessModuleEntryPointList() and ProcessModuleEntryPointList() is never expected to return. + The STANDALONE MM Core is responsible for calling ProcessLibraryConstructorList() as soon as the EFI + System Table and the image handle for the STANDALONE MM Core itself have been established. + If ProcessModuleEntryPointList() returns, then ASSERT() and halt the system. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +_ModuleEntryPoint ( + IN VOID *HobStart + ); + + +/** + Required by the EBC compiler and identical in functionality to _ModuleEntryPoint(). + + This function is required to call _ModuleEntryPoint() passing in HobStart. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +EfiMain ( + IN VOID *HobStart + ); + + +/** + Auto generated function that calls the library constructors for all of the module's dependent libraries. + + This function must be called by _ModuleEntryPoint(). + This function calls the set of library constructors for the set of library instances + that a module depends on. This includes library instances that a module depends on + directly and library instances that a module depends on indirectly through other + libraries. This function is auto generated by build tools and those build tools are + responsible for collecting the set of library instances, determine which ones have + constructors, and calling the library constructors in the proper order based upon + each of the library instances own dependencies. + + @param ImageHandle The image handle of the STANDALONE MM Core. + @param SystemTable A pointer to the EFI System Table. + +**/ +VOID +EFIAPI +ProcessLibraryConstructorList ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *MmSystemTable + ); + + +/** + Autogenerated function that calls a set of module entry points. + + This function must be called by _ModuleEntryPoint(). + This function calls the set of module entry points. + This function is auto generated by build tools and those build tools are responsible + for collecting the module entry points and calling them in a specified order. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +ProcessModuleEntryPointList ( + IN VOID *HobStart + ); + +#endif diff --git a/StandaloneMmPkg/Include/StandaloneMm.h b/StandaloneMmPkg/Include/StandaloneMm.h new file mode 100644 index 0000000000..2517d41e74 --- /dev/null +++ b/StandaloneMmPkg/Include/StandaloneMm.h @@ -0,0 +1,36 @@ +/** @file + Standalone MM. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2016 - 2018, ARM Limited. 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 _STANDALONE_MM_H_ +#define _STANDALONE_MM_H_ + +#include + +typedef +EFI_STATUS +(EFIAPI *MM_IMAGE_ENTRY_POINT) ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *MmSystemTable + ); + +typedef +EFI_STATUS +(EFIAPI *STANDALONE_MM_FOUNDATION_ENTRY_POINT) ( + IN VOID *HobStart + ); + +#endif