diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore')
24 files changed, 16997 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dependency.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dependency.c new file mode 100644 index 00000000..fed72974 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dependency.c @@ -0,0 +1,382 @@ +/** @file + SMM Driver Dispatcher Dependency Evaluator + + This routine evaluates a dependency expression (DEPENDENCY_EXPRESSION) to determine + if a driver can be scheduled for execution. The criteria for + schedulability is that the dependency expression is satisfied. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.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 +SmmIsSchedulable ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + EFI_STATUS Status; + UINT8 *Iterator; + BOOLEAN Operator; + BOOLEAN Operator2; + EFI_GUID DriverGuid; + VOID *Interface; + + Operator = FALSE; + Operator2 = FALSE; + + if (DriverEntry->After || DriverEntry->Before) { + // + // If Before or After Depex skip as SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter () + // processes them. + // + return FALSE; + } + + DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); + + if (DriverEntry->Depex == NULL) { + // + // A NULL Depex means that the SMM driver is not built correctly. + // All SMM drivers must have a valid depex expression. + // + 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 = SmmLocateProtocol (&DriverGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + // + // For SMM Driver, it may depend on uefi protocols + // + Status = gBS->LocateProtocol (&DriverGuid, NULL, &Interface); + } + + if (EFI_ERROR (Status)) { + 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 its intermediate value + // may be strange. + // + Iterator++; + } + +Done: + return FALSE; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dispatcher.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dispatcher.c new file mode 100644 index 00000000..8f5645ca --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dispatcher.c @@ -0,0 +1,1499 @@ +/** @file + SMM Driver Dispatcher. + + Step #1 - When a FV protocol is added to the system every driver in the FV + is added to the mDiscoveredList. The 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 + processes the After dependencies 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 Expression. + + Copyright (c) 2014, Hewlett-Packard Development Company, L.P. + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +// +// SMM Dispatcher Data structures +// +#define KNOWN_HANDLE_SIGNATURE SIGNATURE_32('k','n','o','w') +typedef struct { + UINTN Signature; + LIST_ENTRY Link; // mFvHandleList + EFI_HANDLE Handle; +} KNOWN_HANDLE; + +// +// Function Prototypes +// + +/** + Insert InsertedDriverEntry onto the mScheduledQueue. To do this you + must add any driver with a before dependency on InsertedDriverEntry first. + You do this by recursively calling this routine. After all the Befores are + processed you can add InsertedDriverEntry to the mScheduledQueue. + Then you can add any driver with an After dependency on InsertedDriverEntry + by recursively calling this routine. + + @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue + +**/ +VOID +SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( + IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry + ); + +// +// The Driver List contains one copy of every driver that has been discovered. +// Items are never removed from the driver list. List of EFI_SMM_DRIVER_ENTRY +// +LIST_ENTRY mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList); + +// +// Queue of drivers that are ready to dispatch. This queue is a subset of the +// mDiscoveredList.list of EFI_SMM_DRIVER_ENTRY. +// +LIST_ENTRY mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue); + +// +// List of handles who's Fv's have been parsed and added to the mFwDriverList. +// +LIST_ENTRY mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList); + +// +// Flag for the SMM Dispatcher. TRUE if dispatcher is executing. +// +BOOLEAN gDispatcherRunning = FALSE; + +// +// Flag for the SMM Dispatcher. TRUE if there is one or more SMM drivers ready to be dispatched +// +BOOLEAN gRequestDispatch = FALSE; + +// +// List of file types supported by dispatcher +// +EFI_FV_FILETYPE mSmmFileTypes[] = { + EFI_FV_FILETYPE_SMM, + EFI_FV_FILETYPE_COMBINED_SMM_DXE, + EFI_FV_FILETYPE_SMM_CORE, + // + // Note: DXE core will process the FV image file, so skip it in SMM core + // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE + // +}; + +typedef struct { + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH File; + EFI_DEVICE_PATH_PROTOCOL End; +} FV_FILEPATH_DEVICE_PATH; + +FV_FILEPATH_DEVICE_PATH mFvDevicePath; + +// +// DXE Architecture Protocols +// +EFI_SECURITY_ARCH_PROTOCOL *mSecurity = NULL; +EFI_SECURITY2_ARCH_PROTOCOL *mSecurity2 = NULL; + +// +// The global variable is defined for Loading modules at fixed address feature to track the SMM code +// memory range usage. It is a bit mapped array in which every bit indicates the corresponding +// memory page available or not. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT64 *mSmmCodeMemoryRangeUsageBitMap=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 available, the function will mark the corresponding 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 address 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 SmmCodePageNumber; + UINT64 SmmCodeSize; + EFI_PHYSICAL_ADDRESS SmmCodeBase; + UINTN BaseOffsetPageNumber; + UINTN TopOffsetPageNumber; + UINTN Index; + // + // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber + // + SmmCodePageNumber = PcdGet32(PcdLoadFixAddressSmmCodePageNumber); + SmmCodeSize = EFI_PAGES_TO_SIZE (SmmCodePageNumber); + SmmCodeBase = gLoadModuleAtFixAddressSmramBase; + + // + // 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 (mSmmCodeMemoryRangeUsageBitMap == NULL) { + mSmmCodeMemoryRangeUsageBitMap = AllocateZeroPool(((SmmCodePageNumber / 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 (mSmmCodeMemoryRangeUsageBitMap == NULL) { + return EFI_NOT_FOUND; + } + // + // see if the memory range for loading the image is in the SMM code range. + // + if (SmmCodeBase + SmmCodeSize < ImageBase + ImageSize || SmmCodeBase > ImageBase) { + return EFI_NOT_FOUND; + } + // + // Test if the memory is available or not. + // + BaseOffsetPageNumber = EFI_SIZE_TO_PAGES((UINT32)(ImageBase - SmmCodeBase)); + TopOffsetPageNumber = EFI_SIZE_TO_PAGES((UINT32)(ImageBase + ImageSize - SmmCodeBase)); + for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) { + if ((mSmmCodeMemoryRangeUsageBitMap[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 ++) { + mSmmCodeMemoryRangeUsageBitMap[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 loading 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)(gLoadModuleAtFixAddressSmramBase + (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 ((EFI_D_INFO|EFI_D_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_SMM_DRIVER_ENTRY instance + + @return EFI_STATUS + +**/ +EFI_STATUS +EFIAPI +SmmLoadImage ( + IN OUT EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + UINT32 AuthenticationStatus; + UINTN FilePathSize; + VOID *Buffer; + UINTN Size; + UINTN PageCount; + EFI_GUID *NameGuid; + EFI_STATUS Status; + EFI_STATUS SecurityStatus; + EFI_HANDLE DeviceHandle; + EFI_PHYSICAL_ADDRESS DstBuffer; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_DEVICE_PATH_PROTOCOL *OriginalFilePath; + EFI_DEVICE_PATH_PROTOCOL *HandleFilePath; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + PERF_LOAD_IMAGE_BEGIN (DriverEntry->ImageHandle); + + Buffer = NULL; + Size = 0; + Fv = DriverEntry->Fv; + NameGuid = &DriverEntry->FileName; + FilePath = DriverEntry->FvFileDevicePath; + + OriginalFilePath = FilePath; + HandleFilePath = FilePath; + DeviceHandle = NULL; + SecurityStatus = EFI_SUCCESS; + Status = EFI_SUCCESS; + AuthenticationStatus = 0; + + // + // Try to get the image device handle by checking the match protocol. + // + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &HandleFilePath, &DeviceHandle); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // If the Security2 and Security Architectural Protocol has not been located yet, then attempt to locate it + // + if (mSecurity2 == NULL) { + gBS->LocateProtocol (&gEfiSecurity2ArchProtocolGuid, NULL, (VOID**)&mSecurity2); + } + if (mSecurity == NULL) { + gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity); + } + // + // When Security2 is installed, Security Architectural Protocol must be published. + // + ASSERT (mSecurity2 == NULL || mSecurity != NULL); + + // + // Pull out just the file portion of the DevicePath for the LoadedImage FilePath + // + FilePath = OriginalFilePath; + Status = gBS->HandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath); + if (!EFI_ERROR (Status)) { + FilePathSize = GetDevicePathSize (HandleFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); + FilePath = (EFI_DEVICE_PATH_PROTOCOL *) (((UINT8 *)FilePath) + FilePathSize ); + } + + // + // Try reading PE32 section firstly + // + Status = Fv->ReadSection ( + Fv, + NameGuid, + EFI_SECTION_PE32, + 0, + &Buffer, + &Size, + &AuthenticationStatus + ); + + if (EFI_ERROR (Status)) { + // + // Try reading TE section secondly + // + Buffer = NULL; + Size = 0; + Status = Fv->ReadSection ( + Fv, + NameGuid, + EFI_SECTION_TE, + 0, + &Buffer, + &Size, + &AuthenticationStatus + ); + } + + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + return Status; + } + + // + // Verify File Authentication through the Security2 Architectural Protocol + // + if (mSecurity2 != NULL) { + SecurityStatus = mSecurity2->FileAuthentication ( + mSecurity2, + OriginalFilePath, + Buffer, + Size, + FALSE + ); + } + + // + // Verify the Authentication Status through the Security Architectural Protocol + // Only on images that have been read using Firmware Volume protocol. + // All SMM images are from FV protocol. + // + if (!EFI_ERROR (SecurityStatus) && (mSecurity != NULL)) { + SecurityStatus = mSecurity->FileAuthenticationState ( + mSecurity, + AuthenticationStatus, + OriginalFilePath + ); + } + + if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) { + Status = SecurityStatus; + return Status; + } + + // + // Initialize ImageContext + // + ImageContext.Handle = Buffer; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + return Status; + } + // + // if Loading module at Fixed Address feature is enabled, then cut out a memory range started from TESG BASE + // to hold the Smm driver code + // + if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { + // + // Get the fixed loading address assigned by Build tool + // + Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext); + if (!EFI_ERROR (Status)) { + // + // Since the memory range to load Smm core already been cut out, so no need to allocate and free this range + // following statements is to bypass SmmFreePages + // + PageCount = 0; + DstBuffer = (UINTN)gLoadModuleAtFixAddressSmramBase; + } else { + DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); + // + // allocate the memory to load the SMM driver + // + PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); + DstBuffer = (UINTN)(-1); + + Status = SmmAllocatePages ( + AllocateMaxAddress, + EfiRuntimeServicesCode, + PageCount, + &DstBuffer + ); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + return Status; + } + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; + } + } else { + PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); + DstBuffer = (UINTN)(-1); + + Status = SmmAllocatePages ( + AllocateMaxAddress, + EfiRuntimeServicesCode, + PageCount, + &DstBuffer + ); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + return Status; + } + + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; + } + // + // Align buffer on section boundary + // + 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) { + gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + + // + // Flush the instruction cache so the image data are written before we execute it + // + InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize); + + // + // Save Image EntryPoint in DriverEntry + // + DriverEntry->ImageEntryPoint = ImageContext.EntryPoint; + DriverEntry->ImageBuffer = DstBuffer; + DriverEntry->NumberOfPage = PageCount; + + // + // Allocate a Loaded Image Protocol in EfiBootServicesData + // + Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&DriverEntry->LoadedImage); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + SmmFreePages (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 = gSmmCorePrivate->SmmIplImageHandle; + DriverEntry->LoadedImage->SystemTable = gST; + DriverEntry->LoadedImage->DeviceHandle = DeviceHandle; + + DriverEntry->SmmLoadedImage.Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; + DriverEntry->SmmLoadedImage.ParentHandle = gSmmCorePrivate->SmmIplImageHandle; + DriverEntry->SmmLoadedImage.SystemTable = gST; + DriverEntry->SmmLoadedImage.DeviceHandle = DeviceHandle; + + // + // Make an EfiBootServicesData buffer copy of FilePath + // + Status = gBS->AllocatePool (EfiBootServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->LoadedImage->FilePath); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + SmmFreePages (DstBuffer, PageCount); + return Status; + } + CopyMem (DriverEntry->LoadedImage->FilePath, FilePath, GetDevicePathSize (FilePath)); + + DriverEntry->LoadedImage->ImageBase = (VOID *)(UINTN) ImageContext.ImageAddress; + DriverEntry->LoadedImage->ImageSize = ImageContext.ImageSize; + DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode; + DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData; + + // + // Make a buffer copy of FilePath + // + Status = SmmAllocatePool (EfiRuntimeServicesData, GetDevicePathSize(FilePath), (VOID **)&DriverEntry->SmmLoadedImage.FilePath); + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + gBS->FreePool (Buffer); + } + gBS->FreePool (DriverEntry->LoadedImage->FilePath); + SmmFreePages (DstBuffer, PageCount); + return Status; + } + CopyMem (DriverEntry->SmmLoadedImage.FilePath, FilePath, GetDevicePathSize(FilePath)); + + DriverEntry->SmmLoadedImage.ImageBase = (VOID *)(UINTN) ImageContext.ImageAddress; + DriverEntry->SmmLoadedImage.ImageSize = ImageContext.ImageSize; + DriverEntry->SmmLoadedImage.ImageCodeType = EfiRuntimeServicesCode; + DriverEntry->SmmLoadedImage.ImageDataType = EfiRuntimeServicesData; + + // + // Create a new image handle in the UEFI handle database for the SMM Driver + // + DriverEntry->ImageHandle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &DriverEntry->ImageHandle, + &gEfiLoadedImageProtocolGuid, DriverEntry->LoadedImage, + NULL + ); + + // + // Create a new image handle in the SMM handle database for the SMM Driver + // + DriverEntry->SmmImageHandle = NULL; + Status = SmmInstallProtocolInterface ( + &DriverEntry->SmmImageHandle, + &gEfiLoadedImageProtocolGuid, + EFI_NATIVE_INTERFACE, + &DriverEntry->SmmLoadedImage + ); + + PERF_LOAD_IMAGE_END (DriverEntry->ImageHandle); + + // + // 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 SMM 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 redundant characters to avoid overflow in array boundary. + // + for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) { + EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex]; + if (EfiFileName[Index] == 0) { + EfiFileName[Index] = '.'; + } + if (EfiFileName[Index] == '.') { + EfiFileName[Index + 1] = 'e'; + EfiFileName[Index + 2] = 'f'; + EfiFileName[Index + 3] = 'i'; + EfiFileName[Index + 4] = 0; + break; + } + } + + if (Index == sizeof (EfiFileName) - 4) { + EfiFileName[Index] = 0; + } + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex])); + } + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n")); + + DEBUG_CODE_END (); + + // + // Free buffer allocated by Fv->ReadSection. + // + // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection + // used the UEFI Boot Services AllocatePool() function + // + Status = gBS->FreePool(Buffer); + if (!EFI_ERROR (Status) && EFI_ERROR (SecurityStatus)) { + Status = SecurityStatus; + } + 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 +SmmPreProcessDepex ( + IN EFI_SMM_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 preprocessed + @retval EFI_PROTOCOL_ERROR The section extraction protocol returned an error + and Depex reading needs to be retried. + @retval Error DEPEX not found. + +**/ +EFI_STATUS +SmmGetDepexSectionAndPreProccess ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + EFI_STATUS Status; + EFI_SECTION_TYPE SectionType; + UINT32 AuthenticationStatus; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + + Fv = DriverEntry->Fv; + + // + // Grab Depex info, it will never be free'ed. + // (Note: DriverEntry->Depex is in DXE memory) + // + SectionType = EFI_SECTION_SMM_DEPEX; + Status = Fv->ReadSection ( + DriverEntry->Fv, + &DriverEntry->FileName, + SectionType, + 0, + &DriverEntry->Depex, + (UINTN *)&DriverEntry->DepexSize, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_PROTOCOL_ERROR) { + // + // The section extraction protocol failed so set protocol error flag + // + DriverEntry->DepexProtocolError = TRUE; + } else { + // + // If no Depex assume depend on all architectural protocols + // + DriverEntry->Depex = NULL; + DriverEntry->Dependent = TRUE; + DriverEntry->DepexProtocolError = FALSE; + } + } else { + // + // Set Before and After state information based on Depex + // Driver will be put in Dependent state + // + SmmPreProcessDepex (DriverEntry); + DriverEntry->DepexProtocolError = FALSE; + } + + return Status; +} + +/** + This is the main Dispatcher for SMM and it exits when there are no more + drivers to run. Drain the mScheduledQueue and load and start a PE + image for each driver. Search the mDiscoveredList to see if any driver can + be placed on the mScheduledQueue. If no drivers are placed on the + mScheduledQueue exit the function. + + @retval EFI_SUCCESS All of the SMM Drivers that could be dispatched + have been run and the SMM Entry Point has been + registered. + @retval EFI_NOT_READY The SMM Driver that registered the SMM Entry Point + was just dispatched. + @retval EFI_NOT_FOUND There are no SMM Drivers available to be dispatched. + @retval EFI_ALREADY_STARTED The SMM Dispatcher is already running + +**/ +EFI_STATUS +SmmDispatcher ( + VOID + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + BOOLEAN ReadyToRun; + BOOLEAN PreviousSmmEntryPointRegistered; + + if (!gRequestDispatch) { + return EFI_NOT_FOUND; + } + + if (gDispatcherRunning) { + // + // If the dispatcher is running don't let it be restarted. + // + return EFI_ALREADY_STARTED; + } + + gDispatcherRunning = TRUE; + + do { + // + // Drain the Scheduled Queue + // + while (!IsListEmpty (&mScheduledQueue)) { + DriverEntry = CR ( + mScheduledQueue.ForwardLink, + EFI_SMM_DRIVER_ENTRY, + ScheduledLink, + EFI_SMM_DRIVER_ENTRY_SIGNATURE + ); + + // + // Load the SMM Driver image into memory. If the Driver was transitioned from + // Untrused to Scheduled it would have already been loaded so we may need to + // skip the LoadImage + // + if (DriverEntry->ImageHandle == NULL) { + Status = SmmLoadImage (DriverEntry); + + // + // Update the driver state to reflect that it's been loaded + // + if (EFI_ERROR (Status)) { + // + // The SMM 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); + + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_BEGIN, + &DriverEntry->ImageHandle, + sizeof (DriverEntry->ImageHandle) + ); + + // + // Cache state of SmmEntryPointRegistered before calling entry point + // + PreviousSmmEntryPointRegistered = gSmmCorePrivate->SmmEntryPointRegistered; + + // + // For each SMM driver, pass NULL as ImageHandle + // + RegisterSmramProfileImage (DriverEntry, TRUE); + PERF_START_IMAGE_BEGIN (DriverEntry->ImageHandle); + Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint)(DriverEntry->ImageHandle, gST); + PERF_START_IMAGE_END (DriverEntry->ImageHandle); + if (EFI_ERROR(Status)){ + DEBUG (( + DEBUG_ERROR, + "Error: SMM image at %11p start failed: %r\n", + DriverEntry->SmmLoadedImage.ImageBase, + Status + )); + UnregisterSmramProfileImage (DriverEntry, TRUE); + SmmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage); + // + // Uninstall LoadedImage + // + Status = gBS->UninstallProtocolInterface ( + DriverEntry->ImageHandle, + &gEfiLoadedImageProtocolGuid, + DriverEntry->LoadedImage + ); + if (!EFI_ERROR (Status)) { + if (DriverEntry->LoadedImage->FilePath != NULL) { + gBS->FreePool (DriverEntry->LoadedImage->FilePath); + } + gBS->FreePool (DriverEntry->LoadedImage); + } + Status = SmmUninstallProtocolInterface ( + DriverEntry->SmmImageHandle, + &gEfiLoadedImageProtocolGuid, + &DriverEntry->SmmLoadedImage + ); + if (!EFI_ERROR(Status)) { + if (DriverEntry->SmmLoadedImage.FilePath != NULL) { + SmmFreePool (DriverEntry->SmmLoadedImage.FilePath); + } + } + } + + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_END, + &DriverEntry->ImageHandle, + sizeof (DriverEntry->ImageHandle) + ); + + if (!PreviousSmmEntryPointRegistered && gSmmCorePrivate->SmmEntryPointRegistered) { + // + // Return immediately if the SMM Entry Point was registered by the SMM + // Driver that was just dispatched. The SMM IPL will reinvoke the SMM + // Core Dispatcher. This is required so SMM Mode may be enabled as soon + // as all the dependent SMM Drivers for SMM Mode have been dispatched. + // Once the SMM Entry Point has been registered, then SMM Mode will be + // used. + // + gRequestDispatch = TRUE; + gDispatcherRunning = FALSE; + return EFI_NOT_READY; + } + } + + // + // Search DriverList for items to place on Scheduled Queue + // + ReadyToRun = FALSE; + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + + if (DriverEntry->DepexProtocolError){ + // + // If Section Extraction Protocol did not let the Depex be read before retry the read + // + Status = SmmGetDepexSectionAndPreProccess (DriverEntry); + } + + if (DriverEntry->Dependent) { + if (SmmIsSchedulable (DriverEntry)) { + SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); + ReadyToRun = TRUE; + } + } + } + } while (ReadyToRun); + + // + // If there is no more SMM driver to dispatch, stop the dispatch request + // + gRequestDispatch = FALSE; + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + + if (!DriverEntry->Initialized){ + // + // We have SMM driver pending to dispatch + // + gRequestDispatch = TRUE; + break; + } + } + + gDispatcherRunning = FALSE; + + return 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 +SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( + IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry + ) +{ + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + // + // Process Before Dependency + // + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->Before && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) { + DEBUG ((DEBUG_DISPATCH, "Evaluate SMM 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")); + SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (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_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->After && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) { + DEBUG ((DEBUG_DISPATCH, "Evaluate SMM 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")); + SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (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 its drivers placed on the + mDiscoveredList. This function 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 +FvIsBeingProcessed ( + IN EFI_HANDLE FvHandle + ) +{ + KNOWN_HANDLE *KnownHandle; + + KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE)); + ASSERT (KnownHandle != NULL); + + KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE; + KnownHandle->Handle = FvHandle; + InsertTailList (&mFvHandleList, &KnownHandle->Link); +} + +/** + Convert FvHandle and DriverName into an EFI device path + + @param Fv Fv protocol, needed to read Depex info out of + FLASH. + @param FvHandle Handle for Fv, needed in the + EFI_SMM_DRIVER_ENTRY so that the PE image can be + read out of the FV at a later time. + @param DriverName Name of driver to add to mDiscoveredList. + + @return Pointer to device path constructed from FvHandle and DriverName + +**/ +EFI_DEVICE_PATH_PROTOCOL * +SmmFvToDevicePath ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, + IN EFI_HANDLE FvHandle, + IN EFI_GUID *DriverName + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; + EFI_DEVICE_PATH_PROTOCOL *FileNameDevicePath; + + // + // Remember the device path of the FV + // + Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); + if (EFI_ERROR (Status)) { + FileNameDevicePath = NULL; + } else { + // + // Build a device path to the file in the FV to pass into gBS->LoadImage + // + EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, DriverName); + SetDevicePathEndNode (&mFvDevicePath.End); + + // + // Note: FileNameDevicePath is in DXE memory + // + FileNameDevicePath = AppendDevicePath ( + FvDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath + ); + } + return FileNameDevicePath; +} + +/** + Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry, + and initialize 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 SMM driver states. + + @param Fv Fv protocol, needed to read Depex info out of + FLASH. + @param FvHandle Handle for Fv, needed in the + EFI_SMM_DRIVER_ENTRY so that the PE image can be + read out of the FV at a later time. + @param DriverName Name of driver to add to mDiscoveredList. + + @retval EFI_SUCCESS If driver was added to the mDiscoveredList. + @retval EFI_ALREADY_STARTED The driver has already been started. Only one + DriverName may be active in the system at any one + time. + +**/ +EFI_STATUS +SmmAddToDriverList ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, + IN EFI_HANDLE FvHandle, + IN EFI_GUID *DriverName + ) +{ + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + // + // Create the Driver Entry for the list. ZeroPool initializes lots of variables to + // NULL or FALSE. + // + DriverEntry = AllocateZeroPool (sizeof (EFI_SMM_DRIVER_ENTRY)); + ASSERT (DriverEntry != NULL); + + DriverEntry->Signature = EFI_SMM_DRIVER_ENTRY_SIGNATURE; + CopyGuid (&DriverEntry->FileName, DriverName); + DriverEntry->FvHandle = FvHandle; + DriverEntry->Fv = Fv; + DriverEntry->FvFileDevicePath = SmmFvToDevicePath (Fv, FvHandle, DriverName); + + SmmGetDepexSectionAndPreProccess (DriverEntry); + + InsertTailList (&mDiscoveredList, &DriverEntry->Link); + gRequestDispatch = TRUE; + + return EFI_SUCCESS; +} + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + Event notification that is fired every time a FV dispatch protocol is added. + More than one protocol may have been added when this event is fired, so you + must loop on SmmLocateHandle () to see how many protocols were added and + do the following to each FV: + If the Fv has already been processed, skip it. If the Fv has not been + processed then mark it as being processed, as we are about to process it. + Read the Fv and add any driver in the Fv to the mDiscoveredList.The + mDiscoveredList is never free'ed and contains variables that define + the other states the SMM driver transitions to.. + While you are at it read the A Priori file into memory. + Place drivers in the A Priori list onto the mScheduledQueue. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmDriverDispatchHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_STATUS GetNextFileStatus; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; + EFI_HANDLE FvHandle; + EFI_GUID NameGuid; + UINTN Key; + EFI_FV_FILETYPE Type; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINTN Size; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + EFI_GUID *AprioriFile; + UINTN AprioriEntryCount; + UINTN HandleIndex; + UINTN SmmTypeIndex; + UINTN AprioriIndex; + LIST_ENTRY *Link; + UINT32 AuthenticationStatus; + UINTN SizeOfBuffer; + + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + FvHandle = HandleBuffer[HandleIndex]; + + if (FvHasBeenProcessed (FvHandle)) { + // + // This Fv has already been processed so lets skip it! + // + continue; + } + + // + // Since we are about to process this Fv mark it as processed. + // + FvIsBeingProcessed (FvHandle); + + Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv); + if (EFI_ERROR (Status)) { + // + // FvHandle must have a Firmware Volume2 Protocol thus we should never get here. + // + ASSERT (FALSE); + continue; + } + + Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); + if (EFI_ERROR (Status)) { + // + // The Firmware volume doesn't have device path, can't be dispatched. + // + continue; + } + + // + // Discover Drivers in FV and add them to the Discovered Driver List. + // Process EFI_FV_FILETYPE_SMM type and then EFI_FV_FILETYPE_COMBINED_SMM_DXE + // EFI_FV_FILETYPE_SMM_CORE is processed to produce a Loaded Image protocol for the core + // + for (SmmTypeIndex = 0; SmmTypeIndex < sizeof (mSmmFileTypes)/sizeof (EFI_FV_FILETYPE); SmmTypeIndex++) { + // + // Initialize the search key + // + Key = 0; + do { + Type = mSmmFileTypes[SmmTypeIndex]; + GetNextFileStatus = Fv->GetNextFile ( + Fv, + &Key, + &Type, + &NameGuid, + &Attributes, + &Size + ); + if (!EFI_ERROR (GetNextFileStatus)) { + if (Type == EFI_FV_FILETYPE_SMM_CORE) { + // + // If this is the SMM core fill in it's DevicePath & DeviceHandle + // + if (mSmmCoreLoadedImage->FilePath == NULL) { + // + // Maybe one special FV contains only one SMM_CORE module, so its device path must + // be initialized completely. + // + EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid); + SetDevicePathEndNode (&mFvDevicePath.End); + + // + // Make an EfiBootServicesData buffer copy of FilePath + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath), + (VOID **)&mSmmCoreLoadedImage->FilePath + ); + ASSERT_EFI_ERROR (Status); + CopyMem (mSmmCoreLoadedImage->FilePath, &mFvDevicePath, GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath)); + + mSmmCoreLoadedImage->DeviceHandle = FvHandle; + } + if (mSmmCoreDriverEntry->SmmLoadedImage.FilePath == NULL) { + // + // Maybe one special FV contains only one SMM_CORE module, so its device path must + // be initialized completely. + // + EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid); + SetDevicePathEndNode (&mFvDevicePath.End); + + // + // Make a buffer copy FilePath + // + Status = SmmAllocatePool ( + EfiRuntimeServicesData, + GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath), + (VOID **)&mSmmCoreDriverEntry->SmmLoadedImage.FilePath + ); + ASSERT_EFI_ERROR (Status); + CopyMem (mSmmCoreDriverEntry->SmmLoadedImage.FilePath, &mFvDevicePath, GetDevicePathSize((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath)); + + mSmmCoreDriverEntry->SmmLoadedImage.DeviceHandle = FvHandle; + } + } else { + SmmAddToDriverList (Fv, FvHandle, &NameGuid); + } + } + } while (!EFI_ERROR (GetNextFileStatus)); + } + + // + // Read the array of GUIDs from the Apriori file if it is present in the firmware volume + // (Note: AprioriFile is in DXE memory) + // + AprioriFile = NULL; + Status = Fv->ReadSection ( + Fv, + &gAprioriGuid, + EFI_SECTION_RAW, + 0, + (VOID **)&AprioriFile, + &SizeOfBuffer, + &AuthenticationStatus + ); + if (!EFI_ERROR (Status)) { + AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID); + } else { + AprioriEntryCount = 0; + } + + // + // Put drivers on Apriori List on the Scheduled queue. The Discovered List includes + // drivers not in the current FV and these must be skipped since the a priori list + // is only valid for the FV that it resided in. + // + + for (AprioriIndex = 0; AprioriIndex < AprioriEntryCount; AprioriIndex++) { + for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (CompareGuid (&DriverEntry->FileName, &AprioriFile[AprioriIndex]) && + (FvHandle == DriverEntry->FvHandle)) { + DriverEntry->Dependent = FALSE; + DriverEntry->Scheduled = TRUE; + InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink); + DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); + DEBUG ((DEBUG_DISPATCH, " RESULT = TRUE (Apriori)\n")); + break; + } + } + } + + // + // Free data allocated by Fv->ReadSection () + // + // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection + // used the UEFI Boot Services AllocatePool() function + // + gBS->FreePool (AprioriFile); + } + + // + // Execute the SMM Dispatcher on any newly discovered FVs and previously + // discovered SMM drivers that have been discovered but not dispatched. + // + Status = SmmDispatcher (); + + // + // 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 SMM Core Entry Point was just registered, then set flag to + // request the SMM Dispatcher to be restarted. + // + *(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_RESTART; + } else if (!EFI_ERROR (Status)) { + // + // Set the flag to show that the SMM Dispatcher executed without errors + // + *(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_SUCCESS; + } else { + // + // Set the flag to show that the SMM Dispatcher encountered an error + // + *(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_ERROR; + } + } + } + + return EFI_SUCCESS; +} + +/** + Traverse the discovered list for any drivers that were discovered but not loaded + because the dependency expressions evaluated to false. + +**/ +VOID +SmmDisplayDiscoveredNotDispatched ( + VOID + ) +{ + LIST_ENTRY *Link; + EFI_SMM_DRIVER_ENTRY *DriverEntry; + + for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) { + DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); + if (DriverEntry->Dependent) { + DEBUG ((DEBUG_LOAD, "SMM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName)); + } + } +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Handle.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Handle.c new file mode 100644 index 00000000..799efe39 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Handle.c @@ -0,0 +1,528 @@ +/** @file + SMM handle & protocol handling. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +// +// mProtocolDatabase - A list of all protocols in the system. (simple list for now) +// gHandleList - A list of all the handles in the system +// +LIST_ENTRY mProtocolDatabase = INITIALIZE_LIST_HEAD_VARIABLE (mProtocolDatabase); +LIST_ENTRY gHandleList = INITIALIZE_LIST_HEAD_VARIABLE (gHandleList); + +/** + Check whether a handle is a valid EFI_HANDLE + + @param UserHandle The handle to check + + @retval EFI_INVALID_PARAMETER The handle is NULL or not a valid EFI_HANDLE. + @retval EFI_SUCCESS The handle is valid EFI_HANDLE. + +**/ +EFI_STATUS +SmmValidateHandle ( + IN EFI_HANDLE UserHandle + ) +{ + IHANDLE *Handle; + + Handle = (IHANDLE *)UserHandle; + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + if (Handle->Signature != EFI_HANDLE_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +/** + Finds the protocol entry for the requested protocol. + + @param Protocol The ID of the protocol + @param Create Create a new entry if not found + + @return Protocol entry + +**/ +PROTOCOL_ENTRY * +SmmFindProtocolEntry ( + IN EFI_GUID *Protocol, + IN BOOLEAN Create + ) +{ + LIST_ENTRY *Link; + PROTOCOL_ENTRY *Item; + PROTOCOL_ENTRY *ProtEntry; + + // + // Search the database for the matching GUID + // + + ProtEntry = NULL; + for (Link = mProtocolDatabase.ForwardLink; + Link != &mProtocolDatabase; + Link = Link->ForwardLink) { + + Item = CR(Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE); + if (CompareGuid (&Item->ProtocolID, Protocol)) { + // + // This is the protocol entry + // + ProtEntry = Item; + break; + } + } + + // + // If the protocol entry was not found and Create is TRUE, then + // allocate a new entry + // + if ((ProtEntry == NULL) && Create) { + ProtEntry = AllocatePool (sizeof(PROTOCOL_ENTRY)); + if (ProtEntry != NULL) { + // + // Initialize new protocol entry structure + // + ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE; + CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol); + InitializeListHead (&ProtEntry->Protocols); + InitializeListHead (&ProtEntry->Notify); + + // + // Add it to protocol database + // + InsertTailList (&mProtocolDatabase, &ProtEntry->AllEntries); + } + } + return ProtEntry; +} + +/** + Finds the protocol instance for the requested handle and protocol. + Note: This function doesn't do parameters checking, it's caller's responsibility + to pass in valid parameters. + + @param Handle The handle to search the protocol on + @param Protocol GUID of the protocol + @param Interface The interface for the protocol being searched + + @return Protocol instance (NULL: Not found) + +**/ +PROTOCOL_INTERFACE * +SmmFindProtocolInterface ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_ENTRY *ProtEntry; + LIST_ENTRY *Link; + + Prot = NULL; + + // + // Lookup the protocol entry for this protocol ID + // + ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); + if (ProtEntry != NULL) { + // + // Look at each protocol interface for any matches + // + for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link=Link->ForwardLink) { + // + // If this protocol interface matches, remove it + // + Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE); + if (Prot->Interface == Interface && Prot->Protocol == ProtEntry) { + break; + } + Prot = NULL; + } + } + return Prot; +} + +/** + Wrapper function to SmmInstallProtocolInterfaceNotify. This is the public API which + Calls the private one which contains a BOOLEAN parameter for notifications + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + + @return Status code + +**/ +EFI_STATUS +EFIAPI +SmmInstallProtocolInterface ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface + ) +{ + return SmmInstallProtocolInterfaceNotify ( + UserHandle, + Protocol, + InterfaceType, + Interface, + TRUE + ); +} + +/** + Installs a protocol interface into the boot services environment. + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + @param Notify indicates whether notify the notification list + for this protocol + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate + @retval EFI_SUCCESS Protocol interface successfully installed + +**/ +EFI_STATUS +SmmInstallProtocolInterfaceNotify ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface, + IN BOOLEAN Notify + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_ENTRY *ProtEntry; + IHANDLE *Handle; + EFI_STATUS Status; + VOID *ExistingInterface; + + // + // returns EFI_INVALID_PARAMETER if InterfaceType is invalid. + // Also added check for invalid UserHandle and Protocol pointers. + // + if (UserHandle == NULL || Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (InterfaceType != EFI_NATIVE_INTERFACE) { + return EFI_INVALID_PARAMETER; + } + + // + // Print debug message + // + DEBUG((DEBUG_LOAD | DEBUG_INFO, "SmmInstallProtocolInterface: %g %p\n", Protocol, Interface)); + + Status = EFI_OUT_OF_RESOURCES; + Prot = NULL; + Handle = NULL; + + if (*UserHandle != NULL) { + Status = SmmHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface); + if (!EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Lookup the Protocol Entry for the requested protocol + // + ProtEntry = SmmFindProtocolEntry (Protocol, TRUE); + if (ProtEntry == NULL) { + goto Done; + } + + // + // Allocate a new protocol interface structure + // + Prot = AllocateZeroPool (sizeof(PROTOCOL_INTERFACE)); + if (Prot == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // If caller didn't supply a handle, allocate a new one + // + Handle = (IHANDLE *)*UserHandle; + if (Handle == NULL) { + Handle = AllocateZeroPool (sizeof(IHANDLE)); + if (Handle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Initialize new handler structure + // + Handle->Signature = EFI_HANDLE_SIGNATURE; + InitializeListHead (&Handle->Protocols); + + // + // Add this handle to the list global list of all handles + // in the system + // + InsertTailList (&gHandleList, &Handle->AllHandles); + } else { + Status = SmmValidateHandle (Handle); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "SmmInstallProtocolInterface: input handle at 0x%x is invalid\n", Handle)); + goto Done; + } + } + + // + // Each interface that is added must be unique + // + ASSERT (SmmFindProtocolInterface (Handle, Protocol, Interface) == NULL); + + // + // Initialize the protocol interface structure + // + Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE; + Prot->Handle = Handle; + Prot->Protocol = ProtEntry; + Prot->Interface = Interface; + + // + // Add this protocol interface to the head of the supported + // protocol list for this handle + // + InsertHeadList (&Handle->Protocols, &Prot->Link); + + // + // Add this protocol interface to the tail of the + // protocol entry + // + InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol); + + // + // Notify the notification list for this protocol + // + if (Notify) { + SmmNotifyProtocol (Prot); + } + Status = EFI_SUCCESS; + +Done: + if (!EFI_ERROR (Status)) { + // + // Return the new handle back to the caller + // + *UserHandle = Handle; + } else { + // + // There was an error, clean up + // + if (Prot != NULL) { + FreePool (Prot); + } + DEBUG((DEBUG_ERROR, "SmmInstallProtocolInterface: %g %p failed with %r\n", Protocol, Interface, Status)); + } + return Status; +} + +/** + Uninstalls all instances of a protocol:interfacer from a handle. + If the last protocol interface is remove from the handle, the + handle is freed. + + @param UserHandle The handle to remove the protocol handler from + @param Protocol The protocol, of protocol:interface, to remove + @param Interface The interface, of protocol:interface, to remove + + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_SUCCESS Protocol interface successfully uninstalled. + +**/ +EFI_STATUS +EFIAPI +SmmUninstallProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + EFI_STATUS Status; + IHANDLE *Handle; + PROTOCOL_INTERFACE *Prot; + + // + // Check that Protocol is valid + // + if (Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check that UserHandle is a valid handle + // + Status = SmmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check that Protocol exists on UserHandle, and Interface matches the interface in the database + // + Prot = SmmFindProtocolInterface (UserHandle, Protocol, Interface); + if (Prot == NULL) { + return EFI_NOT_FOUND; + } + + // + // Remove the protocol interface from the protocol + // + Status = EFI_NOT_FOUND; + Handle = (IHANDLE *)UserHandle; + Prot = SmmRemoveInterfaceFromProtocol (Handle, Protocol, Interface); + + if (Prot != NULL) { + // + // Remove the protocol interface from the handle + // + RemoveEntryList (&Prot->Link); + + // + // Free the memory + // + Prot->Signature = 0; + FreePool (Prot); + Status = EFI_SUCCESS; + } + + // + // If there are no more handlers for the handle, free the handle + // + if (IsListEmpty (&Handle->Protocols)) { + Handle->Signature = 0; + RemoveEntryList (&Handle->AllHandles); + FreePool (Handle); + } + return Status; +} + +/** + Locate a certain GUID protocol interface in a Handle's protocols. + + @param UserHandle The handle to obtain the protocol interface on + @param Protocol The GUID of the protocol + + @return The requested protocol interface for the handle + +**/ +PROTOCOL_INTERFACE * +SmmGetProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol + ) +{ + EFI_STATUS Status; + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_INTERFACE *Prot; + IHANDLE *Handle; + LIST_ENTRY *Link; + + Status = SmmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return NULL; + } + + Handle = (IHANDLE *)UserHandle; + + // + // Look at each protocol interface for a match + // + for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link = Link->ForwardLink) { + Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE); + ProtEntry = Prot->Protocol; + if (CompareGuid (&ProtEntry->ProtocolID, Protocol)) { + return Prot; + } + } + return NULL; +} + +/** + Queries a handle to determine if it supports a specified protocol. + + @param UserHandle The handle being queried. + @param Protocol The published unique identifier of the protocol. + @param Interface Supplies the address where a pointer to the + corresponding Protocol Interface is returned. + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the specified protocol. + @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE.. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER Interface is NULL. + +**/ +EFI_STATUS +EFIAPI +SmmHandleProtocol ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + PROTOCOL_INTERFACE *Prot; + + // + // Check for invalid Protocol + // + if (Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check for invalid Interface + // + if (Interface == NULL) { + return EFI_INVALID_PARAMETER; + } else { + *Interface = NULL; + } + + // + // Check for invalid UserHandle + // + Status = SmmValidateHandle (UserHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Look at each protocol interface for a match + // + Prot = SmmGetProtocolInterface (UserHandle, Protocol); + if (Prot == NULL) { + return EFI_UNSUPPORTED; + } + + // + // This is the protocol interface entry for this protocol + // + *Interface = Prot->Interface; + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.c new file mode 100644 index 00000000..e19e8834 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.c @@ -0,0 +1,1404 @@ +/** @file + UEFI Heap Guard functions. + +Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HeapGuard.h" + +// +// Global to avoid infinite reentrance of memory allocation when updating +// page table attributes, which may need allocating pages for new PDE/PTE. +// +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mOnGuarding = FALSE; + +// +// Pointer to table tracking the Guarded memory with bitmap, in which '1' +// is used to indicate memory guarded. '0' might be free memory or Guard +// page itself, depending on status of memory adjacent to it. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT64 mGuardedMemoryMap = 0; + +// +// Current depth level of map table pointed by mGuardedMemoryMap. +// mMapLevel must be initialized at least by 1. It will be automatically +// updated according to the address of memory just tracked. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMapLevel = 1; + +// +// Shift and mask for each level of map table +// +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH] + = GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelMask[GUARDED_HEAP_MAP_TABLE_DEPTH] + = GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS; + +// +// SMM memory attribute protocol +// +EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *mSmmMemoryAttribute = NULL; + +/** + Set corresponding bits in bitmap table to 1 according to the address. + + @param[in] Address Start address to set for. + @param[in] BitNumber Number of bits to set. + @param[in] BitMap Pointer to bitmap which covers the Address. + + @return VOID +**/ +STATIC +VOID +SetBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN BitNumber, + IN UINT64 *BitMap + ) +{ + UINTN Lsbs; + UINTN Qwords; + UINTN Msbs; + UINTN StartBit; + UINTN EndBit; + + StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address); + EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + + if ((StartBit + BitNumber) >= GUARDED_HEAP_MAP_ENTRY_BITS) { + Msbs = (GUARDED_HEAP_MAP_ENTRY_BITS - StartBit) % + GUARDED_HEAP_MAP_ENTRY_BITS; + Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + Qwords = (BitNumber - Msbs) / GUARDED_HEAP_MAP_ENTRY_BITS; + } else { + Msbs = BitNumber; + Lsbs = 0; + Qwords = 0; + } + + if (Msbs > 0) { + *BitMap |= LShiftU64 (LShiftU64 (1, Msbs) - 1, StartBit); + BitMap += 1; + } + + if (Qwords > 0) { + SetMem64 ((VOID *)BitMap, Qwords * GUARDED_HEAP_MAP_ENTRY_BYTES, + (UINT64)-1); + BitMap += Qwords; + } + + if (Lsbs > 0) { + *BitMap |= (LShiftU64 (1, Lsbs) - 1); + } +} + +/** + Set corresponding bits in bitmap table to 0 according to the address. + + @param[in] Address Start address to set for. + @param[in] BitNumber Number of bits to set. + @param[in] BitMap Pointer to bitmap which covers the Address. + + @return VOID. +**/ +STATIC +VOID +ClearBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN BitNumber, + IN UINT64 *BitMap + ) +{ + UINTN Lsbs; + UINTN Qwords; + UINTN Msbs; + UINTN StartBit; + UINTN EndBit; + + StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address); + EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + + if ((StartBit + BitNumber) >= GUARDED_HEAP_MAP_ENTRY_BITS) { + Msbs = (GUARDED_HEAP_MAP_ENTRY_BITS - StartBit) % + GUARDED_HEAP_MAP_ENTRY_BITS; + Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + Qwords = (BitNumber - Msbs) / GUARDED_HEAP_MAP_ENTRY_BITS; + } else { + Msbs = BitNumber; + Lsbs = 0; + Qwords = 0; + } + + if (Msbs > 0) { + *BitMap &= ~LShiftU64 (LShiftU64 (1, Msbs) - 1, StartBit); + BitMap += 1; + } + + if (Qwords > 0) { + SetMem64 ((VOID *)BitMap, Qwords * GUARDED_HEAP_MAP_ENTRY_BYTES, 0); + BitMap += Qwords; + } + + if (Lsbs > 0) { + *BitMap &= ~(LShiftU64 (1, Lsbs) - 1); + } +} + +/** + Get corresponding bits in bitmap table according to the address. + + The value of bit 0 corresponds to the status of memory at given Address. + No more than 64 bits can be retrieved in one call. + + @param[in] Address Start address to retrieve bits for. + @param[in] BitNumber Number of bits to get. + @param[in] BitMap Pointer to bitmap which covers the Address. + + @return An integer containing the bits information. +**/ +STATIC +UINT64 +GetBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN BitNumber, + IN UINT64 *BitMap + ) +{ + UINTN StartBit; + UINTN EndBit; + UINTN Lsbs; + UINTN Msbs; + UINT64 Result; + + ASSERT (BitNumber <= GUARDED_HEAP_MAP_ENTRY_BITS); + + StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address); + EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + + if ((StartBit + BitNumber) > GUARDED_HEAP_MAP_ENTRY_BITS) { + Msbs = GUARDED_HEAP_MAP_ENTRY_BITS - StartBit; + Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + } else { + Msbs = BitNumber; + Lsbs = 0; + } + + if (StartBit == 0 && BitNumber == GUARDED_HEAP_MAP_ENTRY_BITS) { + Result = *BitMap; + } else { + Result = RShiftU64((*BitMap), StartBit) & (LShiftU64(1, Msbs) - 1); + if (Lsbs > 0) { + BitMap += 1; + Result |= LShiftU64 ((*BitMap) & (LShiftU64 (1, Lsbs) - 1), Msbs); + } + } + + return Result; +} + +/** + Helper function to allocate pages without Guard for internal uses. + + @param[in] Pages Page number. + + @return Address of memory allocated. +**/ +VOID * +PageAlloc ( + IN UINTN Pages + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Memory; + + Status = SmmInternalAllocatePages (AllocateAnyPages, EfiRuntimeServicesData, + Pages, &Memory, FALSE); + if (EFI_ERROR (Status)) { + Memory = 0; + } + + return (VOID *)(UINTN)Memory; +} + +/** + Locate the pointer of bitmap from the guarded memory bitmap tables, which + covers the given Address. + + @param[in] Address Start address to search the bitmap for. + @param[in] AllocMapUnit Flag to indicate memory allocation for the table. + @param[out] BitMap Pointer to bitmap which covers the Address. + + @return The bit number from given Address to the end of current map table. +**/ +UINTN +FindGuardedMemoryMap ( + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN AllocMapUnit, + OUT UINT64 **BitMap + ) +{ + UINTN Level; + UINT64 *GuardMap; + UINT64 MapMemory; + UINTN Index; + UINTN Size; + UINTN BitsToUnitEnd; + + // + // Adjust current map table depth according to the address to access + // + while (AllocMapUnit && + mMapLevel < GUARDED_HEAP_MAP_TABLE_DEPTH && + RShiftU64 ( + Address, + mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1] + ) != 0) { + + if (mGuardedMemoryMap != 0) { + Size = (mLevelMask[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1] + 1) + * GUARDED_HEAP_MAP_ENTRY_BYTES; + MapMemory = (UINT64)(UINTN)PageAlloc (EFI_SIZE_TO_PAGES (Size)); + ASSERT (MapMemory != 0); + + SetMem ((VOID *)(UINTN)MapMemory, Size, 0); + + *(UINT64 *)(UINTN)MapMemory = mGuardedMemoryMap; + mGuardedMemoryMap = MapMemory; + } + + mMapLevel++; + + } + + GuardMap = &mGuardedMemoryMap; + for (Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Level < GUARDED_HEAP_MAP_TABLE_DEPTH; + ++Level) { + + if (*GuardMap == 0) { + if (!AllocMapUnit) { + GuardMap = NULL; + break; + } + + Size = (mLevelMask[Level] + 1) * GUARDED_HEAP_MAP_ENTRY_BYTES; + MapMemory = (UINT64)(UINTN)PageAlloc (EFI_SIZE_TO_PAGES (Size)); + ASSERT (MapMemory != 0); + + SetMem ((VOID *)(UINTN)MapMemory, Size, 0); + *GuardMap = MapMemory; + } + + Index = (UINTN)RShiftU64 (Address, mLevelShift[Level]); + Index &= mLevelMask[Level]; + GuardMap = (UINT64 *)(UINTN)((*GuardMap) + Index * sizeof (UINT64)); + + } + + BitsToUnitEnd = GUARDED_HEAP_MAP_BITS - GUARDED_HEAP_MAP_BIT_INDEX (Address); + *BitMap = GuardMap; + + return BitsToUnitEnd; +} + +/** + Set corresponding bits in bitmap table to 1 according to given memory range. + + @param[in] Address Memory address to guard from. + @param[in] NumberOfPages Number of pages to guard. + + @return VOID +**/ +VOID +EFIAPI +SetGuardedMemoryBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN NumberOfPages + ) +{ + UINT64 *BitMap; + UINTN Bits; + UINTN BitsToUnitEnd; + + while (NumberOfPages > 0) { + BitsToUnitEnd = FindGuardedMemoryMap (Address, TRUE, &BitMap); + ASSERT (BitMap != NULL); + + if (NumberOfPages > BitsToUnitEnd) { + // Cross map unit + Bits = BitsToUnitEnd; + } else { + Bits = NumberOfPages; + } + + SetBits (Address, Bits, BitMap); + + NumberOfPages -= Bits; + Address += EFI_PAGES_TO_SIZE (Bits); + } +} + +/** + Clear corresponding bits in bitmap table according to given memory range. + + @param[in] Address Memory address to unset from. + @param[in] NumberOfPages Number of pages to unset guard. + + @return VOID +**/ +VOID +EFIAPI +ClearGuardedMemoryBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN NumberOfPages + ) +{ + UINT64 *BitMap; + UINTN Bits; + UINTN BitsToUnitEnd; + + while (NumberOfPages > 0) { + BitsToUnitEnd = FindGuardedMemoryMap (Address, TRUE, &BitMap); + ASSERT (BitMap != NULL); + + if (NumberOfPages > BitsToUnitEnd) { + // Cross map unit + Bits = BitsToUnitEnd; + } else { + Bits = NumberOfPages; + } + + ClearBits (Address, Bits, BitMap); + + NumberOfPages -= Bits; + Address += EFI_PAGES_TO_SIZE (Bits); + } +} + +/** + Retrieve corresponding bits in bitmap table according to given memory range. + + @param[in] Address Memory address to retrieve from. + @param[in] NumberOfPages Number of pages to retrieve. + + @return An integer containing the guarded memory bitmap. +**/ +UINTN +GetGuardedMemoryBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN NumberOfPages + ) +{ + UINT64 *BitMap; + UINTN Bits; + UINTN Result; + UINTN Shift; + UINTN BitsToUnitEnd; + + ASSERT (NumberOfPages <= GUARDED_HEAP_MAP_ENTRY_BITS); + + Result = 0; + Shift = 0; + while (NumberOfPages > 0) { + BitsToUnitEnd = FindGuardedMemoryMap (Address, FALSE, &BitMap); + + if (NumberOfPages > BitsToUnitEnd) { + // Cross map unit + Bits = BitsToUnitEnd; + } else { + Bits = NumberOfPages; + } + + if (BitMap != NULL) { + Result |= LShiftU64 (GetBits (Address, Bits, BitMap), Shift); + } + + Shift += Bits; + NumberOfPages -= Bits; + Address += EFI_PAGES_TO_SIZE (Bits); + } + + return Result; +} + +/** + Get bit value in bitmap table for the given address. + + @param[in] Address The address to retrieve for. + + @return 1 or 0. +**/ +UINTN +EFIAPI +GetGuardMapBit ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + UINT64 *GuardMap; + + FindGuardedMemoryMap (Address, FALSE, &GuardMap); + if (GuardMap != NULL) { + if (RShiftU64 (*GuardMap, + GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address)) & 1) { + return 1; + } + } + + return 0; +} + + +/** + Check to see if the page at the given address is a Guard page or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is a Guard page. + @return FALSE The page at Address is not a Guard page. +**/ +BOOLEAN +EFIAPI +IsGuardPage ( + IN EFI_PHYSICAL_ADDRESS Address +) +{ + UINTN BitMap; + + // + // There must be at least one guarded page before and/or after given + // address if it's a Guard page. The bitmap pattern should be one of + // 001, 100 and 101 + // + BitMap = GetGuardedMemoryBits (Address - EFI_PAGE_SIZE, 3); + return ((BitMap == BIT0) || (BitMap == BIT2) || (BitMap == (BIT2 | BIT0))); +} + + + +/** + Check to see if the page at the given address is guarded or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is guarded. + @return FALSE The page at Address is not guarded. +**/ +BOOLEAN +EFIAPI +IsMemoryGuarded ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + return (GetGuardMapBit (Address) == 1); +} + +/** + Set the page at the given address to be a Guard page. + + This is done by changing the page table attribute to be NOT PRESENT. + + @param[in] BaseAddress Page address to Guard at. + + @return VOID. +**/ +VOID +EFIAPI +SetGuardPage ( + IN EFI_PHYSICAL_ADDRESS BaseAddress + ) +{ + EFI_STATUS Status; + + if (mSmmMemoryAttribute != NULL) { + mOnGuarding = TRUE; + Status = mSmmMemoryAttribute->SetMemoryAttributes ( + mSmmMemoryAttribute, + BaseAddress, + EFI_PAGE_SIZE, + EFI_MEMORY_RP + ); + ASSERT_EFI_ERROR (Status); + mOnGuarding = FALSE; + } +} + +/** + Unset the Guard page at the given address to the normal memory. + + This is done by changing the page table attribute to be PRESENT. + + @param[in] BaseAddress Page address to Guard at. + + @return VOID. +**/ +VOID +EFIAPI +UnsetGuardPage ( + IN EFI_PHYSICAL_ADDRESS BaseAddress + ) +{ + EFI_STATUS Status; + + if (mSmmMemoryAttribute != NULL) { + mOnGuarding = TRUE; + Status = mSmmMemoryAttribute->ClearMemoryAttributes ( + mSmmMemoryAttribute, + BaseAddress, + EFI_PAGE_SIZE, + EFI_MEMORY_RP + ); + ASSERT_EFI_ERROR (Status); + mOnGuarding = FALSE; + } +} + +/** + Check to see if the memory at the given address should be guarded or not. + + @param[in] MemoryType Memory type to check. + @param[in] AllocateType Allocation type to check. + @param[in] PageOrPool Indicate a page allocation or pool allocation. + + + @return TRUE The given type of memory should be guarded. + @return FALSE The given type of memory should not be guarded. +**/ +BOOLEAN +IsMemoryTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType, + IN EFI_ALLOCATE_TYPE AllocateType, + IN UINT8 PageOrPool + ) +{ + UINT64 TestBit; + UINT64 ConfigBit; + + if ((PcdGet8 (PcdHeapGuardPropertyMask) & PageOrPool) == 0 + || mOnGuarding + || AllocateType == AllocateAddress) { + return FALSE; + } + + ConfigBit = 0; + if ((PageOrPool & GUARD_HEAP_TYPE_POOL) != 0) { + ConfigBit |= PcdGet64 (PcdHeapGuardPoolType); + } + + if ((PageOrPool & GUARD_HEAP_TYPE_PAGE) != 0) { + ConfigBit |= PcdGet64 (PcdHeapGuardPageType); + } + + if (MemoryType == EfiRuntimeServicesData || + MemoryType == EfiRuntimeServicesCode) { + TestBit = LShiftU64 (1, MemoryType); + } else if (MemoryType == EfiMaxMemoryType) { + TestBit = (UINT64)-1; + } else { + TestBit = 0; + } + + return ((ConfigBit & TestBit) != 0); +} + +/** + Check to see if the pool at the given address should be guarded or not. + + @param[in] MemoryType Pool type to check. + + + @return TRUE The given type of pool should be guarded. + @return FALSE The given type of pool should not be guarded. +**/ +BOOLEAN +IsPoolTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + return IsMemoryTypeToGuard (MemoryType, AllocateAnyPages, + GUARD_HEAP_TYPE_POOL); +} + +/** + Check to see if the page at the given address should be guarded or not. + + @param[in] MemoryType Page type to check. + @param[in] AllocateType Allocation type to check. + + @return TRUE The given type of page should be guarded. + @return FALSE The given type of page should not be guarded. +**/ +BOOLEAN +IsPageTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType, + IN EFI_ALLOCATE_TYPE AllocateType + ) +{ + return IsMemoryTypeToGuard (MemoryType, AllocateType, GUARD_HEAP_TYPE_PAGE); +} + +/** + Check to see if the heap guard is enabled for page and/or pool allocation. + + @return TRUE/FALSE. +**/ +BOOLEAN +IsHeapGuardEnabled ( + VOID + ) +{ + return IsMemoryTypeToGuard (EfiMaxMemoryType, AllocateAnyPages, + GUARD_HEAP_TYPE_POOL|GUARD_HEAP_TYPE_PAGE); +} + +/** + Set head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to set guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID. +**/ +VOID +SetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_PHYSICAL_ADDRESS GuardPage; + + // + // Set tail Guard + // + GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages); + if (!IsGuardPage (GuardPage)) { + SetGuardPage (GuardPage); + } + + // Set head Guard + GuardPage = Memory - EFI_PAGES_TO_SIZE (1); + if (!IsGuardPage (GuardPage)) { + SetGuardPage (GuardPage); + } + + // + // Mark the memory range as Guarded + // + SetGuardedMemoryBits (Memory, NumberOfPages); +} + +/** + Unset head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to unset guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID. +**/ +VOID +UnsetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_PHYSICAL_ADDRESS GuardPage; + UINT64 GuardBitmap; + + if (NumberOfPages == 0) { + return; + } + + // + // Head Guard must be one page before, if any. + // + // MSB-> 1 0 <-LSB + // ------------------- + // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard) + // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard) + // 1 X -> Don't free first page (need a new Guard) + // (it'll be turned into a Guard page later) + // ------------------- + // Start -> -1 -2 + // + GuardPage = Memory - EFI_PAGES_TO_SIZE (1); + GuardBitmap = GetGuardedMemoryBits (Memory - EFI_PAGES_TO_SIZE (2), 2); + if ((GuardBitmap & BIT1) == 0) { + // + // Head Guard exists. + // + if ((GuardBitmap & BIT0) == 0) { + // + // If the head Guard is not a tail Guard of adjacent memory block, + // unset it. + // + UnsetGuardPage (GuardPage); + } + } else { + // + // Pages before memory to free are still in Guard. It's a partial free + // case. Turn first page of memory block to free into a new Guard. + // + SetGuardPage (Memory); + } + + // + // Tail Guard must be the page after this memory block to free, if any. + // + // MSB-> 1 0 <-LSB + // -------------------- + // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard) + // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard) + // X 1 -> Don't free last page (need a new Guard) + // (it'll be turned into a Guard page later) + // -------------------- + // +1 +0 <- End + // + GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages); + GuardBitmap = GetGuardedMemoryBits (GuardPage, 2); + if ((GuardBitmap & BIT0) == 0) { + // + // Tail Guard exists. + // + if ((GuardBitmap & BIT1) == 0) { + // + // If the tail Guard is not a head Guard of adjacent memory block, + // free it; otherwise, keep it. + // + UnsetGuardPage (GuardPage); + } + } else { + // + // Pages after memory to free are still in Guard. It's a partial free + // case. We need to keep one page to be a head Guard. + // + SetGuardPage (GuardPage - EFI_PAGES_TO_SIZE (1)); + } + + // + // No matter what, we just clear the mark of the Guarded memory. + // + ClearGuardedMemoryBits(Memory, NumberOfPages); +} + + + +/** + Adjust the start address and number of pages to free according to Guard. + + The purpose of this function is to keep the shared Guard page with adjacent + memory block if it's still in guard, or free it if no more sharing. Another + is to reserve pages as Guard pages in partial page free situation. + + @param[in,out] Memory Base address of memory to free. + @param[in,out] NumberOfPages Size of memory to free. + + @return VOID. +**/ +VOID +AdjustMemoryF ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ) +{ + EFI_PHYSICAL_ADDRESS Start; + EFI_PHYSICAL_ADDRESS MemoryToTest; + UINTN PagesToFree; + UINT64 GuardBitmap; + UINT64 Attributes; + + if (Memory == NULL || NumberOfPages == NULL || *NumberOfPages == 0) { + return; + } + + Start = *Memory; + PagesToFree = *NumberOfPages; + + // + // In case the memory to free is marked as read-only (e.g. EfiRuntimeServicesCode). + // + if (mSmmMemoryAttribute != NULL) { + Attributes = 0; + mSmmMemoryAttribute->GetMemoryAttributes ( + mSmmMemoryAttribute, + Start, + EFI_PAGES_TO_SIZE (PagesToFree), + &Attributes + ); + if ((Attributes & EFI_MEMORY_RO) != 0) { + mSmmMemoryAttribute->ClearMemoryAttributes ( + mSmmMemoryAttribute, + Start, + EFI_PAGES_TO_SIZE (PagesToFree), + EFI_MEMORY_RO + ); + } + } + + // + // Head Guard must be one page before, if any. + // + // MSB-> 1 0 <-LSB + // ------------------- + // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard) + // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard) + // 1 X -> Don't free first page (need a new Guard) + // (it'll be turned into a Guard page later) + // ------------------- + // Start -> -1 -2 + // + MemoryToTest = Start - EFI_PAGES_TO_SIZE (2); + GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2); + if ((GuardBitmap & BIT1) == 0) { + // + // Head Guard exists. + // + if ((GuardBitmap & BIT0) == 0) { + // + // If the head Guard is not a tail Guard of adjacent memory block, + // free it; otherwise, keep it. + // + Start -= EFI_PAGES_TO_SIZE (1); + PagesToFree += 1; + } + } else { + // + // No Head Guard, and pages before memory to free are still in Guard. It's a + // partial free case. We need to keep one page to be a tail Guard. + // + Start += EFI_PAGES_TO_SIZE (1); + PagesToFree -= 1; + } + + // + // Tail Guard must be the page after this memory block to free, if any. + // + // MSB-> 1 0 <-LSB + // -------------------- + // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard) + // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard) + // X 1 -> Don't free last page (need a new Guard) + // (it'll be turned into a Guard page later) + // -------------------- + // +1 +0 <- End + // + MemoryToTest = Start + EFI_PAGES_TO_SIZE (PagesToFree); + GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2); + if ((GuardBitmap & BIT0) == 0) { + // + // Tail Guard exists. + // + if ((GuardBitmap & BIT1) == 0) { + // + // If the tail Guard is not a head Guard of adjacent memory block, + // free it; otherwise, keep it. + // + PagesToFree += 1; + } + } else if (PagesToFree > 0) { + // + // No Tail Guard, and pages after memory to free are still in Guard. It's a + // partial free case. We need to keep one page to be a head Guard. + // + PagesToFree -= 1; + } + + *Memory = Start; + *NumberOfPages = PagesToFree; +} + + +/** + Adjust the pool head position to make sure the Guard page is adjavent to + pool tail or pool head. + + @param[in] Memory Base address of memory allocated. + @param[in] NoPages Number of pages actually allocated. + @param[in] Size Size of memory requested. + (plus pool head/tail overhead) + + @return Address of pool head +**/ +VOID * +AdjustPoolHeadA ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages, + IN UINTN Size + ) +{ + if (Memory == 0 || (PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0) { + // + // Pool head is put near the head Guard + // + return (VOID *)(UINTN)Memory; + } + + // + // Pool head is put near the tail Guard + // + Size = ALIGN_VALUE (Size, 8); + return (VOID *)(UINTN)(Memory + EFI_PAGES_TO_SIZE (NoPages) - Size); +} + +/** + Get the page base address according to pool head address. + + @param[in] Memory Head address of pool to free. + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadF ( + IN EFI_PHYSICAL_ADDRESS Memory + ) +{ + if (Memory == 0 || (PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0) { + // + // Pool head is put near the head Guard + // + return (VOID *)(UINTN)Memory; + } + + // + // Pool head is put near the tail Guard + // + return (VOID *)(UINTN)(Memory & ~EFI_PAGE_MASK); +} + +/** + Helper function of memory allocation with Guard pages. + + @param FreePageList The free page node. + @param NumberOfPages Number of pages to be allocated. + @param MaxAddress Request to allocate memory below this address. + @param MemoryType Type of memory requested. + + @return Memory address of allocated pages. +**/ +UINTN +InternalAllocMaxAddressWithGuard ( + IN OUT LIST_ENTRY *FreePageList, + IN UINTN NumberOfPages, + IN UINTN MaxAddress, + IN EFI_MEMORY_TYPE MemoryType + + ) +{ + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + UINTN PagesToAlloc; + UINTN HeadGuard; + UINTN TailGuard; + UINTN Address; + + 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) { + + // + // We may need 1 or 2 more pages for Guard. Check it out. + // + PagesToAlloc = NumberOfPages; + TailGuard = (UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages); + if (!IsGuardPage (TailGuard)) { + // + // Add one if no Guard at the end of current free memory block. + // + PagesToAlloc += 1; + TailGuard = 0; + } + + HeadGuard = (UINTN)Pages + + EFI_PAGES_TO_SIZE (Pages->NumberOfPages - PagesToAlloc) - + EFI_PAGE_SIZE; + if (!IsGuardPage (HeadGuard)) { + // + // Add one if no Guard at the page before the address to allocate + // + PagesToAlloc += 1; + HeadGuard = 0; + } + + if (Pages->NumberOfPages < PagesToAlloc) { + // Not enough space to allocate memory with Guards? Try next block. + continue; + } + + Address = InternalAllocPagesOnOneNode (Pages, PagesToAlloc, MaxAddress); + ConvertSmmMemoryMapEntry(MemoryType, Address, PagesToAlloc, FALSE); + CoreFreeMemoryMapStack(); + if (HeadGuard == 0) { + // Don't pass the Guard page to user. + Address += EFI_PAGE_SIZE; + } + SetGuardForMemory (Address, NumberOfPages); + return Address; + } + } + + return (UINTN)(-1); +} + +/** + Helper function of memory free with Guard pages. + + @param[in] Memory Base address of memory being freed. + @param[in] NumberOfPages The number of pages to free. + @param[in] AddRegion If this memory is new added region. + + @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 +SmmInternalFreePagesExWithGuard ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN AddRegion + ) +{ + EFI_PHYSICAL_ADDRESS MemoryToFree; + UINTN PagesToFree; + + if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) { + return EFI_INVALID_PARAMETER; + } + + MemoryToFree = Memory; + PagesToFree = NumberOfPages; + + AdjustMemoryF (&MemoryToFree, &PagesToFree); + UnsetGuardForMemory (Memory, NumberOfPages); + if (PagesToFree == 0) { + return EFI_SUCCESS; + } + + return SmmInternalFreePagesEx (MemoryToFree, PagesToFree, AddRegion); +} + +/** + Set all Guard pages which cannot be set during the non-SMM mode time. +**/ +VOID +SetAllGuardPages ( + VOID + ) +{ + UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 TableEntry; + UINT64 Address; + UINT64 GuardPage; + INTN Level; + UINTN Index; + BOOLEAN OnGuarding; + + if (mGuardedMemoryMap == 0 || + mMapLevel == 0 || + mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH) { + return; + } + + CopyMem (Entries, mLevelMask, sizeof (Entries)); + CopyMem (Shifts, mLevelShift, sizeof (Shifts)); + + SetMem (Tables, sizeof(Tables), 0); + SetMem (Addresses, sizeof(Addresses), 0); + SetMem (Indices, sizeof(Indices), 0); + + Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Tables[Level] = mGuardedMemoryMap; + Address = 0; + OnGuarding = FALSE; + + DEBUG_CODE ( + DumpGuardedMemoryBitmap (); + ); + + while (TRUE) { + if (Indices[Level] > Entries[Level]) { + Tables[Level] = 0; + Level -= 1; + } else { + + TableEntry = ((UINT64 *)(UINTN)(Tables[Level]))[Indices[Level]]; + Address = Addresses[Level]; + + if (TableEntry == 0) { + + OnGuarding = FALSE; + + } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + + Level += 1; + Tables[Level] = TableEntry; + Addresses[Level] = Address; + Indices[Level] = 0; + + continue; + + } else { + + Index = 0; + while (Index < GUARDED_HEAP_MAP_ENTRY_BITS) { + if ((TableEntry & 1) == 1) { + if (OnGuarding) { + GuardPage = 0; + } else { + GuardPage = Address - EFI_PAGE_SIZE; + } + OnGuarding = TRUE; + } else { + if (OnGuarding) { + GuardPage = Address; + } else { + GuardPage = 0; + } + OnGuarding = FALSE; + } + + if (GuardPage != 0) { + SetGuardPage (GuardPage); + } + + if (TableEntry == 0) { + break; + } + + TableEntry = RShiftU64 (TableEntry, 1); + Address += EFI_PAGE_SIZE; + Index += 1; + } + } + } + + if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) { + break; + } + + Indices[Level] += 1; + Address = (Level == 0) ? 0 : Addresses[Level - 1]; + Addresses[Level] = Address | LShiftU64(Indices[Level], Shifts[Level]); + + } +} + +/** + Hook function used to set all Guard pages after entering SMM mode. +**/ +VOID +SmmEntryPointMemoryManagementHook ( + VOID + ) +{ + EFI_STATUS Status; + + if (mSmmMemoryAttribute == NULL) { + Status = SmmLocateProtocol ( + &gEdkiiSmmMemoryAttributeProtocolGuid, + NULL, + (VOID **)&mSmmMemoryAttribute + ); + if (!EFI_ERROR(Status)) { + SetAllGuardPages (); + } + } +} + +/** + Helper function to convert a UINT64 value in binary to a string. + + @param[in] Value Value of a UINT64 integer. + @param[out] BinString String buffer to contain the conversion result. + + @return VOID. +**/ +VOID +Uint64ToBinString ( + IN UINT64 Value, + OUT CHAR8 *BinString + ) +{ + UINTN Index; + + if (BinString == NULL) { + return; + } + + for (Index = 64; Index > 0; --Index) { + BinString[Index - 1] = '0' + (Value & 1); + Value = RShiftU64 (Value, 1); + } + BinString[64] = '\0'; +} + +/** + Dump the guarded memory bit map. +**/ +VOID +EFIAPI +DumpGuardedMemoryBitmap ( + VOID + ) +{ + UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 TableEntry; + UINT64 Address; + INTN Level; + UINTN RepeatZero; + CHAR8 String[GUARDED_HEAP_MAP_ENTRY_BITS + 1]; + CHAR8 *Ruler1; + CHAR8 *Ruler2; + + if (mGuardedMemoryMap == 0 || + mMapLevel == 0 || + mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH) { + return; + } + + Ruler1 = " 3 2 1 0"; + Ruler2 = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210"; + + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "=============================" + " Guarded Memory Bitmap " + "==============================\r\n")); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, " %a\r\n", Ruler1)); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, " %a\r\n", Ruler2)); + + CopyMem (Entries, mLevelMask, sizeof (Entries)); + CopyMem (Shifts, mLevelShift, sizeof (Shifts)); + + SetMem (Indices, sizeof(Indices), 0); + SetMem (Tables, sizeof(Tables), 0); + SetMem (Addresses, sizeof(Addresses), 0); + + Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Tables[Level] = mGuardedMemoryMap; + Address = 0; + RepeatZero = 0; + + while (TRUE) { + if (Indices[Level] > Entries[Level]) { + + Tables[Level] = 0; + Level -= 1; + RepeatZero = 0; + + DEBUG (( + HEAP_GUARD_DEBUG_LEVEL, + "=========================================" + "=========================================\r\n" + )); + + } else { + + TableEntry = ((UINT64 *)(UINTN)Tables[Level])[Indices[Level]]; + Address = Addresses[Level]; + + if (TableEntry == 0) { + + if (Level == GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + if (RepeatZero == 0) { + Uint64ToBinString(TableEntry, String); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "%016lx: %a\r\n", Address, String)); + } else if (RepeatZero == 1) { + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "... : ...\r\n")); + } + RepeatZero += 1; + } + + } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + + Level += 1; + Tables[Level] = TableEntry; + Addresses[Level] = Address; + Indices[Level] = 0; + RepeatZero = 0; + + continue; + + } else { + + RepeatZero = 0; + Uint64ToBinString(TableEntry, String); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "%016lx: %a\r\n", Address, String)); + + } + } + + if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) { + break; + } + + Indices[Level] += 1; + Address = (Level == 0) ? 0 : Addresses[Level - 1]; + Addresses[Level] = Address | LShiftU64(Indices[Level], Shifts[Level]); + + } +} + +/** + Debug function used to verify if the Guard page is well set or not. + + @param[in] BaseAddress Address of memory to check. + @param[in] NumberOfPages Size of memory in pages. + + @return TRUE The head Guard and tail Guard are both well set. + @return FALSE The head Guard and/or tail Guard are not well set. +**/ +BOOLEAN +VerifyMemoryGuard ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumberOfPages + ) +{ + EFI_STATUS Status; + UINT64 Attribute; + EFI_PHYSICAL_ADDRESS Address; + + if (mSmmMemoryAttribute == NULL) { + return TRUE; + } + + Attribute = 0; + Address = BaseAddress - EFI_PAGE_SIZE; + Status = mSmmMemoryAttribute->GetMemoryAttributes ( + mSmmMemoryAttribute, + Address, + EFI_PAGE_SIZE, + &Attribute + ); + if (EFI_ERROR (Status) || (Attribute & EFI_MEMORY_RP) == 0) { + DEBUG ((DEBUG_ERROR, "Head Guard is not set at: %016lx (%016lX)!!!\r\n", + Address, Attribute)); + DumpGuardedMemoryBitmap (); + return FALSE; + } + + Attribute = 0; + Address = BaseAddress + EFI_PAGES_TO_SIZE (NumberOfPages); + Status = mSmmMemoryAttribute->GetMemoryAttributes ( + mSmmMemoryAttribute, + Address, + EFI_PAGE_SIZE, + &Attribute + ); + if (EFI_ERROR (Status) || (Attribute & EFI_MEMORY_RP) == 0) { + DEBUG ((DEBUG_ERROR, "Tail Guard is not set at: %016lx (%016lX)!!!\r\n", + Address, Attribute)); + DumpGuardedMemoryBitmap (); + return FALSE; + } + + return TRUE; +} + diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.h new file mode 100644 index 00000000..0d138714 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.h @@ -0,0 +1,392 @@ +/** @file + Data structure and functions to allocate and free memory space. + +Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HEAPGUARD_H_ +#define _HEAPGUARD_H_ + +#include "PiSmmCore.h" + +// +// Following macros are used to define and access the guarded memory bitmap +// table. +// +// To simplify the access and reduce the memory used for this table, the +// table is constructed in the similar way as page table structure but in +// reverse direction, i.e. from bottom growing up to top. +// +// - 1-bit tracks 1 page (4KB) +// - 1-UINT64 map entry tracks 256KB memory +// - 1K-UINT64 map table tracks 256MB memory +// - Five levels of tables can track any address of memory of 64-bit +// system, like below. +// +// 512 * 512 * 512 * 512 * 1K * 64b * 4K +// 111111111 111111111 111111111 111111111 1111111111 111111 111111111111 +// 63 54 45 36 27 17 11 0 +// 9b 9b 9b 9b 10b 6b 12b +// L0 -> L1 -> L2 -> L3 -> L4 -> bits -> page +// 1FF 1FF 1FF 1FF 3FF 3F FFF +// +// L4 table has 1K * sizeof(UINT64) = 8K (2-page), which can track 256MB +// memory. Each table of L0-L3 will be allocated when its memory address +// range is to be tracked. Only 1-page will be allocated each time. This +// can save memories used to establish this map table. +// +// For a normal configuration of system with 4G memory, two levels of tables +// can track the whole memory, because two levels (L3+L4) of map tables have +// already covered 37-bit of memory address. And for a normal UEFI BIOS, +// less than 128M memory would be consumed during boot. That means we just +// need +// +// 1-page (L3) + 2-page (L4) +// +// memory (3 pages) to track the memory allocation works. In this case, +// there's no need to setup L0-L2 tables. +// + +// +// Each entry occupies 8B/64b. 1-page can hold 512 entries, which spans 9 +// bits in address. (512 = 1 << 9) +// +#define BYTE_LENGTH_SHIFT 3 // (8 = 1 << 3) + +#define GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT \ + (EFI_PAGE_SHIFT - BYTE_LENGTH_SHIFT) + +#define GUARDED_HEAP_MAP_TABLE_DEPTH 5 + +// Use UINT64_index + bit_index_of_UINT64 to locate the bit in may +#define GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT 6 // (64 = 1 << 6) + +#define GUARDED_HEAP_MAP_ENTRY_BITS \ + (1 << GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) + +#define GUARDED_HEAP_MAP_ENTRY_BYTES \ + (GUARDED_HEAP_MAP_ENTRY_BITS / 8) + +// L4 table address width: 64 - 9 * 4 - 6 - 12 = 10b +#define GUARDED_HEAP_MAP_ENTRY_SHIFT \ + (GUARDED_HEAP_MAP_ENTRY_BITS \ + - GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 4 \ + - GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \ + - EFI_PAGE_SHIFT) + +// L4 table address mask: (1 << 10 - 1) = 0x3FF +#define GUARDED_HEAP_MAP_ENTRY_MASK \ + ((1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) - 1) + +// Size of each L4 table: (1 << 10) * 8 = 8KB = 2-page +#define GUARDED_HEAP_MAP_SIZE \ + ((1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) * GUARDED_HEAP_MAP_ENTRY_BYTES) + +// Memory size tracked by one L4 table: 8KB * 8 * 4KB = 256MB +#define GUARDED_HEAP_MAP_UNIT_SIZE \ + (GUARDED_HEAP_MAP_SIZE * 8 * EFI_PAGE_SIZE) + +// L4 table entry number: 8KB / 8 = 1024 +#define GUARDED_HEAP_MAP_ENTRIES_PER_UNIT \ + (GUARDED_HEAP_MAP_SIZE / GUARDED_HEAP_MAP_ENTRY_BYTES) + +// L4 table entry indexing +#define GUARDED_HEAP_MAP_ENTRY_INDEX(Address) \ + (RShiftU64 (Address, EFI_PAGE_SHIFT \ + + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) \ + & GUARDED_HEAP_MAP_ENTRY_MASK) + +// L4 table entry bit indexing +#define GUARDED_HEAP_MAP_ENTRY_BIT_INDEX(Address) \ + (RShiftU64 (Address, EFI_PAGE_SHIFT) \ + & ((1 << GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) - 1)) + +// +// Total bits (pages) tracked by one L4 table (65536-bit) +// +#define GUARDED_HEAP_MAP_BITS \ + (1 << (GUARDED_HEAP_MAP_ENTRY_SHIFT \ + + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT)) + +// +// Bit indexing inside the whole L4 table (0 - 65535) +// +#define GUARDED_HEAP_MAP_BIT_INDEX(Address) \ + (RShiftU64 (Address, EFI_PAGE_SHIFT) \ + & ((1 << (GUARDED_HEAP_MAP_ENTRY_SHIFT \ + + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT)) - 1)) + +// +// Memory address bit width tracked by L4 table: 10 + 6 + 12 = 28 +// +#define GUARDED_HEAP_MAP_TABLE_SHIFT \ + (GUARDED_HEAP_MAP_ENTRY_SHIFT + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \ + + EFI_PAGE_SHIFT) + +// +// Macro used to initialize the local array variable for map table traversing +// {55, 46, 37, 28, 18} +// +#define GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS \ + { \ + GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 3, \ + GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 2, \ + GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT, \ + GUARDED_HEAP_MAP_TABLE_SHIFT, \ + EFI_PAGE_SHIFT + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \ + } + +// +// Masks used to extract address range of each level of table +// {0x1FF, 0x1FF, 0x1FF, 0x1FF, 0x3FF} +// +#define GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS \ + { \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) - 1 \ + } + +// +// Memory type to guard (matching the related PCD definition) +// +#define GUARD_HEAP_TYPE_PAGE BIT2 +#define GUARD_HEAP_TYPE_POOL BIT3 + +// +// Debug message level +// +#define HEAP_GUARD_DEBUG_LEVEL (DEBUG_POOL|DEBUG_PAGE) + +typedef struct { + UINT32 TailMark; + UINT32 HeadMark; + EFI_PHYSICAL_ADDRESS Address; + LIST_ENTRY Link; +} HEAP_GUARD_NODE; + +/** + Set head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to set guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID. +**/ +VOID +SetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Unset head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to unset guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID. +**/ +VOID +UnsetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Adjust the base and number of pages to really allocate according to Guard. + + @param[in,out] Memory Base address of free memory. + @param[in,out] NumberOfPages Size of memory to allocate. + + @return VOID. +**/ +VOID +AdjustMemoryA ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ); + +/** + Adjust the start address and number of pages to free according to Guard. + + The purpose of this function is to keep the shared Guard page with adjacent + memory block if it's still in guard, or free it if no more sharing. Another + is to reserve pages as Guard pages in partial page free situation. + + @param[in,out] Memory Base address of memory to free. + @param[in,out] NumberOfPages Size of memory to free. + + @return VOID. +**/ +VOID +AdjustMemoryF ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ); + +/** + Check to see if the pool at the given address should be guarded or not. + + @param[in] MemoryType Pool type to check. + + + @return TRUE The given type of pool should be guarded. + @return FALSE The given type of pool should not be guarded. +**/ +BOOLEAN +IsPoolTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType + ); + +/** + Check to see if the page at the given address should be guarded or not. + + @param[in] MemoryType Page type to check. + @param[in] AllocateType Allocation type to check. + + @return TRUE The given type of page should be guarded. + @return FALSE The given type of page should not be guarded. +**/ +BOOLEAN +IsPageTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType, + IN EFI_ALLOCATE_TYPE AllocateType + ); + +/** + Check to see if the page at the given address is guarded or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is guarded. + @return FALSE The page at Address is not guarded. +**/ +BOOLEAN +EFIAPI +IsMemoryGuarded ( + IN EFI_PHYSICAL_ADDRESS Address + ); + +/** + Check to see if the page at the given address is a Guard page or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is a Guard page. + @return FALSE The page at Address is not a Guard page. +**/ +BOOLEAN +EFIAPI +IsGuardPage ( + IN EFI_PHYSICAL_ADDRESS Address + ); + +/** + Dump the guarded memory bit map. +**/ +VOID +EFIAPI +DumpGuardedMemoryBitmap ( + VOID + ); + +/** + Adjust the pool head position to make sure the Guard page is adjavent to + pool tail or pool head. + + @param[in] Memory Base address of memory allocated. + @param[in] NoPages Number of pages actually allocated. + @param[in] Size Size of memory requested. + (plus pool head/tail overhead) + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadA ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages, + IN UINTN Size + ); + +/** + Get the page base address according to pool head address. + + @param[in] Memory Head address of pool to free. + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadF ( + IN EFI_PHYSICAL_ADDRESS Memory + ); + +/** + Helper function of memory allocation with Guard pages. + + @param FreePageList The free page node. + @param NumberOfPages Number of pages to be allocated. + @param MaxAddress Request to allocate memory below this address. + @param MemoryType Type of memory requested. + + @return Memory address of allocated pages. +**/ +UINTN +InternalAllocMaxAddressWithGuard ( + IN OUT LIST_ENTRY *FreePageList, + IN UINTN NumberOfPages, + IN UINTN MaxAddress, + IN EFI_MEMORY_TYPE MemoryType + ); + +/** + Helper function of memory free with Guard pages. + + @param[in] Memory Base address of memory being freed. + @param[in] NumberOfPages The number of pages to free. + @param[in] AddRegion If this memory is new added region. + + @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 +SmmInternalFreePagesExWithGuard ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN AddRegion + ); + +/** + Check to see if the heap guard is enabled for page and/or pool allocation. + + @return TRUE/FALSE. +**/ +BOOLEAN +IsHeapGuardEnabled ( + VOID + ); + +/** + Debug function used to verify if the Guard page is well set or not. + + @param[in] BaseAddress Address of memory to check. + @param[in] NumberOfPages Size of memory in pages. + + @return TRUE The head Guard and tail Guard are both well set. + @return FALSE The head Guard and/or tail Guard are not well set. +**/ +BOOLEAN +VerifyMemoryGuard ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumberOfPages + ); + +extern BOOLEAN mOnGuarding; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c new file mode 100644 index 00000000..45ecf87a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c @@ -0,0 +1,171 @@ +/** @file + System Management System Table Services SmmInstallConfigurationTable service + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +#define CONFIG_TABLE_SIZE_INCREASED 0x10 + +UINTN mSmmSystemTableAllocateSize = 0; + +/** + The SmmInstallConfigurationTable() function is used to maintain the list + of configuration tables that are stored in the System Management System + Table. The list is stored as an array of (GUID, Pointer) pairs. The list + must be allocated from pool memory with PoolType set to EfiRuntimeServicesData. + + @param SystemTable A pointer to the SMM System Table (SMST). + @param Guid A pointer to the GUID for the entry to add, update, or remove. + @param Table A pointer to the buffer of the table to add. + @param TableSize The size of the table to install. + + @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. + @retval EFI_INVALID_PARAMETER Guid is not valid. + @retval EFI_NOT_FOUND An attempt was made to delete a non-existent entry. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. + +**/ +EFI_STATUS +EFIAPI +SmmInstallConfigurationTable ( + IN CONST EFI_SMM_SYSTEM_TABLE2 *SystemTable, + IN CONST EFI_GUID *Guid, + IN VOID *Table, + IN UINTN TableSize + ) +{ + UINTN Index; + EFI_CONFIGURATION_TABLE *ConfigurationTable; + EFI_CONFIGURATION_TABLE *OldTable; + + // + // If Guid is NULL, then this operation cannot be performed + // + if (Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConfigurationTable = gSmmCoreSmst.SmmConfigurationTable; + + // + // Search all the table for an entry that matches Guid + // + for (Index = 0; Index < gSmmCoreSmst.NumberOfTableEntries; Index++) { + if (CompareGuid (Guid, &(ConfigurationTable[Index].VendorGuid))) { + break; + } + } + + if (Index < gSmmCoreSmst.NumberOfTableEntries) { + // + // A match was found, so this is either a modify or a delete operation + // + if (Table != NULL) { + // + // If Table is not NULL, then this is a modify operation. + // Modify the table entry and return. + // + ConfigurationTable[Index].VendorTable = Table; + return EFI_SUCCESS; + } + + // + // A match was found and Table is NULL, so this is a delete operation. + // + gSmmCoreSmst.NumberOfTableEntries--; + + // + // Copy over deleted entry + // + CopyMem ( + &(ConfigurationTable[Index]), + &(ConfigurationTable[Index + 1]), + (gSmmCoreSmst.NumberOfTableEntries - Index) * sizeof (EFI_CONFIGURATION_TABLE) + ); + + } else { + // + // No matching GUIDs were found, so this is an add operation. + // + if (Table == NULL) { + // + // If Table is NULL on an add operation, then return an error. + // + return EFI_NOT_FOUND; + } + + // + // Assume that Index == gSmmCoreSmst.NumberOfTableEntries + // + if ((Index * sizeof (EFI_CONFIGURATION_TABLE)) >= mSmmSystemTableAllocateSize) { + // + // Allocate a table with one additional entry. + // + mSmmSystemTableAllocateSize += (CONFIG_TABLE_SIZE_INCREASED * sizeof (EFI_CONFIGURATION_TABLE)); + ConfigurationTable = AllocatePool (mSmmSystemTableAllocateSize); + if (ConfigurationTable == NULL) { + // + // If a new table could not be allocated, then return an error. + // + return EFI_OUT_OF_RESOURCES; + } + + if (gSmmCoreSmst.SmmConfigurationTable != NULL) { + // + // Copy the old table to the new table. + // + CopyMem ( + ConfigurationTable, + gSmmCoreSmst.SmmConfigurationTable, + Index * sizeof (EFI_CONFIGURATION_TABLE) + ); + + // + // Record the old table pointer. + // + OldTable = gSmmCoreSmst.SmmConfigurationTable; + + // + // As the SmmInstallConfigurationTable() 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 gSmmCoreSmst.SmmConfigurationTable point to the new + // table and avoid the errors of use-after-free to the old table by the + // reenter of SmmInstallConfigurationTable() in FreePool()'s calling stack. + // + gSmmCoreSmst.SmmConfigurationTable = ConfigurationTable; + + // + // Free the old table after updating System Table to the new table pointer. + // + FreePool (OldTable); + } else { + // + // Update System Table + // + gSmmCoreSmst.SmmConfigurationTable = ConfigurationTable; + } + } + + // + // Fill in the new entry + // + CopyGuid ((VOID *)&ConfigurationTable[Index].VendorGuid, Guid); + ConfigurationTable[Index].VendorTable = Table; + + // + // This is an add operation, so increment the number of table entries + // + gSmmCoreSmst.NumberOfTableEntries++; + } + + // + // CRC-32 field is ignorable for SMM System Table and should be set to zero + // + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Locate.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Locate.c new file mode 100644 index 00000000..078c1a28 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Locate.c @@ -0,0 +1,489 @@ +/** @file + Locate handle functions + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +// +// ProtocolRequest - Last LocateHandle request ID +// +UINTN mEfiLocateHandleRequest = 0; + +// +// Internal prototypes +// + +typedef struct { + EFI_GUID *Protocol; + VOID *SearchKey; + LIST_ENTRY *Position; + PROTOCOL_ENTRY *ProtEntry; +} LOCATE_POSITION; + +typedef +IHANDLE * +(* CORE_GET_NEXT) ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ); + +/** + Routine to get the next Handle, when you are searching for all handles. + + @param Position Information about which Handle to search for. + @param Interface Return the interface structure for the matching + protocol. + + @return An pointer to IHANDLE if the next Position is not the end of the list. + Otherwise,NULL is returned. + +**/ +IHANDLE * +SmmGetNextLocateAllHandles ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ) +{ + IHANDLE *Handle; + + // + // Next handle + // + Position->Position = Position->Position->ForwardLink; + + // + // If not at the end of the list, get the handle + // + Handle = NULL; + *Interface = NULL; + if (Position->Position != &gHandleList) { + Handle = CR (Position->Position, IHANDLE, AllHandles, EFI_HANDLE_SIGNATURE); + } + return Handle; +} + +/** + Routine to get the next Handle, when you are searching for register protocol + notifies. + + @param Position Information about which Handle to search for. + @param Interface Return the interface structure for the matching + protocol. + + @return An pointer to IHANDLE if the next Position is not the end of the list. + Otherwise,NULL is returned. + +**/ +IHANDLE * +SmmGetNextLocateByRegisterNotify ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ) +{ + IHANDLE *Handle; + PROTOCOL_NOTIFY *ProtNotify; + PROTOCOL_INTERFACE *Prot; + LIST_ENTRY *Link; + + Handle = NULL; + *Interface = NULL; + ProtNotify = Position->SearchKey; + + // + // If this is the first request, get the next handle + // + if (ProtNotify != NULL) { + ASSERT(ProtNotify->Signature == PROTOCOL_NOTIFY_SIGNATURE); + Position->SearchKey = NULL; + + // + // If not at the end of the list, get the next handle + // + Link = ProtNotify->Position->ForwardLink; + if (Link != &ProtNotify->Protocol->Protocols) { + Prot = CR (Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE); + Handle = Prot->Handle; + *Interface = Prot->Interface; + } + } + return Handle; +} + +/** + Routine to get the next Handle, when you are searching for a given protocol. + + @param Position Information about which Handle to search for. + @param Interface Return the interface structure for the matching + protocol. + + @return An pointer to IHANDLE if the next Position is not the end of the list. + Otherwise,NULL is returned. + +**/ +IHANDLE * +SmmGetNextLocateByProtocol ( + IN OUT LOCATE_POSITION *Position, + OUT VOID **Interface + ) +{ + IHANDLE *Handle; + LIST_ENTRY *Link; + PROTOCOL_INTERFACE *Prot; + + Handle = NULL; + *Interface = NULL; + for (; ;) { + // + // Next entry + // + Link = Position->Position->ForwardLink; + Position->Position = Link; + + // + // If not at the end, return the handle + // + if (Link == &Position->ProtEntry->Protocols) { + Handle = NULL; + break; + } + + // + // Get the handle + // + Prot = CR(Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE); + Handle = Prot->Handle; + *Interface = Prot->Interface; + + // + // If this handle has not been returned this request, then + // return it now + // + if (Handle->LocateRequest != mEfiLocateHandleRequest) { + Handle->LocateRequest = mEfiLocateHandleRequest; + break; + } + } + return Handle; +} + +/** + Return the first Protocol Interface that matches the Protocol GUID. If + Registration is pasased in return a Protocol Instance that was just add + to the system. If Registration is NULL return the first Protocol Interface + you find. + + @param Protocol The protocol to search for + @param Registration Optional Registration Key returned from + RegisterProtocolNotify() + @param Interface Return the Protocol interface (instance). + + @retval EFI_SUCCESS If a valid Interface is returned + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_NOT_FOUND Protocol interface not found + +**/ +EFI_STATUS +EFIAPI +SmmLocateProtocol ( + IN EFI_GUID *Protocol, + IN VOID *Registration OPTIONAL, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + LOCATE_POSITION Position; + PROTOCOL_NOTIFY *ProtNotify; + IHANDLE *Handle; + + if ((Interface == NULL) || (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 = SmmFindProtocolEntry (Protocol, FALSE); + if (Position.ProtEntry == NULL) { + return EFI_NOT_FOUND; + } + Position.Position = &Position.ProtEntry->Protocols; + + Handle = SmmGetNextLocateByProtocol (&Position, Interface); + } else { + Handle = SmmGetNextLocateByRegisterNotify (&Position, Interface); + } + + if (Handle == NULL) { + Status = EFI_NOT_FOUND; + } else if (Registration != NULL) { + // + // If this is a search by register notify and a handle was + // returned, update the register notification position + // + ProtNotify = Registration; + ProtNotify->Position = ProtNotify->Position->ForwardLink; + } + + return Status; +} + +/** + Locates the requested handle(s) and returns them in Buffer. + + @param SearchType The type of search to perform to locate the + handles + @param Protocol The protocol to search for + @param SearchKey Dependant on SearchType + @param BufferSize On input the size of Buffer. On output the + size of data returned. + @param Buffer The buffer to return the results in + + @retval EFI_BUFFER_TOO_SMALL Buffer too small, required buffer size is + returned in BufferSize. + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully found the requested handle(s) and + returns them in Buffer. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandle ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *BufferSize, + OUT EFI_HANDLE *Buffer + ) +{ + EFI_STATUS Status; + LOCATE_POSITION Position; + PROTOCOL_NOTIFY *ProtNotify; + CORE_GET_NEXT GetNext; + UINTN ResultSize; + IHANDLE *Handle; + IHANDLE **ResultBuffer; + VOID *Interface; + + if (BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((*BufferSize > 0) && (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + GetNext = NULL; + + // + // Set initial position + // + Position.Protocol = Protocol; + Position.SearchKey = SearchKey; + Position.Position = &gHandleList; + + ResultSize = 0; + ResultBuffer = (IHANDLE **) Buffer; + Status = EFI_SUCCESS; + + // + // Get the search function based on type + // + switch (SearchType) { + case AllHandles: + GetNext = SmmGetNextLocateAllHandles; + break; + + case ByRegisterNotify: + GetNext = SmmGetNextLocateByRegisterNotify; + // + // Must have SearchKey for locate ByRegisterNotify + // + if (SearchKey == NULL) { + Status = EFI_INVALID_PARAMETER; + } + break; + + case ByProtocol: + GetNext = SmmGetNextLocateByProtocol; + if (Protocol == NULL) { + Status = EFI_INVALID_PARAMETER; + break; + } + // + // Look up the protocol entry and set the head pointer + // + Position.ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); + if (Position.ProtEntry == NULL) { + Status = EFI_NOT_FOUND; + break; + } + Position.Position = &Position.ProtEntry->Protocols; + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Enumerate out the matching handles + // + mEfiLocateHandleRequest += 1; + for (; ;) { + // + // Get the next handle. If no more handles, stop + // + Handle = GetNext (&Position, &Interface); + if (NULL == Handle) { + break; + } + + // + // Increase the resulting buffer size, and if this handle + // fits return it + // + ResultSize += sizeof(Handle); + if (ResultSize <= *BufferSize) { + *ResultBuffer = Handle; + ResultBuffer += 1; + } + } + + // + // If the result is a zero length buffer, then there were no + // matching handles + // + if (ResultSize == 0) { + Status = EFI_NOT_FOUND; + } else { + // + // Return the resulting buffer size. If it's larger than what + // was passed, then set the error code + // + if (ResultSize > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ResultSize; + + if (SearchType == ByRegisterNotify && !EFI_ERROR(Status)) { + 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 SmmLocateHandle() + that allocates a buffer for the caller. + + @param SearchType Specifies which handle(s) are to be returned. + @param Protocol Provides the protocol to search by. This + parameter is only valid for SearchType + ByProtocol. + @param SearchKey Supplies the search key depending on the + SearchType. + @param NumberHandles The number of handles returned in Buffer. + @param Buffer A pointer to the buffer to return the requested + array of handles that support Protocol. + + @retval EFI_SUCCESS The result array of handles was returned. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the + matching results. + @retval EFI_INVALID_PARAMETER One or more parameters are not valid. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandleBuffer ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + if (NumberHandles == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + *NumberHandles = 0; + *Buffer = NULL; + Status = SmmLocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + // + // LocateHandleBuffer() returns incorrect status code if SearchType is + // invalid. + // + // Add code to correctly handle expected errors from SmmLocateHandle(). + // + if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) { + if (Status != EFI_INVALID_PARAMETER) { + Status = EFI_NOT_FOUND; + } + return Status; + } + + *Buffer = AllocatePool (BufferSize); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = SmmLocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + + *NumberHandles = BufferSize / sizeof(EFI_HANDLE); + if (EFI_ERROR(Status)) { + *NumberHandles = 0; + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/MemoryAttributesTable.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/MemoryAttributesTable.c new file mode 100644 index 00000000..77db3de2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/MemoryAttributesTable.c @@ -0,0 +1,1368 @@ +/** @file + PI SMM MemoryAttributes support + +Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/SmmServicesTableLib.h> +#include <Library/DebugLib.h> +#include <Library/PcdLib.h> + +#include <Library/PeCoffLib.h> +#include <Library/PeCoffGetEntryPointLib.h> + +#include <Guid/PiSmmMemoryAttributesTable.h> + +#include "PiSmmCore.h" + +#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ + ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) + +#define IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE SIGNATURE_32 ('I','P','R','C') + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_PHYSICAL_ADDRESS CodeSegmentBase; + UINT64 CodeSegmentSize; +} IMAGE_PROPERTIES_RECORD_CODE_SECTION; + +#define IMAGE_PROPERTIES_RECORD_SIGNATURE SIGNATURE_32 ('I','P','R','D') + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_PHYSICAL_ADDRESS ImageBase; + UINT64 ImageSize; + UINTN CodeSegmentCount; + LIST_ENTRY CodeSegmentList; +} IMAGE_PROPERTIES_RECORD; + +#define IMAGE_PROPERTIES_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('I','P','P','D') + +typedef struct { + UINT32 Signature; + UINTN ImageRecordCount; + UINTN CodeSegmentCountMax; + LIST_ENTRY ImageRecordList; +} IMAGE_PROPERTIES_PRIVATE_DATA; + +IMAGE_PROPERTIES_PRIVATE_DATA mImagePropertiesPrivateData = { + IMAGE_PROPERTIES_PRIVATE_DATA_SIGNATURE, + 0, + 0, + INITIALIZE_LIST_HEAD_VARIABLE (mImagePropertiesPrivateData.ImageRecordList) +}; + +#define EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA BIT0 + +UINT64 mMemoryProtectionAttribute = EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA; + +// +// Below functions are for MemoryMap +// + +/** + Converts a number of EFI_PAGEs to a size in bytes. + + NOTE: Do not use EFI_PAGES_TO_SIZE because it handles UINTN only. + + @param[in] Pages The number of EFI_PAGES. + + @return The number of bytes associated with the number of EFI_PAGEs specified + by Pages. +**/ +STATIC +UINT64 +EfiPagesToSize ( + IN UINT64 Pages + ) +{ + return LShiftU64 (Pages, EFI_PAGE_SHIFT); +} + +/** + Converts a size, in bytes, to a number of EFI_PAGESs. + + NOTE: Do not use EFI_SIZE_TO_PAGES because it handles UINTN only. + + @param[in] Size A size in bytes. + + @return The number of EFI_PAGESs associated with the number of bytes specified + by Size. + +**/ +STATIC +UINT64 +EfiSizeToPages ( + IN UINT64 Size + ) +{ + return RShiftU64 (Size, EFI_PAGE_SHIFT) + ((((UINTN)Size) & EFI_PAGE_MASK) ? 1 : 0); +} + + +/** + Sort memory map entries based upon PhysicalStart, from low to high. + + @param[in,out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[in] MemoryMapSize Size, in bytes, of the MemoryMap buffer. + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +VOID +SortMemoryMap ( + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + EFI_MEMORY_DESCRIPTOR TempMemoryMap; + + MemoryMapEntry = MemoryMap; + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize); + while (MemoryMapEntry < MemoryMapEnd) { + while (NextMemoryMapEntry < MemoryMapEnd) { + if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) { + CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); + CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); + CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR)); + } + + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); + } + + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + } + + return ; +} + +/** + Merge continuous memory map entries whose have same attributes. + + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + the current memory map. On output, + it is the size of new memory map after merge. + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +VOID +MergeMemoryMap ( + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN OUT UINTN *MemoryMapSize, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + UINT64 MemoryBlockLength; + EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; + + MemoryMapEntry = MemoryMap; + NewMemoryMapEntry = MemoryMap; + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize); + while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) { + CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + + do { + MemoryBlockLength = (UINT64) (EfiPagesToSize (MemoryMapEntry->NumberOfPages)); + if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) && + (MemoryMapEntry->Type == NextMemoryMapEntry->Type) && + (MemoryMapEntry->Attribute == NextMemoryMapEntry->Attribute) && + ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) { + MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; + if (NewMemoryMapEntry != MemoryMapEntry) { + NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; + } + + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); + continue; + } else { + MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); + break; + } + } while (TRUE); + + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize); + } + + *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap; + + return ; +} + +/** + Enforce memory map attributes. + This function will set EfiRuntimeServicesData/EfiMemoryMappedIO/EfiMemoryMappedIOPortSpace to be EFI_MEMORY_XP. + + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[in] MemoryMapSize Size, in bytes, of the MemoryMap buffer. + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +VOID +EnforceMemoryMapAttribute ( + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + + MemoryMapEntry = MemoryMap; + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize); + while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) { + if (MemoryMapEntry->Attribute != 0) { + // It is PE image, the attribute is already set. + } else { + switch (MemoryMapEntry->Type) { + case EfiRuntimeServicesCode: + MemoryMapEntry->Attribute = EFI_MEMORY_RO; + break; + case EfiRuntimeServicesData: + default: + MemoryMapEntry->Attribute |= EFI_MEMORY_XP; + break; + } + } + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + } + + return ; +} + +/** + Return the first image record, whose [ImageBase, ImageSize] covered by [Buffer, Length]. + + @param[in] Buffer Start Address + @param[in] Length Address length + + @return first image record covered by [buffer, length] +**/ +STATIC +IMAGE_PROPERTIES_RECORD * +GetImageRecordByAddress ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ) +{ + IMAGE_PROPERTIES_RECORD *ImageRecord; + LIST_ENTRY *ImageRecordLink; + LIST_ENTRY *ImageRecordList; + + ImageRecordList = &mImagePropertiesPrivateData.ImageRecordList; + + for (ImageRecordLink = ImageRecordList->ForwardLink; + ImageRecordLink != ImageRecordList; + ImageRecordLink = ImageRecordLink->ForwardLink) { + ImageRecord = CR ( + ImageRecordLink, + IMAGE_PROPERTIES_RECORD, + Link, + IMAGE_PROPERTIES_RECORD_SIGNATURE + ); + + if ((Buffer <= ImageRecord->ImageBase) && + (Buffer + Length >= ImageRecord->ImageBase + ImageRecord->ImageSize)) { + return ImageRecord; + } + } + + return NULL; +} + +/** + Set the memory map to new entries, according to one old entry, + based upon PE code section and data section in image record + + @param[in] ImageRecord An image record whose [ImageBase, ImageSize] covered + by old memory map entry. + @param[in, out] NewRecord A pointer to several new memory map entries. + The caller guarantee the buffer size be 1 + + (SplitRecordCount * DescriptorSize) calculated + below. + @param[in] OldRecord A pointer to one old memory map entry. + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +UINTN +SetNewRecord ( + IN IMAGE_PROPERTIES_RECORD *ImageRecord, + IN OUT EFI_MEMORY_DESCRIPTOR *NewRecord, + IN EFI_MEMORY_DESCRIPTOR *OldRecord, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR TempRecord; + IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; + LIST_ENTRY *ImageRecordCodeSectionLink; + LIST_ENTRY *ImageRecordCodeSectionEndLink; + LIST_ENTRY *ImageRecordCodeSectionList; + UINTN NewRecordCount; + UINT64 PhysicalEnd; + UINT64 ImageEnd; + + CopyMem (&TempRecord, OldRecord, sizeof(EFI_MEMORY_DESCRIPTOR)); + PhysicalEnd = TempRecord.PhysicalStart + EfiPagesToSize(TempRecord.NumberOfPages); + NewRecordCount = 0; + + // + // Always create a new entry for non-PE image record + // + if (ImageRecord->ImageBase > TempRecord.PhysicalStart) { + NewRecord->Type = TempRecord.Type; + NewRecord->PhysicalStart = TempRecord.PhysicalStart; + NewRecord->VirtualStart = 0; + NewRecord->NumberOfPages = EfiSizeToPages(ImageRecord->ImageBase - TempRecord.PhysicalStart); + NewRecord->Attribute = TempRecord.Attribute; + NewRecord = NEXT_MEMORY_DESCRIPTOR (NewRecord, DescriptorSize); + NewRecordCount ++; + TempRecord.PhysicalStart = ImageRecord->ImageBase; + TempRecord.NumberOfPages = EfiSizeToPages(PhysicalEnd - TempRecord.PhysicalStart); + } + + ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList; + + ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink; + ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList; + while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) { + ImageRecordCodeSection = CR ( + ImageRecordCodeSectionLink, + IMAGE_PROPERTIES_RECORD_CODE_SECTION, + Link, + IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE + ); + ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink; + + if (TempRecord.PhysicalStart <= ImageRecordCodeSection->CodeSegmentBase) { + // + // DATA + // + NewRecord->Type = EfiRuntimeServicesData; + NewRecord->PhysicalStart = TempRecord.PhysicalStart; + NewRecord->VirtualStart = 0; + NewRecord->NumberOfPages = EfiSizeToPages(ImageRecordCodeSection->CodeSegmentBase - NewRecord->PhysicalStart); + NewRecord->Attribute = TempRecord.Attribute | EFI_MEMORY_XP; + if (NewRecord->NumberOfPages != 0) { + NewRecord = NEXT_MEMORY_DESCRIPTOR (NewRecord, DescriptorSize); + NewRecordCount ++; + } + + // + // CODE + // + NewRecord->Type = EfiRuntimeServicesCode; + NewRecord->PhysicalStart = ImageRecordCodeSection->CodeSegmentBase; + NewRecord->VirtualStart = 0; + NewRecord->NumberOfPages = EfiSizeToPages(ImageRecordCodeSection->CodeSegmentSize); + NewRecord->Attribute = (TempRecord.Attribute & (~EFI_MEMORY_XP)) | EFI_MEMORY_RO; + if (NewRecord->NumberOfPages != 0) { + NewRecord = NEXT_MEMORY_DESCRIPTOR (NewRecord, DescriptorSize); + NewRecordCount ++; + } + + TempRecord.PhysicalStart = ImageRecordCodeSection->CodeSegmentBase + EfiPagesToSize (EfiSizeToPages(ImageRecordCodeSection->CodeSegmentSize)); + TempRecord.NumberOfPages = EfiSizeToPages(PhysicalEnd - TempRecord.PhysicalStart); + if (TempRecord.NumberOfPages == 0) { + break; + } + } + } + + ImageEnd = ImageRecord->ImageBase + ImageRecord->ImageSize; + + // + // Final DATA + // + if (TempRecord.PhysicalStart < ImageEnd) { + NewRecord->Type = EfiRuntimeServicesData; + NewRecord->PhysicalStart = TempRecord.PhysicalStart; + NewRecord->VirtualStart = 0; + NewRecord->NumberOfPages = EfiSizeToPages (ImageEnd - TempRecord.PhysicalStart); + NewRecord->Attribute = TempRecord.Attribute | EFI_MEMORY_XP; + NewRecordCount ++; + } + + return NewRecordCount; +} + +/** + Return the max number of new splitted entries, according to one old entry, + based upon PE code section and data section. + + @param[in] OldRecord A pointer to one old memory map entry. + + @retval 0 no entry need to be splitted. + @return the max number of new splitted entries +**/ +STATIC +UINTN +GetMaxSplitRecordCount ( + IN EFI_MEMORY_DESCRIPTOR *OldRecord + ) +{ + IMAGE_PROPERTIES_RECORD *ImageRecord; + UINTN SplitRecordCount; + UINT64 PhysicalStart; + UINT64 PhysicalEnd; + + SplitRecordCount = 0; + PhysicalStart = OldRecord->PhysicalStart; + PhysicalEnd = OldRecord->PhysicalStart + EfiPagesToSize(OldRecord->NumberOfPages); + + do { + ImageRecord = GetImageRecordByAddress (PhysicalStart, PhysicalEnd - PhysicalStart); + if (ImageRecord == NULL) { + break; + } + SplitRecordCount += (2 * ImageRecord->CodeSegmentCount + 2); + PhysicalStart = ImageRecord->ImageBase + ImageRecord->ImageSize; + } while ((ImageRecord != NULL) && (PhysicalStart < PhysicalEnd)); + + return SplitRecordCount; +} + +/** + Split the memory map to new entries, according to one old entry, + based upon PE code section and data section. + + @param[in] OldRecord A pointer to one old memory map entry. + @param[in, out] NewRecord A pointer to several new memory map entries. + The caller guarantee the buffer size be 1 + + (SplitRecordCount * DescriptorSize) calculated + below. + @param[in] MaxSplitRecordCount The max number of splitted entries + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. + + @retval 0 no entry is splitted. + @return the real number of splitted record. +**/ +STATIC +UINTN +SplitRecord ( + IN EFI_MEMORY_DESCRIPTOR *OldRecord, + IN OUT EFI_MEMORY_DESCRIPTOR *NewRecord, + IN UINTN MaxSplitRecordCount, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR TempRecord; + IMAGE_PROPERTIES_RECORD *ImageRecord; + IMAGE_PROPERTIES_RECORD *NewImageRecord; + UINT64 PhysicalStart; + UINT64 PhysicalEnd; + UINTN NewRecordCount; + UINTN TotalNewRecordCount; + + if (MaxSplitRecordCount == 0) { + CopyMem (NewRecord, OldRecord, DescriptorSize); + return 0; + } + + TotalNewRecordCount = 0; + + // + // Override previous record + // + CopyMem (&TempRecord, OldRecord, sizeof(EFI_MEMORY_DESCRIPTOR)); + PhysicalStart = TempRecord.PhysicalStart; + PhysicalEnd = TempRecord.PhysicalStart + EfiPagesToSize(TempRecord.NumberOfPages); + + ImageRecord = NULL; + do { + NewImageRecord = GetImageRecordByAddress (PhysicalStart, PhysicalEnd - PhysicalStart); + if (NewImageRecord == NULL) { + // + // No more image covered by this range, stop + // + if (PhysicalEnd > PhysicalStart) { + // + // Always create a new entry for non-PE image record + // + NewRecord->Type = TempRecord.Type; + NewRecord->PhysicalStart = TempRecord.PhysicalStart; + NewRecord->VirtualStart = 0; + NewRecord->NumberOfPages = TempRecord.NumberOfPages; + NewRecord->Attribute = TempRecord.Attribute; + TotalNewRecordCount ++; + } + break; + } + ImageRecord = NewImageRecord; + + // + // Set new record + // + NewRecordCount = SetNewRecord (ImageRecord, NewRecord, &TempRecord, DescriptorSize); + TotalNewRecordCount += NewRecordCount; + NewRecord = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)NewRecord + NewRecordCount * DescriptorSize); + + // + // Update PhysicalStart, in order to exclude the image buffer already splitted. + // + PhysicalStart = ImageRecord->ImageBase + ImageRecord->ImageSize; + TempRecord.PhysicalStart = PhysicalStart; + TempRecord.NumberOfPages = EfiSizeToPages (PhysicalEnd - PhysicalStart); + } while ((ImageRecord != NULL) && (PhysicalStart < PhysicalEnd)); + + return TotalNewRecordCount - 1; +} + +/** + Split the original memory map, and add more entries to describe PE code section and data section. + This function will set EfiRuntimeServicesData to be EFI_MEMORY_XP. + This function will merge entries with same attributes finally. + + NOTE: It assumes PE code/data section are page aligned. + NOTE: It assumes enough entry is prepared for new memory map. + + Split table: + +---------------+ + | Record X | + +---------------+ + | Record RtCode | + +---------------+ + | Record Y | + +---------------+ + ==> + +---------------+ + | Record X | + +---------------+ + | Record RtCode | + +---------------+ ---- + | Record RtData | | + +---------------+ | + | Record RtCode | |-> PE/COFF1 + +---------------+ | + | Record RtData | | + +---------------+ ---- + | Record RtCode | + +---------------+ ---- + | Record RtData | | + +---------------+ | + | Record RtCode | |-> PE/COFF2 + +---------------+ | + | Record RtData | | + +---------------+ ---- + | Record RtCode | + +---------------+ + | Record Y | + +---------------+ + + @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + old MemoryMap before split. The actual buffer + size of MemoryMap is MemoryMapSize + + (AdditionalRecordCount * DescriptorSize) calculated + below. On output, it is the size of new MemoryMap + after split. + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +VOID +SplitTable ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN DescriptorSize + ) +{ + INTN IndexOld; + INTN IndexNew; + UINTN MaxSplitRecordCount; + UINTN RealSplitRecordCount; + UINTN TotalSplitRecordCount; + UINTN AdditionalRecordCount; + + AdditionalRecordCount = (2 * mImagePropertiesPrivateData.CodeSegmentCountMax + 2) * mImagePropertiesPrivateData.ImageRecordCount; + + TotalSplitRecordCount = 0; + // + // Let old record point to end of valid MemoryMap buffer. + // + IndexOld = ((*MemoryMapSize) / DescriptorSize) - 1; + // + // Let new record point to end of full MemoryMap buffer. + // + IndexNew = ((*MemoryMapSize) / DescriptorSize) - 1 + AdditionalRecordCount; + for (; IndexOld >= 0; IndexOld--) { + MaxSplitRecordCount = GetMaxSplitRecordCount ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + IndexOld * DescriptorSize)); + // + // Split this MemoryMap record + // + IndexNew -= MaxSplitRecordCount; + RealSplitRecordCount = SplitRecord ( + (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + IndexOld * DescriptorSize), + (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + IndexNew * DescriptorSize), + MaxSplitRecordCount, + DescriptorSize + ); + // + // Adjust IndexNew according to real split. + // + if (MaxSplitRecordCount != RealSplitRecordCount) { + CopyMem ( + ((UINT8 *)MemoryMap + (IndexNew + MaxSplitRecordCount - RealSplitRecordCount) * DescriptorSize), + ((UINT8 *)MemoryMap + IndexNew * DescriptorSize), + (RealSplitRecordCount + 1) * DescriptorSize + ); + } + IndexNew = IndexNew + MaxSplitRecordCount - RealSplitRecordCount; + TotalSplitRecordCount += RealSplitRecordCount; + IndexNew --; + } + // + // Move all records to the beginning. + // + CopyMem ( + MemoryMap, + (UINT8 *)MemoryMap + (AdditionalRecordCount - TotalSplitRecordCount) * DescriptorSize, + (*MemoryMapSize) + TotalSplitRecordCount * DescriptorSize + ); + + *MemoryMapSize = (*MemoryMapSize) + DescriptorSize * TotalSplitRecordCount; + + // + // Sort from low to high (Just in case) + // + SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize); + + // + // Set RuntimeData to XP + // + EnforceMemoryMapAttribute (MemoryMap, *MemoryMapSize, DescriptorSize); + + // + // Merge same type to save entry size + // + MergeMemoryMap (MemoryMap, MemoryMapSize, DescriptorSize); + + return ; +} + +/** + This function for GetMemoryMap() with memory attributes table. + + It calls original GetMemoryMap() to get the original memory map information. Then + plus the additional memory map entries for PE Code/Data separation. + + @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + the buffer allocated by the caller. On output, + it is the size of the buffer returned by the + firmware if the buffer was large enough, or the + size of the buffer needed to contain the map if + the buffer was too small. + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[out] MapKey A pointer to the location in which firmware + returns the key for the current memory map. + @param[out] DescriptorSize A pointer to the location in which firmware + returns the size, in bytes, of an individual + EFI_MEMORY_DESCRIPTOR. + @param[out] DescriptorVersion A pointer to the location in which firmware + returns the version number associated with the + EFI_MEMORY_DESCRIPTOR. + + @retval EFI_SUCCESS The memory map was returned in the MemoryMap + buffer. + @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current + buffer size needed to hold the memory map is + returned in MemoryMapSize. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + +**/ +STATIC +EFI_STATUS +EFIAPI +SmmCoreGetMemoryMapMemoryAttributesTable ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ) +{ + EFI_STATUS Status; + UINTN OldMemoryMapSize; + UINTN AdditionalRecordCount; + + // + // If PE code/data is not aligned, just return. + // + if ((mMemoryProtectionAttribute & EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) == 0) { + return SmmCoreGetMemoryMap (MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion); + } + + if (MemoryMapSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + AdditionalRecordCount = (2 * mImagePropertiesPrivateData.CodeSegmentCountMax + 2) * mImagePropertiesPrivateData.ImageRecordCount; + + OldMemoryMapSize = *MemoryMapSize; + Status = SmmCoreGetMemoryMap (MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion); + if (Status == EFI_BUFFER_TOO_SMALL) { + *MemoryMapSize = *MemoryMapSize + (*DescriptorSize) * AdditionalRecordCount; + } else if (Status == EFI_SUCCESS) { + if (OldMemoryMapSize - *MemoryMapSize < (*DescriptorSize) * AdditionalRecordCount) { + *MemoryMapSize = *MemoryMapSize + (*DescriptorSize) * AdditionalRecordCount; + // + // Need update status to buffer too small + // + Status = EFI_BUFFER_TOO_SMALL; + } else { + // + // Split PE code/data + // + ASSERT(MemoryMap != NULL); + SplitTable (MemoryMapSize, MemoryMap, *DescriptorSize); + } + } + + return Status; +} + +// +// Below functions are for ImageRecord +// + +/** + Set MemoryProtectionAttribute according to PE/COFF image section alignment. + + @param[in] SectionAlignment PE/COFF section alignment +**/ +STATIC +VOID +SetMemoryAttributesTableSectionAlignment ( + IN UINT32 SectionAlignment + ) +{ + if (((SectionAlignment & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) && + ((mMemoryProtectionAttribute & EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) != 0)) { + DEBUG ((DEBUG_VERBOSE, "SMM SetMemoryAttributesTableSectionAlignment - Clear\n")); + mMemoryProtectionAttribute &= ~((UINT64)EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); + } +} + +/** + Swap two code sections in image record. + + @param[in] FirstImageRecordCodeSection first code section in image record + @param[in] SecondImageRecordCodeSection second code section in image record +**/ +STATIC +VOID +SwapImageRecordCodeSection ( + IN IMAGE_PROPERTIES_RECORD_CODE_SECTION *FirstImageRecordCodeSection, + IN IMAGE_PROPERTIES_RECORD_CODE_SECTION *SecondImageRecordCodeSection + ) +{ + IMAGE_PROPERTIES_RECORD_CODE_SECTION TempImageRecordCodeSection; + + TempImageRecordCodeSection.CodeSegmentBase = FirstImageRecordCodeSection->CodeSegmentBase; + TempImageRecordCodeSection.CodeSegmentSize = FirstImageRecordCodeSection->CodeSegmentSize; + + FirstImageRecordCodeSection->CodeSegmentBase = SecondImageRecordCodeSection->CodeSegmentBase; + FirstImageRecordCodeSection->CodeSegmentSize = SecondImageRecordCodeSection->CodeSegmentSize; + + SecondImageRecordCodeSection->CodeSegmentBase = TempImageRecordCodeSection.CodeSegmentBase; + SecondImageRecordCodeSection->CodeSegmentSize = TempImageRecordCodeSection.CodeSegmentSize; +} + +/** + Sort code section in image record, based upon CodeSegmentBase from low to high. + + @param[in] ImageRecord image record to be sorted +**/ +STATIC +VOID +SortImageRecordCodeSection ( + IN IMAGE_PROPERTIES_RECORD *ImageRecord + ) +{ + IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; + IMAGE_PROPERTIES_RECORD_CODE_SECTION *NextImageRecordCodeSection; + LIST_ENTRY *ImageRecordCodeSectionLink; + LIST_ENTRY *NextImageRecordCodeSectionLink; + LIST_ENTRY *ImageRecordCodeSectionEndLink; + LIST_ENTRY *ImageRecordCodeSectionList; + + ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList; + + ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink; + NextImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink; + ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList; + while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) { + ImageRecordCodeSection = CR ( + ImageRecordCodeSectionLink, + IMAGE_PROPERTIES_RECORD_CODE_SECTION, + Link, + IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE + ); + while (NextImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) { + NextImageRecordCodeSection = CR ( + NextImageRecordCodeSectionLink, + IMAGE_PROPERTIES_RECORD_CODE_SECTION, + Link, + IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE + ); + if (ImageRecordCodeSection->CodeSegmentBase > NextImageRecordCodeSection->CodeSegmentBase) { + SwapImageRecordCodeSection (ImageRecordCodeSection, NextImageRecordCodeSection); + } + NextImageRecordCodeSectionLink = NextImageRecordCodeSectionLink->ForwardLink; + } + + ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink; + NextImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink; + } +} + +/** + Check if code section in image record is valid. + + @param[in] ImageRecord image record to be checked + + @retval TRUE image record is valid + @retval FALSE image record is invalid +**/ +STATIC +BOOLEAN +IsImageRecordCodeSectionValid ( + IN IMAGE_PROPERTIES_RECORD *ImageRecord + ) +{ + IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; + IMAGE_PROPERTIES_RECORD_CODE_SECTION *LastImageRecordCodeSection; + LIST_ENTRY *ImageRecordCodeSectionLink; + LIST_ENTRY *ImageRecordCodeSectionEndLink; + LIST_ENTRY *ImageRecordCodeSectionList; + + DEBUG ((DEBUG_VERBOSE, "SMM ImageCode SegmentCount - 0x%x\n", ImageRecord->CodeSegmentCount)); + + ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList; + + ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink; + ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList; + LastImageRecordCodeSection = NULL; + while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) { + ImageRecordCodeSection = CR ( + ImageRecordCodeSectionLink, + IMAGE_PROPERTIES_RECORD_CODE_SECTION, + Link, + IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE + ); + if (ImageRecordCodeSection->CodeSegmentSize == 0) { + return FALSE; + } + if (ImageRecordCodeSection->CodeSegmentBase < ImageRecord->ImageBase) { + return FALSE; + } + if (ImageRecordCodeSection->CodeSegmentBase >= MAX_ADDRESS - ImageRecordCodeSection->CodeSegmentSize) { + return FALSE; + } + if ((ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize) > (ImageRecord->ImageBase + ImageRecord->ImageSize)) { + return FALSE; + } + if (LastImageRecordCodeSection != NULL) { + if ((LastImageRecordCodeSection->CodeSegmentBase + LastImageRecordCodeSection->CodeSegmentSize) > ImageRecordCodeSection->CodeSegmentBase) { + return FALSE; + } + } + + LastImageRecordCodeSection = ImageRecordCodeSection; + ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink; + } + + return TRUE; +} + +/** + Swap two image records. + + @param[in] FirstImageRecord first image record. + @param[in] SecondImageRecord second image record. +**/ +STATIC +VOID +SwapImageRecord ( + IN IMAGE_PROPERTIES_RECORD *FirstImageRecord, + IN IMAGE_PROPERTIES_RECORD *SecondImageRecord + ) +{ + IMAGE_PROPERTIES_RECORD TempImageRecord; + + TempImageRecord.ImageBase = FirstImageRecord->ImageBase; + TempImageRecord.ImageSize = FirstImageRecord->ImageSize; + TempImageRecord.CodeSegmentCount = FirstImageRecord->CodeSegmentCount; + + FirstImageRecord->ImageBase = SecondImageRecord->ImageBase; + FirstImageRecord->ImageSize = SecondImageRecord->ImageSize; + FirstImageRecord->CodeSegmentCount = SecondImageRecord->CodeSegmentCount; + + SecondImageRecord->ImageBase = TempImageRecord.ImageBase; + SecondImageRecord->ImageSize = TempImageRecord.ImageSize; + SecondImageRecord->CodeSegmentCount = TempImageRecord.CodeSegmentCount; + + SwapListEntries (&FirstImageRecord->CodeSegmentList, &SecondImageRecord->CodeSegmentList); +} + +/** + Sort image record based upon the ImageBase from low to high. +**/ +STATIC +VOID +SortImageRecord ( + VOID + ) +{ + IMAGE_PROPERTIES_RECORD *ImageRecord; + IMAGE_PROPERTIES_RECORD *NextImageRecord; + LIST_ENTRY *ImageRecordLink; + LIST_ENTRY *NextImageRecordLink; + LIST_ENTRY *ImageRecordEndLink; + LIST_ENTRY *ImageRecordList; + + ImageRecordList = &mImagePropertiesPrivateData.ImageRecordList; + + ImageRecordLink = ImageRecordList->ForwardLink; + NextImageRecordLink = ImageRecordLink->ForwardLink; + ImageRecordEndLink = ImageRecordList; + while (ImageRecordLink != ImageRecordEndLink) { + ImageRecord = CR ( + ImageRecordLink, + IMAGE_PROPERTIES_RECORD, + Link, + IMAGE_PROPERTIES_RECORD_SIGNATURE + ); + while (NextImageRecordLink != ImageRecordEndLink) { + NextImageRecord = CR ( + NextImageRecordLink, + IMAGE_PROPERTIES_RECORD, + Link, + IMAGE_PROPERTIES_RECORD_SIGNATURE + ); + if (ImageRecord->ImageBase > NextImageRecord->ImageBase) { + SwapImageRecord (ImageRecord, NextImageRecord); + } + NextImageRecordLink = NextImageRecordLink->ForwardLink; + } + + ImageRecordLink = ImageRecordLink->ForwardLink; + NextImageRecordLink = ImageRecordLink->ForwardLink; + } +} + +/** + Dump image record. +**/ +STATIC +VOID +DumpImageRecord ( + VOID + ) +{ + IMAGE_PROPERTIES_RECORD *ImageRecord; + LIST_ENTRY *ImageRecordLink; + LIST_ENTRY *ImageRecordList; + UINTN Index; + + ImageRecordList = &mImagePropertiesPrivateData.ImageRecordList; + + for (ImageRecordLink = ImageRecordList->ForwardLink, Index= 0; + ImageRecordLink != ImageRecordList; + ImageRecordLink = ImageRecordLink->ForwardLink, Index++) { + ImageRecord = CR ( + ImageRecordLink, + IMAGE_PROPERTIES_RECORD, + Link, + IMAGE_PROPERTIES_RECORD_SIGNATURE + ); + DEBUG ((DEBUG_VERBOSE, "SMM Image[%d]: 0x%016lx - 0x%016lx\n", Index, ImageRecord->ImageBase, ImageRecord->ImageSize)); + } +} + +/** + Insert image record. + + @param[in] DriverEntry Driver information +**/ +VOID +SmmInsertImageRecord ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ) +{ + VOID *ImageAddress; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT32 PeCoffHeaderOffset; + UINT32 SectionAlignment; + EFI_IMAGE_SECTION_HEADER *Section; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + UINT8 *Name; + UINTN Index; + IMAGE_PROPERTIES_RECORD *ImageRecord; + CHAR8 *PdbPointer; + IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; + + DEBUG ((DEBUG_VERBOSE, "SMM InsertImageRecord - 0x%x\n", DriverEntry)); + DEBUG ((DEBUG_VERBOSE, "SMM InsertImageRecord - 0x%016lx - 0x%08x\n", DriverEntry->ImageBuffer, DriverEntry->NumberOfPage)); + + ImageRecord = AllocatePool (sizeof(*ImageRecord)); + if (ImageRecord == NULL) { + return ; + } + ImageRecord->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE; + + DEBUG ((DEBUG_VERBOSE, "SMM ImageRecordCount - 0x%x\n", mImagePropertiesPrivateData.ImageRecordCount)); + + // + // Step 1: record whole region + // + ImageRecord->ImageBase = DriverEntry->ImageBuffer; + ImageRecord->ImageSize = EfiPagesToSize(DriverEntry->NumberOfPage); + + ImageAddress = (VOID *)(UINTN)DriverEntry->ImageBuffer; + + PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress); + if (PdbPointer != NULL) { + DEBUG ((DEBUG_VERBOSE, "SMM Image - %a\n", PdbPointer)); + } + + // + // Check PE/COFF image + // + DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; + PeCoffHeaderOffset = 0; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + PeCoffHeaderOffset = DosHdr->e_lfanew; + } + + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); + if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + DEBUG ((DEBUG_VERBOSE, "SMM Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature)); + goto Finish; + } + + // + // Get SectionAlignment + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment; + } else { + SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment; + } + + SetMemoryAttributesTableSectionAlignment (SectionAlignment); + if ((SectionAlignment & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) { + DEBUG ((DEBUG_WARN, "SMM !!!!!!!! InsertImageRecord - Section Alignment(0x%x) is not %dK !!!!!!!!\n", + SectionAlignment, RUNTIME_PAGE_ALLOCATION_GRANULARITY >> 10)); + PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress); + if (PdbPointer != NULL) { + DEBUG ((DEBUG_WARN, "SMM !!!!!!!! Image - %a !!!!!!!!\n", PdbPointer)); + } + goto Finish; + } + + Section = (EFI_IMAGE_SECTION_HEADER *) ( + (UINT8 *) (UINTN) ImageAddress + + PeCoffHeaderOffset + + sizeof(UINT32) + + sizeof(EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + ImageRecord->CodeSegmentCount = 0; + InitializeListHead (&ImageRecord->CodeSegmentList); + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Name = Section[Index].Name; + DEBUG (( + DEBUG_VERBOSE, + "SMM Section - '%c%c%c%c%c%c%c%c'\n", + Name[0], + Name[1], + Name[2], + Name[3], + Name[4], + Name[5], + Name[6], + Name[7] + )); + + if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) { + DEBUG ((DEBUG_VERBOSE, "SMM VirtualSize - 0x%08x\n", Section[Index].Misc.VirtualSize)); + DEBUG ((DEBUG_VERBOSE, "SMM VirtualAddress - 0x%08x\n", Section[Index].VirtualAddress)); + DEBUG ((DEBUG_VERBOSE, "SMM SizeOfRawData - 0x%08x\n", Section[Index].SizeOfRawData)); + DEBUG ((DEBUG_VERBOSE, "SMM PointerToRawData - 0x%08x\n", Section[Index].PointerToRawData)); + DEBUG ((DEBUG_VERBOSE, "SMM PointerToRelocations - 0x%08x\n", Section[Index].PointerToRelocations)); + DEBUG ((DEBUG_VERBOSE, "SMM PointerToLinenumbers - 0x%08x\n", Section[Index].PointerToLinenumbers)); + DEBUG ((DEBUG_VERBOSE, "SMM NumberOfRelocations - 0x%08x\n", Section[Index].NumberOfRelocations)); + DEBUG ((DEBUG_VERBOSE, "SMM NumberOfLinenumbers - 0x%08x\n", Section[Index].NumberOfLinenumbers)); + DEBUG ((DEBUG_VERBOSE, "SMM Characteristics - 0x%08x\n", Section[Index].Characteristics)); + + // + // Step 2: record code section + // + ImageRecordCodeSection = AllocatePool (sizeof(*ImageRecordCodeSection)); + if (ImageRecordCodeSection == NULL) { + return ; + } + ImageRecordCodeSection->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; + + ImageRecordCodeSection->CodeSegmentBase = (UINTN)ImageAddress + Section[Index].VirtualAddress; + ImageRecordCodeSection->CodeSegmentSize = Section[Index].SizeOfRawData; + + DEBUG ((DEBUG_VERBOSE, "SMM ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection->CodeSegmentBase, ImageRecordCodeSection->CodeSegmentSize)); + + InsertTailList (&ImageRecord->CodeSegmentList, &ImageRecordCodeSection->Link); + ImageRecord->CodeSegmentCount++; + } + } + + if (ImageRecord->CodeSegmentCount == 0) { + SetMemoryAttributesTableSectionAlignment (1); + DEBUG ((DEBUG_ERROR, "SMM !!!!!!!! InsertImageRecord - CodeSegmentCount is 0 !!!!!!!!\n")); + PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress); + if (PdbPointer != NULL) { + DEBUG ((DEBUG_ERROR, "SMM !!!!!!!! Image - %a !!!!!!!!\n", PdbPointer)); + } + goto Finish; + } + + // + // Final + // + SortImageRecordCodeSection (ImageRecord); + // + // Check overlap all section in ImageBase/Size + // + if (!IsImageRecordCodeSectionValid (ImageRecord)) { + DEBUG ((DEBUG_ERROR, "SMM IsImageRecordCodeSectionValid - FAIL\n")); + goto Finish; + } + + InsertTailList (&mImagePropertiesPrivateData.ImageRecordList, &ImageRecord->Link); + mImagePropertiesPrivateData.ImageRecordCount++; + + if (mImagePropertiesPrivateData.CodeSegmentCountMax < ImageRecord->CodeSegmentCount) { + mImagePropertiesPrivateData.CodeSegmentCountMax = ImageRecord->CodeSegmentCount; + } + + SortImageRecord (); + +Finish: + return ; +} + + +/** + Publish MemoryAttributesTable to SMM configuration table. +**/ +VOID +PublishMemoryAttributesTable ( + VOID + ) +{ + UINTN MemoryMapSize; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN MapKey; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + UINTN Index; + EFI_STATUS Status; + UINTN RuntimeEntryCount; + EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable; + EFI_MEMORY_DESCRIPTOR *MemoryAttributesEntry; + UINTN MemoryAttributesTableSize; + + MemoryMapSize = 0; + MemoryMap = NULL; + Status = SmmCoreGetMemoryMapMemoryAttributesTable ( + &MemoryMapSize, + MemoryMap, + &MapKey, + &DescriptorSize, + &DescriptorVersion + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + do { + DEBUG ((DEBUG_INFO, "MemoryMapSize - 0x%x\n", MemoryMapSize)); + MemoryMap = AllocatePool (MemoryMapSize); + ASSERT (MemoryMap != NULL); + DEBUG ((DEBUG_INFO, "MemoryMap - 0x%x\n", MemoryMap)); + + Status = SmmCoreGetMemoryMapMemoryAttributesTable ( + &MemoryMapSize, + MemoryMap, + &MapKey, + &DescriptorSize, + &DescriptorVersion + ); + if (EFI_ERROR (Status)) { + FreePool (MemoryMap); + } + } while (Status == EFI_BUFFER_TOO_SMALL); + + // + // Allocate MemoryAttributesTable + // + RuntimeEntryCount = MemoryMapSize/DescriptorSize; + MemoryAttributesTableSize = sizeof(EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE) + DescriptorSize * RuntimeEntryCount; + MemoryAttributesTable = AllocatePool (sizeof(EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE) + DescriptorSize * RuntimeEntryCount); + ASSERT (MemoryAttributesTable != NULL); + MemoryAttributesTable->Version = EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE_VERSION; + MemoryAttributesTable->NumberOfEntries = (UINT32)RuntimeEntryCount; + MemoryAttributesTable->DescriptorSize = (UINT32)DescriptorSize; + MemoryAttributesTable->Reserved = 0; + DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n")); + DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version)); + DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries)); + DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize)); + MemoryAttributesEntry = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1); + for (Index = 0; Index < MemoryMapSize/DescriptorSize; Index++) { + CopyMem (MemoryAttributesEntry, MemoryMap, DescriptorSize); + DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryAttributesEntry)); + DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryAttributesEntry->Type)); + DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryAttributesEntry->PhysicalStart)); + DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryAttributesEntry->VirtualStart)); + DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryAttributesEntry->NumberOfPages)); + DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryAttributesEntry->Attribute)); + MemoryAttributesEntry = NEXT_MEMORY_DESCRIPTOR(MemoryAttributesEntry, DescriptorSize); + + MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize); + } + + Status = gSmst->SmmInstallConfigurationTable (gSmst, &gEdkiiPiSmmMemoryAttributesTableGuid, MemoryAttributesTable, MemoryAttributesTableSize); + ASSERT_EFI_ERROR (Status); +} + + +/** + This function installs all SMM image record information. +**/ +VOID +SmmInstallImageRecord ( + VOID + ) +{ + EFI_STATUS Status; + UINTN NoHandles; + EFI_HANDLE *HandleBuffer; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + UINTN Index; + EFI_SMM_DRIVER_ENTRY DriverEntry; + + Status = SmmLocateHandleBuffer ( + ByProtocol, + &gEfiLoadedImageProtocolGuid, + NULL, + &NoHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return ; + } + + for (Index = 0; Index < NoHandles; Index++) { + Status = gSmst->SmmHandleProtocol ( + HandleBuffer[Index], + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (EFI_ERROR (Status)) { + continue; + } + DEBUG ((DEBUG_VERBOSE, "LoadedImage - 0x%x 0x%x ", LoadedImage->ImageBase, LoadedImage->ImageSize)); + { + VOID *PdbPointer; + PdbPointer = PeCoffLoaderGetPdbPointer (LoadedImage->ImageBase); + if (PdbPointer != NULL) { + DEBUG ((DEBUG_VERBOSE, "(%a) ", PdbPointer)); + } + } + DEBUG ((DEBUG_VERBOSE, "\n")); + ZeroMem (&DriverEntry, sizeof(DriverEntry)); + DriverEntry.ImageBuffer = (UINTN)LoadedImage->ImageBase; + DriverEntry.NumberOfPage = EFI_SIZE_TO_PAGES((UINTN)LoadedImage->ImageSize); + SmmInsertImageRecord (&DriverEntry); + } + + FreePool (HandleBuffer); +} + +/** + Install MemoryAttributesTable. + + @param[in] Protocol Points to the protocol's unique identifier. + @param[in] Interface Points to the interface instance. + @param[in] Handle The handle on which the interface was installed. + + @retval EFI_SUCCESS Notification runs successfully. +**/ +EFI_STATUS +EFIAPI +SmmInstallMemoryAttributesTable ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + SmmInstallImageRecord (); + + DEBUG ((DEBUG_INFO, "SMM MemoryProtectionAttribute - 0x%016lx\n", mMemoryProtectionAttribute)); + if ((mMemoryProtectionAttribute & EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) == 0) { + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_VERBOSE, "SMM Total Image Count - 0x%x\n", mImagePropertiesPrivateData.ImageRecordCount)); + DEBUG ((DEBUG_VERBOSE, "SMM Dump ImageRecord:\n")); + DumpImageRecord (); + + PublishMemoryAttributesTable (); + + return EFI_SUCCESS; +} + +/** + Initialize MemoryAttributesTable support. +**/ +VOID +EFIAPI +SmmCoreInitializeMemoryAttributesTable ( + VOID + ) +{ + EFI_STATUS Status; + VOID *Registration; + + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmEndOfDxeProtocolGuid, + SmmInstallMemoryAttributesTable, + &Registration + ); + ASSERT_EFI_ERROR (Status); + + return ; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Notify.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Notify.c new file mode 100644 index 00000000..b1db3810 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Notify.c @@ -0,0 +1,196 @@ +/** @file + Support functions for UEFI protocol notification infrastructure. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +/** + Signal event for every protocol in protocol entry. + + @param Prot Protocol interface + +**/ +VOID +SmmNotifyProtocol ( + IN PROTOCOL_INTERFACE *Prot + ) +{ + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_NOTIFY *ProtNotify; + LIST_ENTRY *Link; + + ProtEntry = Prot->Protocol; + for (Link=ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link=Link->ForwardLink) { + ProtNotify = CR(Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE); + ProtNotify->Function (&ProtEntry->ProtocolID, Prot->Interface, Prot->Handle); + } +} + +/** + Removes Protocol from the protocol list (but not the handle list). + + @param Handle The handle to remove protocol on. + @param Protocol GUID of the protocol to be moved + @param Interface The interface of the protocol + + @return Protocol Entry + +**/ +PROTOCOL_INTERFACE * +SmmRemoveInterfaceFromProtocol ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ) +{ + PROTOCOL_INTERFACE *Prot; + PROTOCOL_NOTIFY *ProtNotify; + PROTOCOL_ENTRY *ProtEntry; + LIST_ENTRY *Link; + + Prot = SmmFindProtocolInterface (Handle, Protocol, Interface); + if (Prot != NULL) { + + ProtEntry = Prot->Protocol; + + // + // If there's a protocol notify location pointing to this entry, back it up one + // + for(Link = ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link=Link->ForwardLink) { + ProtNotify = CR(Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE); + + if (ProtNotify->Position == &Prot->ByProtocol) { + ProtNotify->Position = Prot->ByProtocol.BackLink; + } + } + + // + // Remove the protocol interface entry + // + RemoveEntryList (&Prot->ByProtocol); + } + + return Prot; +} + +/** + Add a new protocol notification record for the request protocol. + + @param Protocol The requested protocol to add the notify + registration + @param Function Points to the notification function + @param Registration Returns the registration record + + @retval EFI_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 +SmmRegisterProtocolNotify ( + IN CONST EFI_GUID *Protocol, + IN EFI_SMM_NOTIFY_FN Function, + OUT VOID **Registration + ) +{ + PROTOCOL_ENTRY *ProtEntry; + PROTOCOL_NOTIFY *ProtNotify; + LIST_ENTRY *Link; + EFI_STATUS Status; + + if (Protocol == NULL || Registration == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Function == NULL) { + // + // Get the protocol entry per Protocol + // + ProtEntry = SmmFindProtocolEntry ((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 = SmmFindProtocolEntry ((EFI_GUID *) Protocol, TRUE); + if (ProtEntry != NULL) { + // + // Find whether notification already exist + // + for (Link = ProtEntry->Notify.ForwardLink; + Link != &ProtEntry->Notify; + Link = Link->ForwardLink) { + + ProtNotify = CR(Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE); + if (CompareGuid (&ProtNotify->Protocol->ProtocolID, Protocol) && + (ProtNotify->Function == Function)) { + + // + // Notification already exist + // + *Registration = ProtNotify; + + return EFI_SUCCESS; + } + } + + // + // Allocate a new notification record + // + ProtNotify = AllocatePool (sizeof(PROTOCOL_NOTIFY)); + if (ProtNotify != NULL) { + ProtNotify->Signature = PROTOCOL_NOTIFY_SIGNATURE; + ProtNotify->Protocol = ProtEntry; + ProtNotify->Function = Function; + // + // Start at the ending + // + ProtNotify->Position = ProtEntry->Protocols.BackLink; + + InsertTailList (&ProtEntry->Notify, &ProtNotify->Link); + } + } + + // + // Done. If we have a protocol notify entry, then return it. + // Otherwise, we must have run out of resources trying to add one + // + Status = EFI_OUT_OF_RESOURCES; + if (ProtNotify != NULL) { + *Registration = ProtNotify; + Status = EFI_SUCCESS; + } + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Page.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Page.c new file mode 100644 index 00000000..f361dc7e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Page.c @@ -0,0 +1,1071 @@ +/** @file + SMM Memory page management functions. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" +#include <Library/SmmServicesTableLib.h> + +#define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT) + +LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap); + +// +// For GetMemoryMap() +// + +#define MEMORY_MAP_SIGNATURE SIGNATURE_32('m','m','a','p') +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + BOOLEAN FromStack; + EFI_MEMORY_TYPE Type; + UINT64 Start; + UINT64 End; + +} MEMORY_MAP; + +LIST_ENTRY gMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap); + + +#define MAX_MAP_DEPTH 6 + +/// +/// mMapDepth - depth of new descriptor stack +/// +UINTN mMapDepth = 0; +/// +/// mMapStack - space to use as temp storage to build new map descriptors +/// +MEMORY_MAP mMapStack[MAX_MAP_DEPTH]; +UINTN mFreeMapStack = 0; +/// +/// This list maintain the free memory map list +/// +LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList); + +/** + Allocates pages from the memory map. + + @param[in] Type The type of allocation to perform. + @param[in] MemoryType The type of memory to turn the allocated pages + into. + @param[in] NumberOfPages The number of pages to allocate. + @param[out] Memory A pointer to receive the base allocated memory + address. + @param[in] AddRegion If this memory is new added region. + @param[in] NeedGuard Flag to indicate Guard page is needed + or not + + @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 +SmmInternalAllocatePagesEx ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN AddRegion, + IN BOOLEAN NeedGuard + ); + +/** + Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList. + If the list is emtry, then allocate a new page to refuel the list. + Please Note this algorithm to allocate the memory map descriptor has a property + that the memory allocated for memory entries always grows, and will never really be freed. + + @return The Memory map descriptor dequeued from the mFreeMemoryMapEntryList + +**/ +MEMORY_MAP * +AllocateMemoryMapEntry ( + VOID + ) +{ + EFI_PHYSICAL_ADDRESS Mem; + EFI_STATUS Status; + MEMORY_MAP* FreeDescriptorEntries; + MEMORY_MAP* Entry; + UINTN Index; + + //DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n")); + + if (IsListEmpty (&mFreeMemoryMapEntryList)) { + //DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n")); + // + // The list is empty, to allocate one page to refuel the list + // + Status = SmmInternalAllocatePagesEx ( + AllocateAnyPages, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (RUNTIME_PAGE_ALLOCATION_GRANULARITY), + &Mem, + TRUE, + FALSE + ); + ASSERT_EFI_ERROR (Status); + if(!EFI_ERROR (Status)) { + FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem; + //DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries)); + // + // Enqueue the free memory map entries into the list + // + for (Index = 0; Index< RUNTIME_PAGE_ALLOCATION_GRANULARITY / sizeof(MEMORY_MAP); Index++) { + FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE; + InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link); + } + } else { + return NULL; + } + } + // + // dequeue the first descriptor from the list + // + Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + RemoveEntryList (&Entry->Link); + + return Entry; +} + + +/** + Internal function. Moves any memory descriptors that are on the + temporary descriptor stack to heap. + +**/ +VOID +CoreFreeMemoryMapStack ( + VOID + ) +{ + MEMORY_MAP *Entry; + + // + // If already freeing the map stack, then return + // + if (mFreeMapStack != 0) { + ASSERT (FALSE); + return ; + } + + // + // Move the temporary memory descriptor stack into pool + // + mFreeMapStack += 1; + + while (mMapDepth != 0) { + // + // Deque an memory map entry from mFreeMemoryMapEntryList + // + Entry = AllocateMemoryMapEntry (); + ASSERT (Entry); + + // + // Update to proper entry + // + mMapDepth -= 1; + + if (mMapStack[mMapDepth].Link.ForwardLink != NULL) { + + CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP)); + Entry->FromStack = FALSE; + + // + // Move this entry to general memory + // + InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link); + RemoveEntryList (&mMapStack[mMapDepth].Link); + mMapStack[mMapDepth].Link.ForwardLink = NULL; + } + } + + mFreeMapStack -= 1; +} + +/** + Insert new entry from memory map. + + @param[in] Link The old memory map entry to be linked. + @param[in] Start The start address of new memory map entry. + @param[in] End The end address of new memory map entry. + @param[in] Type The type of new memory map entry. + @param[in] Next If new entry is inserted to the next of old entry. + @param[in] AddRegion If this memory is new added region. +**/ +VOID +InsertNewEntry ( + IN LIST_ENTRY *Link, + IN UINT64 Start, + IN UINT64 End, + IN EFI_MEMORY_TYPE Type, + IN BOOLEAN Next, + IN BOOLEAN AddRegion + ) +{ + MEMORY_MAP *Entry; + + Entry = &mMapStack[mMapDepth]; + mMapDepth += 1; + ASSERT (mMapDepth < MAX_MAP_DEPTH); + Entry->FromStack = TRUE; + + Entry->Signature = MEMORY_MAP_SIGNATURE; + Entry->Type = Type; + Entry->Start = Start; + Entry->End = End; + if (Next) { + InsertHeadList (Link, &Entry->Link); + } else { + InsertTailList (Link, &Entry->Link); + } +} + +/** + Remove old entry from memory map. + + @param[in] Entry Memory map entry to be removed. +**/ +VOID +RemoveOldEntry ( + IN MEMORY_MAP *Entry + ) +{ + RemoveEntryList (&Entry->Link); + Entry->Link.ForwardLink = NULL; + + if (!Entry->FromStack) { + InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); + } +} + +/** + Update SMM memory map entry. + + @param[in] Type The type of allocation to perform. + @param[in] Memory The base of memory address. + @param[in] NumberOfPages The number of pages to allocate. + @param[in] AddRegion If this memory is new added region. +**/ +VOID +ConvertSmmMemoryMapEntry ( + IN EFI_MEMORY_TYPE Type, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN AddRegion + ) +{ + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + MEMORY_MAP *NextEntry; + LIST_ENTRY *NextLink; + MEMORY_MAP *PreviousEntry; + LIST_ENTRY *PreviousLink; + EFI_PHYSICAL_ADDRESS Start; + EFI_PHYSICAL_ADDRESS End; + + Start = Memory; + End = Memory + EFI_PAGES_TO_SIZE(NumberOfPages) - 1; + + // + // Exclude memory region + // + Link = gMemoryMap.ForwardLink; + while (Link != &gMemoryMap) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + Link = Link->ForwardLink; + + // + // --------------------------------------------------- + // | +----------+ +------+ +------+ +------+ | + // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|--- + // +----------+ ^ +------+ +------+ +------+ + // | + // +------+ + // |EntryX| + // +------+ + // + if (Entry->Start > End) { + if ((Entry->Start == End + 1) && (Entry->Type == Type)) { + Entry->Start = Start; + return ; + } + InsertNewEntry ( + &Entry->Link, + Start, + End, + Type, + FALSE, + AddRegion + ); + return ; + } + + if ((Entry->Start <= Start) && (Entry->End >= End)) { + if (Entry->Type != Type) { + if (Entry->Start < Start) { + // + // --------------------------------------------------- + // | +----------+ +------+ +------+ +------+ | + // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|--- + // +----------+ +------+ ^ +------+ +------+ + // | + // +------+ + // |EntryA| + // +------+ + // + InsertNewEntry ( + &Entry->Link, + Entry->Start, + Start - 1, + Entry->Type, + FALSE, + AddRegion + ); + } + if (Entry->End > End) { + // + // --------------------------------------------------- + // | +----------+ +------+ +------+ +------+ | + // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|--- + // +----------+ +------+ +------+ ^ +------+ + // | + // +------+ + // |EntryZ| + // +------+ + // + InsertNewEntry ( + &Entry->Link, + End + 1, + Entry->End, + Entry->Type, + TRUE, + AddRegion + ); + } + // + // Update this node + // + Entry->Start = Start; + Entry->End = End; + Entry->Type = Type; + + // + // Check adjacent + // + NextLink = Entry->Link.ForwardLink; + if (NextLink != &gMemoryMap) { + NextEntry = CR (NextLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + // + // --------------------------------------------------- + // | +----------+ +------+ +-----------------+ | + // ---|gMemoryMep|---|Entry1|---|EntryX Entry3|--- + // +----------+ +------+ +-----------------+ + // + if ((Entry->Type == NextEntry->Type) && (Entry->End + 1 == NextEntry->Start)) { + Entry->End = NextEntry->End; + RemoveOldEntry (NextEntry); + } + } + PreviousLink = Entry->Link.BackLink; + if (PreviousLink != &gMemoryMap) { + PreviousEntry = CR (PreviousLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + // + // --------------------------------------------------- + // | +----------+ +-----------------+ +------+ | + // ---|gMemoryMep|---|Entry1 EntryX|---|Entry3|--- + // +----------+ +-----------------+ +------+ + // + if ((PreviousEntry->Type == Entry->Type) && (PreviousEntry->End + 1 == Entry->Start)) { + PreviousEntry->End = Entry->End; + RemoveOldEntry (Entry); + } + } + } + return ; + } + } + + // + // --------------------------------------------------- + // | +----------+ +------+ +------+ +------+ | + // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|--- + // +----------+ +------+ +------+ +------+ ^ + // | + // +------+ + // |EntryX| + // +------+ + // + Link = gMemoryMap.BackLink; + if (Link != &gMemoryMap) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + if ((Entry->End + 1 == Start) && (Entry->Type == Type)) { + Entry->End = End; + return ; + } + } + InsertNewEntry ( + &gMemoryMap, + Start, + End, + Type, + FALSE, + AddRegion + ); + return ; +} + +/** + Return the count of Smm memory map entry. + + @return The count of Smm memory map entry. +**/ +UINTN +GetSmmMemoryMapEntryCount ( + VOID + ) +{ + LIST_ENTRY *Link; + UINTN Count; + + Count = 0; + Link = gMemoryMap.ForwardLink; + while (Link != &gMemoryMap) { + Link = Link->ForwardLink; + Count++; + } + return Count; +} + + + +/** + 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[in] Type The type of allocation to perform. + @param[in] MemoryType The type of memory to turn the allocated pages + into. + @param[in] NumberOfPages The number of pages to allocate. + @param[out] Memory A pointer to receive the base allocated memory + address. + @param[in] AddRegion If this memory is new added region. + @param[in] NeedGuard Flag to indicate Guard page is needed + or not + + @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 +SmmInternalAllocatePagesEx ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN AddRegion, + IN BOOLEAN NeedGuard + ) +{ + UINTN RequestedAddress; + + if (MemoryType != EfiRuntimeServicesCode && + MemoryType != EfiRuntimeServicesData) { + return EFI_INVALID_PARAMETER; + } + + if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) { + return EFI_OUT_OF_RESOURCES; + } + + // + // We don't track memory type in SMM + // + RequestedAddress = (UINTN)*Memory; + switch (Type) { + case AllocateAnyPages: + RequestedAddress = (UINTN)(-1); + case AllocateMaxAddress: + if (NeedGuard) { + *Memory = InternalAllocMaxAddressWithGuard ( + &mSmmMemoryMap, + NumberOfPages, + RequestedAddress, + MemoryType + ); + if (*Memory == (UINTN)-1) { + return EFI_OUT_OF_RESOURCES; + } else { + ASSERT (VerifyMemoryGuard (*Memory, NumberOfPages) == TRUE); + return EFI_SUCCESS; + } + } + + *Memory = InternalAllocMaxAddress ( + &mSmmMemoryMap, + NumberOfPages, + RequestedAddress + ); + if (*Memory == (UINTN)-1) { + return EFI_OUT_OF_RESOURCES; + } + break; + case AllocateAddress: + *Memory = InternalAllocAddress ( + &mSmmMemoryMap, + NumberOfPages, + RequestedAddress + ); + if (*Memory != RequestedAddress) { + return EFI_NOT_FOUND; + } + break; + default: + return EFI_INVALID_PARAMETER; + } + + // + // Update SmmMemoryMap here. + // + ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion); + if (!AddRegion) { + CoreFreeMemoryMapStack(); + } + + return EFI_SUCCESS; +} + +/** + Allocates pages from the memory map. + + @param[in] Type The type of allocation to perform. + @param[in] MemoryType The type of memory to turn the allocated pages + into. + @param[in] NumberOfPages The number of pages to allocate. + @param[out] Memory A pointer to receive the base allocated memory + address. + @param[in] NeedGuard Flag to indicate Guard page is needed + or not + + @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 +SmmInternalAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN NeedGuard + ) +{ + return SmmInternalAllocatePagesEx (Type, MemoryType, NumberOfPages, Memory, + FALSE, NeedGuard); +} + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform. + @param MemoryType The type of memory to turn the allocated pages + into. + @param NumberOfPages The number of pages to allocate. + @param Memory A pointer to receive the base allocated memory + address. + + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ) +{ + EFI_STATUS Status; + BOOLEAN NeedGuard; + + NeedGuard = IsPageTypeToGuard (MemoryType, Type); + Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory, + NeedGuard); + if (!EFI_ERROR (Status)) { + SmmCoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), + MemoryProfileActionAllocatePages, + MemoryType, + EFI_PAGES_TO_SIZE (NumberOfPages), + (VOID *) (UINTN) *Memory, + NULL + ); + } + 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[in] Memory Base address of memory being freed. + @param[in] NumberOfPages The number of pages to free. + @param[in] AddRegion If this memory is new added region. + + @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 +SmmInternalFreePagesEx ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN AddRegion + ) +{ + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + + if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) { + return EFI_INVALID_PARAMETER; + } + + Pages = NULL; + Node = mSmmMemoryMap.ForwardLink; + while (Node != &mSmmMemoryMap) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + if (Memory < (UINTN)Pages) { + break; + } + Node = Node->ForwardLink; + } + + if (Node != &mSmmMemoryMap && + Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) { + return EFI_INVALID_PARAMETER; + } + + if (Node->BackLink != &mSmmMemoryMap) { + Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link); + if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) { + return EFI_INVALID_PARAMETER; + } + } + + Pages = (FREE_PAGE_LIST*)(UINTN)Memory; + Pages->NumberOfPages = NumberOfPages; + InsertTailList (Node, &Pages->Link); + + if (Pages->Link.BackLink != &mSmmMemoryMap) { + Pages = InternalMergeNodes ( + BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link) + ); + } + + if (Node != &mSmmMemoryMap) { + InternalMergeNodes (Pages); + } + + // + // Update SmmMemoryMap here. + // + ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion); + if (!AddRegion) { + CoreFreeMemoryMapStack(); + } + + return EFI_SUCCESS; +} + +/** + Frees previous allocated pages. + + @param[in] Memory Base address of memory being freed. + @param[in] NumberOfPages The number of pages to free. + @param[in] IsGuarded Is the memory to free guarded or not. + + @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 +SmmInternalFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN IsGuarded + ) +{ + if (IsGuarded) { + return SmmInternalFreePagesExWithGuard (Memory, NumberOfPages, FALSE); + } + return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE); +} + +/** + Check whether the input range is in memory map. + + @param Memory Base address of memory being inputed. + @param NumberOfPages The number of pages. + + @retval TRUE In memory map. + @retval FALSE Not in memory map. + +**/ +BOOLEAN +InMemMap ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + EFI_PHYSICAL_ADDRESS Last; + + Last = Memory + EFI_PAGES_TO_SIZE (NumberOfPages) - 1; + + Link = gMemoryMap.ForwardLink; + while (Link != &gMemoryMap) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + Link = Link->ForwardLink; + + if ((Entry->Start <= Memory) && (Entry->End >= Last)) { + return TRUE; + } + } + + return FALSE; +} + +/** + 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 +SmmFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_STATUS Status; + BOOLEAN IsGuarded; + + if (!InMemMap(Memory, NumberOfPages)) { + return EFI_NOT_FOUND; + } + + IsGuarded = IsHeapGuardEnabled () && IsMemoryGuarded (Memory); + Status = SmmInternalFreePages (Memory, NumberOfPages, IsGuarded); + if (!EFI_ERROR (Status)) { + SmmCoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), + MemoryProfileActionFreePages, + EfiMaxMemoryType, + EFI_PAGES_TO_SIZE (NumberOfPages), + (VOID *) (UINTN) Memory, + NULL + ); + } + return Status; +} + +/** + Add free SMRAM region for use by memory service. + + @param MemBase Base address of memory region. + @param MemLength Length of the memory region. + @param Type Memory type. + @param Attributes Memory region state. + +**/ +VOID +SmmAddMemoryRegion ( + IN EFI_PHYSICAL_ADDRESS MemBase, + IN UINT64 MemLength, + IN EFI_MEMORY_TYPE Type, + IN UINT64 Attributes + ) +{ + UINTN AlignedMemBase; + + // + // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization + // + if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { + Type = EfiRuntimeServicesData; + } else { + Type = EfiConventionalMemory; + } + + DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n")); + DEBUG ((DEBUG_INFO, " MemBase - 0x%lx\n", MemBase)); + DEBUG ((DEBUG_INFO, " MemLength - 0x%lx\n", MemLength)); + DEBUG ((DEBUG_INFO, " Type - 0x%x\n", Type)); + DEBUG ((DEBUG_INFO, " Attributes - 0x%lx\n", Attributes)); + + // + // Align range on an EFI_PAGE_SIZE boundary + // + AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + MemLength -= AlignedMemBase - MemBase; + if (Type == EfiConventionalMemory) { + SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE); + } else { + ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE); + } + + CoreFreeMemoryMapStack (); +} + +/** + This function returns a copy of the current memory map. The map is an array of + memory descriptors, each of which describes a contiguous block of memory. + + @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + the buffer allocated by the caller. On output, + it is the size of the buffer returned by the + firmware if the buffer was large enough, or the + size of the buffer needed to contain the map if + the buffer was too small. + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[out] MapKey A pointer to the location in which firmware + returns the key for the current memory map. + @param[out] DescriptorSize A pointer to the location in which firmware + returns the size, in bytes, of an individual + EFI_MEMORY_DESCRIPTOR. + @param[out] DescriptorVersion A pointer to the location in which firmware + returns the version number associated with the + EFI_MEMORY_DESCRIPTOR. + + @retval EFI_SUCCESS The memory map was returned in the MemoryMap + buffer. + @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current + buffer size needed to hold the memory map is + returned in MemoryMapSize. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + +**/ +EFI_STATUS +EFIAPI +SmmCoreGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ) +{ + UINTN Count; + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + UINTN Size; + UINTN BufferSize; + + Size = sizeof (EFI_MEMORY_DESCRIPTOR); + + // + // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will + // prevent people from having pointer math bugs in their code. + // now you have to use *DescriptorSize to make things work. + // + Size += sizeof(UINT64) - (Size % sizeof (UINT64)); + + if (DescriptorSize != NULL) { + *DescriptorSize = Size; + } + + if (DescriptorVersion != NULL) { + *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION; + } + + Count = GetSmmMemoryMapEntryCount (); + BufferSize = Size * Count; + if (*MemoryMapSize < BufferSize) { + *MemoryMapSize = BufferSize; + return EFI_BUFFER_TOO_SMALL; + } + + *MemoryMapSize = BufferSize; + if (MemoryMap == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (MemoryMap, BufferSize); + Link = gMemoryMap.ForwardLink; + while (Link != &gMemoryMap) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + Link = Link->ForwardLink; + + MemoryMap->Type = Entry->Type; + MemoryMap->PhysicalStart = Entry->Start; + MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT); + + MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c new file mode 100644 index 00000000..3ceb46a1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c @@ -0,0 +1,920 @@ +/** @file + SMM Core Main Entry Point + + Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +// +// Physical pointer to private structure shared between SMM IPL and the SMM Core +// +SMM_CORE_PRIVATE_DATA *gSmmCorePrivate; + +// +// SMM Core global variable for SMM System Table. Only accessed as a physical structure in SMRAM. +// +EFI_SMM_SYSTEM_TABLE2 gSmmCoreSmst = { + { + SMM_SMST_SIGNATURE, + EFI_SMM_SYSTEM_TABLE2_REVISION, + sizeof (gSmmCoreSmst.Hdr) + }, + NULL, // SmmFirmwareVendor + 0, // SmmFirmwareRevision + SmmInstallConfigurationTable, + { + { + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5, // SmmMemRead + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5 // SmmMemWrite + }, + { + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5, // SmmIoRead + (EFI_SMM_CPU_IO2) SmmEfiNotAvailableYetArg5 // SmmIoWrite + } + }, + SmmAllocatePool, + SmmFreePool, + SmmAllocatePages, + SmmFreePages, + NULL, // SmmStartupThisAp + 0, // CurrentlyExecutingCpu + 0, // NumberOfCpus + NULL, // CpuSaveStateSize + NULL, // CpuSaveState + 0, // NumberOfTableEntries + NULL, // SmmConfigurationTable + SmmInstallProtocolInterface, + SmmUninstallProtocolInterface, + SmmHandleProtocol, + SmmRegisterProtocolNotify, + SmmLocateHandle, + SmmLocateProtocol, + SmiManage, + SmiHandlerRegister, + SmiHandlerUnRegister +}; + +// +// Flag to determine if the platform has performed a legacy boot. +// If this flag is TRUE, then the runtime code and runtime data associated with the +// SMM IPL are converted to free memory, so the SMM Core must guarantee that is +// does not touch of the code/data associated with the SMM IPL if this flag is TRUE. +// +BOOLEAN mInLegacyBoot = FALSE; + +// +// Flag to determine if it is during S3 resume. +// It will be set in S3 entry callback and cleared at EndOfS3Resume. +// +BOOLEAN mDuringS3Resume = FALSE; + +// +// Flag to determine if platform enabled S3. +// Get the value from PcdAcpiS3Enable. +// +BOOLEAN mAcpiS3Enable = FALSE; + +// +// Table of SMI Handlers that are registered by the SMM Core when it is initialized +// +SMM_CORE_SMI_HANDLERS mSmmCoreSmiHandlers[] = { + { SmmDriverDispatchHandler, &gEfiEventDxeDispatchGuid, NULL, TRUE }, + { SmmReadyToLockHandler, &gEfiDxeSmmReadyToLockProtocolGuid, NULL, TRUE }, + { SmmLegacyBootHandler, &gEfiEventLegacyBootGuid, NULL, FALSE }, + { SmmExitBootServicesHandler, &gEfiEventExitBootServicesGuid, NULL, FALSE }, + { SmmReadyToBootHandler, &gEfiEventReadyToBootGuid, NULL, FALSE }, + { SmmEndOfDxeHandler, &gEfiEndOfDxeEventGroupGuid, NULL, TRUE }, + { NULL, NULL, NULL, FALSE } +}; + +// +// Table of SMI Handlers that are registered by the SMM Core when it is initialized +// +SMM_CORE_SMI_HANDLERS mSmmCoreS3SmiHandlers[] = { + { SmmS3SmmInitDoneHandler, &gEdkiiS3SmmInitDoneGuid, NULL, FALSE }, + { SmmEndOfS3ResumeHandler, &gEdkiiEndOfS3ResumeGuid, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +UINTN mFullSmramRangeCount; +EFI_SMRAM_DESCRIPTOR *mFullSmramRanges; + +EFI_SMM_DRIVER_ENTRY *mSmmCoreDriverEntry; + +EFI_LOADED_IMAGE_PROTOCOL *mSmmCoreLoadedImage; + +/** + Place holder function until all the SMM System Table Service are available. + + Note: This function is only used by SMRAM invocation. It is never used by DXE invocation. + + @param Arg1 Undefined + @param Arg2 Undefined + @param Arg3 Undefined + @param Arg4 Undefined + @param Arg5 Undefined + + @return EFI_NOT_AVAILABLE_YET + +**/ +EFI_STATUS +EFIAPI +SmmEfiNotAvailableYetArg5 ( + UINTN Arg1, + UINTN Arg2, + UINTN Arg3, + UINTN Arg4, + UINTN Arg5 + ) +{ + // + // This function should never be executed. If it does, then the architectural protocols + // have not been designed correctly. + // + return EFI_NOT_AVAILABLE_YET; +} + +/** + Software SMI handler that is called when a Legacy Boot event is signalled. The SMM + Core uses this signal to know that a Legacy Boot has been performed and that + gSmmCorePrivate that is shared between the UEFI and SMM execution environments can + not be accessed from SMM anymore since that structure is considered free memory by + a legacy OS. Then the SMM Core also install SMM Legacy Boot protocol to notify SMM + driver that system enter legacy boot. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmLegacyBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + UINTN Index; + + // + // Install SMM Legacy Boot protocol. + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEdkiiSmmLegacyBootProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + mInLegacyBoot = TRUE; + + SmiHandlerUnRegister (DispatchHandle); + + // + // It is legacy boot, unregister ExitBootService SMI handler. + // + for (Index = 0; mSmmCoreSmiHandlers[Index].HandlerType != NULL; Index++) { + if (CompareGuid (mSmmCoreSmiHandlers[Index].HandlerType, &gEfiEventExitBootServicesGuid)) { + SmiHandlerUnRegister (mSmmCoreSmiHandlers[Index].DispatchHandle); + break; + } + } + + return Status; +} + +/** + Software SMI handler that is called when an Exit Boot Services event is signalled. + Then the SMM Core also install SMM Exit Boot Services protocol to notify SMM driver + that system enter exit boot services. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmExitBootServicesHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + UINTN Index; + + // + // Install SMM Exit Boot Services protocol. + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEdkiiSmmExitBootServicesProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + SmiHandlerUnRegister (DispatchHandle); + + // + // It is UEFI boot, unregister LegacyBoot SMI handler. + // + for (Index = 0; mSmmCoreSmiHandlers[Index].HandlerType != NULL; Index++) { + if (CompareGuid (mSmmCoreSmiHandlers[Index].HandlerType, &gEfiEventLegacyBootGuid)) { + SmiHandlerUnRegister (mSmmCoreSmiHandlers[Index].DispatchHandle); + break; + } + } + + return Status; +} + +/** + Main entry point for an SMM handler dispatch or communicate-based callback. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] Context Points to an optional handler context which was specified when the + handler was registered. + @param[in,out] CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param[in,out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. +**/ +EFI_STATUS +EFIAPI +SmmS3EntryCallBack ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + mDuringS3Resume = TRUE; + return EFI_SUCCESS; +} + +/** + Software SMI handler that is called when an Ready To Boot event is signalled. + Then the SMM Core also install SMM Ready To Boot protocol to notify SMM driver + that system enter ready to boot. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmReadyToBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + + // + // Install SMM Ready To Boot protocol. + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEdkiiSmmReadyToBootProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + SmiHandlerUnRegister (DispatchHandle); + + return Status; +} + +/** + Software SMI handler that is called when the DxeSmmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signalled. This function unregisters the + Software SMIs that are nor required after SMRAM is locked and installs the + SMM Ready To Lock Protocol so SMM Drivers are informed that SMRAM is about + to be locked. It also verifies the SMM CPU I/O 2 Protocol has been installed + and NULLs gBS and gST because they can not longer be used after SMRAM is locked. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmReadyToLockHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_HANDLE SmmHandle; + VOID *Interface; + + // + // Unregister SMI Handlers that are no required after the SMM driver dispatch is stopped + // + for (Index = 0; mSmmCoreSmiHandlers[Index].HandlerType != NULL; Index++) { + if (mSmmCoreSmiHandlers[Index].UnRegister) { + SmiHandlerUnRegister (mSmmCoreSmiHandlers[Index].DispatchHandle); + } + } + + // + // Install SMM Ready to lock protocol + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEfiSmmReadyToLockProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + // + // Make sure SMM CPU I/O 2 Protocol has been installed into the handle database + // + Status = SmmLocateProtocol (&gEfiSmmCpuIo2ProtocolGuid, NULL, &Interface); + + // + // Print a message on a debug build if the SMM CPU I/O 2 Protocol is not installed + // + DEBUG_CODE_BEGIN (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "\nSMM: SmmCpuIo Arch Protocol not present!!\n")); + } + DEBUG_CODE_END (); + + // + // Assert if the CPU I/O 2 Protocol is not installed + // + ASSERT_EFI_ERROR (Status); + + // + // Display any drivers that were not dispatched because dependency expression + // evaluated to false if this is a debug build + // + DEBUG_CODE_BEGIN (); + SmmDisplayDiscoveredNotDispatched (); + DEBUG_CODE_END (); + + // + // Not allowed to use gST or gBS after lock + // + gST = NULL; + gBS = NULL; + + SmramProfileReadyToLock (); + + return Status; +} + +/** + Software SMI handler that is called when the EndOfDxe event is signalled. + This function installs the SMM EndOfDxe Protocol so SMM Drivers are informed that + platform code will invoke 3rd part code. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmEndOfDxeHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + EFI_SMM_SX_DISPATCH2_PROTOCOL *SxDispatch; + EFI_SMM_SX_REGISTER_CONTEXT EntryRegisterContext; + EFI_HANDLE S3EntryHandle; + + DEBUG ((EFI_D_INFO, "SmmEndOfDxeHandler\n")); + + // + // Install SMM EndOfDxe protocol + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEfiSmmEndOfDxeProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + if (mAcpiS3Enable) { + // + // Locate SmmSxDispatch2 protocol. + // + Status = SmmLocateProtocol ( + &gEfiSmmSxDispatch2ProtocolGuid, + NULL, + (VOID **)&SxDispatch + ); + if (!EFI_ERROR (Status) && (SxDispatch != NULL)) { + // + // Register a S3 entry callback function to + // determine if it will be during S3 resume. + // + EntryRegisterContext.Type = SxS3; + EntryRegisterContext.Phase = SxEntry; + Status = SxDispatch->Register ( + SxDispatch, + SmmS3EntryCallBack, + &EntryRegisterContext, + &S3EntryHandle + ); + ASSERT_EFI_ERROR (Status); + } + } + + return EFI_SUCCESS; +} + +/** + Software SMI handler that is called when the S3SmmInitDone signal is triggered. + This function installs the SMM S3SmmInitDone Protocol so SMM Drivers are informed that + S3 SMM initialization has been done. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmS3SmmInitDoneHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + + DEBUG ((DEBUG_INFO, "SmmS3SmmInitDoneHandler\n")); + + if (!mDuringS3Resume) { + DEBUG ((DEBUG_ERROR, "It is not during S3 resume\n")); + return EFI_SUCCESS; + } + + // + // Install SMM S3SmmInitDone protocol + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEdkiiS3SmmInitDoneGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Uninstall the protocol here because the comsumer just hook the + // installation event. + // + Status = SmmUninstallProtocolInterface ( + SmmHandle, + &gEdkiiS3SmmInitDoneGuid, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Software SMI handler that is called when the EndOfS3Resume signal is triggered. + This function installs the SMM EndOfS3Resume Protocol so SMM Drivers are informed that + S3 resume has finished. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmEndOfS3ResumeHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + + DEBUG ((DEBUG_INFO, "SmmEndOfS3ResumeHandler\n")); + + if (!mDuringS3Resume) { + DEBUG ((DEBUG_ERROR, "It is not during S3 resume\n")); + return EFI_SUCCESS; + } + + // + // Install SMM EndOfS3Resume protocol + // + SmmHandle = NULL; + Status = SmmInstallProtocolInterface ( + &SmmHandle, + &gEdkiiEndOfS3ResumeGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Uninstall the protocol here because the consumer just hook the + // installation event. + // + Status = SmmUninstallProtocolInterface ( + SmmHandle, + &gEdkiiEndOfS3ResumeGuid, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mDuringS3Resume = FALSE; + return Status; +} + +/** + Determine if two buffers overlap in memory. + + @param[in] Buff1 Pointer to first buffer + @param[in] Size1 Size of Buff1 + @param[in] Buff2 Pointer to second buffer + @param[in] Size2 Size of Buff2 + + @retval TRUE Buffers overlap in memory. + @retval FALSE Buffer doesn't overlap. + +**/ +BOOLEAN +InternalIsBufferOverlapped ( + IN UINT8 *Buff1, + IN UINTN Size1, + IN UINT8 *Buff2, + IN UINTN Size2 + ) +{ + // + // If buff1's end is less than the start of buff2, then it's ok. + // Also, if buff1's start is beyond buff2's end, then it's ok. + // + if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) { + return FALSE; + } + + return TRUE; +} + +/** + The main entry point to SMM Foundation. + + Note: This function is only used by SMRAM invocation. It is never used by DXE invocation. + + @param SmmEntryContext Processor information and functionality + needed by SMM Foundation. + +**/ +VOID +EFIAPI +SmmEntryPoint ( + IN CONST EFI_SMM_ENTRY_CONTEXT *SmmEntryContext +) +{ + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *CommunicateHeader; + BOOLEAN InLegacyBoot; + BOOLEAN IsOverlapped; + VOID *CommunicationBuffer; + UINTN BufferSize; + + // + // Update SMST with contents of the SmmEntryContext structure + // + gSmmCoreSmst.SmmStartupThisAp = SmmEntryContext->SmmStartupThisAp; + gSmmCoreSmst.CurrentlyExecutingCpu = SmmEntryContext->CurrentlyExecutingCpu; + gSmmCoreSmst.NumberOfCpus = SmmEntryContext->NumberOfCpus; + gSmmCoreSmst.CpuSaveStateSize = SmmEntryContext->CpuSaveStateSize; + gSmmCoreSmst.CpuSaveState = SmmEntryContext->CpuSaveState; + + // + // Call platform hook before Smm Dispatch + // + PlatformHookBeforeSmmDispatch (); + + // + // Call memory management hook function + // + SmmEntryPointMemoryManagementHook (); + + // + // If a legacy boot has occurred, then make sure gSmmCorePrivate is not accessed + // + InLegacyBoot = mInLegacyBoot; + if (!InLegacyBoot) { + // + // Mark the InSmm flag as TRUE, it will be used by SmmBase2 protocol + // + gSmmCorePrivate->InSmm = TRUE; + + // + // Check to see if this is a Synchronous SMI sent through the SMM Communication + // Protocol or an Asynchronous SMI + // + CommunicationBuffer = gSmmCorePrivate->CommunicationBuffer; + BufferSize = gSmmCorePrivate->BufferSize; + if (CommunicationBuffer != NULL) { + // + // Synchronous SMI for SMM Core or request from Communicate protocol + // + IsOverlapped = InternalIsBufferOverlapped ( + (UINT8 *) CommunicationBuffer, + BufferSize, + (UINT8 *) gSmmCorePrivate, + sizeof (*gSmmCorePrivate) + ); + if (!SmmIsBufferOutsideSmmValid ((UINTN)CommunicationBuffer, BufferSize) || IsOverlapped) { + // + // If CommunicationBuffer is not in valid address scope, + // or there is overlap between gSmmCorePrivate and CommunicationBuffer, + // return EFI_INVALID_PARAMETER + // + gSmmCorePrivate->CommunicationBuffer = NULL; + gSmmCorePrivate->ReturnStatus = EFI_ACCESS_DENIED; + } else { + CommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)CommunicationBuffer; + BufferSize -= OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + Status = SmiManage ( + &CommunicateHeader->HeaderGuid, + NULL, + CommunicateHeader->Data, + &BufferSize + ); + // + // Update CommunicationBuffer, BufferSize and ReturnStatus + // Communicate service finished, reset the pointer to CommBuffer to NULL + // + gSmmCorePrivate->BufferSize = BufferSize + OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + gSmmCorePrivate->CommunicationBuffer = NULL; + gSmmCorePrivate->ReturnStatus = (Status == EFI_SUCCESS) ? EFI_SUCCESS : EFI_NOT_FOUND; + } + } + } + + // + // Process Asynchronous SMI sources + // + SmiManage (NULL, NULL, NULL, NULL); + + // + // Call platform hook after Smm Dispatch + // + PlatformHookAfterSmmDispatch (); + + // + // If a legacy boot has occurred, then make sure gSmmCorePrivate is not accessed + // + if (!InLegacyBoot) { + // + // Clear the InSmm flag as we are going to leave SMM + // + gSmmCorePrivate->InSmm = FALSE; + } +} + +/** + Install LoadedImage protocol for SMM Core. +**/ +VOID +SmmCoreInstallLoadedImage ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + // + // Allocate a Loaded Image Protocol in EfiBootServicesData + // + Status = gBS->AllocatePool (EfiBootServicesData, sizeof(EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&mSmmCoreLoadedImage); + ASSERT_EFI_ERROR (Status); + + ZeroMem (mSmmCoreLoadedImage, 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. + // + mSmmCoreLoadedImage->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; + mSmmCoreLoadedImage->ParentHandle = gSmmCorePrivate->SmmIplImageHandle; + mSmmCoreLoadedImage->SystemTable = gST; + + mSmmCoreLoadedImage->ImageBase = (VOID *)(UINTN)gSmmCorePrivate->PiSmmCoreImageBase; + mSmmCoreLoadedImage->ImageSize = gSmmCorePrivate->PiSmmCoreImageSize; + mSmmCoreLoadedImage->ImageCodeType = EfiRuntimeServicesCode; + mSmmCoreLoadedImage->ImageDataType = EfiRuntimeServicesData; + + // + // Create a new image handle in the UEFI handle database for the SMM Driver + // + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiLoadedImageProtocolGuid, mSmmCoreLoadedImage, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Allocate a Loaded Image Protocol in SMM + // + Status = SmmAllocatePool (EfiRuntimeServicesData, sizeof(EFI_SMM_DRIVER_ENTRY), (VOID **)&mSmmCoreDriverEntry); + ASSERT_EFI_ERROR(Status); + + ZeroMem (mSmmCoreDriverEntry, sizeof(EFI_SMM_DRIVER_ENTRY)); + // + // Fill in the remaining fields of the Loaded Image Protocol instance. + // + mSmmCoreDriverEntry->Signature = EFI_SMM_DRIVER_ENTRY_SIGNATURE; + mSmmCoreDriverEntry->SmmLoadedImage.Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; + mSmmCoreDriverEntry->SmmLoadedImage.ParentHandle = gSmmCorePrivate->SmmIplImageHandle; + mSmmCoreDriverEntry->SmmLoadedImage.SystemTable = gST; + + mSmmCoreDriverEntry->SmmLoadedImage.ImageBase = (VOID *)(UINTN)gSmmCorePrivate->PiSmmCoreImageBase; + mSmmCoreDriverEntry->SmmLoadedImage.ImageSize = gSmmCorePrivate->PiSmmCoreImageSize; + mSmmCoreDriverEntry->SmmLoadedImage.ImageCodeType = EfiRuntimeServicesCode; + mSmmCoreDriverEntry->SmmLoadedImage.ImageDataType = EfiRuntimeServicesData; + + mSmmCoreDriverEntry->ImageEntryPoint = gSmmCorePrivate->PiSmmCoreEntryPoint; + mSmmCoreDriverEntry->ImageBuffer = gSmmCorePrivate->PiSmmCoreImageBase; + mSmmCoreDriverEntry->NumberOfPage = EFI_SIZE_TO_PAGES((UINTN)gSmmCorePrivate->PiSmmCoreImageSize); + + // + // Create a new image handle in the SMM handle database for the SMM Driver + // + mSmmCoreDriverEntry->SmmImageHandle = NULL; + Status = SmmInstallProtocolInterface ( + &mSmmCoreDriverEntry->SmmImageHandle, + &gEfiLoadedImageProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmCoreDriverEntry->SmmLoadedImage + ); + ASSERT_EFI_ERROR(Status); + + return ; +} + +/** + The Entry Point for SMM Core + + Install DXE Protocols and reload SMM Core into SMRAM and register SMM Core + EntryPoint on the SMI vector. + + Note: This function is called for both DXE invocation and SMRAM invocation. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +SmmMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + + // + // Get SMM Core Private context passed in from SMM IPL in ImageHandle. + // + gSmmCorePrivate = (SMM_CORE_PRIVATE_DATA *)ImageHandle; + + // + // Fill in SMRAM physical address for the SMM Services Table and the SMM Entry Point. + // + gSmmCorePrivate->Smst = &gSmmCoreSmst; + gSmmCorePrivate->SmmEntryPoint = SmmEntryPoint; + + // + // No need to initialize memory service. + // It is done in constructor of PiSmmCoreMemoryAllocationLib(), + // so that the library linked with PiSmmCore can use AllocatePool() in constructor. + // + + SmramProfileInit (); + + // + // Copy FullSmramRanges to SMRAM + // + mFullSmramRangeCount = gSmmCorePrivate->SmramRangeCount; + mFullSmramRanges = AllocatePool (mFullSmramRangeCount * sizeof (EFI_SMRAM_DESCRIPTOR)); + ASSERT (mFullSmramRanges != NULL); + CopyMem (mFullSmramRanges, gSmmCorePrivate->SmramRanges, mFullSmramRangeCount * sizeof (EFI_SMRAM_DESCRIPTOR)); + + // + // Register all SMI Handlers required by the SMM Core + // + for (Index = 0; mSmmCoreSmiHandlers[Index].HandlerType != NULL; Index++) { + Status = SmiHandlerRegister ( + mSmmCoreSmiHandlers[Index].Handler, + mSmmCoreSmiHandlers[Index].HandlerType, + &mSmmCoreSmiHandlers[Index].DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + } + + mAcpiS3Enable = PcdGetBool (PcdAcpiS3Enable); + if (mAcpiS3Enable) { + // + // Register all S3 related SMI Handlers required by the SMM Core + // + for (Index = 0; mSmmCoreS3SmiHandlers[Index].HandlerType != NULL; Index++) { + Status = SmiHandlerRegister ( + mSmmCoreS3SmiHandlers[Index].Handler, + mSmmCoreS3SmiHandlers[Index].HandlerType, + &mSmmCoreS3SmiHandlers[Index].DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + } + } + + RegisterSmramProfileHandler (); + SmramProfileInstallProtocol (); + + SmmCoreInstallLoadedImage (); + + SmmCoreInitializeMemoryAttributesTable (); + + SmmCoreInitializeSmiHandlerProfile (); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h new file mode 100644 index 00000000..e67b0202 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h @@ -0,0 +1,1353 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by SmmCore module. + + Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_CORE_H_ +#define _SMM_CORE_H_ + +#include <PiSmm.h> + +#include <Protocol/DxeSmmReadyToLock.h> +#include <Protocol/SmmReadyToLock.h> +#include <Protocol/SmmEndOfDxe.h> +#include <Protocol/CpuIo2.h> +#include <Protocol/SmmCommunication.h> +#include <Protocol/SmmAccess2.h> +#include <Protocol/FirmwareVolume2.h> +#include <Protocol/LoadedImage.h> +#include <Protocol/DevicePath.h> +#include <Protocol/Security.h> +#include <Protocol/Security2.h> +#include <Protocol/SmmExitBootServices.h> +#include <Protocol/SmmLegacyBoot.h> +#include <Protocol/SmmReadyToBoot.h> +#include <Protocol/SmmMemoryAttribute.h> +#include <Protocol/SmmSxDispatch2.h> + +#include <Guid/Apriori.h> +#include <Guid/EventGroup.h> +#include <Guid/EventLegacyBios.h> +#include <Guid/MemoryProfile.h> +#include <Guid/LoadModuleAtFixedAddress.h> +#include <Guid/SmiHandlerProfile.h> +#include <Guid/EndOfS3Resume.h> +#include <Guid/S3SmmInitDone.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/PeCoffLib.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Library/CacheMaintenanceLib.h> +#include <Library/DebugLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DevicePathLib.h> +#include <Library/UefiLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/PcdLib.h> +#include <Library/SmmCorePlatformHookLib.h> +#include <Library/PerformanceLib.h> +#include <Library/HobLib.h> +#include <Library/SmmMemLib.h> + +#include "PiSmmCorePrivateData.h" +#include "HeapGuard.h" + +// +// Used to build a table of SMI Handlers that the SMM Core registers +// +typedef struct { + EFI_SMM_HANDLER_ENTRY_POINT2 Handler; + EFI_GUID *HandlerType; + EFI_HANDLE DispatchHandle; + BOOLEAN UnRegister; +} SMM_CORE_SMI_HANDLERS; + +// +// SMM_HANDLER - used for each SMM handler +// + +#define SMI_ENTRY_SIGNATURE SIGNATURE_32('s','m','i','e') + + typedef struct { + UINTN Signature; + LIST_ENTRY AllEntries; // All entries + + EFI_GUID HandlerType; // Type of interrupt + LIST_ENTRY SmiHandlers; // All handlers +} SMI_ENTRY; + +#define SMI_HANDLER_SIGNATURE SIGNATURE_32('s','m','i','h') + + typedef struct { + UINTN Signature; + LIST_ENTRY Link; // Link on SMI_ENTRY.SmiHandlers + EFI_SMM_HANDLER_ENTRY_POINT2 Handler; // The smm handler's entry point + UINTN CallerAddr; // The address of caller who register the SMI handler. + SMI_ENTRY *SmiEntry; + VOID *Context; // for profile + UINTN ContextSize; // for profile +} SMI_HANDLER; + +// +// Structure for recording the state of an SMM Driver +// +#define EFI_SMM_DRIVER_ENTRY_SIGNATURE SIGNATURE_32('s', 'd','r','v') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; // mDriverList + + LIST_ENTRY ScheduledLink; // mScheduledQueue + + EFI_HANDLE FvHandle; + EFI_GUID FileName; + EFI_DEVICE_PATH_PROTOCOL *FvFileDevicePath; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + + VOID *Depex; + UINTN DepexSize; + + BOOLEAN Before; + BOOLEAN After; + EFI_GUID BeforeAfterGuid; + + BOOLEAN Dependent; + BOOLEAN Scheduled; + BOOLEAN Initialized; + BOOLEAN DepexProtocolError; + + EFI_HANDLE ImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + // + // Image EntryPoint in SMRAM + // + PHYSICAL_ADDRESS ImageEntryPoint; + // + // Image Buffer in SMRAM + // + PHYSICAL_ADDRESS ImageBuffer; + // + // Image Page Number + // + UINTN NumberOfPage; + EFI_HANDLE SmmImageHandle; + EFI_LOADED_IMAGE_PROTOCOL SmmLoadedImage; +} EFI_SMM_DRIVER_ENTRY; + +#define EFI_HANDLE_SIGNATURE SIGNATURE_32('s','h','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('s','p','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; + /// Registered notification handlers + LIST_ENTRY Notify; +} PROTOCOL_ENTRY; + +#define PROTOCOL_INTERFACE_SIGNATURE SIGNATURE_32('s','p','i','f') + +/// +/// 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('s','p','t','n') + +/// +/// PROTOCOL_NOTIFY - used for each register notification for a protocol +/// +typedef struct { + UINTN Signature; + PROTOCOL_ENTRY *Protocol; + /// All notifications for this protocol + LIST_ENTRY Link; + /// Notification function + EFI_SMM_NOTIFY_FN Function; + /// Last position notified + LIST_ENTRY *Position; +} PROTOCOL_NOTIFY; + +// +// SMM Core Global Variables +// +extern SMM_CORE_PRIVATE_DATA *gSmmCorePrivate; +extern EFI_SMM_SYSTEM_TABLE2 gSmmCoreSmst; +extern LIST_ENTRY gHandleList; +extern EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressSmramBase; + +/** + Called to initialize the memory service. + + @param SmramRangeCount Number of SMRAM Regions + @param SmramRanges Pointer to SMRAM Descriptors + +**/ +VOID +SmmInitializeMemoryServices ( + IN UINTN SmramRangeCount, + IN EFI_SMRAM_DESCRIPTOR *SmramRanges + ); + +/** + The SmmInstallConfigurationTable() function is used to maintain the list + of configuration tables that are stored in the System Management System + Table. The list is stored as an array of (GUID, Pointer) pairs. The list + must be allocated from pool memory with PoolType set to EfiRuntimeServicesData. + + @param SystemTable A pointer to the SMM System Table (SMST). + @param Guid A pointer to the GUID for the entry to add, update, or remove. + @param Table A pointer to the buffer of the table to add. + @param TableSize The size of the table to install. + + @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. + @retval EFI_INVALID_PARAMETER Guid is not valid. + @retval EFI_NOT_FOUND An attempt was made to delete a non-existent entry. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. + +**/ +EFI_STATUS +EFIAPI +SmmInstallConfigurationTable ( + IN CONST EFI_SMM_SYSTEM_TABLE2 *SystemTable, + IN CONST EFI_GUID *Guid, + IN VOID *Table, + IN UINTN TableSize + ); + +/** + Wrapper function to SmmInstallProtocolInterfaceNotify. This is the public API which + Calls the private one which contains a BOOLEAN parameter for notifications + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + + @return Status code + +**/ +EFI_STATUS +EFIAPI +SmmInstallProtocolInterface ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface + ); + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform + @param MemoryType The type of memory to turn the allocated pages + into + @param NumberOfPages The number of pages to allocate + @param Memory A pointer to receive the base allocated memory + address + + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ); + +/** + 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 + @param NeedGuard Flag to indicate Guard page is needed or not + + @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 +SmmInternalAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN NeedGuard + ); + +/** + 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 +SmmFreePages ( + 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 + @param IsGuarded Flag to indicate if the memory is guarded + or not + + @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 +SmmInternalFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN IsGuarded + ); + +/** + Allocate pool of a particular type. + + @param PoolType Type of pool to allocate + @param Size The amount of pool to allocate + @param Buffer The address to return a pointer to the allocated + pool + + @retval EFI_INVALID_PARAMETER PoolType not valid + @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. + @retval EFI_SUCCESS Pool successfully allocated. + +**/ +EFI_STATUS +EFIAPI +SmmAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ); + +/** + 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 +SmmInternalAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ); + +/** + Frees pool. + + @param Buffer The allocated pool entry to free + + @retval EFI_INVALID_PARAMETER Buffer is not a valid value. + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +EFIAPI +SmmFreePool ( + IN VOID *Buffer + ); + +/** + 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 +SmmInternalFreePool ( + IN VOID *Buffer + ); + +/** + Installs a protocol interface into the boot services environment. + + @param UserHandle The handle to install the protocol handler on, + or NULL if a new handle is to be allocated + @param Protocol The protocol to add to the handle + @param InterfaceType Indicates whether Interface is supplied in + native form. + @param Interface The interface for the protocol being added + @param Notify indicates whether notify the notification list + for this protocol + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate + @retval EFI_SUCCESS Protocol interface successfully installed + +**/ +EFI_STATUS +SmmInstallProtocolInterfaceNotify ( + IN OUT EFI_HANDLE *UserHandle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface, + IN BOOLEAN Notify + ); + +/** + Uninstalls all instances of a protocol:interfacer from a handle. + If the last protocol interface is remove from the handle, the + handle is freed. + + @param UserHandle The handle to remove the protocol handler from + @param Protocol The protocol, of protocol:interface, to remove + @param Interface The interface, of protocol:interface, to remove + + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_SUCCESS Protocol interface successfully uninstalled. + +**/ +EFI_STATUS +EFIAPI +SmmUninstallProtocolInterface ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + Queries a handle to determine if it supports a specified protocol. + + @param UserHandle The handle being queried. + @param Protocol The published unique identifier of the protocol. + @param Interface Supplies the address where a pointer to the + corresponding Protocol Interface is returned. + + @return The requested protocol interface for the handle + +**/ +EFI_STATUS +EFIAPI +SmmHandleProtocol ( + IN EFI_HANDLE UserHandle, + IN EFI_GUID *Protocol, + OUT VOID **Interface + ); + +/** + Add a new protocol notification record for the request protocol. + + @param Protocol The requested protocol to add the notify + registration + @param Function Points to the notification function + @param Registration Returns the registration record + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully returned the registration record + that has been added + +**/ +EFI_STATUS +EFIAPI +SmmRegisterProtocolNotify ( + IN CONST EFI_GUID *Protocol, + IN EFI_SMM_NOTIFY_FN Function, + OUT VOID **Registration + ); + +/** + Locates the requested handle(s) and returns them in Buffer. + + @param SearchType The type of search to perform to locate the + handles + @param Protocol The protocol to search for + @param SearchKey Dependant on SearchType + @param BufferSize On input the size of Buffer. On output the + size of data returned. + @param Buffer The buffer to return the results in + + @retval EFI_BUFFER_TOO_SMALL Buffer too small, required buffer size is + returned in BufferSize. + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_SUCCESS Successfully found the requested handle(s) and + returns them in Buffer. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandle ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *BufferSize, + OUT EFI_HANDLE *Buffer + ); + +/** + Return the first Protocol Interface that matches the Protocol GUID. If + Registration is pasased in return a Protocol Instance that was just add + to the system. If Registration is NULL return the first Protocol Interface + you find. + + @param Protocol The protocol to search for + @param Registration Optional Registration Key returned from + RegisterProtocolNotify() + @param Interface Return the Protocol interface (instance). + + @retval EFI_SUCCESS If a valid Interface is returned + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_NOT_FOUND Protocol interface not found + +**/ +EFI_STATUS +EFIAPI +SmmLocateProtocol ( + IN EFI_GUID *Protocol, + IN VOID *Registration OPTIONAL, + OUT VOID **Interface + ); + +/** + Function returns an array of handles that support the requested protocol + in a buffer allocated from pool. This is a version of SmmLocateHandle() + that allocates a buffer for the caller. + + @param SearchType Specifies which handle(s) are to be returned. + @param Protocol Provides the protocol to search by. This + parameter is only valid for SearchType + ByProtocol. + @param SearchKey Supplies the search key depending on the + SearchType. + @param NumberHandles The number of handles returned in Buffer. + @param Buffer A pointer to the buffer to return the requested + array of handles that support Protocol. + + @retval EFI_SUCCESS The result array of handles was returned. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the + matching results. + @retval EFI_INVALID_PARAMETER One or more parameters are not valid. + +**/ +EFI_STATUS +EFIAPI +SmmLocateHandleBuffer ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ); + +/** + Manage SMI of a particular type. + + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param Context Points to an optional context buffer. + @param CommBuffer Points to the optional communication buffer. + @param CommBufferSize Points to the size of the optional communication buffer. + + @retval EFI_SUCCESS Interrupt source was processed successfully but not quiesced. + @retval EFI_INTERRUPT_PENDING One or more SMI sources could not be quiesced. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was not handled or quiesced. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED Interrupt source was handled and quiesced. + +**/ +EFI_STATUS +EFIAPI +SmiManage ( + IN CONST EFI_GUID *HandlerType, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + Registers a handler to execute within SMM. + + @param Handler Handler service function pointer. + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function. + + @retval EFI_SUCCESS Handler register success. + @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerRegister ( + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN CONST EFI_GUID *HandlerType OPTIONAL, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a handler in SMM. + + @param DispatchHandle The handle that was specified when the handler was registered. + + @retval EFI_SUCCESS Handler function was successfully unregistered. + @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerUnRegister ( + IN EFI_HANDLE DispatchHandle + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmDriverDispatchHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmLegacyBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmReadyToLockHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmEndOfDxeHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmExitBootServicesHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + This function is the main entry point for an SMM handler dispatch + or communicate-based callback. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmReadyToBootHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + Software SMI handler that is called when the S3SmmInitDone signal is triggered. + This function installs the SMM S3SmmInitDone Protocol so SMM Drivers are informed that + S3 SMM initialization has been done. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmS3SmmInitDoneHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + Software SMI handler that is called when the EndOfS3Resume event is trigged. + This function installs the SMM EndOfS3Resume Protocol so SMM Drivers are informed that + S3 resume has finished. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @return Status Code + +**/ +EFI_STATUS +EFIAPI +SmmEndOfS3ResumeHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, OPTIONAL + IN OUT VOID *CommBuffer, OPTIONAL + IN OUT UINTN *CommBufferSize OPTIONAL + ); + +/** + Place holder function until all the SMM System Table Service are available. + + @param Arg1 Undefined + @param Arg2 Undefined + @param Arg3 Undefined + @param Arg4 Undefined + @param Arg5 Undefined + + @return EFI_NOT_AVAILABLE_YET + +**/ +EFI_STATUS +EFIAPI +SmmEfiNotAvailableYetArg5 ( + UINTN Arg1, + UINTN Arg2, + UINTN Arg3, + UINTN Arg4, + UINTN Arg5 + ); + +// +//Functions used during debug builds +// + +/** + Traverse the discovered list for any drivers that were discovered but not loaded + because the dependency expressions evaluated to false. + +**/ +VOID +SmmDisplayDiscoveredNotDispatched ( + VOID + ); + +/** + Add free SMRAM region for use by memory service. + + @param MemBase Base address of memory region. + @param MemLength Length of the memory region. + @param Type Memory type. + @param Attributes Memory region state. + +**/ +VOID +SmmAddMemoryRegion ( + IN EFI_PHYSICAL_ADDRESS MemBase, + IN UINT64 MemLength, + IN EFI_MEMORY_TYPE Type, + IN UINT64 Attributes + ); + +/** + Finds the protocol entry for the requested protocol. + + @param Protocol The ID of the protocol + @param Create Create a new entry if not found + + @return Protocol entry + +**/ +PROTOCOL_ENTRY * +SmmFindProtocolEntry ( + IN EFI_GUID *Protocol, + IN BOOLEAN Create + ); + +/** + Signal event for every protocol in protocol entry. + + @param Prot Protocol interface + +**/ +VOID +SmmNotifyProtocol ( + IN PROTOCOL_INTERFACE *Prot + ); + +/** + Finds the protocol instance for the requested handle and protocol. + Note: This function doesn't do parameters checking, it's caller's responsibility + to pass in valid parameters. + + @param Handle The handle to search the protocol on + @param Protocol GUID of the protocol + @param Interface The interface for the protocol being searched + + @return Protocol instance (NULL: Not found) + +**/ +PROTOCOL_INTERFACE * +SmmFindProtocolInterface ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + Removes Protocol from the protocol list (but not the handle list). + + @param Handle The handle to remove protocol on. + @param Protocol GUID of the protocol to be moved + @param Interface The interface of the protocol + + @return Protocol Entry + +**/ +PROTOCOL_INTERFACE * +SmmRemoveInterfaceFromProtocol ( + IN IHANDLE *Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + This is the POSTFIX version of the dependency evaluator. This code does + not need to handle Before or After, as it is not valid to call this + routine in this case. POSTFIX means all the math is done on top of the stack. + + @param DriverEntry DriverEntry element to update. + + @retval TRUE If driver is ready to run. + @retval FALSE If driver is not ready to run or some fatal error + was found. + +**/ +BOOLEAN +SmmIsSchedulable ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry + ); + +// +// SmramProfile +// + +/** + Initialize SMRAM profile. + +**/ +VOID +SmramProfileInit ( + VOID + ); + +/** + Install SMRAM profile protocol. + +**/ +VOID +SmramProfileInstallProtocol ( + VOID + ); + +/** + Register SMM image to SMRAM profile. + + @param DriverEntry SMM image info. + @param RegisterToDxe Register image to DXE. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource for this register. + +**/ +EFI_STATUS +RegisterSmramProfileImage ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry, + IN BOOLEAN RegisterToDxe + ); + +/** + Unregister image from SMRAM profile. + + @param DriverEntry SMM image info. + @param UnregisterToDxe Unregister image from DXE. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +UnregisterSmramProfileImage ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry, + IN BOOLEAN UnregisterToDxe + ); + +/** + Update SMRAM profile information. + + @param CallerAddress Address of caller who call Allocate or Free. + @param Action This Allocate or Free action. + @param MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param Size Buffer size. + @param Buffer Buffer address. + @param ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +SmmCoreUpdateProfile ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, // Valid for AllocatePages/AllocatePool + IN UINTN Size, // Valid for AllocatePages/FreePages/AllocatePool + IN VOID *Buffer, + IN CHAR8 *ActionString OPTIONAL + ); + +/** + Register SMRAM profile handler. + +**/ +VOID +RegisterSmramProfileHandler ( + VOID + ); + +/** + SMRAM profile ready to lock callback function. + +**/ +VOID +SmramProfileReadyToLock ( + VOID + ); + +/** + Initialize MemoryAttributes support. +**/ +VOID +EFIAPI +SmmCoreInitializeMemoryAttributesTable ( + VOID + ); + +/** + This function returns a copy of the current memory map. The map is an array of + memory descriptors, each of which describes a contiguous block of memory. + + @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + the buffer allocated by the caller. On output, + it is the size of the buffer returned by the + firmware if the buffer was large enough, or the + size of the buffer needed to contain the map if + the buffer was too small. + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[out] MapKey A pointer to the location in which firmware + returns the key for the current memory map. + @param[out] DescriptorSize A pointer to the location in which firmware + returns the size, in bytes, of an individual + EFI_MEMORY_DESCRIPTOR. + @param[out] DescriptorVersion A pointer to the location in which firmware + returns the version number associated with the + EFI_MEMORY_DESCRIPTOR. + + @retval EFI_SUCCESS The memory map was returned in the MemoryMap + buffer. + @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current + buffer size needed to hold the memory map is + returned in MemoryMapSize. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + +**/ +EFI_STATUS +EFIAPI +SmmCoreGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ); + +/** + Initialize SmiHandler profile feature. +**/ +VOID +SmmCoreInitializeSmiHandlerProfile ( + VOID + ); + +/** + This function is called by SmmChildDispatcher module to report + a new SMI handler is registered, to SmmCore. + + @param This The protocol instance + @param HandlerGuid The GUID to identify the type of the handler. + For the SmmChildDispatch protocol, the HandlerGuid + must be the GUID of SmmChildDispatch protocol. + @param Handler The SMI handler. + @param CallerAddress The address of the module who registers the SMI handler. + @param Context The context of the SMI handler. + For the SmmChildDispatch protocol, the Context + must match the one defined for SmmChildDispatch protocol. + @param ContextSize The size of the context in bytes. + For the SmmChildDispatch protocol, the Context + must match the one defined for SmmChildDispatch protocol. + + @retval EFI_SUCCESS The information is recorded. + @retval EFI_OUT_OF_RESOURCES There is no enough resource to record the information. +**/ +EFI_STATUS +EFIAPI +SmiHandlerProfileRegisterHandler ( + IN SMI_HANDLER_PROFILE_PROTOCOL *This, + IN EFI_GUID *HandlerGuid, + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN PHYSICAL_ADDRESS CallerAddress, + IN VOID *Context, OPTIONAL + IN UINTN ContextSize OPTIONAL + ); + +/** + This function is called by SmmChildDispatcher module to report + an existing SMI handler is unregistered, to SmmCore. + + @param This The protocol instance + @param HandlerGuid The GUID to identify the type of the handler. + For the SmmChildDispatch protocol, the HandlerGuid + must be the GUID of SmmChildDispatch protocol. + @param Handler The SMI handler. + @param Context The context of the SMI handler. + If it is NOT NULL, it will be used to check what is registered. + @param ContextSize The size of the context in bytes. + If Context is NOT NULL, it will be used to check what is registered. + + @retval EFI_SUCCESS The original record is removed. + @retval EFI_NOT_FOUND There is no record for the HandlerGuid and handler. +**/ +EFI_STATUS +EFIAPI +SmiHandlerProfileUnregisterHandler ( + IN SMI_HANDLER_PROFILE_PROTOCOL *This, + IN EFI_GUID *HandlerGuid, + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN VOID *Context, OPTIONAL + IN UINTN ContextSize OPTIONAL + ); + +extern UINTN mFullSmramRangeCount; +extern EFI_SMRAM_DESCRIPTOR *mFullSmramRanges; + +extern EFI_SMM_DRIVER_ENTRY *mSmmCoreDriverEntry; + +extern EFI_LOADED_IMAGE_PROTOCOL *mSmmCoreLoadedImage; + +// +// Page management +// + +typedef struct { + LIST_ENTRY Link; + UINTN NumberOfPages; +} FREE_PAGE_LIST; + +extern LIST_ENTRY mSmmMemoryMap; + +// +// 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) + +#define POOL_HEAD_SIGNATURE SIGNATURE_32('s','p','h','d') + +typedef struct { + UINT32 Signature; + BOOLEAN Available; + EFI_MEMORY_TYPE Type; + UINTN Size; +} POOL_HEADER; + +#define POOL_TAIL_SIGNATURE SIGNATURE_32('s','p','t','l') + +typedef struct { + UINT32 Signature; + UINT32 Reserved; + UINTN Size; +} POOL_TAIL; + +#define POOL_OVERHEAD (sizeof(POOL_HEADER) + sizeof(POOL_TAIL)) + +#define HEAD_TO_TAIL(a) \ + ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL))); + +typedef struct { + POOL_HEADER Header; + LIST_ENTRY Link; +} FREE_POOL_HEADER; + +typedef enum { + SmmPoolTypeCode, + SmmPoolTypeData, + SmmPoolTypeMax, +} SMM_POOL_TYPE; + +extern LIST_ENTRY mSmmPoolLists[SmmPoolTypeMax][MAX_POOL_INDEX]; + +/** + 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 + ); + +/** + Update SMM memory map entry. + + @param[in] Type The type of allocation to perform. + @param[in] Memory The base of memory address. + @param[in] NumberOfPages The number of pages to allocate. + @param[in] AddRegion If this memory is new added region. +**/ +VOID +ConvertSmmMemoryMapEntry ( + IN EFI_MEMORY_TYPE Type, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN AddRegion + ); + +/** + Internal function. Moves any memory descriptors that are on the + temporary descriptor stack to heap. + +**/ +VOID +CoreFreeMemoryMapStack ( + VOID + ); + +/** + Frees previous allocated pages. + + @param[in] Memory Base address of memory being freed. + @param[in] NumberOfPages The number of pages to free. + @param[in] AddRegion If this memory is new added region. + + @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 +SmmInternalFreePagesEx ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + IN BOOLEAN AddRegion + ); + +/** + Hook function used to set all Guard pages after entering SMM mode. +**/ +VOID +SmmEntryPointMemoryManagementHook ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf new file mode 100644 index 00000000..00ea2ce5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf @@ -0,0 +1,123 @@ +## @file +# This module provide an SMM CIS compliant implementation of SMM Core. +# +# Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmCore + MODULE_UNI_FILE = PiSmmCore.uni + FILE_GUID = E94F54CD-81EB-47ed-AEC3-856F5DC157A9 + MODULE_TYPE = SMM_CORE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmMain + +# VALID_ARCHITECTURES = IA32 X64 + +[Sources] + PiSmmCore.c + PiSmmCore.h + PiSmmCorePrivateData.h + Page.c + Pool.c + Handle.c + Locate.c + Notify.c + Dependency.c + Dispatcher.c + Smi.c + InstallConfigurationTable.c + SmramProfileRecord.c + MemoryAttributesTable.c + SmiHandlerProfile.c + HeapGuard.c + HeapGuard.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + PeCoffLib + PeCoffGetEntryPointLib + CacheMaintenanceLib + DebugLib + ReportStatusCodeLib + DevicePathLib + UefiLib + UefiBootServicesTableLib + MemoryAllocationLib + PcdLib + SmmCorePlatformHookLib + PerformanceLib + HobLib + SmmMemLib + +[Protocols] + gEfiDxeSmmReadyToLockProtocolGuid ## UNDEFINED # SmiHandlerRegister + gEfiSmmReadyToLockProtocolGuid ## PRODUCES + gEfiSmmCpuIo2ProtocolGuid ## CONSUMES + gEfiFirmwareVolume2ProtocolGuid ## CONSUMES + gEfiSmmEndOfDxeProtocolGuid ## PRODUCES + gEfiSecurityArchProtocolGuid ## SOMETIMES_CONSUMES + gEfiSecurity2ArchProtocolGuid ## SOMETIMES_CONSUMES + gEfiLoadedImageProtocolGuid ## PRODUCES + gEfiDevicePathProtocolGuid ## CONSUMES + gEdkiiSmmExitBootServicesProtocolGuid ## SOMETIMES_PRODUCES + gEdkiiSmmLegacyBootProtocolGuid ## SOMETIMES_PRODUCES + gEdkiiSmmReadyToBootProtocolGuid ## PRODUCES + + gEfiSmmSwDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmSxDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmPowerButtonDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmStandbyButtonDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmPeriodicTimerDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmGpiDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmIoTrapDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmUsbDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiSmmMemoryAttributeProtocolGuid ## CONSUMES + gEfiSmmSxDispatch2ProtocolGuid ## SOMETIMES_CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdLoadFixAddressSmmCodePageNumber ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdLoadModuleAtFixAddressEnable ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMemoryProfileMemoryType ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMemoryProfilePropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMemoryProfileDriverPath ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSmiHandlerProfilePropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPageType ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES + +[Guids] + gAprioriGuid ## SOMETIMES_CONSUMES ## File + gEfiEventDxeDispatchGuid ## PRODUCES ## GUID # SmiHandlerRegister + gEfiEventLegacyBootGuid ## PRODUCES ## GUID # SmiHandlerRegister + gEfiEventExitBootServicesGuid ## PRODUCES ## GUID # SmiHandlerRegister + gEfiEventReadyToBootGuid ## PRODUCES ## GUID # SmiHandlerRegister + gEfiEndOfDxeEventGroupGuid ## PRODUCES ## GUID # SmiHandlerRegister + ## SOMETIMES_CONSUMES ## GUID # Locate protocol + ## SOMETIMES_PRODUCES ## GUID # SmiHandlerRegister + gEdkiiMemoryProfileGuid + ## SOMETIMES_PRODUCES ## GUID # Install protocol + gEdkiiSmmMemoryProfileGuid + gEdkiiPiSmmMemoryAttributesTableGuid ## PRODUCES ## SystemTable + ## SOMETIMES_CONSUMES ## SystemTable + gLoadFixedAddressConfigurationTableGuid + ## SOMETIMES_PRODUCES ## GUID # Install protocol + ## SOMETIMES_PRODUCES ## GUID # SmiHandlerRegister + gSmiHandlerProfileGuid + gEdkiiEndOfS3ResumeGuid ## SOMETIMES_PRODUCES ## GUID # Install protocol + gEdkiiS3SmmInitDoneGuid ## SOMETIMES_PRODUCES ## GUID # Install protocol + +[UserExtensions.TianoCore."ExtraFiles"] + PiSmmCoreExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.uni new file mode 100644 index 00000000..9f11394c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.uni @@ -0,0 +1,16 @@ +// /** @file
+// This module provide an SMM CIS compliant implementation of SMM Core.
+//
+// This module provide an SMM CIS compliant implementation of SMM Core.
+//
+// Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides an SMM CIS compliant implementation of SMM Core"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provide an SMM CIS compliant implementation of SMM Core."
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCoreExtra.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCoreExtra.uni new file mode 100644 index 00000000..44bf418e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCoreExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// PiSmmCore Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Core SMM Services Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h new file mode 100644 index 00000000..5bf47603 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h @@ -0,0 +1,119 @@ +/** @file + The internal header file that declared a data structure that is shared + between the SMM IPL and the SMM Core. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PI_SMM_CORE_PRIVATE_DATA_H_ +#define _PI_SMM_CORE_PRIVATE_DATA_H_ + +/// +/// Define values for the communications buffer used when gEfiEventDxeDispatchGuid is +/// event signaled. This event is signaled by the DXE Core each time the DXE Core +/// dispatcher has completed its work. When this event is signaled, the SMM Core +/// if notified, so the SMM Core can dispatch SMM drivers. If COMM_BUFFER_SMM_DISPATCH_ERROR +/// is returned in the communication buffer, then an error occurred dispatching SMM +/// Drivers. If COMM_BUFFER_SMM_DISPATCH_SUCCESS is returned, then the SMM Core +/// dispatched all the drivers it could. If COMM_BUFFER_SMM_DISPATCH_RESTART is +/// returned, then the SMM Core just dispatched the SMM Driver that registered +/// the SMM Entry Point enabling the use of SMM Mode. In this case, the SMM Core +/// should be notified again to dispatch more SMM Drivers using SMM Mode. +/// +#define COMM_BUFFER_SMM_DISPATCH_ERROR 0x00 +#define COMM_BUFFER_SMM_DISPATCH_SUCCESS 0x01 +#define COMM_BUFFER_SMM_DISPATCH_RESTART 0x02 + +/// +/// Signature for the private structure shared between the SMM IPL and the SMM Core +/// +#define SMM_CORE_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('s', 'm', 'm', 'c') + +/// +/// Private structure that is used to share information between the SMM IPL and +/// the SMM Core. This structure is allocated from memory of type EfiRuntimeServicesData. +/// Since runtime memory types are converted to available memory when a legacy boot +/// is performed, the SMM Core must not access any fields of this structure if a legacy +/// boot is performed. As a result, the SMM IPL must create an event notification +/// for the Legacy Boot event and notify the SMM Core that a legacy boot is being +/// performed. The SMM Core can then use this information to filter accesses to +/// thos structure. +/// +typedef struct { + UINTN Signature; + + /// + /// The ImageHandle passed into the entry point of the SMM IPL. This ImageHandle + /// is used by the SMM Core to fill in the ParentImageHandle field of the Loaded + /// Image Protocol for each SMM Driver that is dispatched by the SMM Core. + /// + EFI_HANDLE SmmIplImageHandle; + + /// + /// The number of SMRAM ranges passed from the SMM IPL to the SMM Core. The SMM + /// Core uses these ranges of SMRAM to initialize the SMM Core memory manager. + /// + UINTN SmramRangeCount; + + /// + /// A table of SMRAM ranges passed from the SMM IPL to the SMM Core. The SMM + /// Core uses these ranges of SMRAM to initialize the SMM Core memory manager. + /// + EFI_SMRAM_DESCRIPTOR *SmramRanges; + + /// + /// The SMM Foundation Entry Point. The SMM Core fills in this field when the + /// SMM Core is initialized. The SMM IPL is responsible for registering this entry + /// point with the SMM Configuration Protocol. The SMM Configuration Protocol may + /// not be available at the time the SMM IPL and SMM Core are started, so the SMM IPL + /// sets up a protocol notification on the SMM Configuration Protocol and registers + /// the SMM Foundation Entry Point as soon as the SMM Configuration Protocol is + /// available. + /// + EFI_SMM_ENTRY_POINT SmmEntryPoint; + + /// + /// Boolean flag set to TRUE while an SMI is being processed by the SMM Core. + /// + BOOLEAN SmmEntryPointRegistered; + + /// + /// Boolean flag set to TRUE while an SMI is being processed by the SMM Core. + /// + BOOLEAN InSmm; + + /// + /// This field is set by the SMM Core then the SMM Core is initialized. This field is + /// used by the SMM Base 2 Protocol and SMM Communication Protocol implementations in + /// the SMM IPL. + /// + EFI_SMM_SYSTEM_TABLE2 *Smst; + + /// + /// This field is used by the SMM Communication Protocol to pass a buffer into + /// a software SMI handler and for the software SMI handler to pass a buffer back to + /// the caller of the SMM Communication Protocol. + /// + VOID *CommunicationBuffer; + + /// + /// This field is used by the SMM Communication Protocol to pass the size of a buffer, + /// in bytes, into a software SMI handler and for the software SMI handler to pass the + /// size, in bytes, of a buffer back to the caller of the SMM Communication Protocol. + /// + UINTN BufferSize; + + /// + /// This field is used by the SMM Communication Protocol to pass the return status from + /// a software SMI handler back to the caller of the SMM Communication Protocol. + /// + EFI_STATUS ReturnStatus; + + EFI_PHYSICAL_ADDRESS PiSmmCoreImageBase; + UINT64 PiSmmCoreImageSize; + EFI_PHYSICAL_ADDRESS PiSmmCoreEntryPoint; +} SMM_CORE_PRIVATE_DATA; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c new file mode 100644 index 00000000..86896fe5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c @@ -0,0 +1,1865 @@ +/** @file + SMM IPL that produces SMM related runtime protocols and load the SMM Core into SMRAM + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <Protocol/SmmBase2.h> +#include <Protocol/SmmCommunication.h> +#include <Protocol/MmCommunication2.h> +#include <Protocol/SmmAccess2.h> +#include <Protocol/SmmConfiguration.h> +#include <Protocol/SmmControl2.h> +#include <Protocol/DxeSmmReadyToLock.h> +#include <Protocol/Cpu.h> + +#include <Guid/EventGroup.h> +#include <Guid/EventLegacyBios.h> +#include <Guid/LoadModuleAtFixedAddress.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/PeCoffLib.h> +#include <Library/CacheMaintenanceLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/DxeServicesLib.h> +#include <Library/UefiLib.h> +#include <Library/UefiRuntimeLib.h> +#include <Library/PcdLib.h> +#include <Library/ReportStatusCodeLib.h> + +#include "PiSmmCorePrivateData.h" + +#define SMRAM_CAPABILITIES (EFI_MEMORY_WB | EFI_MEMORY_UC) + +// +// Function prototypes from produced protocols +// + +/** + Indicate whether the driver is currently executing in the SMM Initialization phase. + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param InSmram Pointer to a Boolean which, on return, indicates that the driver is currently executing + inside of SMRAM (TRUE) or outside of SMRAM (FALSE). + + @retval EFI_INVALID_PARAMETER InSmram was NULL. + @retval EFI_SUCCESS The call returned successfully. + +**/ +EFI_STATUS +EFIAPI +SmmBase2InSmram ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT BOOLEAN *InSmram + ); + +/** + Retrieves the location of the System Management System Table (SMST). + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param Smst On return, points to a pointer to the System Management Service Table (SMST). + + @retval EFI_INVALID_PARAMETER Smst or This was invalid. + @retval EFI_SUCCESS The memory was returned to the system. + @retval EFI_UNSUPPORTED Not in SMM. + +**/ +EFI_STATUS +EFIAPI +SmmBase2GetSmstLocation ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT EFI_SMM_SYSTEM_TABLE2 **Smst + ); + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered + UEFI service. This function is part of the SMM Communication Protocol that may + be called in physical mode prior to SetVirtualAddressMap() and in virtual mode + after SetVirtualAddressMap(). + + @param[in] This The EFI_SMM_COMMUNICATION_PROTOCOL instance. + @param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM. + @param[in, out] CommSize The size of the data buffer being passed in. On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. + @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation. + If this error is returned, the MessageLength field + in the CommBuffer header or the integer pointed by + CommSize, are updated to reflect the maximum payload + size the implementation can accommodate. + @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter, + if not omitted, are in address range that cannot be + accessed by the MM environment. + +**/ +EFI_STATUS +EFIAPI +SmmCommunicationCommunicate ( + IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommSize OPTIONAL + ); + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered UEFI service. + + @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL instance. + @param[in] CommBufferPhysical Physical address of the MM communication buffer + @param[in] CommBufferVirtual Virtual address of the MM communication buffer + @param[in] CommSize The size of the data buffer being passed in. On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. + @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation. + If this error is returned, the MessageLength field + in the CommBuffer header or the integer pointed by + CommSize, are updated to reflect the maximum payload + size the implementation can accommodate. + @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter, + if not omitted, are in address range that cannot be + accessed by the MM environment. + +**/ +EFI_STATUS +EFIAPI +SmmCommunicationMmCommunicate2 ( + IN CONST EFI_MM_COMMUNICATION2_PROTOCOL *This, + IN OUT VOID *CommBufferPhysical, + IN OUT VOID *CommBufferVirtual, + IN OUT UINTN *CommSize OPTIONAL + ); + +/** + Event notification that is fired every time a gEfiSmmConfigurationProtocol installs. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplSmmConfigurationEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Event notification that is fired every time a DxeSmmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signalled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplReadyToLockEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Event notification that is fired when DxeDispatch Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplDxeDispatchEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Event notification that is fired when a GUIDed Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplGuidedEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Event notification that is fired when EndOfDxe Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplEndOfDxeEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmIplSetVirtualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// Data structure used to declare a table of protocol notifications and event +// notifications required by the SMM IPL +// +typedef struct { + BOOLEAN Protocol; + BOOLEAN CloseOnLock; + EFI_GUID *Guid; + EFI_EVENT_NOTIFY NotifyFunction; + VOID *NotifyContext; + EFI_TPL NotifyTpl; + EFI_EVENT Event; +} SMM_IPL_EVENT_NOTIFICATION; + +// +// Handle to install the SMM Base2 Protocol and the SMM Communication Protocol +// +EFI_HANDLE mSmmIplHandle = NULL; + +// +// SMM Base 2 Protocol instance +// +EFI_SMM_BASE2_PROTOCOL mSmmBase2 = { + SmmBase2InSmram, + SmmBase2GetSmstLocation +}; + +// +// SMM Communication Protocol instance +// +EFI_SMM_COMMUNICATION_PROTOCOL mSmmCommunication = { + SmmCommunicationCommunicate +}; + +// +// PI 1.7 MM Communication Protocol 2 instance +// +EFI_MM_COMMUNICATION2_PROTOCOL mMmCommunication2 = { + SmmCommunicationMmCommunicate2 +}; + +// +// SMM Core Private Data structure that contains the data shared between +// the SMM IPL and the SMM Core. +// +SMM_CORE_PRIVATE_DATA mSmmCorePrivateData = { + SMM_CORE_PRIVATE_DATA_SIGNATURE, // Signature + NULL, // SmmIplImageHandle + 0, // SmramRangeCount + NULL, // SmramRanges + NULL, // SmmEntryPoint + FALSE, // SmmEntryPointRegistered + FALSE, // InSmm + NULL, // Smst + NULL, // CommunicationBuffer + 0, // BufferSize + EFI_SUCCESS // ReturnStatus +}; + +// +// Global pointer used to access mSmmCorePrivateData from outside and inside SMM +// +SMM_CORE_PRIVATE_DATA *gSmmCorePrivate = &mSmmCorePrivateData; + +// +// SMM IPL global variables +// +EFI_SMM_CONTROL2_PROTOCOL *mSmmControl2; +EFI_SMM_ACCESS2_PROTOCOL *mSmmAccess; +EFI_SMRAM_DESCRIPTOR *mCurrentSmramRange; +BOOLEAN mSmmLocked = FALSE; +BOOLEAN mEndOfDxe = FALSE; +EFI_PHYSICAL_ADDRESS mSmramCacheBase; +UINT64 mSmramCacheSize; + +EFI_SMM_COMMUNICATE_HEADER mCommunicateHeader; +EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *mLMFAConfigurationTable = NULL; + +// +// Table of Protocol notification and GUIDed Event notifications that the SMM IPL requires +// +SMM_IPL_EVENT_NOTIFICATION mSmmIplEvents[] = { + // + // Declare protocol notification on the SMM Configuration protocol. When this notification is established, + // the associated event is immediately signalled, so the notification function will be executed and the + // SMM Configuration Protocol will be found if it is already in the handle database. + // + { TRUE, FALSE, &gEfiSmmConfigurationProtocolGuid, SmmIplSmmConfigurationEventNotify, &gEfiSmmConfigurationProtocolGuid, TPL_NOTIFY, NULL }, + // + // Declare protocol notification on DxeSmmReadyToLock protocols. When this notification is established, + // the associated event is immediately signalled, so the notification function will be executed and the + // DXE SMM Ready To Lock Protocol will be found if it is already in the handle database. + // + { TRUE, TRUE, &gEfiDxeSmmReadyToLockProtocolGuid, SmmIplReadyToLockEventNotify, &gEfiDxeSmmReadyToLockProtocolGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on EndOfDxe event. When this notification is established, + // the associated event is immediately signalled, so the notification function will be executed and the + // SMM End Of Dxe Protocol will be found if it is already in the handle database. + // + { FALSE, TRUE, &gEfiEndOfDxeEventGroupGuid, SmmIplGuidedEventNotify, &gEfiEndOfDxeEventGroupGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on EndOfDxe event. This is used to set EndOfDxe event signaled flag. + // + { FALSE, TRUE, &gEfiEndOfDxeEventGroupGuid, SmmIplEndOfDxeEventNotify, &gEfiEndOfDxeEventGroupGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on the DXE Dispatch Event Group. This event is signaled by the DXE Core + // each time the DXE Core dispatcher has completed its work. When this event is signalled, the SMM Core + // if notified, so the SMM Core can dispatch SMM drivers. + // + { FALSE, TRUE, &gEfiEventDxeDispatchGuid, SmmIplDxeDispatchEventNotify, &gEfiEventDxeDispatchGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on Ready To Boot Event Group. This is an extra event notification that is + // used to make sure SMRAM is locked before any boot options are processed. + // + { FALSE, TRUE, &gEfiEventReadyToBootGuid, SmmIplReadyToLockEventNotify, &gEfiEventReadyToBootGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on Legacy Boot Event Group. This is used to inform the SMM Core that the platform + // is performing a legacy boot operation, and that the UEFI environment is no longer available and the SMM Core + // must guarantee that it does not access any UEFI related structures outside of SMRAM. + // It is also to inform the SMM Core to notify SMM driver that system enter legacy boot. + // + { FALSE, FALSE, &gEfiEventLegacyBootGuid, SmmIplGuidedEventNotify, &gEfiEventLegacyBootGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on Exit Boot Services Event Group. This is used to inform the SMM Core + // to notify SMM driver that system enter exit boot services. + // + { FALSE, FALSE, &gEfiEventExitBootServicesGuid, SmmIplGuidedEventNotify, &gEfiEventExitBootServicesGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on Ready To Boot Event Group. This is used to inform the SMM Core + // to notify SMM driver that system enter ready to boot. + // + { FALSE, FALSE, &gEfiEventReadyToBootGuid, SmmIplGuidedEventNotify, &gEfiEventReadyToBootGuid, TPL_CALLBACK, NULL }, + // + // Declare event notification on SetVirtualAddressMap() Event Group. This is used to convert gSmmCorePrivate + // and mSmmControl2 from physical addresses to virtual addresses. + // + { FALSE, FALSE, &gEfiEventVirtualAddressChangeGuid, SmmIplSetVirtualAddressNotify, NULL, TPL_CALLBACK, NULL }, + // + // Terminate the table of event notifications + // + { FALSE, FALSE, NULL, NULL, NULL, TPL_CALLBACK, NULL } +}; + +/** + Find the maximum SMRAM cache range that covers the range specified by SmramRange. + + This function searches and joins all adjacent ranges of SmramRange into a range to be cached. + + @param SmramRange The SMRAM range to search from. + @param SmramCacheBase The returned cache range base. + @param SmramCacheSize The returned cache range size. + +**/ +VOID +GetSmramCacheRange ( + IN EFI_SMRAM_DESCRIPTOR *SmramRange, + OUT EFI_PHYSICAL_ADDRESS *SmramCacheBase, + OUT UINT64 *SmramCacheSize + ) +{ + UINTN Index; + EFI_PHYSICAL_ADDRESS RangeCpuStart; + UINT64 RangePhysicalSize; + BOOLEAN FoundAjacentRange; + + *SmramCacheBase = SmramRange->CpuStart; + *SmramCacheSize = SmramRange->PhysicalSize; + + do { + FoundAjacentRange = FALSE; + for (Index = 0; Index < gSmmCorePrivate->SmramRangeCount; Index++) { + RangeCpuStart = gSmmCorePrivate->SmramRanges[Index].CpuStart; + RangePhysicalSize = gSmmCorePrivate->SmramRanges[Index].PhysicalSize; + if (RangeCpuStart < *SmramCacheBase && *SmramCacheBase == (RangeCpuStart + RangePhysicalSize)) { + *SmramCacheBase = RangeCpuStart; + *SmramCacheSize += RangePhysicalSize; + FoundAjacentRange = TRUE; + } else if ((*SmramCacheBase + *SmramCacheSize) == RangeCpuStart && RangePhysicalSize > 0) { + *SmramCacheSize += RangePhysicalSize; + FoundAjacentRange = TRUE; + } + } + } while (FoundAjacentRange); + +} + +/** + Indicate whether the driver is currently executing in the SMM Initialization phase. + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param InSmram Pointer to a Boolean which, on return, indicates that the driver is currently executing + inside of SMRAM (TRUE) or outside of SMRAM (FALSE). + + @retval EFI_INVALID_PARAMETER InSmram was NULL. + @retval EFI_SUCCESS The call returned successfully. + +**/ +EFI_STATUS +EFIAPI +SmmBase2InSmram ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT BOOLEAN *InSmram + ) +{ + if (InSmram == NULL) { + return EFI_INVALID_PARAMETER; + } + + *InSmram = gSmmCorePrivate->InSmm; + + return EFI_SUCCESS; +} + +/** + Retrieves the location of the System Management System Table (SMST). + + @param This The EFI_SMM_BASE2_PROTOCOL instance. + @param Smst On return, points to a pointer to the System Management Service Table (SMST). + + @retval EFI_INVALID_PARAMETER Smst or This was invalid. + @retval EFI_SUCCESS The memory was returned to the system. + @retval EFI_UNSUPPORTED Not in SMM. + +**/ +EFI_STATUS +EFIAPI +SmmBase2GetSmstLocation ( + IN CONST EFI_SMM_BASE2_PROTOCOL *This, + OUT EFI_SMM_SYSTEM_TABLE2 **Smst + ) +{ + if ((This == NULL) ||(Smst == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!gSmmCorePrivate->InSmm) { + return EFI_UNSUPPORTED; + } + + *Smst = gSmmCorePrivate->Smst; + + return EFI_SUCCESS; +} + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered + UEFI service. This function is part of the SMM Communication Protocol that may + be called in physical mode prior to SetVirtualAddressMap() and in virtual mode + after SetVirtualAddressMap(). + + @param[in] This The EFI_SMM_COMMUNICATION_PROTOCOL instance. + @param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM. + @param[in, out] CommSize The size of the data buffer being passed in. On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. + @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation. + If this error is returned, the MessageLength field + in the CommBuffer header or the integer pointed by + CommSize, are updated to reflect the maximum payload + size the implementation can accommodate. + @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter, + if not omitted, are in address range that cannot be + accessed by the MM environment. + +**/ +EFI_STATUS +EFIAPI +SmmCommunicationCommunicate ( + IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *CommunicateHeader; + BOOLEAN OldInSmm; + UINTN TempCommSize; + + // + // Check parameters + // + if (CommBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + CommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer; + + if (CommSize == NULL) { + TempCommSize = OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + CommunicateHeader->MessageLength; + } else { + TempCommSize = *CommSize; + // + // CommSize must hold HeaderGuid and MessageLength + // + if (TempCommSize < OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // If not already in SMM, then generate a Software SMI + // + if (!gSmmCorePrivate->InSmm && gSmmCorePrivate->SmmEntryPointRegistered) { + // + // Put arguments for Software SMI in gSmmCorePrivate + // + gSmmCorePrivate->CommunicationBuffer = CommBuffer; + gSmmCorePrivate->BufferSize = TempCommSize; + + // + // Generate Software SMI + // + Status = mSmmControl2->Trigger (mSmmControl2, NULL, NULL, FALSE, 0); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Return status from software SMI + // + if (CommSize != NULL) { + *CommSize = gSmmCorePrivate->BufferSize; + } + return gSmmCorePrivate->ReturnStatus; + } + + // + // If we are in SMM, then the execution mode must be physical, which means that + // OS established virtual addresses can not be used. If SetVirtualAddressMap() + // has been called, then a direct invocation of the Software SMI is not allowed, + // so return EFI_INVALID_PARAMETER. + // + if (EfiGoneVirtual()) { + return EFI_INVALID_PARAMETER; + } + + // + // If we are not in SMM, don't allow call SmiManage() directly when SMRAM is closed or locked. + // + if ((!gSmmCorePrivate->InSmm) && (!mSmmAccess->OpenState || mSmmAccess->LockState)) { + return EFI_INVALID_PARAMETER; + } + + // + // Save current InSmm state and set InSmm state to TRUE + // + OldInSmm = gSmmCorePrivate->InSmm; + gSmmCorePrivate->InSmm = TRUE; + + // + // Before SetVirtualAddressMap(), we are in SMM or SMRAM is open and unlocked, call SmiManage() directly. + // + TempCommSize -= OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + Status = gSmmCorePrivate->Smst->SmiManage ( + &CommunicateHeader->HeaderGuid, + NULL, + CommunicateHeader->Data, + &TempCommSize + ); + TempCommSize += OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data); + if (CommSize != NULL) { + *CommSize = TempCommSize; + } + + // + // Restore original InSmm state + // + gSmmCorePrivate->InSmm = OldInSmm; + + return (Status == EFI_SUCCESS) ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered UEFI service. + + @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL instance. + @param[in] CommBufferPhysical Physical address of the MM communication buffer + @param[in] CommBufferVirtual Virtual address of the MM communication buffer + @param[in] CommSize The size of the data buffer being passed in. On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. + @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation. + If this error is returned, the MessageLength field + in the CommBuffer header or the integer pointed by + CommSize, are updated to reflect the maximum payload + size the implementation can accommodate. + @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter, + if not omitted, are in address range that cannot be + accessed by the MM environment. + +**/ +EFI_STATUS +EFIAPI +SmmCommunicationMmCommunicate2 ( + IN CONST EFI_MM_COMMUNICATION2_PROTOCOL *This, + IN OUT VOID *CommBufferPhysical, + IN OUT VOID *CommBufferVirtual, + IN OUT UINTN *CommSize OPTIONAL + ) +{ + return SmmCommunicationCommunicate (&mSmmCommunication, + CommBufferPhysical, + CommSize); +} + +/** + Event notification that is fired when GUIDed Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplGuidedEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Size; + + // + // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure + // + CopyGuid (&mCommunicateHeader.HeaderGuid, (EFI_GUID *)Context); + mCommunicateHeader.MessageLength = 1; + mCommunicateHeader.Data[0] = 0; + + // + // Generate the Software SMI and return the result + // + Size = sizeof (mCommunicateHeader); + SmmCommunicationCommunicate (&mSmmCommunication, &mCommunicateHeader, &Size); +} + +/** + Event notification that is fired when EndOfDxe Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplEndOfDxeEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + mEndOfDxe = TRUE; +} + +/** + Event notification that is fired when DxeDispatch Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplDxeDispatchEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Size; + EFI_STATUS Status; + + // + // Keep calling the SMM Core Dispatcher until there is no request to restart it. + // + while (TRUE) { + // + // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure + // Clear the buffer passed into the Software SMI. This buffer will return + // the status of the SMM Core Dispatcher. + // + CopyGuid (&mCommunicateHeader.HeaderGuid, (EFI_GUID *)Context); + mCommunicateHeader.MessageLength = 1; + mCommunicateHeader.Data[0] = 0; + + // + // Generate the Software SMI and return the result + // + Size = sizeof (mCommunicateHeader); + SmmCommunicationCommunicate (&mSmmCommunication, &mCommunicateHeader, &Size); + + // + // Return if there is no request to restart the SMM Core Dispatcher + // + if (mCommunicateHeader.Data[0] != COMM_BUFFER_SMM_DISPATCH_RESTART) { + return; + } + + // + // Close all SMRAM ranges to protect SMRAM + // NOTE: SMRR is enabled by CPU SMM driver by calling SmmCpuFeaturesInitializeProcessor() from SmmCpuFeaturesLib + // so no need to reset the SMRAM to UC in MTRR. + // + Status = mSmmAccess->Close (mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Print debug message that the SMRAM window is now closed. + // + DEBUG ((DEBUG_INFO, "SMM IPL closed SMRAM window\n")); + } +} + +/** + Event notification that is fired every time a gEfiSmmConfigurationProtocol installs. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplSmmConfigurationEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration; + + // + // Make sure this notification is for this handler + // + Status = gBS->LocateProtocol (Context, NULL, (VOID **)&SmmConfiguration); + if (EFI_ERROR (Status)) { + return; + } + + // + // Register the SMM Entry Point provided by the SMM Core with the SMM Configuration protocol + // + Status = SmmConfiguration->RegisterSmmEntry (SmmConfiguration, gSmmCorePrivate->SmmEntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Set flag to indicate that the SMM Entry Point has been registered which + // means that SMIs are now fully operational. + // + gSmmCorePrivate->SmmEntryPointRegistered = TRUE; + + // + // Print debug message showing SMM Core entry point address. + // + DEBUG ((DEBUG_INFO, "SMM IPL registered SMM Entry Point address %p\n", (VOID *)(UINTN)gSmmCorePrivate->SmmEntryPoint)); +} + +/** + Event notification that is fired every time a DxeSmmReadyToLock protocol is added + or if gEfiEventReadyToBootGuid is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +VOID +EFIAPI +SmmIplReadyToLockEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *Interface; + UINTN Index; + + // + // See if we are already locked + // + if (mSmmLocked) { + return; + } + + // + // Make sure this notification is for this handler + // + if (CompareGuid ((EFI_GUID *)Context, &gEfiDxeSmmReadyToLockProtocolGuid)) { + Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + return; + } + } else { + // + // If SMM is not locked yet and we got here from gEfiEventReadyToBootGuid being + // signaled, then gEfiDxeSmmReadyToLockProtocolGuid was not installed as expected. + // Print a warning on debug builds. + // + DEBUG ((DEBUG_WARN, "SMM IPL! DXE SMM Ready To Lock Protocol not installed before Ready To Boot signal\n")); + } + + if (!mEndOfDxe) { + DEBUG ((DEBUG_ERROR, "EndOfDxe Event must be signaled before DxeSmmReadyToLock Protocol installation!\n")); + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED, + (EFI_SOFTWARE_SMM_DRIVER | EFI_SW_EC_ILLEGAL_SOFTWARE_STATE) + ); + ASSERT (FALSE); + } + + // + // Lock the SMRAM (Note: Locking SMRAM may not be supported on all platforms) + // + mSmmAccess->Lock (mSmmAccess); + + // + // Close protocol and event notification events that do not apply after the + // DXE SMM Ready To Lock Protocol has been installed or the Ready To Boot + // event has been signalled. + // + for (Index = 0; mSmmIplEvents[Index].NotifyFunction != NULL; Index++) { + if (mSmmIplEvents[Index].CloseOnLock) { + gBS->CloseEvent (mSmmIplEvents[Index].Event); + } + } + + // + // Inform SMM Core that the DxeSmmReadyToLock protocol was installed + // + SmmIplGuidedEventNotify (Event, (VOID *)&gEfiDxeSmmReadyToLockProtocolGuid); + + // + // Print debug message that the SMRAM window is now locked. + // + DEBUG ((DEBUG_INFO, "SMM IPL locked SMRAM window\n")); + + // + // Set flag so this operation will not be performed again + // + mSmmLocked = TRUE; +} + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmIplSetVirtualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EfiConvertPointer (0x0, (VOID **)&mSmmControl2); +} + +/** + 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 loading 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; + EFI_PHYSICAL_ADDRESS SmramBase; + UINT64 SmmCodeSize; + UINT64 ValueInSectionHeader; + // + // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber + // + SmmCodeSize = EFI_PAGES_TO_SIZE (PcdGet32(PcdLoadFixAddressSmmCodePageNumber)); + + FixLoadingAddress = 0; + Status = EFI_NOT_FOUND; + SmramBase = mLMFAConfigurationTable->SmramBase; + // + // 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 saves the offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields in the + // first section header that doesn't point to code section in image header. And there is an assumption that when the + // feature is enabled, if a module is assigned a loading address by tools, 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)(SmramBase + (INT64)ValueInSectionHeader); + + if (SmramBase + SmmCodeSize > FixLoadingAddress && SmramBase <= FixLoadingAddress) { + // + // The assigned address is valid. Return the specified loading address + // + ImageContext->ImageAddress = FixLoadingAddress; + Status = EFI_SUCCESS; + } + } + break; + } + SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); + } + DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r \n", FixLoadingAddress, Status)); + return Status; +} +/** + Load the SMM Core image into SMRAM and executes the SMM Core from SMRAM. + + @param[in, out] SmramRange Descriptor for the range of SMRAM to reload the + currently executing image, the rang of SMRAM to + hold SMM Core will be excluded. + @param[in, out] SmramRangeSmmCore Descriptor for the range of SMRAM to hold SMM Core. + + @param[in] Context Context to pass into SMM Core + + @return EFI_STATUS + +**/ +EFI_STATUS +ExecuteSmmCoreFromSmram ( + IN OUT EFI_SMRAM_DESCRIPTOR *SmramRange, + IN OUT EFI_SMRAM_DESCRIPTOR *SmramRangeSmmCore, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *SourceBuffer; + UINTN SourceSize; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + UINTN PageCount; + EFI_IMAGE_ENTRY_POINT EntryPoint; + + // + // Search all Firmware Volumes for a PE/COFF image in a file of type SMM_CORE + // + Status = GetSectionFromAnyFvByFileType ( + EFI_FV_FILETYPE_SMM_CORE, + 0, + EFI_SECTION_PE32, + 0, + &SourceBuffer, + &SourceSize + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize ImageContext + // + ImageContext.Handle = SourceBuffer; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + // + // if Loading module at Fixed Address feature is enabled, the SMM core driver will be loaded to + // the address assigned by build tool. + // + if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { + // + // Get the fixed loading address assigned by Build tool + // + Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext); + if (!EFI_ERROR (Status)) { + // + // Since the memory range to load SMM CORE will be cut out in SMM core, so no need to allocate and free this range + // + PageCount = 0; + // + // Reserved Smram Region for SmmCore is not used, and remove it from SmramRangeCount. + // + gSmmCorePrivate->SmramRangeCount --; + } else { + DEBUG ((EFI_D_INFO, "LOADING MODULE FIXED ERROR: Loading module at fixed address at address failed\n")); + // + // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR + // specified by SmramRange + // + PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); + + ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0); + ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount)); + + SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount); + SmramRangeSmmCore->CpuStart = SmramRange->CpuStart + SmramRange->PhysicalSize; + SmramRangeSmmCore->PhysicalStart = SmramRange->PhysicalStart + SmramRange->PhysicalSize; + SmramRangeSmmCore->RegionState = SmramRange->RegionState | EFI_ALLOCATED; + SmramRangeSmmCore->PhysicalSize = EFI_PAGES_TO_SIZE (PageCount); + + // + // Align buffer on section boundary + // + ImageContext.ImageAddress = SmramRangeSmmCore->CpuStart; + } + } else { + // + // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR + // specified by SmramRange + // + PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); + + ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0); + ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount)); + + SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount); + SmramRangeSmmCore->CpuStart = SmramRange->CpuStart + SmramRange->PhysicalSize; + SmramRangeSmmCore->PhysicalStart = SmramRange->PhysicalStart + SmramRange->PhysicalSize; + SmramRangeSmmCore->RegionState = SmramRange->RegionState | EFI_ALLOCATED; + SmramRangeSmmCore->PhysicalSize = EFI_PAGES_TO_SIZE (PageCount); + + // + // Align buffer on section boundary + // + ImageContext.ImageAddress = SmramRangeSmmCore->CpuStart; + } + + ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; + ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1); + + // + // Print debug message showing SMM Core load address. + // + DEBUG ((DEBUG_INFO, "SMM IPL loading SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.ImageAddress)); + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (!EFI_ERROR (Status)) { + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (!EFI_ERROR (Status)) { + // + // Flush the instruction cache so the image data are written before we execute it + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + + // + // Print debug message showing SMM Core entry point address. + // + DEBUG ((DEBUG_INFO, "SMM IPL calling SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.EntryPoint)); + + gSmmCorePrivate->PiSmmCoreImageBase = ImageContext.ImageAddress; + gSmmCorePrivate->PiSmmCoreImageSize = ImageContext.ImageSize; + DEBUG ((DEBUG_INFO, "PiSmmCoreImageBase - 0x%016lx\n", gSmmCorePrivate->PiSmmCoreImageBase)); + DEBUG ((DEBUG_INFO, "PiSmmCoreImageSize - 0x%016lx\n", gSmmCorePrivate->PiSmmCoreImageSize)); + + gSmmCorePrivate->PiSmmCoreEntryPoint = ImageContext.EntryPoint; + + // + // Execute image + // + EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)ImageContext.EntryPoint; + Status = EntryPoint ((EFI_HANDLE)Context, gST); + } + } + + // + // Always free memory allocated by GetFileBufferByFilePath () + // + FreePool (SourceBuffer); + + return Status; +} + +/** + SMM split SMRAM entry. + + @param[in, out] RangeToCompare Pointer to EFI_SMRAM_DESCRIPTOR to compare. + @param[in, out] ReservedRangeToCompare Pointer to EFI_SMM_RESERVED_SMRAM_REGION to compare. + @param[out] Ranges Output pointer to hold split EFI_SMRAM_DESCRIPTOR entry. + @param[in, out] RangeCount Pointer to range count. + @param[out] ReservedRanges Output pointer to hold split EFI_SMM_RESERVED_SMRAM_REGION entry. + @param[in, out] ReservedRangeCount Pointer to reserved range count. + @param[out] FinalRanges Output pointer to hold split final EFI_SMRAM_DESCRIPTOR entry + that no need to be split anymore. + @param[in, out] FinalRangeCount Pointer to final range count. + +**/ +VOID +SmmSplitSmramEntry ( + IN OUT EFI_SMRAM_DESCRIPTOR *RangeToCompare, + IN OUT EFI_SMM_RESERVED_SMRAM_REGION *ReservedRangeToCompare, + OUT EFI_SMRAM_DESCRIPTOR *Ranges, + IN OUT UINTN *RangeCount, + OUT EFI_SMM_RESERVED_SMRAM_REGION *ReservedRanges, + IN OUT UINTN *ReservedRangeCount, + OUT EFI_SMRAM_DESCRIPTOR *FinalRanges, + IN OUT UINTN *FinalRangeCount + ) +{ + UINT64 RangeToCompareEnd; + UINT64 ReservedRangeToCompareEnd; + + RangeToCompareEnd = RangeToCompare->CpuStart + RangeToCompare->PhysicalSize; + ReservedRangeToCompareEnd = ReservedRangeToCompare->SmramReservedStart + ReservedRangeToCompare->SmramReservedSize; + + if ((RangeToCompare->CpuStart >= ReservedRangeToCompare->SmramReservedStart) && + (RangeToCompare->CpuStart < ReservedRangeToCompareEnd)) { + if (RangeToCompareEnd < ReservedRangeToCompareEnd) { + // + // RangeToCompare ReservedRangeToCompare + // ---- ---- -------------------------------------- + // | | | | -> 1. ReservedRangeToCompare + // ---- | | |--| -------------------------------------- + // | | | | | | + // | | | | | | -> 2. FinalRanges[*FinalRangeCount] and increment *FinalRangeCount + // | | | | | | RangeToCompare->PhysicalSize = 0 + // ---- | | |--| -------------------------------------- + // | | | | -> 3. ReservedRanges[*ReservedRangeCount] and increment *ReservedRangeCount + // ---- ---- -------------------------------------- + // + + // + // 1. Update ReservedRangeToCompare. + // + ReservedRangeToCompare->SmramReservedSize = RangeToCompare->CpuStart - ReservedRangeToCompare->SmramReservedStart; + // + // 2. Update FinalRanges[FinalRangeCount] and increment *FinalRangeCount. + // Zero RangeToCompare->PhysicalSize. + // + FinalRanges[*FinalRangeCount].CpuStart = RangeToCompare->CpuStart; + FinalRanges[*FinalRangeCount].PhysicalStart = RangeToCompare->PhysicalStart; + FinalRanges[*FinalRangeCount].RegionState = RangeToCompare->RegionState | EFI_ALLOCATED; + FinalRanges[*FinalRangeCount].PhysicalSize = RangeToCompare->PhysicalSize; + *FinalRangeCount += 1; + RangeToCompare->PhysicalSize = 0; + // + // 3. Update ReservedRanges[*ReservedRangeCount] and increment *ReservedRangeCount. + // + ReservedRanges[*ReservedRangeCount].SmramReservedStart = FinalRanges[*FinalRangeCount - 1].CpuStart + FinalRanges[*FinalRangeCount - 1].PhysicalSize; + ReservedRanges[*ReservedRangeCount].SmramReservedSize = ReservedRangeToCompareEnd - RangeToCompareEnd; + *ReservedRangeCount += 1; + } else { + // + // RangeToCompare ReservedRangeToCompare + // ---- ---- -------------------------------------- + // | | | | -> 1. ReservedRangeToCompare + // ---- | | |--| -------------------------------------- + // | | | | | | + // | | | | | | -> 2. FinalRanges[*FinalRangeCount] and increment *FinalRangeCount + // | | | | | | + // | | ---- |--| -------------------------------------- + // | | | | -> 3. RangeToCompare + // ---- ---- -------------------------------------- + // + + // + // 1. Update ReservedRangeToCompare. + // + ReservedRangeToCompare->SmramReservedSize = RangeToCompare->CpuStart - ReservedRangeToCompare->SmramReservedStart; + // + // 2. Update FinalRanges[FinalRangeCount] and increment *FinalRangeCount. + // + FinalRanges[*FinalRangeCount].CpuStart = RangeToCompare->CpuStart; + FinalRanges[*FinalRangeCount].PhysicalStart = RangeToCompare->PhysicalStart; + FinalRanges[*FinalRangeCount].RegionState = RangeToCompare->RegionState | EFI_ALLOCATED; + FinalRanges[*FinalRangeCount].PhysicalSize = ReservedRangeToCompareEnd - RangeToCompare->CpuStart; + *FinalRangeCount += 1; + // + // 3. Update RangeToCompare. + // + RangeToCompare->CpuStart += FinalRanges[*FinalRangeCount - 1].PhysicalSize; + RangeToCompare->PhysicalStart += FinalRanges[*FinalRangeCount - 1].PhysicalSize; + RangeToCompare->PhysicalSize -= FinalRanges[*FinalRangeCount - 1].PhysicalSize; + } + } else if ((ReservedRangeToCompare->SmramReservedStart >= RangeToCompare->CpuStart) && + (ReservedRangeToCompare->SmramReservedStart < RangeToCompareEnd)) { + if (ReservedRangeToCompareEnd < RangeToCompareEnd) { + // + // RangeToCompare ReservedRangeToCompare + // ---- ---- -------------------------------------- + // | | | | -> 1. RangeToCompare + // | | ---- |--| -------------------------------------- + // | | | | | | + // | | | | | | -> 2. FinalRanges[*FinalRangeCount] and increment *FinalRangeCount + // | | | | | | ReservedRangeToCompare->SmramReservedSize = 0 + // | | ---- |--| -------------------------------------- + // | | | | -> 3. Ranges[*RangeCount] and increment *RangeCount + // ---- ---- -------------------------------------- + // + + // + // 1. Update RangeToCompare. + // + RangeToCompare->PhysicalSize = ReservedRangeToCompare->SmramReservedStart - RangeToCompare->CpuStart; + // + // 2. Update FinalRanges[FinalRangeCount] and increment *FinalRangeCount. + // ReservedRangeToCompare->SmramReservedSize = 0 + // + FinalRanges[*FinalRangeCount].CpuStart = ReservedRangeToCompare->SmramReservedStart; + FinalRanges[*FinalRangeCount].PhysicalStart = RangeToCompare->PhysicalStart + RangeToCompare->PhysicalSize; + FinalRanges[*FinalRangeCount].RegionState = RangeToCompare->RegionState | EFI_ALLOCATED; + FinalRanges[*FinalRangeCount].PhysicalSize = ReservedRangeToCompare->SmramReservedSize; + *FinalRangeCount += 1; + ReservedRangeToCompare->SmramReservedSize = 0; + // + // 3. Update Ranges[*RangeCount] and increment *RangeCount. + // + Ranges[*RangeCount].CpuStart = FinalRanges[*FinalRangeCount - 1].CpuStart + FinalRanges[*FinalRangeCount - 1].PhysicalSize; + Ranges[*RangeCount].PhysicalStart = FinalRanges[*FinalRangeCount - 1].PhysicalStart + FinalRanges[*FinalRangeCount - 1].PhysicalSize; + Ranges[*RangeCount].RegionState = RangeToCompare->RegionState; + Ranges[*RangeCount].PhysicalSize = RangeToCompareEnd - ReservedRangeToCompareEnd; + *RangeCount += 1; + } else { + // + // RangeToCompare ReservedRangeToCompare + // ---- ---- -------------------------------------- + // | | | | -> 1. RangeToCompare + // | | ---- |--| -------------------------------------- + // | | | | | | + // | | | | | | -> 2. FinalRanges[*FinalRangeCount] and increment *FinalRangeCount + // | | | | | | + // ---- | | |--| -------------------------------------- + // | | | | -> 3. ReservedRangeToCompare + // ---- ---- -------------------------------------- + // + + // + // 1. Update RangeToCompare. + // + RangeToCompare->PhysicalSize = ReservedRangeToCompare->SmramReservedStart - RangeToCompare->CpuStart; + // + // 2. Update FinalRanges[FinalRangeCount] and increment *FinalRangeCount. + // ReservedRangeToCompare->SmramReservedSize = 0 + // + FinalRanges[*FinalRangeCount].CpuStart = ReservedRangeToCompare->SmramReservedStart; + FinalRanges[*FinalRangeCount].PhysicalStart = RangeToCompare->PhysicalStart + RangeToCompare->PhysicalSize; + FinalRanges[*FinalRangeCount].RegionState = RangeToCompare->RegionState | EFI_ALLOCATED; + FinalRanges[*FinalRangeCount].PhysicalSize = RangeToCompareEnd - ReservedRangeToCompare->SmramReservedStart; + *FinalRangeCount += 1; + // + // 3. Update ReservedRangeToCompare. + // + ReservedRangeToCompare->SmramReservedStart += FinalRanges[*FinalRangeCount - 1].PhysicalSize; + ReservedRangeToCompare->SmramReservedSize -= FinalRanges[*FinalRangeCount - 1].PhysicalSize; + } + } +} + +/** + Returns if SMRAM range and SMRAM reserved range are overlapped. + + @param[in] RangeToCompare Pointer to EFI_SMRAM_DESCRIPTOR to compare. + @param[in] ReservedRangeToCompare Pointer to EFI_SMM_RESERVED_SMRAM_REGION to compare. + + @retval TRUE There is overlap. + @retval FALSE There is no overlap. + +**/ +BOOLEAN +SmmIsSmramOverlap ( + IN EFI_SMRAM_DESCRIPTOR *RangeToCompare, + IN EFI_SMM_RESERVED_SMRAM_REGION *ReservedRangeToCompare + ) +{ + UINT64 RangeToCompareEnd; + UINT64 ReservedRangeToCompareEnd; + + RangeToCompareEnd = RangeToCompare->CpuStart + RangeToCompare->PhysicalSize; + ReservedRangeToCompareEnd = ReservedRangeToCompare->SmramReservedStart + ReservedRangeToCompare->SmramReservedSize; + + if ((RangeToCompare->CpuStart >= ReservedRangeToCompare->SmramReservedStart) && + (RangeToCompare->CpuStart < ReservedRangeToCompareEnd)) { + return TRUE; + } else if ((ReservedRangeToCompare->SmramReservedStart >= RangeToCompare->CpuStart) && + (ReservedRangeToCompare->SmramReservedStart < RangeToCompareEnd)) { + return TRUE; + } + return FALSE; +} + +/** + Get full SMRAM ranges. + + It will get SMRAM ranges from SmmAccess protocol and SMRAM reserved ranges from + SmmConfiguration protocol, split the entries if there is overlap between them. + It will also reserve one entry for SMM core. + + @param[out] FullSmramRangeCount Output pointer to full SMRAM range count. + + @return Pointer to full SMRAM ranges. + +**/ +EFI_SMRAM_DESCRIPTOR * +GetFullSmramRanges ( + OUT UINTN *FullSmramRangeCount + ) +{ + EFI_STATUS Status; + EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration; + UINTN Size; + UINTN Index; + UINTN Index2; + EFI_SMRAM_DESCRIPTOR *FullSmramRanges; + UINTN TempSmramRangeCount; + UINTN AdditionSmramRangeCount; + EFI_SMRAM_DESCRIPTOR *TempSmramRanges; + UINTN SmramRangeCount; + EFI_SMRAM_DESCRIPTOR *SmramRanges; + UINTN SmramReservedCount; + EFI_SMM_RESERVED_SMRAM_REGION *SmramReservedRanges; + UINTN MaxCount; + BOOLEAN Rescan; + + // + // Get SMM Configuration Protocol if it is present. + // + SmmConfiguration = NULL; + Status = gBS->LocateProtocol (&gEfiSmmConfigurationProtocolGuid, NULL, (VOID **) &SmmConfiguration); + + // + // Get SMRAM information. + // + Size = 0; + Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, NULL); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR); + + // + // Get SMRAM reserved region count. + // + SmramReservedCount = 0; + if (SmmConfiguration != NULL) { + while (SmmConfiguration->SmramReservedRegions[SmramReservedCount].SmramReservedSize != 0) { + SmramReservedCount++; + } + } + + // + // Reserve one entry for SMM Core in the full SMRAM ranges. + // + AdditionSmramRangeCount = 1; + if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { + // + // Reserve two entries for all SMM drivers and SMM Core in the full SMRAM ranges. + // + AdditionSmramRangeCount = 2; + } + + if (SmramReservedCount == 0) { + // + // No reserved SMRAM entry from SMM Configuration Protocol. + // + *FullSmramRangeCount = SmramRangeCount + AdditionSmramRangeCount; + Size = (*FullSmramRangeCount) * sizeof (EFI_SMRAM_DESCRIPTOR); + FullSmramRanges = (EFI_SMRAM_DESCRIPTOR *) AllocateZeroPool (Size); + ASSERT (FullSmramRanges != NULL); + + Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, FullSmramRanges); + ASSERT_EFI_ERROR (Status); + + return FullSmramRanges; + } + + // + // Why MaxCount = X + 2 * Y? + // Take Y = 1 as example below, Y > 1 case is just the iteration of Y = 1. + // + // X = 1 Y = 1 MaxCount = 3 = 1 + 2 * 1 + // ---- ---- + // | | ---- |--| + // | | | | -> | | + // | | ---- |--| + // ---- ---- + // + // X = 2 Y = 1 MaxCount = 4 = 2 + 2 * 1 + // ---- ---- + // | | | | + // | | ---- |--| + // | | | | | | + // |--| | | -> |--| + // | | | | | | + // | | ---- |--| + // | | | | + // ---- ---- + // + // X = 3 Y = 1 MaxCount = 5 = 3 + 2 * 1 + // ---- ---- + // | | | | + // | | ---- |--| + // |--| | | |--| + // | | | | -> | | + // |--| | | |--| + // | | ---- |--| + // | | | | + // ---- ---- + // + // ...... + // + MaxCount = SmramRangeCount + 2 * SmramReservedCount; + + Size = MaxCount * sizeof (EFI_SMM_RESERVED_SMRAM_REGION); + SmramReservedRanges = (EFI_SMM_RESERVED_SMRAM_REGION *) AllocatePool (Size); + ASSERT (SmramReservedRanges != NULL); + for (Index = 0; Index < SmramReservedCount; Index++) { + CopyMem (&SmramReservedRanges[Index], &SmmConfiguration->SmramReservedRegions[Index], sizeof (EFI_SMM_RESERVED_SMRAM_REGION)); + } + + Size = MaxCount * sizeof (EFI_SMRAM_DESCRIPTOR); + TempSmramRanges = (EFI_SMRAM_DESCRIPTOR *) AllocatePool (Size); + ASSERT (TempSmramRanges != NULL); + TempSmramRangeCount = 0; + + SmramRanges = (EFI_SMRAM_DESCRIPTOR *) AllocatePool (Size); + ASSERT (SmramRanges != NULL); + Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, SmramRanges); + ASSERT_EFI_ERROR (Status); + + do { + Rescan = FALSE; + for (Index = 0; (Index < SmramRangeCount) && !Rescan; Index++) { + // + // Skip zero size entry. + // + if (SmramRanges[Index].PhysicalSize != 0) { + for (Index2 = 0; (Index2 < SmramReservedCount) && !Rescan; Index2++) { + // + // Skip zero size entry. + // + if (SmramReservedRanges[Index2].SmramReservedSize != 0) { + if (SmmIsSmramOverlap ( + &SmramRanges[Index], + &SmramReservedRanges[Index2] + )) { + // + // There is overlap, need to split entry and then rescan. + // + SmmSplitSmramEntry ( + &SmramRanges[Index], + &SmramReservedRanges[Index2], + SmramRanges, + &SmramRangeCount, + SmramReservedRanges, + &SmramReservedCount, + TempSmramRanges, + &TempSmramRangeCount + ); + Rescan = TRUE; + } + } + } + if (!Rescan) { + // + // No any overlap, copy the entry to the temp SMRAM ranges. + // Zero SmramRanges[Index].PhysicalSize = 0; + // + CopyMem (&TempSmramRanges[TempSmramRangeCount++], &SmramRanges[Index], sizeof (EFI_SMRAM_DESCRIPTOR)); + SmramRanges[Index].PhysicalSize = 0; + } + } + } + } while (Rescan); + ASSERT (TempSmramRangeCount <= MaxCount); + + // + // Sort the entries + // + FullSmramRanges = AllocateZeroPool ((TempSmramRangeCount + AdditionSmramRangeCount) * sizeof (EFI_SMRAM_DESCRIPTOR)); + ASSERT (FullSmramRanges != NULL); + *FullSmramRangeCount = 0; + do { + for (Index = 0; Index < TempSmramRangeCount; Index++) { + if (TempSmramRanges[Index].PhysicalSize != 0) { + break; + } + } + ASSERT (Index < TempSmramRangeCount); + for (Index2 = 0; Index2 < TempSmramRangeCount; Index2++) { + if ((Index2 != Index) && (TempSmramRanges[Index2].PhysicalSize != 0) && (TempSmramRanges[Index2].CpuStart < TempSmramRanges[Index].CpuStart)) { + Index = Index2; + } + } + CopyMem (&FullSmramRanges[*FullSmramRangeCount], &TempSmramRanges[Index], sizeof (EFI_SMRAM_DESCRIPTOR)); + *FullSmramRangeCount += 1; + TempSmramRanges[Index].PhysicalSize = 0; + } while (*FullSmramRangeCount < TempSmramRangeCount); + ASSERT (*FullSmramRangeCount == TempSmramRangeCount); + *FullSmramRangeCount += AdditionSmramRangeCount; + + FreePool (SmramRanges); + FreePool (SmramReservedRanges); + FreePool (TempSmramRanges); + + return FullSmramRanges; +} + +/** + The Entry Point for SMM IPL + + Load SMM Core into SMRAM, register SMM Core entry point for SMIs, install + SMM Base 2 Protocol and SMM Communication Protocol, and register for the + critical events required to coordinate between DXE and SMM environments. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +SmmIplEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT64 MaxSize; + VOID *Registration; + UINT64 SmmCodeSize; + EFI_CPU_ARCH_PROTOCOL *CpuArch; + EFI_STATUS SetAttrStatus; + EFI_SMRAM_DESCRIPTOR *SmramRangeSmmDriver; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc; + + // + // Fill in the image handle of the SMM IPL so the SMM Core can use this as the + // ParentImageHandle field of the Load Image Protocol for all SMM Drivers loaded + // by the SMM Core + // + mSmmCorePrivateData.SmmIplImageHandle = ImageHandle; + + // + // Get SMM Access Protocol + // + Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Get SMM Control2 Protocol + // + Status = gBS->LocateProtocol (&gEfiSmmControl2ProtocolGuid, NULL, (VOID **)&mSmmControl2); + ASSERT_EFI_ERROR (Status); + + gSmmCorePrivate->SmramRanges = GetFullSmramRanges (&gSmmCorePrivate->SmramRangeCount); + + // + // Open all SMRAM ranges + // + Status = mSmmAccess->Open (mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Print debug message that the SMRAM window is now open. + // + DEBUG ((DEBUG_INFO, "SMM IPL opened SMRAM window\n")); + + // + // Find the largest SMRAM range between 1MB and 4GB that is at least 256KB - 4K in size + // + mCurrentSmramRange = NULL; + for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < gSmmCorePrivate->SmramRangeCount; Index++) { + // + // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization + // + if ((gSmmCorePrivate->SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { + continue; + } + + if (gSmmCorePrivate->SmramRanges[Index].CpuStart >= BASE_1MB) { + if ((gSmmCorePrivate->SmramRanges[Index].CpuStart + gSmmCorePrivate->SmramRanges[Index].PhysicalSize - 1) <= MAX_ADDRESS) { + if (gSmmCorePrivate->SmramRanges[Index].PhysicalSize >= MaxSize) { + MaxSize = gSmmCorePrivate->SmramRanges[Index].PhysicalSize; + mCurrentSmramRange = &gSmmCorePrivate->SmramRanges[Index]; + } + } + } + } + + if (mCurrentSmramRange != NULL) { + // + // Print debug message showing SMRAM window that will be used by SMM IPL and SMM Core + // + DEBUG ((DEBUG_INFO, "SMM IPL found SMRAM window %p - %p\n", + (VOID *)(UINTN)mCurrentSmramRange->CpuStart, + (VOID *)(UINTN)(mCurrentSmramRange->CpuStart + mCurrentSmramRange->PhysicalSize - 1) + )); + + GetSmramCacheRange (mCurrentSmramRange, &mSmramCacheBase, &mSmramCacheSize); + // + // Make sure we can change the desired memory attributes. + // + Status = gDS->GetMemorySpaceDescriptor ( + mSmramCacheBase, + &MemDesc + ); + ASSERT_EFI_ERROR (Status); + if ((MemDesc.Capabilities & SMRAM_CAPABILITIES) != SMRAM_CAPABILITIES) { + gDS->SetMemorySpaceCapabilities ( + mSmramCacheBase, + mSmramCacheSize, + MemDesc.Capabilities | SMRAM_CAPABILITIES + ); + } + // + // If CPU AP is present, attempt to set SMRAM cacheability to WB and clear + // all paging attributes. + // Note that it is expected that cacheability of SMRAM has been set to WB if CPU AP + // is not available here. + // + CpuArch = NULL; + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&CpuArch); + if (!EFI_ERROR (Status)) { + MemDesc.Attributes &= ~(EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK); + MemDesc.Attributes |= EFI_MEMORY_WB; + Status = gDS->SetMemorySpaceAttributes ( + mSmramCacheBase, + mSmramCacheSize, + MemDesc.Attributes + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "SMM IPL failed to set SMRAM window to EFI_MEMORY_WB\n")); + } + + DEBUG_CODE ( + gDS->GetMemorySpaceDescriptor ( + mSmramCacheBase, + &MemDesc + ); + DEBUG ((DEBUG_INFO, "SMRAM attributes: %016lx\n", MemDesc.Attributes)); + ASSERT ((MemDesc.Attributes & EFI_MEMORY_ATTRIBUTE_MASK) == 0); + ); + } + // + // if Loading module at Fixed Address feature is enabled, save the SMRAM base to Load + // Modules At Fixed Address Configuration Table. + // + if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { + // + // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber + // + SmmCodeSize = LShiftU64 (PcdGet32(PcdLoadFixAddressSmmCodePageNumber), EFI_PAGE_SHIFT); + // + // The SMRAM available memory is assumed to be larger than SmmCodeSize + // + ASSERT (mCurrentSmramRange->PhysicalSize > SmmCodeSize); + // + // Retrieve Load modules At fixed address configuration table and save the SMRAM base. + // + Status = EfiGetSystemConfigurationTable ( + &gLoadFixedAddressConfigurationTableGuid, + (VOID **) &mLMFAConfigurationTable + ); + if (!EFI_ERROR (Status) && mLMFAConfigurationTable != NULL) { + mLMFAConfigurationTable->SmramBase = mCurrentSmramRange->CpuStart; + // + // Print the SMRAM base + // + DEBUG ((EFI_D_INFO, "LOADING MODULE FIXED INFO: TSEG BASE is %x. \n", mLMFAConfigurationTable->SmramBase)); + } + + // + // Fill the Smram range for all SMM code + // + SmramRangeSmmDriver = &gSmmCorePrivate->SmramRanges[gSmmCorePrivate->SmramRangeCount - 2]; + SmramRangeSmmDriver->CpuStart = mCurrentSmramRange->CpuStart; + SmramRangeSmmDriver->PhysicalStart = mCurrentSmramRange->PhysicalStart; + SmramRangeSmmDriver->RegionState = mCurrentSmramRange->RegionState | EFI_ALLOCATED; + SmramRangeSmmDriver->PhysicalSize = SmmCodeSize; + + mCurrentSmramRange->PhysicalSize -= SmmCodeSize; + mCurrentSmramRange->CpuStart = mCurrentSmramRange->CpuStart + SmmCodeSize; + mCurrentSmramRange->PhysicalStart = mCurrentSmramRange->PhysicalStart + SmmCodeSize; + } + // + // Load SMM Core into SMRAM and execute it from SMRAM + // + Status = ExecuteSmmCoreFromSmram ( + mCurrentSmramRange, + &gSmmCorePrivate->SmramRanges[gSmmCorePrivate->SmramRangeCount - 1], + gSmmCorePrivate + ); + if (EFI_ERROR (Status)) { + // + // Print error message that the SMM Core failed to be loaded and executed. + // + DEBUG ((DEBUG_ERROR, "SMM IPL could not load and execute SMM Core from SMRAM\n")); + + // + // Attempt to reset SMRAM cacheability to UC + // + if (CpuArch != NULL) { + SetAttrStatus = gDS->SetMemorySpaceAttributes( + mSmramCacheBase, + mSmramCacheSize, + EFI_MEMORY_UC + ); + if (EFI_ERROR (SetAttrStatus)) { + DEBUG ((DEBUG_WARN, "SMM IPL failed to reset SMRAM window to EFI_MEMORY_UC\n")); + } + } + } + } else { + // + // Print error message that there are not enough SMRAM resources to load the SMM Core. + // + DEBUG ((DEBUG_ERROR, "SMM IPL could not find a large enough SMRAM region to load SMM Core\n")); + } + + // + // If the SMM Core could not be loaded then close SMRAM window, free allocated + // resources, and return an error so SMM IPL will be unloaded. + // + if (mCurrentSmramRange == NULL || EFI_ERROR (Status)) { + // + // Close all SMRAM ranges + // + Status = mSmmAccess->Close (mSmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Print debug message that the SMRAM window is now closed. + // + DEBUG ((DEBUG_INFO, "SMM IPL closed SMRAM window\n")); + + // + // Free all allocated resources + // + FreePool (gSmmCorePrivate->SmramRanges); + + return EFI_UNSUPPORTED; + } + + // + // Install SMM Base2 Protocol and SMM Communication Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mSmmIplHandle, + &gEfiSmmBase2ProtocolGuid, &mSmmBase2, + &gEfiSmmCommunicationProtocolGuid, &mSmmCommunication, + &gEfiMmCommunication2ProtocolGuid, &mMmCommunication2, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Create the set of protocol and event notifications that the SMM IPL requires + // + for (Index = 0; mSmmIplEvents[Index].NotifyFunction != NULL; Index++) { + if (mSmmIplEvents[Index].Protocol) { + mSmmIplEvents[Index].Event = EfiCreateProtocolNotifyEvent ( + mSmmIplEvents[Index].Guid, + mSmmIplEvents[Index].NotifyTpl, + mSmmIplEvents[Index].NotifyFunction, + mSmmIplEvents[Index].NotifyContext, + &Registration + ); + } else { + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + mSmmIplEvents[Index].NotifyTpl, + mSmmIplEvents[Index].NotifyFunction, + mSmmIplEvents[Index].NotifyContext, + mSmmIplEvents[Index].Guid, + &mSmmIplEvents[Index].Event + ); + ASSERT_EFI_ERROR (Status); + } + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf new file mode 100644 index 00000000..35d661f3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf @@ -0,0 +1,91 @@ +## @file +# This module provide an SMM CIS compliant implementation of SMM IPL. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmIpl + MODULE_UNI_FILE = PiSmmIpl.uni + FILE_GUID = 2FA2A6DA-11D5-4dc3-999A-749648B03C56 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmIplEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + PiSmmIpl.c + PiSmmCorePrivateData.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + PeCoffLib + CacheMaintenanceLib + MemoryAllocationLib + DebugLib + UefiBootServicesTableLib + DxeServicesTableLib + UefiLib + UefiRuntimeLib + DxeServicesLib + PcdLib + ReportStatusCodeLib + +[Protocols] + gEfiSmmBase2ProtocolGuid ## PRODUCES + gEfiSmmCommunicationProtocolGuid ## PRODUCES + gEfiMmCommunication2ProtocolGuid ## PRODUCES + gEfiSmmAccess2ProtocolGuid ## CONSUMES + ## NOTIFY + ## CONSUMES + gEfiSmmConfigurationProtocolGuid + gEfiSmmControl2ProtocolGuid ## CONSUMES + ## NOTIFY + ## SOMETIMES_CONSUMES + ## UNDEFINED # Used to do smm communication + gEfiDxeSmmReadyToLockProtocolGuid + gEfiCpuArchProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + ## CONSUMES ## Event + ## PRODUCES ## UNDEFINED # Used to do smm communication + gEfiEventDxeDispatchGuid + gEfiEventReadyToBootGuid ## CONSUMES ## Event + ## SOMETIMES_CONSUMES ## Event + ## SOMETIMES_PRODUCES ## UNDEFINED # Used to do smm communication + gEfiEventLegacyBootGuid + ## SOMETIMES_CONSUMES ## Event + ## SOMETIMES_PRODUCES ## UNDEFINED # Used to do smm communication + gEfiEventExitBootServicesGuid + ## SOMETIMES_CONSUMES ## Event + ## SOMETIMES_PRODUCES ## UNDEFINED # Used to do smm communication + gEfiEventReadyToBootGuid + gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + gLoadFixedAddressConfigurationTableGuid ## SOMETIMES_CONSUMES ## SystemTable + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdLoadFixAddressSmmCodePageNumber ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdLoadModuleAtFixAddressEnable ## CONSUMES + +[Depex] + gEfiSmmAccess2ProtocolGuid AND gEfiSmmControl2ProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + PiSmmIplExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.uni new file mode 100644 index 00000000..a2796854 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.uni @@ -0,0 +1,16 @@ +// /** @file
+// This module provide an SMM CIS compliant implementation of SMM IPL.
+//
+// This module provide an SMM CIS compliant implementation of SMM IPL.
+//
+// Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides an SMM CIS compliant implementation of SMM IPL"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provide an SMM CIS compliant implementation of SMM IPL."
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIplExtra.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIplExtra.uni new file mode 100644 index 00000000..d7674de9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIplExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// PiSmmIpl Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Core SMM Services Initial Program Loader"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Pool.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Pool.c new file mode 100644 index 00000000..a3db40f0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Pool.c @@ -0,0 +1,449 @@ +/** @file + SMM Memory pool management functions. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +LIST_ENTRY mSmmPoolLists[SmmPoolTypeMax][MAX_POOL_INDEX]; +// +// To cache the SMRAM base since when Loading modules At fixed address feature is enabled, +// all module is assigned an offset relative the SMRAM base in build time. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressSmramBase = 0; + +/** + Convert a UEFI memory type to SMM pool type. + + @param[in] MemoryType Type of pool to allocate. + + @return SMM pool type +**/ +SMM_POOL_TYPE +UefiMemoryTypeToSmmPoolType ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + ASSERT ((MemoryType == EfiRuntimeServicesCode) || (MemoryType == EfiRuntimeServicesData)); + switch (MemoryType) { + case EfiRuntimeServicesCode: + return SmmPoolTypeCode; + case EfiRuntimeServicesData: + return SmmPoolTypeData; + default: + return SmmPoolTypeMax; + } +} + + +/** + Called to initialize the memory service. + + @param SmramRangeCount Number of SMRAM Regions + @param SmramRanges Pointer to SMRAM Descriptors + +**/ +VOID +SmmInitializeMemoryServices ( + IN UINTN SmramRangeCount, + IN EFI_SMRAM_DESCRIPTOR *SmramRanges + ) +{ + UINTN Index; + EFI_STATUS Status; + UINTN SmmPoolTypeIndex; + EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable; + + // + // Initialize Pool list + // + for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) { + for (Index = 0; Index < ARRAY_SIZE (mSmmPoolLists[SmmPoolTypeIndex]); Index++) { + InitializeListHead (&mSmmPoolLists[SmmPoolTypeIndex][Index]); + } + } + + Status = EfiGetSystemConfigurationTable ( + &gLoadFixedAddressConfigurationTableGuid, + (VOID **) &LMFAConfigurationTable + ); + if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) { + gLoadModuleAtFixAddressSmramBase = LMFAConfigurationTable->SmramBase; + } + + // + // Add Free SMRAM regions + // Need add Free memory at first, to let gSmmMemoryMap record data + // + for (Index = 0; Index < SmramRangeCount; Index++) { + if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { + continue; + } + SmmAddMemoryRegion ( + SmramRanges[Index].CpuStart, + SmramRanges[Index].PhysicalSize, + EfiConventionalMemory, + SmramRanges[Index].RegionState + ); + } + + // + // Add the allocated SMRAM regions + // + for (Index = 0; Index < SmramRangeCount; Index++) { + if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) == 0) { + continue; + } + SmmAddMemoryRegion ( + SmramRanges[Index].CpuStart, + SmramRanges[Index].PhysicalSize, + EfiConventionalMemory, + SmramRanges[Index].RegionState + ); + } + +} + +/** + Internal Function. Allocate a pool by specified PoolIndex. + + @param PoolType Type of pool to allocate. + @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 EFI_MEMORY_TYPE PoolType, + IN UINTN PoolIndex, + OUT FREE_POOL_HEADER **FreePoolHdr + ) +{ + EFI_STATUS Status; + FREE_POOL_HEADER *Hdr; + POOL_TAIL *Tail; + EFI_PHYSICAL_ADDRESS Address; + SMM_POOL_TYPE SmmPoolType; + + Address = 0; + SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType); + + ASSERT (PoolIndex <= MAX_POOL_INDEX); + Status = EFI_SUCCESS; + Hdr = NULL; + if (PoolIndex == MAX_POOL_INDEX) { + Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, + EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), + &Address, FALSE); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + Hdr = (FREE_POOL_HEADER *) (UINTN) Address; + } else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) { + Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link); + RemoveEntryList (&Hdr->Link); + } else { + Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr); + if (!EFI_ERROR (Status)) { + Hdr->Header.Signature = 0; + Hdr->Header.Size >>= 1; + Hdr->Header.Available = TRUE; + Hdr->Header.Type = 0; + Tail = HEAD_TO_TAIL(&Hdr->Header); + Tail->Signature = 0; + Tail->Size = 0; + InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link); + Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size); + } + } + + if (!EFI_ERROR (Status)) { + Hdr->Header.Signature = POOL_HEAD_SIGNATURE; + Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex; + Hdr->Header.Available = FALSE; + Hdr->Header.Type = PoolType; + Tail = HEAD_TO_TAIL(&Hdr->Header); + Tail->Signature = POOL_TAIL_SIGNATURE; + Tail->Size = Hdr->Header.Size; + } + + *FreePoolHdr = Hdr; + return Status; +} + +/** + Internal Function. Free a pool by specified PoolIndex. + + @param FreePoolHdr The pool to free. + @param PoolTail The pointer to the pool tail. + + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +InternalFreePoolByIndex ( + IN FREE_POOL_HEADER *FreePoolHdr, + IN POOL_TAIL *PoolTail + ) +{ + UINTN PoolIndex; + SMM_POOL_TYPE SmmPoolType; + + ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0); + ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0); + ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE); + + SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type); + + PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT); + FreePoolHdr->Header.Signature = 0; + FreePoolHdr->Header.Available = TRUE; + FreePoolHdr->Header.Type = 0; + PoolTail->Signature = 0; + PoolTail->Size = 0; + ASSERT (PoolIndex < MAX_POOL_INDEX); + InsertHeadList (&mSmmPoolLists[SmmPoolType][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 +SmmInternalAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + POOL_HEADER *PoolHdr; + POOL_TAIL *PoolTail; + FREE_POOL_HEADER *FreePoolHdr; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + UINTN PoolIndex; + BOOLEAN HasPoolTail; + BOOLEAN NeedGuard; + UINTN NoPages; + + Address = 0; + + if (PoolType != EfiRuntimeServicesCode && + PoolType != EfiRuntimeServicesData) { + return EFI_INVALID_PARAMETER; + } + + NeedGuard = IsPoolTypeToGuard (PoolType); + HasPoolTail = !(NeedGuard && + ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0)); + + // + // Adjust the size by the pool header & tail overhead + // + Size += POOL_OVERHEAD; + if (Size > MAX_POOL_SIZE || NeedGuard) { + if (!HasPoolTail) { + Size -= sizeof (POOL_TAIL); + } + + NoPages = EFI_SIZE_TO_PAGES (Size); + Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, NoPages, + &Address, NeedGuard); + if (EFI_ERROR (Status)) { + return Status; + } + + if (NeedGuard) { + ASSERT (VerifyMemoryGuard (Address, NoPages) == TRUE); + Address = (EFI_PHYSICAL_ADDRESS)(UINTN)AdjustPoolHeadA ( + Address, + NoPages, + Size + ); + } + + PoolHdr = (POOL_HEADER*)(UINTN)Address; + PoolHdr->Signature = POOL_HEAD_SIGNATURE; + PoolHdr->Size = EFI_PAGES_TO_SIZE (NoPages); + PoolHdr->Available = FALSE; + PoolHdr->Type = PoolType; + + if (HasPoolTail) { + PoolTail = HEAD_TO_TAIL (PoolHdr); + PoolTail->Signature = POOL_TAIL_SIGNATURE; + PoolTail->Size = PoolHdr->Size; + } + + *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 (PoolType, 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 +SmmAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + + Status = SmmInternalAllocatePool (PoolType, Size, Buffer); + if (!EFI_ERROR (Status)) { + SmmCoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), + MemoryProfileActionAllocatePool, + PoolType, + Size, + *Buffer, + NULL + ); + } + 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 +SmmInternalFreePool ( + IN VOID *Buffer + ) +{ + FREE_POOL_HEADER *FreePoolHdr; + POOL_TAIL *PoolTail; + BOOLEAN HasPoolTail; + BOOLEAN MemoryGuarded; + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + MemoryGuarded = IsHeapGuardEnabled () && + IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer); + HasPoolTail = !(MemoryGuarded && + ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0)); + + FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1); + ASSERT (FreePoolHdr->Header.Signature == POOL_HEAD_SIGNATURE); + ASSERT (!FreePoolHdr->Header.Available); + if (FreePoolHdr->Header.Signature != POOL_HEAD_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + if (HasPoolTail) { + PoolTail = HEAD_TO_TAIL (&FreePoolHdr->Header); + ASSERT (PoolTail->Signature == POOL_TAIL_SIGNATURE); + ASSERT (FreePoolHdr->Header.Size == PoolTail->Size); + if (PoolTail->Signature != POOL_TAIL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + if (FreePoolHdr->Header.Size != PoolTail->Size) { + return EFI_INVALID_PARAMETER; + } + } else { + PoolTail = NULL; + } + + if (MemoryGuarded) { + Buffer = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr); + return SmmInternalFreePages ( + (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, + EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size), + TRUE + ); + } + + if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) { + ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0); + ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0); + return SmmInternalFreePages ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr, + EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size), + FALSE + ); + } + return InternalFreePoolByIndex (FreePoolHdr, PoolTail); +} + +/** + Frees pool. + + @param Buffer The allocated pool entry to free. + + @retval EFI_INVALID_PARAMETER Buffer is not a valid value. + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +EFIAPI +SmmFreePool ( + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + + Status = SmmInternalFreePool (Buffer); + if (!EFI_ERROR (Status)) { + SmmCoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), + MemoryProfileActionFreePool, + EfiMaxMemoryType, + 0, + Buffer, + NULL + ); + } + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Smi.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Smi.c new file mode 100644 index 00000000..6e056503 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Smi.c @@ -0,0 +1,333 @@ +/** @file + SMI management. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +LIST_ENTRY mSmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList); + +SMI_ENTRY mRootSmiEntry = { + SMI_ENTRY_SIGNATURE, + INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiEntry.AllEntries), + {0}, + INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiEntry.SmiHandlers), +}; + +/** + Finds the SMI entry for the requested handler type. + + @param HandlerType The type of the interrupt + @param Create Create a new entry if not found + + @return SMI entry + +**/ +SMI_ENTRY * +EFIAPI +SmmCoreFindSmiEntry ( + IN EFI_GUID *HandlerType, + IN BOOLEAN Create + ) +{ + LIST_ENTRY *Link; + SMI_ENTRY *Item; + SMI_ENTRY *SmiEntry; + + // + // Search the SMI entry list for the matching GUID + // + SmiEntry = NULL; + for (Link = mSmiEntryList.ForwardLink; + Link != &mSmiEntryList; + Link = Link->ForwardLink) { + + Item = CR (Link, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + if (CompareGuid (&Item->HandlerType, HandlerType)) { + // + // This is the SMI entry + // + SmiEntry = Item; + break; + } + } + + // + // If the protocol entry was not found and Create is TRUE, then + // allocate a new entry + // + if ((SmiEntry == NULL) && Create) { + SmiEntry = AllocatePool (sizeof(SMI_ENTRY)); + if (SmiEntry != NULL) { + // + // Initialize new SMI entry structure + // + SmiEntry->Signature = SMI_ENTRY_SIGNATURE; + CopyGuid ((VOID *)&SmiEntry->HandlerType, HandlerType); + InitializeListHead (&SmiEntry->SmiHandlers); + + // + // Add it to SMI entry list + // + InsertTailList (&mSmiEntryList, &SmiEntry->AllEntries); + } + } + return SmiEntry; +} + +/** + Manage SMI of a particular type. + + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param Context Points to an optional context buffer. + @param CommBuffer Points to the optional communication buffer. + @param CommBufferSize Points to the size of the optional communication buffer. + + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was processed successfully but not quiesced. + @retval EFI_INTERRUPT_PENDING One or more SMI 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 +SmiManage ( + IN CONST EFI_GUID *HandlerType, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *Head; + SMI_ENTRY *SmiEntry; + SMI_HANDLER *SmiHandler; + BOOLEAN SuccessReturn; + EFI_STATUS Status; + + Status = EFI_NOT_FOUND; + SuccessReturn = FALSE; + if (HandlerType == NULL) { + // + // Root SMI handler + // + SmiEntry = &mRootSmiEntry; + } else { + // + // Non-root SMI handler + // + SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, FALSE); + if (SmiEntry == NULL) { + // + // There is no handler registered for this interrupt source + // + return Status; + } + } + Head = &SmiEntry->SmiHandlers; + + for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { + SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + + Status = SmiHandler->Handler ( + (EFI_HANDLE) SmiHandler, + Context, + CommBuffer, + CommBufferSize + ); + + switch (Status) { + case EFI_INTERRUPT_PENDING: + // + // If a handler returns EFI_INTERRUPT_PENDING 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 SMM. + + @param Handler Handler service function pointer. + @param HandlerType Points to the handler type or NULL for root SMI handlers. + @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function. + + @retval EFI_SUCCESS Handler register success. + @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerRegister ( + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN CONST EFI_GUID *HandlerType OPTIONAL, + OUT EFI_HANDLE *DispatchHandle + ) +{ + SMI_HANDLER *SmiHandler; + SMI_ENTRY *SmiEntry; + LIST_ENTRY *List; + + if (Handler == NULL || DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER)); + if (SmiHandler == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SmiHandler->Signature = SMI_HANDLER_SIGNATURE; + SmiHandler->Handler = Handler; + SmiHandler->CallerAddr = (UINTN)RETURN_ADDRESS (0); + + if (HandlerType == NULL) { + // + // This is root SMI handler + // + SmiEntry = &mRootSmiEntry; + } else { + // + // None root SMI handler + // + SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE); + if (SmiEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + List = &SmiEntry->SmiHandlers; + + SmiHandler->SmiEntry = SmiEntry; + InsertTailList (List, &SmiHandler->Link); + + *DispatchHandle = (EFI_HANDLE) SmiHandler; + + return EFI_SUCCESS; +} + +/** + Unregister a handler in SMM. + + @param DispatchHandle The handle that was specified when the handler was registered. + + @retval EFI_SUCCESS Handler function was successfully unregistered. + @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle. + +**/ +EFI_STATUS +EFIAPI +SmiHandlerUnRegister ( + IN EFI_HANDLE DispatchHandle + ) +{ + SMI_HANDLER *SmiHandler; + SMI_ENTRY *SmiEntry; + LIST_ENTRY *EntryLink; + LIST_ENTRY *HandlerLink; + + if (DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Look for it in root SMI handlers + // + SmiHandler = NULL; + for ( HandlerLink = GetFirstNode (&mRootSmiEntry.SmiHandlers) + ; !IsNull (&mRootSmiEntry.SmiHandlers, HandlerLink) && ((EFI_HANDLE) SmiHandler != DispatchHandle) + ; HandlerLink = GetNextNode (&mRootSmiEntry.SmiHandlers, HandlerLink) + ) { + SmiHandler = CR (HandlerLink, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + } + + // + // Look for it in non-root SMI handlers + // + for ( EntryLink = GetFirstNode (&mSmiEntryList) + ; !IsNull (&mSmiEntryList, EntryLink) && ((EFI_HANDLE) SmiHandler != DispatchHandle) + ; EntryLink = GetNextNode (&mSmiEntryList, EntryLink) + ) { + SmiEntry = CR (EntryLink, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + for ( HandlerLink = GetFirstNode (&SmiEntry->SmiHandlers) + ; !IsNull (&SmiEntry->SmiHandlers, HandlerLink) && ((EFI_HANDLE) SmiHandler != DispatchHandle) + ; HandlerLink = GetNextNode (&SmiEntry->SmiHandlers, HandlerLink) + ) { + SmiHandler = CR (HandlerLink, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + } + } + + if ((EFI_HANDLE) SmiHandler != DispatchHandle) { + return EFI_INVALID_PARAMETER; + } + + SmiEntry = SmiHandler->SmiEntry; + + RemoveEntryList (&SmiHandler->Link); + FreePool (SmiHandler); + + if (SmiEntry == NULL) { + // + // This is root SMI handler + // + return EFI_SUCCESS; + } + + if (IsListEmpty (&SmiEntry->SmiHandlers)) { + // + // No handler registered for this interrupt now, remove the SMI_ENTRY + // + RemoveEntryList (&SmiEntry->AllEntries); + + FreePool (SmiEntry); + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmiHandlerProfile.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmiHandlerProfile.c new file mode 100644 index 00000000..85623b9a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmiHandlerProfile.c @@ -0,0 +1,1367 @@ +/** @file + SMI handler profile support. + +Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiSmm.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/SmmServicesTableLib.h> +#include <Library/DebugLib.h> +#include <Library/PrintLib.h> +#include <Library/UefiLib.h> +#include <Library/DevicePathLib.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Protocol/LoadedImage.h> +#include <Protocol/SmmAccess2.h> +#include <Protocol/SmmReadyToLock.h> +#include <Protocol/SmmEndOfDxe.h> + +#include <Guid/SmiHandlerProfile.h> + +#include "PiSmmCore.h" + +#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \ + ((ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1))) + +typedef struct { + EFI_GUID FileGuid; + PHYSICAL_ADDRESS EntryPoint; + PHYSICAL_ADDRESS ImageBase; + UINT64 ImageSize; + UINT32 ImageRef; + UINT16 PdbStringSize; + CHAR8 *PdbString; +} IMAGE_STRUCT; + +/** + Register SMI handler profile handler. +**/ +VOID +RegisterSmiHandlerProfileHandler( + VOID + ); + +/** + Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded + into system memory with the PE/COFF Loader Library functions. + + Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry + point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then + return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS. + If Pe32Data is NULL, then ASSERT(). + If EntryPoint is NULL, then ASSERT(). + + @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. + @param EntryPoint The pointer to entry point to the PE/COFF image to return. + + @retval RETURN_SUCCESS EntryPoint was returned. + @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image. + +**/ +RETURN_STATUS +InternalPeCoffGetEntryPoint ( + IN VOID *Pe32Data, + OUT VOID **EntryPoint + ); + +extern LIST_ENTRY mSmiEntryList; +extern LIST_ENTRY mHardwareSmiEntryList; +extern SMI_ENTRY mRootSmiEntry; + +extern SMI_HANDLER_PROFILE_PROTOCOL mSmiHandlerProfile; + +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mHardwareSmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mHardwareSmiEntryList); + +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mRootSmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiEntryList); + +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY *mSmmCoreRootSmiEntryList = &mRootSmiEntryList; +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY *mSmmCoreSmiEntryList = &mSmiEntryList; +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY *mSmmCoreHardwareSmiEntryList = &mHardwareSmiEntryList; + +GLOBAL_REMOVE_IF_UNREFERENCED IMAGE_STRUCT *mImageStruct; +GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mImageStructCountMax; +GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mImageStructCount; + +GLOBAL_REMOVE_IF_UNREFERENCED VOID *mSmiHandlerProfileDatabase; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mSmiHandlerProfileDatabaseSize; + +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mSmmImageDatabaseSize; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mSmmRootSmiDatabaseSize; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mSmmSmiDatabaseSize; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mSmmHardwareSmiDatabaseSize; + +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mSmiHandlerProfileRecordingStatus; + +GLOBAL_REMOVE_IF_UNREFERENCED SMI_HANDLER_PROFILE_PROTOCOL mSmiHandlerProfile = { + SmiHandlerProfileRegisterHandler, + SmiHandlerProfileUnregisterHandler, +}; + +/** + This function dump raw data. + + @param Data raw data + @param Size raw data size +**/ +VOID +InternalDumpData ( + IN UINT8 *Data, + IN UINTN Size + ) +{ + UINTN Index; + for (Index = 0; Index < Size; Index++) { + DEBUG ((DEBUG_INFO, "%02x ", (UINTN)Data[Index])); + } +} + +/** + Get GUID name for an image. + + @param[in] LoadedImage LoadedImage protocol. + @param[out] Guid Guid of the FFS +**/ +VOID +GetDriverGuid ( + IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage, + OUT EFI_GUID *Guid + ) +{ + EFI_GUID *FileName; + + FileName = NULL; + if ((DevicePathType(LoadedImage->FilePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType(LoadedImage->FilePath) == MEDIA_PIWG_FW_FILE_DP)) { + FileName = &((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)LoadedImage->FilePath)->FvFileName; + } + if (FileName != NULL) { + CopyGuid(Guid, FileName); + } else { + ZeroMem(Guid, sizeof(EFI_GUID)); + } +} + +/** + Add image structure. + + @param ImageBase image base + @param ImageSize image size + @param EntryPoint image entry point + @param Guid FFS GUID of the image + @param PdbString image PDB string +**/ +VOID +AddImageStruct( + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN PHYSICAL_ADDRESS EntryPoint, + IN EFI_GUID *Guid, + IN CHAR8 *PdbString + ) +{ + UINTN PdbStringSize; + + if (mImageStructCount >= mImageStructCountMax) { + ASSERT(FALSE); + return; + } + + CopyGuid(&mImageStruct[mImageStructCount].FileGuid, Guid); + mImageStruct[mImageStructCount].ImageRef = mImageStructCount; + mImageStruct[mImageStructCount].ImageBase = ImageBase; + mImageStruct[mImageStructCount].ImageSize = ImageSize; + mImageStruct[mImageStructCount].EntryPoint = EntryPoint; + if (PdbString != NULL) { + PdbStringSize = AsciiStrSize(PdbString); + mImageStruct[mImageStructCount].PdbString = AllocateCopyPool (PdbStringSize, PdbString); + if (mImageStruct[mImageStructCount].PdbString != NULL) { + mImageStruct[mImageStructCount].PdbStringSize = (UINT16) PdbStringSize; + } + } + + mImageStructCount++; +} + +/** + return an image structure based upon image address. + + @param Address image address + + @return image structure +**/ +IMAGE_STRUCT * +AddressToImageStruct( + IN UINTN Address + ) +{ + UINTN Index; + + for (Index = 0; Index < mImageStructCount; Index++) { + if ((Address >= mImageStruct[Index].ImageBase) && + (Address < mImageStruct[Index].ImageBase + mImageStruct[Index].ImageSize)) { + return &mImageStruct[Index]; + } + } + return NULL; +} + +/** + return an image reference index based upon image address. + + @param Address image address + + @return image reference index +**/ +UINT32 +AddressToImageRef( + IN UINTN Address + ) +{ + IMAGE_STRUCT *ImageStruct; + + ImageStruct = AddressToImageStruct(Address); + if (ImageStruct != NULL) { + return ImageStruct->ImageRef; + } + return (UINT32)-1; +} + +/** + Collect SMM image information based upon loaded image protocol. +**/ +VOID +GetSmmLoadedImage( + VOID + ) +{ + EFI_STATUS Status; + UINTN NoHandles; + UINTN HandleBufferSize; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + CHAR16 *PathStr; + EFI_SMM_DRIVER_ENTRY *LoadedImagePrivate; + PHYSICAL_ADDRESS EntryPoint; + VOID *EntryPointInImage; + EFI_GUID Guid; + CHAR8 *PdbString; + PHYSICAL_ADDRESS RealImageBase; + + HandleBufferSize = 0; + HandleBuffer = NULL; + Status = gSmst->SmmLocateHandle( + ByProtocol, + &gEfiLoadedImageProtocolGuid, + NULL, + &HandleBufferSize, + HandleBuffer + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return; + } + HandleBuffer = AllocateZeroPool (HandleBufferSize); + if (HandleBuffer == NULL) { + return; + } + Status = gSmst->SmmLocateHandle( + ByProtocol, + &gEfiLoadedImageProtocolGuid, + NULL, + &HandleBufferSize, + HandleBuffer + ); + if (EFI_ERROR(Status)) { + return; + } + + NoHandles = HandleBufferSize/sizeof(EFI_HANDLE); + mImageStructCountMax = (UINT32) NoHandles; + mImageStruct = AllocateZeroPool(mImageStructCountMax * sizeof(IMAGE_STRUCT)); + if (mImageStruct == NULL) { + goto Done; + } + + for (Index = 0; Index < NoHandles; Index++) { + Status = gSmst->SmmHandleProtocol( + HandleBuffer[Index], + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (EFI_ERROR(Status)) { + continue; + } + PathStr = ConvertDevicePathToText(LoadedImage->FilePath, TRUE, TRUE); + GetDriverGuid(LoadedImage, &Guid); + DEBUG ((DEBUG_INFO, "Image: %g ", &Guid)); + + EntryPoint = 0; + LoadedImagePrivate = BASE_CR(LoadedImage, EFI_SMM_DRIVER_ENTRY, SmmLoadedImage); + RealImageBase = (UINTN)LoadedImage->ImageBase; + if (LoadedImagePrivate->Signature == EFI_SMM_DRIVER_ENTRY_SIGNATURE) { + EntryPoint = LoadedImagePrivate->ImageEntryPoint; + if ((EntryPoint != 0) && ((EntryPoint < (UINTN)LoadedImage->ImageBase) || (EntryPoint >= ((UINTN)LoadedImage->ImageBase + LoadedImage->ImageSize)))) { + // + // If the EntryPoint is not in the range of image buffer, it should come from emulation environment. + // So patch ImageBuffer here to align the EntryPoint. + // + Status = InternalPeCoffGetEntryPoint(LoadedImage->ImageBase, &EntryPointInImage); + ASSERT_EFI_ERROR(Status); + RealImageBase = (UINTN)LoadedImage->ImageBase + EntryPoint - (UINTN)EntryPointInImage; + } + } + DEBUG ((DEBUG_INFO, "(0x%lx - 0x%lx", RealImageBase, LoadedImage->ImageSize)); + if (EntryPoint != 0) { + DEBUG ((DEBUG_INFO, ", EntryPoint:0x%lx", EntryPoint)); + } + DEBUG ((DEBUG_INFO, ")\n")); + + if (RealImageBase != 0) { + PdbString = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) RealImageBase); + DEBUG ((DEBUG_INFO, " pdb - %a\n", PdbString)); + } else { + PdbString = NULL; + } + DEBUG ((DEBUG_INFO, " (%s)\n", PathStr)); + + AddImageStruct(RealImageBase, LoadedImage->ImageSize, EntryPoint, &Guid, PdbString); + } + +Done: + FreePool(HandleBuffer); + return; +} + +/** + Dump SMI child context. + + @param HandlerType the handler type + @param Context the handler context + @param ContextSize the handler context size +**/ +VOID +DumpSmiChildContext ( + IN EFI_GUID *HandlerType, + IN VOID *Context, + IN UINTN ContextSize + ) +{ + CHAR16 *Str; + + if (CompareGuid (HandlerType, &gEfiSmmSwDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " SwSmi - 0x%lx\n", ((SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT *)Context)->SwSmiInputValue)); + } else if (CompareGuid (HandlerType, &gEfiSmmSxDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " SxType - 0x%x\n", ((EFI_SMM_SX_REGISTER_CONTEXT *)Context)->Type)); + DEBUG ((DEBUG_INFO, " SxPhase - 0x%x\n", ((EFI_SMM_SX_REGISTER_CONTEXT *)Context)->Phase)); + } else if (CompareGuid (HandlerType, &gEfiSmmPowerButtonDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " PowerButtonPhase - 0x%x\n", ((EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT *)Context)->Phase)); + } else if (CompareGuid (HandlerType, &gEfiSmmStandbyButtonDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " StandbyButtonPhase - 0x%x\n", ((EFI_SMM_STANDBY_BUTTON_REGISTER_CONTEXT *)Context)->Phase)); + } else if (CompareGuid (HandlerType, &gEfiSmmPeriodicTimerDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " PeriodicTimerPeriod - %ld\n", ((EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT *)Context)->Period)); + DEBUG ((DEBUG_INFO, " PeriodicTimerSmiTickInterval - %ld\n", ((EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT *)Context)->SmiTickInterval)); + } else if (CompareGuid (HandlerType, &gEfiSmmGpiDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " GpiNum - 0x%lx\n", ((EFI_SMM_GPI_REGISTER_CONTEXT *)Context)->GpiNum)); + } else if (CompareGuid (HandlerType, &gEfiSmmIoTrapDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " IoTrapAddress - 0x%x\n", ((EFI_SMM_IO_TRAP_REGISTER_CONTEXT *)Context)->Address)); + DEBUG ((DEBUG_INFO, " IoTrapLength - 0x%x\n", ((EFI_SMM_IO_TRAP_REGISTER_CONTEXT *)Context)->Length)); + DEBUG ((DEBUG_INFO, " IoTrapType - 0x%x\n", ((EFI_SMM_IO_TRAP_REGISTER_CONTEXT *)Context)->Type)); + } else if (CompareGuid (HandlerType, &gEfiSmmUsbDispatch2ProtocolGuid)) { + DEBUG ((DEBUG_INFO, " UsbType - 0x%x\n", ((SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT *)Context)->Type)); + Str = ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL *)(((SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT *)Context) + 1), TRUE, TRUE); + DEBUG ((DEBUG_INFO, " UsbDevicePath - %s\n", Str)); + if (Str != NULL) { + FreePool (Str); + } + } else { + DEBUG ((DEBUG_INFO, " Context - ")); + InternalDumpData (Context, ContextSize); + DEBUG ((DEBUG_INFO, "\n")); + } +} + +/** + Dump all SMI handlers associated with SmiEntry. + + @param SmiEntry SMI entry. +**/ +VOID +DumpSmiHandlerOnSmiEntry( + IN SMI_ENTRY *SmiEntry + ) +{ + LIST_ENTRY *ListEntry; + SMI_HANDLER *SmiHandler; + IMAGE_STRUCT *ImageStruct; + + ListEntry = &SmiEntry->SmiHandlers; + for (ListEntry = ListEntry->ForwardLink; + ListEntry != &SmiEntry->SmiHandlers; + ListEntry = ListEntry->ForwardLink) { + SmiHandler = CR(ListEntry, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + ImageStruct = AddressToImageStruct((UINTN)SmiHandler->Handler); + if (ImageStruct != NULL) { + DEBUG ((DEBUG_INFO, " Module - %g", &ImageStruct->FileGuid)); + } + if ((ImageStruct != NULL) && (ImageStruct->PdbString[0] != 0)) { + DEBUG ((DEBUG_INFO, " (Pdb - %a)", ImageStruct->PdbString)); + } + DEBUG ((DEBUG_INFO, "\n")); + if (SmiHandler->ContextSize != 0) { + DumpSmiChildContext (&SmiEntry->HandlerType, SmiHandler->Context, SmiHandler->ContextSize); + } + DEBUG ((DEBUG_INFO, " Handler - 0x%x", SmiHandler->Handler)); + if (ImageStruct != NULL) { + DEBUG ((DEBUG_INFO, " <== RVA - 0x%x", (UINTN)SmiHandler->Handler - (UINTN) ImageStruct->ImageBase)); + } + DEBUG ((DEBUG_INFO, "\n")); + DEBUG ((DEBUG_INFO, " CallerAddr - 0x%x", SmiHandler->CallerAddr)); + if (ImageStruct != NULL) { + DEBUG ((DEBUG_INFO, " <== RVA - 0x%x", SmiHandler->CallerAddr - (UINTN) ImageStruct->ImageBase)); + } + DEBUG ((DEBUG_INFO, "\n")); + } + + return; +} + +/** + Dump all SMI entry on the list. + + @param SmiEntryList a list of SMI entry. +**/ +VOID +DumpSmiEntryList( + IN LIST_ENTRY *SmiEntryList + ) +{ + LIST_ENTRY *ListEntry; + SMI_ENTRY *SmiEntry; + + ListEntry = SmiEntryList; + for (ListEntry = ListEntry->ForwardLink; + ListEntry != SmiEntryList; + ListEntry = ListEntry->ForwardLink) { + SmiEntry = CR(ListEntry, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + DEBUG ((DEBUG_INFO, "SmiEntry - %g\n", &SmiEntry->HandlerType)); + DumpSmiHandlerOnSmiEntry(SmiEntry); + } + + return; +} + +/** + SMM Ready To Lock event notification handler. + + This function collects all SMM image information and build SmiHandleProfile database, + and register SmiHandlerProfile SMI handler. + + @param[in] Protocol Points to the protocol's unique identifier. + @param[in] Interface Points to the interface instance. + @param[in] Handle The handle on which the interface was installed. + + @retval EFI_SUCCESS Notification handler runs successfully. +**/ +EFI_STATUS +EFIAPI +SmmReadyToLockInSmiHandlerProfile ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + // + // Dump all image + // + DEBUG ((DEBUG_INFO, "##################\n")); + DEBUG ((DEBUG_INFO, "# IMAGE DATABASE #\n")); + DEBUG ((DEBUG_INFO, "##################\n")); + GetSmmLoadedImage (); + DEBUG ((DEBUG_INFO, "\n")); + + // + // Dump SMI Handler + // + DEBUG ((DEBUG_INFO, "########################\n")); + DEBUG ((DEBUG_INFO, "# SMI Handler DATABASE #\n")); + DEBUG ((DEBUG_INFO, "########################\n")); + + DEBUG ((DEBUG_INFO, "# 1. ROOT SMI Handler #\n")); + DEBUG_CODE ( + DumpSmiEntryList(mSmmCoreRootSmiEntryList); + ); + + DEBUG ((DEBUG_INFO, "# 2. GUID SMI Handler #\n")); + DEBUG_CODE ( + DumpSmiEntryList(mSmmCoreSmiEntryList); + ); + + DEBUG ((DEBUG_INFO, "# 3. Hardware SMI Handler #\n")); + DEBUG_CODE ( + DumpSmiEntryList(mSmmCoreHardwareSmiEntryList); + ); + + DEBUG ((DEBUG_INFO, "\n")); + + RegisterSmiHandlerProfileHandler(); + + if (mImageStruct != NULL) { + FreePool(mImageStruct); + } + + return EFI_SUCCESS; +} + +/** + returns SMM image data base size. + + @return SMM image data base size. +**/ +UINTN +GetSmmImageDatabaseSize( + VOID + ) +{ + UINTN Size; + UINT32 Index; + + Size = 0; + for (Index = 0; Index < mImageStructCount; Index++) { + Size += sizeof(SMM_CORE_IMAGE_DATABASE_STRUCTURE) + GET_OCCUPIED_SIZE (mImageStruct[Index].PdbStringSize, sizeof (UINT64)); + } + return Size; +} + +/** + returns all SMI handlers' size associated with SmiEntry. + + @param SmiEntry SMI entry. + + @return all SMI handlers' size associated with SmiEntry. +**/ +UINTN +GetSmmSmiHandlerSizeOnSmiEntry( + IN SMI_ENTRY *SmiEntry + ) +{ + LIST_ENTRY *ListEntry; + SMI_HANDLER *SmiHandler; + UINTN Size; + + Size = 0; + ListEntry = &SmiEntry->SmiHandlers; + for (ListEntry = ListEntry->ForwardLink; + ListEntry != &SmiEntry->SmiHandlers; + ListEntry = ListEntry->ForwardLink) { + SmiHandler = CR(ListEntry, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + Size += sizeof(SMM_CORE_SMI_HANDLER_STRUCTURE) + GET_OCCUPIED_SIZE (SmiHandler->ContextSize, sizeof (UINT64)); + } + + return Size; +} + +/** + return all SMI handler database size on the SMI entry list. + + @param SmiEntryList a list of SMI entry. + + @return all SMI handler database size on the SMI entry list. +**/ +UINTN +GetSmmSmiDatabaseSize( + IN LIST_ENTRY *SmiEntryList + ) +{ + LIST_ENTRY *ListEntry; + SMI_ENTRY *SmiEntry; + UINTN Size; + + Size = 0; + ListEntry = SmiEntryList; + for (ListEntry = ListEntry->ForwardLink; + ListEntry != SmiEntryList; + ListEntry = ListEntry->ForwardLink) { + SmiEntry = CR(ListEntry, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + Size += sizeof(SMM_CORE_SMI_DATABASE_STRUCTURE); + Size += GetSmmSmiHandlerSizeOnSmiEntry(SmiEntry); + } + return Size; +} + +/** + return SMI handler profile database size. + + @return SMI handler profile database size. +**/ +UINTN +GetSmiHandlerProfileDatabaseSize ( + VOID + ) +{ + mSmmImageDatabaseSize = GetSmmImageDatabaseSize(); + mSmmRootSmiDatabaseSize = GetSmmSmiDatabaseSize(mSmmCoreRootSmiEntryList); + mSmmSmiDatabaseSize = GetSmmSmiDatabaseSize(mSmmCoreSmiEntryList); + mSmmHardwareSmiDatabaseSize = GetSmmSmiDatabaseSize(mSmmCoreHardwareSmiEntryList); + + return mSmmImageDatabaseSize + mSmmSmiDatabaseSize + mSmmRootSmiDatabaseSize + mSmmHardwareSmiDatabaseSize; +} + +/** + get SMM image database. + + @param Data The buffer to hold SMM image database + @param ExpectedSize The expected size of the SMM image database + + @return SMM image data base size. +**/ +UINTN +GetSmmImageDatabaseData ( + IN OUT VOID *Data, + IN UINTN ExpectedSize + ) +{ + SMM_CORE_IMAGE_DATABASE_STRUCTURE *ImageStruct; + UINTN Size; + UINTN Index; + + ImageStruct = Data; + Size = 0; + for (Index = 0; Index < mImageStructCount; Index++) { + if (Size >= ExpectedSize) { + return 0; + } + if (sizeof(SMM_CORE_IMAGE_DATABASE_STRUCTURE) + GET_OCCUPIED_SIZE (mImageStruct[Index].PdbStringSize, sizeof (UINT64)) > ExpectedSize - Size) { + return 0; + } + ImageStruct->Header.Signature = SMM_CORE_IMAGE_DATABASE_SIGNATURE; + ImageStruct->Header.Length = (UINT32)(sizeof(SMM_CORE_IMAGE_DATABASE_STRUCTURE) + GET_OCCUPIED_SIZE (mImageStruct[Index].PdbStringSize, sizeof (UINT64))); + ImageStruct->Header.Revision = SMM_CORE_IMAGE_DATABASE_REVISION; + CopyGuid(&ImageStruct->FileGuid, &mImageStruct[Index].FileGuid); + ImageStruct->ImageRef = mImageStruct[Index].ImageRef; + ImageStruct->EntryPoint = mImageStruct[Index].EntryPoint; + ImageStruct->ImageBase = mImageStruct[Index].ImageBase; + ImageStruct->ImageSize = mImageStruct[Index].ImageSize; + if (mImageStruct[Index].PdbStringSize != 0) { + ImageStruct->PdbStringOffset = sizeof(SMM_CORE_IMAGE_DATABASE_STRUCTURE); + CopyMem ((VOID *)((UINTN)ImageStruct + ImageStruct->PdbStringOffset), mImageStruct[Index].PdbString, mImageStruct[Index].PdbStringSize); + } else { + ImageStruct->PdbStringOffset = 0; + } + ImageStruct = (SMM_CORE_IMAGE_DATABASE_STRUCTURE *)((UINTN)ImageStruct + ImageStruct->Header.Length); + Size += sizeof(SMM_CORE_IMAGE_DATABASE_STRUCTURE) + GET_OCCUPIED_SIZE (mImageStruct[Index].PdbStringSize, sizeof (UINT64)); + } + + if (ExpectedSize != Size) { + return 0; + } + return Size; +} + +/** + get all SMI handler data associated with SmiEntry. + + @param SmiEntry SMI entry. + @param Data The buffer to hold all SMI handler data + @param MaxSize The max size of the SMM image database + @param Count The count of the SMI handler. + + @return SMM image data base size. +**/ +UINTN +GetSmmSmiHandlerDataOnSmiEntry( + IN SMI_ENTRY *SmiEntry, + IN OUT VOID *Data, + IN UINTN MaxSize, + OUT UINT32 *Count + ) +{ + SMM_CORE_SMI_HANDLER_STRUCTURE *SmiHandlerStruct; + LIST_ENTRY *ListEntry; + SMI_HANDLER *SmiHandler; + UINTN Size; + + SmiHandlerStruct = Data; + Size = 0; + *Count = 0; + ListEntry = &SmiEntry->SmiHandlers; + for (ListEntry = ListEntry->ForwardLink; + ListEntry != &SmiEntry->SmiHandlers; + ListEntry = ListEntry->ForwardLink) { + SmiHandler = CR(ListEntry, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + if (Size >= MaxSize) { + *Count = 0; + return 0; + } + if (sizeof(SMM_CORE_SMI_HANDLER_STRUCTURE) + GET_OCCUPIED_SIZE (SmiHandler->ContextSize, sizeof (UINT64)) > MaxSize - Size) { + *Count = 0; + return 0; + } + SmiHandlerStruct->Length = (UINT32)(sizeof(SMM_CORE_SMI_HANDLER_STRUCTURE) + GET_OCCUPIED_SIZE (SmiHandler->ContextSize, sizeof (UINT64))); + SmiHandlerStruct->CallerAddr = (UINTN)SmiHandler->CallerAddr; + SmiHandlerStruct->Handler = (UINTN)SmiHandler->Handler; + SmiHandlerStruct->ImageRef = AddressToImageRef((UINTN)SmiHandler->Handler); + SmiHandlerStruct->ContextBufferSize = (UINT32)SmiHandler->ContextSize; + if (SmiHandler->ContextSize != 0) { + SmiHandlerStruct->ContextBufferOffset = sizeof(SMM_CORE_SMI_HANDLER_STRUCTURE); + CopyMem ((UINT8 *)SmiHandlerStruct + SmiHandlerStruct->ContextBufferOffset, SmiHandler->Context, SmiHandler->ContextSize); + } else { + SmiHandlerStruct->ContextBufferOffset = 0; + } + Size += sizeof(SMM_CORE_SMI_HANDLER_STRUCTURE) + GET_OCCUPIED_SIZE (SmiHandler->ContextSize, sizeof (UINT64)); + SmiHandlerStruct = (SMM_CORE_SMI_HANDLER_STRUCTURE *)((UINTN)SmiHandlerStruct + SmiHandlerStruct->Length); + *Count = *Count + 1; + } + + return Size; +} + +/** + get all SMI handler database on the SMI entry list. + + @param SmiEntryList a list of SMI entry. + @param HandlerCategory The handler category + @param Data The buffer to hold all SMI handler database + @param ExpectedSize The expected size of the SMM image database + + @return all SMI database size on the SMI entry list. +**/ +UINTN +GetSmmSmiDatabaseData( + IN LIST_ENTRY *SmiEntryList, + IN UINT32 HandlerCategory, + IN OUT VOID *Data, + IN UINTN ExpectedSize + ) +{ + SMM_CORE_SMI_DATABASE_STRUCTURE *SmiStruct; + LIST_ENTRY *ListEntry; + SMI_ENTRY *SmiEntry; + UINTN Size; + UINTN SmiHandlerSize; + UINT32 SmiHandlerCount; + + SmiStruct = Data; + Size = 0; + ListEntry = SmiEntryList; + for (ListEntry = ListEntry->ForwardLink; + ListEntry != SmiEntryList; + ListEntry = ListEntry->ForwardLink) { + SmiEntry = CR(ListEntry, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + if (Size >= ExpectedSize) { + return 0; + } + if (sizeof(SMM_CORE_SMI_DATABASE_STRUCTURE) > ExpectedSize - Size) { + return 0; + } + + SmiStruct->Header.Signature = SMM_CORE_SMI_DATABASE_SIGNATURE; + SmiStruct->Header.Length = sizeof(SMM_CORE_SMI_DATABASE_STRUCTURE); + SmiStruct->Header.Revision = SMM_CORE_SMI_DATABASE_REVISION; + SmiStruct->HandlerCategory = HandlerCategory; + CopyGuid(&SmiStruct->HandlerType, &SmiEntry->HandlerType); + Size += sizeof(SMM_CORE_SMI_DATABASE_STRUCTURE); + SmiHandlerSize = GetSmmSmiHandlerDataOnSmiEntry(SmiEntry, (UINT8 *)SmiStruct + SmiStruct->Header.Length, ExpectedSize - Size, &SmiHandlerCount); + SmiStruct->HandlerCount = SmiHandlerCount; + Size += SmiHandlerSize; + SmiStruct->Header.Length += (UINT32)SmiHandlerSize; + SmiStruct = (VOID *)((UINTN)SmiStruct + SmiStruct->Header.Length); + } + if (ExpectedSize != Size) { + return 0; + } + return Size; +} + +/** + Get SMI handler profile database. + + @param Data the buffer to hold SMI handler profile database + + @retval EFI_SUCCESS the database is got. + @retval EFI_INVALID_PARAMETER the database size mismatch. +**/ +EFI_STATUS +GetSmiHandlerProfileDatabaseData( + IN OUT VOID *Data + ) +{ + UINTN SmmImageDatabaseSize; + UINTN SmmSmiDatabaseSize; + UINTN SmmRootSmiDatabaseSize; + UINTN SmmHardwareSmiDatabaseSize; + + DEBUG((DEBUG_VERBOSE, "GetSmiHandlerProfileDatabaseData\n")); + SmmImageDatabaseSize = GetSmmImageDatabaseData(Data, mSmmImageDatabaseSize); + if (SmmImageDatabaseSize != mSmmImageDatabaseSize) { + DEBUG((DEBUG_ERROR, "GetSmiHandlerProfileDatabaseData - SmmImageDatabaseSize mismatch!\n")); + return EFI_INVALID_PARAMETER; + } + SmmRootSmiDatabaseSize = GetSmmSmiDatabaseData(mSmmCoreRootSmiEntryList, SmmCoreSmiHandlerCategoryRootHandler, (UINT8 *)Data + SmmImageDatabaseSize, mSmmRootSmiDatabaseSize); + if (SmmRootSmiDatabaseSize != mSmmRootSmiDatabaseSize) { + DEBUG((DEBUG_ERROR, "GetSmiHandlerProfileDatabaseData - SmmRootSmiDatabaseSize mismatch!\n")); + return EFI_INVALID_PARAMETER; + } + SmmSmiDatabaseSize = GetSmmSmiDatabaseData(mSmmCoreSmiEntryList, SmmCoreSmiHandlerCategoryGuidHandler, (UINT8 *)Data + SmmImageDatabaseSize + mSmmRootSmiDatabaseSize, mSmmSmiDatabaseSize); + if (SmmSmiDatabaseSize != mSmmSmiDatabaseSize) { + DEBUG((DEBUG_ERROR, "GetSmiHandlerProfileDatabaseData - SmmSmiDatabaseSize mismatch!\n")); + return EFI_INVALID_PARAMETER; + } + SmmHardwareSmiDatabaseSize = GetSmmSmiDatabaseData(mSmmCoreHardwareSmiEntryList, SmmCoreSmiHandlerCategoryHardwareHandler, (UINT8 *)Data + SmmImageDatabaseSize + SmmRootSmiDatabaseSize + SmmSmiDatabaseSize, mSmmHardwareSmiDatabaseSize); + if (SmmHardwareSmiDatabaseSize != mSmmHardwareSmiDatabaseSize) { + DEBUG((DEBUG_ERROR, "GetSmiHandlerProfileDatabaseData - SmmHardwareSmiDatabaseSize mismatch!\n")); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + build SMI handler profile database. +**/ +VOID +BuildSmiHandlerProfileDatabase( + VOID + ) +{ + EFI_STATUS Status; + mSmiHandlerProfileDatabaseSize = GetSmiHandlerProfileDatabaseSize(); + mSmiHandlerProfileDatabase = AllocatePool(mSmiHandlerProfileDatabaseSize); + if (mSmiHandlerProfileDatabase == NULL) { + return; + } + Status = GetSmiHandlerProfileDatabaseData(mSmiHandlerProfileDatabase); + if (EFI_ERROR(Status)) { + FreePool(mSmiHandlerProfileDatabase); + mSmiHandlerProfileDatabase = NULL; + } +} + +/** + Copy SMI handler profile data. + + @param DataBuffer The buffer to hold SMI handler profile data. + @param DataSize On input, data buffer size. + On output, actual data buffer size copied. + @param DataOffset On input, data buffer offset to copy. + On output, next time data buffer offset to copy. + +**/ +VOID +SmiHandlerProfileCopyData( + OUT VOID *DataBuffer, + IN OUT UINT64 *DataSize, + IN OUT UINT64 *DataOffset + ) +{ + if (*DataOffset >= mSmiHandlerProfileDatabaseSize) { + *DataOffset = mSmiHandlerProfileDatabaseSize; + return; + } + if (mSmiHandlerProfileDatabaseSize - *DataOffset < *DataSize) { + *DataSize = mSmiHandlerProfileDatabaseSize - *DataOffset; + } + + CopyMem( + DataBuffer, + (UINT8 *)mSmiHandlerProfileDatabase + *DataOffset, + (UINTN)*DataSize + ); + *DataOffset = *DataOffset + *DataSize; +} + +/** + SMI handler profile handler to get info. + + @param SmiHandlerProfileParameterGetInfo The parameter of SMI handler profile get info. + +**/ +VOID +SmiHandlerProfileHandlerGetInfo( + IN SMI_HANDLER_PROFILE_PARAMETER_GET_INFO *SmiHandlerProfileParameterGetInfo + ) +{ + BOOLEAN SmiHandlerProfileRecordingStatus; + + SmiHandlerProfileRecordingStatus = mSmiHandlerProfileRecordingStatus; + mSmiHandlerProfileRecordingStatus = FALSE; + + SmiHandlerProfileParameterGetInfo->DataSize = mSmiHandlerProfileDatabaseSize; + SmiHandlerProfileParameterGetInfo->Header.ReturnStatus = 0; + + mSmiHandlerProfileRecordingStatus = SmiHandlerProfileRecordingStatus; +} + +/** + SMI handler profile handler to get data by offset. + + @param SmiHandlerProfileParameterGetDataByOffset The parameter of SMI handler profile get data by offset. + +**/ +VOID +SmiHandlerProfileHandlerGetDataByOffset( + IN SMI_HANDLER_PROFILE_PARAMETER_GET_DATA_BY_OFFSET *SmiHandlerProfileParameterGetDataByOffset + ) +{ + SMI_HANDLER_PROFILE_PARAMETER_GET_DATA_BY_OFFSET SmiHandlerProfileGetDataByOffset; + BOOLEAN SmiHandlerProfileRecordingStatus; + + SmiHandlerProfileRecordingStatus = mSmiHandlerProfileRecordingStatus; + mSmiHandlerProfileRecordingStatus = FALSE; + + CopyMem(&SmiHandlerProfileGetDataByOffset, SmiHandlerProfileParameterGetDataByOffset, sizeof(SmiHandlerProfileGetDataByOffset)); + + // + // Sanity check + // + if (!SmmIsBufferOutsideSmmValid((UINTN)SmiHandlerProfileGetDataByOffset.DataBuffer, (UINTN)SmiHandlerProfileGetDataByOffset.DataSize)) { + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandlerGetDataByOffset: SMI handler profile get data in SMRAM or overflow!\n")); + SmiHandlerProfileParameterGetDataByOffset->Header.ReturnStatus = (UINT64)(INT64)(INTN)EFI_ACCESS_DENIED; + goto Done; + } + + SmiHandlerProfileCopyData((VOID *)(UINTN)SmiHandlerProfileGetDataByOffset.DataBuffer, &SmiHandlerProfileGetDataByOffset.DataSize, &SmiHandlerProfileGetDataByOffset.DataOffset); + CopyMem(SmiHandlerProfileParameterGetDataByOffset, &SmiHandlerProfileGetDataByOffset, sizeof(SmiHandlerProfileGetDataByOffset)); + SmiHandlerProfileParameterGetDataByOffset->Header.ReturnStatus = 0; + +Done: + mSmiHandlerProfileRecordingStatus = SmiHandlerProfileRecordingStatus; +} + +/** + Dispatch function for a Software SMI handler. + + Caution: This function may receive untrusted input. + Communicate buffer and buffer size are external input, so this function will do basic validation. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the + handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS Command is handled successfully. +**/ +EFI_STATUS +EFIAPI +SmiHandlerProfileHandler( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + SMI_HANDLER_PROFILE_PARAMETER_HEADER *SmiHandlerProfileParameterHeader; + UINTN TempCommBufferSize; + + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandler Enter\n")); + + if (mSmiHandlerProfileDatabase == NULL) { + return EFI_SUCCESS; + } + + // + // If input is invalid, stop processing this SMI + // + if (CommBuffer == NULL || CommBufferSize == NULL) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if (TempCommBufferSize < sizeof(SMI_HANDLER_PROFILE_PARAMETER_HEADER)) { + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + + if (!SmmIsBufferOutsideSmmValid((UINTN)CommBuffer, TempCommBufferSize)) { + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandler: SMM communication buffer in SMRAM or overflow!\n")); + return EFI_SUCCESS; + } + + SmiHandlerProfileParameterHeader = (SMI_HANDLER_PROFILE_PARAMETER_HEADER *)((UINTN)CommBuffer); + SmiHandlerProfileParameterHeader->ReturnStatus = (UINT64)-1; + + switch (SmiHandlerProfileParameterHeader->Command) { + case SMI_HANDLER_PROFILE_COMMAND_GET_INFO: + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandlerGetInfo\n")); + if (TempCommBufferSize != sizeof(SMI_HANDLER_PROFILE_PARAMETER_GET_INFO)) { + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmiHandlerProfileHandlerGetInfo((SMI_HANDLER_PROFILE_PARAMETER_GET_INFO *)(UINTN)CommBuffer); + break; + case SMI_HANDLER_PROFILE_COMMAND_GET_DATA_BY_OFFSET: + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandlerGetDataByOffset\n")); + if (TempCommBufferSize != sizeof(SMI_HANDLER_PROFILE_PARAMETER_GET_DATA_BY_OFFSET)) { + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmiHandlerProfileHandlerGetDataByOffset((SMI_HANDLER_PROFILE_PARAMETER_GET_DATA_BY_OFFSET *)(UINTN)CommBuffer); + break; + default: + break; + } + + DEBUG((DEBUG_ERROR, "SmiHandlerProfileHandler Exit\n")); + + return EFI_SUCCESS; +} + +/** + Register SMI handler profile handler. +**/ +VOID +RegisterSmiHandlerProfileHandler ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE DispatchHandle; + + Status = gSmst->SmiHandlerRegister ( + SmiHandlerProfileHandler, + &gSmiHandlerProfileGuid, + &DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + BuildSmiHandlerProfileDatabase(); +} + +/** + Finds the SMI entry for the requested handler type. + + @param HandlerType The type of the interrupt + @param Create Create a new entry if not found + + @return SMI entry +**/ +SMI_ENTRY * +SmmCoreFindHardwareSmiEntry ( + IN EFI_GUID *HandlerType, + IN BOOLEAN Create + ) +{ + LIST_ENTRY *Link; + SMI_ENTRY *Item; + SMI_ENTRY *SmiEntry; + + // + // Search the SMI entry list for the matching GUID + // + SmiEntry = NULL; + for (Link = mHardwareSmiEntryList.ForwardLink; + Link != &mHardwareSmiEntryList; + Link = Link->ForwardLink) { + + Item = CR (Link, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + if (CompareGuid (&Item->HandlerType, HandlerType)) { + // + // This is the SMI entry + // + SmiEntry = Item; + break; + } + } + + // + // If the protocol entry was not found and Create is TRUE, then + // allocate a new entry + // + if ((SmiEntry == NULL) && Create) { + SmiEntry = AllocatePool (sizeof(SMI_ENTRY)); + if (SmiEntry != NULL) { + // + // Initialize new SMI entry structure + // + SmiEntry->Signature = SMI_ENTRY_SIGNATURE; + CopyGuid ((VOID *)&SmiEntry->HandlerType, HandlerType); + InitializeListHead (&SmiEntry->SmiHandlers); + + // + // Add it to SMI entry list + // + InsertTailList (&mHardwareSmiEntryList, &SmiEntry->AllEntries); + } + } + return SmiEntry; +} + +/** + Convert EFI_SMM_USB_REGISTER_CONTEXT to SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT. + + @param UsbContext A pointer to EFI_SMM_USB_REGISTER_CONTEXT + @param UsbContextSize The size of EFI_SMM_USB_REGISTER_CONTEXT in bytes + @param SmiHandlerUsbContextSize The size of SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT in bytes + + @return SmiHandlerUsbContext A pointer to SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT +**/ +SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT * +ConvertSmiHandlerUsbContext ( + IN EFI_SMM_USB_REGISTER_CONTEXT *UsbContext, + IN UINTN UsbContextSize, + OUT UINTN *SmiHandlerUsbContextSize + ) +{ + UINTN DevicePathSize; + SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT *SmiHandlerUsbContext; + + ASSERT (UsbContextSize == sizeof(EFI_SMM_USB_REGISTER_CONTEXT)); + + DevicePathSize = GetDevicePathSize (UsbContext->Device); + SmiHandlerUsbContext = AllocatePool (sizeof (SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT) + DevicePathSize); + if (SmiHandlerUsbContext == NULL) { + *SmiHandlerUsbContextSize = 0; + return NULL; + } + SmiHandlerUsbContext->Type = UsbContext->Type; + SmiHandlerUsbContext->DevicePathSize = (UINT32)DevicePathSize; + CopyMem (SmiHandlerUsbContext + 1, UsbContext->Device, DevicePathSize); + *SmiHandlerUsbContextSize = sizeof (SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT) + DevicePathSize; + return SmiHandlerUsbContext; +} + +/** + Convert EFI_SMM_SW_REGISTER_CONTEXT to SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT. + + @param SwContext A pointer to EFI_SMM_SW_REGISTER_CONTEXT + @param SwContextSize The size of EFI_SMM_SW_REGISTER_CONTEXT in bytes + @param SmiHandlerSwContextSize The size of SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT in bytes + + @return SmiHandlerSwContext A pointer to SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT +**/ +SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT * +ConvertSmiHandlerSwContext ( + IN EFI_SMM_SW_REGISTER_CONTEXT *SwContext, + IN UINTN SwContextSize, + OUT UINTN *SmiHandlerSwContextSize + ) +{ + SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT *SmiHandlerSwContext; + + ASSERT (SwContextSize == sizeof(EFI_SMM_SW_REGISTER_CONTEXT)); + + SmiHandlerSwContext = AllocatePool (sizeof (SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT)); + if (SmiHandlerSwContext == NULL) { + *SmiHandlerSwContextSize = 0; + return NULL; + } + SmiHandlerSwContext->SwSmiInputValue = SwContext->SwSmiInputValue; + *SmiHandlerSwContextSize = sizeof (SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT); + return SmiHandlerSwContext; +} + +/** + This function is called by SmmChildDispatcher module to report + a new SMI handler is registered, to SmmCore. + + @param This The protocol instance + @param HandlerGuid The GUID to identify the type of the handler. + For the SmmChildDispatch protocol, the HandlerGuid + must be the GUID of SmmChildDispatch protocol. + @param Handler The SMI handler. + @param CallerAddress The address of the module who registers the SMI handler. + @param Context The context of the SMI handler. + For the SmmChildDispatch protocol, the Context + must match the one defined for SmmChildDispatch protocol. + @param ContextSize The size of the context in bytes. + For the SmmChildDispatch protocol, the Context + must match the one defined for SmmChildDispatch protocol. + + @retval EFI_SUCCESS The information is recorded. + @retval EFI_OUT_OF_RESOURCES There is no enough resource to record the information. +**/ +EFI_STATUS +EFIAPI +SmiHandlerProfileRegisterHandler ( + IN SMI_HANDLER_PROFILE_PROTOCOL *This, + IN EFI_GUID *HandlerGuid, + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN PHYSICAL_ADDRESS CallerAddress, + IN VOID *Context, OPTIONAL + IN UINTN ContextSize OPTIONAL + ) +{ + SMI_HANDLER *SmiHandler; + SMI_ENTRY *SmiEntry; + LIST_ENTRY *List; + + if (((ContextSize == 0) && (Context != NULL)) || + ((ContextSize != 0) && (Context == NULL))) { + return EFI_INVALID_PARAMETER; + } + + SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER)); + if (SmiHandler == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SmiHandler->Signature = SMI_HANDLER_SIGNATURE; + SmiHandler->Handler = Handler; + SmiHandler->CallerAddr = (UINTN)CallerAddress; + SmiHandler->Context = Context; + SmiHandler->ContextSize = ContextSize; + + if (Context != NULL) { + if (CompareGuid (HandlerGuid, &gEfiSmmUsbDispatch2ProtocolGuid)) { + SmiHandler->Context = ConvertSmiHandlerUsbContext (Context, ContextSize, &SmiHandler->ContextSize); + } else if (CompareGuid (HandlerGuid, &gEfiSmmSwDispatch2ProtocolGuid)) { + SmiHandler->Context = ConvertSmiHandlerSwContext (Context, ContextSize, &SmiHandler->ContextSize); + } else { + SmiHandler->Context = AllocateCopyPool (ContextSize, Context); + } + } + if (SmiHandler->Context == NULL) { + SmiHandler->ContextSize = 0; + } + + SmiEntry = SmmCoreFindHardwareSmiEntry (HandlerGuid, TRUE); + if (SmiEntry == NULL) { + if (SmiHandler->Context != NULL) { + FreePool (SmiHandler->Context); + } + FreePool (SmiHandler); + return EFI_OUT_OF_RESOURCES; + } + + List = &SmiEntry->SmiHandlers; + + SmiHandler->SmiEntry = SmiEntry; + InsertTailList (List, &SmiHandler->Link); + + return EFI_SUCCESS; +} + +/** + This function is called by SmmChildDispatcher module to report + an existing SMI handler is unregistered, to SmmCore. + + @param This The protocol instance + @param HandlerGuid The GUID to identify the type of the handler. + For the SmmChildDispatch protocol, the HandlerGuid + must be the GUID of SmmChildDispatch protocol. + @param Handler The SMI handler. + @param Context The context of the SMI handler. + If it is NOT NULL, it will be used to check what is registered. + @param ContextSize The size of the context in bytes. + If Context is NOT NULL, it will be used to check what is registered. + + @retval EFI_SUCCESS The original record is removed. + @retval EFI_NOT_FOUND There is no record for the HandlerGuid and handler. +**/ +EFI_STATUS +EFIAPI +SmiHandlerProfileUnregisterHandler ( + IN SMI_HANDLER_PROFILE_PROTOCOL *This, + IN EFI_GUID *HandlerGuid, + IN EFI_SMM_HANDLER_ENTRY_POINT2 Handler, + IN VOID *Context, OPTIONAL + IN UINTN ContextSize OPTIONAL + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *Head; + SMI_HANDLER *SmiHandler; + SMI_ENTRY *SmiEntry; + SMI_HANDLER *TargetSmiHandler; + VOID *SearchContext; + UINTN SearchContextSize; + + if (((ContextSize == 0) && (Context != NULL)) || + ((ContextSize != 0) && (Context == NULL))) { + return EFI_INVALID_PARAMETER; + } + + SmiEntry = SmmCoreFindHardwareSmiEntry (HandlerGuid, FALSE); + if (SmiEntry == NULL) { + return EFI_NOT_FOUND; + } + + SearchContext = Context; + SearchContextSize = ContextSize; + if (Context != NULL) { + if (CompareGuid (HandlerGuid, &gEfiSmmUsbDispatch2ProtocolGuid)) { + SearchContext = ConvertSmiHandlerUsbContext (Context, ContextSize, &SearchContextSize); + } else if (CompareGuid (HandlerGuid, &gEfiSmmSwDispatch2ProtocolGuid)) { + SearchContext = ConvertSmiHandlerSwContext (Context, ContextSize, &SearchContextSize); + } + } + + TargetSmiHandler = NULL; + Head = &SmiEntry->SmiHandlers; + for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { + SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + if (SmiHandler->Handler == Handler) { + if ((SearchContext == NULL) || + ((SearchContextSize == SmiHandler->ContextSize) && (CompareMem (SearchContext, SmiHandler->Context, SearchContextSize) == 0))) { + TargetSmiHandler = SmiHandler; + break; + } + } + } + + if (SearchContext != NULL) { + if (CompareGuid (HandlerGuid, &gEfiSmmUsbDispatch2ProtocolGuid)) { + FreePool (SearchContext); + } + } + + if (TargetSmiHandler == NULL) { + return EFI_NOT_FOUND; + } + SmiHandler = TargetSmiHandler; + + RemoveEntryList (&SmiHandler->Link); + if (SmiHandler->Context != NULL) { + FreePool (SmiHandler->Context); + } + FreePool (SmiHandler); + + if (IsListEmpty (&SmiEntry->SmiHandlers)) { + RemoveEntryList (&SmiEntry->AllEntries); + FreePool (SmiEntry); + } + + return EFI_SUCCESS; +} + +/** + Initialize SmiHandler profile feature. +**/ +VOID +SmmCoreInitializeSmiHandlerProfile ( + VOID + ) +{ + EFI_STATUS Status; + VOID *Registration; + EFI_HANDLE Handle; + + if ((PcdGet8 (PcdSmiHandlerProfilePropertyMask) & 0x1) != 0) { + InsertTailList (&mRootSmiEntryList, &mRootSmiEntry.AllEntries); + + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmReadyToLockProtocolGuid, + SmmReadyToLockInSmiHandlerProfile, + &Registration + ); + ASSERT_EFI_ERROR (Status); + + Handle = NULL; + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gSmiHandlerProfileGuid, + EFI_NATIVE_INTERFACE, + &mSmiHandlerProfile + ); + ASSERT_EFI_ERROR (Status); + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmramProfileRecord.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmramProfileRecord.c new file mode 100644 index 00000000..c27c6bf4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmramProfileRecord.c @@ -0,0 +1,2817 @@ +/** @file + Support routines for SMRAM profile. + + Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCore.h" + +#define IS_SMRAM_PROFILE_ENABLED ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT1) != 0) +#define IS_UEFI_MEMORY_PROFILE_ENABLED ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) + +#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \ + ((ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1))) + +typedef struct { + UINT32 Signature; + MEMORY_PROFILE_CONTEXT Context; + LIST_ENTRY *DriverInfoList; +} MEMORY_PROFILE_CONTEXT_DATA; + +typedef struct { + UINT32 Signature; + MEMORY_PROFILE_DRIVER_INFO DriverInfo; + LIST_ENTRY *AllocInfoList; + CHAR8 *PdbString; + LIST_ENTRY Link; +} MEMORY_PROFILE_DRIVER_INFO_DATA; + +typedef struct { + UINT32 Signature; + MEMORY_PROFILE_ALLOC_INFO AllocInfo; + CHAR8 *ActionString; + LIST_ENTRY Link; +} MEMORY_PROFILE_ALLOC_INFO_DATA; + +// +// When free memory less than 4 pages, dump it. +// +#define SMRAM_INFO_DUMP_PAGE_THRESHOLD 4 + +GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_FREE_MEMORY mSmramFreeMemory = { + { + MEMORY_PROFILE_FREE_MEMORY_SIGNATURE, + sizeof (MEMORY_PROFILE_FREE_MEMORY), + MEMORY_PROFILE_FREE_MEMORY_REVISION + }, + 0, + 0 +}; + +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mImageQueue = INITIALIZE_LIST_HEAD_VARIABLE (mImageQueue); +GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA mSmramProfileContext = { + MEMORY_PROFILE_CONTEXT_SIGNATURE, + { + { + MEMORY_PROFILE_CONTEXT_SIGNATURE, + sizeof (MEMORY_PROFILE_CONTEXT), + MEMORY_PROFILE_CONTEXT_REVISION + }, + 0, + 0, + {0}, + {0}, + 0, + 0, + 0 + }, + &mImageQueue, +}; +GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA *mSmramProfileContextPtr = NULL; + +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mSmramReadyToLock; +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mSmramProfileGettingStatus = FALSE; +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mSmramProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_DEVICE_PATH_PROTOCOL *mSmramProfileDriverPath; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mSmramProfileDriverPathSize; + +/** + Dump SMRAM information. + +**/ +VOID +DumpSmramInfo ( + VOID + ); + +/** + Get memory profile data. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer. + On return, points to the size of the data returned in ProfileBuffer. + @param[out] ProfileBuffer Profile buffer. + + @return EFI_SUCCESS Get the memory profile data successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data. + ProfileSize is updated with the size required. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolGetData ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN OUT UINT64 *ProfileSize, + OUT VOID *ProfileBuffer + ); + +/** + Register image to memory profile. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + @param[in] FileType File type of the image. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCE No enough resource for this register. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolRegisterImage ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN EFI_FV_FILETYPE FileType + ); + +/** + Unregister image from memory profile. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolUnregisterImage ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize + ); + +/** + Get memory profile recording state. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[out] RecordingState Recording state. + + @return EFI_SUCCESS Memory profile recording state is returned. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_INVALID_PARAMETER RecordingState is NULL. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolGetRecordingState ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + OUT BOOLEAN *RecordingState + ); + +/** + Set memory profile recording state. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] RecordingState Recording state. + + @return EFI_SUCCESS Set memory profile recording state successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolSetRecordingState ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN BOOLEAN RecordingState + ); + +/** + Record memory profile of multilevel caller. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] CallerAddress Address of caller. + @param[in] Action Memory profile action. + @param[in] MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param[in] Buffer Buffer address. + @param[in] Size Buffer size. + @param[in] ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolRecord ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN VOID *Buffer, + IN UINTN Size, + IN CHAR8 *ActionString OPTIONAL + ); + +GLOBAL_REMOVE_IF_UNREFERENCED EDKII_SMM_MEMORY_PROFILE_PROTOCOL mSmmProfileProtocol = { + SmramProfileProtocolGetData, + SmramProfileProtocolRegisterImage, + SmramProfileProtocolUnregisterImage, + SmramProfileProtocolGetRecordingState, + SmramProfileProtocolSetRecordingState, + SmramProfileProtocolRecord, +}; + +/** + Return SMRAM profile context. + + @return SMRAM profile context. + +**/ +MEMORY_PROFILE_CONTEXT_DATA * +GetSmramProfileContext ( + VOID + ) +{ + return mSmramProfileContextPtr; +} + +/** + Retrieves and returns the Subsystem of a PE/COFF image that has been loaded into system memory. + If Pe32Data is NULL, then ASSERT(). + + @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. + + @return The Subsystem of the PE/COFF image. + +**/ +UINT16 +InternalPeCoffGetSubsystem ( + IN VOID *Pe32Data + ) +{ + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT16 Magic; + + ASSERT (Pe32Data != NULL); + + DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) ((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); + } else { + // + // DOS image header is not present, so PE header is at the image base. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) Pe32Data; + } + + if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + return Hdr.Te->Subsystem; + } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + Magic = Hdr.Pe32->OptionalHeader.Magic; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return Hdr.Pe32->OptionalHeader.Subsystem; + } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + return Hdr.Pe32Plus->OptionalHeader.Subsystem; + } + } + + return 0x0000; +} + +/** + Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded + into system memory with the PE/COFF Loader Library functions. + + Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry + point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then + return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS. + If Pe32Data is NULL, then ASSERT(). + If EntryPoint is NULL, then ASSERT(). + + @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. + @param EntryPoint The pointer to entry point to the PE/COFF image to return. + + @retval RETURN_SUCCESS EntryPoint was returned. + @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image. + +**/ +RETURN_STATUS +InternalPeCoffGetEntryPoint ( + IN VOID *Pe32Data, + OUT VOID **EntryPoint + ) +{ + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + + ASSERT (Pe32Data != NULL); + ASSERT (EntryPoint != NULL); + + DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) ((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); + } else { + // + // DOS image header is not present, so PE header is at the image base. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) Pe32Data; + } + + // + // Calculate the entry point relative to the start of the image. + // AddressOfEntryPoint is common for PE32 & PE32+ + // + if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + *EntryPoint = (VOID *) ((UINTN) Pe32Data + (UINTN) (Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize); + return RETURN_SUCCESS; + } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + *EntryPoint = (VOID *) ((UINTN) Pe32Data + (UINTN) (Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff)); + return RETURN_SUCCESS; + } + + return RETURN_UNSUPPORTED; +} + +/** + Build driver info. + + @param ContextData Memory profile context. + @param FileName File name of the image. + @param ImageBase Image base address. + @param ImageSize Image size. + @param EntryPoint Entry point of the image. + @param ImageSubsystem Image subsystem of the image. + @param FileType File type of the image. + + @return Pointer to memory profile driver info. + +**/ +MEMORY_PROFILE_DRIVER_INFO_DATA * +BuildDriverInfo ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData, + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN PHYSICAL_ADDRESS EntryPoint, + IN UINT16 ImageSubsystem, + IN EFI_FV_FILETYPE FileType + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + VOID *EntryPointInImage; + CHAR8 *PdbString; + UINTN PdbSize; + UINTN PdbOccupiedSize; + + PdbSize = 0; + PdbOccupiedSize = 0; + PdbString = NULL; + if (ImageBase != 0) { + PdbString = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageBase); + if (PdbString != NULL) { + PdbSize = AsciiStrSize (PdbString); + PdbOccupiedSize = GET_OCCUPIED_SIZE (PdbSize, sizeof (UINT64)); + } + } + + // + // Use SmmInternalAllocatePool() that will not update profile for this AllocatePool action. + // + Status = SmmInternalAllocatePool ( + EfiRuntimeServicesData, + sizeof (*DriverInfoData) + sizeof (LIST_ENTRY) + PdbSize, + (VOID **) &DriverInfoData + ); + if (EFI_ERROR (Status)) { + return NULL; + } + ASSERT (DriverInfoData != NULL); + + ZeroMem (DriverInfoData, sizeof (*DriverInfoData)); + + DriverInfo = &DriverInfoData->DriverInfo; + DriverInfoData->Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE; + DriverInfo->Header.Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE; + DriverInfo->Header.Length = (UINT16) (sizeof (MEMORY_PROFILE_DRIVER_INFO) + PdbOccupiedSize); + DriverInfo->Header.Revision = MEMORY_PROFILE_DRIVER_INFO_REVISION; + if (FileName != NULL) { + CopyMem (&DriverInfo->FileName, FileName, sizeof (EFI_GUID)); + } + DriverInfo->ImageBase = ImageBase; + DriverInfo->ImageSize = ImageSize; + DriverInfo->EntryPoint = EntryPoint; + DriverInfo->ImageSubsystem = ImageSubsystem; + if ((EntryPoint != 0) && ((EntryPoint < ImageBase) || (EntryPoint >= (ImageBase + ImageSize)))) { + // + // If the EntryPoint is not in the range of image buffer, it should come from emulation environment. + // So patch ImageBuffer here to align the EntryPoint. + // + Status = InternalPeCoffGetEntryPoint ((VOID *) (UINTN) ImageBase, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverInfo->ImageBase = ImageBase + EntryPoint - (PHYSICAL_ADDRESS) (UINTN) EntryPointInImage; + } + DriverInfo->FileType = FileType; + DriverInfoData->AllocInfoList = (LIST_ENTRY *) (DriverInfoData + 1); + InitializeListHead (DriverInfoData->AllocInfoList); + DriverInfo->CurrentUsage = 0; + DriverInfo->PeakUsage = 0; + DriverInfo->AllocRecordCount = 0; + if (PdbSize != 0) { + DriverInfo->PdbStringOffset = (UINT16) sizeof (MEMORY_PROFILE_DRIVER_INFO); + DriverInfoData->PdbString = (CHAR8 *) (DriverInfoData->AllocInfoList + 1); + CopyMem (DriverInfoData->PdbString, PdbString, PdbSize); + } else { + DriverInfo->PdbStringOffset = 0; + DriverInfoData->PdbString = NULL; + } + + InsertTailList (ContextData->DriverInfoList, &DriverInfoData->Link); + ContextData->Context.ImageCount ++; + ContextData->Context.TotalImageSize += DriverInfo->ImageSize; + + return DriverInfoData; +} + +/** + Register image to DXE. + + @param FileName File name of the image. + @param ImageBase Image base address. + @param ImageSize Image size. + @param FileType File type of the image. + +**/ +VOID +RegisterImageToDxe ( + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN EFI_FV_FILETYPE FileType + ) +{ + EFI_STATUS Status; + EDKII_MEMORY_PROFILE_PROTOCOL *ProfileProtocol; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)]; + + if (IS_UEFI_MEMORY_PROFILE_ENABLED) { + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer; + Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **) &ProfileProtocol); + if (!EFI_ERROR (Status)) { + EfiInitializeFwVolDevicepathNode (FilePath, FileName); + SetDevicePathEndNode (FilePath + 1); + + Status = ProfileProtocol->RegisterImage ( + ProfileProtocol, + (EFI_DEVICE_PATH_PROTOCOL *) FilePath, + ImageBase, + ImageSize, + FileType + ); + } + } +} + +/** + Unregister image from DXE. + + @param FileName File name of the image. + @param ImageBase Image base address. + @param ImageSize Image size. + +**/ +VOID +UnregisterImageFromDxe ( + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize + ) +{ + EFI_STATUS Status; + EDKII_MEMORY_PROFILE_PROTOCOL *ProfileProtocol; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)]; + + if (IS_UEFI_MEMORY_PROFILE_ENABLED) { + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer; + Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID *) &ProfileProtocol); + if (!EFI_ERROR (Status)) { + EfiInitializeFwVolDevicepathNode (FilePath, FileName); + SetDevicePathEndNode (FilePath + 1); + + Status = ProfileProtocol->UnregisterImage ( + ProfileProtocol, + (EFI_DEVICE_PATH_PROTOCOL *) FilePath, + ImageBase, + ImageSize + ); + } + } +} + +/** + Return if record for this driver is needed.. + + @param DriverFilePath Driver file path. + + @retval TRUE Record for this driver is needed. + @retval FALSE Record for this driver is not needed. + +**/ +BOOLEAN +NeedRecordThisDriver ( + IN EFI_DEVICE_PATH_PROTOCOL *DriverFilePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance; + UINTN DevicePathSize; + UINTN FilePathSize; + + if (!IsDevicePathValid (mSmramProfileDriverPath, mSmramProfileDriverPathSize)) { + // + // Invalid Device Path means record all. + // + return TRUE; + } + + // + // Record FilePath without end node. + // + FilePathSize = GetDevicePathSize (DriverFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); + + DevicePathInstance = mSmramProfileDriverPath; + do { + // + // Find End node (it might be END_ENTIRE or END_INSTANCE) + // + TmpDevicePath = DevicePathInstance; + while (!IsDevicePathEndType (TmpDevicePath)) { + TmpDevicePath = NextDevicePathNode (TmpDevicePath); + } + + // + // Do not compare END node + // + DevicePathSize = (UINTN)TmpDevicePath - (UINTN)DevicePathInstance; + if ((FilePathSize == DevicePathSize) && + (CompareMem (DriverFilePath, DevicePathInstance, DevicePathSize) == 0)) { + return TRUE; + } + + // + // Get next instance + // + DevicePathInstance = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)DevicePathInstance + DevicePathSize + DevicePathNodeLength(TmpDevicePath)); + } while (DevicePathSubType (TmpDevicePath) != END_ENTIRE_DEVICE_PATH_SUBTYPE); + + return FALSE; +} + +/** + Register SMM Core to SMRAM profile. + + @param ContextData SMRAM profile context. + + @retval TRUE Register success. + @retval FALSE Register fail. + +**/ +BOOLEAN +RegisterSmmCore ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData + ) +{ + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + PHYSICAL_ADDRESS ImageBase; + UINT8 TempBuffer[sizeof(MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof(EFI_DEVICE_PATH_PROTOCOL)]; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) TempBuffer; + EfiInitializeFwVolDevicepathNode (FilePath, &gEfiCallerIdGuid); + SetDevicePathEndNode (FilePath + 1); + + if (!NeedRecordThisDriver ((EFI_DEVICE_PATH_PROTOCOL *) FilePath)) { + return FALSE; + } + + ImageBase = gSmmCorePrivate->PiSmmCoreImageBase; + DriverInfoData = BuildDriverInfo ( + ContextData, + &gEfiCallerIdGuid, + ImageBase, + gSmmCorePrivate->PiSmmCoreImageSize, + gSmmCorePrivate->PiSmmCoreEntryPoint, + InternalPeCoffGetSubsystem ((VOID *) (UINTN) ImageBase), + EFI_FV_FILETYPE_SMM_CORE + ); + if (DriverInfoData == NULL) { + return FALSE; + } + + return TRUE; +} + +/** + Initialize SMRAM profile. + +**/ +VOID +SmramProfileInit ( + VOID + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *SmramProfileContext; + + RegisterImageToDxe ( + &gEfiCallerIdGuid, + gSmmCorePrivate->PiSmmCoreImageBase, + gSmmCorePrivate->PiSmmCoreImageSize, + EFI_FV_FILETYPE_SMM_CORE + ); + + if (!IS_SMRAM_PROFILE_ENABLED) { + return; + } + + SmramProfileContext = GetSmramProfileContext (); + if (SmramProfileContext != NULL) { + return; + } + + mSmramProfileGettingStatus = FALSE; + if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT7) != 0) { + mSmramProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE; + } else { + mSmramProfileRecordingEnable = MEMORY_PROFILE_RECORDING_ENABLE; + } + mSmramProfileDriverPathSize = PcdGetSize (PcdMemoryProfileDriverPath); + mSmramProfileDriverPath = AllocateCopyPool (mSmramProfileDriverPathSize, PcdGetPtr (PcdMemoryProfileDriverPath)); + mSmramProfileContextPtr = &mSmramProfileContext; + + RegisterSmmCore (&mSmramProfileContext); + + DEBUG ((EFI_D_INFO, "SmramProfileInit SmramProfileContext - 0x%x\n", &mSmramProfileContext)); +} + +/** + Install SMRAM profile protocol. + +**/ +VOID +SmramProfileInstallProtocol ( + VOID + ) +{ + EFI_HANDLE Handle; + EFI_STATUS Status; + + if (!IS_SMRAM_PROFILE_ENABLED) { + return; + } + + Handle = NULL; + Status = SmmInstallProtocolInterface ( + &Handle, + &gEdkiiSmmMemoryProfileGuid, + EFI_NATIVE_INTERFACE, + &mSmmProfileProtocol + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Get the GUID file name from the file path. + + @param FilePath File path. + + @return The GUID file name from the file path. + +**/ +EFI_GUID * +GetFileNameFromFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *ThisFilePath; + EFI_GUID *FileName; + + FileName = NULL; + if (FilePath != NULL) { + ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) FilePath; + while (!IsDevicePathEnd (ThisFilePath)) { + FileName = EfiGetNameGuidFromFwVolDevicePathNode (ThisFilePath); + if (FileName != NULL) { + break; + } + ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) NextDevicePathNode (ThisFilePath); + } + } + + return FileName; +} + +/** + Register SMM image to SMRAM profile. + + @param DriverEntry SMM image info. + @param RegisterToDxe Register image to DXE. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource for this register. + +**/ +EFI_STATUS +RegisterSmramProfileImage ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry, + IN BOOLEAN RegisterToDxe + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + UINT8 TempBuffer[sizeof(MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof(EFI_DEVICE_PATH_PROTOCOL)]; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + + if (RegisterToDxe) { + RegisterImageToDxe ( + &DriverEntry->FileName, + DriverEntry->ImageBuffer, + EFI_PAGES_TO_SIZE (DriverEntry->NumberOfPage), + EFI_FV_FILETYPE_SMM + ); + } + + if (!IS_SMRAM_PROFILE_ENABLED) { + return EFI_UNSUPPORTED; + } + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) TempBuffer; + EfiInitializeFwVolDevicepathNode (FilePath, &DriverEntry->FileName); + SetDevicePathEndNode (FilePath + 1); + + if (!NeedRecordThisDriver ((EFI_DEVICE_PATH_PROTOCOL *) FilePath)) { + return EFI_UNSUPPORTED; + } + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = BuildDriverInfo ( + ContextData, + &DriverEntry->FileName, + DriverEntry->ImageBuffer, + EFI_PAGES_TO_SIZE (DriverEntry->NumberOfPage), + DriverEntry->ImageEntryPoint, + InternalPeCoffGetSubsystem ((VOID *) (UINTN) DriverEntry->ImageBuffer), + EFI_FV_FILETYPE_SMM + ); + if (DriverInfoData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Search image from memory profile. + + @param ContextData Memory profile context. + @param FileName Image file name. + @param Address Image Address. + + @return Pointer to memory profile driver info. + +**/ +MEMORY_PROFILE_DRIVER_INFO_DATA * +GetMemoryProfileDriverInfoByFileNameAndAddress ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData, + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS Address + ) +{ + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + LIST_ENTRY *DriverLink; + LIST_ENTRY *DriverInfoList; + + DriverInfoList = ContextData->DriverInfoList; + + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + DriverInfo = &DriverInfoData->DriverInfo; + if ((CompareGuid (&DriverInfo->FileName, FileName)) && + (Address >= DriverInfo->ImageBase) && + (Address < (DriverInfo->ImageBase + DriverInfo->ImageSize))) { + return DriverInfoData; + } + } + + return NULL; +} + +/** + Search image from memory profile. + It will return image, if (Address >= ImageBuffer) AND (Address < ImageBuffer + ImageSize) + + @param ContextData Memory profile context. + @param Address Image or Function address. + + @return Pointer to memory profile driver info. + +**/ +MEMORY_PROFILE_DRIVER_INFO_DATA * +GetMemoryProfileDriverInfoFromAddress ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData, + IN PHYSICAL_ADDRESS Address + ) +{ + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + LIST_ENTRY *DriverLink; + LIST_ENTRY *DriverInfoList; + + DriverInfoList = ContextData->DriverInfoList; + + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + DriverInfo = &DriverInfoData->DriverInfo; + if ((Address >= DriverInfo->ImageBase) && + (Address < (DriverInfo->ImageBase + DriverInfo->ImageSize))) { + return DriverInfoData; + } + } + + return NULL; +} + +/** + Unregister image from SMRAM profile. + + @param DriverEntry SMM image info. + @param UnregisterFromDxe Unregister image from DXE. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +UnregisterSmramProfileImage ( + IN EFI_SMM_DRIVER_ENTRY *DriverEntry, + IN BOOLEAN UnregisterFromDxe + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + EFI_GUID *FileName; + PHYSICAL_ADDRESS ImageAddress; + VOID *EntryPointInImage; + UINT8 TempBuffer[sizeof(MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof(EFI_DEVICE_PATH_PROTOCOL)]; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + + if (UnregisterFromDxe) { + UnregisterImageFromDxe ( + &DriverEntry->FileName, + DriverEntry->ImageBuffer, + EFI_PAGES_TO_SIZE (DriverEntry->NumberOfPage) + ); + } + + if (!IS_SMRAM_PROFILE_ENABLED) { + return EFI_UNSUPPORTED; + } + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) TempBuffer; + EfiInitializeFwVolDevicepathNode (FilePath, &DriverEntry->FileName); + SetDevicePathEndNode (FilePath + 1); + + if (!NeedRecordThisDriver ((EFI_DEVICE_PATH_PROTOCOL *) FilePath)) { + return EFI_UNSUPPORTED; + } + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = NULL; + FileName = &DriverEntry->FileName; + ImageAddress = DriverEntry->ImageBuffer; + if ((DriverEntry->ImageEntryPoint < ImageAddress) || (DriverEntry->ImageEntryPoint >= (ImageAddress + EFI_PAGES_TO_SIZE (DriverEntry->NumberOfPage)))) { + // + // If the EntryPoint is not in the range of image buffer, it should come from emulation environment. + // So patch ImageAddress here to align the EntryPoint. + // + Status = InternalPeCoffGetEntryPoint ((VOID *) (UINTN) ImageAddress, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + ImageAddress = ImageAddress + (UINTN) DriverEntry->ImageEntryPoint - (UINTN) EntryPointInImage; + } + if (FileName != NULL) { + DriverInfoData = GetMemoryProfileDriverInfoByFileNameAndAddress (ContextData, FileName, ImageAddress); + } + if (DriverInfoData == NULL) { + DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, ImageAddress); + } + if (DriverInfoData == NULL) { + return EFI_NOT_FOUND; + } + + ContextData->Context.TotalImageSize -= DriverInfoData->DriverInfo.ImageSize; + + // Keep the ImageBase for RVA calculation in Application. + //DriverInfoData->DriverInfo.ImageBase = 0; + DriverInfoData->DriverInfo.ImageSize = 0; + + if (DriverInfoData->DriverInfo.PeakUsage == 0) { + ContextData->Context.ImageCount --; + RemoveEntryList (&DriverInfoData->Link); + // + // Use SmmInternalFreePool() that will not update profile for this FreePool action. + // + SmmInternalFreePool (DriverInfoData); + } + + return EFI_SUCCESS; +} + +/** + Return if this memory type needs to be recorded into memory profile. + Only need to record EfiRuntimeServicesCode and EfiRuntimeServicesData for SMRAM profile. + + @param MemoryType Memory type. + + @retval TRUE This memory type need to be recorded. + @retval FALSE This memory type need not to be recorded. + +**/ +BOOLEAN +SmmCoreNeedRecordProfile ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + UINT64 TestBit; + + if (MemoryType != EfiRuntimeServicesCode && + MemoryType != EfiRuntimeServicesData) { + return FALSE; + } + + TestBit = LShiftU64 (1, MemoryType); + + if ((PcdGet64 (PcdMemoryProfileMemoryType) & TestBit) != 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Convert EFI memory type to profile memory index. The rule is: + If BIOS memory type (0 ~ EfiMaxMemoryType - 1), ProfileMemoryIndex = MemoryType. + As SMRAM profile is only to record EfiRuntimeServicesCode and EfiRuntimeServicesData, + so return input memory type directly. + + @param MemoryType Memory type. + + @return EFI memory type as profile memory index. + +**/ +EFI_MEMORY_TYPE +GetProfileMemoryIndex ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + return MemoryType; +} + +/** + Update SMRAM profile FreeMemoryPages information + + @param ContextData Memory profile context. + +**/ +VOID +SmramProfileUpdateFreePages ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData + ) +{ + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + LIST_ENTRY *FreePageList; + UINTN NumberOfPages; + + NumberOfPages = 0; + FreePageList = &mSmmMemoryMap; + for (Node = FreePageList->BackLink; + Node != FreePageList; + Node = Node->BackLink) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + NumberOfPages += Pages->NumberOfPages; + } + + mSmramFreeMemory.TotalFreeMemoryPages = NumberOfPages; + + if (NumberOfPages <= SMRAM_INFO_DUMP_PAGE_THRESHOLD) { + DumpSmramInfo (); + } +} + +/** + Update SMRAM profile Allocate information. + + @param CallerAddress Address of caller who call Allocate. + @param Action This Allocate action. + @param MemoryType Memory type. + @param Size Buffer size. + @param Buffer Buffer address. + @param ActionString String for memory profile action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + +**/ +EFI_STATUS +SmmCoreUpdateProfileAllocate ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Size, + IN VOID *Buffer, + IN CHAR8 *ActionString OPTIONAL + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + EFI_MEMORY_TYPE ProfileMemoryIndex; + MEMORY_PROFILE_ACTION BasicAction; + UINTN ActionStringSize; + UINTN ActionStringOccupiedSize; + + BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress); + if (DriverInfoData == NULL) { + return EFI_UNSUPPORTED; + } + + ActionStringSize = 0; + ActionStringOccupiedSize = 0; + if (ActionString != NULL) { + ActionStringSize = AsciiStrSize (ActionString); + ActionStringOccupiedSize = GET_OCCUPIED_SIZE (ActionStringSize, sizeof (UINT64)); + } + + // + // Use SmmInternalAllocatePool() that will not update profile for this AllocatePool action. + // + AllocInfoData = NULL; + Status = SmmInternalAllocatePool ( + EfiRuntimeServicesData, + sizeof (*AllocInfoData) + ActionStringSize, + (VOID **) &AllocInfoData + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + ASSERT (AllocInfoData != NULL); + + // + // Only update SequenceCount if and only if it is basic action. + // + if (Action == BasicAction) { + ContextData->Context.SequenceCount ++; + } + + AllocInfo = &AllocInfoData->AllocInfo; + AllocInfoData->Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE; + AllocInfo->Header.Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE; + AllocInfo->Header.Length = (UINT16) (sizeof (MEMORY_PROFILE_ALLOC_INFO) + ActionStringOccupiedSize); + AllocInfo->Header.Revision = MEMORY_PROFILE_ALLOC_INFO_REVISION; + AllocInfo->CallerAddress = CallerAddress; + AllocInfo->SequenceId = ContextData->Context.SequenceCount; + AllocInfo->Action = Action; + AllocInfo->MemoryType = MemoryType; + AllocInfo->Buffer = (PHYSICAL_ADDRESS) (UINTN) Buffer; + AllocInfo->Size = Size; + if (ActionString != NULL) { + AllocInfo->ActionStringOffset = (UINT16) sizeof (MEMORY_PROFILE_ALLOC_INFO); + AllocInfoData->ActionString = (CHAR8 *) (AllocInfoData + 1); + CopyMem (AllocInfoData->ActionString, ActionString, ActionStringSize); + } else { + AllocInfo->ActionStringOffset = 0; + AllocInfoData->ActionString = NULL; + } + + InsertTailList (DriverInfoData->AllocInfoList, &AllocInfoData->Link); + + Context = &ContextData->Context; + DriverInfo = &DriverInfoData->DriverInfo; + DriverInfo->AllocRecordCount ++; + + // + // Update summary if and only if it is basic action. + // + if (Action == BasicAction) { + ProfileMemoryIndex = GetProfileMemoryIndex (MemoryType); + + DriverInfo->CurrentUsage += Size; + if (DriverInfo->PeakUsage < DriverInfo->CurrentUsage) { + DriverInfo->PeakUsage = DriverInfo->CurrentUsage; + } + DriverInfo->CurrentUsageByType[ProfileMemoryIndex] += Size; + if (DriverInfo->PeakUsageByType[ProfileMemoryIndex] < DriverInfo->CurrentUsageByType[ProfileMemoryIndex]) { + DriverInfo->PeakUsageByType[ProfileMemoryIndex] = DriverInfo->CurrentUsageByType[ProfileMemoryIndex]; + } + + Context->CurrentTotalUsage += Size; + if (Context->PeakTotalUsage < Context->CurrentTotalUsage) { + Context->PeakTotalUsage = Context->CurrentTotalUsage; + } + Context->CurrentTotalUsageByType[ProfileMemoryIndex] += Size; + if (Context->PeakTotalUsageByType[ProfileMemoryIndex] < Context->CurrentTotalUsageByType[ProfileMemoryIndex]) { + Context->PeakTotalUsageByType[ProfileMemoryIndex] = Context->CurrentTotalUsageByType[ProfileMemoryIndex]; + } + + SmramProfileUpdateFreePages (ContextData); + } + + return EFI_SUCCESS; +} + +/** + Get memory profile alloc info from memory profile + + @param DriverInfoData Driver info + @param BasicAction This Free basic action + @param Size Buffer size + @param Buffer Buffer address + + @return Pointer to memory profile alloc info. +**/ +MEMORY_PROFILE_ALLOC_INFO_DATA * +GetMemoryProfileAllocInfoFromAddress ( + IN MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData, + IN MEMORY_PROFILE_ACTION BasicAction, + IN UINTN Size, + IN VOID *Buffer + ) +{ + LIST_ENTRY *AllocInfoList; + LIST_ENTRY *AllocLink; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + + AllocInfoList = DriverInfoData->AllocInfoList; + + for (AllocLink = AllocInfoList->ForwardLink; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink) { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + AllocInfo = &AllocInfoData->AllocInfo; + if ((AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK) != BasicAction) { + continue; + } + switch (BasicAction) { + case MemoryProfileActionAllocatePages: + if ((AllocInfo->Buffer <= (PHYSICAL_ADDRESS) (UINTN) Buffer) && + ((AllocInfo->Buffer + AllocInfo->Size) >= ((PHYSICAL_ADDRESS) (UINTN) Buffer + Size))) { + return AllocInfoData; + } + break; + case MemoryProfileActionAllocatePool: + if (AllocInfo->Buffer == (PHYSICAL_ADDRESS) (UINTN) Buffer) { + return AllocInfoData; + } + break; + default: + ASSERT (FALSE); + break; + } + } + + return NULL; +} + +/** + Update SMRAM profile Free information. + + @param CallerAddress Address of caller who call Free. + @param Action This Free action. + @param Size Buffer size. + @param Buffer Buffer address. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +SmmCoreUpdateProfileFree ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN UINTN Size, + IN VOID *Buffer + ) +{ + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + LIST_ENTRY *DriverLink; + LIST_ENTRY *DriverInfoList; + MEMORY_PROFILE_DRIVER_INFO_DATA *ThisDriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + EFI_MEMORY_TYPE ProfileMemoryIndex; + MEMORY_PROFILE_ACTION BasicAction; + BOOLEAN Found; + + BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress); + + // + // Do not return if DriverInfoData == NULL here, + // because driver A might free memory allocated by driver B. + // + + // + // Need use do-while loop to find all possible record, + // because one address might be recorded multiple times. + // + Found = FALSE; + AllocInfoData = NULL; + do { + if (DriverInfoData != NULL) { + switch (BasicAction) { + case MemoryProfileActionFreePages: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer); + break; + case MemoryProfileActionFreePool: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer); + break; + default: + ASSERT (FALSE); + AllocInfoData = NULL; + break; + } + } + if (AllocInfoData == NULL) { + // + // Legal case, because driver A might free memory allocated by driver B, by some protocol. + // + DriverInfoList = ContextData->DriverInfoList; + + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) { + ThisDriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + switch (BasicAction) { + case MemoryProfileActionFreePages: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer); + break; + case MemoryProfileActionFreePool: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer); + break; + default: + ASSERT (FALSE); + AllocInfoData = NULL; + break; + } + if (AllocInfoData != NULL) { + DriverInfoData = ThisDriverInfoData; + break; + } + } + + if (AllocInfoData == NULL) { + // + // If (!Found), no matched allocate info is found for this free action. + // It is because the specified memory type allocate actions have been filtered by + // CoreNeedRecordProfile(), but free actions have no memory type information, + // they can not be filtered by CoreNeedRecordProfile(). Then, they will be + // filtered here. + // + // If (Found), it is normal exit path. + return (Found ? EFI_SUCCESS : EFI_NOT_FOUND); + } + } + + ASSERT (DriverInfoData != NULL); + ASSERT (AllocInfoData != NULL); + + Found = TRUE; + + Context = &ContextData->Context; + DriverInfo = &DriverInfoData->DriverInfo; + AllocInfo = &AllocInfoData->AllocInfo; + + DriverInfo->AllocRecordCount --; + // + // Update summary if and only if it is basic action. + // + if (AllocInfo->Action == (AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK)) { + ProfileMemoryIndex = GetProfileMemoryIndex (AllocInfo->MemoryType); + + Context->CurrentTotalUsage -= AllocInfo->Size; + Context->CurrentTotalUsageByType[ProfileMemoryIndex] -= AllocInfo->Size; + + DriverInfo->CurrentUsage -= AllocInfo->Size; + DriverInfo->CurrentUsageByType[ProfileMemoryIndex] -= AllocInfo->Size; + } + + RemoveEntryList (&AllocInfoData->Link); + + if (BasicAction == MemoryProfileActionFreePages) { + if (AllocInfo->Buffer != (PHYSICAL_ADDRESS) (UINTN) Buffer) { + SmmCoreUpdateProfileAllocate ( + AllocInfo->CallerAddress, + AllocInfo->Action, + AllocInfo->MemoryType, + (UINTN) ((PHYSICAL_ADDRESS) (UINTN) Buffer - AllocInfo->Buffer), + (VOID *) (UINTN) AllocInfo->Buffer, + AllocInfoData->ActionString + ); + } + if (AllocInfo->Buffer + AllocInfo->Size != ((PHYSICAL_ADDRESS) (UINTN) Buffer + Size)) { + SmmCoreUpdateProfileAllocate ( + AllocInfo->CallerAddress, + AllocInfo->Action, + AllocInfo->MemoryType, + (UINTN) ((AllocInfo->Buffer + AllocInfo->Size) - ((PHYSICAL_ADDRESS) (UINTN) Buffer + Size)), + (VOID *) ((UINTN) Buffer + Size), + AllocInfoData->ActionString + ); + } + } + + // + // Use SmmInternalFreePool() that will not update profile for this FreePool action. + // + SmmInternalFreePool (AllocInfoData); + } while (TRUE); +} + +/** + Update SMRAM profile information. + + @param CallerAddress Address of caller who call Allocate or Free. + @param Action This Allocate or Free action. + @param MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param Size Buffer size. + @param Buffer Buffer address. + @param ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +SmmCoreUpdateProfile ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, // Valid for AllocatePages/AllocatePool + IN UINTN Size, // Valid for AllocatePages/FreePages/AllocatePool + IN VOID *Buffer, + IN CHAR8 *ActionString OPTIONAL + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_ACTION BasicAction; + + if (!IS_SMRAM_PROFILE_ENABLED) { + return EFI_UNSUPPORTED; + } + + if (mSmramProfileGettingStatus) { + return EFI_ACCESS_DENIED; + } + + if (!mSmramProfileRecordingEnable) { + return EFI_ABORTED; + } + + // + // Get the basic action to know how to process the record + // + BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK; + + // + // Free operations have no memory type information, so skip the check. + // + if ((BasicAction == MemoryProfileActionAllocatePages) || (BasicAction == MemoryProfileActionAllocatePool)) { + // + // Only record limited MemoryType. + // + if (!SmmCoreNeedRecordProfile (MemoryType)) { + return EFI_UNSUPPORTED; + } + } + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + switch (BasicAction) { + case MemoryProfileActionAllocatePages: + Status = SmmCoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString); + break; + case MemoryProfileActionFreePages: + Status = SmmCoreUpdateProfileFree (CallerAddress, Action, Size, Buffer); + break; + case MemoryProfileActionAllocatePool: + Status = SmmCoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString); + break; + case MemoryProfileActionFreePool: + Status = SmmCoreUpdateProfileFree (CallerAddress, Action, 0, Buffer); + break; + default: + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + break; + } + + return Status; +} + +/** + SMRAM profile ready to lock callback function. + +**/ +VOID +SmramProfileReadyToLock ( + VOID + ) +{ + if (!IS_SMRAM_PROFILE_ENABLED) { + return; + } + + DEBUG ((EFI_D_INFO, "SmramProfileReadyToLock\n")); + mSmramReadyToLock = TRUE; +} + +//////////////////// + +/** + Get SMRAM profile data size. + + @return SMRAM profile data size. + +**/ +UINTN +SmramProfileGetDataSize ( + VOID + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + LIST_ENTRY *DriverInfoList; + LIST_ENTRY *DriverLink; + LIST_ENTRY *AllocInfoList; + LIST_ENTRY *AllocLink; + UINTN TotalSize; + LIST_ENTRY *Node; + LIST_ENTRY *FreePageList; + LIST_ENTRY *FreePoolList; + FREE_POOL_HEADER *Pool; + UINTN PoolListIndex; + UINTN Index; + UINTN SmmPoolTypeIndex; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return 0; + } + + TotalSize = sizeof (MEMORY_PROFILE_CONTEXT); + + DriverInfoList = ContextData->DriverInfoList; + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + TotalSize += DriverInfoData->DriverInfo.Header.Length; + + AllocInfoList = DriverInfoData->AllocInfoList; + for (AllocLink = AllocInfoList->ForwardLink; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink) { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + TotalSize += AllocInfoData->AllocInfo.Header.Length; + } + } + + + Index = 0; + FreePageList = &mSmmMemoryMap; + for (Node = FreePageList->BackLink; + Node != FreePageList; + Node = Node->BackLink) { + Index++; + } + for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) { + for (PoolListIndex = 0; PoolListIndex < MAX_POOL_INDEX; PoolListIndex++) { + FreePoolList = &mSmmPoolLists[SmmPoolTypeIndex][PoolListIndex]; + for (Node = FreePoolList->BackLink; + Node != FreePoolList; + Node = Node->BackLink) { + Pool = BASE_CR (Node, FREE_POOL_HEADER, Link); + if (Pool->Header.Available) { + Index++; + } + } + } + } + + TotalSize += (sizeof (MEMORY_PROFILE_FREE_MEMORY) + Index * sizeof (MEMORY_PROFILE_DESCRIPTOR)); + TotalSize += (sizeof (MEMORY_PROFILE_MEMORY_RANGE) + mFullSmramRangeCount * sizeof (MEMORY_PROFILE_DESCRIPTOR)); + + return TotalSize; +} + +/** + Copy SMRAM profile data. + + @param ProfileBuffer The buffer to hold SMRAM profile data. + @param ProfileSize On input, profile buffer size. + On output, actual profile data size copied. + @param ProfileOffset On input, profile buffer offset to copy. + On output, next time profile buffer offset to copy. + +**/ +VOID +SmramProfileCopyData ( + OUT VOID *ProfileBuffer, + IN OUT UINT64 *ProfileSize, + IN OUT UINT64 *ProfileOffset + ) +{ + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + LIST_ENTRY *DriverInfoList; + LIST_ENTRY *DriverLink; + LIST_ENTRY *AllocInfoList; + LIST_ENTRY *AllocLink; + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + LIST_ENTRY *FreePageList; + LIST_ENTRY *FreePoolList; + FREE_POOL_HEADER *Pool; + UINTN PoolListIndex; + UINT32 Index; + MEMORY_PROFILE_FREE_MEMORY *FreeMemory; + MEMORY_PROFILE_MEMORY_RANGE *MemoryRange; + MEMORY_PROFILE_DESCRIPTOR *MemoryProfileDescriptor; + UINT64 Offset; + UINT64 RemainingSize; + UINTN PdbSize; + UINTN ActionStringSize; + UINTN SmmPoolTypeIndex; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + RemainingSize = *ProfileSize; + Offset = 0; + + if (*ProfileOffset < sizeof (MEMORY_PROFILE_CONTEXT)) { + if (RemainingSize >= sizeof (MEMORY_PROFILE_CONTEXT)) { + Context = ProfileBuffer; + CopyMem (Context, &ContextData->Context, sizeof (MEMORY_PROFILE_CONTEXT)); + RemainingSize -= sizeof (MEMORY_PROFILE_CONTEXT); + ProfileBuffer = (UINT8 *) ProfileBuffer + sizeof (MEMORY_PROFILE_CONTEXT); + } else { + goto Done; + } + } + Offset += sizeof (MEMORY_PROFILE_CONTEXT); + + DriverInfoList = ContextData->DriverInfoList; + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + if (*ProfileOffset < (Offset + DriverInfoData->DriverInfo.Header.Length)) { + if (RemainingSize >= DriverInfoData->DriverInfo.Header.Length) { + DriverInfo = ProfileBuffer; + CopyMem (DriverInfo, &DriverInfoData->DriverInfo, sizeof (MEMORY_PROFILE_DRIVER_INFO)); + if (DriverInfo->PdbStringOffset != 0) { + PdbSize = AsciiStrSize (DriverInfoData->PdbString); + CopyMem ((VOID *) ((UINTN) DriverInfo + DriverInfo->PdbStringOffset), DriverInfoData->PdbString, PdbSize); + } + RemainingSize -= DriverInfo->Header.Length; + ProfileBuffer = (UINT8 *) ProfileBuffer + DriverInfo->Header.Length; + } else { + goto Done; + } + } + Offset += DriverInfoData->DriverInfo.Header.Length; + + AllocInfoList = DriverInfoData->AllocInfoList; + for (AllocLink = AllocInfoList->ForwardLink; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink) { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + if (*ProfileOffset < (Offset + AllocInfoData->AllocInfo.Header.Length)) { + if (RemainingSize >= AllocInfoData->AllocInfo.Header.Length) { + AllocInfo = ProfileBuffer; + CopyMem (AllocInfo, &AllocInfoData->AllocInfo, sizeof (MEMORY_PROFILE_ALLOC_INFO)); + if (AllocInfo->ActionStringOffset) { + ActionStringSize = AsciiStrSize (AllocInfoData->ActionString); + CopyMem ((VOID *) ((UINTN) AllocInfo + AllocInfo->ActionStringOffset), AllocInfoData->ActionString, ActionStringSize); + } + RemainingSize -= AllocInfo->Header.Length; + ProfileBuffer = (UINT8 *) ProfileBuffer + AllocInfo->Header.Length; + } else { + goto Done; + } + } + Offset += AllocInfoData->AllocInfo.Header.Length; + } + } + + + if (*ProfileOffset < (Offset + sizeof (MEMORY_PROFILE_FREE_MEMORY))) { + if (RemainingSize >= sizeof (MEMORY_PROFILE_FREE_MEMORY)) { + FreeMemory = ProfileBuffer; + CopyMem (FreeMemory, &mSmramFreeMemory, sizeof (MEMORY_PROFILE_FREE_MEMORY)); + Index = 0; + FreePageList = &mSmmMemoryMap; + for (Node = FreePageList->BackLink; + Node != FreePageList; + Node = Node->BackLink) { + Index++; + } + for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) { + for (PoolListIndex = 0; PoolListIndex < MAX_POOL_INDEX; PoolListIndex++) { + FreePoolList = &mSmmPoolLists[SmmPoolTypeIndex][MAX_POOL_INDEX - PoolListIndex - 1]; + for (Node = FreePoolList->BackLink; + Node != FreePoolList; + Node = Node->BackLink) { + Pool = BASE_CR (Node, FREE_POOL_HEADER, Link); + if (Pool->Header.Available) { + Index++; + } + } + } + } + FreeMemory->FreeMemoryEntryCount = Index; + + RemainingSize -= sizeof (MEMORY_PROFILE_FREE_MEMORY); + ProfileBuffer = (UINT8 *) ProfileBuffer + sizeof (MEMORY_PROFILE_FREE_MEMORY); + } else { + goto Done; + } + } + Offset += sizeof (MEMORY_PROFILE_FREE_MEMORY); + FreePageList = &mSmmMemoryMap; + for (Node = FreePageList->BackLink; + Node != FreePageList; + Node = Node->BackLink) { + if (*ProfileOffset < (Offset + sizeof (MEMORY_PROFILE_DESCRIPTOR))) { + if (RemainingSize >= sizeof (MEMORY_PROFILE_DESCRIPTOR)) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + MemoryProfileDescriptor = ProfileBuffer; + MemoryProfileDescriptor->Header.Signature = MEMORY_PROFILE_DESCRIPTOR_SIGNATURE; + MemoryProfileDescriptor->Header.Length = sizeof (MEMORY_PROFILE_DESCRIPTOR); + MemoryProfileDescriptor->Header.Revision = MEMORY_PROFILE_DESCRIPTOR_REVISION; + MemoryProfileDescriptor->Address = (PHYSICAL_ADDRESS) (UINTN) Pages; + MemoryProfileDescriptor->Size = EFI_PAGES_TO_SIZE (Pages->NumberOfPages); + + RemainingSize -= sizeof (MEMORY_PROFILE_DESCRIPTOR); + ProfileBuffer = (UINT8 *) ProfileBuffer + sizeof (MEMORY_PROFILE_DESCRIPTOR); + } else { + goto Done; + } + } + Offset += sizeof (MEMORY_PROFILE_DESCRIPTOR); + } + for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) { + for (PoolListIndex = 0; PoolListIndex < MAX_POOL_INDEX; PoolListIndex++) { + FreePoolList = &mSmmPoolLists[SmmPoolTypeIndex][MAX_POOL_INDEX - PoolListIndex - 1]; + for (Node = FreePoolList->BackLink; + Node != FreePoolList; + Node = Node->BackLink) { + Pool = BASE_CR (Node, FREE_POOL_HEADER, Link); + if (Pool->Header.Available) { + if (*ProfileOffset < (Offset + sizeof (MEMORY_PROFILE_DESCRIPTOR))) { + if (RemainingSize >= sizeof (MEMORY_PROFILE_DESCRIPTOR)) { + MemoryProfileDescriptor = ProfileBuffer; + MemoryProfileDescriptor->Header.Signature = MEMORY_PROFILE_DESCRIPTOR_SIGNATURE; + MemoryProfileDescriptor->Header.Length = sizeof (MEMORY_PROFILE_DESCRIPTOR); + MemoryProfileDescriptor->Header.Revision = MEMORY_PROFILE_DESCRIPTOR_REVISION; + MemoryProfileDescriptor->Address = (PHYSICAL_ADDRESS) (UINTN) Pool; + MemoryProfileDescriptor->Size = Pool->Header.Size; + + RemainingSize -= sizeof (MEMORY_PROFILE_DESCRIPTOR); + ProfileBuffer = (UINT8 *) ProfileBuffer + sizeof (MEMORY_PROFILE_DESCRIPTOR); + } else { + goto Done; + } + } + Offset += sizeof (MEMORY_PROFILE_DESCRIPTOR); + } + } + } + } + + if (*ProfileOffset < (Offset + sizeof (MEMORY_PROFILE_MEMORY_RANGE))) { + if (RemainingSize >= sizeof (MEMORY_PROFILE_MEMORY_RANGE)) { + MemoryRange = ProfileBuffer; + MemoryRange->Header.Signature = MEMORY_PROFILE_MEMORY_RANGE_SIGNATURE; + MemoryRange->Header.Length = sizeof (MEMORY_PROFILE_MEMORY_RANGE); + MemoryRange->Header.Revision = MEMORY_PROFILE_MEMORY_RANGE_REVISION; + MemoryRange->MemoryRangeCount = (UINT32) mFullSmramRangeCount; + + RemainingSize -= sizeof (MEMORY_PROFILE_MEMORY_RANGE); + ProfileBuffer = (UINT8 *) ProfileBuffer + sizeof (MEMORY_PROFILE_MEMORY_RANGE); + } else { + goto Done; + } + } + Offset += sizeof (MEMORY_PROFILE_MEMORY_RANGE); + for (Index = 0; Index < mFullSmramRangeCount; Index++) { + if (*ProfileOffset < (Offset + sizeof (MEMORY_PROFILE_DESCRIPTOR))) { + if (RemainingSize >= sizeof (MEMORY_PROFILE_DESCRIPTOR)) { + MemoryProfileDescriptor = ProfileBuffer; + MemoryProfileDescriptor->Header.Signature = MEMORY_PROFILE_DESCRIPTOR_SIGNATURE; + MemoryProfileDescriptor->Header.Length = sizeof (MEMORY_PROFILE_DESCRIPTOR); + MemoryProfileDescriptor->Header.Revision = MEMORY_PROFILE_DESCRIPTOR_REVISION; + MemoryProfileDescriptor->Address = mFullSmramRanges[Index].PhysicalStart; + MemoryProfileDescriptor->Size = mFullSmramRanges[Index].PhysicalSize; + + RemainingSize -= sizeof (MEMORY_PROFILE_DESCRIPTOR); + ProfileBuffer = (UINT8 *) ProfileBuffer + sizeof (MEMORY_PROFILE_DESCRIPTOR); + } else { + goto Done; + } + } + Offset += sizeof (MEMORY_PROFILE_DESCRIPTOR); + } + +Done: + // + // On output, actual profile data size copied. + // + *ProfileSize -= RemainingSize; + // + // On output, next time profile buffer offset to copy. + // + *ProfileOffset = Offset; +} + +/** + Get memory profile data. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer. + On return, points to the size of the data returned in ProfileBuffer. + @param[out] ProfileBuffer Profile buffer. + + @return EFI_SUCCESS Get the memory profile data successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data. + ProfileSize is updated with the size required. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolGetData ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN OUT UINT64 *ProfileSize, + OUT VOID *ProfileBuffer + ) +{ + UINT64 Size; + UINT64 Offset; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + Size = SmramProfileGetDataSize (); + + if (*ProfileSize < Size) { + *ProfileSize = Size; + mSmramProfileGettingStatus = SmramProfileGettingStatus; + return EFI_BUFFER_TOO_SMALL; + } + + Offset = 0; + SmramProfileCopyData (ProfileBuffer, &Size, &Offset); + *ProfileSize = Size; + + mSmramProfileGettingStatus = SmramProfileGettingStatus; + return EFI_SUCCESS; +} + +/** + Register image to memory profile. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + @param[in] FileType File type of the image. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource for this register. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolRegisterImage ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN EFI_FV_FILETYPE FileType + ) +{ + EFI_STATUS Status; + EFI_SMM_DRIVER_ENTRY DriverEntry; + VOID *EntryPointInImage; + EFI_GUID *Name; + + ZeroMem (&DriverEntry, sizeof (DriverEntry)); + Name = GetFileNameFromFilePath (FilePath); + if (Name != NULL) { + CopyMem (&DriverEntry.FileName, Name, sizeof (EFI_GUID)); + } + DriverEntry.ImageBuffer = ImageBase; + DriverEntry.NumberOfPage = EFI_SIZE_TO_PAGES ((UINTN) ImageSize); + Status = InternalPeCoffGetEntryPoint ((VOID *) (UINTN) DriverEntry.ImageBuffer, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverEntry.ImageEntryPoint = (PHYSICAL_ADDRESS) (UINTN) EntryPointInImage; + + return RegisterSmramProfileImage (&DriverEntry, FALSE); +} + +/** + Unregister image from memory profile. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolUnregisterImage ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize + ) +{ + EFI_STATUS Status; + EFI_SMM_DRIVER_ENTRY DriverEntry; + VOID *EntryPointInImage; + EFI_GUID *Name; + + ZeroMem (&DriverEntry, sizeof (DriverEntry)); + Name = GetFileNameFromFilePath (FilePath); + if (Name != NULL) { + CopyMem (&DriverEntry.FileName, Name, sizeof (EFI_GUID)); + } + DriverEntry.ImageBuffer = ImageBase; + DriverEntry.NumberOfPage = EFI_SIZE_TO_PAGES ((UINTN) ImageSize); + Status = InternalPeCoffGetEntryPoint ((VOID *) (UINTN) DriverEntry.ImageBuffer, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverEntry.ImageEntryPoint = (PHYSICAL_ADDRESS) (UINTN) EntryPointInImage; + + return UnregisterSmramProfileImage (&DriverEntry, FALSE); +} + +/** + Get memory profile recording state. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[out] RecordingState Recording state. + + @return EFI_SUCCESS Memory profile recording state is returned. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_INVALID_PARAMETER RecordingState is NULL. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolGetRecordingState ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + OUT BOOLEAN *RecordingState + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + if (RecordingState == NULL) { + return EFI_INVALID_PARAMETER; + } + *RecordingState = mSmramProfileRecordingEnable; + return EFI_SUCCESS; +} + +/** + Set memory profile recording state. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] RecordingState Recording state. + + @return EFI_SUCCESS Set memory profile recording state successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolSetRecordingState ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN BOOLEAN RecordingState + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + mSmramProfileRecordingEnable = RecordingState; + return EFI_SUCCESS; +} + +/** + Record memory profile of multilevel caller. + + @param[in] This The EDKII_SMM_MEMORY_PROFILE_PROTOCOL instance. + @param[in] CallerAddress Address of caller. + @param[in] Action Memory profile action. + @param[in] MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param[in] Buffer Buffer address. + @param[in] Size Buffer size. + @param[in] ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +SmramProfileProtocolRecord ( + IN EDKII_SMM_MEMORY_PROFILE_PROTOCOL *This, + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN VOID *Buffer, + IN UINTN Size, + IN CHAR8 *ActionString OPTIONAL + ) +{ + return SmmCoreUpdateProfile (CallerAddress, Action, MemoryType, Size, Buffer, ActionString); +} + +/** + SMRAM profile handler to get profile info. + + @param SmramProfileParameterGetInfo The parameter of SMM profile get size. + +**/ +VOID +SmramProfileHandlerGetInfo ( + IN SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO *SmramProfileParameterGetInfo + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + SmramProfileParameterGetInfo->ProfileSize = SmramProfileGetDataSize(); + SmramProfileParameterGetInfo->Header.ReturnStatus = 0; + + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +/** + SMRAM profile handler to get profile data. + + @param SmramProfileParameterGetData The parameter of SMM profile get data. + +**/ +VOID +SmramProfileHandlerGetData ( + IN SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA *SmramProfileParameterGetData + ) +{ + UINT64 ProfileSize; + UINT64 ProfileOffset; + SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA SmramProfileGetData; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + + CopyMem (&SmramProfileGetData, SmramProfileParameterGetData, sizeof (SmramProfileGetData)); + + ProfileSize = SmramProfileGetDataSize(); + + // + // Sanity check + // + if (!SmmIsBufferOutsideSmmValid ((UINTN) SmramProfileGetData.ProfileBuffer, (UINTN) ProfileSize)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerGetData: SMM ProfileBuffer in SMRAM or overflow!\n")); + SmramProfileParameterGetData->ProfileSize = ProfileSize; + SmramProfileParameterGetData->Header.ReturnStatus = (UINT64) (INT64) (INTN) EFI_ACCESS_DENIED; + goto Done; + } + + if (SmramProfileGetData.ProfileSize < ProfileSize) { + SmramProfileParameterGetData->ProfileSize = ProfileSize; + SmramProfileParameterGetData->Header.ReturnStatus = (UINT64) (INT64) (INTN) EFI_BUFFER_TOO_SMALL; + goto Done; + } + + ProfileOffset = 0; + SmramProfileCopyData ((VOID *) (UINTN) SmramProfileGetData.ProfileBuffer, &ProfileSize, &ProfileOffset); + SmramProfileParameterGetData->ProfileSize = ProfileSize; + SmramProfileParameterGetData->Header.ReturnStatus = 0; + +Done: + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +/** + SMRAM profile handler to get profile data by offset. + + @param SmramProfileParameterGetDataByOffset The parameter of SMM profile get data by offset. + +**/ +VOID +SmramProfileHandlerGetDataByOffset ( + IN SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET *SmramProfileParameterGetDataByOffset + ) +{ + SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET SmramProfileGetDataByOffset; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + + CopyMem (&SmramProfileGetDataByOffset, SmramProfileParameterGetDataByOffset, sizeof (SmramProfileGetDataByOffset)); + + // + // Sanity check + // + if (!SmmIsBufferOutsideSmmValid ((UINTN) SmramProfileGetDataByOffset.ProfileBuffer, (UINTN) SmramProfileGetDataByOffset.ProfileSize)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerGetDataByOffset: SMM ProfileBuffer in SMRAM or overflow!\n")); + SmramProfileParameterGetDataByOffset->Header.ReturnStatus = (UINT64) (INT64) (INTN) EFI_ACCESS_DENIED; + goto Done; + } + + SmramProfileCopyData ((VOID *) (UINTN) SmramProfileGetDataByOffset.ProfileBuffer, &SmramProfileGetDataByOffset.ProfileSize, &SmramProfileGetDataByOffset.ProfileOffset); + CopyMem (SmramProfileParameterGetDataByOffset, &SmramProfileGetDataByOffset, sizeof (SmramProfileGetDataByOffset)); + SmramProfileParameterGetDataByOffset->Header.ReturnStatus = 0; + +Done: + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +/** + SMRAM profile handler to register SMM image. + + @param SmramProfileParameterRegisterImage The parameter of SMM profile register image. + +**/ +VOID +SmramProfileHandlerRegisterImage ( + IN SMRAM_PROFILE_PARAMETER_REGISTER_IMAGE *SmramProfileParameterRegisterImage + ) +{ + EFI_STATUS Status; + EFI_SMM_DRIVER_ENTRY DriverEntry; + VOID *EntryPointInImage; + + ZeroMem (&DriverEntry, sizeof (DriverEntry)); + CopyMem (&DriverEntry.FileName, &SmramProfileParameterRegisterImage->FileName, sizeof(EFI_GUID)); + DriverEntry.ImageBuffer = SmramProfileParameterRegisterImage->ImageBuffer; + DriverEntry.NumberOfPage = (UINTN) SmramProfileParameterRegisterImage->NumberOfPage; + Status = InternalPeCoffGetEntryPoint ((VOID *) (UINTN) DriverEntry.ImageBuffer, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverEntry.ImageEntryPoint = (PHYSICAL_ADDRESS) (UINTN) EntryPointInImage; + + Status = RegisterSmramProfileImage (&DriverEntry, FALSE); + if (!EFI_ERROR (Status)) { + SmramProfileParameterRegisterImage->Header.ReturnStatus = 0; + } +} + +/** + SMRAM profile handler to unregister SMM image. + + @param SmramProfileParameterUnregisterImage The parameter of SMM profile unregister image. + +**/ +VOID +SmramProfileHandlerUnregisterImage ( + IN SMRAM_PROFILE_PARAMETER_UNREGISTER_IMAGE *SmramProfileParameterUnregisterImage + ) +{ + EFI_STATUS Status; + EFI_SMM_DRIVER_ENTRY DriverEntry; + VOID *EntryPointInImage; + + ZeroMem (&DriverEntry, sizeof (DriverEntry)); + CopyMem (&DriverEntry.FileName, &SmramProfileParameterUnregisterImage->FileName, sizeof (EFI_GUID)); + DriverEntry.ImageBuffer = SmramProfileParameterUnregisterImage->ImageBuffer; + DriverEntry.NumberOfPage = (UINTN) SmramProfileParameterUnregisterImage->NumberOfPage; + Status = InternalPeCoffGetEntryPoint ((VOID *) (UINTN) DriverEntry.ImageBuffer, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverEntry.ImageEntryPoint = (PHYSICAL_ADDRESS) (UINTN) EntryPointInImage; + + Status = UnregisterSmramProfileImage (&DriverEntry, FALSE); + if (!EFI_ERROR (Status)) { + SmramProfileParameterUnregisterImage->Header.ReturnStatus = 0; + } +} + +/** + Dispatch function for a Software SMI handler. + + Caution: This function may receive untrusted input. + Communicate buffer and buffer size are external input, so this function will do basic validation. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the + handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS Command is handled successfully. + +**/ +EFI_STATUS +EFIAPI +SmramProfileHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + SMRAM_PROFILE_PARAMETER_HEADER *SmramProfileParameterHeader; + UINTN TempCommBufferSize; + SMRAM_PROFILE_PARAMETER_RECORDING_STATE *ParameterRecordingState; + + DEBUG ((EFI_D_ERROR, "SmramProfileHandler Enter\n")); + + // + // If input is invalid, stop processing this SMI + // + if (CommBuffer == NULL || CommBufferSize == NULL) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if (TempCommBufferSize < sizeof (SMRAM_PROFILE_PARAMETER_HEADER)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + + if (mSmramReadyToLock && !SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer in SMRAM or overflow!\n")); + return EFI_SUCCESS; + } + + SmramProfileParameterHeader = (SMRAM_PROFILE_PARAMETER_HEADER *) ((UINTN) CommBuffer); + + SmramProfileParameterHeader->ReturnStatus = (UINT64)-1; + + if (GetSmramProfileContext () == NULL) { + SmramProfileParameterHeader->ReturnStatus = (UINT64) (INT64) (INTN) EFI_UNSUPPORTED; + return EFI_SUCCESS; + } + + switch (SmramProfileParameterHeader->Command) { + case SMRAM_PROFILE_COMMAND_GET_PROFILE_INFO: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerGetInfo\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmramProfileHandlerGetInfo ((SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO *) (UINTN) CommBuffer); + break; + case SMRAM_PROFILE_COMMAND_GET_PROFILE_DATA: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerGetData\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmramProfileHandlerGetData ((SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA *) (UINTN) CommBuffer); + break; + case SMRAM_PROFILE_COMMAND_GET_PROFILE_DATA_BY_OFFSET: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerGetDataByOffset\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmramProfileHandlerGetDataByOffset ((SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET *) (UINTN) CommBuffer); + break; + case SMRAM_PROFILE_COMMAND_REGISTER_IMAGE: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerRegisterImage\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_REGISTER_IMAGE)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + if (mSmramReadyToLock) { + return EFI_SUCCESS; + } + SmramProfileHandlerRegisterImage ((SMRAM_PROFILE_PARAMETER_REGISTER_IMAGE *) (UINTN) CommBuffer); + break; + case SMRAM_PROFILE_COMMAND_UNREGISTER_IMAGE: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerUnregisterImage\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_UNREGISTER_IMAGE)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + if (mSmramReadyToLock) { + return EFI_SUCCESS; + } + SmramProfileHandlerUnregisterImage ((SMRAM_PROFILE_PARAMETER_UNREGISTER_IMAGE *) (UINTN) CommBuffer); + break; + case SMRAM_PROFILE_COMMAND_GET_RECORDING_STATE: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerGetRecordingState\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_RECORDING_STATE)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + ParameterRecordingState = (SMRAM_PROFILE_PARAMETER_RECORDING_STATE *) (UINTN) CommBuffer; + ParameterRecordingState->RecordingState = mSmramProfileRecordingEnable; + ParameterRecordingState->Header.ReturnStatus = 0; + break; + case SMRAM_PROFILE_COMMAND_SET_RECORDING_STATE: + DEBUG ((EFI_D_ERROR, "SmramProfileHandlerSetRecordingState\n")); + if (TempCommBufferSize != sizeof (SMRAM_PROFILE_PARAMETER_RECORDING_STATE)) { + DEBUG ((EFI_D_ERROR, "SmramProfileHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + ParameterRecordingState = (SMRAM_PROFILE_PARAMETER_RECORDING_STATE *) (UINTN) CommBuffer; + mSmramProfileRecordingEnable = ParameterRecordingState->RecordingState; + ParameterRecordingState->Header.ReturnStatus = 0; + break; + + default: + break; + } + + DEBUG ((EFI_D_ERROR, "SmramProfileHandler Exit\n")); + + return EFI_SUCCESS; +} + +/** + Register SMRAM profile handler. + +**/ +VOID +RegisterSmramProfileHandler ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE DispatchHandle; + + if (!IS_SMRAM_PROFILE_ENABLED) { + return; + } + + Status = SmiHandlerRegister ( + SmramProfileHandler, + &gEdkiiMemoryProfileGuid, + &DispatchHandle + ); + ASSERT_EFI_ERROR (Status); +} + +//////////////////// + +/** + Dump SMRAM range. + +**/ +VOID +DumpSmramRange ( + VOID + ) +{ + UINTN Index; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + DEBUG ((EFI_D_INFO, "FullSmramRange address - 0x%08x\n", mFullSmramRanges)); + + DEBUG ((EFI_D_INFO, "======= SmramProfile begin =======\n")); + + DEBUG ((EFI_D_INFO, "FullSmramRange:\n")); + for (Index = 0; Index < mFullSmramRangeCount; Index++) { + DEBUG ((EFI_D_INFO, " FullSmramRange (0x%x)\n", Index)); + DEBUG ((EFI_D_INFO, " PhysicalStart - 0x%016lx\n", mFullSmramRanges[Index].PhysicalStart)); + DEBUG ((EFI_D_INFO, " CpuStart - 0x%016lx\n", mFullSmramRanges[Index].CpuStart)); + DEBUG ((EFI_D_INFO, " PhysicalSize - 0x%016lx\n", mFullSmramRanges[Index].PhysicalSize)); + DEBUG ((EFI_D_INFO, " RegionState - 0x%016lx\n", mFullSmramRanges[Index].RegionState)); + } + + DEBUG ((EFI_D_INFO, "======= SmramProfile end =======\n")); + + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +/** + Dump SMRAM free page list. + +**/ +VOID +DumpFreePagesList ( + VOID + ) +{ + LIST_ENTRY *FreePageList; + LIST_ENTRY *Node; + FREE_PAGE_LIST *Pages; + UINTN Index; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + DEBUG ((EFI_D_INFO, "======= SmramProfile begin =======\n")); + + DEBUG ((EFI_D_INFO, "FreePagesList:\n")); + FreePageList = &mSmmMemoryMap; + for (Node = FreePageList->BackLink, Index = 0; + Node != FreePageList; + Node = Node->BackLink, Index++) { + Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); + DEBUG ((EFI_D_INFO, " Index - 0x%x\n", Index)); + DEBUG ((EFI_D_INFO, " PhysicalStart - 0x%016lx\n", (PHYSICAL_ADDRESS) (UINTN) Pages)); + DEBUG ((EFI_D_INFO, " NumberOfPages - 0x%08x\n", Pages->NumberOfPages)); + } + + DEBUG ((EFI_D_INFO, "======= SmramProfile end =======\n")); + + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +/** + Dump SMRAM free pool list. + +**/ +VOID +DumpFreePoolList ( + VOID + ) +{ + LIST_ENTRY *FreePoolList; + LIST_ENTRY *Node; + FREE_POOL_HEADER *Pool; + UINTN Index; + UINTN PoolListIndex; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN SmramProfileGettingStatus; + UINTN SmmPoolTypeIndex; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + DEBUG ((DEBUG_INFO, "======= SmramProfile begin =======\n")); + + for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) { + for (PoolListIndex = 0; PoolListIndex < MAX_POOL_INDEX; PoolListIndex++) { + DEBUG ((DEBUG_INFO, "FreePoolList(%d)(%d):\n", SmmPoolTypeIndex, PoolListIndex)); + FreePoolList = &mSmmPoolLists[SmmPoolTypeIndex][PoolListIndex]; + for (Node = FreePoolList->BackLink, Index = 0; + Node != FreePoolList; + Node = Node->BackLink, Index++) { + Pool = BASE_CR (Node, FREE_POOL_HEADER, Link); + DEBUG ((DEBUG_INFO, " Index - 0x%x\n", Index)); + DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", (PHYSICAL_ADDRESS) (UINTN) Pool)); + DEBUG ((DEBUG_INFO, " Size - 0x%08x\n", Pool->Header.Size)); + DEBUG ((DEBUG_INFO, " Available - 0x%02x\n", Pool->Header.Available)); + } + } + } + + DEBUG ((DEBUG_INFO, "======= SmramProfile end =======\n")); + + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mSmmActionString[] = { + "SmmUnknown", + "gSmst->SmmAllocatePages", + "gSmst->SmmFreePages", + "gSmst->SmmAllocatePool", + "gSmst->SmmFreePool", +}; + +typedef struct { + MEMORY_PROFILE_ACTION Action; + CHAR8 *String; +} ACTION_STRING; + +GLOBAL_REMOVE_IF_UNREFERENCED ACTION_STRING mExtActionString[] = { + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_PAGES, "Lib:AllocatePages"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_PAGES, "Lib:AllocateRuntimePages"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_PAGES, "Lib:AllocateReservedPages"}, + {MEMORY_PROFILE_ACTION_LIB_FREE_PAGES, "Lib:FreePages"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_PAGES, "Lib:AllocateAlignedPages"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_RUNTIME_PAGES, "Lib:AllocateAlignedRuntimePages"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_RESERVED_PAGES, "Lib:AllocateAlignedReservedPages"}, + {MEMORY_PROFILE_ACTION_LIB_FREE_ALIGNED_PAGES, "Lib:FreeAlignedPages"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_POOL, "Lib:AllocatePool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_POOL, "Lib:AllocateRuntimePool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_POOL, "Lib:AllocateReservedPool"}, + {MEMORY_PROFILE_ACTION_LIB_FREE_POOL, "Lib:FreePool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ZERO_POOL, "Lib:AllocateZeroPool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_ZERO_POOL, "Lib:AllocateRuntimeZeroPool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_ZERO_POOL, "Lib:AllocateReservedZeroPool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_COPY_POOL, "Lib:AllocateCopyPool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_COPY_POOL, "Lib:AllocateRuntimeCopyPool"}, + {MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_COPY_POOL, "Lib:AllocateReservedCopyPool"}, + {MEMORY_PROFILE_ACTION_LIB_REALLOCATE_POOL, "Lib:ReallocatePool"}, + {MEMORY_PROFILE_ACTION_LIB_REALLOCATE_RUNTIME_POOL, "Lib:ReallocateRuntimePool"}, + {MEMORY_PROFILE_ACTION_LIB_REALLOCATE_RESERVED_POOL, "Lib:ReallocateReservedPool"}, +}; + +typedef struct { + EFI_MEMORY_TYPE MemoryType; + CHAR8 *MemoryTypeStr; +} PROFILE_MEMORY_TYPE_STRING; + +GLOBAL_REMOVE_IF_UNREFERENCED PROFILE_MEMORY_TYPE_STRING mMemoryTypeString[] = { + {EfiRuntimeServicesCode, "EfiRuntimeServicesCode"}, + {EfiRuntimeServicesData, "EfiRuntimeServicesData"} +}; + +/** + Memory type to string. + + @param[in] MemoryType Memory type. + + @return Pointer to string. + +**/ +CHAR8 * +ProfileMemoryTypeToStr ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + UINTN Index; + for (Index = 0; Index < ARRAY_SIZE (mMemoryTypeString); Index++) { + if (mMemoryTypeString[Index].MemoryType == MemoryType) { + return mMemoryTypeString[Index].MemoryTypeStr; + } + } + + return "UnexpectedMemoryType"; +} + +/** + Action to string. + + @param[in] Action Profile action. + + @return Pointer to string. + +**/ +CHAR8 * +ProfileActionToStr ( + IN MEMORY_PROFILE_ACTION Action + ) +{ + UINTN Index; + UINTN ActionStringCount; + CHAR8 **ActionString; + + ActionString = mSmmActionString; + ActionStringCount = ARRAY_SIZE (mSmmActionString); + + if ((UINTN) (UINT32) Action < ActionStringCount) { + return ActionString[Action]; + } + for (Index = 0; Index < ARRAY_SIZE (mExtActionString); Index++) { + if (mExtActionString[Index].Action == Action) { + return mExtActionString[Index].String; + } + } + + return ActionString[0]; +} + +/** + Dump SMRAM profile. + +**/ +VOID +DumpSmramProfile ( + VOID + ) +{ + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + LIST_ENTRY *SmramDriverInfoList; + UINTN DriverIndex; + LIST_ENTRY *DriverLink; + LIST_ENTRY *AllocInfoList; + UINTN AllocIndex; + LIST_ENTRY *AllocLink; + BOOLEAN SmramProfileGettingStatus; + UINTN TypeIndex; + + ContextData = GetSmramProfileContext (); + if (ContextData == NULL) { + return ; + } + + SmramProfileGettingStatus = mSmramProfileGettingStatus; + mSmramProfileGettingStatus = TRUE; + + Context = &ContextData->Context; + DEBUG ((EFI_D_INFO, "======= SmramProfile begin =======\n")); + DEBUG ((EFI_D_INFO, "MEMORY_PROFILE_CONTEXT\n")); + + DEBUG ((EFI_D_INFO, " CurrentTotalUsage - 0x%016lx\n", Context->CurrentTotalUsage)); + DEBUG ((EFI_D_INFO, " PeakTotalUsage - 0x%016lx\n", Context->PeakTotalUsage)); + for (TypeIndex = 0; TypeIndex < sizeof (Context->CurrentTotalUsageByType) / sizeof (Context->CurrentTotalUsageByType[0]); TypeIndex++) { + if ((Context->CurrentTotalUsageByType[TypeIndex] != 0) || + (Context->PeakTotalUsageByType[TypeIndex] != 0)) { + DEBUG ((EFI_D_INFO, " CurrentTotalUsage[0x%02x] - 0x%016lx (%a)\n", TypeIndex, Context->CurrentTotalUsageByType[TypeIndex], ProfileMemoryTypeToStr (TypeIndex))); + DEBUG ((EFI_D_INFO, " PeakTotalUsage[0x%02x] - 0x%016lx (%a)\n", TypeIndex, Context->PeakTotalUsageByType[TypeIndex], ProfileMemoryTypeToStr (TypeIndex))); + } + } + DEBUG ((EFI_D_INFO, " TotalImageSize - 0x%016lx\n", Context->TotalImageSize)); + DEBUG ((EFI_D_INFO, " ImageCount - 0x%08x\n", Context->ImageCount)); + DEBUG ((EFI_D_INFO, " SequenceCount - 0x%08x\n", Context->SequenceCount)); + + SmramDriverInfoList = ContextData->DriverInfoList; + for (DriverLink = SmramDriverInfoList->ForwardLink, DriverIndex = 0; + DriverLink != SmramDriverInfoList; + DriverLink = DriverLink->ForwardLink, DriverIndex++) { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + DriverInfo = &DriverInfoData->DriverInfo; + DEBUG ((EFI_D_INFO, " MEMORY_PROFILE_DRIVER_INFO (0x%x)\n", DriverIndex)); + DEBUG ((EFI_D_INFO, " FileName - %g\n", &DriverInfo->FileName)); + DEBUG ((EFI_D_INFO, " ImageBase - 0x%016lx\n", DriverInfo->ImageBase)); + DEBUG ((EFI_D_INFO, " ImageSize - 0x%016lx\n", DriverInfo->ImageSize)); + DEBUG ((EFI_D_INFO, " EntryPoint - 0x%016lx\n", DriverInfo->EntryPoint)); + DEBUG ((EFI_D_INFO, " ImageSubsystem - 0x%04x\n", DriverInfo->ImageSubsystem)); + DEBUG ((EFI_D_INFO, " FileType - 0x%02x\n", DriverInfo->FileType)); + DEBUG ((EFI_D_INFO, " CurrentUsage - 0x%016lx\n", DriverInfo->CurrentUsage)); + DEBUG ((EFI_D_INFO, " PeakUsage - 0x%016lx\n", DriverInfo->PeakUsage)); + for (TypeIndex = 0; TypeIndex < sizeof (DriverInfo->CurrentUsageByType) / sizeof (DriverInfo->CurrentUsageByType[0]); TypeIndex++) { + if ((DriverInfo->CurrentUsageByType[TypeIndex] != 0) || + (DriverInfo->PeakUsageByType[TypeIndex] != 0)) { + DEBUG ((EFI_D_INFO, " CurrentUsage[0x%02x] - 0x%016lx (%a)\n", TypeIndex, DriverInfo->CurrentUsageByType[TypeIndex], ProfileMemoryTypeToStr (TypeIndex))); + DEBUG ((EFI_D_INFO, " PeakUsage[0x%02x] - 0x%016lx (%a)\n", TypeIndex, DriverInfo->PeakUsageByType[TypeIndex], ProfileMemoryTypeToStr (TypeIndex))); + } + } + DEBUG ((EFI_D_INFO, " AllocRecordCount - 0x%08x\n", DriverInfo->AllocRecordCount)); + + AllocInfoList = DriverInfoData->AllocInfoList; + for (AllocLink = AllocInfoList->ForwardLink, AllocIndex = 0; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink, AllocIndex++) { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + AllocInfo = &AllocInfoData->AllocInfo; + DEBUG ((EFI_D_INFO, " MEMORY_PROFILE_ALLOC_INFO (0x%x)\n", AllocIndex)); + DEBUG ((EFI_D_INFO, " CallerAddress - 0x%016lx (Offset: 0x%08x)\n", AllocInfo->CallerAddress, AllocInfo->CallerAddress - DriverInfo->ImageBase)); + DEBUG ((EFI_D_INFO, " SequenceId - 0x%08x\n", AllocInfo->SequenceId)); + if ((AllocInfo->Action & MEMORY_PROFILE_ACTION_USER_DEFINED_MASK) != 0) { + if (AllocInfoData->ActionString != NULL) { + DEBUG ((EFI_D_INFO, " Action - 0x%08x (%a)\n", AllocInfo->Action, AllocInfoData->ActionString)); + } else { + DEBUG ((EFI_D_INFO, " Action - 0x%08x (UserDefined-0x%08x)\n", AllocInfo->Action, AllocInfo->Action)); + } + } else { + DEBUG ((EFI_D_INFO, " Action - 0x%08x (%a)\n", AllocInfo->Action, ProfileActionToStr (AllocInfo->Action))); + } + DEBUG ((EFI_D_INFO, " MemoryType - 0x%08x (%a)\n", AllocInfo->MemoryType, ProfileMemoryTypeToStr (AllocInfo->MemoryType))); + DEBUG ((EFI_D_INFO, " Buffer - 0x%016lx\n", AllocInfo->Buffer)); + DEBUG ((EFI_D_INFO, " Size - 0x%016lx\n", AllocInfo->Size)); + } + } + + DEBUG ((EFI_D_INFO, "======= SmramProfile end =======\n")); + + mSmramProfileGettingStatus = SmramProfileGettingStatus; +} + +/** + Dump SMRAM information. + +**/ +VOID +DumpSmramInfo ( + VOID + ) +{ + DEBUG_CODE ( + if (IS_SMRAM_PROFILE_ENABLED) { + DumpSmramProfile (); + DumpFreePagesList (); + DumpFreePoolList (); + DumpSmramRange (); + } + ); +} + |