summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dependency.c382
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Dispatcher.c1499
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Handle.c528
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.c1404
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/HeapGuard.h392
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/InstallConfigurationTable.c171
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Locate.c489
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/MemoryAttributesTable.c1368
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Notify.c196
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Page.c1071
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c920
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h1353
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf123
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCore.uni16
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCoreExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h119
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c1865
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf91
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIpl.uni16
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/PiSmmIplExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Pool.c449
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/Smi.c333
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmiHandlerProfile.c1367
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/PiSmmCore/SmramProfileRecord.c2817
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 ();
+ }
+ );
+}
+