[edk2-devel] [PATCH 4/5] UefiPayloadPkg: Add SmmStoreLib

Sean Rhodes sean at starlabs.systems
Mon Mar 21 09:10:12 UTC 2022


From: Patrick Rudolph <patrick.rudolph at 9elements.com>

Implement all of the FVB protocol functions on top of the SmmStore
as a library. The library consumes the introduced gEfiSmmStoreInfoHobGuid.

The SMI handler uses a fixed communication buffer in reserved DRAM.
To initiate a transaction you must write to the I/O APM_CNT port.

Tests on Intel(R) Xeon(R) E-2288G CPU @ 3.70G showed that the SMI isn't
triggered with a probability of 1:40 of all cases when called in a tight
loop. The CPU continues running and the SMI is triggeres asynchronously
a few clock cycles later. coreboot only handels synchronous APM request
and does nothing on asynchronous APM triggers.

As there's no livesign from SMM it's impossible to tell if the handler
has run. Just wait a bit and try again to trigger a synchronous SMI.

Tests confirmed that out of 5 million tries the SMI is now always handled.

When a synchronous SMI happens with the correct write to the APM_CNT port,
the ebx register is checked first that it doesn't point to SMRAM.
If it doesn't it's used to read in the arguments that define a SmmStore
transaction.

The SMI handler will only operate on a predefined and memory mapped region in
the boot media.

Cc: Guo Dong <guo.dong at intel.com>
Cc: Ray Ni <ray.ni at intel.com>
Cc: Maurice Ma <maurice.ma at intel.com>
Cc: Benjamin You <benjamin.you at intel.com>
Cc: Sean Rhodes <sean at starlabs.systems>
Signed-off-by: Patrick Rudolph <patrick.rudolph at 9elements.com>
---
 UefiPayloadPkg/Include/Library/SmmStoreLib.h  | 120 +++++
 UefiPayloadPkg/Library/SmmStoreLib/SmmStore.c | 473 ++++++++++++++++++
 UefiPayloadPkg/Library/SmmStoreLib/SmmStore.h |  81 +++
 .../Library/SmmStoreLib/SmmStoreLib.inf       |  41 ++
 .../Library/SmmStoreLib/X64/SmmStore.nasm     |  48 ++
 UefiPayloadPkg/UefiPayloadPkg.dsc             |   1 +
 6 files changed, 764 insertions(+)
 create mode 100644 UefiPayloadPkg/Include/Library/SmmStoreLib.h
 create mode 100644 UefiPayloadPkg/Library/SmmStoreLib/SmmStore.c
 create mode 100644 UefiPayloadPkg/Library/SmmStoreLib/SmmStore.h
 create mode 100644 UefiPayloadPkg/Library/SmmStoreLib/SmmStoreLib.inf
 create mode 100644 UefiPayloadPkg/Library/SmmStoreLib/X64/SmmStore.nasm

diff --git a/UefiPayloadPkg/Include/Library/SmmStoreLib.h b/UefiPayloadPkg/Include/Library/SmmStoreLib.h
new file mode 100644
index 0000000000..6ade375b1d
--- /dev/null
+++ b/UefiPayloadPkg/Include/Library/SmmStoreLib.h
@@ -0,0 +1,120 @@
+/** @file  SmmStoreLib.h
+
+  Copyright (c) 2022, 9elements GmbH<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMM_STORE_LIB_H_
+#define SMM_STORE_LIB_H_
+
+#include <Base.h>
+#include <Uefi/UefiBaseType.h>
+#include <Guid/SmmStoreInfoGuid.h>
+
+#define SMMSTORE_COMBUF_SIZE  16
+
+/**
+  Get the SmmStore block size
+
+  @param BlockSize    The pointer to store the block size in.
+
+**/
+EFI_STATUS
+SmmStoreLibGetBlockSize (
+  OUT UINTN  *BlockSize
+  );
+
+/**
+  Get the SmmStore number of blocks
+
+  @param NumBlocks    The pointer to store the number of blocks in.
+
+**/
+EFI_STATUS
+SmmStoreLibGetNumBlocks (
+  OUT UINTN  *NumBlocks
+  );
+
+/**
+  Get the SmmStore MMIO address
+
+  @param MmioAddress    The pointer to store the address in.
+
+**/
+EFI_STATUS
+SmmStoreLibGetMmioAddress (
+  OUT EFI_PHYSICAL_ADDRESS  *MmioAddress
+  );
+
+/**
+  Read from SmmStore
+
+  @param[in] Lba      The starting logical block index to read from.
+  @param[in] Offset   Offset into the block at which to begin reading.
+  @param[in] NumBytes On input, indicates the requested read size. On
+                      output, indicates the actual number of bytes read
+  @param[in] Buffer   Pointer to the buffer to read into.
+
+**/
+EFI_STATUS
+SmmStoreLibRead (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  );
+
+/**
+  Write to SmmStore
+
+  @param[in] Lba      The starting logical block index to write to.
+  @param[in] Offset   Offset into the block at which to begin writing.
+  @param[in] NumBytes On input, indicates the requested write size. On
+                      output, indicates the actual number of bytes written
+  @param[in] Buffer   Pointer to the data to write.
+
+**/
+EFI_STATUS
+SmmStoreLibWrite (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  );
+
+/**
+  Erase a block using the SmmStore
+
+  @param Lba    The logical block index to erase.
+
+**/
+EFI_STATUS
+SmmStoreLibEraseBlock (
+  IN         EFI_LBA  Lba
+  );
+
+/**
+  Initializes SmmStore support
+
+  @retval EFI_WRITE_PROTECTED   The SmmStore is not present.
+  @retval EFI_UNSUPPORTED       The SmmStoreInfo HOB wasn't found.
+  @retval EFI_SUCCESS           The SmmStore is supported.
+
+**/
+EFI_STATUS
+SmmStoreLibInitialize (
+  VOID
+  );
+
+/**
+  Denitializes SmmStore support
+**/
+VOID
+EFIAPI
+SmmStoreLibDeinitialize (
+  VOID
+  );
+
+#endif /* SMM_STORE_LIB_H_ */
diff --git a/UefiPayloadPkg/Library/SmmStoreLib/SmmStore.c b/UefiPayloadPkg/Library/SmmStoreLib/SmmStore.c
new file mode 100644
index 0000000000..25426a4d1a
--- /dev/null
+++ b/UefiPayloadPkg/Library/SmmStoreLib/SmmStore.c
@@ -0,0 +1,473 @@
+/** @file  SmmStore.c
+
+  Copyright (c) 2022, 9elements GmbH<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <PiDxe.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/SmmStoreLib.h>
+#include "SmmStore.h"
+
+/*
+ * A memory buffer to place arguments in.
+ */
+STATIC SMM_STORE_COM_BUF     *mArgComBuf;
+STATIC EFI_PHYSICAL_ADDRESS  mArgComBufPhys;
+
+/*
+ * Metadata provided by the first stage bootloader.
+ */
+STATIC SMMSTORE_INFO  *mSmmStoreInfo;
+
+STATIC EFI_EVENT  mSmmStoreLibVirtualAddrChangeEvent;
+
+/**
+  Calls into SMM to use the SMMSTOREv2 implementation for persistent storage.
+
+  @param Cmd     The command to write into the APM port. This allows to enter the
+                 Smi special command handler.
+  @param SubCmd  The subcommand to execute in the Smi handler.
+  @param Arg     Optional argument to pass to the Smi handler. Typically a pointer
+                 in 'flat' memory mode, which points to read only memory.
+
+  @retval EFI_NO_RESPONSE       The SmmStore is not present or didn't response.
+  @retval EFI_UNSUPPORTED       The request isn't suppored.
+  @retval EFI_DEVICE_ERROR      An error occured while executing the request.
+  @retval EFI_SUCCESS           The operation was executed successfully.
+**/
+STATIC
+EFI_STATUS
+CallSmm (
+  UINT8  Cmd,
+  UINT8  SubCmd,
+  UINTN  Arg
+  )
+{
+  CONST UINTN  Rax = ((SubCmd << 8) | Cmd);
+  CONST UINTN  Rbx = Arg;
+  UINTN        Result;
+
+  Result = TriggerSmi (Rax, Rbx, 5);
+  if (Result == Rax) {
+    return EFI_NO_RESPONSE;
+  } else if (Result == SMMSTORE_RET_SUCCESS) {
+    return EFI_SUCCESS;
+  } else if (Result == SMMSTORE_RET_UNSUPPORTED) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_DEVICE_ERROR;
+}
+
+/**
+  Get the SmmStore block size
+
+  @param BlockSize    The pointer to store the block size in.
+
+**/
+EFI_STATUS
+SmmStoreLibGetBlockSize (
+  OUT UINTN  *BlockSize
+  )
+{
+  if (mSmmStoreInfo == NULL) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (BlockSize == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *BlockSize = mSmmStoreInfo->BlockSize;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Get the SmmStore number of blocks
+
+  @param NumBlocks    The pointer to store the number of blocks in.
+
+**/
+EFI_STATUS
+SmmStoreLibGetNumBlocks (
+  OUT UINTN  *NumBlocks
+  )
+{
+  if (mSmmStoreInfo == NULL) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (NumBlocks == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *NumBlocks = mSmmStoreInfo->NumBlocks;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Get the SmmStore MMIO address
+
+  @param MmioAddress    The pointer to store the address in.
+
+**/
+EFI_STATUS
+SmmStoreLibGetMmioAddress (
+  OUT EFI_PHYSICAL_ADDRESS  *MmioAddress
+  )
+{
+  if (mSmmStoreInfo == NULL) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (MmioAddress == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *MmioAddress = mSmmStoreInfo->MmioAddress;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Read from SmmStore
+
+  @param[in] Lba      The starting logical block index to read from.
+  @param[in] Offset   Offset into the block at which to begin reading.
+  @param[in] NumBytes On input, indicates the requested read size. On
+                      output, indicates the actual number of bytes read
+  @param[in] Buffer   Pointer to the buffer to read into.
+
+**/
+EFI_STATUS
+SmmStoreLibRead (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  )
+{
+  EFI_STATUS  Status;
+
+  if (mSmmStoreInfo == NULL) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (Lba >= mSmmStoreInfo->NumBlocks) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((*NumBytes + Offset) > mSmmStoreInfo->BlockSize) ||
+      ((*NumBytes + Offset) > mSmmStoreInfo->ComBufferSize))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  mArgComBuf->Read.BufSize   = *NumBytes;
+  mArgComBuf->Read.BufOffset = Offset;
+  mArgComBuf->Read.BlockId   = Lba;
+
+  Status = CallSmm (mSmmStoreInfo->ApmCmd, SMMSTORE_CMD_RAW_READ, mArgComBufPhys);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  CopyMem (Buffer, (VOID *)(UINTN)(mSmmStoreInfo->ComBuffer + Offset), *NumBytes);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Write to SmmStore
+
+  @param[in] Lba      The starting logical block index to write to.
+  @param[in] Offset   Offset into the block at which to begin writing.
+  @param[in] NumBytes On input, indicates the requested write size. On
+                      output, indicates the actual number of bytes written
+  @param[in] Buffer   Pointer to the data to write.
+
+**/
+EFI_STATUS
+SmmStoreLibWrite (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  )
+{
+  if (mSmmStoreInfo == NULL) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (Lba >= mSmmStoreInfo->NumBlocks) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((*NumBytes + Offset) > mSmmStoreInfo->BlockSize) ||
+      ((*NumBytes + Offset) > mSmmStoreInfo->ComBufferSize))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  mArgComBuf->Write.BufSize   = *NumBytes;
+  mArgComBuf->Write.BufOffset = Offset;
+  mArgComBuf->Write.BlockId   = Lba;
+
+  CopyMem ((VOID *)(UINTN)(mSmmStoreInfo->ComBuffer + Offset), Buffer, *NumBytes);
+
+  return CallSmm (mSmmStoreInfo->ApmCmd, SMMSTORE_CMD_RAW_WRITE, mArgComBufPhys);
+}
+
+/**
+  Erase a SmmStore block
+
+  @param Lba    The logical block index to erase.
+
+**/
+EFI_STATUS
+SmmStoreLibEraseBlock (
+  IN   EFI_LBA  Lba
+  )
+{
+  if (mSmmStoreInfo == NULL) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (Lba >= mSmmStoreInfo->NumBlocks) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  mArgComBuf->Clear.BlockId = Lba;
+
+  return CallSmm (mSmmStoreInfo->ApmCmd, SMMSTORE_CMD_RAW_CLEAR, mArgComBufPhys);
+}
+
+/**
+  Fixup internal data so that EFI can be call in virtual mode.
+  Call the passed in Child Notify event and convert any pointers in
+  lib to virtual mode.
+
+  @param[in]    Event   The Event that is being processed
+  @param[in]    Context Event Context
+**/
+STATIC
+VOID
+EFIAPI
+SmmStoreLibVirtualNotifyEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EfiConvertPointer (0x0, (VOID **)&mArgComBuf);
+  if (mSmmStoreInfo != NULL) {
+    EfiConvertPointer (0x0, (VOID **)&mSmmStoreInfo->ComBuffer);
+    EfiConvertPointer (0x0, (VOID **)&mSmmStoreInfo);
+  }
+
+  return;
+}
+
+/**
+  Initializes SmmStore support
+
+  @retval EFI_WRITE_PROTECTED   The SmmStore is not present.
+  @retval EFI_OUT_OF_RESOURCES  Run out of memory.
+  @retval EFI_SUCCESS           The SmmStore is supported.
+
+**/
+EFI_STATUS
+SmmStoreLibInitialize (
+  VOID
+  )
+{
+  EFI_STATUS                       Status;
+  VOID                             *GuidHob;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  GcdDescriptor;
+
+  //
+  // Find the SmmStore information guid hob
+  //
+  GuidHob = GetFirstGuidHob (&gEfiSmmStoreInfoHobGuid);
+  if (GuidHob == NULL) {
+    DEBUG ((DEBUG_WARN, "SmmStore not supported! Skipping driver init.\n"));
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Place SmmStore information hob in a runtime buffer
+  //
+  mSmmStoreInfo = AllocateRuntimePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
+  if (mSmmStoreInfo == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CopyMem (mSmmStoreInfo, GET_GUID_HOB_DATA (GuidHob), GET_GUID_HOB_DATA_SIZE (GuidHob));
+
+  //
+  // Validate input
+  //
+  if ((mSmmStoreInfo->MmioAddress == 0) ||
+      (mSmmStoreInfo->ComBuffer == 0) ||
+      (mSmmStoreInfo->BlockSize == 0) ||
+      (mSmmStoreInfo->NumBlocks == 0))
+  {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid data in SmmStore Info hob\n", __FUNCTION__));
+    FreePool (mSmmStoreInfo);
+    mSmmStoreInfo = NULL;
+    return EFI_WRITE_PROTECTED;
+  }
+
+  //
+  // Allocate Communication Buffer for arguments to pass to SMM.
+  // The argument com buffer is only read by SMM, but never written.
+  // The FVB data send/retrieved will be placed in a separate bootloader
+  // pre-allocated memory region, the ComBuffer.
+  //
+  if (mSmmStoreInfo->ComBuffer < BASE_4GB) {
+    //
+    // Assume that SMM handler is running in 32-bit mode when ComBuffer is
+    // is placed below BASE_4GB.
+    //
+    mArgComBufPhys = BASE_4GB - 1;
+  } else {
+    mArgComBufPhys = BASE_8EB - 1;
+  }
+
+  Status = gBS->AllocatePages (
+                  AllocateMaxAddress,
+                  EfiRuntimeServicesData,
+                  EFI_SIZE_TO_PAGES (sizeof (SMM_STORE_COM_BUF)),
+                  &mArgComBufPhys
+                  );
+
+  if (EFI_ERROR (Status)) {
+    FreePool (mSmmStoreInfo);
+    mSmmStoreInfo = NULL;
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  mArgComBuf = (VOID *)mArgComBufPhys;
+
+  //
+  // Register for the virtual address change event
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  SmmStoreLibVirtualNotifyEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mSmmStoreLibVirtualAddrChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Finally mark the SMM communication buffer provided by CB or SBL as runtime memory
+  //
+  Status = gDS->GetMemorySpaceDescriptor (mSmmStoreInfo->ComBuffer, &GcdDescriptor);
+  if (EFI_ERROR (Status) || (GcdDescriptor.GcdMemoryType != EfiGcdMemoryTypeReserved)) {
+    DEBUG (
+      (
+       DEBUG_INFO,
+       "%a: No memory space descriptor for com buffer found\n",
+       __FUNCTION__
+      )
+      );
+
+    //
+    // Add a new entry if not covered by existing mapping
+    //
+    Status = gDS->AddMemorySpace (
+                    EfiGcdMemoryTypeReserved,
+                    mSmmStoreInfo->ComBuffer,
+                    mSmmStoreInfo->ComBufferSize,
+                    EFI_MEMORY_WB | EFI_MEMORY_RUNTIME
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  //
+  // Mark as runtime service
+  //
+  Status = gDS->SetMemorySpaceAttributes (
+                  mSmmStoreInfo->ComBuffer,
+                  mSmmStoreInfo->ComBufferSize,
+                  EFI_MEMORY_RUNTIME
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Mark the memory mapped store as MMIO memory
+  //
+  Status = gDS->GetMemorySpaceDescriptor (mSmmStoreInfo->MmioAddress, &GcdDescriptor);
+  if (EFI_ERROR (Status) || (GcdDescriptor.GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo)) {
+    DEBUG (
+      (
+       DEBUG_INFO,
+       "%a: No memory space descriptor for com buffer found\n",
+       __FUNCTION__
+      )
+      );
+
+    //
+    // Add a new entry if not covered by existing mapping
+    //
+    Status = gDS->AddMemorySpace (
+                    EfiGcdMemoryTypeMemoryMappedIo,
+                    mSmmStoreInfo->MmioAddress,
+                    mSmmStoreInfo->NumBlocks * mSmmStoreInfo->BlockSize,
+                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  //
+  // Mark as runtime service
+  //
+  Status = gDS->SetMemorySpaceAttributes (
+                  mSmmStoreInfo->MmioAddress,
+                  mSmmStoreInfo->NumBlocks * mSmmStoreInfo->BlockSize,
+                  EFI_MEMORY_RUNTIME
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Denitializes SmmStore support by freeing allocated memory and unregistering
+  the virtual address change event.
+**/
+VOID
+EFIAPI
+SmmStoreLibDeinitialize (
+  VOID
+  )
+{
+  if (mArgComBuf != NULL) {
+    gBS->FreePages (mArgComBufPhys, EFI_SIZE_TO_PAGES (sizeof (SMM_STORE_COM_BUF)));
+    mArgComBuf = NULL;
+  }
+
+  if (mSmmStoreInfo != NULL) {
+    FreePool (mSmmStoreInfo);
+    mSmmStoreInfo = NULL;
+  }
+
+  if (mSmmStoreLibVirtualAddrChangeEvent != NULL) {
+    gBS->CloseEvent (mSmmStoreLibVirtualAddrChangeEvent);
+    mSmmStoreLibVirtualAddrChangeEvent = NULL;
+  }
+}
diff --git a/UefiPayloadPkg/Library/SmmStoreLib/SmmStore.h b/UefiPayloadPkg/Library/SmmStoreLib/SmmStore.h
new file mode 100644
index 0000000000..619b00fa30
--- /dev/null
+++ b/UefiPayloadPkg/Library/SmmStoreLib/SmmStore.h
@@ -0,0 +1,81 @@
+/** @file  SmmStore.h
+
+  Copyright (c) 2022, 9elements GmbH<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef COREBOOT_SMMSTORE_H_
+#define COREBOOT_SMMSTORE_H_
+
+#define SMMSTORE_RET_SUCCESS      0
+#define SMMSTORE_RET_FAILURE      1
+#define SMMSTORE_RET_UNSUPPORTED  2
+
+/* Version 2 only */
+#define SMMSTORE_CMD_INIT       4
+#define SMMSTORE_CMD_RAW_READ   5
+#define SMMSTORE_CMD_RAW_WRITE  6
+#define SMMSTORE_CMD_RAW_CLEAR  7
+
+/*
+ * This allows the payload to store raw data in the flash regions.
+ * This can be used by a FaultTolerantWrite implementation, that uses at least
+ * two regions in an A/B update scheme.
+ */
+
+#pragma pack(1)
+
+/*
+ * Reads a chunk of raw data with size BufSize from the block specified by
+ * block_id starting at BufOffset.
+ * The read data is placed in buf.
+ *
+ * block_id must be less than num_blocks
+ * BufOffset + BufSize must be less than block_size
+ */
+typedef struct {
+  UINT32    BufSize;
+  UINT32    BufOffset;
+  UINT32    BlockId;
+} SMM_STORE_PARAMS_WRITE;
+
+/*
+ * Writes a chunk of raw data with size BufSize to the block specified by
+ * block_id starting at BufOffset.
+ *
+ * block_id must be less than num_blocks
+ * BufOffset + BufSize must be less than block_size
+ */
+typedef struct {
+  UINT32    BufSize;
+  UINT32    BufOffset;
+  UINT32    BlockId;
+} SMM_STORE_PARAMS_READ;
+
+/*
+ * Erases the specified block.
+ *
+ * block_id must be less than num_blocks
+ */
+typedef struct {
+  UINT32    BlockId;
+} SMM_STORE_PARAMS_CLEAR;
+
+typedef union {
+  SMM_STORE_PARAMS_WRITE    Write;
+  SMM_STORE_PARAMS_READ     Read;
+  SMM_STORE_PARAMS_CLEAR    Clear;
+} SMM_STORE_COM_BUF;
+#pragma pack(0)
+
+UINTN
+EFIAPI
+TriggerSmi (
+  IN UINTN  Cmd,
+  IN UINTN  Arg,
+  IN UINTN  Retry
+  );
+
+#endif // COREBOOT_SMMSTORE_H_
diff --git a/UefiPayloadPkg/Library/SmmStoreLib/SmmStoreLib.inf b/UefiPayloadPkg/Library/SmmStoreLib/SmmStoreLib.inf
new file mode 100644
index 0000000000..13a520f358
--- /dev/null
+++ b/UefiPayloadPkg/Library/SmmStoreLib/SmmStoreLib.inf
@@ -0,0 +1,41 @@
+## @file
+#  SmmStore library for coreboot
+#
+#  Copyright (c) 2022 9elements GmbH.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SmmStoreLib
+  FILE_GUID                      = 40A2CBC6-CFB8-447b-A90E-298E88FD345E
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = SmmStoreLib
+
+[Sources]
+  SmmStore.c
+  SmmStore.h
+
+[Sources.X64]
+  X64/SmmStore.nasm
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  DxeServicesTableLib
+  HobLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiRuntimeLib
+
+[Guids]
+  gEfiSmmStoreInfoHobGuid           ## CONSUMES
+  gEfiEventVirtualAddressChangeGuid ## CONSUMES
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiPayloadPkg/UefiPayloadPkg.dec
diff --git a/UefiPayloadPkg/Library/SmmStoreLib/X64/SmmStore.nasm b/UefiPayloadPkg/Library/SmmStoreLib/X64/SmmStore.nasm
new file mode 100644
index 0000000000..1a8b5b64c8
--- /dev/null
+++ b/UefiPayloadPkg/Library/SmmStoreLib/X64/SmmStore.nasm
@@ -0,0 +1,48 @@
+;------------------------------------------------------------------------------ ;
+; Copyright (c) 2022, 9elements GmbH. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;-------------------------------------------------------------------------------
+
+%include "Nasm.inc"
+
+DEFAULT REL
+SECTION .text
+
+;UINTN
+;EFIAPI
+;TriggerSmi (
+;  UINTN   Cmd,
+;  UINTN   Arg,
+;  UINTN   Retry
+;  )
+
+global ASM_PFX(TriggerSmi)
+ASM_PFX(TriggerSmi):
+    push    rbx
+    mov     rax, rcx                    ; Smi handler expect Cmd in RAX
+    mov     rbx, rdx                    ; Smi handler expect Argument in RBX
+ at Trigger:
+    out     0b2h, al                    ; write to APM port to trigger SMI
+
+; There might ba a delay between writing the Smi trigger register and
+; entering SMM, in which case the Smi handler will do nothing as only
+; synchronous Smis are handled. In addition when there's no Smi handler
+; or the SmmStore feature isn't compiled in, no register will be modified.
+
+; As there's no livesign from SMM, just wait a bit for the handler to fire,
+; and then try again.
+
+    cmp     rax, rcx                    ; Check if rax was modified by SMM
+    jne     @Return                     ; SMM modified rax, return now
+    push    rcx                         ; save rcx to stack
+    mov     rcx, 10000
+    rep     pause                       ; add a small delay
+    pop     rcx                         ; restore rcx
+    cmp     r8, 0
+    je      @Return
+    dec     r8
+    jmp     @Trigger
+ at Return:
+    pop     rbx
+    ret
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc
index 14a8d157a2..cf440fee58 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkg.dsc
@@ -261,6 +261,7 @@
   LockBoxLib|MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf
   FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
   AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+  SmmStoreLib|UefiPayloadPkg/Library/SmmStoreLib/SmmStoreLib.inf
 !if $(VARIABLE_SUPPORT) == "EMU"
   TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
 !elseif $(VARIABLE_SUPPORT) == "SPI"
-- 
2.32.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#87781): https://edk2.groups.io/g/devel/message/87781
Mute This Topic: https://groups.io/mt/89923798/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list