public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v1 1/1] StandaloneMmPkg: Add StandaloneMmIplPei driver.
@ 2022-11-09  6:16 Hongbin1 Zhang
  2022-11-09 10:19 ` Ard Biesheuvel
  0 siblings, 1 reply; 2+ messages in thread
From: Hongbin1 Zhang @ 2022-11-09  6:16 UTC (permalink / raw)
  To: devel
  Cc: Hongbin1 Zhang, Jiewen Yao, Ray Ni, Star Zeng, Sami Mujawar,
	Ard Biesheuvel, Supreeth Venkatesh

Add StandaloneMmIplPei IA32/X64 driver at PEI stage.
FSP will use this driver to load Standalone MM code
to dispatch other Standalone MM drivers.

Signed-off-by: Hongbin1 Zhang <hongbin1.zhang@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Star Zeng <star.zeng@intel.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
---
 StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c     | 442 ++++++++++++
 StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c   | 726 ++++++++++++++++++++
 StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c      |  32 +
 StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm  | 148 ++++
 StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h   |  47 ++
 StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf |  74 ++
 StandaloneMmPkg/StandaloneMmPkg.ci.yaml                           |   4 +-
 StandaloneMmPkg/StandaloneMmPkg.dsc                               |  15 +-
 8 files changed, 1485 insertions(+), 3 deletions(-)

diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c
new file mode 100644
index 000000000000..0dfb574bd228
--- /dev/null
+++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c
@@ -0,0 +1,442 @@
+/** @file
+  SMM IPL that load the SMM Core into SMRAM
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <PiSmm.h>
+#include <StandaloneMm.h>
+#include <Library/HobLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <StandaloneMmIplPei.h>
+
+#pragma pack(1)
+
+//
+// Page-Map Level-4 Offset (PML4) and
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
+//
+
+typedef union {
+  struct {
+    UINT64    Present              : 1;   // 0 = Not present in memory, 1 = Present in memory
+    UINT64    ReadWrite            : 1;   // 0 = Read-Only, 1= Read/Write
+    UINT64    UserSupervisor       : 1;   // 0 = Supervisor, 1=User
+    UINT64    WriteThrough         : 1;   // 0 = Write-Back caching, 1=Write-Through caching
+    UINT64    CacheDisabled        : 1;   // 0 = Cached, 1=Non-Cached
+    UINT64    Accessed             : 1;   // 0 = Not accessed, 1 = Accessed (set by CPU)
+    UINT64    Reserved             : 1;   // Reserved
+    UINT64    MustBeZero           : 2;   // Must Be Zero
+    UINT64    Available            : 3;   // Available for use by system software
+    UINT64    PageTableBaseAddress : 40;  // Page Table Base Address
+    UINT64    AvailableHigh        : 11;  // Available for use by system software
+    UINT64    Nx                   : 1;   // No Execute bit
+  } Bits;
+  UINT64    Uint64;
+} PAGE_MAP_AND_DIRECTORY_POINTER;
+
+//
+// Page Table Entry 2MB
+//
+typedef union {
+  struct {
+    UINT64    Present              : 1;   // 0 = Not present in memory, 1 = Present in memory
+    UINT64    ReadWrite            : 1;   // 0 = Read-Only, 1= Read/Write
+    UINT64    UserSupervisor       : 1;   // 0 = Supervisor, 1=User
+    UINT64    WriteThrough         : 1;   // 0 = Write-Back caching, 1=Write-Through caching
+    UINT64    CacheDisabled        : 1;   // 0 = Cached, 1=Non-Cached
+    UINT64    Accessed             : 1;   // 0 = Not accessed, 1 = Accessed (set by CPU)
+    UINT64    Dirty                : 1;   // 0 = Not Dirty, 1 = written by processor on access to page
+    UINT64    MustBe1              : 1;   // Must be 1
+    UINT64    Global               : 1;   // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+    UINT64    Available            : 3;   // Available for use by system software
+    UINT64    Pat                  : 1;   //
+    UINT64    MustBeZero           : 8;   // Must be zero
+    UINT64    PageTableBaseAddress : 31;  // Page Table Base Address
+    UINT64    AvailableHigh        : 11;  // Available for use by system software
+    UINT64    Nx                   : 1;   // 0 = Execute Code, 1 = No Code Execution
+  } Bits;
+  UINT64    Uint64;
+} PAGE_TABLE_ENTRY;
+
+//
+// Page Table Entry 1GB
+//
+typedef union {
+  struct {
+    UINT64    Present              : 1;   // 0 = Not present in memory, 1 = Present in memory
+    UINT64    ReadWrite            : 1;   // 0 = Read-Only, 1= Read/Write
+    UINT64    UserSupervisor       : 1;   // 0 = Supervisor, 1=User
+    UINT64    WriteThrough         : 1;   // 0 = Write-Back caching, 1=Write-Through caching
+    UINT64    CacheDisabled        : 1;   // 0 = Cached, 1=Non-Cached
+    UINT64    Accessed             : 1;   // 0 = Not accessed, 1 = Accessed (set by CPU)
+    UINT64    Dirty                : 1;   // 0 = Not Dirty, 1 = written by processor on access to page
+    UINT64    MustBe1              : 1;   // Must be 1
+    UINT64    Global               : 1;   // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+    UINT64    Available            : 3;   // Available for use by system software
+    UINT64    Pat                  : 1;   //
+    UINT64    MustBeZero           : 17;  // Must be zero;
+    UINT64    PageTableBaseAddress : 22;  // Page Table Base Address
+    UINT64    AvailableHigh        : 11;  // Available for use by system software
+    UINT64    Nx                   : 1;   // 0 = Execute Code, 1 = No Code Execution
+  } Bits;
+  UINT64    Uint64;
+} PAGE_TABLE_1G_ENTRY;
+
+#pragma pack()
+
+//
+// Global Descriptor Table (GDT)
+//
+GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR  mGdtEntries[] = {
+  /* selector { Global Segment Descriptor                              } */
+  /* 0x00 */ {
+    { 0,      0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0, 0 }
+  },                                                                      // null descriptor
+  /* 0x08 */ {
+    { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
+  },                                                                      // linear data segment descriptor
+  /* 0x10 */ {
+    { 0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
+  },                                                                      // linear code segment descriptor
+  /* 0x18 */ {
+    { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
+  },                                                                      // system data segment descriptor
+  /* 0x20 */ {
+    { 0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
+  },                                                                      // system code segment descriptor
+  /* 0x28 */ {
+    { 0,      0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0, 0 }
+  },                                                                      // spare segment descriptor
+  /* 0x30 */ {
+    { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
+  },                                                                      // system data segment descriptor
+  /* 0x38 */ {
+    { 0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0 }
+  },                                                                      // system code segment descriptor
+  /* 0x40 */ {
+    { 0,      0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0, 0 }
+  },                                                                      // spare segment descriptor
+};
+
+//
+// IA32 Gdt register
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR  mGdt = {
+  sizeof (mGdtEntries) - 1,
+  (UINTN)mGdtEntries
+};
+
+/**
+  Calculate the total size of page table.
+
+  @return The size of page table.
+
+**/
+UINTN
+CalculatePageTableSize (
+  VOID
+  )
+{
+  UINT32   RegEax;
+  UINT32   RegEdx;
+  UINTN    TotalPagesNum;
+  UINT8    PhysicalAddressBits;
+  VOID     *Hob;
+  UINT32   NumberOfPml4EntriesNeeded;
+  UINT32   NumberOfPdpEntriesNeeded;
+  BOOLEAN  Page1GSupport;
+
+  Page1GSupport = FALSE;
+  if (PcdGetBool (PcdUse1GPageTable)) {
+    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+    if (RegEax >= 0x80000001) {
+      AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+      if ((RegEdx & BIT26) != 0) {
+        Page1GSupport = TRUE;
+      }
+    }
+  }
+
+  //
+  // Get physical address bits supported.
+  //
+  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+  if (Hob != NULL) {
+    PhysicalAddressBits = ((EFI_HOB_CPU *)Hob)->SizeOfMemorySpace;
+  } else {
+    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+    if (RegEax >= 0x80000008) {
+      AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+      PhysicalAddressBits = (UINT8)RegEax;
+    } else {
+      PhysicalAddressBits = 36;
+    }
+  }
+
+  //
+  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
+  //
+  ASSERT (PhysicalAddressBits <= 52);
+  if (PhysicalAddressBits > 48) {
+    PhysicalAddressBits = 48;
+  }
+
+  //
+  // Calculate the table entries needed.
+  //
+  if (PhysicalAddressBits <= 39 ) {
+    NumberOfPml4EntriesNeeded = 1;
+    NumberOfPdpEntriesNeeded  = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
+  } else {
+    NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
+    NumberOfPdpEntriesNeeded  = 512;
+  }
+
+  if (!Page1GSupport) {
+    TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
+  } else {
+    TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
+  }
+
+  return EFI_PAGES_TO_SIZE (TotalPagesNum);
+}
+
+/**
+  Allocates and fills in the Page Directory and Page Table Entries to
+  establish a 1:1 Virtual to Physical mapping.
+
+  @param[in]  PageTablesAddress  The base address of page table.
+
+**/
+VOID
+CreateIdentityMappingPageTables (
+  IN  EFI_PHYSICAL_ADDRESS  PageTablesAddress
+  )
+{
+  UINT32                          RegEax;
+  UINT32                          RegEdx;
+  UINT8                           PhysicalAddressBits;
+  EFI_PHYSICAL_ADDRESS            PageAddress;
+  UINTN                           IndexOfPml4Entries;
+  UINTN                           IndexOfPdpEntries;
+  UINTN                           IndexOfPageDirectoryEntries;
+  UINT32                          NumberOfPml4EntriesNeeded;
+  UINT32                          NumberOfPdpEntriesNeeded;
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageMapLevel4Entry;
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageMap;
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageDirectoryPointerEntry;
+  PAGE_TABLE_ENTRY                *PageDirectoryEntry;
+  UINTN                           BigPageAddress;
+  VOID                            *Hob;
+  BOOLEAN                         Page1GSupport;
+  PAGE_TABLE_1G_ENTRY             *PageDirectory1GEntry;
+
+  Page1GSupport = FALSE;
+  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+  if (RegEax >= 0x80000001) {
+    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+    if ((RegEdx & BIT26) != 0) {
+      Page1GSupport = TRUE;
+    }
+  }
+
+  //
+  // Get physical address bits supported.
+  //
+  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+  if (Hob != NULL) {
+    PhysicalAddressBits = ((EFI_HOB_CPU *)Hob)->SizeOfMemorySpace;
+  } else {
+    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+    if (RegEax >= 0x80000008) {
+      AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+      PhysicalAddressBits = (UINT8)RegEax;
+    } else {
+      PhysicalAddressBits = 36;
+    }
+  }
+
+  //
+  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
+  //
+  ASSERT (PhysicalAddressBits <= 52);
+  if (PhysicalAddressBits > 48) {
+    PhysicalAddressBits = 48;
+  }
+
+  //
+  // Calculate the table entries needed.
+  //
+  if (PhysicalAddressBits <= 39 ) {
+    NumberOfPml4EntriesNeeded = 1;
+    NumberOfPdpEntriesNeeded  = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
+  } else {
+    NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
+    NumberOfPdpEntriesNeeded  = 512;
+  }
+
+  //
+  // Pre-allocate big pages to avoid later allocations.
+  //
+  BigPageAddress = (UINTN)PageTablesAddress;
+
+  //
+  // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
+  //
+  PageMap         = (VOID *)BigPageAddress;
+  BigPageAddress += SIZE_4KB;
+
+  PageMapLevel4Entry = PageMap;
+  PageAddress        = 0;
+  for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+    //
+    // Each PML4 entry points to a page of Page Directory Pointer entires.
+    // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
+    //
+    PageDirectoryPointerEntry = (VOID *)BigPageAddress;
+    BigPageAddress           += SIZE_4KB;
+
+    //
+    // Make a PML4 Entry
+    //
+    PageMapLevel4Entry->Uint64         = (UINT64)(UINTN)PageDirectoryPointerEntry;
+    PageMapLevel4Entry->Bits.ReadWrite = 1;
+    PageMapLevel4Entry->Bits.Present   = 1;
+
+    if (Page1GSupport) {
+      PageDirectory1GEntry = (VOID *)PageDirectoryPointerEntry;
+
+      for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
+        //
+        // Fill in the Page Directory entries
+        //
+        PageDirectory1GEntry->Uint64         = (UINT64)PageAddress;
+        PageDirectory1GEntry->Bits.ReadWrite = 1;
+        PageDirectory1GEntry->Bits.Present   = 1;
+        PageDirectory1GEntry->Bits.MustBe1   = 1;
+      }
+    } else {
+      for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
+        //
+        // Each Directory Pointer entries points to a page of Page Directory entires.
+        // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
+        //
+        PageDirectoryEntry = (VOID *)BigPageAddress;
+        BigPageAddress    += SIZE_4KB;
+
+        //
+        // Fill in a Page Directory Pointer Entries
+        //
+        PageDirectoryPointerEntry->Uint64         = (UINT64)(UINTN)PageDirectoryEntry;
+        PageDirectoryPointerEntry->Bits.ReadWrite = 1;
+        PageDirectoryPointerEntry->Bits.Present   = 1;
+
+        for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
+          //
+          // Fill in the Page Directory entries
+          //
+          PageDirectoryEntry->Uint64         = (UINT64)PageAddress;
+          PageDirectoryEntry->Bits.ReadWrite = 1;
+          PageDirectoryEntry->Bits.Present   = 1;
+          PageDirectoryEntry->Bits.MustBe1   = 1;
+        }
+      }
+
+      for ( ; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
+        ZeroMem (
+          PageDirectoryPointerEntry,
+          sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
+          );
+      }
+    }
+  }
+
+  //
+  // For the PML4 entries we are not using fill in a null entry.
+  //
+  for ( ; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+    ZeroMem (
+      PageMapLevel4Entry,
+      sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
+      );
+  }
+}
+
+/**
+  If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
+
+  @param  Entry                     Entry of Standalone MM Foundation.
+  @param  Context1                  A pointer to the context to pass into the EntryPoint
+                                    function.
+  @param  Context2                  A pointer to the context to pass into the EntryPoint
+                                    function.
+  @retval EFI_SUCCESS               Successfully switched to long mode and execute coalesce.
+  @retval Others                    Failed to execute coalesce in long mode.
+
+**/
+EFI_STATUS
+ModeSwitch (
+  IN EFI_PHYSICAL_ADDRESS  Entry,
+  IN VOID                  *Context1,
+  IN VOID                  *Context2
+  )
+{
+  UINTN       PageTableAddress;
+  UINTN       PageTableSize;
+  EFI_STATUS  Status;
+
+  DEBUG ((DEBUG_INFO, "ModeSwitch\n"));
+
+  PageTableSize = CalculatePageTableSize ();
+
+  PageTableAddress = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PageTableSize));
+  ASSERT (PageTableAddress != 0);
+
+  CreateIdentityMappingPageTables (PageTableAddress);
+
+  AsmWriteGdtr (&mGdt);
+
+  AsmWriteCr3 ((UINTN)PageTableAddress);
+
+  DEBUG ((DEBUG_INFO, "AsmExecute64BitCode ...\n"));
+
+  Status = AsmExecute64BitCode (Entry, (UINT64)(UINTN)Context1, (UINT64)(UINTN)Context2, NULL);
+  if (Status != 0) {
+    Status = Status | MAX_BIT;
+  }
+
+  DEBUG ((DEBUG_INFO, "AsmExecute64BitCode - %r\n", Status));
+
+  return Status;
+}
+
+/**
+  Load SMM core to dispatch other Standalone MM drivers.
+
+  @param  Entry                     Entry of Standalone MM Foundation.
+  @param  Context1                  A pointer to the context to pass into the EntryPoint
+                                    function.
+  @retval EFI_SUCCESS               Successfully loaded SMM core.
+  @retval Others                    Failed to load SMM core.
+**/
+EFI_STATUS
+LoadSmmCore (
+  IN EFI_PHYSICAL_ADDRESS  Entry,
+  IN VOID                  *Context1
+  )
+{
+  STANDALONE_MM_FOUNDATION_ENTRY_POINT  EntryPoint;
+
+  if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
+    return ModeSwitch (Entry, Context1, NULL);
+  }
+
+  EntryPoint = (STANDALONE_MM_FOUNDATION_ENTRY_POINT)(UINTN)Entry;
+  return EntryPoint (Context1);
+}
diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c
new file mode 100644
index 000000000000..e1ac6a2b3119
--- /dev/null
+++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c
@@ -0,0 +1,726 @@
+/** @file
+  SMM IPL that load the SMM Core into SMRAM at PEI stage
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <PiSmm.h>
+#include <Ppi/SmmAccess.h>
+#include <Ppi/SmmControl.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/PeiServicesTablePointerLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/HobLib.h>
+#include <Library/PcdLib.h>
+#include <Guid/SmramMemoryReserve.h>
+#include <Guid/MmCoreData.h>
+#include <StandaloneMmIplPei.h>
+
+//
+// MM Core Private Data structure that contains the data shared between
+// the SMM IPL and the Standalone MM Core.
+//
+MM_CORE_PRIVATE_DATA  mMmCorePrivateData = {
+  MM_CORE_PRIVATE_DATA_SIGNATURE,     // Signature
+  0,                                  // MmramRangeCount
+  0,                                  // MmramRanges
+  0,                                  // MmEntryPoint
+  FALSE,                              // MmEntryPointRegistered
+  FALSE,                              // InMm
+  0,                                  // Mmst
+  0,                                  // CommunicationBuffer
+  0,                                  // BufferSize
+  EFI_SUCCESS,                        // ReturnStatus
+  0,                                  // MmCoreImageBase
+  0,                                  // MmCoreImageSize
+  0,                                  // MmCoreEntryPoint
+  0,                                  // StandaloneBfvAddress
+};
+
+//
+// Global pointer used to access mMmCorePrivateData from outside and inside SMM
+//
+MM_CORE_PRIVATE_DATA  *gMmCorePrivate;
+
+//
+// SMM IPL global variables
+//
+PEI_SMM_ACCESS_PPI    *mSmmAccess;
+EFI_SMRAM_DESCRIPTOR  *mCurrentSmramRange;
+BOOLEAN               mSmmLocked = FALSE;
+EFI_PHYSICAL_ADDRESS  mSmramCacheBase;
+UINT64                mSmramCacheSize;
+
+/**
+  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               FoundAdjacentRange;
+  EFI_SMRAM_DESCRIPTOR  *SmramRanges;
+
+  *SmramCacheBase = SmramRange->CpuStart;
+  *SmramCacheSize = SmramRange->PhysicalSize;
+
+  SmramRanges = (EFI_SMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges;
+  do {
+    FoundAdjacentRange = FALSE;
+    for (Index = 0; Index < gMmCorePrivate->MmramRangeCount; Index++) {
+      RangeCpuStart     = SmramRanges[Index].CpuStart;
+      RangePhysicalSize = SmramRanges[Index].PhysicalSize;
+      if ((RangeCpuStart < *SmramCacheBase) && (*SmramCacheBase == (RangeCpuStart + RangePhysicalSize))) {
+        *SmramCacheBase    = RangeCpuStart;
+        *SmramCacheSize   += RangePhysicalSize;
+        FoundAdjacentRange = TRUE;
+      } else if (((*SmramCacheBase + *SmramCacheSize) == RangeCpuStart) && (RangePhysicalSize > 0)) {
+        *SmramCacheSize   += RangePhysicalSize;
+        FoundAdjacentRange = TRUE;
+      }
+    }
+  } while (FoundAdjacentRange);
+}
+
+/**
+  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         = mCurrentSmramRange->CpuStart;
+  //
+  // Get PeHeader pointer
+  //
+  ImgHdr              = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8 *)ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
+  SectionHeaderOffset = (UINTN)(
+                                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 ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r \n", FixLoadingAddress, Status));
+  return Status;
+}
+
+/**
+  Searches all the available firmware volumes and returns the first matching FFS section.
+
+  This function searches all the firmware volumes for FFS files with FV file type specified by FileType
+  The order that the firmware volumes is searched is not deterministic. For each available FV a search
+  is made for FFS file of type FileType. If the FV contains more than one FFS file with the same FileType,
+  the FileInstance instance will be the matched FFS file. For each FFS file found a search
+  is made for FFS sections of type SectionType. If the FFS file contains at least SectionInstance instances
+  of the FFS section specified by SectionType, then the SectionInstance instance is returned in Buffer.
+  Buffer is allocated using AllocatePool(), and the size of the allocated buffer is returned in Size.
+  It is the caller's responsibility to use FreePool() to free the allocated buffer.
+
+  If Buffer is NULL, then ASSERT().
+  If Size is NULL, then ASSERT().
+
+  @param  FileType             Indicates the FV file type to search for within all available FVs.
+  @param  FileInstance         Indicates which file instance within all available FVs specified by FileType.
+                               FileInstance starts from zero.
+  @param  SectionType          Indicates the FFS section type to search for within the FFS file
+                               specified by FileType with FileInstance.
+  @param  SectionInstance      Indicates which section instance within the FFS file
+                               specified by FileType with FileInstance to retrieve. SectionInstance starts from zero.
+  @param  Buffer               On output, a pointer to a callee allocated buffer containing the FFS file section that was found.
+                               Is it the caller's responsibility to free this buffer using FreePool().
+  @param  Size                 On output, a pointer to the size, in bytes, of Buffer.
+
+  @retval  EFI_SUCCESS          The specified FFS section was returned.
+  @retval  EFI_NOT_FOUND        The specified FFS section could not be found.
+  @retval  EFI_OUT_OF_RESOURCES There are not enough resources available to retrieve the matching FFS section.
+
+**/
+EFI_STATUS
+EFIAPI
+GetSectionFromAnyFvByFileType  (
+  IN  EFI_FV_FILETYPE   FileType,
+  IN  UINTN             FileInstance,
+  IN  EFI_SECTION_TYPE  SectionType,
+  IN  UINTN             SectionInstance,
+  OUT VOID              **Buffer,
+  OUT UINTN             *Size
+  )
+{
+  EFI_STATUS           Status;
+  UINTN                FvIndex;
+  EFI_PEI_FV_HANDLE    VolumeHandle;
+  EFI_PEI_FILE_HANDLE  FileHandle;
+  EFI_PE32_SECTION     *SectionData;
+  UINT32               SectionSize;
+
+  //
+  // Search all FV
+  //
+  VolumeHandle = NULL;
+  for (FvIndex = 0; ; FvIndex++) {
+    Status = PeiServicesFfsFindNextVolume (FvIndex, &VolumeHandle);
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    //
+    // Search PEIM FFS
+    //
+    FileHandle = NULL;
+    Status     = PeiServicesFfsFindNextFile (FileType, VolumeHandle, &FileHandle);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    //
+    // Search Section
+    //
+    SectionData = NULL;
+    Status      = PeiServicesFfsFindSectionData (SectionType, FileHandle, Buffer);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    //
+    // Great!
+    //
+    SectionData = (EFI_PE32_SECTION *)((UINT8 *)*Buffer - sizeof (EFI_PE32_SECTION));
+    ASSERT (SectionData->Type == SectionType);
+    SectionSize  = *(UINT32 *)SectionData->Size;
+    SectionSize &= 0xFFFFFF;
+    *Size        = SectionSize - sizeof (EFI_PE32_SECTION);
+
+    if (FileType == EFI_FV_FILETYPE_MM_CORE_STANDALONE) {
+      EFI_FV_INFO  VolumeInfo;
+      //
+      // This is SMM BFV
+      //
+      Status = PeiServicesFfsGetVolumeInfo (VolumeHandle, &VolumeInfo);
+      if (!EFI_ERROR (Status)) {
+        gMmCorePrivate->StandaloneBfvAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)VolumeInfo.FvStart;
+      }
+    }
+
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  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;
+  VOID                          *HobList;
+
+  Status = PeiServicesGetHobList (&HobList);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Search all Firmware Volumes for a PE/COFF image in a file of type SMM_CORE
+  //
+  Status = GetSectionFromAnyFvByFileType (
+             EFI_FV_FILETYPE_MM_CORE_STANDALONE,
+             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.
+      //
+      gMmCorePrivate->MmramRangeCount--;
+    } else {
+      DEBUG ((DEBUG_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));
+
+      gMmCorePrivate->MmCoreImageBase = ImageContext.ImageAddress;
+      gMmCorePrivate->MmCoreImageSize = ImageContext.ImageSize;
+      DEBUG ((DEBUG_INFO, "SmmCoreImageBase - 0x%016lx\n", gMmCorePrivate->MmCoreImageBase));
+      DEBUG ((DEBUG_INFO, "SmmCoreImageSize - 0x%016lx\n", gMmCorePrivate->MmCoreImageSize));
+
+      gMmCorePrivate->MmCoreEntryPoint = ImageContext.EntryPoint;
+
+      //
+      // Print debug message showing Standalone MM Core entry point address.
+      //
+      DEBUG ((DEBUG_INFO, "SMM IPL calling Standalone MM Core at SMRAM address - 0x%016lx\n", gMmCorePrivate->MmCoreEntryPoint));
+
+      //
+      // Execute image
+      //
+      LoadSmmCore (ImageContext.EntryPoint, HobList);
+    }
+  }
+
+  //
+  // If the load operation, relocate operation, or the image execution return an
+  // error, then free memory allocated from the EFI_SRAM_DESCRIPTOR specified by
+  // SmramRange
+  //
+  if (EFI_ERROR (Status)) {
+    SmramRange->PhysicalSize += EFI_PAGES_TO_SIZE (PageCount);
+  }
+
+  //
+  // Always free memory allocated by GetFileBufferByFilePath ()
+  //
+  FreePool (SourceBuffer);
+
+  return Status;
+}
+
+/**
+  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[in]  PeiServices           Describes the list of possible PEI Services.
+  @param[out] FullSmramRangeCount   Output pointer to full SMRAM range count.
+
+  @return Pointer to full SMRAM ranges.
+
+**/
+EFI_SMRAM_DESCRIPTOR *
+GetFullSmramRanges (
+  IN  CONST EFI_PEI_SERVICES  **PeiServices,
+  OUT       UINTN             *FullSmramRangeCount
+  )
+{
+  EFI_STATUS            Status;
+  UINTN                 Size;
+  EFI_SMRAM_DESCRIPTOR  *FullSmramRanges;
+  UINTN                 AdditionSmramRangeCount;
+  UINTN                 SmramRangeCount;
+
+  //
+  // Get SMRAM information.
+  //
+  Size   = 0;
+  Status = mSmmAccess->GetCapabilities ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, &Size, NULL);
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+
+  SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
+
+  //
+  // Reserve one entry SMM Core in the full SMRAM ranges.
+  //
+  AdditionSmramRangeCount = 1;
+  if (PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) {
+    //
+    // Reserve three entries for all SMM drivers & SMM Core in the full SMRAM ranges.
+    //
+    AdditionSmramRangeCount = 2;
+  }
+
+  //
+  // 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);
+  if (FullSmramRanges == NULL) {
+    return NULL;
+  }
+
+  Status = mSmmAccess->GetCapabilities ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, &Size, FullSmramRanges);
+
+  ASSERT_EFI_ERROR (Status);
+
+  return FullSmramRanges;
+}
+
+/**
+  The Entry Point for SMM IPL
+
+  Load SMM Core into SMRAM.
+
+  @param  FileHandle  Handle of the file being invoked.
+  @param  PeiServices Describes the list of possible PEI Services.
+
+  @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_PEI_FILE_HANDLE  FileHandle,
+  IN CONST EFI_PEI_SERVICES     **PeiServices
+  )
+{
+  EFI_STATUS             Status;
+  UINTN                  Index;
+  UINT64                 MaxSize;
+  UINT64                 SmmCodeSize;
+  MM_CORE_DATA_HOB_DATA  SmmCoreDataHobData;
+  EFI_SMRAM_DESCRIPTOR   *MmramRanges;
+
+  //
+  // Build Hob for SMM and DXE phase
+  //
+  SmmCoreDataHobData.Address = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateRuntimePages (EFI_SIZE_TO_PAGES (sizeof (mMmCorePrivateData)));
+  ASSERT (SmmCoreDataHobData.Address != 0);
+  gMmCorePrivate = (VOID *)(UINTN)SmmCoreDataHobData.Address;
+  CopyMem ((VOID *)(UINTN)SmmCoreDataHobData.Address, &mMmCorePrivateData, sizeof (mMmCorePrivateData));
+  DEBUG ((DEBUG_INFO, "gMmCorePrivate - 0x%x\n", gMmCorePrivate));
+
+  BuildGuidDataHob (
+    &gMmCoreDataHobGuid,
+    (VOID *)&SmmCoreDataHobData,
+    sizeof (SmmCoreDataHobData)
+    );
+
+  //
+  // Get SMM Access Protocol
+  //
+  Status = PeiServicesLocatePpi (&gPeiSmmAccessPpiGuid, 0, NULL, (VOID **)&mSmmAccess);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Get SMRAM information
+  //
+  gMmCorePrivate->MmramRanges = (EFI_PHYSICAL_ADDRESS)(UINTN)GetFullSmramRanges (PeiServices, (UINTN *)&gMmCorePrivate->MmramRangeCount);
+  ASSERT (gMmCorePrivate->MmramRanges != 0);
+  if (gMmCorePrivate->MmramRanges == 0) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Open all SMRAM ranges
+  //
+  Status = mSmmAccess->Open ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, 0);
+  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;
+  MmramRanges        = (EFI_MMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges;
+  if (MmramRanges == NULL) {
+    DEBUG ((DEBUG_ERROR, "Fail to retrieve MmramRanges\n"));
+    return EFI_UNSUPPORTED;
+  }
+
+  for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < gMmCorePrivate->MmramRangeCount; Index++) {
+    //
+    // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization
+    //
+    if ((MmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
+      continue;
+    }
+
+    if (MmramRanges[Index].CpuStart >= BASE_1MB) {
+      if ((MmramRanges[Index].CpuStart + MmramRanges[Index].PhysicalSize) <= BASE_4GB) {
+        if (MmramRanges[Index].PhysicalSize >= MaxSize) {
+          MaxSize            = MmramRanges[Index].PhysicalSize;
+          mCurrentSmramRange = &MmramRanges[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);
+
+    //
+    // 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);
+    }
+
+    //
+    // Load SMM Core into SMRAM and execute it from SMRAM
+    // Note: SmramRanges specific for SMM Core will put in the gMmCorePrivate->MmramRangeCount - 1.
+    //
+    Status = ExecuteSmmCoreFromSmram (
+               mCurrentSmramRange,
+               &(((EFI_MMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges)[gMmCorePrivate->MmramRangeCount - 1]),
+               gMmCorePrivate
+               );
+    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"));
+    }
+  } 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 ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, 0);
+    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 ((VOID *)(UINTN)gMmCorePrivate->MmramRanges);
+
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c
new file mode 100644
index 000000000000..5cabf310e33c
--- /dev/null
+++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c
@@ -0,0 +1,32 @@
+/** @file
+  SMM IPL that load the SMM Core into SMRAM
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <PiSmm.h>
+#include <StandaloneMm.h>
+
+/**
+  Load SMM core to dispatch other Standalone MM drivers.
+
+  @param  Entry                     Entry of Standalone MM Foundation.
+  @param  Context1                  A pointer to the context to pass into the EntryPoint
+                                    function.
+  @retval EFI_SUCCESS               Successfully loaded SMM core.
+  @retval Others                    Failed to load SMM core.
+**/
+EFI_STATUS
+LoadSmmCore (
+  IN EFI_PHYSICAL_ADDRESS  Entry,
+  IN VOID                  *Context1
+  )
+{
+  STANDALONE_MM_FOUNDATION_ENTRY_POINT  EntryPoint;
+
+  EntryPoint = (STANDALONE_MM_FOUNDATION_ENTRY_POINT)(UINTN)Entry;
+  return EntryPoint (Context1);
+}
diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm
new file mode 100644
index 000000000000..2332d32e5885
--- /dev/null
+++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm
@@ -0,0 +1,148 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;    Thunk32To64.nasm
+;
+; Abstract:
+;
+;   This is the assembly code to transition from long mode to compatibility
+;   mode to execute 32-bit code and then transit back to long mode.
+;
+;------------------------------------------------------------------------------
+
+    SECTION .text
+
+;------------------------------------------------------------------------------
+; Procedure:    AsmExecute64BitCode
+;
+; Input:        None
+;
+; Output:       None
+;
+; Prototype:    UINT32
+;               AsmExecute64BitCode (
+;                 IN UINT64           Function,
+;                 IN UINT64           Param1,
+;                 IN UINT64           Param2,
+;                 IN IA32_DESCRIPTOR  *InternalGdtr
+;                 );
+;
+;
+; Description:  A thunk function to execute 32-bit code in long mode.
+;
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmExecute64BitCode)
+ASM_PFX(AsmExecute64BitCode):
+;
+; +---------+
+; | EIP(64) |
+; +---------+
+; | CS (64) |
+; +---------+
+; | EIP(32) |
+; +---------+
+; | CS (32) |<-ESP (16 bytes aligned)
+; +---------+
+; | ...     |
+; +---------+
+; | ebx     |<-EBP
+; +---------+
+; | ebp     |<-EBP + 4
+; +---------+
+; | esi     |<-EBP + 8
+; +---------+
+; | edi     |<-EBP + 12
+; +---------+
+; | RFlags  |<-EBP + 16
+; +---------+
+; | RetAddr |<-EBP (org)
+; +---------+
+; | Func    |<-EBP + 24
+; | Func    |
+; +---------+
+; | Param1  |<-EBP + 32
+; | Param1  |
+; +---------+
+; | Param2  |<-EBP + 40
+; | Param2  |
+; +---------+
+; | Gdtr    |
+; +---------+
+;
+    ;
+    ; Save general purpose register and RFlags register
+    ;
+    pushfd
+    push    edi
+    push    esi
+    push    ebp
+    push    ebx
+    mov     ebp, esp
+
+    and     esp, 0FFFFFFF0h
+
+    push    010h                        ; protected mode selector on stack
+    mov     eax, Compatible             ; offset for LongMode
+    push    eax                         ; offset on stack
+
+    push    038h                        ; long mode selector on stack
+    mov     eax, LongMode               ; offset for LongMode
+    push    eax                         ; offset on stack
+
+    mov     eax, cr4
+    or      al, 020h
+    mov     cr4, eax                    ; enable PAE
+    mov     ecx, 0c0000080h
+    rdmsr
+    or      ah, 1                       ; set LME
+    wrmsr
+    mov     eax, cr0
+    bts     eax, 31                     ; set PG
+    mov     cr0, eax                    ; enable paging
+    retf                                ; topmost 2 dwords hold the address
+LongMode:                               ; long mode starts here
+
+    ; Call long mode function
+    DB      67h, 48h                    ; 32-bit address size, 64-bit operand size
+    mov     eax, [ebp + 24]             ; mov rbx, [ebp + 24]
+    DB      67h, 48h
+    mov     ecx, [ebp + 24 + 8]         ; mov rcx, [ebp + 24 + 8]
+    DB      67h, 48h
+    mov     edx, [ebp + 24 + 16]        ; mov rdx, [ebp + 24 + 16]
+
+    DB      48h
+    add     esp, -20h                   ; add rsp, -20h
+    call    eax                         ; call rax
+    DB      48h
+    add     esp, 20h                    ; add rsp, 20h
+
+    ; after long mode function call
+    mov     ebx, eax
+
+    retf
+Compatible:
+    mov     ecx, cr0
+    btc     ecx, 31                     ; clear PG
+    mov     cr0, ecx                    ; disable paging
+    mov     ecx, 0C0000080h
+    rdmsr
+    btc     eax, 8                      ; clear LME
+    wrmsr
+
+    ;
+    ; Restore C register and eax hold the return status from 32-bit function.
+    ; Note: Do not touch rax from now which hold the return value from IA32 function
+    ;
+    mov     eax, ebx                    ; put return status to EAX
+    mov     esp, ebp                    ; restore stack pointer
+    pop     ebx
+    pop     ebp
+    pop     esi
+    pop     edi
+    popfd
+
+    ret
diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h
new file mode 100644
index 000000000000..5e27dfe1454c
--- /dev/null
+++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h
@@ -0,0 +1,47 @@
+/** @file
+  Private header with declarations and definitions specific to the Standalone
+  MM IPL PEI driver
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef STANDALONE_MM_IPL_PEI_H_
+#define STANDALONE_MM_IPL_PEI_H_
+
+/**
+  Load SMM core to dispatch other Standalone MM drivers.
+
+  @param  Entry                     Entry of Standalone MM Foundation.
+  @param  Context1                  A pointer to the context to pass into the EntryPoint
+                                    function.
+  @retval EFI_SUCCESS               Successfully loaded SMM core.
+  @retval Others                    Failed to load SMM core.
+**/
+EFI_STATUS
+LoadSmmCore (
+  IN EFI_PHYSICAL_ADDRESS  Entry,
+  IN VOID                  *Context1
+  );
+
+/**
+  Assembly function to transition from long mode to compatibility mode to
+  execute 32-bit code and then transit back to long mode.
+
+  @param[in] Function     The 32bit code entry to be executed.
+  @param[in] Param1       The first parameter to pass to 32bit code
+  @param[in] Param2       The second parameter to pass to 32bit code
+  @param[in] InternalGdtr The GDT and GDT descriptor used by this library
+
+  @return status.
+**/
+UINT32
+AsmExecute64BitCode (
+  IN UINT64           Function,
+  IN UINT64           Param1,
+  IN UINT64           Param2,
+  IN IA32_DESCRIPTOR  *InternalGdtr
+  );
+
+#endif
diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf
new file mode 100644
index 000000000000..e531280abdfe
--- /dev/null
+++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf
@@ -0,0 +1,74 @@
+## @file
+#  This module provide a Standalone SMM compliant implementation of SMM IPL PEIM.
+#
+#  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = StandaloneMmIplPei
+  FILE_GUID                      = 578A0D17-2DC0-4C7D-A121-D8D771923BB0
+  MODULE_TYPE                    = PEIM
+  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]
+  StandaloneMmIplPei.h
+  StandaloneMmIplPei.c
+
+[Sources.Ia32]
+  Ia32/LoadSmmCore.c
+  Ia32/Thunk32To64.nasm
+
+[Sources.X64]
+  X64/LoadSmmCore.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  StandaloneMmPkg/StandaloneMmPkg.dec
+
+[LibraryClasses]
+  PeimEntryPoint
+  PeiServicesTablePointerLib
+  PeiServicesLib
+  BaseLib
+  BaseMemoryLib
+  PeCoffLib
+  CacheMaintenanceLib
+  MemoryAllocationLib
+  DebugLib
+  HobLib
+  IntrinsicLib
+
+[Guids]
+  gMmCoreDataHobGuid
+
+[Ppis]
+  gPeiSmmAccessPpiGuid                     ## CONSUMES
+  gPeiSmmControlPpiGuid                    ## CONSUMES
+
+[FeaturePcd.IA32]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode      ## CONSUMES
+
+[Pcd.IA32]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable              ## SOMETIMES_CONSUMES
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdLoadFixAddressSmmCodePageNumber     ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdLoadModuleAtFixAddressEnable        ## CONSUMES
+
+[Depex]
+  gPeiSmmAccessPpiGuid AND
+  gPeiSmmControlPpiGuid
+
diff --git a/StandaloneMmPkg/StandaloneMmPkg.ci.yaml b/StandaloneMmPkg/StandaloneMmPkg.ci.yaml
index 4777532a7ede..872f7958be84 100644
--- a/StandaloneMmPkg/StandaloneMmPkg.ci.yaml
+++ b/StandaloneMmPkg/StandaloneMmPkg.ci.yaml
@@ -78,7 +78,9 @@
     ## options defined .pytool/Plugin/SpellCheck
     "SpellCheck": {
         "AuditOnly": False,
-        "IgnoreFiles": [],           # use gitignore syntax to ignore errors
+        "IgnoreFiles": [
+            "Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm"
+        ],           # use gitignore syntax to ignore errors
                                      # in matching files
         "ExtendWords": [
             "Bsymbolic",
diff --git a/StandaloneMmPkg/StandaloneMmPkg.dsc b/StandaloneMmPkg/StandaloneMmPkg.dsc
index 8012f93b7dcc..d88471fe82be 100644
--- a/StandaloneMmPkg/StandaloneMmPkg.dsc
+++ b/StandaloneMmPkg/StandaloneMmPkg.dsc
@@ -20,7 +20,7 @@
   PLATFORM_VERSION               = 1.0
   DSC_SPECIFICATION              = 0x00010011
   OUTPUT_DIRECTORY               = Build/StandaloneMm
-  SUPPORTED_ARCHITECTURES        = AARCH64|X64|ARM
+  SUPPORTED_ARCHITECTURES        = AARCH64|X64|ARM|IA32
   BUILD_TARGETS                  = DEBUG|RELEASE
   SKUID_IDENTIFIER               = DEFAULT
 
@@ -60,6 +60,14 @@
   StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf
   VariableMmDependency|StandaloneMmPkg/Library/VariableMmDependency/VariableMmDependency.inf
 
+[LibraryClasses.common.PEIM]
+  HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
+  MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+  PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
+  IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf
+
 [LibraryClasses.AARCH64, LibraryClasses.ARM]
   ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
   StandaloneMmMmuLib|ArmPkg/Library/StandaloneMmMmuLib/ArmMmuStandaloneMmLib.inf
@@ -104,7 +112,7 @@
 #       generated for it, but the binary will not be put into any firmware volume.
 #
 ###################################################################################################
-[Components.common]
+[Components.AARCH64, Components.ARM, Components.X64]
   #
   # MM Core
   #
@@ -122,6 +130,9 @@
   StandaloneMmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf
   StandaloneMmPkg/Library/StandaloneMmPeCoffExtraActionLib/StandaloneMmPeCoffExtraActionLib.inf
 
+[Components.X64, Components.IA32]
+  StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf
+
 ###################################################################################################
 #
 # BuildOptions Section - Define the module specific tool chain flags that should be used as
-- 
2.26.2.windows.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v1 1/1] StandaloneMmPkg: Add StandaloneMmIplPei driver.
  2022-11-09  6:16 [PATCH v1 1/1] StandaloneMmPkg: Add StandaloneMmIplPei driver Hongbin1 Zhang
@ 2022-11-09 10:19 ` Ard Biesheuvel
  0 siblings, 0 replies; 2+ messages in thread
From: Ard Biesheuvel @ 2022-11-09 10:19 UTC (permalink / raw)
  To: Hongbin1 Zhang; +Cc: devel, Jiewen Yao, Ray Ni, Star Zeng, Sami Mujawar

On Wed, 9 Nov 2022 at 07:17, Hongbin1 Zhang <hongbin1.zhang@intel.com> wrote:
>
> Add StandaloneMmIplPei IA32/X64 driver at PEI stage.
> FSP will use this driver to load Standalone MM code
> to dispatch other Standalone MM drivers.
>
> Signed-off-by: Hongbin1 Zhang <hongbin1.zhang@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Star Zeng <star.zeng@intel.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>

I cannot review this but I'm happy for it to go in.

Acked-by: Ard Biesheuvel <ardb@kernel.org>

> ---
>  StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c     | 442 ++++++++++++
>  StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c   | 726 ++++++++++++++++++++
>  StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c      |  32 +
>  StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm  | 148 ++++
>  StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h   |  47 ++
>  StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf |  74 ++
>  StandaloneMmPkg/StandaloneMmPkg.ci.yaml                           |   4 +-
>  StandaloneMmPkg/StandaloneMmPkg.dsc                               |  15 +-
>  8 files changed, 1485 insertions(+), 3 deletions(-)
>
> diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c
> new file mode 100644
> index 000000000000..0dfb574bd228
> --- /dev/null
> +++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/LoadSmmCore.c
> @@ -0,0 +1,442 @@
> +/** @file
> +  SMM IPL that load the SMM Core into SMRAM
> +
> +  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiPei.h>
> +#include <PiSmm.h>
> +#include <StandaloneMm.h>
> +#include <Library/HobLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <StandaloneMmIplPei.h>
> +
> +#pragma pack(1)
> +
> +//
> +// Page-Map Level-4 Offset (PML4) and
> +// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
> +//
> +
> +typedef union {
> +  struct {
> +    UINT64    Present              : 1;   // 0 = Not present in memory, 1 = Present in memory
> +    UINT64    ReadWrite            : 1;   // 0 = Read-Only, 1= Read/Write
> +    UINT64    UserSupervisor       : 1;   // 0 = Supervisor, 1=User
> +    UINT64    WriteThrough         : 1;   // 0 = Write-Back caching, 1=Write-Through caching
> +    UINT64    CacheDisabled        : 1;   // 0 = Cached, 1=Non-Cached
> +    UINT64    Accessed             : 1;   // 0 = Not accessed, 1 = Accessed (set by CPU)
> +    UINT64    Reserved             : 1;   // Reserved
> +    UINT64    MustBeZero           : 2;   // Must Be Zero
> +    UINT64    Available            : 3;   // Available for use by system software
> +    UINT64    PageTableBaseAddress : 40;  // Page Table Base Address
> +    UINT64    AvailableHigh        : 11;  // Available for use by system software
> +    UINT64    Nx                   : 1;   // No Execute bit
> +  } Bits;
> +  UINT64    Uint64;
> +} PAGE_MAP_AND_DIRECTORY_POINTER;
> +
> +//
> +// Page Table Entry 2MB
> +//
> +typedef union {
> +  struct {
> +    UINT64    Present              : 1;   // 0 = Not present in memory, 1 = Present in memory
> +    UINT64    ReadWrite            : 1;   // 0 = Read-Only, 1= Read/Write
> +    UINT64    UserSupervisor       : 1;   // 0 = Supervisor, 1=User
> +    UINT64    WriteThrough         : 1;   // 0 = Write-Back caching, 1=Write-Through caching
> +    UINT64    CacheDisabled        : 1;   // 0 = Cached, 1=Non-Cached
> +    UINT64    Accessed             : 1;   // 0 = Not accessed, 1 = Accessed (set by CPU)
> +    UINT64    Dirty                : 1;   // 0 = Not Dirty, 1 = written by processor on access to page
> +    UINT64    MustBe1              : 1;   // Must be 1
> +    UINT64    Global               : 1;   // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
> +    UINT64    Available            : 3;   // Available for use by system software
> +    UINT64    Pat                  : 1;   //
> +    UINT64    MustBeZero           : 8;   // Must be zero
> +    UINT64    PageTableBaseAddress : 31;  // Page Table Base Address
> +    UINT64    AvailableHigh        : 11;  // Available for use by system software
> +    UINT64    Nx                   : 1;   // 0 = Execute Code, 1 = No Code Execution
> +  } Bits;
> +  UINT64    Uint64;
> +} PAGE_TABLE_ENTRY;
> +
> +//
> +// Page Table Entry 1GB
> +//
> +typedef union {
> +  struct {
> +    UINT64    Present              : 1;   // 0 = Not present in memory, 1 = Present in memory
> +    UINT64    ReadWrite            : 1;   // 0 = Read-Only, 1= Read/Write
> +    UINT64    UserSupervisor       : 1;   // 0 = Supervisor, 1=User
> +    UINT64    WriteThrough         : 1;   // 0 = Write-Back caching, 1=Write-Through caching
> +    UINT64    CacheDisabled        : 1;   // 0 = Cached, 1=Non-Cached
> +    UINT64    Accessed             : 1;   // 0 = Not accessed, 1 = Accessed (set by CPU)
> +    UINT64    Dirty                : 1;   // 0 = Not Dirty, 1 = written by processor on access to page
> +    UINT64    MustBe1              : 1;   // Must be 1
> +    UINT64    Global               : 1;   // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
> +    UINT64    Available            : 3;   // Available for use by system software
> +    UINT64    Pat                  : 1;   //
> +    UINT64    MustBeZero           : 17;  // Must be zero;
> +    UINT64    PageTableBaseAddress : 22;  // Page Table Base Address
> +    UINT64    AvailableHigh        : 11;  // Available for use by system software
> +    UINT64    Nx                   : 1;   // 0 = Execute Code, 1 = No Code Execution
> +  } Bits;
> +  UINT64    Uint64;
> +} PAGE_TABLE_1G_ENTRY;
> +
> +#pragma pack()
> +
> +//
> +// Global Descriptor Table (GDT)
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR  mGdtEntries[] = {
> +  /* selector { Global Segment Descriptor                              } */
> +  /* 0x00 */ {
> +    { 0,      0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0, 0 }
> +  },                                                                      // null descriptor
> +  /* 0x08 */ {
> +    { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
> +  },                                                                      // linear data segment descriptor
> +  /* 0x10 */ {
> +    { 0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
> +  },                                                                      // linear code segment descriptor
> +  /* 0x18 */ {
> +    { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
> +  },                                                                      // system data segment descriptor
> +  /* 0x20 */ {
> +    { 0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
> +  },                                                                      // system code segment descriptor
> +  /* 0x28 */ {
> +    { 0,      0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0, 0 }
> +  },                                                                      // spare segment descriptor
> +  /* 0x30 */ {
> +    { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 }
> +  },                                                                      // system data segment descriptor
> +  /* 0x38 */ {
> +    { 0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0 }
> +  },                                                                      // system code segment descriptor
> +  /* 0x40 */ {
> +    { 0,      0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0, 0 }
> +  },                                                                      // spare segment descriptor
> +};
> +
> +//
> +// IA32 Gdt register
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR  mGdt = {
> +  sizeof (mGdtEntries) - 1,
> +  (UINTN)mGdtEntries
> +};
> +
> +/**
> +  Calculate the total size of page table.
> +
> +  @return The size of page table.
> +
> +**/
> +UINTN
> +CalculatePageTableSize (
> +  VOID
> +  )
> +{
> +  UINT32   RegEax;
> +  UINT32   RegEdx;
> +  UINTN    TotalPagesNum;
> +  UINT8    PhysicalAddressBits;
> +  VOID     *Hob;
> +  UINT32   NumberOfPml4EntriesNeeded;
> +  UINT32   NumberOfPdpEntriesNeeded;
> +  BOOLEAN  Page1GSupport;
> +
> +  Page1GSupport = FALSE;
> +  if (PcdGetBool (PcdUse1GPageTable)) {
> +    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> +    if (RegEax >= 0x80000001) {
> +      AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> +      if ((RegEdx & BIT26) != 0) {
> +        Page1GSupport = TRUE;
> +      }
> +    }
> +  }
> +
> +  //
> +  // Get physical address bits supported.
> +  //
> +  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
> +  if (Hob != NULL) {
> +    PhysicalAddressBits = ((EFI_HOB_CPU *)Hob)->SizeOfMemorySpace;
> +  } else {
> +    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> +    if (RegEax >= 0x80000008) {
> +      AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
> +      PhysicalAddressBits = (UINT8)RegEax;
> +    } else {
> +      PhysicalAddressBits = 36;
> +    }
> +  }
> +
> +  //
> +  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
> +  //
> +  ASSERT (PhysicalAddressBits <= 52);
> +  if (PhysicalAddressBits > 48) {
> +    PhysicalAddressBits = 48;
> +  }
> +
> +  //
> +  // Calculate the table entries needed.
> +  //
> +  if (PhysicalAddressBits <= 39 ) {
> +    NumberOfPml4EntriesNeeded = 1;
> +    NumberOfPdpEntriesNeeded  = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
> +  } else {
> +    NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
> +    NumberOfPdpEntriesNeeded  = 512;
> +  }
> +
> +  if (!Page1GSupport) {
> +    TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
> +  } else {
> +    TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
> +  }
> +
> +  return EFI_PAGES_TO_SIZE (TotalPagesNum);
> +}
> +
> +/**
> +  Allocates and fills in the Page Directory and Page Table Entries to
> +  establish a 1:1 Virtual to Physical mapping.
> +
> +  @param[in]  PageTablesAddress  The base address of page table.
> +
> +**/
> +VOID
> +CreateIdentityMappingPageTables (
> +  IN  EFI_PHYSICAL_ADDRESS  PageTablesAddress
> +  )
> +{
> +  UINT32                          RegEax;
> +  UINT32                          RegEdx;
> +  UINT8                           PhysicalAddressBits;
> +  EFI_PHYSICAL_ADDRESS            PageAddress;
> +  UINTN                           IndexOfPml4Entries;
> +  UINTN                           IndexOfPdpEntries;
> +  UINTN                           IndexOfPageDirectoryEntries;
> +  UINT32                          NumberOfPml4EntriesNeeded;
> +  UINT32                          NumberOfPdpEntriesNeeded;
> +  PAGE_MAP_AND_DIRECTORY_POINTER  *PageMapLevel4Entry;
> +  PAGE_MAP_AND_DIRECTORY_POINTER  *PageMap;
> +  PAGE_MAP_AND_DIRECTORY_POINTER  *PageDirectoryPointerEntry;
> +  PAGE_TABLE_ENTRY                *PageDirectoryEntry;
> +  UINTN                           BigPageAddress;
> +  VOID                            *Hob;
> +  BOOLEAN                         Page1GSupport;
> +  PAGE_TABLE_1G_ENTRY             *PageDirectory1GEntry;
> +
> +  Page1GSupport = FALSE;
> +  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> +  if (RegEax >= 0x80000001) {
> +    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> +    if ((RegEdx & BIT26) != 0) {
> +      Page1GSupport = TRUE;
> +    }
> +  }
> +
> +  //
> +  // Get physical address bits supported.
> +  //
> +  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
> +  if (Hob != NULL) {
> +    PhysicalAddressBits = ((EFI_HOB_CPU *)Hob)->SizeOfMemorySpace;
> +  } else {
> +    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> +    if (RegEax >= 0x80000008) {
> +      AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
> +      PhysicalAddressBits = (UINT8)RegEax;
> +    } else {
> +      PhysicalAddressBits = 36;
> +    }
> +  }
> +
> +  //
> +  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
> +  //
> +  ASSERT (PhysicalAddressBits <= 52);
> +  if (PhysicalAddressBits > 48) {
> +    PhysicalAddressBits = 48;
> +  }
> +
> +  //
> +  // Calculate the table entries needed.
> +  //
> +  if (PhysicalAddressBits <= 39 ) {
> +    NumberOfPml4EntriesNeeded = 1;
> +    NumberOfPdpEntriesNeeded  = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
> +  } else {
> +    NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
> +    NumberOfPdpEntriesNeeded  = 512;
> +  }
> +
> +  //
> +  // Pre-allocate big pages to avoid later allocations.
> +  //
> +  BigPageAddress = (UINTN)PageTablesAddress;
> +
> +  //
> +  // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
> +  //
> +  PageMap         = (VOID *)BigPageAddress;
> +  BigPageAddress += SIZE_4KB;
> +
> +  PageMapLevel4Entry = PageMap;
> +  PageAddress        = 0;
> +  for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
> +    //
> +    // Each PML4 entry points to a page of Page Directory Pointer entires.
> +    // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
> +    //
> +    PageDirectoryPointerEntry = (VOID *)BigPageAddress;
> +    BigPageAddress           += SIZE_4KB;
> +
> +    //
> +    // Make a PML4 Entry
> +    //
> +    PageMapLevel4Entry->Uint64         = (UINT64)(UINTN)PageDirectoryPointerEntry;
> +    PageMapLevel4Entry->Bits.ReadWrite = 1;
> +    PageMapLevel4Entry->Bits.Present   = 1;
> +
> +    if (Page1GSupport) {
> +      PageDirectory1GEntry = (VOID *)PageDirectoryPointerEntry;
> +
> +      for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
> +        //
> +        // Fill in the Page Directory entries
> +        //
> +        PageDirectory1GEntry->Uint64         = (UINT64)PageAddress;
> +        PageDirectory1GEntry->Bits.ReadWrite = 1;
> +        PageDirectory1GEntry->Bits.Present   = 1;
> +        PageDirectory1GEntry->Bits.MustBe1   = 1;
> +      }
> +    } else {
> +      for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
> +        //
> +        // Each Directory Pointer entries points to a page of Page Directory entires.
> +        // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
> +        //
> +        PageDirectoryEntry = (VOID *)BigPageAddress;
> +        BigPageAddress    += SIZE_4KB;
> +
> +        //
> +        // Fill in a Page Directory Pointer Entries
> +        //
> +        PageDirectoryPointerEntry->Uint64         = (UINT64)(UINTN)PageDirectoryEntry;
> +        PageDirectoryPointerEntry->Bits.ReadWrite = 1;
> +        PageDirectoryPointerEntry->Bits.Present   = 1;
> +
> +        for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
> +          //
> +          // Fill in the Page Directory entries
> +          //
> +          PageDirectoryEntry->Uint64         = (UINT64)PageAddress;
> +          PageDirectoryEntry->Bits.ReadWrite = 1;
> +          PageDirectoryEntry->Bits.Present   = 1;
> +          PageDirectoryEntry->Bits.MustBe1   = 1;
> +        }
> +      }
> +
> +      for ( ; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
> +        ZeroMem (
> +          PageDirectoryPointerEntry,
> +          sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
> +          );
> +      }
> +    }
> +  }
> +
> +  //
> +  // For the PML4 entries we are not using fill in a null entry.
> +  //
> +  for ( ; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
> +    ZeroMem (
> +      PageMapLevel4Entry,
> +      sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
> +      );
> +  }
> +}
> +
> +/**
> +  If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
> +
> +  @param  Entry                     Entry of Standalone MM Foundation.
> +  @param  Context1                  A pointer to the context to pass into the EntryPoint
> +                                    function.
> +  @param  Context2                  A pointer to the context to pass into the EntryPoint
> +                                    function.
> +  @retval EFI_SUCCESS               Successfully switched to long mode and execute coalesce.
> +  @retval Others                    Failed to execute coalesce in long mode.
> +
> +**/
> +EFI_STATUS
> +ModeSwitch (
> +  IN EFI_PHYSICAL_ADDRESS  Entry,
> +  IN VOID                  *Context1,
> +  IN VOID                  *Context2
> +  )
> +{
> +  UINTN       PageTableAddress;
> +  UINTN       PageTableSize;
> +  EFI_STATUS  Status;
> +
> +  DEBUG ((DEBUG_INFO, "ModeSwitch\n"));
> +
> +  PageTableSize = CalculatePageTableSize ();
> +
> +  PageTableAddress = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PageTableSize));
> +  ASSERT (PageTableAddress != 0);
> +
> +  CreateIdentityMappingPageTables (PageTableAddress);
> +
> +  AsmWriteGdtr (&mGdt);
> +
> +  AsmWriteCr3 ((UINTN)PageTableAddress);
> +
> +  DEBUG ((DEBUG_INFO, "AsmExecute64BitCode ...\n"));
> +
> +  Status = AsmExecute64BitCode (Entry, (UINT64)(UINTN)Context1, (UINT64)(UINTN)Context2, NULL);
> +  if (Status != 0) {
> +    Status = Status | MAX_BIT;
> +  }
> +
> +  DEBUG ((DEBUG_INFO, "AsmExecute64BitCode - %r\n", Status));
> +
> +  return Status;
> +}
> +
> +/**
> +  Load SMM core to dispatch other Standalone MM drivers.
> +
> +  @param  Entry                     Entry of Standalone MM Foundation.
> +  @param  Context1                  A pointer to the context to pass into the EntryPoint
> +                                    function.
> +  @retval EFI_SUCCESS               Successfully loaded SMM core.
> +  @retval Others                    Failed to load SMM core.
> +**/
> +EFI_STATUS
> +LoadSmmCore (
> +  IN EFI_PHYSICAL_ADDRESS  Entry,
> +  IN VOID                  *Context1
> +  )
> +{
> +  STANDALONE_MM_FOUNDATION_ENTRY_POINT  EntryPoint;
> +
> +  if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
> +    return ModeSwitch (Entry, Context1, NULL);
> +  }
> +
> +  EntryPoint = (STANDALONE_MM_FOUNDATION_ENTRY_POINT)(UINTN)Entry;
> +  return EntryPoint (Context1);
> +}
> diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c
> new file mode 100644
> index 000000000000..e1ac6a2b3119
> --- /dev/null
> +++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.c
> @@ -0,0 +1,726 @@
> +/** @file
> +  SMM IPL that load the SMM Core into SMRAM at PEI stage
> +
> +  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiPei.h>
> +#include <PiSmm.h>
> +#include <Ppi/SmmAccess.h>
> +#include <Ppi/SmmControl.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/PeiServicesTablePointerLib.h>
> +#include <Library/PeiServicesLib.h>
> +#include <Library/HobLib.h>
> +#include <Library/PcdLib.h>
> +#include <Guid/SmramMemoryReserve.h>
> +#include <Guid/MmCoreData.h>
> +#include <StandaloneMmIplPei.h>
> +
> +//
> +// MM Core Private Data structure that contains the data shared between
> +// the SMM IPL and the Standalone MM Core.
> +//
> +MM_CORE_PRIVATE_DATA  mMmCorePrivateData = {
> +  MM_CORE_PRIVATE_DATA_SIGNATURE,     // Signature
> +  0,                                  // MmramRangeCount
> +  0,                                  // MmramRanges
> +  0,                                  // MmEntryPoint
> +  FALSE,                              // MmEntryPointRegistered
> +  FALSE,                              // InMm
> +  0,                                  // Mmst
> +  0,                                  // CommunicationBuffer
> +  0,                                  // BufferSize
> +  EFI_SUCCESS,                        // ReturnStatus
> +  0,                                  // MmCoreImageBase
> +  0,                                  // MmCoreImageSize
> +  0,                                  // MmCoreEntryPoint
> +  0,                                  // StandaloneBfvAddress
> +};
> +
> +//
> +// Global pointer used to access mMmCorePrivateData from outside and inside SMM
> +//
> +MM_CORE_PRIVATE_DATA  *gMmCorePrivate;
> +
> +//
> +// SMM IPL global variables
> +//
> +PEI_SMM_ACCESS_PPI    *mSmmAccess;
> +EFI_SMRAM_DESCRIPTOR  *mCurrentSmramRange;
> +BOOLEAN               mSmmLocked = FALSE;
> +EFI_PHYSICAL_ADDRESS  mSmramCacheBase;
> +UINT64                mSmramCacheSize;
> +
> +/**
> +  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               FoundAdjacentRange;
> +  EFI_SMRAM_DESCRIPTOR  *SmramRanges;
> +
> +  *SmramCacheBase = SmramRange->CpuStart;
> +  *SmramCacheSize = SmramRange->PhysicalSize;
> +
> +  SmramRanges = (EFI_SMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges;
> +  do {
> +    FoundAdjacentRange = FALSE;
> +    for (Index = 0; Index < gMmCorePrivate->MmramRangeCount; Index++) {
> +      RangeCpuStart     = SmramRanges[Index].CpuStart;
> +      RangePhysicalSize = SmramRanges[Index].PhysicalSize;
> +      if ((RangeCpuStart < *SmramCacheBase) && (*SmramCacheBase == (RangeCpuStart + RangePhysicalSize))) {
> +        *SmramCacheBase    = RangeCpuStart;
> +        *SmramCacheSize   += RangePhysicalSize;
> +        FoundAdjacentRange = TRUE;
> +      } else if (((*SmramCacheBase + *SmramCacheSize) == RangeCpuStart) && (RangePhysicalSize > 0)) {
> +        *SmramCacheSize   += RangePhysicalSize;
> +        FoundAdjacentRange = TRUE;
> +      }
> +    }
> +  } while (FoundAdjacentRange);
> +}
> +
> +/**
> +  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         = mCurrentSmramRange->CpuStart;
> +  //
> +  // Get PeHeader pointer
> +  //
> +  ImgHdr              = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8 *)ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
> +  SectionHeaderOffset = (UINTN)(
> +                                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 ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r \n", FixLoadingAddress, Status));
> +  return Status;
> +}
> +
> +/**
> +  Searches all the available firmware volumes and returns the first matching FFS section.
> +
> +  This function searches all the firmware volumes for FFS files with FV file type specified by FileType
> +  The order that the firmware volumes is searched is not deterministic. For each available FV a search
> +  is made for FFS file of type FileType. If the FV contains more than one FFS file with the same FileType,
> +  the FileInstance instance will be the matched FFS file. For each FFS file found a search
> +  is made for FFS sections of type SectionType. If the FFS file contains at least SectionInstance instances
> +  of the FFS section specified by SectionType, then the SectionInstance instance is returned in Buffer.
> +  Buffer is allocated using AllocatePool(), and the size of the allocated buffer is returned in Size.
> +  It is the caller's responsibility to use FreePool() to free the allocated buffer.
> +
> +  If Buffer is NULL, then ASSERT().
> +  If Size is NULL, then ASSERT().
> +
> +  @param  FileType             Indicates the FV file type to search for within all available FVs.
> +  @param  FileInstance         Indicates which file instance within all available FVs specified by FileType.
> +                               FileInstance starts from zero.
> +  @param  SectionType          Indicates the FFS section type to search for within the FFS file
> +                               specified by FileType with FileInstance.
> +  @param  SectionInstance      Indicates which section instance within the FFS file
> +                               specified by FileType with FileInstance to retrieve. SectionInstance starts from zero.
> +  @param  Buffer               On output, a pointer to a callee allocated buffer containing the FFS file section that was found.
> +                               Is it the caller's responsibility to free this buffer using FreePool().
> +  @param  Size                 On output, a pointer to the size, in bytes, of Buffer.
> +
> +  @retval  EFI_SUCCESS          The specified FFS section was returned.
> +  @retval  EFI_NOT_FOUND        The specified FFS section could not be found.
> +  @retval  EFI_OUT_OF_RESOURCES There are not enough resources available to retrieve the matching FFS section.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetSectionFromAnyFvByFileType  (
> +  IN  EFI_FV_FILETYPE   FileType,
> +  IN  UINTN             FileInstance,
> +  IN  EFI_SECTION_TYPE  SectionType,
> +  IN  UINTN             SectionInstance,
> +  OUT VOID              **Buffer,
> +  OUT UINTN             *Size
> +  )
> +{
> +  EFI_STATUS           Status;
> +  UINTN                FvIndex;
> +  EFI_PEI_FV_HANDLE    VolumeHandle;
> +  EFI_PEI_FILE_HANDLE  FileHandle;
> +  EFI_PE32_SECTION     *SectionData;
> +  UINT32               SectionSize;
> +
> +  //
> +  // Search all FV
> +  //
> +  VolumeHandle = NULL;
> +  for (FvIndex = 0; ; FvIndex++) {
> +    Status = PeiServicesFfsFindNextVolume (FvIndex, &VolumeHandle);
> +    if (EFI_ERROR (Status)) {
> +      break;
> +    }
> +
> +    //
> +    // Search PEIM FFS
> +    //
> +    FileHandle = NULL;
> +    Status     = PeiServicesFfsFindNextFile (FileType, VolumeHandle, &FileHandle);
> +    if (EFI_ERROR (Status)) {
> +      continue;
> +    }
> +
> +    //
> +    // Search Section
> +    //
> +    SectionData = NULL;
> +    Status      = PeiServicesFfsFindSectionData (SectionType, FileHandle, Buffer);
> +    if (EFI_ERROR (Status)) {
> +      continue;
> +    }
> +
> +    //
> +    // Great!
> +    //
> +    SectionData = (EFI_PE32_SECTION *)((UINT8 *)*Buffer - sizeof (EFI_PE32_SECTION));
> +    ASSERT (SectionData->Type == SectionType);
> +    SectionSize  = *(UINT32 *)SectionData->Size;
> +    SectionSize &= 0xFFFFFF;
> +    *Size        = SectionSize - sizeof (EFI_PE32_SECTION);
> +
> +    if (FileType == EFI_FV_FILETYPE_MM_CORE_STANDALONE) {
> +      EFI_FV_INFO  VolumeInfo;
> +      //
> +      // This is SMM BFV
> +      //
> +      Status = PeiServicesFfsGetVolumeInfo (VolumeHandle, &VolumeInfo);
> +      if (!EFI_ERROR (Status)) {
> +        gMmCorePrivate->StandaloneBfvAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)VolumeInfo.FvStart;
> +      }
> +    }
> +
> +    return EFI_SUCCESS;
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/**
> +  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;
> +  VOID                          *HobList;
> +
> +  Status = PeiServicesGetHobList (&HobList);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Search all Firmware Volumes for a PE/COFF image in a file of type SMM_CORE
> +  //
> +  Status = GetSectionFromAnyFvByFileType (
> +             EFI_FV_FILETYPE_MM_CORE_STANDALONE,
> +             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.
> +      //
> +      gMmCorePrivate->MmramRangeCount--;
> +    } else {
> +      DEBUG ((DEBUG_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));
> +
> +      gMmCorePrivate->MmCoreImageBase = ImageContext.ImageAddress;
> +      gMmCorePrivate->MmCoreImageSize = ImageContext.ImageSize;
> +      DEBUG ((DEBUG_INFO, "SmmCoreImageBase - 0x%016lx\n", gMmCorePrivate->MmCoreImageBase));
> +      DEBUG ((DEBUG_INFO, "SmmCoreImageSize - 0x%016lx\n", gMmCorePrivate->MmCoreImageSize));
> +
> +      gMmCorePrivate->MmCoreEntryPoint = ImageContext.EntryPoint;
> +
> +      //
> +      // Print debug message showing Standalone MM Core entry point address.
> +      //
> +      DEBUG ((DEBUG_INFO, "SMM IPL calling Standalone MM Core at SMRAM address - 0x%016lx\n", gMmCorePrivate->MmCoreEntryPoint));
> +
> +      //
> +      // Execute image
> +      //
> +      LoadSmmCore (ImageContext.EntryPoint, HobList);
> +    }
> +  }
> +
> +  //
> +  // If the load operation, relocate operation, or the image execution return an
> +  // error, then free memory allocated from the EFI_SRAM_DESCRIPTOR specified by
> +  // SmramRange
> +  //
> +  if (EFI_ERROR (Status)) {
> +    SmramRange->PhysicalSize += EFI_PAGES_TO_SIZE (PageCount);
> +  }
> +
> +  //
> +  // Always free memory allocated by GetFileBufferByFilePath ()
> +  //
> +  FreePool (SourceBuffer);
> +
> +  return Status;
> +}
> +
> +/**
> +  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[in]  PeiServices           Describes the list of possible PEI Services.
> +  @param[out] FullSmramRangeCount   Output pointer to full SMRAM range count.
> +
> +  @return Pointer to full SMRAM ranges.
> +
> +**/
> +EFI_SMRAM_DESCRIPTOR *
> +GetFullSmramRanges (
> +  IN  CONST EFI_PEI_SERVICES  **PeiServices,
> +  OUT       UINTN             *FullSmramRangeCount
> +  )
> +{
> +  EFI_STATUS            Status;
> +  UINTN                 Size;
> +  EFI_SMRAM_DESCRIPTOR  *FullSmramRanges;
> +  UINTN                 AdditionSmramRangeCount;
> +  UINTN                 SmramRangeCount;
> +
> +  //
> +  // Get SMRAM information.
> +  //
> +  Size   = 0;
> +  Status = mSmmAccess->GetCapabilities ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, &Size, NULL);
> +  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
> +
> +  SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
> +
> +  //
> +  // Reserve one entry SMM Core in the full SMRAM ranges.
> +  //
> +  AdditionSmramRangeCount = 1;
> +  if (PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) {
> +    //
> +    // Reserve three entries for all SMM drivers & SMM Core in the full SMRAM ranges.
> +    //
> +    AdditionSmramRangeCount = 2;
> +  }
> +
> +  //
> +  // 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);
> +  if (FullSmramRanges == NULL) {
> +    return NULL;
> +  }
> +
> +  Status = mSmmAccess->GetCapabilities ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, &Size, FullSmramRanges);
> +
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return FullSmramRanges;
> +}
> +
> +/**
> +  The Entry Point for SMM IPL
> +
> +  Load SMM Core into SMRAM.
> +
> +  @param  FileHandle  Handle of the file being invoked.
> +  @param  PeiServices Describes the list of possible PEI Services.
> +
> +  @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_PEI_FILE_HANDLE  FileHandle,
> +  IN CONST EFI_PEI_SERVICES     **PeiServices
> +  )
> +{
> +  EFI_STATUS             Status;
> +  UINTN                  Index;
> +  UINT64                 MaxSize;
> +  UINT64                 SmmCodeSize;
> +  MM_CORE_DATA_HOB_DATA  SmmCoreDataHobData;
> +  EFI_SMRAM_DESCRIPTOR   *MmramRanges;
> +
> +  //
> +  // Build Hob for SMM and DXE phase
> +  //
> +  SmmCoreDataHobData.Address = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateRuntimePages (EFI_SIZE_TO_PAGES (sizeof (mMmCorePrivateData)));
> +  ASSERT (SmmCoreDataHobData.Address != 0);
> +  gMmCorePrivate = (VOID *)(UINTN)SmmCoreDataHobData.Address;
> +  CopyMem ((VOID *)(UINTN)SmmCoreDataHobData.Address, &mMmCorePrivateData, sizeof (mMmCorePrivateData));
> +  DEBUG ((DEBUG_INFO, "gMmCorePrivate - 0x%x\n", gMmCorePrivate));
> +
> +  BuildGuidDataHob (
> +    &gMmCoreDataHobGuid,
> +    (VOID *)&SmmCoreDataHobData,
> +    sizeof (SmmCoreDataHobData)
> +    );
> +
> +  //
> +  // Get SMM Access Protocol
> +  //
> +  Status = PeiServicesLocatePpi (&gPeiSmmAccessPpiGuid, 0, NULL, (VOID **)&mSmmAccess);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Get SMRAM information
> +  //
> +  gMmCorePrivate->MmramRanges = (EFI_PHYSICAL_ADDRESS)(UINTN)GetFullSmramRanges (PeiServices, (UINTN *)&gMmCorePrivate->MmramRangeCount);
> +  ASSERT (gMmCorePrivate->MmramRanges != 0);
> +  if (gMmCorePrivate->MmramRanges == 0) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  //
> +  // Open all SMRAM ranges
> +  //
> +  Status = mSmmAccess->Open ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, 0);
> +  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;
> +  MmramRanges        = (EFI_MMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges;
> +  if (MmramRanges == NULL) {
> +    DEBUG ((DEBUG_ERROR, "Fail to retrieve MmramRanges\n"));
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < gMmCorePrivate->MmramRangeCount; Index++) {
> +    //
> +    // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization
> +    //
> +    if ((MmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
> +      continue;
> +    }
> +
> +    if (MmramRanges[Index].CpuStart >= BASE_1MB) {
> +      if ((MmramRanges[Index].CpuStart + MmramRanges[Index].PhysicalSize) <= BASE_4GB) {
> +        if (MmramRanges[Index].PhysicalSize >= MaxSize) {
> +          MaxSize            = MmramRanges[Index].PhysicalSize;
> +          mCurrentSmramRange = &MmramRanges[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);
> +
> +    //
> +    // 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);
> +    }
> +
> +    //
> +    // Load SMM Core into SMRAM and execute it from SMRAM
> +    // Note: SmramRanges specific for SMM Core will put in the gMmCorePrivate->MmramRangeCount - 1.
> +    //
> +    Status = ExecuteSmmCoreFromSmram (
> +               mCurrentSmramRange,
> +               &(((EFI_MMRAM_DESCRIPTOR *)(UINTN)gMmCorePrivate->MmramRanges)[gMmCorePrivate->MmramRangeCount - 1]),
> +               gMmCorePrivate
> +               );
> +    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"));
> +    }
> +  } 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 ((EFI_PEI_SERVICES **)PeiServices, mSmmAccess, 0);
> +    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 ((VOID *)(UINTN)gMmCorePrivate->MmramRanges);
> +
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c
> new file mode 100644
> index 000000000000..5cabf310e33c
> --- /dev/null
> +++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/X64/LoadSmmCore.c
> @@ -0,0 +1,32 @@
> +/** @file
> +  SMM IPL that load the SMM Core into SMRAM
> +
> +  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiPei.h>
> +#include <PiSmm.h>
> +#include <StandaloneMm.h>
> +
> +/**
> +  Load SMM core to dispatch other Standalone MM drivers.
> +
> +  @param  Entry                     Entry of Standalone MM Foundation.
> +  @param  Context1                  A pointer to the context to pass into the EntryPoint
> +                                    function.
> +  @retval EFI_SUCCESS               Successfully loaded SMM core.
> +  @retval Others                    Failed to load SMM core.
> +**/
> +EFI_STATUS
> +LoadSmmCore (
> +  IN EFI_PHYSICAL_ADDRESS  Entry,
> +  IN VOID                  *Context1
> +  )
> +{
> +  STANDALONE_MM_FOUNDATION_ENTRY_POINT  EntryPoint;
> +
> +  EntryPoint = (STANDALONE_MM_FOUNDATION_ENTRY_POINT)(UINTN)Entry;
> +  return EntryPoint (Context1);
> +}
> diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm
> new file mode 100644
> index 000000000000..2332d32e5885
> --- /dev/null
> +++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm
> @@ -0,0 +1,148 @@
> +;------------------------------------------------------------------------------
> +;
> +; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +; SPDX-License-Identifier: BSD-2-Clause-Patent
> +;
> +; Module Name:
> +;
> +;    Thunk32To64.nasm
> +;
> +; Abstract:
> +;
> +;   This is the assembly code to transition from long mode to compatibility
> +;   mode to execute 32-bit code and then transit back to long mode.
> +;
> +;------------------------------------------------------------------------------
> +
> +    SECTION .text
> +
> +;------------------------------------------------------------------------------
> +; Procedure:    AsmExecute64BitCode
> +;
> +; Input:        None
> +;
> +; Output:       None
> +;
> +; Prototype:    UINT32
> +;               AsmExecute64BitCode (
> +;                 IN UINT64           Function,
> +;                 IN UINT64           Param1,
> +;                 IN UINT64           Param2,
> +;                 IN IA32_DESCRIPTOR  *InternalGdtr
> +;                 );
> +;
> +;
> +; Description:  A thunk function to execute 32-bit code in long mode.
> +;
> +;------------------------------------------------------------------------------
> +global ASM_PFX(AsmExecute64BitCode)
> +ASM_PFX(AsmExecute64BitCode):
> +;
> +; +---------+
> +; | EIP(64) |
> +; +---------+
> +; | CS (64) |
> +; +---------+
> +; | EIP(32) |
> +; +---------+
> +; | CS (32) |<-ESP (16 bytes aligned)
> +; +---------+
> +; | ...     |
> +; +---------+
> +; | ebx     |<-EBP
> +; +---------+
> +; | ebp     |<-EBP + 4
> +; +---------+
> +; | esi     |<-EBP + 8
> +; +---------+
> +; | edi     |<-EBP + 12
> +; +---------+
> +; | RFlags  |<-EBP + 16
> +; +---------+
> +; | RetAddr |<-EBP (org)
> +; +---------+
> +; | Func    |<-EBP + 24
> +; | Func    |
> +; +---------+
> +; | Param1  |<-EBP + 32
> +; | Param1  |
> +; +---------+
> +; | Param2  |<-EBP + 40
> +; | Param2  |
> +; +---------+
> +; | Gdtr    |
> +; +---------+
> +;
> +    ;
> +    ; Save general purpose register and RFlags register
> +    ;
> +    pushfd
> +    push    edi
> +    push    esi
> +    push    ebp
> +    push    ebx
> +    mov     ebp, esp
> +
> +    and     esp, 0FFFFFFF0h
> +
> +    push    010h                        ; protected mode selector on stack
> +    mov     eax, Compatible             ; offset for LongMode
> +    push    eax                         ; offset on stack
> +
> +    push    038h                        ; long mode selector on stack
> +    mov     eax, LongMode               ; offset for LongMode
> +    push    eax                         ; offset on stack
> +
> +    mov     eax, cr4
> +    or      al, 020h
> +    mov     cr4, eax                    ; enable PAE
> +    mov     ecx, 0c0000080h
> +    rdmsr
> +    or      ah, 1                       ; set LME
> +    wrmsr
> +    mov     eax, cr0
> +    bts     eax, 31                     ; set PG
> +    mov     cr0, eax                    ; enable paging
> +    retf                                ; topmost 2 dwords hold the address
> +LongMode:                               ; long mode starts here
> +
> +    ; Call long mode function
> +    DB      67h, 48h                    ; 32-bit address size, 64-bit operand size
> +    mov     eax, [ebp + 24]             ; mov rbx, [ebp + 24]
> +    DB      67h, 48h
> +    mov     ecx, [ebp + 24 + 8]         ; mov rcx, [ebp + 24 + 8]
> +    DB      67h, 48h
> +    mov     edx, [ebp + 24 + 16]        ; mov rdx, [ebp + 24 + 16]
> +
> +    DB      48h
> +    add     esp, -20h                   ; add rsp, -20h
> +    call    eax                         ; call rax
> +    DB      48h
> +    add     esp, 20h                    ; add rsp, 20h
> +
> +    ; after long mode function call
> +    mov     ebx, eax
> +
> +    retf
> +Compatible:
> +    mov     ecx, cr0
> +    btc     ecx, 31                     ; clear PG
> +    mov     cr0, ecx                    ; disable paging
> +    mov     ecx, 0C0000080h
> +    rdmsr
> +    btc     eax, 8                      ; clear LME
> +    wrmsr
> +
> +    ;
> +    ; Restore C register and eax hold the return status from 32-bit function.
> +    ; Note: Do not touch rax from now which hold the return value from IA32 function
> +    ;
> +    mov     eax, ebx                    ; put return status to EAX
> +    mov     esp, ebp                    ; restore stack pointer
> +    pop     ebx
> +    pop     ebp
> +    pop     esi
> +    pop     edi
> +    popfd
> +
> +    ret
> diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h
> new file mode 100644
> index 000000000000..5e27dfe1454c
> --- /dev/null
> +++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.h
> @@ -0,0 +1,47 @@
> +/** @file
> +  Private header with declarations and definitions specific to the Standalone
> +  MM IPL PEI driver
> +
> +  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef STANDALONE_MM_IPL_PEI_H_
> +#define STANDALONE_MM_IPL_PEI_H_
> +
> +/**
> +  Load SMM core to dispatch other Standalone MM drivers.
> +
> +  @param  Entry                     Entry of Standalone MM Foundation.
> +  @param  Context1                  A pointer to the context to pass into the EntryPoint
> +                                    function.
> +  @retval EFI_SUCCESS               Successfully loaded SMM core.
> +  @retval Others                    Failed to load SMM core.
> +**/
> +EFI_STATUS
> +LoadSmmCore (
> +  IN EFI_PHYSICAL_ADDRESS  Entry,
> +  IN VOID                  *Context1
> +  );
> +
> +/**
> +  Assembly function to transition from long mode to compatibility mode to
> +  execute 32-bit code and then transit back to long mode.
> +
> +  @param[in] Function     The 32bit code entry to be executed.
> +  @param[in] Param1       The first parameter to pass to 32bit code
> +  @param[in] Param2       The second parameter to pass to 32bit code
> +  @param[in] InternalGdtr The GDT and GDT descriptor used by this library
> +
> +  @return status.
> +**/
> +UINT32
> +AsmExecute64BitCode (
> +  IN UINT64           Function,
> +  IN UINT64           Param1,
> +  IN UINT64           Param2,
> +  IN IA32_DESCRIPTOR  *InternalGdtr
> +  );
> +
> +#endif
> diff --git a/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf
> new file mode 100644
> index 000000000000..e531280abdfe
> --- /dev/null
> +++ b/StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf
> @@ -0,0 +1,74 @@
> +## @file
> +#  This module provide a Standalone SMM compliant implementation of SMM IPL PEIM.
> +#
> +#  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005
> +  BASE_NAME                      = StandaloneMmIplPei
> +  FILE_GUID                      = 578A0D17-2DC0-4C7D-A121-D8D771923BB0
> +  MODULE_TYPE                    = PEIM
> +  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]
> +  StandaloneMmIplPei.h
> +  StandaloneMmIplPei.c
> +
> +[Sources.Ia32]
> +  Ia32/LoadSmmCore.c
> +  Ia32/Thunk32To64.nasm
> +
> +[Sources.X64]
> +  X64/LoadSmmCore.c
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  StandaloneMmPkg/StandaloneMmPkg.dec
> +
> +[LibraryClasses]
> +  PeimEntryPoint
> +  PeiServicesTablePointerLib
> +  PeiServicesLib
> +  BaseLib
> +  BaseMemoryLib
> +  PeCoffLib
> +  CacheMaintenanceLib
> +  MemoryAllocationLib
> +  DebugLib
> +  HobLib
> +  IntrinsicLib
> +
> +[Guids]
> +  gMmCoreDataHobGuid
> +
> +[Ppis]
> +  gPeiSmmAccessPpiGuid                     ## CONSUMES
> +  gPeiSmmControlPpiGuid                    ## CONSUMES
> +
> +[FeaturePcd.IA32]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode      ## CONSUMES
> +
> +[Pcd.IA32]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable              ## SOMETIMES_CONSUMES
> +
> +[Pcd]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdLoadFixAddressSmmCodePageNumber     ## SOMETIMES_CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdLoadModuleAtFixAddressEnable        ## CONSUMES
> +
> +[Depex]
> +  gPeiSmmAccessPpiGuid AND
> +  gPeiSmmControlPpiGuid
> +
> diff --git a/StandaloneMmPkg/StandaloneMmPkg.ci.yaml b/StandaloneMmPkg/StandaloneMmPkg.ci.yaml
> index 4777532a7ede..872f7958be84 100644
> --- a/StandaloneMmPkg/StandaloneMmPkg.ci.yaml
> +++ b/StandaloneMmPkg/StandaloneMmPkg.ci.yaml
> @@ -78,7 +78,9 @@
>      ## options defined .pytool/Plugin/SpellCheck
>      "SpellCheck": {
>          "AuditOnly": False,
> -        "IgnoreFiles": [],           # use gitignore syntax to ignore errors
> +        "IgnoreFiles": [
> +            "Drivers/StandaloneMmIplPei/Ia32/Thunk32To64.nasm"
> +        ],           # use gitignore syntax to ignore errors
>                                       # in matching files
>          "ExtendWords": [
>              "Bsymbolic",
> diff --git a/StandaloneMmPkg/StandaloneMmPkg.dsc b/StandaloneMmPkg/StandaloneMmPkg.dsc
> index 8012f93b7dcc..d88471fe82be 100644
> --- a/StandaloneMmPkg/StandaloneMmPkg.dsc
> +++ b/StandaloneMmPkg/StandaloneMmPkg.dsc
> @@ -20,7 +20,7 @@
>    PLATFORM_VERSION               = 1.0
>    DSC_SPECIFICATION              = 0x00010011
>    OUTPUT_DIRECTORY               = Build/StandaloneMm
> -  SUPPORTED_ARCHITECTURES        = AARCH64|X64|ARM
> +  SUPPORTED_ARCHITECTURES        = AARCH64|X64|ARM|IA32
>    BUILD_TARGETS                  = DEBUG|RELEASE
>    SKUID_IDENTIFIER               = DEFAULT
>
> @@ -60,6 +60,14 @@
>    StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf
>    VariableMmDependency|StandaloneMmPkg/Library/VariableMmDependency/VariableMmDependency.inf
>
> +[LibraryClasses.common.PEIM]
> +  HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
> +  PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
> +  MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
> +  PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
> +  PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
> +  IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf
> +
>  [LibraryClasses.AARCH64, LibraryClasses.ARM]
>    ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
>    StandaloneMmMmuLib|ArmPkg/Library/StandaloneMmMmuLib/ArmMmuStandaloneMmLib.inf
> @@ -104,7 +112,7 @@
>  #       generated for it, but the binary will not be put into any firmware volume.
>  #
>  ###################################################################################################
> -[Components.common]
> +[Components.AARCH64, Components.ARM, Components.X64]
>    #
>    # MM Core
>    #
> @@ -122,6 +130,9 @@
>    StandaloneMmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf
>    StandaloneMmPkg/Library/StandaloneMmPeCoffExtraActionLib/StandaloneMmPeCoffExtraActionLib.inf
>
> +[Components.X64, Components.IA32]
> +  StandaloneMmPkg/Drivers/StandaloneMmIplPei/StandaloneMmIplPei.inf
> +
>  ###################################################################################################
>  #
>  # BuildOptions Section - Define the module specific tool chain flags that should be used as
> --
> 2.26.2.windows.1
>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-11-09 10:20 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-11-09  6:16 [PATCH v1 1/1] StandaloneMmPkg: Add StandaloneMmIplPei driver Hongbin1 Zhang
2022-11-09 10:19 ` Ard Biesheuvel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox