public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload
@ 2021-10-22 15:46 Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 1/8] UefiPayloadPkg: Add a common SmmAccessDxe module Guo Dong
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

V3: Add SMM communication region EFI_ALLOCATED check
    in UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
V2: Added SMM communication region size check
    Fixed ECC reported issues and other minor update.

https://bugzilla.tianocore.org/show_bug.cgi?id=3084

Currently UEFI payload uses emulated variable driver. So it could
not support secureboot and measured boot since both need NV variable
support.

EDKII already has SMM modules and variable modules. And modern Intel
platform supports SPI flash hardware sequence to operate flash. So it
is possible to have a common SPI module for Intel platforms.

This patch enhances UEFI payload to support SMM variable with a
common SPI library for Intel platforms. To avoid impact existing
usage, all the new modules are included under SMM_ENABLE and
VARIABLE_SUPPORT and by default SMM variable is not be enabled.

SMM variable could be enabled only when UNIVERSAL_PAYLOAD is set
since non-universal payload need update ParseLib to provide SMM
variable related infromation which is not in the plan.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com

Guo Dong (8):
  UefiPayloadPkg: Add a common SmmAccessDxe module
  UefiPayloadPkg: Add a common SMM control Runtime DXE module
  UefiPayloadPkg: Add bootloader SMM support module
  UefiPayloadPkg: Add SpiFlashLib
  UefiPayloadPkg: Add FlashDeviceLib
  UefiPayloadPkg: Add a common FVB SMM module
  UefiPayloadPkg: Add a SMM dispatch module
  UefiPayloadPkg: Add SMM support and SMM variable support

 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c    |  431 +++++++
 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h    |   41 +
 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf  |   49 +
 UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c        |  151 +++
 UefiPayloadPkg/FvbRuntimeDxe/FvbService.c     | 1088 +++++++++++++++++
 UefiPayloadPkg/FvbRuntimeDxe/FvbService.h     |  187 +++
 UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c  |  139 +++
 UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf       |   71 ++
 UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h   |   69 ++
 .../Include/Guid/NvVariableInfoGuid.h         |   24 +
 .../Include/Guid/SmmRegisterInfoGuid.h        |   48 +
 .../Include/Guid/SmmS3CommunicationInfoGuid.h |   54 +
 .../Include/Guid/SpiFlashInfoGuid.h           |   38 +
 .../Include/Library/FlashDeviceLib.h          |  108 ++
 UefiPayloadPkg/Include/Library/SpiFlashLib.h  |  215 ++++
 .../Library/FlashDeviceLib/FlashDeviceLib.c   |  165 +++
 .../Library/FlashDeviceLib/FlashDeviceLib.inf |   38 +
 UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c   |  173 +++
 UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h  |  129 ++
 .../Library/SpiFlashLib/SpiCommon.h           |  208 ++++
 .../Library/SpiFlashLib/SpiFlashLib.c         |  857 +++++++++++++
 .../Library/SpiFlashLib/SpiFlashLib.inf       |   48 +
 .../PchSmiDispatchSmm/PchSmiDispatchSmm.c     |  455 +++++++
 .../PchSmiDispatchSmm/PchSmiDispatchSmm.h     |   37 +
 .../PchSmiDispatchSmm/PchSmiDispatchSmm.inf   |   51 +
 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c    |  254 ++++
 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h    |   37 +
 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf  |   51 +
 .../SmmControlRuntimeDxe.c                    |  256 ++++
 .../SmmControlRuntimeDxe.inf                  |   50 +
 UefiPayloadPkg/UefiPayloadPkg.dec             |   10 +
 UefiPayloadPkg/UefiPayloadPkg.dsc             |  101 +-
 UefiPayloadPkg/UefiPayloadPkg.fdf             |   38 +-
 33 files changed, 5660 insertions(+), 11 deletions(-)
 create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
 create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
 create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
 create mode 100644 UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h
 create mode 100644 UefiPayloadPkg/Include/Guid/SmmRegisterInfoGuid.h
 create mode 100644 UefiPayloadPkg/Include/Guid/SmmS3CommunicationInfoGuid.h
 create mode 100644 UefiPayloadPkg/Include/Guid/SpiFlashInfoGuid.h
 create mode 100644 UefiPayloadPkg/Include/Library/FlashDeviceLib.h
 create mode 100644 UefiPayloadPkg/Include/Library/SpiFlashLib.h
 create mode 100644 UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.c
 create mode 100644 UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiCommon.h
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.c
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf
 create mode 100644 UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c
 create mode 100644 UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.h
 create mode 100644 UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf
 create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c
 create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h
 create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf
 create mode 100644 UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.c
 create mode 100644 UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf

-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 1/8] UefiPayloadPkg: Add a common SmmAccessDxe module
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 2/8] UefiPayloadPkg: Add a common SMM control Runtime DXE module Guo Dong
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

SmmAccessDxe module would consume EFI_SMRAM_HOB_DESCRIPTOR_BLOCK HOB to
produce SMM access protocol gEfiSmmAccess2ProtocolGuid (open, close, lock,
and GetCapabilities.)

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c   | 254 +++++++++++++++++++
 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h   |  37 +++
 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf |  51 ++++
 3 files changed, 342 insertions(+)
 create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c
 create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h
 create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf

diff --git a/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c b/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c
new file mode 100644
index 0000000000..ce7026dc9a
--- /dev/null
+++ b/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c
@@ -0,0 +1,254 @@
+/** @file
+  This driver publishes the SMM Access 2 Protocol.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "SmmAccessDxe.h"
+
+SMM_ACCESS_PRIVATE_DATA         mSmmAccess;
+
+/**
+   Update region state from SMRAM description
+
+   @param[in] OrLogic     Indicate to use OR if true or AND if false.
+   @param[in] Value       The value to set to region state based on OrLogic.
+
+**/
+VOID
+SyncRegionState2SmramDesc(
+  IN BOOLEAN                OrLogic,
+  IN UINT64                 Value
+  )
+{
+  UINT32                    Index;
+
+  for (Index = 0; Index < mSmmAccess.NumberRegions; Index++) {
+    if (OrLogic) {
+      mSmmAccess.SmramDesc[Index].RegionState |= Value;
+    } else {
+      mSmmAccess.SmramDesc[Index].RegionState &= Value;
+    }
+  }
+}
+
+/**
+   This routine accepts a request to "open" a region of SMRAM.  The
+   region could be legacy ABSEG, HSEG, or TSEG near top of physical memory.
+   The use of "open" means that the memory is visible from all boot-service
+   and SMM agents.
+
+   @param This                    Pointer to the SMM Access Interface.
+
+   @retval EFI_SUCCESS            The region was successfully opened.
+   @retval EFI_DEVICE_ERROR       The region could not be opened because locked by chipset.
+   @retval EFI_INVALID_PARAMETER  The descriptor index was out of bounds.
+
+**/
+EFI_STATUS
+EFIAPI
+Open (
+  IN EFI_SMM_ACCESS2_PROTOCOL          *This
+  )
+{
+  if ((mSmmAccess.SmmRegionState & EFI_SMRAM_LOCKED) != 0) {
+    //
+    // Cannot open a "locked" region
+    //
+    DEBUG ((DEBUG_INFO, "Cannot open the locked SMRAM Region\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  mSmmAccess.SmmRegionState &= ~(EFI_SMRAM_CLOSED | EFI_ALLOCATED);
+  SyncRegionState2SmramDesc(FALSE, (UINT64)(UINTN)(~(EFI_SMRAM_CLOSED | EFI_ALLOCATED)));
+
+  mSmmAccess.SmmRegionState |= EFI_SMRAM_OPEN;
+  SyncRegionState2SmramDesc(TRUE, EFI_SMRAM_OPEN);
+  mSmmAccess.SmmAccess.OpenState = TRUE;
+
+  return EFI_SUCCESS;
+}
+
+/**
+   This routine accepts a request to "close" a region of SMRAM. The region
+   could be legacy AB or TSEG near top of physical memory.
+   The use of "close" means that the memory is only visible from SMM agents,
+   not from BS or RT code.
+
+   @param This                      Pointer to the SMM Access Interface.
+
+   @retval EFI_SUCCESS              The region was successfully closed.
+   @retval EFI_DEVICE_ERROR         The region could not be closed because locked by
+                                    chipset.
+   @retval EFI_INVALID_PARAMETER    The descriptor index was out of bounds.
+
+**/
+EFI_STATUS
+EFIAPI
+Close (
+  IN EFI_SMM_ACCESS2_PROTOCOL    *This
+  )
+{
+  if ((mSmmAccess.SmmRegionState & EFI_SMRAM_LOCKED) != 0) {
+    //
+    // Cannot close a "locked" region
+    //
+    DEBUG ((DEBUG_INFO, "Cannot close the locked SMRAM Region\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  if ((mSmmAccess.SmmRegionState & EFI_SMRAM_CLOSED) != 0) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  mSmmAccess.SmmRegionState &= ~EFI_SMRAM_OPEN;
+  SyncRegionState2SmramDesc(FALSE, (UINT64)(UINTN)(~EFI_SMRAM_OPEN));
+
+  mSmmAccess.SmmRegionState |= (EFI_SMRAM_CLOSED | EFI_ALLOCATED);
+  SyncRegionState2SmramDesc(TRUE, EFI_SMRAM_CLOSED | EFI_ALLOCATED);
+
+  mSmmAccess.SmmAccess.OpenState = FALSE;
+
+  return EFI_SUCCESS;
+}
+
+/**
+   This routine accepts a request to "lock" SMRAM.  The
+   region could be legacy AB or TSEG near top of physical memory.
+   The use of "lock" means that the memory can no longer be opened
+   to BS state.
+
+   @param This                     Pointer to the SMM Access Interface.
+
+   @retval EFI_SUCCESS             The region was successfully locked.
+   @retval EFI_DEVICE_ERROR        The region could not be locked because at least
+                                   one range is still open.
+   @retval EFI_INVALID_PARAMETER   The descriptor index was out of bounds.
+
+**/
+EFI_STATUS
+EFIAPI
+Lock (
+  IN EFI_SMM_ACCESS2_PROTOCOL    *This
+  )
+{
+  if (mSmmAccess.SmmAccess.OpenState) {
+    DEBUG ((DEBUG_INFO, "Cannot lock SMRAM when it is still open\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  mSmmAccess.SmmRegionState |= EFI_SMRAM_LOCKED;
+  SyncRegionState2SmramDesc(TRUE, EFI_SMRAM_LOCKED);
+  mSmmAccess.SmmAccess.LockState = TRUE;
+  return EFI_SUCCESS;
+}
+
+/**
+   This routine services a user request to discover the SMRAM
+   capabilities of this platform.  This will report the possible
+   ranges that are possible for SMRAM access, based upon the
+   memory controller capabilities.
+
+   @param This            Pointer to the SMRAM Access Interface.
+   @param SmramMapSize    Pointer to the variable containing size of the
+                          buffer to contain the description information.
+   @param SmramMap        Buffer containing the data describing the Smram
+                          region descriptors.
+
+   @retval EFI_BUFFER_TOO_SMALL  The user did not provide a sufficient buffer.
+   @retval EFI_SUCCESS           The user provided a sufficiently-sized buffer.
+
+**/
+EFI_STATUS
+EFIAPI
+GetCapabilities (
+  IN CONST EFI_SMM_ACCESS2_PROTOCOL     *This,
+  IN OUT   UINTN                        *SmramMapSize,
+  IN OUT   EFI_SMRAM_DESCRIPTOR         *SmramMap
+  )
+{
+  EFI_STATUS                            Status;
+  UINTN                                 NecessaryBufferSize;
+
+  NecessaryBufferSize = mSmmAccess.NumberRegions * sizeof(EFI_SMRAM_DESCRIPTOR);
+  if (*SmramMapSize < NecessaryBufferSize) {
+    Status = EFI_BUFFER_TOO_SMALL;
+  } else {
+    CopyMem(SmramMap, mSmmAccess.SmramDesc, NecessaryBufferSize);
+    Status = EFI_SUCCESS;
+  }
+
+  *SmramMapSize = NecessaryBufferSize;
+  return Status;
+}
+
+/**
+  This function installs EFI_SMM_ACCESS_PROTOCOL.
+
+  @param  ImageHandle Handle for the image of this driver
+  @param  SystemTable Pointer to the EFI System Table
+
+  @retval EFI_UNSUPPORTED There's no Intel ICH on this platform
+  @return The status returned from InstallProtocolInterface().
+
+**/
+EFI_STATUS
+EFIAPI
+SmmAccessEntryPoint (
+  IN EFI_HANDLE                   ImageHandle,
+  IN EFI_SYSTEM_TABLE             *SystemTable
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_HOB_GUID_TYPE               *GuidHob;
+  UINT32                          SmmRegionNum;
+  EFI_SMRAM_HOB_DESCRIPTOR_BLOCK  *SmramHob;
+  UINT32                          Index;
+
+  //
+  // Get SMRAM info HOB
+  //
+  GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
+  if (GuidHob == NULL) {
+    DEBUG ((DEBUG_INFO, "SMRAM HOB NOT found\n"));
+    return EFI_NOT_FOUND;
+  }
+  SmramHob     = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *) GET_GUID_HOB_DATA(GuidHob);
+  SmmRegionNum = SmramHob->NumberOfSmmReservedRegions;
+  mSmmAccess.SmramDesc = AllocateZeroPool (sizeof (EFI_SMRAM_DESCRIPTOR) * SmmRegionNum);
+  if (mSmmAccess.SmramDesc == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  CopyMem (mSmmAccess.SmramDesc, &SmramHob->Descriptor, sizeof (EFI_SMRAM_DESCRIPTOR) * SmmRegionNum);
+
+  DEBUG ((DEBUG_INFO, "NumberOfSmmReservedRegions = 0x%x\n", SmmRegionNum));
+  for (Index = 0; Index < SmmRegionNum; Index++) {
+    DEBUG ((DEBUG_INFO, "%d: base=0x%x, size = 0x%x, State=0x%x\n",Index,
+       SmramHob->Descriptor[Index].PhysicalStart,
+       SmramHob->Descriptor[Index].PhysicalSize,
+       SmramHob->Descriptor[Index].RegionState));
+     mSmmAccess.SmramDesc[Index].RegionState &= EFI_ALLOCATED;
+     mSmmAccess.SmramDesc[Index].RegionState |= EFI_SMRAM_CLOSED | EFI_CACHEABLE;
+  }
+
+  mSmmAccess.Signature                    = SMM_ACCESS_PRIVATE_DATA_SIGNATURE;
+  mSmmAccess.NumberRegions                = SmmRegionNum;
+  mSmmAccess.SmmAccess.Open               = Open;
+  mSmmAccess.SmmAccess.Close              = Close;
+  mSmmAccess.SmmAccess.Lock               = Lock;
+  mSmmAccess.SmmAccess.GetCapabilities    = GetCapabilities;
+  mSmmAccess.SmmAccess.LockState          = FALSE;
+  mSmmAccess.SmmAccess.OpenState          = FALSE;
+  mSmmAccess.SmmRegionState               = EFI_SMRAM_CLOSED;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mSmmAccess.Handle,
+                  &gEfiSmmAccess2ProtocolGuid,
+                  &mSmmAccess.SmmAccess,
+                  NULL
+                  );
+
+  return Status;
+}
diff --git a/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h b/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h
new file mode 100644
index 0000000000..b6d76daef3
--- /dev/null
+++ b/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h
@@ -0,0 +1,37 @@
+/** @file
+  The header file of SMM access DXE.
+
+Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMM_ACCESS_DRIVER_H_
+#define SMM_ACCESS_DRIVER_H_
+
+#include <PiDxe.h>
+#include <Protocol/SmmAccess2.h>
+#include <Library/HobLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Guid/SmramMemoryReserve.h>
+
+
+#define  SMM_ACCESS_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('S', 'M', 'M', 'A')
+
+typedef struct {
+  UINTN                           Signature;
+  EFI_HANDLE                      Handle;
+  EFI_SMM_ACCESS2_PROTOCOL        SmmAccess;
+  //
+  // Local Data for SMM Access interface goes here
+  //
+  UINT32                          SmmRegionState;
+  UINT32                          NumberRegions;
+  EFI_SMRAM_DESCRIPTOR            *SmramDesc;
+} SMM_ACCESS_PRIVATE_DATA;
+
+#endif
diff --git a/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf b/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf
new file mode 100644
index 0000000000..aac5ee8f28
--- /dev/null
+++ b/UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf
@@ -0,0 +1,51 @@
+## @file
+# SMM Access 2 Protocol Dxe Driver
+#
+# This module produces the SMM Access 2 Protocol.
+#
+#  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SmmAccessDxe
+  FILE_GUID                      = 47579CF5-1E4F-4b41-99BB-A5C334846D3B
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = SmmAccessEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SmmAccessDxe.c
+  SmmAccessDxe.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  DebugLib
+  BaseLib
+  BaseMemoryLib
+  MemoryAllocationLib
+  HobLib
+
+[Guids]
+  gEfiSmmSmramMemoryGuid
+
+[Protocols]
+  gEfiSmmAccess2ProtocolGuid                    ## PRODUCES
+
+[Depex]
+  TRUE
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 2/8] UefiPayloadPkg: Add a common SMM control Runtime DXE module
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 1/8] UefiPayloadPkg: Add a common SmmAccessDxe module Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 3/8] UefiPayloadPkg: Add bootloader SMM support module Guo Dong
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

This module consumes SMM Registers HOB (SMI_GBL_EN and SMI_APM_EN) to
install SMM control 2 protocol gEfiSmmControl2ProtocolGuid.
The protocol activate() would set SMI_GBL_EN and SMI_APM_EN and trigger
SMI by writing to IO port 0xB3 and 0xB2.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 .../Include/Guid/SmmRegisterInfoGuid.h        |  48 ++++
 .../SmmControlRuntimeDxe.c                    | 256 ++++++++++++++++++
 .../SmmControlRuntimeDxe.inf                  |  50 ++++
 UefiPayloadPkg/UefiPayloadPkg.dec             |   2 +
 4 files changed, 356 insertions(+)
 create mode 100644 UefiPayloadPkg/Include/Guid/SmmRegisterInfoGuid.h
 create mode 100644 UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.c
 create mode 100644 UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf

diff --git a/UefiPayloadPkg/Include/Guid/SmmRegisterInfoGuid.h b/UefiPayloadPkg/Include/Guid/SmmRegisterInfoGuid.h
new file mode 100644
index 0000000000..8a1d3d7486
--- /dev/null
+++ b/UefiPayloadPkg/Include/Guid/SmmRegisterInfoGuid.h
@@ -0,0 +1,48 @@
+/** @file
+  This file defines the SMM info hob structure.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PAYLOAD_SMM_REGISTER_INFO_GUID_H_
+#define PAYLOAD_SMM_REGISTER_INFO_GUID_H_
+
+#include <IndustryStandard/Acpi.h>
+
+///
+/// SMM Information GUID
+///
+extern EFI_GUID gSmmRegisterInfoGuid;
+
+///
+/// Reuse ACPI definition
+/// AddressSpaceId(0xC0-0xFF) is defined by OEM for MSR and other spaces
+///
+typedef EFI_ACPI_3_0_GENERIC_ADDRESS_STRUCTURE  PLD_GENERIC_ADDRESS;
+
+#define REGISTER_ID_SMI_GBL_EN         1
+#define REGISTER_ID_SMI_GBL_EN_LOCK    2
+#define REGISTER_ID_SMI_EOS            3
+#define REGISTER_ID_SMI_APM_EN         4
+#define REGISTER_ID_SMI_APM_STS        5
+
+#pragma pack(1)
+typedef struct {
+  UINT64                Id;
+  UINT64                Value;
+  PLD_GENERIC_ADDRESS   Address;
+} PLD_GENERIC_REGISTER;
+
+typedef struct {
+  UINT16                Revision;
+  UINT16                Reserved;
+  UINT32                Count;
+  PLD_GENERIC_REGISTER  Registers[0];
+} PLD_SMM_REGISTERS;
+
+
+#pragma pack()
+
+#endif
diff --git a/UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.c b/UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.c
new file mode 100644
index 0000000000..6dd91e2601
--- /dev/null
+++ b/UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.c
@@ -0,0 +1,256 @@
+/** @file
+  This module produces the SMM Control2 Protocol
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Protocol/SmmControl2.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/HobLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Guid/SmmRegisterInfoGuid.h>
+
+#define SMM_DATA_PORT       0xB3
+#define SMM_CONTROL_PORT    0xB2
+
+typedef struct {
+  UINT8   GblBitOffset;
+  UINT8   ApmBitOffset;
+  UINT32  Address;
+} SMM_CONTROL2_REG;
+
+SMM_CONTROL2_REG        mSmiCtrlReg;
+
+/**
+  Invokes SMI activation from either the preboot or runtime environment.
+
+  This function generates an SMI.
+
+  @param[in]     This                The EFI_SMM_CONTROL2_PROTOCOL instance.
+  @param[in,out] CommandPort         The value written to the command port.
+  @param[in,out] DataPort            The value written to the data port.
+  @param[in]     Periodic            Optional mechanism to engender a periodic stream.
+  @param[in]     ActivationInterval  Optional parameter to repeat at this period one
+                                     time or, if the Periodic Boolean is set, periodically.
+
+  @retval EFI_SUCCESS            The SMI has been engendered.
+  @retval EFI_DEVICE_ERROR       The timing is unsupported.
+  @retval EFI_INVALID_PARAMETER  The activation period is unsupported.
+  @retval EFI_INVALID_PARAMETER  The last periodic activation has not been cleared.
+  @retval EFI_NOT_STARTED        The MM base service has not been initialized.
+**/
+EFI_STATUS
+EFIAPI
+Activate (
+  IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
+  IN OUT  UINT8                          *CommandPort       OPTIONAL,
+  IN OUT  UINT8                          *DataPort          OPTIONAL,
+  IN      BOOLEAN                        Periodic           OPTIONAL,
+  IN      EFI_SMM_PERIOD                 ActivationInterval OPTIONAL
+  )
+{
+  UINT32                                 SmiEn;
+  UINT32                                 SmiEnableBits;
+
+  if (Periodic) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SmiEn         = IoRead32 (mSmiCtrlReg.Address);
+  SmiEnableBits = (1 << mSmiCtrlReg.GblBitOffset) | (1 << mSmiCtrlReg.ApmBitOffset);
+  if ((SmiEn & SmiEnableBits) != SmiEnableBits) {
+    //
+    // Set the "global SMI enable" bit and APM bit
+    //
+    IoWrite32 (mSmiCtrlReg.Address, SmiEn | SmiEnableBits);
+  }
+
+  IoWrite8 (SMM_DATA_PORT,    DataPort    == NULL ? 0 : *DataPort);
+  IoWrite8 (SMM_CONTROL_PORT, CommandPort == NULL ? 0 : *CommandPort);
+  return EFI_SUCCESS;
+}
+
+/**
+  Clears an SMI.
+
+  @param  This      Pointer to an instance of EFI_SMM_CONTROL2_PROTOCOL
+  @param  Periodic  TRUE to indicate a periodical SMI
+
+  @return Return value from SmmClear ()
+
+**/
+EFI_STATUS
+EFIAPI
+Deactivate (
+  IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
+  IN       BOOLEAN                       Periodic
+  )
+{
+  if (Periodic) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Temporarily do nothing here
+  //
+  return EFI_SUCCESS;
+}
+
+///
+/// SMM COntrol2 Protocol instance
+///
+EFI_SMM_CONTROL2_PROTOCOL mSmmControl2 = {
+  Activate,
+  Deactivate,
+  0
+};
+
+/**
+  Get specified SMI register based on given register ID
+
+  @param[in]  SmmRegister  SMI related register array from bootloader
+  @param[in]  Id           The register ID to get.
+
+  @retval NULL             The register is not found or the format is not expected.
+  @return smi register
+
+**/
+PLD_GENERIC_REGISTER *
+GetSmmCtrlRegById (
+  IN PLD_SMM_REGISTERS    *SmmRegister,
+  IN UINT32               Id
+  )
+{
+  UINT32                  Index;
+  PLD_GENERIC_REGISTER    *PldReg;
+
+  PldReg = NULL;
+  for (Index = 0; Index < SmmRegister->Count; Index++) {
+    if (SmmRegister->Registers[Index].Id == Id) {
+      PldReg = &SmmRegister->Registers[Index];
+      break;
+    }
+  }
+
+  if (PldReg == NULL) {
+    DEBUG ((DEBUG_INFO, "Register %d not found.\n", Id));
+    return NULL;
+  }
+
+  //
+  // Checking the register if it is expected.
+  //
+  if ((PldReg->Address.AccessSize       != EFI_ACPI_3_0_DWORD) ||
+      (PldReg->Address.Address          == 0) ||
+      (PldReg->Address.RegisterBitWidth != 1) ||
+      (PldReg->Address.AddressSpaceId   != EFI_ACPI_3_0_SYSTEM_IO) ||
+      (PldReg->Value != 1)) {
+    DEBUG ((DEBUG_INFO, "Unexpected SMM register.\n"));
+    DEBUG ((DEBUG_INFO, "AddressSpaceId= 0x%x\n", PldReg->Address.AddressSpaceId));
+    DEBUG ((DEBUG_INFO, "RegBitWidth   = 0x%x\n", PldReg->Address.RegisterBitWidth));
+    DEBUG ((DEBUG_INFO, "RegBitOffset  = 0x%x\n", PldReg->Address.RegisterBitOffset));
+    DEBUG ((DEBUG_INFO, "AccessSize    = 0x%x\n", PldReg->Address.AccessSize));
+    DEBUG ((DEBUG_INFO, "Address       = 0x%lx\n",PldReg->Address.Address ));
+    return NULL;
+  }
+  return PldReg;
+}
+
+
+/**
+  Fixup data pointers so that the services can be called in virtual mode.
+
+  @param[in] Event                The event registered.
+  @param[in] Context              Event context.
+
+**/
+VOID
+EFIAPI
+SmmControlVirtualAddressChangeEvent (
+  IN EFI_EVENT                  Event,
+  IN VOID                       *Context
+  )
+{
+  EfiConvertPointer (0x0, (VOID **) &(mSmmControl2.Trigger));
+  EfiConvertPointer (0x0, (VOID **) &(mSmmControl2.Clear));
+}
+
+
+/**
+  This function installs EFI_SMM_CONTROL2_PROTOCOL.
+
+  @param  ImageHandle Handle for the image of this driver
+  @param  SystemTable Pointer to the EFI System Table
+
+  @retval EFI_UNSUPPORTED There's no Intel ICH on this platform
+  @return The status returned from InstallProtocolInterface().
+
+**/
+EFI_STATUS
+EFIAPI
+SmmControlEntryPoint (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+  EFI_HOB_GUID_TYPE       *GuidHob;
+  PLD_SMM_REGISTERS       *SmmRegister;
+  PLD_GENERIC_REGISTER    *SmiGblEnReg;
+  PLD_GENERIC_REGISTER    *SmiApmEnReg;
+  EFI_EVENT               Event;
+
+  GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
+  if (GuidHob == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  SmmRegister = (PLD_SMM_REGISTERS *) (GET_GUID_HOB_DATA(GuidHob));
+  SmiGblEnReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_GBL_EN);
+  if (SmiGblEnReg == NULL) {
+    DEBUG ((DEBUG_ERROR, "SMI global enable reg not found.\n"));
+    return EFI_NOT_FOUND;
+  }
+  mSmiCtrlReg.Address      = (UINT32)SmiGblEnReg->Address.Address;
+  mSmiCtrlReg.GblBitOffset = SmiGblEnReg->Address.RegisterBitOffset;
+
+  SmiApmEnReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_EN);
+  if (SmiApmEnReg == NULL) {
+    DEBUG ((DEBUG_ERROR, "SMI APM enable reg not found.\n"));
+    return EFI_NOT_FOUND;
+  }
+
+  if (SmiApmEnReg->Address.Address != mSmiCtrlReg.Address) {
+    DEBUG ((DEBUG_ERROR, "SMI APM EN and SMI GBL EN are expected to have same register base\n"));
+    DEBUG ((DEBUG_ERROR, "APM:0x%x, GBL:0x%x\n", SmiApmEnReg->Address.Address, mSmiCtrlReg.Address));
+    return EFI_UNSUPPORTED;
+  }
+  mSmiCtrlReg.ApmBitOffset = SmiApmEnReg->Address.RegisterBitOffset;
+
+  //
+  // Install our protocol interfaces on the device's handle
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &ImageHandle,
+                  &gEfiSmmControl2ProtocolGuid,
+                  &mSmmControl2,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  SmmControlVirtualAddressChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &Event
+                  );
+  return Status;
+}
diff --git a/UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf b/UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf
new file mode 100644
index 0000000000..f0c2a4586b
--- /dev/null
+++ b/UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf
@@ -0,0 +1,50 @@
+## @file
+# SMM Control runtime DXE Module
+#
+# Provides the ability to generate a software SMI.
+#
+#  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SmmControlRuntimeDxe
+  FILE_GUID                      = C3099578-F815-4a96-84A3-FC593760181D
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = SmmControlEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SmmControlRuntimeDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  DebugLib
+  UefiBootServicesTableLib
+  UefiRuntimeLib
+  PcdLib
+  IoLib
+  HobLib
+
+[Guids]
+  gSmmRegisterInfoGuid
+  gEfiEventVirtualAddressChangeGuid
+
+[Protocols]
+  gEfiSmmControl2ProtocolGuid             ## PRODUCES
+
+[Depex]
+  TRUE
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dec b/UefiPayloadPkg/UefiPayloadPkg.dec
index e5e8db8863..4f93d3e671 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dec
+++ b/UefiPayloadPkg/UefiPayloadPkg.dec
@@ -37,6 +37,8 @@
   gUefiSerialPortInfoGuid  = { 0x6c6872fe, 0x56a9, 0x4403, { 0xbb, 0x98, 0x95, 0x8d, 0x62, 0xde, 0x87, 0xf1 } }
   gLoaderMemoryMapInfoGuid = { 0xa1ff7424, 0x7a1a, 0x478e, { 0xa9, 0xe4, 0x92, 0xf3, 0x57, 0xd1, 0x28, 0x32 } }
 
+  gSmmRegisterInfoGuid     = { 0xaa9bd7a7, 0xcafb, 0x4499, { 0xa4, 0xa9, 0xb, 0x34, 0x6b, 0x40, 0xa6, 0x22 } }
+
 [Ppis]
   gEfiPayLoadHobBasePpiGuid = { 0xdbe23aa1, 0xa342, 0x4b97, {0x85, 0xb6, 0xb2, 0x26, 0xf1, 0x61, 0x73, 0x89} }
 
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 3/8] UefiPayloadPkg: Add bootloader SMM support module
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 1/8] UefiPayloadPkg: Add a common SmmAccessDxe module Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 2/8] UefiPayloadPkg: Add a common SMM control Runtime DXE module Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 4/8] UefiPayloadPkg: Add SpiFlashLib Guo Dong
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

This module is only used for SMM S3 support for the bootloader that
doesn't support SMM.
The payload would save SMM rebase info to SMM communication area in
normal boot and expect the bootloader in S3 path to rebase the SMM
and trigger SMI by writing 0xB2 port with the given value from SMM
communication area. The payload SMM handler would get chance to
restore some registers in S3 path.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c    | 431 ++++++++++++++++++
 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h    |  41 ++
 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf  |  49 ++
 .../Include/Guid/SmmS3CommunicationInfoGuid.h |  54 +++
 UefiPayloadPkg/UefiPayloadPkg.dec             |   1 +
 5 files changed, 576 insertions(+)
 create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
 create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
 create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
 create mode 100644 UefiPayloadPkg/Include/Guid/SmmS3CommunicationInfoGuid.h

diff --git a/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
new file mode 100644
index 0000000000..659a57578f
--- /dev/null
+++ b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
@@ -0,0 +1,431 @@
+/** @file
+  This driver is used for SMM S3 support for the bootloader that
+  doesn't support SMM.
+  The payload would save SMM rebase info to SMM communication area.
+  The bootloader is expected to rebase the SMM and trigger SMI by
+  writting 0xB2 port with given value from SMM communication area.
+  The paylaod SMM handler got chance to restore regs in S3 path.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <BlSupportSmm.h>
+
+PLD_S3_COMMUNICATION              mPldS3Hob;
+EFI_SMRAM_HOB_DESCRIPTOR_BLOCK    *mSmramHob         = NULL;
+PLD_SMM_REGISTERS                 *mSmmRegisterHob   = NULL;;
+UINT64                            mSmmFeatureControl = 0;
+
+/**
+  Save SMM rebase and SMI handler information to SMM communication area
+
+  The function detects SMM communication region for boot loader, if it is detected, it
+  will save SMM rebase information and S3 SMI handler information to SMM communication
+  region. Bootloader should consume these information in S3 path to restore smm base,
+  and write the 0xB2 port to trigger SMI so that payload could resume S3 registers.
+
+  @param[in] BlSwSmiHandlerInput   Value written to 0xB2 to trigger SMI handler.
+
+  @retval    EFI_SUCCESS           Save SMM info success.
+  @retval    Others                Failed to save SMM info.
+**/
+EFI_STATUS
+SaveSmmInfoForS3 (
+  IN UINT8                         BlSwSmiHandlerInput
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_PROCESSOR_INFORMATION        ProcessorInfo;
+  EFI_MP_SERVICES_PROTOCOL         *MpService;
+  CPU_SMMBASE                      *SmmBaseInfo;
+  PLD_TO_BL_SMM_INFO               *PldSmmInfo;
+  UINTN                            Index;
+
+  PldSmmInfo = (PLD_TO_BL_SMM_INFO *)(UINTN)mPldS3Hob.CommBuffer.PhysicalStart;
+  PldSmmInfo->Header.Header.HobLength = (UINT16)(sizeof (PLD_TO_BL_SMM_INFO) + gSmst->NumberOfCpus * sizeof (CPU_SMMBASE));
+  for (Index = 0; Index < mSmramHob->NumberOfSmmReservedRegions; Index++) {
+    if ((mPldS3Hob.CommBuffer.PhysicalStart >= mSmramHob->Descriptor[Index].PhysicalStart) &&
+        (mPldS3Hob.CommBuffer.PhysicalStart <  mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize)) {
+      break;
+    }
+  }
+  if (Index == mSmramHob->NumberOfSmmReservedRegions) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
+  // 
+  if ((mSmramHob->Descriptor[Index].RegionState & EFI_ALLOCATED) == 0) {
+    DEBUG ((DEBUG_ERROR, "SMM communication region not set to EFI_ALLOCATED\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength) > (mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize)) {
+    DEBUG ((DEBUG_ERROR, "SMM communication buffer (0x%x) is too small.\n", (UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength));
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  CopyGuid (&PldSmmInfo->Header.Name, &gS3CommunicationGuid);
+  PldSmmInfo->Header.Header.HobType    = EFI_HOB_TYPE_GUID_EXTENSION;
+  PldSmmInfo->S3Info.SwSmiTriggerValue = BlSwSmiHandlerInput;
+
+  //
+  // Save APIC ID and SMM base
+  //
+  Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+  PldSmmInfo->S3Info.CpuCount         = (UINT32)gSmst->NumberOfCpus;
+  SmmBaseInfo = &PldSmmInfo->S3Info.SmmBase[0];
+  for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
+    Status = MpService->GetProcessorInfo (MpService, Index, &ProcessorInfo);
+    if (EFI_ERROR(Status)) {
+      return Status;
+    }
+
+    SmmBaseInfo->ApicId  = (UINT32)(UINTN)ProcessorInfo.ProcessorId;
+    SmmBaseInfo->SmmBase = (UINT32)(UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET;
+    DEBUG ((DEBUG_INFO, "CPU%d ID:%02X Base: %08X\n", Index, SmmBaseInfo->ApicId, SmmBaseInfo->SmmBase));
+    SmmBaseInfo++;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Get specified SMI register based on given register ID
+
+  @param[in]  Id           The register ID to get.
+
+  @retval NULL             The register is not found
+  @return smi register
+
+**/
+PLD_GENERIC_REGISTER *
+GetRegisterById (
+  UINT64                Id
+  )
+{
+  UINT32                Index;
+
+  for (Index = 0; Index < mSmmRegisterHob->Count; Index++) {
+    if (mSmmRegisterHob->Registers[Index].Id == Id) {
+      return &mSmmRegisterHob->Registers[Index];
+    }
+  }
+  return NULL;
+}
+
+/**
+  Set SMM SMI Global enable lock
+
+**/
+VOID
+LockSmiGlobalEn (
+  VOID
+  )
+{
+  PLD_GENERIC_REGISTER       *SmiLockReg;
+
+    DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn .....\n"));
+
+  SmiLockReg = GetRegisterById (REGISTER_ID_SMI_GBL_EN_LOCK);
+  if (SmiLockReg == NULL) {
+    DEBUG ((DEBUG_ERROR, "SMI global enable lock reg not found.\n"));
+    return;
+  }
+
+  //
+  // Set SMM SMI lock in S3 path
+  //
+  if ((SmiLockReg->Address.AccessSize       == EFI_ACPI_3_0_DWORD) &&
+      (SmiLockReg->Address.Address          != 0) &&
+      (SmiLockReg->Address.RegisterBitWidth == 1) &&
+      (SmiLockReg->Address.AddressSpaceId   == EFI_ACPI_3_0_SYSTEM_MEMORY) &&
+      (SmiLockReg->Value == 1)) {
+    DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn ....is locked\n"));
+
+    MmioOr32 ((UINT32)SmiLockReg->Address.Address, 1 << SmiLockReg->Address.RegisterBitOffset);
+  } else {
+    DEBUG ((DEBUG_ERROR, "Unexpected SMM SMI lock register, need enhancement here.\n"));
+  }
+}
+
+/**
+  Check and set SMM feature lock bit and code check enable bit
+  in S3 path.
+
+**/
+VOID
+SmmFeatureLockOnS3 (
+  VOID
+  )
+{
+
+  if (mSmmFeatureControl != 0) {
+    return;
+  }
+
+  mSmmFeatureControl = AsmReadMsr64(MSR_SMM_FEATURE_CONTROL);
+  if ((mSmmFeatureControl & 0x5) != 0x5) {
+    //
+    // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
+    //
+    AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, mSmmFeatureControl | 0x5);
+  }
+  mSmmFeatureControl = AsmReadMsr64(MSR_SMM_FEATURE_CONTROL);
+}
+
+
+
+/**
+  Function to program SMRR base and mask.
+
+  @param[in] ProcedureArgument  Pointer to SMRR_BASE_MASK structure.
+**/
+VOID
+SetSmrr (
+  IN VOID                   *ProcedureArgument
+  )
+{
+  if (ProcedureArgument != NULL) {
+    AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE, ((SMRR_BASE_MASK *)ProcedureArgument)->Base);
+    AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK, ((SMRR_BASE_MASK *)ProcedureArgument)->Mask);
+  }
+}
+
+/**
+  Set SMRR in S3 path.
+
+**/
+VOID
+SetSmrrOnS3 (
+  VOID
+  )
+{
+  EFI_STATUS            Status;
+  SMRR_BASE_MASK        Arguments;
+  UINTN                 Index;
+  UINT32                SmmBase;
+  UINT32                SmmSize;
+
+  if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
+    return;
+  }
+
+  SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalStart;
+  SmmSize = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalSize;
+  if ((mSmramHob->NumberOfSmmReservedRegions > 2) || (mSmramHob->NumberOfSmmReservedRegions == 0)) {
+    DEBUG ((DEBUG_ERROR, "%d SMM ranges are not supported.\n", mSmramHob->NumberOfSmmReservedRegions));
+    return;
+  } else if (mSmramHob->NumberOfSmmReservedRegions == 2) {
+    if ((mSmramHob->Descriptor[1].PhysicalStart + mSmramHob->Descriptor[1].PhysicalSize) == SmmBase){
+      SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalStart;
+    } else if (mSmramHob->Descriptor[1].PhysicalStart != (SmmBase + SmmSize)) {
+      DEBUG ((DEBUG_ERROR, "Two SMM regions are not continous.\n"));
+      return;
+    }
+    SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
+  }
+
+  if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
+    DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
+    return ;
+  }
+
+  //
+  // SMRR size must be of length 2^n
+  // SMRR base alignment cannot be less than SMRR length
+  //
+  if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
+    DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
+    return ;
+  }
+
+  //
+  // Calculate smrr base, mask and pass them as arguments.
+  //
+  Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
+  Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
+
+  //
+  // Set SMRR valid bit
+  //
+  Arguments.Mask |= BIT11;
+
+  //
+  // Program smrr base and mask on BSP first and then on APs
+  //
+  SetSmrr(&Arguments);
+  for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
+    if (Index != gSmst->CurrentlyExecutingCpu) {
+      Status = gSmst->SmmStartupThisAp (SetSmrr, Index, (VOID *)&Arguments);
+      if (EFI_ERROR(Status)) {
+        DEBUG ((DEBUG_ERROR, "Programming SMRR on AP# %d status: %r\n", Index, Status));
+      }
+    }
+  }
+}
+
+
+/**
+  Software SMI callback for restoring SMRR base and mask in S3 path.
+
+  @param[in]      DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
+  @param[in]      Context         Points to an optional handler context which was specified when the
+                                  handler was registered.
+  @param[in, out] CommBuffer      A pointer to a collection of data in memory that will
+                                  be conveyed from a non-SMM environment into an SMM environment.
+  @param[in, out] CommBufferSize  The size of the CommBuffer.
+
+  @retval EFI_SUCCESS             The interrupt was handled successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+BlSwSmiHandler (
+  IN EFI_HANDLE                  DispatchHandle,
+  IN CONST VOID                  *Context,
+  IN OUT VOID                    *CommBuffer,
+  IN OUT UINTN                   *CommBufferSize
+  )
+{
+  SetSmrrOnS3 ();
+  SmmFeatureLockOnS3 ();
+  LockSmiGlobalEn ();
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Lock SMI in this SMM ready to lock event.
+
+  @param  Protocol   Points to the protocol's unique identifier
+  @param  Interface  Points to the interface instance
+  @param  Handle     The handle on which the interface was installed
+
+  @retval EFI_SUCCESS   SmmEventCallback runs successfully
+  @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
+ **/
+EFI_STATUS
+EFIAPI
+BlSupportSmmReadyToLockCallback (
+  IN CONST EFI_GUID                       *Protocol,
+  IN VOID                                 *Interface,
+  IN EFI_HANDLE                           Handle
+  )
+{
+  //
+  // Set SMM SMI lock
+  //
+  LockSmiGlobalEn ();
+  return EFI_SUCCESS;
+}
+
+
+/**
+  The driver's entry point.
+
+  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
+  @param[in] SystemTable  A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS     The entry point is executed successfully.
+  @retval Others          Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+BlSupportSmm (
+  IN EFI_HANDLE                  ImageHandle,
+  IN EFI_SYSTEM_TABLE            *SystemTable
+  )
+{
+  EFI_STATUS                     Status;
+  EFI_SMM_SW_DISPATCH2_PROTOCOL  *SwDispatch;
+  EFI_SMM_SW_REGISTER_CONTEXT    SwContext;
+  EFI_HANDLE                     SwHandle;
+  EFI_HOB_GUID_TYPE              *GuidHob;
+  VOID                           *SmmHob;
+  VOID                           *Registration;
+
+  //
+  // Get SMM S3 communication hob and save it
+  //
+  GuidHob = GetFirstGuidHob (&gS3CommunicationGuid);
+  if (GuidHob != NULL) {
+    SmmHob = (VOID *) (GET_GUID_HOB_DATA(GuidHob));
+    CopyMem (&mPldS3Hob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+  if (mPldS3Hob.PldAcpiS3Enable) {
+    // Other drivers will take care of S3.
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Get smram hob and save it
+  //
+  GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
+  if (GuidHob != NULL) {
+    SmmHob = (VOID *) (GET_GUID_HOB_DATA(GuidHob));
+    mSmramHob = AllocatePool (GET_GUID_HOB_DATA_SIZE(GuidHob));
+    if (mSmramHob == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Get SMM register hob and save it
+  //
+  GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
+  if (GuidHob != NULL) {
+    SmmHob = (VOID *) (GET_GUID_HOB_DATA(GuidHob));
+    mSmmRegisterHob = AllocatePool (GET_GUID_HOB_DATA_SIZE(GuidHob));
+    if (mSmmRegisterHob == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Get the Sw dispatch protocol and register SMI handler.
+  //
+  Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID**)&SwDispatch);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  SwContext.SwSmiInputValue = (UINTN) -1;
+  Status = SwDispatch->Register (SwDispatch, BlSwSmiHandler, &SwContext, &SwHandle);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Registering S3 smi handler failed: %r\n", Status));
+    return Status;
+  }
+
+  //
+  // Register SMM ready to lock callback
+  //
+  Status = gSmst->SmmRegisterProtocolNotify (
+                    &gEfiSmmReadyToLockProtocolGuid,
+                    BlSupportSmmReadyToLockCallback,
+                    &Registration
+                    );
+  ASSERT_EFI_ERROR (Status);
+
+  SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
+
+  return EFI_SUCCESS;
+}
+
diff --git a/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
new file mode 100644
index 0000000000..f4386cb842
--- /dev/null
+++ b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
@@ -0,0 +1,41 @@
+/** @file
+  The header file of bootloader support SMM.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef BL_SUPPORT_SMM_H_
+#define BL_SUPPORT_SMM_H_
+
+#include <PiDxe.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/HobLib.h>
+#include <Library/MtrrLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/PciLib.h>
+#include <Protocol/SmmSwDispatch2.h>
+#include <Protocol/SmmAccess2.h>
+#include <protocol/MpService.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Register/Intel/ArchitecturalMsr.h>
+#include <Guid/SmmRegisterInfoGuid.h>
+#include <Guid/SmmS3CommunicationInfoGuid.h>
+#include <Guid/SmramMemoryReserve.h>
+
+#define  EFI_MSR_SMRR_MASK              0xFFFFF000
+#define  MSR_SMM_FEATURE_CONTROL        0x4E0
+#define  SMRAM_SAVE_STATE_MAP_OFFSET    0xFC00  /// Save state offset from SMBASE
+
+typedef struct {
+  UINT32  Base;
+  UINT32  Mask;
+} SMRR_BASE_MASK;
+
+#endif
+
diff --git a/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
new file mode 100644
index 0000000000..75d4777971
--- /dev/null
+++ b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
@@ -0,0 +1,49 @@
+## @file
+#  Bootloader Support SMM module
+#
+#  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = BlSupportSmm
+  FILE_GUID                      = AA292DE7-E11E-42E6-846B-5813A5A8D982
+  MODULE_TYPE                    = DXE_SMM_DRIVER
+  PI_SPECIFICATION_VERSION       = 0x0001000A
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = BlSupportSmm
+
+[Sources]
+  BlSupportSmm.c
+  BlSupportSmm.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  SmmServicesTableLib
+  MemoryAllocationLib
+  BaseLib
+  IoLib
+  HobLib
+
+[Guids]
+  gS3CommunicationGuid
+  gEfiSmmSmramMemoryGuid
+  gSmmRegisterInfoGuid
+
+[Protocols]
+  gEfiSmmSwDispatch2ProtocolGuid
+  gEfiMpServiceProtocolGuid
+  gEfiSmmReadyToLockProtocolGuid
+
+[Depex]
+  gEfiSmmSwDispatch2ProtocolGuid
diff --git a/UefiPayloadPkg/Include/Guid/SmmS3CommunicationInfoGuid.h b/UefiPayloadPkg/Include/Guid/SmmS3CommunicationInfoGuid.h
new file mode 100644
index 0000000000..ee621b25f4
--- /dev/null
+++ b/UefiPayloadPkg/Include/Guid/SmmS3CommunicationInfoGuid.h
@@ -0,0 +1,54 @@
+/** @file
+  This file defines the SMM S3 communication hob structure.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PAYLOAD_S3_COMMUNICATION_GUID_H_
+#define PAYLOAD_S3_COMMUNICATION_GUID_H_
+
+extern EFI_GUID gS3CommunicationGuid;
+
+#pragma pack(1)
+
+typedef struct {
+  EFI_SMRAM_DESCRIPTOR  CommBuffer;
+  BOOLEAN               PldAcpiS3Enable;
+} PLD_S3_COMMUNICATION;
+
+///
+/// The information below is used for communication between bootloader and payload.
+/// It is used to save/store some registers in S3 path
+///
+/// This region exists only when gEfiAcpiVariableGuid HOB exist.
+/// when PLD_S3_INFO.PldAcpiS3Enable is false, the communication buffer is defined as below.
+///
+
+typedef struct {
+  UINT32             ApicId;
+  UINT32             SmmBase;
+} CPU_SMMBASE;
+
+typedef struct {
+  UINT8              SwSmiData;
+  UINT8              SwSmiTriggerValue;
+  UINT16             Reserved;
+  UINT32             CpuCount;
+  CPU_SMMBASE        SmmBase[0];
+} SMM_S3_INFO;
+
+//
+// Payload would save this structure to S3 communication area in normal boot.
+// In S3 path, bootloader need restore SMM base and writie IO port 0xB2 with SwSmiTriggerValue
+// to trigger SMI to let payload to restore S3.
+//
+typedef struct {
+  EFI_HOB_GUID_TYPE  Header;
+  SMM_S3_INFO        S3Info;
+} PLD_TO_BL_SMM_INFO;
+
+#pragma pack()
+
+#endif
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dec b/UefiPayloadPkg/UefiPayloadPkg.dec
index 4f93d3e671..4c3bd0c2eb 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dec
+++ b/UefiPayloadPkg/UefiPayloadPkg.dec
@@ -38,6 +38,7 @@
   gLoaderMemoryMapInfoGuid = { 0xa1ff7424, 0x7a1a, 0x478e, { 0xa9, 0xe4, 0x92, 0xf3, 0x57, 0xd1, 0x28, 0x32 } }
 
   gSmmRegisterInfoGuid     = { 0xaa9bd7a7, 0xcafb, 0x4499, { 0xa4, 0xa9, 0xb, 0x34, 0x6b, 0x40, 0xa6, 0x22 } }
+  gS3CommunicationGuid     = { 0x88e31ba1, 0x1856, 0x4b8b, { 0xbb, 0xdf, 0xf8, 0x16, 0xdd, 0x94, 0xa, 0xef } }
 
 [Ppis]
   gEfiPayLoadHobBasePpiGuid = { 0xdbe23aa1, 0xa342, 0x4b97, {0x85, 0xb6, 0xb2, 0x26, 0xf1, 0x61, 0x73, 0x89} }
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 4/8] UefiPayloadPkg: Add SpiFlashLib
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
                   ` (2 preceding siblings ...)
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 3/8] UefiPayloadPkg: Add bootloader SMM support module Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 5/8] UefiPayloadPkg: Add FlashDeviceLib Guo Dong
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

This is a common SPI Flash library used for the Intel platform that
supports SPI hardware sequence. This library provides actual SPI flash
operation via Intel PCH SPI controller.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 .../Include/Guid/SpiFlashInfoGuid.h           |  38 +
 UefiPayloadPkg/Include/Library/SpiFlashLib.h  | 215 +++++
 UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c   | 173 ++++
 UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h  | 129 +++
 .../Library/SpiFlashLib/SpiCommon.h           | 208 +++++
 .../Library/SpiFlashLib/SpiFlashLib.c         | 857 ++++++++++++++++++
 .../Library/SpiFlashLib/SpiFlashLib.inf       |  48 +
 UefiPayloadPkg/UefiPayloadPkg.dec             |   1 +
 8 files changed, 1669 insertions(+)
 create mode 100644 UefiPayloadPkg/Include/Guid/SpiFlashInfoGuid.h
 create mode 100644 UefiPayloadPkg/Include/Library/SpiFlashLib.h
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiCommon.h
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.c
 create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf

diff --git a/UefiPayloadPkg/Include/Guid/SpiFlashInfoGuid.h b/UefiPayloadPkg/Include/Guid/SpiFlashInfoGuid.h
new file mode 100644
index 0000000000..6241463007
--- /dev/null
+++ b/UefiPayloadPkg/Include/Guid/SpiFlashInfoGuid.h
@@ -0,0 +1,38 @@
+/** @file
+  This file defines the hob structure for the SPI flash variable info.
+
+  Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SPI_FLASH_INFO_GUID_H_
+#define SPI_FLASH_INFO_GUID_H_
+
+#include <IndustryStandard/Acpi.h>
+//
+// SPI Flash infor hob GUID
+//
+extern EFI_GUID gSpiFlashInfoGuid;
+
+//
+// Set this bit if platform need disable SMM write protection when writing flash
+// in SMM mode using this method:  -- AsmWriteMsr32 (0x1FE, MmioRead32 (0xFED30880) | BIT0);
+//
+#define FLAGS_SPI_DISABLE_SMM_WRITE_PROTECT     BIT0
+
+//
+// Reuse ACPI definition
+//
+typedef EFI_ACPI_3_0_GENERIC_ADDRESS_STRUCTURE PLD_GENERIC_ADDRESS;
+#define SPACE_ID_PCI_CONFIGURATION              EFI_ACPI_3_0_PCI_CONFIGURATION_SPACE
+#define REGISTER_BIT_WIDTH_DWORD                EFI_ACPI_3_0_DWORD
+
+typedef struct {
+  UINT8                        Revision;
+  UINT8                        Reserved;
+  UINT16                       Flags;
+  PLD_GENERIC_ADDRESS          SpiAddress;
+} SPI_FLASH_INFO;
+
+#endif
diff --git a/UefiPayloadPkg/Include/Library/SpiFlashLib.h b/UefiPayloadPkg/Include/Library/SpiFlashLib.h
new file mode 100644
index 0000000000..59840afb70
--- /dev/null
+++ b/UefiPayloadPkg/Include/Library/SpiFlashLib.h
@@ -0,0 +1,215 @@
+/** @file
+  PCH SPI Common Driver implements the SPI Host Controller Compatibility Interface.
+
+  Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SPI_FLASH_LIB_H_
+#define SPI_FLASH_LIB_H_
+
+/**
+  Flash Region Type
+**/
+typedef enum {
+  FlashRegionDescriptor,
+  FlashRegionBios,
+  FlashRegionMe,
+  FlashRegionGbE,
+  FlashRegionPlatformData,
+  FlashRegionDer,
+  FlashRegionAll,
+  FlashRegionMax
+} FLASH_REGION_TYPE;
+
+/**
+  Read SFDP data from the flash part.
+
+  @param[in] ComponentNumber      The Component Number for chip select
+  @param[in] ByteCount            Number of bytes in SFDP data portion of the SPI cycle, the max number is 64
+  @param[out] SfdpData            The Pointer to caller-allocated buffer containing the SFDP data received
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashReadSfdp (
+  IN     UINT8              ComponentNumber,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *SfdpData
+  );
+
+/**
+  Read Jedec Id from the flash part.
+
+  @param[in] ComponentNumber      The Component Number for chip select
+  @param[in] ByteCount            Number of bytes in JedecId data portion of the SPI cycle, the data size is 3 typically
+  @param[out] JedecId             The Pointer to caller-allocated buffer containing JEDEC ID received
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashReadJedecId (
+  IN     UINT8              ComponentNumber,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *JedecId
+  );
+
+/**
+  Write the status register in the flash part.
+
+  @param[in] ByteCount            Number of bytes in Status data portion of the SPI cycle, the data size is 1 typically
+  @param[in] StatusValue          The Pointer to caller-allocated buffer containing the value of Status register writing
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashWriteStatus (
+  IN     UINT32             ByteCount,
+  IN     UINT8              *StatusValue
+  );
+
+/**
+  Read status register in the flash part.
+
+  @param[in] ByteCount            Number of bytes in Status data portion of the SPI cycle, the data size is 1 typically
+  @param[out] StatusValue         The Pointer to caller-allocated buffer containing the value of Status register received.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashReadStatus (
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *StatusValue
+  );
+
+/**
+  Read SC Soft Strap Values
+
+  @param[in] SoftStrapAddr        SC Soft Strap address offset from FPSBA.
+  @param[in] ByteCount            Number of bytes in SoftStrap data portion of the SPI cycle
+  @param[out] SoftStrapValue      The Pointer to caller-allocated buffer containing SC Soft Strap Value.
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiReadPchSoftStrap (
+  IN     UINT32             SoftStrapAddr,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *SoftStrapValue
+  );
+
+
+/**
+  Read data from the flash part.
+
+  @param[in] FlashRegionType      The Flash Region type for flash cycle which is listed in the Descriptor.
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+  @param[out] Buffer              The Pointer to caller-allocated buffer containing the dada received.
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashRead (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *Buffer
+  );
+
+/**
+  Erase some area on the flash part.
+
+  @param[in] FlashRegionType      The Flash Region type for flash cycle which is listed in the Descriptor.
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashErase (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount
+  );
+
+/**
+  Write data to the flash part.
+
+  @param[in] FlashRegionType      The Flash Region type for flash cycle which is listed in the Descriptor.
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+  @param[in] Buffer               Pointer to caller-allocated buffer containing the data sent during the SPI cycle.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashWrite (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount,
+  IN     UINT8              *Buffer
+  );
+
+/**
+  Initialize an SPI library.
+
+  @retval EFI_SUCCESS             The protocol instance was properly initialized
+  @retval EFI_NOT_FOUND           The expected SPI info could not be found
+**/
+EFI_STATUS
+EFIAPI
+SpiConstructor (
+  VOID
+  );
+
+/**
+  Get the SPI region base and size, based on the enum type
+
+  @param[in] FlashRegionType      The Flash Region type for for the base address which is listed in the Descriptor.
+  @param[out] BaseAddress         The Flash Linear Address for the Region 'n' Base
+  @param[out] RegionSize          The size for the Region 'n'
+
+  @retval EFI_SUCCESS             Read success
+  @retval EFI_INVALID_PARAMETER   Invalid region type given
+  @retval EFI_DEVICE_ERROR        The region is not used
+**/
+EFI_STATUS
+EFIAPI
+SpiGetRegionAddress (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  OUT    UINT32             *BaseAddress, OPTIONAL
+  OUT    UINT32             *RegionSize OPTIONAL
+  );
+
+#endif
+
diff --git a/UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c b/UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c
new file mode 100644
index 0000000000..1dafce19cb
--- /dev/null
+++ b/UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c
@@ -0,0 +1,173 @@
+/** @file
+
+  Copyright (c) 2017-2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include "SpiCommon.h"
+
+/**
+  Acquire SPI MMIO BAR.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+
+  @retval                         Return SPI BAR Address
+
+**/
+UINT32
+AcquireSpiBar0 (
+  IN  UINTN         PchSpiBase
+  )
+{
+  return MmioRead32 (PchSpiBase + R_SPI_BASE) & ~(B_SPI_BAR0_MASK);
+}
+
+/**
+  Release SPI MMIO BAR. Do nothing.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+
+**/
+VOID
+ReleaseSpiBar0 (
+  IN  UINTN         PchSpiBase
+  )
+{
+}
+
+
+
+/**
+  This function is to enable/disable BIOS Write Protect in SMM phase.
+
+  @param[in] EnableSmmSts        Flag to Enable/disable Bios write protect
+
+**/
+VOID
+CpuSmmDisableBiosWriteProtect (
+   IN  BOOLEAN   EnableSmmSts
+  )
+{
+  UINT32      Data32;
+
+  if(EnableSmmSts){
+    //
+    // Disable BIOS Write Protect in SMM phase.
+    //
+    Data32 = MmioRead32 ((UINTN) (0xFED30880)) | (UINT32) (BIT0);
+    AsmWriteMsr32 (0x000001FE, Data32);
+  } else {
+    //
+    // Enable BIOS Write Protect in SMM phase
+    //
+    Data32 = MmioRead32 ((UINTN) (0xFED30880)) & (UINT32) (~BIT0);
+    AsmWriteMsr32 (0x000001FE, Data32);
+  }
+
+  //
+  // Read FED30880h back to ensure the setting went through.
+  //
+  Data32 = MmioRead32 (0xFED30880);
+}
+
+
+/**
+  This function is a hook for Spi to disable BIOS Write Protect.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+  @param[in] CpuSmmBwp            Need to disable CPU SMM Bios write protection or not
+
+  @retval EFI_SUCCESS             The protocol instance was properly initialized
+  @retval EFI_ACCESS_DENIED       The BIOS Region can only be updated in SMM phase
+
+**/
+EFI_STATUS
+EFIAPI
+DisableBiosWriteProtect (
+  IN  UINTN         PchSpiBase,
+  IN  UINT8         CpuSmmBwp
+  )
+{
+
+  //
+  // Write clear BC_SYNC_SS prior to change WPD from 0 to 1.
+  //
+  MmioOr8 (PchSpiBase + R_SPI_BCR + 1, (B_SPI_BCR_SYNC_SS >> 8));
+
+  //
+  // Enable the access to the BIOS space for both read and write cycles
+  //
+  MmioOr8 (PchSpiBase + R_SPI_BCR, B_SPI_BCR_BIOSWE);
+
+  if (CpuSmmBwp != 0) {
+    CpuSmmDisableBiosWriteProtect (TRUE);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is a hook for Spi to enable BIOS Write Protect.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+  @param[in] CpuSmmBwp            Need to disable CPU SMM Bios write protection or not
+
+**/
+VOID
+EFIAPI
+EnableBiosWriteProtect (
+  IN  UINTN         PchSpiBase,
+  IN  UINT8         CpuSmmBwp
+  )
+{
+
+  //
+  // Disable the access to the BIOS space for write cycles
+  //
+  MmioAnd8 (PchSpiBase + R_SPI_BCR, (UINT8) (~B_SPI_BCR_BIOSWE));
+
+  if (CpuSmmBwp != 0) {
+    CpuSmmDisableBiosWriteProtect (FALSE);
+  }
+}
+
+/**
+  This function disables SPI Prefetching and caching,
+  and returns previous BIOS Control Register value before disabling.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+
+  @retval                         Previous BIOS Control Register value
+
+**/
+UINT8
+SaveAndDisableSpiPrefetchCache (
+  IN  UINTN         PchSpiBase
+  )
+{
+  UINT8           BiosCtlSave;
+
+  BiosCtlSave = MmioRead8 (PchSpiBase + R_SPI_BCR) & B_SPI_BCR_SRC;
+
+  MmioAndThenOr32 (PchSpiBase + R_SPI_BCR, \
+    (UINT32) (~B_SPI_BCR_SRC), \
+    (UINT32) (V_SPI_BCR_SRC_PREF_DIS_CACHE_DIS <<  B_SPI_BCR_SRC));
+
+  return BiosCtlSave;
+}
+
+/**
+  This function updates BIOS Control Register with the given value.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+  @param[in] BiosCtlValue         BIOS Control Register Value to be updated
+
+**/
+VOID
+SetSpiBiosControlRegister (
+  IN  UINTN         PchSpiBase,
+  IN  UINT8         BiosCtlValue
+  )
+{
+  MmioAndThenOr8 (PchSpiBase + R_SPI_BCR, (UINT8) ~B_SPI_BCR_SRC, BiosCtlValue);
+}
diff --git a/UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h b/UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h
new file mode 100644
index 0000000000..5f22623675
--- /dev/null
+++ b/UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h
@@ -0,0 +1,129 @@
+/** @file
+  Register names for SPI device.
+
+  Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef REGS_SPI_H_
+#define REGS_SPI_H_
+
+#define R_SPI_BASE                      0x10           ///< 32-bit Memory Base Address Register
+#define B_SPI_BAR0_MASK                 0x0FFF
+#define R_SPI_BCR                       0xDC           ///< BIOS Control  Register
+#define B_SPI_BCR_SRC                    (BIT3 | BIT2) ///< SPI Read Configuration (SRC)
+#define V_SPI_BCR_SRC_PREF_DIS_CACHE_DIS 0x04          ///< Prefetch Disable, Cache Disable
+#define B_SPI_BCR_SYNC_SS                BIT8
+#define B_SPI_BCR_BIOSWE                 BIT0          ///< Write Protect Disable (WPD)
+
+///
+/// SPI Host Interface Registers
+#define R_SPI_HSFS                       0x04          ///< Hardware Sequencing Flash Status and Control Register(32bits)
+#define B_SPI_HSFS_FDBC_MASK             0x3F000000    ///< Flash Data Byte Count ( <= 64), Count = (Value in this field) + 1.
+#define N_SPI_HSFS_FDBC                  24
+#define B_SPI_HSFS_CYCLE_MASK            0x001E0000    ///< Flash Cycle.
+#define N_SPI_HSFS_CYCLE                 17
+#define V_SPI_HSFS_CYCLE_READ            0             ///< Flash Cycle Read
+#define V_SPI_HSFS_CYCLE_WRITE           2             ///< Flash Cycle Write
+#define V_SPI_HSFS_CYCLE_4K_ERASE        3             ///< Flash Cycle 4K Block Erase
+#define V_SPI_HSFS_CYCLE_64K_ERASE       4             ///< Flash Cycle 64K Sector Erase
+#define V_SPI_HSFS_CYCLE_READ_SFDP       5             ///< Flash Cycle Read SFDP
+#define V_SPI_HSFS_CYCLE_READ_JEDEC_ID   6             ///< Flash Cycle Read JEDEC ID
+#define V_SPI_HSFS_CYCLE_WRITE_STATUS    7             ///< Flash Cycle Write Status
+#define V_SPI_HSFS_CYCLE_READ_STATUS     8             ///< Flash Cycle Read Status
+#define B_SPI_HSFS_CYCLE_FGO             BIT16         ///< Flash Cycle Go.
+#define B_SPI_HSFS_FDV                   BIT14         ///< Flash Descriptor Valid
+#define B_SPI_HSFS_SCIP                  BIT5          ///< SPI Cycle in Progress
+#define B_SPI_HSFS_FCERR                 BIT1          ///< Flash Cycle Error
+#define B_SPI_HSFS_FDONE                 BIT0          ///< Flash Cycle Done
+
+
+#define R_SPI_FADDR                      0x08   ///< SPI Flash Address
+#define B_SPI_FADDR_MASK                 0x07FFFFFF ///< SPI Flash Address Mask (0~26bit)
+
+
+#define R_SPI_FDATA00                    0x10  ///< SPI Data 00 (32 bits)
+
+#define R_SPI_FRAP                       0x50  ///< SPI Flash Regions Access Permissions Register
+#define B_SPI_FRAP_BRWA_PLATFORM         BIT12 //< Region write access for Region4 PlatformData
+#define B_SPI_FRAP_BRWA_GBE              BIT11 //< Region write access for Region3 GbE
+#define B_SPI_FRAP_BRWA_SEC              BIT10 ///< Region Write Access for Region2 SEC
+#define B_SPI_FRAP_BRWA_BIOS             BIT9  ///< Region Write Access for Region1 BIOS
+#define B_SPI_FRAP_BRWA_FLASHD           BIT8  ///< Region Write Access for Region0 Flash Descriptor
+#define B_SPI_FRAP_BRRA_PLATFORM         BIT4       ///< Region read access for Region4 PlatformData
+#define B_SPI_FRAP_BRRA_GBE              BIT3       ///< Region read access for Region3 GbE
+#define B_SPI_FRAP_BRRA_SEC              BIT2       ///< Region Read Access for Region2 SEC
+#define B_SPI_FRAP_BRRA_BIOS             BIT1       ///< Region Read Access for Region1 BIOS
+#define B_SPI_FRAP_BRRA_FLASHD           BIT0       ///< Region Read Access for Region0 Flash Descriptor
+
+
+#define R_SPI_FREG0_FLASHD               0x54       ///< Flash Region 0 (Flash Descriptor) (32bits)
+#define B_SPI_FREG0_LIMIT_MASK           0x7FFF0000 ///< Size, [30:16] here represents limit[26:12]
+#define N_SPI_FREG0_LIMIT                4          ///< Bit 30:16 identifies address bits [26:12]
+#define B_SPI_FREG0_BASE_MASK            0x00007FFF ///< Base, [14:0]  here represents base [26:12]
+#define N_SPI_FREG0_BASE                 12         ///< Bit 14:0 identifies address bits [26:2]
+
+#define R_SPI_FREG1_BIOS                 0x58       ///< Flash Region 1 (BIOS) (32bits)
+#define B_SPI_FREG1_LIMIT_MASK           0x7FFF0000 ///< Size, [30:16] here represents limit[26:12]
+#define N_SPI_FREG1_LIMIT                4          ///< Bit 30:16 identifies address bits [26:12]
+#define B_SPI_FREG1_BASE_MASK            0x00007FFF ///< Base, [14:0]  here represents base [26:12]
+#define N_SPI_FREG1_BASE                 12         ///< Bit 14:0 identifies address bits [26:2]
+
+#define R_SPI_FREG2_SEC                  0x5C       ///< Flash Region 2 (SEC) (32bits)
+#define B_SPI_FREG2_LIMIT_MASK           0x7FFF0000 ///< Size, [30:16] here represents limit[26:12]
+#define N_SPI_FREG2_LIMIT                4          //< Bit 30:16 identifies address bits [26:12]
+#define B_SPI_FREG2_BASE_MASK            0x00007FFF ///< Base, [14:0]  here represents base [26:12]
+#define N_SPI_FREG2_BASE                 12         //< Bit 14:0 identifies address bits [26:2]
+
+#define R_SPI_FREG3_GBE                  0x60       //< Flash Region 3(GbE)(32bits)
+#define B_SPI_FREG3_LIMIT_MASK           0x7FFF0000 ///< Size, [30:16] here represents limit[26:12]
+#define N_SPI_FREG3_LIMIT                4          //< Bit 30:16 identifies address bits [26:12]
+#define B_SPI_FREG3_BASE_MASK            0x00007FFF ///< Base, [14:0]  here represents base [26:12]
+#define N_SPI_FREG3_BASE                 12         //< Bit 14:0 identifies address bits [26:2]
+
+#define R_SPI_FREG4_PLATFORM_DATA        0x64       ///< Flash Region 4 (Platform Data) (32bits)
+#define B_SPI_FREG4_LIMIT_MASK           0x7FFF0000 ///< Size, [30:16] here represents limit[26:12]
+#define N_SPI_FREG4_LIMIT                4          ///< Bit 30:16 identifies address bits [26:12]
+#define B_SPI_FREG4_BASE_MASK            0x00007FFF ///< Base, [14:0]  here represents base [26:12]
+#define N_SPI_FREG4_BASE                 12         ///< Bit 14:0 identifies address bits [26:2]
+
+
+#define S_SPI_FREGX                      4          ///< Size of Flash Region register
+#define B_SPI_FREGX_LIMIT_MASK           0x7FFF0000 ///< Flash Region Limit [30:16] represents [26:12], [11:0] are assumed to be FFFh
+#define N_SPI_FREGX_LIMIT                16         ///< Region limit bit position
+#define N_SPI_FREGX_LIMIT_REPR           12         ///< Region limit bit represents position
+#define B_SPI_FREGX_BASE_MASK            0x00007FFF ///< Flash Region Base, [14:0] represents [26:12]
+
+
+#define R_SPI_FDOC                       0xB4  ///< Flash Descriptor Observability Control Register (32 bits)
+#define B_SPI_FDOC_FDSS_MASK             (BIT14 | BIT13 | BIT12) ///< Flash Descriptor Section Select
+#define V_SPI_FDOC_FDSS_FSDM             0x0000 ///< Flash Signature and Descriptor Map
+#define V_SPI_FDOC_FDSS_COMP             0x1000 ///< Component
+#define B_SPI_FDOC_FDSI_MASK             0x0FFC ///< Flash Descriptor Section Index
+
+#define R_SPI_FDOD                       0xB8  ///< Flash Descriptor Observability Data Register (32 bits)
+
+
+#define R_SPI_LVSCC                      0xC4  ///<Vendor Specific Component Capabilities for Component 0 (32 bits)
+#define B_SPI_LVSCC_EO_64K               BIT29 ///<< 64k Erase valid (EO_64k_valid)
+
+#define R_SPI_UVSCC                      0xC8  ///< Vendor Specific Component Capabilities for Component 1 (32 bits)
+
+
+#define R_SPI_FDBAR_FLASH_MAP0           0x14  ///< Flash MAP 0
+#define N_SPI_FDBAR_NC                   8       ///<< Number Of Components
+#define B_SPI_FDBAR_NC                   0x00000300 ///< Number Of Components
+
+#define R_SPI_FDBAR_FLASH_MAP1           0x18  ///< Flash MAP 1
+#define B_SPI_FDBAR_FPSBA                0x00FF0000 ///< Flash Strap Base Address
+
+
+//
+// Flash Component Base Address (FCBA) from Flash Region 0
+//
+#define R_SPI_FCBA_FLCOMP                0x00  ///< Flash Components Register
+#define B_SPI_FLCOMP_COMP1_MASK          0x0F  ///< Flash Component 1 Density
+
+
+#endif
diff --git a/UefiPayloadPkg/Library/SpiFlashLib/SpiCommon.h b/UefiPayloadPkg/Library/SpiFlashLib/SpiCommon.h
new file mode 100644
index 0000000000..e4f4ad92e1
--- /dev/null
+++ b/UefiPayloadPkg/Library/SpiFlashLib/SpiCommon.h
@@ -0,0 +1,208 @@
+/** @file
+  Header file for the SPI flash module.
+
+  Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SPI_COMMON_LIB_H_
+#define SPI_COMMON_LIB_H_
+
+#include <PiDxe.h>
+#include <Uefi/UefiBaseType.h>
+#include <IndustryStandard/Pci30.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SpiFlashLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/HobLib.h>
+#include <Library/TimerLib.h>
+#include <Guid/SpiFlashInfoGuid.h>
+#include "RegsSpi.h"
+
+///
+/// Maximum time allowed while waiting the SPI cycle to complete
+///  Wait Time = 6 seconds = 6000000 microseconds
+///  Wait Period = 10 microseconds
+///
+#define WAIT_TIME   6000000     ///< Wait Time = 6 seconds = 6000000 microseconds
+#define WAIT_PERIOD 10          ///< Wait Period = 10 microseconds
+
+///
+/// Flash cycle Type
+///
+typedef enum {
+  FlashCycleRead,
+  FlashCycleWrite,
+  FlashCycleErase,
+  FlashCycleReadSfdp,
+  FlashCycleReadJedecId,
+  FlashCycleWriteStatus,
+  FlashCycleReadStatus,
+  FlashCycleMax
+} FLASH_CYCLE_TYPE;
+
+///
+/// Flash Component Number
+///
+typedef enum {
+  FlashComponent0,
+  FlashComponent1,
+  FlashComponentMax
+} FLASH_COMPONENT_NUM;
+
+///
+/// Private data structure definitions for the driver
+///
+#define SC_SPI_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('P', 'S', 'P', 'I')
+
+typedef struct {
+  UINTN                 Signature;
+  EFI_HANDLE            Handle;
+  UINT32                AcpiTmrReg;
+  UINTN                 PchSpiBase;
+  UINT16                RegionPermission;
+  UINT32                SfdpVscc0Value;
+  UINT32                SfdpVscc1Value;
+  UINT32                StrapBaseAddress;
+  UINT8                 NumberOfComponents;
+  UINT16                Flags;
+  UINT32                Component1StartAddr;
+} SPI_INSTANCE;
+
+
+/**
+  Acquire SPI MMIO BAR
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+
+  @retval                         Return SPI BAR Address
+
+**/
+UINT32
+AcquireSpiBar0 (
+  IN  UINTN         PchSpiBase
+  );
+
+
+/**
+  Release SPI MMIO BAR. Do nothing.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+
+  @retval None
+
+**/
+VOID
+ReleaseSpiBar0 (
+  IN  UINTN         PchSpiBase
+  );
+
+
+/**
+  This function is a hook for Spi to disable BIOS Write Protect
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+  @param[in] CpuSmmBwp            Need to disable CPU SMM Bios write protection or not
+
+  @retval EFI_SUCCESS             The protocol instance was properly initialized
+  @retval EFI_ACCESS_DENIED       The BIOS Region can only be updated in SMM phase
+
+**/
+EFI_STATUS
+EFIAPI
+DisableBiosWriteProtect (
+  IN  UINTN         PchSpiBase,
+  IN  UINT8         CpuSmmBwp
+  );
+
+/**
+  This function is a hook for Spi to enable BIOS Write Protect
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+  @param[in] CpuSmmBwp            Need to disable CPU SMM Bios write protection or not
+
+  @retval None
+
+**/
+VOID
+EFIAPI
+EnableBiosWriteProtect (
+  IN  UINTN         PchSpiBase,
+  IN  UINT8         CpuSmmBwp
+  );
+
+
+/**
+  This function disables SPI Prefetching and caching,
+  and returns previous BIOS Control Register value before disabling.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+
+  @retval                         Previous BIOS Control Register value
+
+**/
+UINT8
+SaveAndDisableSpiPrefetchCache (
+  IN  UINTN         PchSpiBase
+  );
+
+/**
+  This function updates BIOS Control Register with the given value.
+
+  @param[in] PchSpiBase           PCH SPI PCI Base Address
+  @param[in] BiosCtlValue         BIOS Control Register Value to be updated
+
+  @retval None
+
+**/
+VOID
+SetSpiBiosControlRegister (
+  IN  UINTN         PchSpiBase,
+  IN  UINT8         BiosCtlValue
+  );
+
+
+/**
+  This function sends the programmed SPI command to the slave device.
+
+  @param[in] SpiRegionType        The SPI Region type for flash cycle which is listed in the Descriptor
+  @param[in] FlashCycleType       The Flash SPI cycle type list in HSFC (Hardware Sequencing Flash Control Register) register
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+  @param[in,out] Buffer           Pointer to caller-allocated buffer containing the data received or sent during the SPI cycle.
+
+  @retval EFI_SUCCESS             SPI command completes successfully.
+  @retval EFI_DEVICE_ERROR        Device error, the command aborts abnormally.
+  @retval EFI_ACCESS_DENIED       Some unrecognized command encountered in hardware sequencing mode
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+**/
+EFI_STATUS
+SendSpiCmd (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     FLASH_CYCLE_TYPE   FlashCycleType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount,
+  IN OUT UINT8              *Buffer
+  );
+
+/**
+  Wait execution cycle to complete on the SPI interface.
+
+  @param[in] PchSpiBar0           Spi MMIO base address
+  @param[in] ErrorCheck           TRUE if the SpiCycle needs to do the error check
+
+  @retval TRUE                    SPI cycle completed on the interface.
+  @retval FALSE                   Time out while waiting the SPI cycle to complete.
+                                  It's not safe to program the next command on the SPI interface.
+**/
+BOOLEAN
+WaitForSpiCycleComplete (
+  IN     UINT32             PchSpiBar0,
+  IN     BOOLEAN            ErrorCheck
+  );
+
+#endif
diff --git a/UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.c b/UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.c
new file mode 100644
index 0000000000..71dfcef740
--- /dev/null
+++ b/UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.c
@@ -0,0 +1,857 @@
+/** @file
+  Generic driver using Hardware Sequencing registers.
+
+  Copyright (c) 2017-2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include "SpiCommon.h"
+
+SPI_INSTANCE      *mSpiInstance = NULL;
+
+/**
+  Get SPI Instance from library global data..
+
+  @retval SpiInstance       Return SPI instance
+**/
+SPI_INSTANCE *
+GetSpiInstance (
+  VOID
+)
+{
+  if (mSpiInstance == NULL) {
+    mSpiInstance  = AllocatePool (sizeof(SPI_INSTANCE));
+    if (mSpiInstance == NULL) {
+      return NULL;
+    }
+    ZeroMem (mSpiInstance, sizeof(SPI_INSTANCE));
+  }
+
+  return mSpiInstance;
+}
+
+
+/**
+  Initialize an SPI library.
+
+  @retval EFI_SUCCESS             The protocol instance was properly initialized
+  @retval EFI_NOT_FOUND           The expected SPI info could not be found
+**/
+EFI_STATUS
+EFIAPI
+SpiConstructor (
+  VOID
+  )
+{
+  UINT32              ScSpiBar0;
+  UINT8               Comp0Density;
+  SPI_INSTANCE        *SpiInstance;
+  EFI_HOB_GUID_TYPE   *GuidHob;
+  SPI_FLASH_INFO      *SpiFlashInfo;
+
+  //
+  // Find SPI flash hob
+  //
+  GuidHob = GetFirstGuidHob (&gSpiFlashInfoGuid);
+  if (GuidHob == NULL) {
+    ASSERT (FALSE);
+    return EFI_NOT_FOUND;
+  }
+  SpiFlashInfo = (SPI_FLASH_INFO *) GET_GUID_HOB_DATA (GuidHob);
+
+  //
+  // Initialize the SPI instance
+  //
+  SpiInstance = GetSpiInstance ();
+  if (SpiInstance == NULL) {
+    return EFI_NOT_FOUND;
+  }
+  DEBUG ((DEBUG_INFO, "SpiInstance = %08X\n", SpiInstance));
+
+  SpiInstance->Signature  = SC_SPI_PRIVATE_DATA_SIGNATURE;
+  SpiInstance->Handle     = NULL;
+
+  //
+  // Check the SPI address
+  //
+  if ((SpiFlashInfo->SpiAddress.AddressSpaceId !=  EFI_ACPI_3_0_PCI_CONFIGURATION_SPACE) ||
+      (SpiFlashInfo->SpiAddress.RegisterBitWidth !=  32) ||
+      (SpiFlashInfo->SpiAddress.RegisterBitOffset !=  0) ||
+      (SpiFlashInfo->SpiAddress.AccessSize !=  EFI_ACPI_3_0_DWORD)){
+    DEBUG ((DEBUG_ERROR, "SPI FLASH HOB is not expected. need check the hob or enhance SPI flash driver.\n"));
+  }
+  SpiInstance->PchSpiBase = (UINT32)(UINTN)SpiFlashInfo->SpiAddress.Address;
+  SpiInstance->Flags      = SpiFlashInfo->Flags;
+  DEBUG ((DEBUG_INFO, "PchSpiBase at 0x%x\n", SpiInstance->PchSpiBase));
+
+  ScSpiBar0 = AcquireSpiBar0 (SpiInstance->PchSpiBase);
+  DEBUG ((DEBUG_INFO, "ScSpiBar0 at 0x%08X\n", ScSpiBar0));
+
+  if (ScSpiBar0 == 0) {
+    ASSERT (FALSE);
+  }
+
+  if ((MmioRead32 (ScSpiBar0 + R_SPI_HSFS) & B_SPI_HSFS_FDV) == 0) {
+    DEBUG ((DEBUG_ERROR, "SPI Flash descriptor invalid, cannot use Hardware Sequencing registers!\n"));
+    ASSERT (FALSE);
+  }
+
+  MmioOr32 (SpiInstance->PchSpiBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
+  SpiInstance->RegionPermission = MmioRead16 (ScSpiBar0 + R_SPI_FRAP);
+  SpiInstance->SfdpVscc0Value   = MmioRead32 (ScSpiBar0 + R_SPI_LVSCC);
+  SpiInstance->SfdpVscc1Value   = MmioRead32 (ScSpiBar0 + R_SPI_UVSCC);
+
+  //
+  // Select to Flash Map 0 Register to get the number of flash Component
+  //
+  MmioAndThenOr32 (
+    ScSpiBar0 + R_SPI_FDOC,
+    (UINT32) (~(B_SPI_FDOC_FDSS_MASK | B_SPI_FDOC_FDSI_MASK)),
+    (UINT32) (V_SPI_FDOC_FDSS_FSDM | R_SPI_FDBAR_FLASH_MAP0)
+    );
+
+  //
+  // Copy Zero based Number Of Components
+  //
+  SpiInstance->NumberOfComponents = (UINT8) ((MmioRead16 (ScSpiBar0 + R_SPI_FDOD) & B_SPI_FDBAR_NC) >> N_SPI_FDBAR_NC);
+
+  MmioAndThenOr32 (
+    ScSpiBar0 + R_SPI_FDOC,
+    (UINT32) (~(B_SPI_FDOC_FDSS_MASK | B_SPI_FDOC_FDSI_MASK)),
+    (UINT32) (V_SPI_FDOC_FDSS_COMP | R_SPI_FCBA_FLCOMP)
+    );
+
+  //
+  // Copy Component 0 Density
+  //
+  Comp0Density = (UINT8) MmioRead32 (ScSpiBar0 + R_SPI_FDOD) & B_SPI_FLCOMP_COMP1_MASK;
+  SpiInstance->Component1StartAddr = (UINT32) (SIZE_512KB << Comp0Density);
+
+  //
+  // Select FLASH_MAP1 to get Flash SC Strap Base Address
+  //
+  MmioAndThenOr32 (
+    (ScSpiBar0 + R_SPI_FDOC),
+    (UINT32) (~(B_SPI_FDOC_FDSS_MASK | B_SPI_FDOC_FDSI_MASK)),
+    (UINT32) (V_SPI_FDOC_FDSS_FSDM | R_SPI_FDBAR_FLASH_MAP1)
+    );
+
+  SpiInstance->StrapBaseAddress = MmioRead32 (ScSpiBar0 + R_SPI_FDOD) & B_SPI_FDBAR_FPSBA;
+
+  //
+  // Align FPSBA with address bits for the SC Strap portion of flash descriptor
+  //
+  SpiInstance->StrapBaseAddress &= B_SPI_FDBAR_FPSBA;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Read data from the flash part.
+
+  @param[in] FlashRegionType      The Flash Region type for flash cycle which is listed in the Descriptor.
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+  @param[out] Buffer              The Pointer to caller-allocated buffer containing the data received.
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashRead (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *Buffer
+  )
+{
+  EFI_STATUS                Status;
+
+  Status = SendSpiCmd (FlashRegionType, FlashCycleRead, Address, ByteCount, Buffer);
+  return Status;
+}
+
+/**
+  Write data to the flash part.
+
+  @param[in] FlashRegionType      The Flash Region type for flash cycle which is listed in the Descriptor.
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+  @param[in] Buffer               Pointer to caller-allocated buffer containing the data sent during the SPI cycle.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashWrite (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount,
+  IN     UINT8              *Buffer
+  )
+{
+  EFI_STATUS                Status;
+
+  Status = SendSpiCmd (FlashRegionType, FlashCycleWrite, Address, ByteCount, Buffer);
+  return Status;
+}
+
+/**
+  Erase some area on the flash part.
+
+  @param[in] FlashRegionType      The Flash Region type for flash cycle which is listed in the Descriptor.
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashErase (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount
+  )
+{
+  EFI_STATUS                Status;
+
+  Status = SendSpiCmd (FlashRegionType, FlashCycleErase, Address, ByteCount, NULL);
+  return Status;
+}
+
+/**
+  Read SFDP data from the flash part.
+
+  @param[in] ComponentNumber      The Component Number for chip select
+  @param[in] ByteCount            Number of bytes in SFDP data portion of the SPI cycle, the max number is 64
+  @param[out] SfdpData            The Pointer to caller-allocated buffer containing the SFDP data received
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashReadSfdp (
+  IN     UINT8              ComponentNumber,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *SfdpData
+  )
+{
+  EFI_STATUS                Status;
+  UINT32                    Address;
+  SPI_INSTANCE              *SpiInstance;
+
+  SpiInstance = GetSpiInstance ();
+  if (SpiInstance == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if ((ByteCount > 64) || (ComponentNumber > SpiInstance->NumberOfComponents)) {
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Address = 0;
+  if (ComponentNumber == FlashComponent1) {
+    Address = SpiInstance->Component1StartAddr;
+  }
+
+  Status = SendSpiCmd (0, FlashCycleReadSfdp, Address, ByteCount, SfdpData);
+  return Status;
+}
+
+/**
+  Read Jedec Id from the flash part.
+
+  @param[in] ComponentNumber      The Component Number for chip select
+  @param[in] ByteCount            Number of bytes in JedecId data portion of the SPI cycle, the data size is 3 typically
+  @param[out] JedecId             The Pointer to caller-allocated buffer containing JEDEC ID received
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashReadJedecId (
+  IN     UINT8              ComponentNumber,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *JedecId
+  )
+{
+  EFI_STATUS                Status;
+  UINT32                    Address;
+  SPI_INSTANCE              *SpiInstance;
+
+  SpiInstance = GetSpiInstance ();
+  if (SpiInstance == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (ComponentNumber > SpiInstance->NumberOfComponents) {
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Address = 0;
+  if (ComponentNumber == FlashComponent1) {
+    Address = SpiInstance->Component1StartAddr;
+  }
+
+  Status = SendSpiCmd (0, FlashCycleReadJedecId, Address, ByteCount, JedecId);
+  return Status;
+}
+
+/**
+  Write the status register in the flash part.
+
+  @param[in] ByteCount            Number of bytes in Status data portion of the SPI cycle, the data size is 1 typically
+  @param[in] StatusValue          The Pointer to caller-allocated buffer containing the value of Status register writing
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashWriteStatus (
+  IN     UINT32             ByteCount,
+  IN     UINT8              *StatusValue
+  )
+{
+  EFI_STATUS                Status;
+
+  Status = SendSpiCmd (0, FlashCycleWriteStatus, 0, ByteCount, StatusValue);
+  return Status;
+}
+
+/**
+  Read status register in the flash part.
+
+  @param[in] ByteCount            Number of bytes in Status data portion of the SPI cycle, the data size is 1 typically
+  @param[out] StatusValue         The Pointer to caller-allocated buffer containing the value of Status register received.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiFlashReadStatus (
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *StatusValue
+  )
+{
+  EFI_STATUS                Status;
+
+  Status = SendSpiCmd (0, FlashCycleReadStatus, 0, ByteCount, StatusValue);
+  return Status;
+}
+
+/**
+  Read SC Soft Strap Values
+
+  @param[in] SoftStrapAddr        SC Soft Strap address offset from FPSBA.
+  @param[in] ByteCount            Number of bytes in SoftStrap data portion of the SPI cycle
+  @param[out] SoftStrapValue      The Pointer to caller-allocated buffer containing SC Soft Strap Value.
+                                  It is the caller's responsibility to make sure Buffer is large enough for the total number of bytes read.
+
+  @retval EFI_SUCCESS             Command succeed.
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+  @retval EFI_DEVICE_ERROR        Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiReadPchSoftStrap (
+  IN     UINT32             SoftStrapAddr,
+  IN     UINT32             ByteCount,
+  OUT    UINT8              *SoftStrapValue
+  )
+{
+  UINT32                    StrapFlashAddr;
+  EFI_STATUS                Status;
+  SPI_INSTANCE              *SpiInstance;
+
+  SpiInstance = GetSpiInstance ();
+  if (SpiInstance == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  ASSERT (SpiInstance->StrapBaseAddress != 0);
+  //
+  // SC Strap Flash Address = FPSBA + RamAddr
+  //
+  StrapFlashAddr = SpiInstance->StrapBaseAddress + SoftStrapAddr;
+
+  Status = SendSpiCmd (FlashRegionDescriptor, FlashCycleRead, StrapFlashAddr, ByteCount, SoftStrapValue);
+  return Status;
+}
+
+/**
+  This function sends the programmed SPI command to the slave device.
+
+  @param[in] FlashRegionType      The SPI Region type for flash cycle which is listed in the Descriptor
+  @param[in] FlashCycleType       The Flash SPI cycle type list in HSFC (Hardware Sequencing Flash Control Register) register
+  @param[in] Address              The Flash Linear Address must fall within a region for which BIOS has access permissions.
+  @param[in] ByteCount            Number of bytes in the data portion of the SPI cycle.
+  @param[in,out] Buffer           Pointer to caller-allocated buffer containing the data received or sent during the SPI cycle.
+
+  @retval EFI_SUCCESS             SPI command completes successfully.
+  @retval EFI_DEVICE_ERROR        Device error, the command aborts abnormally.
+  @retval EFI_ACCESS_DENIED       Some unrecognized command encountered in hardware sequencing mode
+  @retval EFI_INVALID_PARAMETER   The parameters specified are not valid.
+**/
+EFI_STATUS
+SendSpiCmd (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  IN     FLASH_CYCLE_TYPE   FlashCycleType,
+  IN     UINT32             Address,
+  IN     UINT32             ByteCount,
+  IN OUT UINT8              *Buffer
+  )
+{
+  EFI_STATUS                Status;
+  UINT32                    Index;
+  UINTN                     SpiBaseAddress;
+  UINT32                    ScSpiBar0;
+  UINT32                    LimitAddress;
+  UINT32                    HardwareSpiAddr;
+  UINT16                    PermissionBit;
+  UINT32                    SpiDataCount;
+  UINT32                    FlashCycle;
+  UINT8                     BiosCtlSave;
+  SPI_INSTANCE              *SpiInstance;
+  UINT32                    Data32;
+
+  SpiInstance = GetSpiInstance ();
+  if (SpiInstance == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status         = EFI_SUCCESS;
+  SpiBaseAddress = SpiInstance->PchSpiBase;
+  ScSpiBar0      = AcquireSpiBar0 (SpiBaseAddress);
+  BiosCtlSave    = 0;
+  SpiInstance->RegionPermission = MmioRead16 (ScSpiBar0 + R_SPI_FRAP);
+
+  //
+  // If it's write cycle, disable Prefetching, Caching and disable BIOS Write Protect
+  //
+  if ((FlashCycleType == FlashCycleWrite) || (FlashCycleType == FlashCycleErase)) {
+    Status = DisableBiosWriteProtect (SpiBaseAddress, mSpiInstance->Flags & FLAGS_SPI_DISABLE_SMM_WRITE_PROTECT);
+    if (EFI_ERROR (Status)) {
+      goto SendSpiCmdEnd;
+    }
+    BiosCtlSave = SaveAndDisableSpiPrefetchCache (SpiBaseAddress);
+  }
+
+  //
+  // Make sure it's safe to program the command.
+  //
+  if (!WaitForSpiCycleComplete (ScSpiBar0, FALSE)) {
+    Status = EFI_DEVICE_ERROR;
+    goto SendSpiCmdEnd;
+  }
+
+  HardwareSpiAddr = Address;
+  if ((FlashCycleType == FlashCycleRead) ||
+      (FlashCycleType == FlashCycleWrite) ||
+      (FlashCycleType == FlashCycleErase)) {
+
+    switch (FlashRegionType) {
+    case FlashRegionDescriptor:
+      if (FlashCycleType == FlashCycleRead) {
+        PermissionBit = B_SPI_FRAP_BRRA_FLASHD;
+      } else {
+        PermissionBit = B_SPI_FRAP_BRWA_FLASHD;
+      }
+      Data32 = MmioRead32 (ScSpiBar0 + R_SPI_FREG0_FLASHD);
+      HardwareSpiAddr += (Data32 & B_SPI_FREG0_BASE_MASK) << N_SPI_FREG0_BASE;
+      LimitAddress     = (Data32 & B_SPI_FREG0_LIMIT_MASK) >> N_SPI_FREG0_LIMIT;
+      break;
+
+    case FlashRegionBios:
+      if (FlashCycleType == FlashCycleRead) {
+        PermissionBit = B_SPI_FRAP_BRRA_BIOS;
+      } else {
+        PermissionBit = B_SPI_FRAP_BRWA_BIOS;
+      }
+      Data32 = MmioRead32 (ScSpiBar0 + R_SPI_FREG1_BIOS);
+      HardwareSpiAddr += (Data32 & B_SPI_FREG1_BASE_MASK) << N_SPI_FREG1_BASE;
+      LimitAddress     = (Data32 & B_SPI_FREG1_LIMIT_MASK) >> N_SPI_FREG1_LIMIT;
+      break;
+
+    case FlashRegionMe:
+      if (FlashCycleType == FlashCycleRead) {
+        PermissionBit = B_SPI_FRAP_BRRA_SEC;
+      } else {
+        PermissionBit = B_SPI_FRAP_BRWA_SEC;
+      }
+      Data32 = MmioRead32 (ScSpiBar0 + R_SPI_FREG2_SEC);
+      HardwareSpiAddr += (Data32 & B_SPI_FREG2_BASE_MASK) << N_SPI_FREG2_BASE;
+      LimitAddress     = (Data32 & B_SPI_FREG2_LIMIT_MASK) >> N_SPI_FREG2_LIMIT;
+      break;
+
+    case FlashRegionGbE:
+      if (FlashCycleType == FlashCycleRead) {
+        PermissionBit = B_SPI_FRAP_BRRA_GBE;
+      } else {
+        PermissionBit = B_SPI_FRAP_BRWA_GBE;
+      }
+      Data32 = MmioRead32 (ScSpiBar0 + R_SPI_FREG3_GBE);
+      HardwareSpiAddr += (Data32 & B_SPI_FREG3_BASE_MASK) << N_SPI_FREG3_BASE;
+      LimitAddress     = (Data32 & B_SPI_FREG3_LIMIT_MASK) >> N_SPI_FREG3_LIMIT;
+      break;
+
+    case FlashRegionPlatformData:
+      if (FlashCycleType == FlashCycleRead) {
+        PermissionBit = B_SPI_FRAP_BRRA_PLATFORM;
+      } else {
+        PermissionBit = B_SPI_FRAP_BRWA_PLATFORM;
+      }
+      Data32 = MmioRead32 (ScSpiBar0 + R_SPI_FREG4_PLATFORM_DATA);
+      HardwareSpiAddr += (Data32 & B_SPI_FREG4_BASE_MASK) << N_SPI_FREG4_BASE;
+      LimitAddress     = (Data32 & B_SPI_FREG4_LIMIT_MASK) >> N_SPI_FREG4_LIMIT;
+      break;
+
+    case FlashRegionAll:
+      //
+      // FlashRegionAll indicates address is relative to flash device
+      // No error checking for this case
+      //
+      LimitAddress = 0;
+      PermissionBit = 0;
+      break;
+
+    default:
+      Status = EFI_UNSUPPORTED;
+      goto SendSpiCmdEnd;
+    }
+
+    if ((LimitAddress != 0) && (Address > LimitAddress)) {
+      Status = EFI_INVALID_PARAMETER;
+      goto SendSpiCmdEnd;
+    }
+
+    //
+    // If the operation is read, but the region attribute is not read allowed, return error.
+    // If the operation is write, but the region attribute is not write allowed, return error.
+    //
+    if ((PermissionBit != 0) && ((SpiInstance->RegionPermission & PermissionBit) == 0)) {
+      Status = EFI_ACCESS_DENIED;
+      goto SendSpiCmdEnd;
+    }
+  }
+
+  //
+  // Check for SC SPI hardware sequencing required commands
+  //
+  FlashCycle = 0;
+  switch (FlashCycleType) {
+  case FlashCycleRead:
+    FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_READ << N_SPI_HSFS_CYCLE);
+    break;
+
+  case FlashCycleWrite:
+    FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_WRITE << N_SPI_HSFS_CYCLE);
+    break;
+
+  case FlashCycleErase:
+    if (((ByteCount % SIZE_4KB) != 0) || ((HardwareSpiAddr % SIZE_4KB) != 0)) {
+      DEBUG ((DEBUG_ERROR, "Erase and erase size must be 4KB aligned. \n"));
+      ASSERT (FALSE);
+      Status = EFI_INVALID_PARAMETER;
+      goto SendSpiCmdEnd;
+    }
+    break;
+
+  case FlashCycleReadSfdp:
+    FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_READ_SFDP << N_SPI_HSFS_CYCLE);
+    break;
+
+  case FlashCycleReadJedecId:
+    FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_READ_JEDEC_ID << N_SPI_HSFS_CYCLE);
+    break;
+
+  case FlashCycleWriteStatus:
+    FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_WRITE_STATUS << N_SPI_HSFS_CYCLE);
+    break;
+
+  case FlashCycleReadStatus:
+    FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_READ_STATUS << N_SPI_HSFS_CYCLE);
+    break;
+
+  default:
+    //
+    // Unrecognized Operation
+    //
+    ASSERT (FALSE);
+    Status = EFI_INVALID_PARAMETER;
+    goto SendSpiCmdEnd;
+    break;
+  }
+
+  do {
+    SpiDataCount = ByteCount;
+    if ((FlashCycleType == FlashCycleRead) || (FlashCycleType == FlashCycleWrite)) {
+      //
+      // Trim at 256 byte boundary per operation,
+      // - SC SPI controller requires trimming at 4KB boundary
+      // - Some SPI chips require trimming at 256 byte boundary for write operation
+      // - Trimming has limited performance impact as we can read / write at most 64 byte
+      //   per operation
+      //
+      if (HardwareSpiAddr + ByteCount > ((HardwareSpiAddr + BIT8) &~(BIT8 - 1))) {
+        SpiDataCount = (((UINT32) (HardwareSpiAddr) + BIT8) &~(BIT8 - 1)) - (UINT32) (HardwareSpiAddr);
+      }
+      //
+      // Calculate the number of bytes to shift in/out during the SPI data cycle.
+      // Valid settings for the number of bytes during each data portion of the
+      // SC SPI cycles are: 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 24, 32, 40, 48, 56, 64
+      //
+      if (SpiDataCount >= 64) {
+        SpiDataCount = 64;
+      } else if ((SpiDataCount &~0x07) != 0) {
+        SpiDataCount = SpiDataCount &~0x07;
+      }
+    }
+
+    if (FlashCycleType == FlashCycleErase) {
+      if (((ByteCount / SIZE_64KB) != 0) &&
+          ((ByteCount % SIZE_64KB) == 0) &&
+          ((HardwareSpiAddr % SIZE_64KB) == 0)) {
+        if (HardwareSpiAddr < SpiInstance->Component1StartAddr) {
+          //
+          // Check whether Component0 support 64k Erase
+          //
+          if ((SpiInstance->SfdpVscc0Value & B_SPI_LVSCC_EO_64K) != 0) {
+            SpiDataCount = SIZE_64KB;
+          } else {
+            SpiDataCount = SIZE_4KB;
+          }
+        } else {
+          //
+          // Check whether Component1 support 64k Erase
+          //
+          if ((SpiInstance->SfdpVscc1Value & B_SPI_LVSCC_EO_64K) != 0) {
+            SpiDataCount = SIZE_64KB;
+          } else {
+            SpiDataCount = SIZE_4KB;
+          }
+        }
+      } else {
+        SpiDataCount = SIZE_4KB;
+      }
+      if (SpiDataCount == SIZE_4KB) {
+        FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_4K_ERASE << N_SPI_HSFS_CYCLE);
+      } else {
+        FlashCycle = (UINT32) (V_SPI_HSFS_CYCLE_64K_ERASE << N_SPI_HSFS_CYCLE);
+      }
+    }
+
+    //
+    // If it's write cycle, load data into the SPI data buffer.
+    //
+    if ((FlashCycleType == FlashCycleWrite) || (FlashCycleType == FlashCycleWriteStatus)) {
+      if ((SpiDataCount & 0x07) != 0) {
+        //
+        // Use Byte write if Data Count is 0, 1, 2, 3, 4, 5, 6, 7
+        //
+        for (Index = 0; Index < SpiDataCount; Index++) {
+          MmioWrite8 (ScSpiBar0 + R_SPI_FDATA00 + Index, Buffer[Index]);
+        }
+      } else {
+        //
+        // Use Dword write if Data Count is 8, 16, 24, 32, 40, 48, 56, 64
+        //
+        for (Index = 0; Index < SpiDataCount; Index += sizeof (UINT32)) {
+          MmioWrite32 (ScSpiBar0 + R_SPI_FDATA00 + Index, *(UINT32 *) (Buffer + Index));
+        }
+      }
+    }
+
+    //
+    // Set the Flash Address
+    //
+    MmioWrite32 (ScSpiBar0 + R_SPI_FADDR, (UINT32) (HardwareSpiAddr & B_SPI_FADDR_MASK));
+
+    //
+    // Set Data count, Flash cycle, and Set Go bit to start a cycle
+    //
+    MmioAndThenOr32 (
+      ScSpiBar0 + R_SPI_HSFS,
+      (UINT32) (~(B_SPI_HSFS_FDBC_MASK | B_SPI_HSFS_CYCLE_MASK)),
+      (UINT32) (((SpiDataCount - 1) << N_SPI_HSFS_FDBC) | FlashCycle | B_SPI_HSFS_CYCLE_FGO)
+      );
+
+    //
+    // Wait for command execution complete.
+    //
+    if (!WaitForSpiCycleComplete (ScSpiBar0, TRUE)) {
+      Status = EFI_DEVICE_ERROR;
+      goto SendSpiCmdEnd;
+    }
+
+    //
+    // If it's read cycle, load data into the caller's buffer.
+    //
+    if ((FlashCycleType == FlashCycleRead) ||
+        (FlashCycleType == FlashCycleReadSfdp) ||
+        (FlashCycleType == FlashCycleReadJedecId) ||
+        (FlashCycleType == FlashCycleReadStatus)) {
+      if ((SpiDataCount & 0x07) != 0) {
+        //
+        // Use Byte read if Data Count is 0, 1, 2, 3, 4, 5, 6, 7
+        //
+        for (Index = 0; Index < SpiDataCount; Index++) {
+          Buffer[Index] = MmioRead8 (ScSpiBar0 + R_SPI_FDATA00 + Index);
+        }
+      } else {
+        //
+        // Use Dword read if Data Count is 8, 16, 24, 32, 40, 48, 56, 64
+        //
+        for (Index = 0; Index < SpiDataCount; Index += sizeof (UINT32)) {
+          *(UINT32 *) (Buffer + Index) = MmioRead32 (ScSpiBar0 + R_SPI_FDATA00 + Index);
+        }
+      }
+    }
+
+    HardwareSpiAddr += SpiDataCount;
+    Buffer          += SpiDataCount;
+    ByteCount       -= SpiDataCount;
+  } while (ByteCount > 0);
+
+SendSpiCmdEnd:
+  ///
+  /// Restore the settings for SPI Prefetching and Caching and enable BIOS Write Protect
+  ///
+  if ((FlashCycleType == FlashCycleWrite) || (FlashCycleType == FlashCycleErase)) {
+    EnableBiosWriteProtect (SpiBaseAddress,  mSpiInstance->Flags & FLAGS_SPI_DISABLE_SMM_WRITE_PROTECT);
+    SetSpiBiosControlRegister (SpiBaseAddress, BiosCtlSave);
+  }
+
+  ReleaseSpiBar0 (SpiBaseAddress);
+
+  return Status;
+}
+
+/**
+  Wait execution cycle to complete on the SPI interface.
+
+  @param[in] ScSpiBar0            Spi MMIO base address
+  @param[in] ErrorCheck           TRUE if the SpiCycle needs to do the error check
+
+  @retval TRUE                    SPI cycle completed on the interface.
+  @retval FALSE                   Time out while waiting the SPI cycle to complete.
+                                  It's not safe to program the next command on the SPI interface.
+**/
+BOOLEAN
+WaitForSpiCycleComplete (
+  IN     UINT32             ScSpiBar0,
+  IN     BOOLEAN            ErrorCheck
+  )
+{
+  UINT64                    WaitTicks;
+  UINT64                    WaitCount;
+  UINT32                    Data32;
+
+  //
+  // Convert the wait period allowed into to tick count
+  //
+  WaitCount = WAIT_TIME / WAIT_PERIOD;
+  //
+  // Wait for the SPI cycle to complete.
+  //
+  for (WaitTicks = 0; WaitTicks < WaitCount; WaitTicks++) {
+    Data32 = MmioRead32 (ScSpiBar0 + R_SPI_HSFS);
+    if ((Data32 & B_SPI_HSFS_SCIP) == 0) {
+      MmioWrite32 (ScSpiBar0 + R_SPI_HSFS, B_SPI_HSFS_FCERR | B_SPI_HSFS_FDONE);
+      if (((Data32 & B_SPI_HSFS_FCERR) != 0) && ErrorCheck) {
+        return FALSE;
+      } else {
+        return TRUE;
+      }
+    }
+    MicroSecondDelay ( WAIT_PERIOD);
+  }
+  return FALSE;
+}
+
+/**
+  Get the SPI region base and size, based on the enum type
+
+  @param[in] FlashRegionType      The Flash Region type for for the base address which is listed in the Descriptor.
+  @param[out] BaseAddress         The Flash Linear Address for the Region 'n' Base
+  @param[out] RegionSize          The size for the Region 'n'
+
+  @retval EFI_SUCCESS             Read success
+  @retval EFI_INVALID_PARAMETER   Invalid region type given
+  @retval EFI_DEVICE_ERROR        The region is not used
+**/
+EFI_STATUS
+EFIAPI
+SpiGetRegionAddress (
+  IN     FLASH_REGION_TYPE  FlashRegionType,
+  OUT    UINT32             *BaseAddress, OPTIONAL
+  OUT    UINT32             *RegionSize OPTIONAL
+  )
+{
+  UINT32                    ScSpiBar0;
+  UINT32                    ReadValue;
+  UINT32                    Base;
+  SPI_INSTANCE             *SpiInstance;
+
+  if (FlashRegionType >= FlashRegionMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SpiInstance = GetSpiInstance();
+  if (SpiInstance == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (FlashRegionType == FlashRegionAll) {
+    if (BaseAddress != NULL) {
+      *BaseAddress  = 0;
+    }
+    if (RegionSize != NULL) {
+      *RegionSize  = SpiInstance->Component1StartAddr;
+    }
+    return EFI_SUCCESS;
+  }
+
+  ScSpiBar0 = AcquireSpiBar0 (SpiInstance->PchSpiBase);
+  ReadValue = MmioRead32 (ScSpiBar0 + R_SPI_FREG0_FLASHD + S_SPI_FREGX * (UINT32) FlashRegionType);
+  ReleaseSpiBar0 (SpiInstance->PchSpiBase);
+
+  //
+  // If the region is not used, the Region Base is 7FFFh and Region Limit is 0000h
+  //
+  if (ReadValue == B_SPI_FREGX_BASE_MASK) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Base = (ReadValue & B_SPI_FREG1_BASE_MASK) << N_SPI_FREG1_BASE;
+  if (BaseAddress != NULL) {
+    *BaseAddress = Base;
+  }
+
+  if (RegionSize != NULL) {
+    *RegionSize =  ((((ReadValue & B_SPI_FREGX_LIMIT_MASK) >> N_SPI_FREGX_LIMIT) + 1) <<
+                     N_SPI_FREGX_LIMIT_REPR) - Base;
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf b/UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf
new file mode 100644
index 0000000000..3e023079fc
--- /dev/null
+++ b/UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf
@@ -0,0 +1,48 @@
+## @file
+#  Library instance for SPI flash library using SPI hardware sequence
+#
+#  Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SpiFlashLib
+  FILE_GUID                      = 6F96AFCB-DE89-4ca1-A63F-8703EE8FDE50
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = SpiFlashLib
+  CONSTRUCTOR                    = SpiConstructor
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  RegsSpi.h
+  SpiCommon.h
+  PchSpi.c
+  SpiFlashLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  PcdLib
+  IoLib
+  PciLib
+  HobLib
+  TimerLib
+  BaseLib
+
+[Guids]
+  gSpiFlashInfoGuid
+
+[Pcd]
+  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dec b/UefiPayloadPkg/UefiPayloadPkg.dec
index 4c3bd0c2eb..3ffdce550d 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dec
+++ b/UefiPayloadPkg/UefiPayloadPkg.dec
@@ -37,6 +37,7 @@
   gUefiSerialPortInfoGuid  = { 0x6c6872fe, 0x56a9, 0x4403, { 0xbb, 0x98, 0x95, 0x8d, 0x62, 0xde, 0x87, 0xf1 } }
   gLoaderMemoryMapInfoGuid = { 0xa1ff7424, 0x7a1a, 0x478e, { 0xa9, 0xe4, 0x92, 0xf3, 0x57, 0xd1, 0x28, 0x32 } }
 
+  gSpiFlashInfoGuid        = { 0x2d4aac1b, 0x91a5, 0x4cd5, { 0x9b, 0x5c, 0xb4, 0x0f, 0x5d, 0x28, 0x51, 0xa1 } }
   gSmmRegisterInfoGuid     = { 0xaa9bd7a7, 0xcafb, 0x4499, { 0xa4, 0xa9, 0xb, 0x34, 0x6b, 0x40, 0xa6, 0x22 } }
   gS3CommunicationGuid     = { 0x88e31ba1, 0x1856, 0x4b8b, { 0xbb, 0xdf, 0xf8, 0x16, 0xdd, 0x94, 0xa, 0xef } }
 
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 5/8] UefiPayloadPkg: Add FlashDeviceLib
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
                   ` (3 preceding siblings ...)
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 4/8] UefiPayloadPkg: Add SpiFlashLib Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 6/8] UefiPayloadPkg: Add a common FVB SMM module Guo Dong
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

This library provides FlashDeviceLib APIs based on
SpiFlashLib and consumed by FVB driver.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 .../Include/Library/FlashDeviceLib.h          | 108 ++++++++++++
 .../Library/FlashDeviceLib/FlashDeviceLib.c   | 165 ++++++++++++++++++
 .../Library/FlashDeviceLib/FlashDeviceLib.inf |  38 ++++
 3 files changed, 311 insertions(+)
 create mode 100644 UefiPayloadPkg/Include/Library/FlashDeviceLib.h
 create mode 100644 UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.c
 create mode 100644 UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf

diff --git a/UefiPayloadPkg/Include/Library/FlashDeviceLib.h b/UefiPayloadPkg/Include/Library/FlashDeviceLib.h
new file mode 100644
index 0000000000..d71481c1b6
--- /dev/null
+++ b/UefiPayloadPkg/Include/Library/FlashDeviceLib.h
@@ -0,0 +1,108 @@
+/** @file
+  Flash device library class header file.
+
+  Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef FLASHDEVICE_LIB_H_
+#define FLASHDEVICE_LIB_H_
+
+/**
+  Read NumBytes bytes of data from the address specified by
+  PAddress into Buffer.
+
+  @param[in]      PAddress    The starting physical address of the read.
+  @param[in,out]  NumBytes    On input, the number of bytes to read. On output, the number
+                              of bytes actually read.
+  @param[out]     Buffer      The destination data buffer for the read.
+
+  @retval EFI_SUCCESS.        Opertion is successful.
+  @retval EFI_DEVICE_ERROR    If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceRead (
+  IN      UINTN                           PAddress,
+  IN  OUT UINTN                           *NumBytes,
+      OUT UINT8                           *Buffer
+  );
+
+
+/**
+  Write NumBytes bytes of data from Buffer to the address specified by
+  PAddresss.
+
+  @param[in]      PAddress The starting physical address of the write.
+  @param[in,out]  NumBytes On input, the number of bytes to write. On output,
+                           the actual number of bytes written.
+  @param[in]      Buffer   The source data buffer for the write.
+
+  @retval EFI_SUCCESS.            Opertion is successful.
+  @retval EFI_DEVICE_ERROR        If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceWrite (
+  IN        UINTN                           PAddress,
+  IN OUT    UINTN                           *NumBytes,
+  IN        UINT8                           *Buffer
+  );
+
+
+/**
+  Erase the block starting at PAddress.
+
+  @param[in]  PAddress The starting physical address of the region to be erased.
+  @param[in]  LbaLength   The length of the region to be erased. This parameter is necessary
+                       as the physical block size on a flash device could be different than
+                       the logical block size of Firmware Volume Block protocol. Erase on
+                       flash chip is always performed block by block. Therefore, the ERASE
+                       operation to a logical block is converted a number of ERASE operation
+                       (or a partial erase) on the hardware.
+
+  @retval EFI_SUCCESS.            Opertion is successful.
+  @retval EFI_DEVICE_ERROR        If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceBlockErase (
+  IN    UINTN                      PAddress,
+  IN    UINTN                      LbaLength
+);
+
+
+/**
+  Lock or unlock the block starting at PAddress.
+
+  @param[in]  PAddress The starting physical address of region to be (un)locked.
+  @param[in]  LbaLength   The length of the region to be (un)locked. This parameter is necessary
+                       as the physical block size on a flash device could be different than
+                       the logical block size of Firmware Volume Block protocol. (Un)Lock on
+                       flash chip is always performed block by block. Therefore, the (Un)Lock
+                       operation to a logical block is converted a number of (Un)Lock operation
+                       (or a partial erase) on the hardware.
+  @param[in]  Lock     TRUE to lock. FALSE to unlock.
+
+  @retval EFI_SUCCESS. Opertion is successful.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceBlockLock (
+  IN    UINTN                          PAddress,
+  IN    UINTN                          LbaLength,
+  IN    BOOLEAN                        Lock
+);
+
+PHYSICAL_ADDRESS
+EFIAPI
+LibFvbFlashDeviceMemoryMap (
+);
+
+#endif
diff --git a/UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.c b/UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.c
new file mode 100644
index 0000000000..065841bc93
--- /dev/null
+++ b/UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.c
@@ -0,0 +1,165 @@
+/** @file
+  Flash Device Library based on SPI Flash library.
+
+Copyright (c) 2018 - 2021, Intel Corporation. All rights reserved. <BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/SpiFlashLib.h>
+
+/**
+  Initialize spi flash device.
+
+  @retval EFI_SUCCESS              The tested spi flash device is supported.
+  @retval EFI_UNSUPPORTED          The tested spi flash device is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceInit (
+  VOID
+  )
+{
+  return SpiConstructor ();
+}
+
+
+/**
+  Read NumBytes bytes of data from the address specified by
+  PAddress into Buffer.
+
+  @param[in]      PAddress      The starting physical address of the read.
+  @param[in,out]  NumBytes      On input, the number of bytes to read. On output, the number
+                                of bytes actually read.
+  @param[out]     Buffer        The destination data buffer for the read.
+
+  @retval         EFI_SUCCESS.      Opertion is successful.
+  @retval         EFI_DEVICE_ERROR  If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceRead (
+  IN      UINTN                           PAddress,
+  IN  OUT UINTN                           *NumBytes,
+      OUT UINT8                           *Buffer
+  )
+{
+  EFI_STATUS                              Status;
+  UINT32                                  ByteCount;
+  UINT32                                  RgnSize;
+  UINT32                                  AddrOffset;
+
+  Status = SpiGetRegionAddress (FlashRegionBios, NULL, &RgnSize);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // BIOS region offset can be calculated by (PAddress - (0x100000000 - RgnSize))
+  // which equal (PAddress + RgnSize) here.
+  AddrOffset = (UINT32)((UINT32)PAddress + RgnSize);
+  ByteCount  = (UINT32)*NumBytes;
+  return SpiFlashRead (FlashRegionBios, AddrOffset, ByteCount, Buffer);
+}
+
+
+/**
+  Write NumBytes bytes of data from Buffer to the address specified by
+  PAddresss.
+
+  @param[in]      PAddress        The starting physical address of the write.
+  @param[in,out]  NumBytes        On input, the number of bytes to write. On output,
+                                  the actual number of bytes written.
+  @param[in]      Buffer          The source data buffer for the write.
+
+  @retval         EFI_SUCCESS.      Opertion is successful.
+  @retval         EFI_DEVICE_ERROR  If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceWrite (
+  IN        UINTN                           PAddress,
+  IN OUT    UINTN                           *NumBytes,
+  IN        UINT8                           *Buffer
+  )
+{
+  EFI_STATUS                                Status;
+  UINT32                                    ByteCount;
+  UINT32                                    RgnSize;
+  UINT32                                    AddrOffset;
+
+  Status = SpiGetRegionAddress (FlashRegionBios, NULL, &RgnSize);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // BIOS region offset can be calculated by (PAddress - (0x100000000 - RgnSize))
+  // which equal (PAddress + RgnSize) here.
+  AddrOffset = (UINT32)((UINT32)PAddress + RgnSize);
+  ByteCount  = (UINT32)*NumBytes;
+  return SpiFlashWrite (FlashRegionBios, AddrOffset, ByteCount, Buffer);
+}
+
+
+/**
+  Erase the block starting at PAddress.
+
+  @param[in]  PAddress        The starting physical address of the block to be erased.
+                              This library assume that caller garantee that the PAddress
+                              is at the starting address of this block.
+  @param[in]  LbaLength       The length of the logical block to be erased.
+
+  @retval     EFI_SUCCESS.      Opertion is successful.
+  @retval     EFI_DEVICE_ERROR  If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceBlockErase (
+  IN    UINTN                     PAddress,
+  IN    UINTN                     LbaLength
+  )
+{
+  EFI_STATUS                      Status;
+  UINT32                          RgnSize;
+  UINT32                          AddrOffset;
+
+  Status = SpiGetRegionAddress (FlashRegionBios, NULL, &RgnSize);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // BIOS region offset can be calculated by (PAddress - (0x100000000 - RgnSize))
+  // which equal (PAddress + RgnSize) here.
+  AddrOffset = (UINT32)((UINT32)PAddress + RgnSize);
+  return SpiFlashErase (FlashRegionBios, AddrOffset, (UINT32)LbaLength);
+}
+
+
+/**
+  Lock or unlock the block starting at PAddress.
+
+  @param[in]  PAddress        The starting physical address of region to be (un)locked.
+  @param[in]  LbaLength       The length of the logical block to be erased.
+  @param[in]  Lock            TRUE to lock. FALSE to unlock.
+
+  @retval     EFI_SUCCESS.      Opertion is successful.
+  @retval     EFI_DEVICE_ERROR  If there is any device errors.
+
+**/
+EFI_STATUS
+EFIAPI
+LibFvbFlashDeviceBlockLock (
+  IN    UINTN                          PAddress,
+  IN    UINTN                          LbaLength,
+  IN    BOOLEAN                        Lock
+  )
+{
+  return EFI_SUCCESS;
+}
+
diff --git a/UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf b/UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf
new file mode 100644
index 0000000000..0ad22cffba
--- /dev/null
+++ b/UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf
@@ -0,0 +1,38 @@
+## @file
+# Library instace of Flash Device Library Class
+#
+# This library implement the flash device library class for the lakeport platform.
+#@copyright
+# Copyright (c) 2014 - 2021 Intel Corporation. All rights reserved
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FlashDeviceLib
+  FILE_GUID                      = BA7CA537-1C65-4a90-9379-622A24A08141
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = FlashDeviceLib | DXE_SMM_DRIVER DXE_RUNTIME_DRIVER
+
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  FlashDeviceLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  BaseMemoryLib
+  SpiFlashLib
+
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 6/8] UefiPayloadPkg: Add a common FVB SMM module
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
                   ` (4 preceding siblings ...)
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 5/8] UefiPayloadPkg: Add FlashDeviceLib Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 7/8] UefiPayloadPkg: Add a SMM dispatch module Guo Dong
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

This FVB module is used to initialize NV variable region
and provide SMM FVB protocol to read/write SPI variable region.

This module consume HOB gNvVariableInfoGuid and depends on
FlashDeviceLib for the actual SPI device operate.

During FVB initialization, it will initialize the variable region
if the variable region is not valid. And it support to write initial
variable data from FFS file if it is found.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c        |  151 +++
 UefiPayloadPkg/FvbRuntimeDxe/FvbService.c     | 1088 +++++++++++++++++
 UefiPayloadPkg/FvbRuntimeDxe/FvbService.h     |  187 +++
 UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c  |  139 +++
 UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf       |   71 ++
 UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h   |   69 ++
 .../Include/Guid/NvVariableInfoGuid.h         |   24 +
 UefiPayloadPkg/UefiPayloadPkg.dec             |    6 +
 8 files changed, 1735 insertions(+)
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
 create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
 create mode 100644 UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h

diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c b/UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
new file mode 100644
index 0000000000..78b6b81f66
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
@@ -0,0 +1,151 @@
+/** @file
+
+  Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Guid/FirmwareFileSystem2.h>
+#include <Guid/SystemNvDataGuid.h>
+#include <Guid/NvVariableInfoGuid.h>
+#include <Library/HobLib.h>
+
+#define FVB_MEDIA_BLOCK_SIZE        0x1000
+
+typedef struct {
+  EFI_FIRMWARE_VOLUME_HEADER        FvInfo;
+  EFI_FV_BLOCK_MAP_ENTRY            End[1];
+} EFI_FVB2_MEDIA_INFO;
+
+//
+// This data structure contains a template of FV header which is used to restore
+// Fv header if it's corrupted.
+//
+EFI_FVB2_MEDIA_INFO mFvbMediaInfo = {
+  {
+    {0,},           // ZeroVector[16]
+    EFI_SYSTEM_NV_DATA_FV_GUID,
+    0,
+    EFI_FVH_SIGNATURE,
+    0x0004feff,     // check PiFirmwareVolume.h for details on EFI_FVB_ATTRIBUTES_2
+    sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
+    0,              // CheckSum which will be calucated dynamically.
+    0,              // ExtHeaderOffset
+    {0,},
+    EFI_FVH_REVISION,
+    {
+      {
+        0,
+        FVB_MEDIA_BLOCK_SIZE,
+      }
+    }
+  },
+  {
+    {
+      0,
+      0
+    }
+  }
+};
+
+/**
+  Initialize the variable store
+
+  @retval     EFI_SUCCESS if initialize the store success.
+
+**/
+EFI_STATUS
+InitVariableStore (
+  VOID
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              NvStorageBase;
+  UINT32              NvStorageSize;
+  UINT32              NvVariableSize;
+  UINT32              FtwWorkingSize;
+  UINT32              FtwSpareSize;
+  EFI_HOB_GUID_TYPE   *GuidHob;
+  NV_VARIABLE_INFO    *NvVariableInfo;
+
+  //
+  // Find SPI flash variable hob
+  //
+  GuidHob = GetFirstGuidHob (&gNvVariableInfoGuid);
+  if (GuidHob == NULL) {
+    ASSERT (FALSE);
+    return EFI_NOT_FOUND;
+  }
+  NvVariableInfo = (NV_VARIABLE_INFO *) GET_GUID_HOB_DATA (GuidHob);
+
+  //
+  // Get variable region base and size.
+  //
+  NvStorageSize = NvVariableInfo->VariableStoreSize;
+  NvStorageBase = NvVariableInfo->VariableStoreBase;
+
+  //
+  // NvStorageBase needs to be 4KB aligned, NvStorageSize needs to be 8KB * n
+  //
+  if (((NvStorageBase & (SIZE_4KB - 1)) != 0) || ((NvStorageSize & (SIZE_8KB - 1)) != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FtwSpareSize   = NvStorageSize / 2;
+  FtwWorkingSize = 0x2000;
+  NvVariableSize = NvStorageSize / 2 - FtwWorkingSize;
+  DEBUG ((DEBUG_INFO, "NvStorageBase:0x%x, NvStorageSize:0x%x\n", NvStorageBase, NvStorageSize));
+
+  if (NvVariableSize >= 0x80000000) {
+    return EFI_INVALID_PARAMETER;
+  }
+  Status = PcdSet32S(PcdFlashNvStorageVariableSize,   NvVariableSize);
+  ASSERT_EFI_ERROR (Status);
+  Status = PcdSet32S(PcdFlashNvStorageVariableBase,   NvStorageBase);
+  ASSERT_EFI_ERROR (Status);
+  Status = PcdSet64S(PcdFlashNvStorageVariableBase64, NvStorageBase);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = PcdSet32S(PcdFlashNvStorageFtwWorkingSize, FtwWorkingSize);
+  ASSERT_EFI_ERROR (Status);
+  Status = PcdSet32S(PcdFlashNvStorageFtwWorkingBase, NvStorageBase + NvVariableSize);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = PcdSet32S(PcdFlashNvStorageFtwSpareSize,   FtwSpareSize);
+  ASSERT_EFI_ERROR (Status);
+  Status = PcdSet32S(PcdFlashNvStorageFtwSpareBase,   NvStorageBase + FtwSpareSize);
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Get a heathy FV header used for variable store recovery
+
+  @retval     The FV header.
+
+**/
+EFI_FIRMWARE_VOLUME_HEADER *
+GetFvHeaderTemplate (
+  VOID
+  )
+{
+  EFI_FIRMWARE_VOLUME_HEADER        *FvHeader;
+  UINTN                             FvSize;
+
+  FvSize = PcdGet32(PcdFlashNvStorageFtwSpareSize) * 2;
+  FvHeader                        = &mFvbMediaInfo.FvInfo;
+  FvHeader->FvLength              = FvSize;
+  FvHeader->BlockMap[0].NumBlocks = (UINT32) (FvSize / FvHeader->BlockMap[0].Length);
+  FvHeader->Checksum = 0;
+  FvHeader->Checksum = CalculateCheckSum16 ((UINT16 *) FvHeader, FvHeader->HeaderLength);
+
+  return FvHeader;
+}
+
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbService.c b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
new file mode 100644
index 0000000000..d90a39b564
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
@@ -0,0 +1,1088 @@
+/** @file
+Firmware Volume Block Driver to provide FVB service.
+
+  Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "FvbService.h"
+
+//
+// Global variable for this FVB driver  which contains
+// the private data of all firmware volume block instances
+//
+FWB_GLOBAL               mFvbModuleGlobal;
+
+FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_MEMMAP_DP,
+      {
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
+      }
+    },
+    EfiMemoryMappedIO,
+    (EFI_PHYSICAL_ADDRESS) 0,
+    (EFI_PHYSICAL_ADDRESS) 0,
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {
+  {
+    {
+      MEDIA_DEVICE_PATH,
+      MEDIA_PIWG_FW_VOL_DP,
+      {
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
+      }
+    },
+    { 0 }
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+
+EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate = {
+  FVB_DEVICE_SIGNATURE,
+  NULL,
+  0, // Instance
+  {
+    FvbProtocolGetAttributes,
+    FvbProtocolSetAttributes,
+    FvbProtocolGetPhysicalAddress,
+    FvbProtocolGetBlockSize,
+    FvbProtocolRead,
+    FvbProtocolWrite,
+    FvbProtocolEraseBlocks,
+    NULL
+  } // FwVolBlockInstance
+};
+
+
+/**
+  Get the pointer to EFI_FW_VOL_INSTANCE from the buffer pointed
+  by mFvbModuleGlobal.FvInstance based on a index.
+  Each EFI_FW_VOL_INSTANCE is  with variable length as
+  we have a block map at the end of the EFI_FIRMWARE_VOLUME_HEADER.
+
+  @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.
+
+  @return A pointer to EFI_FW_VOL_INSTANCE.
+
+**/
+EFI_FW_VOL_INSTANCE *
+GetFvbInstance (
+  IN  UINTN             Instance
+  )
+{
+  EFI_FW_VOL_INSTANCE   *FwhRecord;
+
+  if ( Instance >= mFvbModuleGlobal.NumFv ) {
+    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+    return NULL;
+  }
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  FwhRecord = mFvbModuleGlobal.FvInstance;
+  while ( Instance > 0 ) {
+    FwhRecord = (EFI_FW_VOL_INSTANCE *) ((UINTN)((UINT8 *)FwhRecord) +
+                FwhRecord->VolumeHeader.HeaderLength +
+                (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER)));
+    Instance--;
+  }
+
+  return FwhRecord;
+
+}
+
+
+/**
+  Get the EFI_FVB_ATTRIBUTES_2 of a FV.
+
+  @param[in]  Instance    The index of the EFI_FW_VOL_INSTANCE.
+
+  @retval     EFI_FVB_ATTRIBUTES_2 of the FV identified by Instance.
+
+**/
+STATIC
+EFI_FVB_ATTRIBUTES_2
+FvbGetVolumeAttributes (
+  IN UINTN                                Instance
+  )
+{
+  EFI_FW_VOL_INSTANCE *    FwInstance;
+  FwInstance = GetFvbInstance(Instance);
+  ASSERT (FwInstance != NULL);
+
+  if (FwInstance == NULL) {
+    return 0;
+  }
+
+  return FwInstance->VolumeHeader.Attributes;
+}
+
+
+
+/**
+  Retrieves the starting address of an LBA in an FV. It also
+  return a few other attribut of the FV.
+
+  @param[in]  Instance        The index of the EFI_FW_VOL_INSTANCE.
+  @param[in]  Lba             The logical block address
+  @param[out] LbaAddress      On output, contains the physical starting address
+                              of the Lba
+  @param[out] LbaLength       On output, contains the length of the block
+  @param[out] NumOfBlocks     A pointer to a caller allocated UINTN in which the
+                              number of consecutive blocks starting with Lba is
+                              returned. All blocks in this range have a size of
+                              BlockSize
+
+  @retval   EFI_SUCCESS Successfully returns
+  @retval   EFI_INVALID_PARAMETER Instance not found
+
+**/
+STATIC
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  UINTN                               Instance,
+  IN  EFI_LBA                             Lba,
+  OUT UINTN                               *LbaAddress,
+  OUT UINTN                               *LbaLength,
+  OUT UINTN                               *NumOfBlocks
+  )
+{
+  UINT32                                  NumBlocks;
+  UINT32                                  BlockLength;
+  UINTN                                   Offset;
+  EFI_LBA                                 StartLba;
+  EFI_LBA                                 NextLba;
+  EFI_FW_VOL_INSTANCE                     *FwhInstance;
+  EFI_FV_BLOCK_MAP_ENTRY                  *BlockMap;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  FwhInstance = GetFvbInstance (Instance);
+  if (FwhInstance == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  StartLba  = 0;
+  Offset    = 0;
+  BlockMap  = &FwhInstance->VolumeHeader.BlockMap[0];
+  ASSERT (BlockMap != NULL);
+
+  //
+  // Parse the blockmap of the FV to find which map entry the Lba belongs to
+  //
+  while (TRUE) {
+    if ( BlockMap != NULL) {
+      NumBlocks   = BlockMap->NumBlocks;
+      BlockLength = BlockMap->Length;
+    }
+
+    if ( NumBlocks == 0 || BlockLength == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    NextLba = StartLba + NumBlocks;
+
+    //
+    // The map entry found
+    //
+    if (Lba >= StartLba && Lba < NextLba) {
+      Offset = Offset + (UINTN)MultU64x32((Lba - StartLba), BlockLength);
+      if (LbaAddress != NULL) {
+        *LbaAddress = FwhInstance->FvBase + Offset;
+      }
+
+      if (LbaLength != NULL) {
+        *LbaLength = BlockLength;
+      }
+
+      if (NumOfBlocks != NULL) {
+        *NumOfBlocks = (UINTN)(NextLba - Lba);
+      }
+      return EFI_SUCCESS;
+    }
+
+    StartLba  = NextLba;
+    Offset    = Offset + NumBlocks * BlockLength;
+    BlockMap++;
+  }
+}
+
+
+/**
+  Reads specified number of bytes into a buffer from the specified block
+
+  @param[in]      Instance        The FV instance to be read from
+  @param[in]      Lba             The logical block address to be read from
+  @param[in]      BlockOffset     Offset into the block at which to begin reading
+  @param[in, out] NumBytes        Pointer that on input contains the total size of
+                                  the buffer. On output, it contains the total number
+                                  of bytes read
+  @param[in]      Buffer          Pointer to a caller allocated buffer that will be
+                                  used to hold the data read
+
+
+  @retval         EFI_SUCCESS         The firmware volume was read successfully and
+                                      contents are in Buffer
+  @retval         EFI_BAD_BUFFER_SIZE Read attempted across a LBA boundary. On output,
+                                      NumBytes contains the total number of bytes returned
+                                      in Buffer
+  @retval         EFI_ACCESS_DENIED   The firmware volume is in the ReadDisabled state
+  @retval         EFI_DEVICE_ERROR    The block device is not functioning correctly and
+                                      could not be read
+  @retval         EFI_INVALID_PARAMETER Instance not found, or NumBytes, Buffer are NULL
+
+**/
+STATIC
+EFI_STATUS
+FvbReadBlock (
+  IN UINTN                                Instance,
+  IN EFI_LBA                              Lba,
+  IN UINTN                                BlockOffset,
+  IN OUT UINTN                            *NumBytes,
+  IN UINT8                                *Buffer
+  )
+{
+  EFI_FVB_ATTRIBUTES_2                    Attributes;
+  UINTN                                   LbaAddress;
+  UINTN                                   LbaLength;
+  EFI_STATUS                              Status;
+  EFI_STATUS                              ReadStatus;
+
+  if ( (NumBytes == NULL) || (Buffer == NULL)) {
+    return (EFI_INVALID_PARAMETER);
+  }
+  if (*NumBytes == 0) {
+    return (EFI_INVALID_PARAMETER);
+  }
+
+  Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  Attributes = FvbGetVolumeAttributes (Instance);
+
+  if ( (Attributes & EFI_FVB2_READ_STATUS) == 0) {
+    return (EFI_ACCESS_DENIED);
+  }
+
+  if (BlockOffset > LbaLength) {
+   return (EFI_INVALID_PARAMETER);
+  }
+
+  if (LbaLength < ( *NumBytes + BlockOffset ) ) {
+    *NumBytes = (UINT32) (LbaLength - BlockOffset);
+    Status = EFI_BAD_BUFFER_SIZE;
+  }
+
+  ReadStatus = LibFvbFlashDeviceRead (LbaAddress + BlockOffset, NumBytes, Buffer);
+  if (EFI_ERROR(ReadStatus)) {
+    return ReadStatus;
+  }
+
+  return Status;
+}
+
+
+/**
+  Writes specified number of bytes from the input buffer to the block
+
+  @param[in]  Instance          The FV instance to be written to
+  @param[in]  Lba               The starting logical block index to write to
+  @param[in]  BlockOffset       Offset into the block at which to begin writing
+  @param[in, out]  NumBytes     Pointer that on input contains the total size of
+                                 the buffer. On output, it contains the total number
+                                 of bytes actually written
+  @param[in]  Buffer            Pointer to a caller allocated buffer that contains
+                                 the source for the write
+  @retval     EFI_SUCCESS         The firmware volume was written successfully
+  @retval     EFI_BAD_BUFFER_SIZE Write attempted across a LBA boundary. On output,
+                                  NumBytes contains the total number of bytes
+                                  actually written
+  @retval     EFI_ACCESS_DENIED   The firmware volume is in the WriteDisabled state
+  @retval     EFI_DEVICE_ERROR    The block device is not functioning correctly and
+                                  could not be written
+  @retval     EFI_INVALID_PARAMETER Instance not found, or NumBytes, Buffer are NULL
+
+**/
+EFI_STATUS
+FvbWriteBlock (
+  IN UINTN                                Instance,
+  IN EFI_LBA                              Lba,
+  IN UINTN                                BlockOffset,
+  IN OUT UINTN                            *NumBytes,
+  IN UINT8                                *Buffer
+  )
+{
+  EFI_FVB_ATTRIBUTES_2                    Attributes;
+  UINTN                                   LbaAddress;
+  UINTN                                   LbaLength;
+  EFI_STATUS                              Status;
+
+  if ( (NumBytes == NULL) || (Buffer == NULL)) {
+    return (EFI_INVALID_PARAMETER);
+  }
+  if (*NumBytes == 0) {
+    return (EFI_INVALID_PARAMETER);
+  }
+
+  Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  //
+  // Check if the FV is write enabled
+  //
+  Attributes = FvbGetVolumeAttributes (Instance);
+  if ( (Attributes & EFI_FVB2_WRITE_STATUS) == 0) {
+    return EFI_ACCESS_DENIED;
+  }
+
+  //
+  // Perform boundary checks and adjust NumBytes
+  //
+  if (BlockOffset > LbaLength) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ( LbaLength < ( *NumBytes + BlockOffset ) ) {
+    DEBUG ((DEBUG_ERROR,
+      "FvWriteBlock: Reducing Numbytes from 0x%x to 0x%x\n",
+      *NumBytes, (UINT32)(LbaLength - BlockOffset)));
+    *NumBytes = (UINT32) (LbaLength - BlockOffset);
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);
+  Status = LibFvbFlashDeviceWrite (LbaAddress + BlockOffset, NumBytes, Buffer);
+
+  LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);
+  WriteBackInvalidateDataCacheRange ((VOID *) (LbaAddress + BlockOffset), *NumBytes);
+  return Status;
+}
+
+
+/**
+  Erases and initializes a firmware volume block
+
+  @param[in]    Instance    The FV instance to be erased
+  @param[in]    Lba         The logical block index to be erased
+
+  @retval   EFI_SUCCESS       The erase request was successfully completed
+  @retval   EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state
+  @retval   EFI_DEVICE_ERROR  The block device is not functioning correctly and
+                              could not be written. Firmware device may have been
+                              partially erased
+  @retval   EFI_INVALID_PARAMETER Instance not found
+
+**/
+EFI_STATUS
+FvbEraseBlock (
+  IN UINTN                                Instance,
+  IN EFI_LBA                              Lba
+  )
+{
+
+  EFI_FVB_ATTRIBUTES_2                    Attributes;
+  UINTN                                   LbaAddress;
+  UINTN                                   LbaLength;
+  EFI_STATUS                              Status;
+
+  //
+  // Check if the FV is write enabled
+  //
+  Attributes = FvbGetVolumeAttributes (Instance);
+
+  if( (Attributes & EFI_FVB2_WRITE_STATUS) == 0)  {
+    return (EFI_ACCESS_DENIED);
+  }
+
+  //
+  // Get the starting address of the block for erase.
+  //
+  Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);
+
+  Status = LibFvbFlashDeviceBlockErase (LbaAddress, LbaLength);
+
+  LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);
+
+  WriteBackInvalidateDataCacheRange ((VOID *) LbaAddress, LbaLength);
+
+  return Status;
+}
+
+/**
+  Modifies the current settings of the firmware volume according to the
+  input parameter, and returns the new setting of the volume
+
+  @param[in]  Instance              The FV instance whose attributes is going to be
+                                    modified
+  @param[in, out]  Attributes       On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
+                                    containing the desired firmware volume settings.
+                                    On successful return, it contains the new settings
+                                    of the firmware volume
+
+  @retval     EFI_SUCCESS           Successfully returns
+  @retval     EFI_ACCESS_DENIED     The volume setting is locked and cannot be modified
+  @retval     EFI_INVALID_PARAMETER Instance not found, or The attributes requested are
+                                    in conflict with the capabilities as declared in the
+                                    firmware volume header
+
+**/
+STATIC
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN UINTN                                Instance,
+  IN OUT EFI_FVB_ATTRIBUTES_2             *Attributes
+  )
+{
+  EFI_FW_VOL_INSTANCE                       *FwhInstance;
+  EFI_FVB_ATTRIBUTES_2                      OldAttributes;
+  EFI_FVB_ATTRIBUTES_2                      *AttribPtr;
+  EFI_FVB_ATTRIBUTES_2                      UnchangedAttributes;
+  UINT32                                    Capabilities;
+  UINT32                                    OldStatus;
+  UINT32                                    NewStatus;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  FwhInstance = GetFvbInstance (Instance);
+  if (FwhInstance == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  AttribPtr = (EFI_FVB_ATTRIBUTES_2 *) &(FwhInstance->VolumeHeader.Attributes);
+  ASSERT (AttribPtr != NULL);
+  if ( AttribPtr == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  OldAttributes = *AttribPtr;
+  Capabilities  = OldAttributes & EFI_FVB2_CAPABILITIES;
+  OldStatus     = OldAttributes & EFI_FVB2_STATUS;
+  NewStatus     = *Attributes   & EFI_FVB2_STATUS;
+
+  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
+                        EFI_FVB2_READ_ENABLED_CAP   | \
+                        EFI_FVB2_WRITE_DISABLED_CAP | \
+                        EFI_FVB2_WRITE_ENABLED_CAP  | \
+                        EFI_FVB2_LOCK_CAP           | \
+                        EFI_FVB2_STICKY_WRITE       | \
+                        EFI_FVB2_MEMORY_MAPPED      | \
+                        EFI_FVB2_ERASE_POLARITY     | \
+                        EFI_FVB2_READ_LOCK_CAP      | \
+                        EFI_FVB2_WRITE_LOCK_CAP     | \
+                        EFI_FVB2_ALIGNMENT;
+
+  //
+  // Some attributes of FV is read only can *not* be set
+  //
+  if ((OldAttributes & UnchangedAttributes) ^ (*Attributes & UnchangedAttributes)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If firmware volume is locked, no status bit can be updated
+  //
+  if ((OldAttributes & EFI_FVB2_LOCK_STATUS) != 0) {
+    if ((OldStatus ^ NewStatus) != 0) {
+      return EFI_ACCESS_DENIED;
+    }
+  }
+
+  //
+  // Test read disable
+  //
+  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test read enable
+  //
+  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_READ_STATUS) != 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test write disable
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test write enable
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_WRITE_STATUS) != 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test lock
+  //
+  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_LOCK_STATUS) != 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
+  *AttribPtr  = (*AttribPtr) | NewStatus;
+  *Attributes = *AttribPtr;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Retrieves the physical address of the device.
+
+  @param[in]  This    A pointer to EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
+  @param[out] Address Output buffer containing the address.
+
+  @retval     EFI_SUCCESS The function always return successfully.
+  @retval     EFI_INVALID_PARAMETER Instance not found.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  OUT EFI_PHYSICAL_ADDRESS                      *Address
+  )
+{
+  EFI_FW_VOL_BLOCK_DEVICE                       *FvbDevice;
+  EFI_FW_VOL_INSTANCE                           *FwhInstance;
+
+  FvbDevice   = FVB_DEVICE_FROM_THIS (This);
+  FwhInstance = GetFvbInstance(FvbDevice->Instance);
+  if (FwhInstance == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Address = FwhInstance->FvBase;
+  return EFI_SUCCESS;
+}
+
+
+
+/**
+  Retrieve the size of a logical block
+
+  @param[in]  This        Calling context
+  @param[in]  Lba         Indicates which block to return the size for.
+  @param[out] BlockSize   A pointer to a caller allocated UINTN in which
+                          the size of the block is returned
+  @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the
+                          number of consecutive blocks starting with Lba is
+                          returned. All blocks in this range have a size of
+                          BlockSize
+
+  @retval     EFI_SUCCESS The function always return successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN  EFI_LBA                                   Lba,
+  OUT UINTN                                     *BlockSize,
+  OUT UINTN                                     *NumOfBlocks
+  )
+{
+  EFI_FW_VOL_BLOCK_DEVICE                       *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+  return FvbGetLbaAddress (FvbDevice->Instance, Lba, NULL, BlockSize, NumOfBlocks);
+}
+
+
+/**
+  Retrieves Volume attributes.  No polarity translations are done.
+
+  @param[in]    This        Calling context
+  @param[out]   Attributes  Output buffer which contains attributes
+
+  @retval       EFI_SUCCESS The function always return successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  OUT EFI_FVB_ATTRIBUTES_2                      *Attributes
+  )
+{
+  EFI_FW_VOL_BLOCK_DEVICE                       *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+  *Attributes = FvbGetVolumeAttributes (FvbDevice->Instance);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Sets Volume attributes. No polarity translations are done.
+
+  @param[in]      This        Calling context
+  @param[in, out] Attributes  Output buffer which contains attributes
+
+  @retval     EFI_SUCCESS The function always return successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                   *Attributes
+  )
+{
+  EFI_STATUS                                    Status;
+  EFI_FW_VOL_BLOCK_DEVICE                       *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+  Status    = FvbSetVolumeAttributes (FvbDevice->Instance, Attributes);
+  return Status;
+}
+
+
+
+/**
+  This function erases one or more blocks as denoted by the
+  variable argument list. The entire parameter list of blocks must be verified
+  prior to erasing any blocks.  If a block is requested that does not exist
+  within the associated firmware volume (it has a larger index than the last
+  block of the firmware volume), the EraseBlock() function must return
+  EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
+
+  @param[in] This         Calling context
+  @param[in] ...          Starting LBA followed by Number of Lba to erase.
+                          a -1 to terminate the list.
+
+  @retval EFI_SUCCESS       The erase request was successfully completed
+  @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state
+  @retval EFI_DEVICE_ERROR  The block device is not functioning correctly and
+                            could not be written. Firmware device may have been
+                            partially erased
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  ...
+  )
+{
+  EFI_FW_VOL_BLOCK_DEVICE                       *FvbDevice;
+  EFI_FW_VOL_INSTANCE                           *FwhInstance;
+  UINTN                                         NumOfBlocks;
+  VA_LIST                                       args;
+  EFI_LBA                                       StartingLba;
+  UINTN                                         NumOfLba;
+  EFI_STATUS                                    Status;
+
+  FvbDevice    = FVB_DEVICE_FROM_THIS (This);
+  FwhInstance  = GetFvbInstance (FvbDevice->Instance);
+  if (FwhInstance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  NumOfBlocks = FwhInstance->NumOfBlocks;
+  VA_START (args, This);
+
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if ( StartingLba == EFI_LBA_LIST_TERMINATOR ) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINT32);
+
+    //
+    // Check input parameters
+    //
+    if (NumOfLba == 0) {
+      VA_END (args);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if ( ( StartingLba + NumOfLba ) > NumOfBlocks ) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } while ( 1 );
+
+  VA_END (args);
+
+  VA_START (args, This);
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINT32);
+
+    while ( NumOfLba > 0 ) {
+      Status = FvbEraseBlock (FvbDevice->Instance, StartingLba);
+      if ( EFI_ERROR(Status)) {
+        VA_END (args);
+        return Status;
+      }
+      StartingLba++;
+      NumOfLba--;
+    }
+  } while ( 1 );
+
+  VA_END (args);
+
+  return EFI_SUCCESS;
+}
+
+
+
+/**
+  Writes data beginning at Lba:Offset from FV. The write terminates either
+  when *NumBytes of data have been written, or when a block boundary is
+  reached.  *NumBytes is updated to reflect the actual number of bytes
+  written. The write opertion does not include erase. This routine will
+  attempt to write only the specified bytes. If the writes do not stick,
+  it will return an error.
+
+  @param[in]      This      Calling context
+  @param[in]      Lba       Block in which to begin write
+  @param[in]      Offset    Offset in the block at which to begin write
+  @param[in,out]  NumBytes  On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes written
+  @param[in]      Buffer    Buffer containing source data for the write.
+
+  @retval EFI_SUCCESS           The firmware volume was written successfully
+  @retval EFI_BAD_BUFFER_SIZE   Write attempted across a LBA boundary. On output,
+                                NumBytes contains the total number of bytes
+                                actually written
+  @retval EFI_ACCESS_DENIED     The firmware volume is in the WriteDisabled state
+  @retval EFI_DEVICE_ERROR      The block device is not functioning correctly and
+                                could not be written
+  @retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN EFI_LBA                                    Lba,
+  IN UINTN                                      Offset,
+  IN OUT UINTN                                  *NumBytes,
+  IN UINT8                                      *Buffer
+  )
+{
+  EFI_FW_VOL_BLOCK_DEVICE                       *FvbDevice;
+  EFI_STATUS                                    Status;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+  Status    = FvbWriteBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);
+  DEBUG((DEBUG_VERBOSE,
+    "FvbWrite: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x Status:%r\n",
+    Lba, Offset, *NumBytes, Buffer, Status));
+
+  return Status;
+}
+
+
+/**
+  Reads data beginning at Lba:Offset from FV. The Read terminates either
+  when *NumBytes of data have been read, or when a block boundary is
+  reached.  *NumBytes is updated to reflect the actual number of bytes
+  written. The write opertion does not include erase. This routine will
+  attempt to write only the specified bytes. If the writes do not stick,
+  it will return an error.
+
+  @param[in]      This      Calling context
+  @param[in]      Lba       Block in which to begin write
+  @param[in]      Offset    Offset in the block at which to begin write
+  @param[in,out]  NumBytes  On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes written
+  @param[out]     Buffer    Buffer containing source data for the write.
+
+
+Returns:
+  @retval EFI_SUCCESS           The firmware volume was read successfully and
+                                contents are in Buffer
+  @retval EFI_BAD_BUFFER_SIZE   Read attempted across a LBA boundary. On output,
+                                NumBytes contains the total number of bytes returned
+                                in Buffer
+  @retval EFI_ACCESS_DENIED     The firmware volume is in the ReadDisabled state
+  @retval EFI_DEVICE_ERROR      The block device is not functioning correctly and
+                                could not be read
+  @retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN EFI_LBA                                    Lba,
+  IN UINTN                                      Offset,
+  IN OUT UINTN                                  *NumBytes,
+  OUT UINT8                                     *Buffer
+  )
+{
+
+  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
+  EFI_STATUS                  Status;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+  Status    = FvbReadBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);
+  DEBUG((DEBUG_VERBOSE,
+    "FvbRead: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x, Status:%r\n",
+    Lba, Offset, *NumBytes, Buffer, Status));
+
+  return Status;
+}
+
+/**
+  Check the integrity of firmware volume header in FvBase
+
+  @param[in]  FvBase        A pointer to firmware volume base address.
+
+  @retval     TRUE          The firmware volume is consistent
+  @retval     FALSE         The firmware volume has corrupted.
+
+**/
+BOOLEAN
+IsFvHeaderValid (
+  IN       EFI_PHYSICAL_ADDRESS          FvBase
+  )
+{
+  UINT16                                 Sum;
+  EFI_FIRMWARE_VOLUME_HEADER             *FwVolHeader;
+
+  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) FvBase;
+  if (FvBase == PcdGet32(PcdFlashNvStorageVariableBase)) {
+    if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid, sizeof(EFI_GUID)) != 0 ) {
+      DEBUG((DEBUG_INFO, "  --FileSystemGuid not match: %g\n", &FwVolHeader->FileSystemGuid));
+      return FALSE;
+    }
+  } else {
+    if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem2Guid, sizeof(EFI_GUID)) != 0 ) {
+      DEBUG((DEBUG_INFO, "  --not expected guid.\n"));
+      return FALSE;
+    }
+  }
+
+  if ( (FwVolHeader->Revision != EFI_FVH_REVISION)   ||
+       (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
+       (FwVolHeader->FvLength == ((UINTN) -1))       ||
+       ((FwVolHeader->HeaderLength & 0x01 ) !=0) )  {
+    DEBUG((DEBUG_INFO, "  -- >Revision = 0x%x, Signature = 0x%x\n", FwVolHeader->Revision, FwVolHeader->Signature ));
+    DEBUG((DEBUG_INFO, "  -- >FvLength = 0x%lx, HeaderLength = 0x%x\n", FwVolHeader->FvLength, FwVolHeader->HeaderLength ));
+    return FALSE;
+  }
+
+  Sum = CalculateSum16 ((UINT16 *) FwVolHeader, FwVolHeader->HeaderLength);
+  if (Sum != 0) {
+    DEBUG((DEBUG_INFO, "error: checksum: 0x%04X (expect 0x0)\n", Sum));
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/**
+  Get intial variable data.
+
+  @param[out]  VarData          Valid variable data.
+  @param[out]  VarSize          Valid variable size.
+
+  @retval RETURN_SUCCESS        Successfully found initial variable data.
+  @retval RETURN_NOT_FOUND      Failed to find the variable data file from FV.
+  @retval EFI_INVALID_PARAMETER VarData or VarSize is null.
+
+**/
+EFI_STATUS
+GetInitialVariableData (
+  OUT VOID                       **VarData,
+  OUT UINTN                      *VarSize
+  )
+{
+  EFI_STATUS                     Status;
+  VOID                           *ImageData;
+  UINTN                          ImageSize;
+  EFI_FIRMWARE_VOLUME_HEADER     *FvHeader;
+  VARIABLE_STORE_HEADER          *VariableStore;
+  AUTHENTICATED_VARIABLE_HEADER  *Variable;
+  UINTN                          VariableSize;
+  UINTN                          VarEndAddr;
+
+  if ((VarData == NULL) || (VarSize == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = GetSectionFromAnyFv (PcdGetPtr(PcdNvsDataFile), EFI_SECTION_RAW, 0, &ImageData, &ImageSize);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  FvHeader      = (EFI_FIRMWARE_VOLUME_HEADER *) ImageData;
+  VariableStore = (VARIABLE_STORE_HEADER *) ((UINT8 *)ImageData + FvHeader->HeaderLength);
+  VarEndAddr    = (UINTN)VariableStore + VariableStore->Size;
+  Variable      = (AUTHENTICATED_VARIABLE_HEADER *) HEADER_ALIGN (VariableStore + 1);
+  *VarData      = (VOID *)Variable;
+  while (((UINTN)Variable < VarEndAddr)) {
+    if (Variable->StartId != VARIABLE_DATA) {
+      break;
+    }
+    VariableSize = sizeof (AUTHENTICATED_VARIABLE_HEADER) + Variable->DataSize + Variable->NameSize;
+    Variable     = (AUTHENTICATED_VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) Variable + VariableSize);
+  }
+
+  *VarSize = (UINTN)Variable - HEADER_ALIGN (VariableStore + 1);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  The function does the necessary initialization work for
+  Firmware Volume Block Driver.
+
+  @retval     EFI_SUCCESS       This funtion always return EFI_SUCCESS.
+                                It will ASSERT on errors.
+
+**/
+EFI_STATUS
+FvbInitialize (
+  VOID
+  )
+{
+  EFI_FW_VOL_INSTANCE                   *FwVolInstance;
+  EFI_FIRMWARE_VOLUME_HEADER            *FvHeader;
+  EFI_FV_BLOCK_MAP_ENTRY                *BlockMap;
+  EFI_PHYSICAL_ADDRESS                  BaseAddress;
+  UINTN                                 WriteAddr;
+  EFI_STATUS                            Status;
+  UINTN                                 BufferSize;
+  UINTN                                 Length;
+  VARIABLE_STORE_HEADER                 VariableStore;
+  VOID                                  *VarData;
+
+  InitVariableStore ();
+  BaseAddress = PcdGet32(PcdFlashNvStorageVariableBase);
+  FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress;
+
+  //
+  // Check FV header and variable store header
+  //
+  if (!IsFvHeaderValid (BaseAddress)) {
+    //
+    //  Write back a healthy FV header
+    //
+    DEBUG ((DEBUG_ERROR, "Fvb: Writing back a healthy FV header: 0x%lx\n", BaseAddress));
+    FvHeader = GetFvHeaderTemplate ();
+    LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FvHeader->BlockMap->Length, FALSE);
+
+    Status = LibFvbFlashDeviceBlockErase ((UINTN)BaseAddress, FvHeader->BlockMap->Length);
+    ASSERT_EFI_ERROR(Status);
+
+    Length    = FvHeader->HeaderLength;
+    WriteAddr = (UINTN)BaseAddress;
+    Status = LibFvbFlashDeviceWrite (WriteAddr, &Length, (UINT8 *) FvHeader);
+    WriteAddr += Length;
+    ASSERT_EFI_ERROR(Status);
+
+    //
+    // Write back variable store header
+    //
+    VariableStore.Size   = PcdGet32(PcdFlashNvStorageVariableSize) - FvHeader->HeaderLength;
+    VariableStore.Format = VARIABLE_STORE_FORMATTED;
+    VariableStore.State  = VARIABLE_STORE_HEALTHY;
+    CopyGuid (&VariableStore.Signature, &gEfiAuthenticatedVariableGuid);
+    BufferSize = sizeof (VARIABLE_STORE_HEADER);
+    Status = LibFvbFlashDeviceWrite (WriteAddr, &BufferSize, (UINT8 *) &VariableStore);
+    WriteAddr += BufferSize;
+    ASSERT_EFI_ERROR(Status);
+
+    //
+    // Write initial variable data if found
+    //
+    Status = GetInitialVariableData (&VarData, &Length);
+    if (!EFI_ERROR (Status)) {
+      Status = LibFvbFlashDeviceWrite (WriteAddr, &Length, (UINT8 *) VarData);
+      ASSERT_EFI_ERROR(Status);
+    }
+
+    LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FvHeader->BlockMap->Length, TRUE);
+    WriteBackInvalidateDataCacheRange ((VOID *) (UINTN) BaseAddress, FvHeader->BlockMap->Length);
+  }
+
+  //
+  // Create a new FW volume instance for NVS variable
+  //
+  BufferSize    = FvHeader->HeaderLength + sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER);
+  FwVolInstance = (EFI_FW_VOL_INSTANCE *) AllocateRuntimeZeroPool (BufferSize);
+  if (FwVolInstance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  FwVolInstance->FvBase = (UINTN)BaseAddress;
+  CopyMem (&FwVolInstance->VolumeHeader, FvHeader, FvHeader->HeaderLength);
+
+  //
+  // Process the block map for each FV. Assume it has same block size.
+  //
+  FwVolInstance->NumOfBlocks = 0;
+  FvHeader = &FwVolInstance->VolumeHeader;
+  for (BlockMap = FvHeader->BlockMap; BlockMap->NumBlocks != 0; BlockMap++) {
+    FwVolInstance->NumOfBlocks += BlockMap->NumBlocks;
+  }
+
+  //
+  // Add a FVB Protocol Instance
+  //
+  Status = InstallFvbProtocol (FwVolInstance, mFvbModuleGlobal.NumFv);
+  mFvbModuleGlobal.NumFv++;
+  mFvbModuleGlobal.FvInstance = FwVolInstance;
+
+  return Status;
+}
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbService.h b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
new file mode 100644
index 0000000000..c07562cbfd
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
@@ -0,0 +1,187 @@
+/** @file
+The header file for Firmware volume block driver.
+
+Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef FW_BLOCK_SERVICE_H_
+#define FW_BLOCK_SERVICE_H_
+
+#include <Guid/EventGroup.h>
+#include <Guid/FirmwareFileSystem2.h>
+#include <Guid/SystemNvDataGuid.h>
+#include <Guid/VariableFormat.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/FlashDeviceLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HobLib.h>
+#include <Library/DxeServicesLib.h>
+#include <Guid/NvVariableInfoGuid.h>
+#include <Register/ArchitecturalMsr.h>
+
+//
+// Define two helper macro to extract the Capability field or Status field in FVB
+// bit fields
+//
+#define EFI_FVB2_CAPABILITIES (EFI_FVB2_READ_DISABLED_CAP | \
+                              EFI_FVB2_READ_ENABLED_CAP | \
+                              EFI_FVB2_WRITE_DISABLED_CAP | \
+                              EFI_FVB2_WRITE_ENABLED_CAP | \
+                              EFI_FVB2_LOCK_CAP \
+                              )
+
+#define EFI_FVB2_STATUS (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
+
+
+typedef struct {
+  UINTN                       FvBase;
+  UINTN                       NumOfBlocks;
+  //
+  // Note!!!: VolumeHeader must be the last element
+  // of the structure.
+  //
+  EFI_FIRMWARE_VOLUME_HEADER  VolumeHeader;
+} EFI_FW_VOL_INSTANCE;
+
+
+typedef struct {
+  EFI_FW_VOL_INSTANCE         *FvInstance;
+  UINT32                      NumFv;
+  UINT32                      Flags;
+} FWB_GLOBAL;
+
+//
+// Fvb Protocol instance data
+//
+#define FVB_DEVICE_FROM_THIS(a) CR(a, EFI_FW_VOL_BLOCK_DEVICE, FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
+#define FVB_EXTEND_DEVICE_FROM_THIS(a) CR(a, EFI_FW_VOL_BLOCK_DEVICE, FvbExtension, FVB_DEVICE_SIGNATURE)
+#define FVB_DEVICE_SIGNATURE       SIGNATURE_32('F','V','B','C')
+
+typedef struct {
+  MEDIA_FW_VOL_DEVICE_PATH  FvDevPath;
+  EFI_DEVICE_PATH_PROTOCOL  EndDevPath;
+} FV_PIWG_DEVICE_PATH;
+
+typedef struct {
+  MEMMAP_DEVICE_PATH          MemMapDevPath;
+  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
+} FV_MEMMAP_DEVICE_PATH;
+
+typedef struct {
+  UINT32                                Signature;
+  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
+  UINTN                                 Instance;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
+} EFI_FW_VOL_BLOCK_DEVICE;
+
+/**
+  Get a heathy FV header used for variable store recovery
+
+  @retval     The FV header.
+
+**/
+EFI_FIRMWARE_VOLUME_HEADER *
+GetFvHeaderTemplate (
+  VOID
+  );
+
+EFI_STATUS
+InitVariableStore (
+  VOID
+  );
+
+//
+// Protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  OUT EFI_FVB_ATTRIBUTES_2                      *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                   *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+       OUT EFI_PHYSICAL_ADDRESS                *Address
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN  EFI_LBA                            Lba,
+  OUT UINTN                              *BlockSize,
+  OUT UINTN                              *NumOfBlocks
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN EFI_LBA                              Lba,
+  IN UINTN                                Offset,
+  IN OUT UINTN                            *NumBytes,
+  OUT UINT8                                *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
+  IN EFI_LBA                              Lba,
+  IN UINTN                                Offset,
+  IN OUT UINTN                            *NumBytes,
+  IN UINT8                                *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *This,
+  ...
+  );
+
+EFI_FW_VOL_INSTANCE *
+GetFvbInstance (
+  IN  UINTN                              Instance
+  );
+
+EFI_STATUS
+InstallFvbProtocol (
+  IN  EFI_FW_VOL_INSTANCE               *FwhInstance,
+  IN  UINTN                             InstanceNum
+  );
+
+EFI_STATUS
+FvbInitialize (
+  VOID
+  );
+
+extern FWB_GLOBAL              mFvbModuleGlobal;
+extern EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate;
+extern FV_MEMMAP_DEVICE_PATH   mFvMemmapDevicePathTemplate;
+extern FV_PIWG_DEVICE_PATH     mFvPIWGDevicePathTemplate;
+
+#endif
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c b/UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
new file mode 100644
index 0000000000..0f1f4b369c
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
@@ -0,0 +1,139 @@
+/** @file
+  SMM Firmware Volume Block Driver.
+
+  Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiSmm.h>
+#include <Library/SmmServicesTableLib.h>
+#include "FvbSmmCommon.h"
+#include "FvbService.h"
+
+/**
+  The function installs EFI_SMM_FIRMWARE_VOLUME_BLOCK protocol
+  for each FV in the system.
+
+  @param[in]  FwhInstance   The pointer to a FW volume instance structure,
+                            which contains the information about one FV.
+  @param[in]  InstanceNum   The instance number which can be used as a ID
+                            to locate this FwhInstance in other functions.
+
+  @retval     EFI_SUCESS    Installed successfully.
+  @retval     Else          Did not install successfully.
+
+**/
+EFI_STATUS
+InstallFvbProtocol (
+  IN  EFI_FW_VOL_INSTANCE               *FwhInstance,
+  IN  UINTN                             InstanceNum
+  )
+{
+  EFI_FW_VOL_BLOCK_DEVICE               *FvbDevice;
+  EFI_FIRMWARE_VOLUME_HEADER            *FwVolHeader;
+  EFI_STATUS                            Status;
+  EFI_HANDLE                            FvbHandle;
+  FV_MEMMAP_DEVICE_PATH                 *FvDevicePath;
+  VOID                                  *TempPtr;
+
+  FvbDevice = (EFI_FW_VOL_BLOCK_DEVICE *) AllocateRuntimeCopyPool (
+                                            sizeof (EFI_FW_VOL_BLOCK_DEVICE),
+                                            &mFvbDeviceTemplate
+                                            );
+  if (FvbDevice == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  FvbDevice->Instance = InstanceNum;
+  FwVolHeader         = &FwhInstance->VolumeHeader;
+
+  //
+  // Set up the devicepath
+  //
+  if (FwVolHeader->ExtHeaderOffset == 0) {
+    //
+    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
+    //
+    TempPtr = AllocateRuntimeCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+    if (FvbDevice->DevicePath == NULL) {
+      ASSERT (FALSE);
+      return EFI_OUT_OF_RESOURCES;
+    }
+    FvDevicePath = (FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath;
+    FvDevicePath->MemMapDevPath.StartingAddress = FwhInstance->FvBase;
+    FvDevicePath->MemMapDevPath.EndingAddress   = FwhInstance->FvBase + FwVolHeader->FvLength - 1;
+  } else {
+    TempPtr = AllocateRuntimeCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+    if (FvbDevice->DevicePath == NULL) {
+      ASSERT (FALSE);
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    CopyGuid (
+      &((FV_PIWG_DEVICE_PATH *)FvbDevice->DevicePath)->FvDevPath.FvName,
+      (GUID *)(UINTN)(FwhInstance->FvBase + FwVolHeader->ExtHeaderOffset)
+      );
+  }
+
+  //
+  // Install the SMM Firmware Volume Block Protocol and Device Path Protocol
+  //
+  FvbHandle = NULL;
+  Status = gSmst->SmmInstallProtocolInterface (
+                    &FvbHandle,
+                    &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    &FvbDevice->FwVolBlockInstance
+                    );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gSmst->SmmInstallProtocolInterface (
+                    &FvbHandle,
+                    &gEfiDevicePathProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    FvbDevice->DevicePath
+                    );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Notify the Fvb wrapper driver SMM fvb is ready
+  //
+  FvbHandle = NULL;
+  Status = gBS->InstallProtocolInterface (
+                  &FvbHandle,
+                  &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &FvbDevice->FwVolBlockInstance
+                  );
+
+  return Status;
+}
+
+
+/**
+  The driver entry point for SMM Firmware Volume Block Driver.
+
+  The function does the necessary initialization work
+  Firmware Volume Block Driver.
+
+  @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.
+  @param[in]  SystemTable       A pointer to the EFI system table.
+
+  @retval     EFI_SUCCESS       This funtion always return EFI_SUCCESS.
+                                It will ASSERT on errors.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbSmmInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  FvbInitialize ();
+
+  return EFI_SUCCESS;
+}
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
new file mode 100644
index 0000000000..2a262076d6
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
@@ -0,0 +1,71 @@
+## @file
+# This driver installs the EFI_SMM_FIRMWARE_VOLUMEN_PROTOCOL.
+#
+#
+#  Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FvbSmm
+  FILE_GUID                      = A4EC8ADB-B7A8-47d1-8E52-EC820D0ACF6F
+  MODULE_TYPE                    = DXE_SMM_DRIVER
+  VERSION_STRING                 = 1.0
+  PI_SPECIFICATION_VERSION       = 0x0001000A
+  ENTRY_POINT                    = FvbSmmInitialize
+
+[Sources]
+  FvbInfo.c
+  FvbService.h
+  FvbService.c
+  FvbServiceSmm.c
+  FvbSmmCommon.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  FlashDeviceLib
+  PcdLib
+  MemoryAllocationLib
+  CacheMaintenanceLib
+  IoLib
+  BaseMemoryLib
+  DebugLib
+  BaseLib
+  UefiLib
+  SmmServicesTableLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  HobLib
+  DxeServicesLib
+
+[Guids]
+  gEfiFirmwareFileSystem2Guid                   # ALWAYS_CONSUMED
+  gEfiSystemNvDataFvGuid                        # ALWAYS_CONSUMED
+  gEfiAuthenticatedVariableGuid
+  gNvVariableInfoGuid
+
+  [Protocols]
+  gEfiDevicePathProtocolGuid                    # PROTOCOL ALWAYS_PRODUCED
+  gEfiSmmFirmwareVolumeBlockProtocolGuid        # PROTOCOL ALWAYS_PRODUCED
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+  gUefiPayloadPkgTokenSpaceGuid.PcdNvsDataFile
+
+[Depex]
+  TRUE
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
new file mode 100644
index 0000000000..5886996cd8
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
@@ -0,0 +1,69 @@
+/** @file
+  The common header file for SMM FVB module.
+
+Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMM_FVB_COMMON_H_
+#define SMM_FVB_COMMON_H_
+
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+
+#define EFI_FUNCTION_GET_ATTRIBUTES           1
+#define EFI_FUNCTION_SET_ATTRIBUTES           2
+#define EFI_FUNCTION_GET_PHYSICAL_ADDRESS     3
+#define EFI_FUNCTION_GET_BLOCK_SIZE           4
+#define EFI_FUNCTION_READ                     5
+#define EFI_FUNCTION_WRITE                    6
+#define EFI_FUNCTION_ERASE_BLOCKS             7
+
+typedef struct {
+  UINTN       Function;
+  EFI_STATUS  ReturnStatus;
+  UINT8       Data[1];
+} SMM_FVB_COMMUNICATE_FUNCTION_HEADER;
+
+
+///
+/// Size of SMM communicate header, without including the payload.
+///
+#define SMM_COMMUNICATE_HEADER_SIZE  (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data))
+
+///
+/// Size of SMM FVB communicate function header, without including the payload.
+///
+#define SMM_FVB_COMMUNICATE_HEADER_SIZE  (OFFSET_OF (SMM_FVB_COMMUNICATE_FUNCTION_HEADER, Data))
+
+typedef struct {
+  EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL     *SmmFvb;
+  EFI_FVB_ATTRIBUTES_2                       Attributes;
+} SMM_FVB_ATTRIBUTES_HEADER;
+
+typedef struct {
+  EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL     *SmmFvb;
+  EFI_PHYSICAL_ADDRESS                       Address;
+} SMM_FVB_PHYSICAL_ADDRESS_HEADER;
+
+typedef struct {
+  EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL     *SmmFvb;
+  EFI_LBA                                    Lba;
+  UINTN                                      BlockSize;
+  UINTN                                      NumOfBlocks;
+} SMM_FVB_BLOCK_SIZE_HEADER;
+
+typedef struct {
+  EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL     *SmmFvb;
+  EFI_LBA                                    Lba;
+  UINTN                                      Offset;
+  UINTN                                      NumBytes;
+} SMM_FVB_READ_WRITE_HEADER;
+
+typedef struct {
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL         *SmmFvb;
+  EFI_LBA                                    StartLba;
+  UINTN                                      NumOfLba;
+} SMM_FVB_BLOCKS_HEADER;
+
+#endif
diff --git a/UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h b/UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h
new file mode 100644
index 0000000000..f22e4e6122
--- /dev/null
+++ b/UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h
@@ -0,0 +1,24 @@
+/** @file
+  This file defines the hob structure for the SPI flash variable info.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef NV_VARIABLE_INFO_GUID_H_
+#define NV_VARIABLE_INFO_GUID_H_
+
+//
+// NV variable hob info GUID
+//
+extern EFI_GUID gNvVariableInfoGuid;
+
+typedef struct {
+  UINT8                  Revision;
+  UINT8                  Reserved[3];
+  UINT32                 VariableStoreBase;
+  UINT32                 VariableStoreSize;
+} NV_VARIABLE_INFO;
+
+#endif
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dec b/UefiPayloadPkg/UefiPayloadPkg.dec
index 3ffdce550d..e385dc7219 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dec
+++ b/UefiPayloadPkg/UefiPayloadPkg.dec
@@ -37,6 +37,8 @@
   gUefiSerialPortInfoGuid  = { 0x6c6872fe, 0x56a9, 0x4403, { 0xbb, 0x98, 0x95, 0x8d, 0x62, 0xde, 0x87, 0xf1 } }
   gLoaderMemoryMapInfoGuid = { 0xa1ff7424, 0x7a1a, 0x478e, { 0xa9, 0xe4, 0x92, 0xf3, 0x57, 0xd1, 0x28, 0x32 } }
 
+  # SMM variable support
+  gNvVariableInfoGuid      = { 0x7a345dca, 0xc26, 0x4f2a,  { 0xa8, 0x9a, 0x57, 0xc0, 0x8d, 0xdd, 0x22, 0xee } }
   gSpiFlashInfoGuid        = { 0x2d4aac1b, 0x91a5, 0x4cd5, { 0x9b, 0x5c, 0xb4, 0x0f, 0x5d, 0x28, 0x51, 0xa1 } }
   gSmmRegisterInfoGuid     = { 0xaa9bd7a7, 0xcafb, 0x4499, { 0xa4, 0xa9, 0xb, 0x34, 0x6b, 0x40, 0xa6, 0x22 } }
   gS3CommunicationGuid     = { 0x88e31ba1, 0x1856, 0x4b8b, { 0xbb, 0xdf, 0xf8, 0x16, 0xdd, 0x94, 0xa, 0xef } }
@@ -81,3 +83,7 @@ gUefiPayloadPkgTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|0x80|UINT32|0x
 gUefiPayloadPkgTokenSpaceGuid.PcdSystemMemoryUefiRegionSize|0x02000000|UINT32|0x00000017
 
 gUefiPayloadPkgTokenSpaceGuid.PcdPcdDriverFile|{ 0x57, 0x72, 0xcf, 0x80, 0xab, 0x87, 0xf9, 0x47, 0xa3, 0xfe, 0xD5, 0x0B, 0x76, 0xd8, 0x95, 0x41 }|VOID*|0x00000018
+
+## FFS filename to find the default variable initial data file.
+# @Prompt FFS Name of variable initial data file
+ gUefiPayloadPkgTokenSpaceGuid.PcdNvsDataFile |{ 0x1a, 0xf1, 0xb1, 0xae, 0x42, 0xcc, 0xcf, 0x4e, 0xac, 0x60, 0xdb, 0xab, 0xf6, 0xca, 0x69, 0xe6 }|VOID*|0x00000025
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 7/8] UefiPayloadPkg: Add a SMM dispatch module
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
                   ` (5 preceding siblings ...)
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 6/8] UefiPayloadPkg: Add a common FVB SMM module Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 8/8] UefiPayloadPkg: Add SMM support and SMM variable support Guo Dong
  2021-10-24  6:24 ` [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Benjamin You
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

PCH SMM module would install SMM SW dispatch2 protocol.
And it supports to register SMI handlers based on SMI APM
interrupt from the bootloader information gSmmRegisterInfoGuid.
It is possible to extend bootloader HOB to pass other information
to support more SMI sources.
If this module is not required by bootloader for some reason, the
bootloader could skip this HOB or build a HOB without EOS info.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 .../PchSmiDispatchSmm/PchSmiDispatchSmm.c     | 455 ++++++++++++++++++
 .../PchSmiDispatchSmm/PchSmiDispatchSmm.h     |  37 ++
 .../PchSmiDispatchSmm/PchSmiDispatchSmm.inf   |  51 ++
 3 files changed, 543 insertions(+)
 create mode 100644 UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c
 create mode 100644 UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.h
 create mode 100644 UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf

diff --git a/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c
new file mode 100644
index 0000000000..5e9ae0db7c
--- /dev/null
+++ b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c
@@ -0,0 +1,455 @@
+/** @file
+  SMM SwDispatch2 Protocol.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+
+
+**/
+
+#include "PchSmiDispatchSmm.h"
+
+typedef struct {
+  UINT8   EosBitOffset;
+  UINT8   ApmBitOffset;
+  UINT32  SmiEosAddr;
+  UINT32  SmiApmStsAddr;
+} SMM_PCH_REGISTER;
+
+SMM_PCH_REGISTER       mSmiPchReg;
+
+EFI_SMM_CPU_PROTOCOL   *mSmmCpuProtocol;
+LIST_ENTRY             mSmmSwDispatch2Queue = INITIALIZE_LIST_HEAD_VARIABLE (mSmmSwDispatch2Queue);
+
+/**
+  Find SmmSwDispatch2Context by SwSmiInputValue.
+
+  @param[in] SwSmiInputValue      The value to indentify the SmmSwDispatch2 context
+
+  @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
+**/
+EFI_SMM_SW_DISPATCH2_CONTEXT *
+FindContextBySwSmiInputValue (
+  IN UINTN                              SwSmiInputValue
+  )
+{
+  LIST_ENTRY                            *Node;
+  EFI_SMM_SW_DISPATCH2_CONTEXT          *Dispatch2Context;
+
+  Node = mSmmSwDispatch2Queue.ForwardLink;
+  for (; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
+    Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
+    if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
+      return Dispatch2Context;
+    }
+  }
+  return NULL;
+}
+
+/**
+  Find SmmSwDispatch2Context by DispatchHandle.
+
+  @param DispatchHandle    The handle to indentify the SmmSwDispatch2 context
+
+  @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
+**/
+EFI_SMM_SW_DISPATCH2_CONTEXT *
+FindContextByDispatchHandle (
+  IN EFI_HANDLE                         DispatchHandle
+  )
+{
+  LIST_ENTRY                            *Node;
+  EFI_SMM_SW_DISPATCH2_CONTEXT          *Dispatch2Context;
+
+  Node = mSmmSwDispatch2Queue.ForwardLink;
+  for (; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
+    Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
+    if (Dispatch2Context->DispatchHandle == DispatchHandle) {
+      return Dispatch2Context;
+    }
+  }
+  return NULL;
+}
+
+/**
+  Dispatch registered SMM handlers
+
+  @param  DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
+  @param  RegisterContext Points to an optional handler context which was specified when the handler was registered.
+  @param  CommBuffer      A pointer to a collection of data in memory that will
+                          be conveyed from a non-SMM environment into an SMM environment.
+  @param  CommBufferSize  The size of the CommBuffer.
+
+  @return Status Code
+
+**/
+EFI_STATUS
+SmmSwDispatcher (
+  IN     EFI_HANDLE               DispatchHandle,
+  IN     CONST VOID               *RegisterContext,
+  IN OUT VOID                     *CommBuffer,
+  IN OUT UINTN                    *CommBufferSize
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_SMM_SW_CONTEXT              SwContext;
+  UINTN                           Index;
+  EFI_SMM_SW_DISPATCH2_CONTEXT    *Context;
+  EFI_SMM_HANDLER_ENTRY_POINT2    DispatchFunction;
+  EFI_SMM_SW_REGISTER_CONTEXT     DispatchContext;
+  UINTN                           Size;
+  EFI_SMM_SAVE_STATE_IO_INFO      IoInfo;
+
+  //
+  // Construct new context
+  //
+  SwContext.SwSmiCpuIndex = 0;
+  SwContext.CommandPort   = IoRead8 (SMM_CONTROL_PORT);
+  SwContext.DataPort      = IoRead8 (SMM_DATA_PORT);
+
+  //
+  // Try to find which CPU trigger SWSMI
+  //
+  for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
+    Status = mSmmCpuProtocol->ReadSaveState (
+                                mSmmCpuProtocol,
+                                sizeof(IoInfo),
+                                EFI_SMM_SAVE_STATE_REGISTER_IO,
+                                Index,
+                                &IoInfo
+                                );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+    if (IoInfo.IoPort == SMM_CONTROL_PORT) {
+      //
+      // Great! Find it.
+      //
+      SwContext.SwSmiCpuIndex = Index;
+      DEBUG ((DEBUG_VERBOSE, "CPU index = 0x%x/0x%x\n", Index, gSmst->NumberOfCpus));
+      break;
+    }
+  }
+
+  if (SwContext.CommandPort == 0) {
+    DEBUG ((DEBUG_VERBOSE, "NOT SW SMI\n"));
+    Status = EFI_SUCCESS;
+    goto End;
+  }
+
+  //
+  // Search context
+  //
+  Context = FindContextBySwSmiInputValue (SwContext.CommandPort);
+  if (Context == NULL) {
+    DEBUG ((DEBUG_INFO, "No handler for SMI value 0x%x\n", SwContext.CommandPort));
+    Status = EFI_SUCCESS;
+    goto End;
+  }
+  DEBUG ((DEBUG_VERBOSE, "Prepare to call handler for 0x%x\n", SwContext.CommandPort));
+
+  //
+  // Dispatch
+  //
+  DispatchContext.SwSmiInputValue = SwContext.CommandPort;
+  Size = sizeof(SwContext);
+  DispatchFunction = (EFI_SMM_HANDLER_ENTRY_POINT2)Context->DispatchFunction;
+  Status = DispatchFunction (DispatchHandle, &DispatchContext, &SwContext, &Size);
+
+End:
+  //
+  // Clear SMI APM status
+  //
+  IoOr32 (mSmiPchReg.SmiApmStsAddr, 1 << mSmiPchReg.ApmBitOffset);
+
+
+  //
+  // Set EOS bit
+  //
+  IoOr32 (mSmiPchReg.SmiEosAddr, 1 << mSmiPchReg.EosBitOffset);
+
+  return Status;
+}
+
+
+/**
+Check the SwSmiInputValue is already used
+
+@param[in]  SwSmiInputValue      To indentify the SmmSwDispatch2 context
+
+@retval EFI_SUCCESS              SwSmiInputValue could be used.
+@retval EFI_INVALID_PARAMETER    SwSmiInputValue is already be used.
+
+**/
+EFI_STATUS
+SmiInputValueCheck (
+  IN UINTN                              SwSmiInputValue
+  )
+{
+  LIST_ENTRY                            *Node;
+  EFI_SMM_SW_DISPATCH2_CONTEXT          *Dispatch2Context;
+
+  Node = mSmmSwDispatch2Queue.ForwardLink;
+  for (; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
+    Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
+    if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Register a child SMI source dispatch function for the specified software SMI.
+
+  This service registers a function (DispatchFunction) which will be called when the software
+  SMI source specified by RegContext->SwSmiCpuIndex is detected. On return, DispatchHandle
+  contains a unique handle which may be used later to unregister the function using UnRegister().
+
+  @param[in]  This               Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
+  @param[in]  DispatchFunction   Function to register for handler when the specified software
+                                 SMI is generated.
+  @param[in, out]  RegContext    Pointer to the dispatch function's context.
+                                 The caller fills this context in before calling
+                                 the register function to indicate to the register
+                                 function which Software SMI input value the
+                                 dispatch function should be invoked for.
+  @param[out] DispatchHandle     Handle generated by the dispatcher to track the
+                                 function instance.
+
+  @retval EFI_SUCCESS            The dispatch function has been successfully
+                                 registered and the SMI source has been enabled.
+  @retval EFI_DEVICE_ERROR       The SW driver was unable to enable the SMI source.
+  @retval EFI_INVALID_PARAMETER  RegisterContext is invalid. The SW SMI input value
+                                 is not within valid range.
+  @retval EFI_OUT_OF_RESOURCES   There is not enough memory (system or SMM) to manage this
+                                 child.
+  @retval EFI_OUT_OF_RESOURCES   A unique software SMI value could not be assigned
+                                 for this dispatch.
+**/
+EFI_STATUS
+EFIAPI
+SmmSwDispatch2Register (
+  IN  CONST EFI_SMM_SW_DISPATCH2_PROTOCOL  *This,
+  IN        EFI_SMM_HANDLER_ENTRY_POINT2   DispatchFunction,
+  IN  OUT   EFI_SMM_SW_REGISTER_CONTEXT    *RegContext,
+  OUT       EFI_HANDLE                     *DispatchHandle
+  )
+{
+  EFI_STATUS                                Status;
+  UINTN                                     Index;
+  EFI_SMM_SW_DISPATCH2_CONTEXT             *Context;
+
+  if (RegContext->SwSmiInputValue == (UINTN)-1) {
+    //
+    // If SwSmiInputValue is set to (UINTN) -1 then a unique value
+    // will be assigned and returned in the structure.
+    //
+    Status = EFI_NOT_FOUND;
+    for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
+      Status = SmiInputValueCheck (Index);
+      if (!EFI_ERROR (Status)) {
+        RegContext->SwSmiInputValue = Index;
+        break;
+      }
+    }
+    if (RegContext->SwSmiInputValue == (UINTN)-1) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  if ((RegContext->SwSmiInputValue > MAXIMUM_SWI_VALUE) || (RegContext->SwSmiInputValue == 0)) {
+    DEBUG ((DEBUG_ERROR, "ERROR: SMI value range (1 ~ 0x%x)\n", MAXIMUM_SWI_VALUE));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Register
+  //
+  Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof(*Context), (VOID **)&Context);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  *DispatchHandle  = (EFI_HANDLE )Context;
+  Context->Signature        = SMI_SW_HANDLER_SIGNATURE;
+  Context->SwSmiInputValue  = RegContext->SwSmiInputValue;
+  Context->DispatchFunction = (UINTN)DispatchFunction;
+  Context->DispatchHandle   = *DispatchHandle;
+  InsertTailList (&mSmmSwDispatch2Queue, &Context->Link);
+
+  return Status;
+}
+
+
+/**
+  Unregister a child SMI source dispatch function for the specified software SMI.
+
+  This service removes the handler associated with DispatchHandle so that it will no longer be
+  called in response to a software SMI.
+
+  @param[in] This                Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
+  @param[in] DispatchHandle      Handle of dispatch function to deregister.
+
+  @retval EFI_SUCCESS            The dispatch function has been successfully unregistered.
+  @retval EFI_INVALID_PARAMETER  The DispatchHandle was not valid.
+**/
+EFI_STATUS
+EFIAPI
+SmmSwDispatch2UnRegister (
+  IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL  *This,
+  IN       EFI_HANDLE                     DispatchHandle
+  )
+{
+  EFI_SMM_SW_DISPATCH2_CONTEXT            *Context;
+
+  //
+  // Unregister
+  //
+  Context = FindContextByDispatchHandle (DispatchHandle);
+  ASSERT (Context != NULL);
+  if (Context != NULL) {
+    RemoveEntryList (&Context->Link);
+    gSmst->SmmFreePool (Context);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_SMM_SW_DISPATCH2_PROTOCOL gSmmSwDispatch2 = {
+  SmmSwDispatch2Register,
+  SmmSwDispatch2UnRegister,
+  MAXIMUM_SWI_VALUE
+};
+
+
+/**
+  Get specified SMI register based on given register ID
+
+  @param[in]  SmmRegister  SMI related register array from bootloader
+  @param[in]  Id           The register ID to get.
+
+  @retval NULL             The register is not found or the format is not expected.
+  @return smi register
+
+**/
+PLD_GENERIC_REGISTER *
+GetSmmCtrlRegById (
+  IN PLD_SMM_REGISTERS    *SmmRegister,
+  IN UINT32               Id
+  )
+{
+  UINT32                  Index;
+  PLD_GENERIC_REGISTER    *PldReg;
+
+  PldReg = NULL;
+  for (Index = 0; Index < SmmRegister->Count; Index++) {
+    if (SmmRegister->Registers[Index].Id == Id) {
+      PldReg = &SmmRegister->Registers[Index];
+      break;
+    }
+  }
+
+  if (PldReg == NULL) {
+    DEBUG ((DEBUG_INFO, "Register %d not found.\n", Id));
+    return NULL;
+  }
+
+  //
+  // Checking the register if it is expected.
+  //
+  if ((PldReg->Address.AccessSize       != EFI_ACPI_3_0_DWORD) ||
+      (PldReg->Address.Address          == 0) ||
+      (PldReg->Address.RegisterBitWidth != 1) ||
+      (PldReg->Address.AddressSpaceId   != EFI_ACPI_3_0_SYSTEM_IO) ||
+      (PldReg->Value != 1)) {
+    DEBUG ((DEBUG_INFO, "Unexpected SMM register.\n"));
+    DEBUG ((DEBUG_INFO, "AddressSpaceId= 0x%x\n", PldReg->Address.AddressSpaceId));
+    DEBUG ((DEBUG_INFO, "RegBitWidth   = 0x%x\n", PldReg->Address.RegisterBitWidth));
+    DEBUG ((DEBUG_INFO, "RegBitOffset  = 0x%x\n", PldReg->Address.RegisterBitOffset));
+    DEBUG ((DEBUG_INFO, "AccessSize    = 0x%x\n", PldReg->Address.AccessSize));
+    DEBUG ((DEBUG_INFO, "Address       = 0x%lx\n",PldReg->Address.Address ));
+    return NULL;
+  }
+
+  return PldReg;
+}
+
+
+/**
+  Entry Point for this driver.
+
+  @param[in] ImageHandle  Image handle of this driver.
+  @param[in] SystemTable  A Pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS  The entry point is executed successfully.
+  @retval other        Some error occurred when executing this entry point.
+**/
+EFI_STATUS
+EFIAPI
+PchSmiDispatchEntryPoint (
+  IN EFI_HANDLE            ImageHandle,
+  IN EFI_SYSTEM_TABLE      *SystemTable
+  )
+{
+  EFI_STATUS               Status;
+  EFI_HANDLE               DispatchHandle;
+  EFI_HOB_GUID_TYPE       *GuidHob;
+  PLD_SMM_REGISTERS       *SmmRegister;
+  PLD_GENERIC_REGISTER    *SmiEosReg;
+  PLD_GENERIC_REGISTER    *SmiApmStsReg;
+
+  GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
+  if (GuidHob == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  SmmRegister = (PLD_SMM_REGISTERS *) GET_GUID_HOB_DATA(GuidHob);
+  SmiEosReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_EOS);
+  if (SmiEosReg == NULL) {
+    DEBUG ((DEBUG_ERROR, "SMI EOS reg not found.\n"));
+    return EFI_NOT_FOUND;
+  }
+  mSmiPchReg.SmiEosAddr   = (UINT32)SmiEosReg->Address.Address;
+  mSmiPchReg.EosBitOffset = SmiEosReg->Address.RegisterBitOffset;
+
+  SmiApmStsReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_STS);
+  if (SmiApmStsReg == NULL) {
+    DEBUG ((DEBUG_ERROR, "SMI APM status reg not found.\n"));
+    return EFI_NOT_FOUND;
+  }
+  mSmiPchReg.SmiApmStsAddr = (UINT32)SmiApmStsReg->Address.Address;
+  mSmiPchReg.ApmBitOffset  = SmiApmStsReg->Address.RegisterBitOffset;
+
+  //
+  // Locate PI SMM CPU protocol
+  //
+  Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Register a SMM handler to handle subsequent SW SMIs.
+  //
+  Status = gSmst->SmiHandlerRegister ((EFI_MM_HANDLER_ENTRY_POINT)SmmSwDispatcher, NULL, &DispatchHandle);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Publish PI SMM SwDispatch2 Protocol
+  //
+  ImageHandle = NULL;
+  Status = gSmst->SmmInstallProtocolInterface (
+                    &ImageHandle,
+                    &gEfiSmmSwDispatch2ProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    &gSmmSwDispatch2
+                    );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
diff --git a/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.h b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.h
new file mode 100644
index 0000000000..930f5a2158
--- /dev/null
+++ b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.h
@@ -0,0 +1,37 @@
+/** @file
+  The header file for SMM SwDispatch2 module.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMM_SW_DISPATCH2_H_
+#define SMM_SW_DISPATCH2_H_
+
+#include <PiDxe.h>
+#include <Protocol/SmmSwDispatch2.h>
+#include <Protocol/SmmCpu.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Guid/SmmRegisterInfoGuid.h>
+#include <Library/HobLib.h>
+
+#define SMI_SW_HANDLER_SIGNATURE  SIGNATURE_32('s','s','w','h')
+#define MAXIMUM_SWI_VALUE         0xFF
+#define SMM_CONTROL_PORT          0xB2
+#define SMM_DATA_PORT             0xB3
+
+typedef struct {
+  UINTN                          Signature;
+  LIST_ENTRY                     Link;
+  EFI_HANDLE                     DispatchHandle;
+  UINTN                          SwSmiInputValue;
+  UINTN                          DispatchFunction;
+} EFI_SMM_SW_DISPATCH2_CONTEXT;
+
+#endif
+
diff --git a/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf
new file mode 100644
index 0000000000..96a154e888
--- /dev/null
+++ b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf
@@ -0,0 +1,51 @@
+## @file
+#  PCH SMM SMI Software dispatch module.
+#
+#  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PchSmiDispatchSmm
+  FILE_GUID                      = 60F343E3-2AE2-4AA7-B01E-BF9BD5C04A3B
+  MODULE_TYPE                    = DXE_SMM_DRIVER
+  VERSION_STRING                 = 1.0
+  PI_SPECIFICATION_VERSION       = 0x0001000A
+  ENTRY_POINT                    = PchSmiDispatchEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  PchSmiDispatchSmm.c
+  PchSmiDispatchSmm.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  MemoryAllocationLib
+  DebugLib
+  UefiBootServicesTableLib
+  SmmServicesTableLib
+  BaseLib
+  IoLib
+  HobLib
+
+[Protocols]
+  gEfiSmmCpuProtocolGuid                   # PROTOCOL ALWAYS_CONSUMED
+  gEfiSmmSwDispatch2ProtocolGuid           # PROTOCOL ALWAYS_PRODUCED
+
+[Guids]
+  gSmmRegisterInfoGuid
+
+[Depex]
+  gEfiSmmCpuProtocolGuid
-- 
2.32.0.windows.2


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

* [`edk2-devel][PATCH V3 8/8] UefiPayloadPkg: Add SMM support and SMM variable support
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
                   ` (6 preceding siblings ...)
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 7/8] UefiPayloadPkg: Add a SMM dispatch module Guo Dong
@ 2021-10-22 15:46 ` Guo Dong
  2021-10-24  6:24 ` [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Benjamin You
  8 siblings, 0 replies; 10+ messages in thread
From: Guo Dong @ 2021-10-22 15:46 UTC (permalink / raw)
  To: devel; +Cc: Guo Dong, Ray Ni, Maurice Ma, Benjamin You

From: Guo Dong <guo.dong@intel.com>

Add SMM variable support for universal UEFI payload.
By default they are disabled.

Signed-off-by: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Benjamin You <benjamin.you@intel.com>
---
 UefiPayloadPkg/UefiPayloadPkg.dsc | 101 +++++++++++++++++++++++++++---
 UefiPayloadPkg/UefiPayloadPkg.fdf |  38 ++++++++++-
 2 files changed, 128 insertions(+), 11 deletions(-)

diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc
index 5aaae94eec..0df5f827c9 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkg.dsc
@@ -32,11 +32,12 @@
   DEFINE SIO_BUS_ENABLE               = FALSE
   DEFINE UNIVERSAL_PAYLOAD            = FALSE
   DEFINE SECURITY_STUB_ENABLE         = TRUE
+  DEFINE SMM_SUPPORT                  = FALSE
   #
   # SBL:      UEFI payload for Slim Bootloader
   # COREBOOT: UEFI payload for coreboot
   #
-  DEFINE   BOOTLOADER = SBL
+  DEFINE   BOOTLOADER                 = SBL
 
   #
   # CPU options
@@ -90,7 +91,13 @@
   #
   DEFINE SHELL_TYPE                   = BUILD_SHELL
 
-  DEFINE EMU_VARIABLE_ENABLE   = TRUE
+  #
+  # EMU:      UEFI payload with EMU variable
+  # SPI:      UEFI payload with SPI NV variable support
+  # NONE:     UEFI payload with no variable modules
+  #
+  DEFINE VARIABLE_SUPPORT      = EMU
+
   DEFINE DISABLE_RESET_SYSTEM  = FALSE
   DEFINE NETWORK_DRIVER_ENABLE = FALSE
 
@@ -241,10 +248,18 @@
   LockBoxLib|MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf
   FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
   AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+!if $(VARIABLE_SUPPORT) == "EMU"
   TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+!elseif $(VARIABLE_SUPPORT) == "SPI"
+  PlatformSecureLib|SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.inf
+  TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf
+  S3BootScriptLib|MdePkg/Library/BaseS3BootScriptLibNull/BaseS3BootScriptLibNull.inf
+  MmUnblockMemoryLib|MdePkg/Library/MmUnblockMemoryLib/MmUnblockMemoryLibNull.inf
+!endif
   VarCheckLib|MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
   VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
   VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.SEC]
   HobLib|UefiPayloadPkg/Library/PayloadEntryHobLib/HobLib.inf
@@ -262,7 +277,6 @@
   DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf
 !endif
   CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
-  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.DXE_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
@@ -274,7 +288,6 @@
 !endif
   CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
-  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.DXE_RUNTIME_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
@@ -287,6 +300,37 @@
   MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
   ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
 
+[LibraryClasses.common.SMM_CORE]
+!if $(SMM_SUPPORT) == TRUE
+  PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  SmmServicesTableLib|MdeModulePkg/Library/PiSmmCoreSmmServicesTableLib/PiSmmCoreSmmServicesTableLib.inf
+
+  MemoryAllocationLib|MdeModulePkg/Library/PiSmmCoreMemoryAllocationLib/PiSmmCoreMemoryAllocationLib.inf
+  SmmCorePlatformHookLib|MdeModulePkg/Library/SmmCorePlatformHookLibNull/SmmCorePlatformHookLibNull.inf
+  SmmMemLib|MdePkg/Library/SmmMemLib/SmmMemLib.inf
+  ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf
+!endif
+
+[LibraryClasses.common.DXE_SMM_DRIVER]
+!if $(SMM_SUPPORT) == TRUE
+  PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+
+  MemoryAllocationLib|MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf
+  SmmServicesTableLib|MdePkg/Library/SmmServicesTableLib/SmmServicesTableLib.inf
+  SmmMemLib|MdePkg/Library/SmmMemLib/SmmMemLib.inf
+  MmServicesTableLib|MdePkg/Library/MmServicesTableLib/MmServicesTableLib.inf
+  DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
+  SmmCpuPlatformHookLib|UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf
+  SmmCpuFeaturesLib|UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf
+  CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf
+  ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf
+!endif
+!if $(VARIABLE_SUPPORT) == "SPI"
+  SpiFlashLib|UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf
+  FlashDeviceLib|UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf
+  BaseCryptLib|CryptoPkg/Library/BaseCryptLib/SmmCryptLib.inf
+!endif
+
 ################################################################################
 #
 # Pcd Section - list of all EDK II PCD Entries defined by this Platform.
@@ -300,15 +344,17 @@
   gEfiMdeModulePkgTokenSpaceGuid.PcdInstallAcpiSdtProtocol|TRUE
   gEfiMdeModulePkgTokenSpaceGuid.PcdHiiOsRuntimeSupport|FALSE
   gEfiMdeModulePkgTokenSpaceGuid.PcdPciDegradeResourceForOptionRom|FALSE
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmEnableBspElection|FALSE
 
 [PcdsFixedAtBuild]
   gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x10000
   gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize|0x8000
   gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize|0x10000
-  #
-  # Make VariableRuntimeDxe work at emulated non-volatile variable mode.
-  #
-  gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable|TRUE
+!if $(VARIABLE_SUPPORT) == "EMU"
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable        |TRUE
+!else
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable        |FALSE
+!endif
 
   gEfiMdeModulePkgTokenSpaceGuid.PcdVpdBaseAddress|0x0
   gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseMemory|FALSE
@@ -319,6 +365,7 @@
 !if $(SOURCE_DEBUG_ENABLE)
   gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugLoadImageMethod|0x2
 !endif
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackSize|0x4000
 
 [PcdsPatchableInModule.X64]
   gPcAtChipsetPkgTokenSpaceGuid.PcdRtcIndexRegister|$(RTC_INDEX_REGISTER)
@@ -390,6 +437,14 @@
   gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase|0
   gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase|0
   gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut|3
+!if $(VARIABLE_SUPPORT) == "SPI"
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize  |0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize  |0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase  |0
+!endif
+  # Disable SMM S3 script
+  gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable|FALSE
 
   ## This PCD defines the video horizontal resolution.
   #  This PCD could be set to 0 then video resolution could be at highest resolution.
@@ -579,6 +634,36 @@
 !endif
   UefiPayloadPkg/GraphicsOutputDxe/GraphicsOutputDxe.inf
 
+  #
+  # SMM Support
+  #
+!if $(SMM_SUPPORT) == TRUE
+  UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf
+  UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf
+  UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
+  MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf
+  MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf
+  UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf
+  UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf
+  UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf
+!endif
+
+!if $(VARIABLE_SUPPORT) == "EMU"
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+!elseif $(VARIABLE_SUPPORT) == "SPI"
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+      NULL|MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
+      NULL|MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
+      NULL|MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf
+  }
+
+  UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
+!endif
+
   #------------------------------
   #  Build the shell
   #------------------------------
diff --git a/UefiPayloadPkg/UefiPayloadPkg.fdf b/UefiPayloadPkg/UefiPayloadPkg.fdf
index 72e4c70467..2f5cb17f62 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.fdf
+++ b/UefiPayloadPkg/UefiPayloadPkg.fdf
@@ -114,11 +114,29 @@ INF MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe
 !if $(DISABLE_RESET_SYSTEM) == FALSE
 INF MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf
 !endif
-
 INF PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
 
-!if $(EMU_VARIABLE_ENABLE) == TRUE
-INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+#
+# SMM Support
+#
+!if $(SMM_SUPPORT) == TRUE
+  INF UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf
+  INF UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf
+  INF UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
+  INF MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf
+  INF MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf
+  INF UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf
+  INF UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf
+  INF UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf
+!endif
+
+!if $(VARIABLE_SUPPORT) == "EMU"
+  INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+!elseif $(VARIABLE_SUPPORT) == "SPI"
+  INF UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
+  INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
+  INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
+  INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
 !endif
 
 INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf
@@ -271,6 +289,20 @@ INF ShellPkg/Application/Shell/Shell.inf
     UI       STRING="$(MODULE_NAME)" Optional
     VERSION  STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
   }
+[Rule.Common.DXE_SMM_DRIVER]
+  FILE SMM = $(NAMED_GUID) {
+    SMM_DEPEX SMM_DEPEX Optional       $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32      PE32                     $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+[Rule.Common.SMM_CORE]
+  FILE SMM_CORE = $(NAMED_GUID) {
+    PE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+    VERSION  STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
 
 [Rule.Common.UEFI_DRIVER]
   FILE DRIVER = $(NAMED_GUID) {
-- 
2.32.0.windows.2


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

* Re: [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload
  2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
                   ` (7 preceding siblings ...)
  2021-10-22 15:46 ` [`edk2-devel][PATCH V3 8/8] UefiPayloadPkg: Add SMM support and SMM variable support Guo Dong
@ 2021-10-24  6:24 ` Benjamin You
  8 siblings, 0 replies; 10+ messages in thread
From: Benjamin You @ 2021-10-24  6:24 UTC (permalink / raw)
  To: Dong, Guo, devel@edk2.groups.io; +Cc: Ni, Ray, Ma, Maurice

Reviewed-by: Benjamin You <benjamin.you@intel.com>

> -----Original Message-----
> From: Dong, Guo <guo.dong@intel.com>
> Sent: Friday, October 22, 2021 11:46 PM
> To: devel@edk2.groups.io
> Cc: Dong, Guo <guo.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; Ma,
> Maurice <maurice.ma@intel.com>; You, Benjamin <benjamin.you@intel.com>
> Subject: [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI
> payload
> 
> From: Guo Dong <guo.dong@intel.com>
> 
> V3: Add SMM communication region EFI_ALLOCATED check
>     in UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
> V2: Added SMM communication region size check
>     Fixed ECC reported issues and other minor update.
> 
> https://bugzilla.tianocore.org/show_bug.cgi?id=3084
> 
> Currently UEFI payload uses emulated variable driver. So it could
> not support secureboot and measured boot since both need NV variable
> support.
> 
> EDKII already has SMM modules and variable modules. And modern Intel
> platform supports SPI flash hardware sequence to operate flash. So it
> is possible to have a common SPI module for Intel platforms.
> 
> This patch enhances UEFI payload to support SMM variable with a
> common SPI library for Intel platforms. To avoid impact existing
> usage, all the new modules are included under SMM_ENABLE and
> VARIABLE_SUPPORT and by default SMM variable is not be enabled.
> 
> SMM variable could be enabled only when UNIVERSAL_PAYLOAD is set
> since non-universal payload need update ParseLib to provide SMM
> variable related infromation which is not in the plan.
> 
> Signed-off-by: Guo Dong <guo.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Maurice Ma <maurice.ma@intel.com>
> Cc: Benjamin You <benjamin.you@intel.com>
> Reviewed-by: Ray Ni <ray.ni@intel.com>
> Reviewed-by: Benjamin You <benjamin.you@intel.com
> 
> Guo Dong (8):
>   UefiPayloadPkg: Add a common SmmAccessDxe module
>   UefiPayloadPkg: Add a common SMM control Runtime DXE module
>   UefiPayloadPkg: Add bootloader SMM support module
>   UefiPayloadPkg: Add SpiFlashLib
>   UefiPayloadPkg: Add FlashDeviceLib
>   UefiPayloadPkg: Add a common FVB SMM module
>   UefiPayloadPkg: Add a SMM dispatch module
>   UefiPayloadPkg: Add SMM support and SMM variable support
> 
>  UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c    |  431 +++++++
>  UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h    |   41 +
>  UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf  |   49 +
>  UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c        |  151 +++
>  UefiPayloadPkg/FvbRuntimeDxe/FvbService.c     | 1088 +++++++++++++++++
>  UefiPayloadPkg/FvbRuntimeDxe/FvbService.h     |  187 +++
>  UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c  |  139 +++
>  UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf       |   71 ++
>  UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h   |   69 ++
>  .../Include/Guid/NvVariableInfoGuid.h         |   24 +
>  .../Include/Guid/SmmRegisterInfoGuid.h        |   48 +
>  .../Include/Guid/SmmS3CommunicationInfoGuid.h |   54 +
>  .../Include/Guid/SpiFlashInfoGuid.h           |   38 +
>  .../Include/Library/FlashDeviceLib.h          |  108 ++
>  UefiPayloadPkg/Include/Library/SpiFlashLib.h  |  215 ++++
>  .../Library/FlashDeviceLib/FlashDeviceLib.c   |  165 +++
>  .../Library/FlashDeviceLib/FlashDeviceLib.inf |   38 +
>  UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c   |  173 +++
>  UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h  |  129 ++
>  .../Library/SpiFlashLib/SpiCommon.h           |  208 ++++
>  .../Library/SpiFlashLib/SpiFlashLib.c         |  857 +++++++++++++
>  .../Library/SpiFlashLib/SpiFlashLib.inf       |   48 +
>  .../PchSmiDispatchSmm/PchSmiDispatchSmm.c     |  455 +++++++
>  .../PchSmiDispatchSmm/PchSmiDispatchSmm.h     |   37 +
>  .../PchSmiDispatchSmm/PchSmiDispatchSmm.inf   |   51 +
>  UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c    |  254 ++++
>  UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h    |   37 +
>  UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf  |   51 +
>  .../SmmControlRuntimeDxe.c                    |  256 ++++
>  .../SmmControlRuntimeDxe.inf                  |   50 +
>  UefiPayloadPkg/UefiPayloadPkg.dec             |   10 +
>  UefiPayloadPkg/UefiPayloadPkg.dsc             |  101 +-
>  UefiPayloadPkg/UefiPayloadPkg.fdf             |   38 +-
>  33 files changed, 5660 insertions(+), 11 deletions(-)
>  create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
>  create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
>  create mode 100644 UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
>  create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
>  create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
>  create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
>  create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
>  create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
>  create mode 100644 UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
>  create mode 100644 UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h
>  create mode 100644 UefiPayloadPkg/Include/Guid/SmmRegisterInfoGuid.h
>  create mode 100644
> UefiPayloadPkg/Include/Guid/SmmS3CommunicationInfoGuid.h
>  create mode 100644 UefiPayloadPkg/Include/Guid/SpiFlashInfoGuid.h
>  create mode 100644 UefiPayloadPkg/Include/Library/FlashDeviceLib.h
>  create mode 100644 UefiPayloadPkg/Include/Library/SpiFlashLib.h
>  create mode 100644 UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.c
>  create mode 100644 UefiPayloadPkg/Library/FlashDeviceLib/FlashDeviceLib.inf
>  create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/PchSpi.c
>  create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/RegsSpi.h
>  create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiCommon.h
>  create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.c
>  create mode 100644 UefiPayloadPkg/Library/SpiFlashLib/SpiFlashLib.inf
>  create mode 100644
> UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c
>  create mode 100644
> UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.h
>  create mode 100644
> UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.inf
>  create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.c
>  create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.h
>  create mode 100644 UefiPayloadPkg/SmmAccessDxe/SmmAccessDxe.inf
>  create mode 100644
> UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.c
>  create mode 100644
> UefiPayloadPkg/SmmControlRuntimeDxe/SmmControlRuntimeDxe.inf
> 
> --
> 2.32.0.windows.2


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

end of thread, other threads:[~2021-10-24  6:24 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-10-22 15:46 [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 1/8] UefiPayloadPkg: Add a common SmmAccessDxe module Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 2/8] UefiPayloadPkg: Add a common SMM control Runtime DXE module Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 3/8] UefiPayloadPkg: Add bootloader SMM support module Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 4/8] UefiPayloadPkg: Add SpiFlashLib Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 5/8] UefiPayloadPkg: Add FlashDeviceLib Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 6/8] UefiPayloadPkg: Add a common FVB SMM module Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 7/8] UefiPayloadPkg: Add a SMM dispatch module Guo Dong
2021-10-22 15:46 ` [`edk2-devel][PATCH V3 8/8] UefiPayloadPkg: Add SMM support and SMM variable support Guo Dong
2021-10-24  6:24 ` [`edk2-devel][PATCH V3 0/8] Add SMM variable support for UEFI payload Benjamin You

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