EFI driver implementing the MmCommunication2Protocol,
but instead of trapping to MM mode (SMM on x86, el3 on arm)
trap to the host, using a virtual device.

Moving the efi variable management to the host allows to have
persistent efi variables without flash storage, and it also
allows to have secure boot support without requiring MM mode
to protect variable storage in flash.

This patch brings the core code, and the x64 bits
which talk to 'qemu -device uefi-vars-isa'.

Signed-off-by: Gerd Hoffmann <kra...@redhat.com>
---
 .../VirtMmCommunication.inf                   |  50 +++
 .../VirtMmCommunication.h                     |  37 ++
 OvmfPkg/VirtMmCommunicationDxe/QemuX64.c      | 106 +++++
 .../VirtMmCommunication.c                     | 380 ++++++++++++++++++
 4 files changed, 573 insertions(+)
 create mode 100644 OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf
 create mode 100644 OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h
 create mode 100644 OvmfPkg/VirtMmCommunicationDxe/QemuX64.c
 create mode 100644 OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c

diff --git a/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf 
b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf
new file mode 100644
index 000000000000..a9998f3db8cd
--- /dev/null
+++ b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf
@@ -0,0 +1,50 @@
+#/** @file
+#
+#  Virt MM Communicate driver
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x0001001A
+  BASE_NAME                      = VirtMmCommunication
+  FILE_GUID                      = 0B807404-2D1C-4066-95F4-F28A58800185
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = VirtMmCommunication2Initialize
+
+[Sources]
+  VirtMmCommunication.c
+
+[Sources.X64]
+  QemuX64.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  DxeServicesTableLib
+  HobLib
+  MemoryAllocationLib
+  UefiDriverEntryPoint
+
+[FeaturePcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEnableVariableRuntimeCache           ## 
CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics            ## 
CONSUMES
+
+[Protocols]
+  gEfiMmCommunication2ProtocolGuid              ## PRODUCES
+  gEfiSmmVariableProtocolGuid                   ## PRODUCES
+
+[Guids]
+  gEfiEndOfDxeEventGroupGuid
+  gEfiEventExitBootServicesGuid
+  gEfiEventReadyToBootGuid
+  gSmmVariableWriteGuid
+
+[Depex]
+  gEfiCpuArchProtocolGuid
diff --git a/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h 
b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h
new file mode 100644
index 000000000000..32f9d16c42e3
--- /dev/null
+++ b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h
@@ -0,0 +1,37 @@
+/** @file
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _VIRT_MM_COMM_DXE_H_
+#define _VIRT_MM_COMM_DXE_H_
+
+/* communication buffer */
+
+#define MAX_BUFFER_SIZE  (64 * 1024)
+
+extern VOID                  *mCommunicateBuffer;
+extern EFI_PHYSICAL_ADDRESS  mCommunicateBufferPhys;
+
+/* arch specific hooks */
+
+EFI_STATUS
+EFIAPI
+VirtMmHwInit (
+  VOID
+  );
+
+EFI_STATUS
+EFIAPI
+VirtMmHwComm (
+  VOID
+  );
+
+EFI_STATUS
+EFIAPI
+VirtMmHwVirtMap (
+  VOID
+  );
+
+#endif /* _VIRT_MM_COMM_DXE_H_ */
diff --git a/OvmfPkg/VirtMmCommunicationDxe/QemuX64.c 
b/OvmfPkg/VirtMmCommunicationDxe/QemuX64.c
new file mode 100644
index 000000000000..6470cfc7f73b
--- /dev/null
+++ b/OvmfPkg/VirtMmCommunicationDxe/QemuX64.c
@@ -0,0 +1,106 @@
+/** @file
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/QemuUefiVars.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+
+#include "VirtMmCommunication.h"
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtMmHwCommand (
+  UINT32  Cmd
+  )
+{
+  UINT32  Count;
+  UINT32  Sts;
+
+  IoWrite16 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_CMD_STS, Cmd);
+  for (Count = 0; Count < 100; Count++) {
+    Sts = IoRead16 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_CMD_STS);
+    DEBUG ((DEBUG_VERBOSE, "%a: Sts: 0x%x\n", __func__, Sts));
+    switch (Sts) {
+      case UEFI_VARS_STS_SUCCESS:
+        return RETURN_SUCCESS;
+      case UEFI_VARS_STS_BUSY:
+        CpuPause ();
+        break;
+      case UEFI_VARS_STS_ERR_NOT_SUPPORTED:
+        return RETURN_UNSUPPORTED;
+      case UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE:
+        return RETURN_BAD_BUFFER_SIZE;
+      default:
+        return RETURN_DEVICE_ERROR;
+    }
+  }
+
+  return RETURN_TIMEOUT;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwInit (
+  VOID
+  )
+{
+  UINT32      Magic, AddrLo, AddrHi;
+  EFI_STATUS  Status;
+
+  Magic = IoRead16 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_MAGIC);
+  if (Magic != UEFI_VARS_MAGIC_VALUE) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Magic value mismatch (0x%x != 0x%x)\n",
+      __func__,
+      Magic,
+      UEFI_VARS_MAGIC_VALUE
+      ));
+    return RETURN_DEVICE_ERROR;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: Magic 0x%x, good\n", __func__, Magic));
+
+  Status = VirtMmHwCommand (UEFI_VARS_CMD_RESET);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Reset failed: %d\n", __func__, Status));
+    return Status;
+  }
+
+  AddrLo = (UINT32)mCommunicateBufferPhys;
+  AddrHi = (UINT32)RShiftU64 (mCommunicateBufferPhys, 32);
+  IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_BUFFER_ADDR_LO, AddrLo);
+  IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_BUFFER_ADDR_HI, AddrHi);
+  IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_BUFFER_SIZE, MAX_BUFFER_SIZE);
+
+  return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwVirtMap (
+  VOID
+  )
+{
+  return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwComm (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = VirtMmHwCommand (UEFI_VARS_CMD_MM);
+  DEBUG ((DEBUG_VERBOSE, "%a: Status: %r\n", __func__, Status));
+
+  return Status;
+}
diff --git a/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c 
b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c
new file mode 100644
index 000000000000..7b4ebe1257c6
--- /dev/null
+++ b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c
@@ -0,0 +1,380 @@
+/** @file
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Protocol/MmCommunication2.h>
+
+#include "VirtMmCommunication.h"
+
+VOID                  *mCommunicateBuffer;
+EFI_PHYSICAL_ADDRESS  mCommunicateBufferPhys;
+
+// Notification event when virtual address map is set.
+STATIC EFI_EVENT  mSetVirtualAddressMapEvent;
+
+// Handle to install the MM Communication Protocol
+STATIC EFI_HANDLE  mMmCommunicateHandle;
+
+// Handle to install the EfiSmmVariableProtocol
+STATIC EFI_HANDLE  mSmmVariableHandle;
+
+// Handle to install the SmmVariableWrite
+STATIC EFI_HANDLE  mSmmVariableWriteHandle;
+
+/**
+  Communicates with a registered handler.
+
+  This function provides a service to send and receive messages from a 
registered UEFI service.
+
+  @param[in] This                     The EFI_MM_COMMUNICATION_PROTOCOL 
instance.
+  @param[in, out] CommBufferPhysical  Physical address of the MM communication 
buffer
+  @param[in, out] CommBufferVirtual   Virtual address of the MM communication 
buffer
+  @param[in, out] CommSize            The size of the data buffer being passed 
in. On input,
+                                      when not omitted, the buffer should 
cover EFI_MM_COMMUNICATE_HEADER
+                                      and the value of MessageLength field. On 
exit, the size
+                                      of data being returned. Zero if the 
handler does not
+                                      wish to reply with any data. This 
parameter is optional
+                                      and may be NULL.
+
+  @retval EFI_SUCCESS            The message was successfully posted.
+  @retval EFI_INVALID_PARAMETER  CommBufferPhysical or CommBufferVirtual was 
NULL, or
+                                 integer value pointed by CommSize does not 
cover
+                                 EFI_MM_COMMUNICATE_HEADER and the value of 
MessageLength
+                                 field.
+  @retval EFI_BAD_BUFFER_SIZE    The buffer is too large for the MM 
implementation.
+                                 If this error is returned, the MessageLength 
field
+                                 in the CommBuffer header or the integer 
pointed by
+                                 CommSize, are updated to reflect the maximum 
payload
+                                 size the implementation can accommodate.
+  @retval EFI_ACCESS_DENIED      The CommunicateBuffer parameter or CommSize 
parameter,
+                                 if not omitted, are in address range that 
cannot be
+                                 accessed by the MM environment.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtMmCommunication2Communicate (
+  IN CONST EFI_MM_COMMUNICATION2_PROTOCOL  *This,
+  IN OUT VOID                              *CommBufferPhysical,
+  IN OUT VOID                              *CommBufferVirtual,
+  IN OUT UINTN                             *CommSize OPTIONAL
+  )
+{
+  EFI_MM_COMMUNICATE_HEADER  *CommunicateHeader;
+  EFI_STATUS                 Status;
+  UINTN                      BufferSize;
+
+  //
+  // Check parameters
+  //
+  if ((CommBufferVirtual == NULL) || (CommBufferPhysical == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status            = EFI_SUCCESS;
+  CommunicateHeader = CommBufferVirtual;
+  // CommBuffer is a mandatory parameter. Hence, Rely on
+  // MessageLength + Header to ascertain the
+  // total size of the communication payload rather than
+  // rely on optional CommSize parameter
+  BufferSize = CommunicateHeader->MessageLength +
+               sizeof (CommunicateHeader->HeaderGuid) +
+               sizeof (CommunicateHeader->MessageLength);
+
+  DEBUG ((
+    DEBUG_VERBOSE,
+    "%a: %g msglen %ld, BufferSize %ld, CommSize %ld\n",
+    __func__,
+    &CommunicateHeader->HeaderGuid,
+    CommunicateHeader->MessageLength,
+    BufferSize,
+    *CommSize
+    ));
+
+  // If CommSize is not omitted, perform size inspection before proceeding.
+  if (CommSize != NULL) {
+    // This case can be used by the consumer of this driver to find out the
+    // max size that can be used for allocating CommBuffer.
+    if ((*CommSize == 0) ||
+        (*CommSize > MAX_BUFFER_SIZE))
+    {
+      *CommSize = MAX_BUFFER_SIZE;
+      Status    = EFI_BAD_BUFFER_SIZE;
+    }
+
+    //
+    // CommSize should cover at least MessageLength + sizeof 
(EFI_MM_COMMUNICATE_HEADER);
+    //
+    if (*CommSize < BufferSize) {
+      Status = EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // If the message length is 0 or greater than what can be tolerated by the MM
+  // environment then return the expected size.
+  //
+  if ((CommunicateHeader->MessageLength == 0) ||
+      (BufferSize > MAX_BUFFER_SIZE))
+  {
+    CommunicateHeader->MessageLength = MAX_BUFFER_SIZE -
+                                       sizeof (CommunicateHeader->HeaderGuid) -
+                                       sizeof 
(CommunicateHeader->MessageLength);
+    Status = EFI_BAD_BUFFER_SIZE;
+  }
+
+  // MessageLength or CommSize check has failed, return here.
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: check error: %r\n", __func__, Status));
+    return Status;
+  }
+
+  // Copy Communication Payload
+  CopyMem (mCommunicateBuffer, CommBufferVirtual, BufferSize);
+
+  Status = VirtMmHwComm ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: comm error: %r\n", __func__, Status));
+    return Status;
+  }
+
+  CopyMem (CommBufferVirtual, mCommunicateBuffer, BufferSize);
+
+  DEBUG ((DEBUG_VERBOSE, "%a: success (%d)\n", __func__, BufferSize));
+  return EFI_SUCCESS;
+}
+
+//
+// MM Communication Protocol instance
+//
+STATIC EFI_MM_COMMUNICATION2_PROTOCOL  mMmCommunication2 = {
+  VirtMmCommunication2Communicate
+};
+
+/**
+  Notification callback on SetVirtualAddressMap event.
+
+  This function notifies the MM communication protocol interface on
+  SetVirtualAddressMap event and converts pointers used in this driver
+  from physical to virtual address.
+
+  @param  Event          SetVirtualAddressMap event.
+  @param  Context        A context when the SetVirtualAddressMap triggered.
+
+  @retval EFI_SUCCESS    The function executed successfully.
+  @retval Other          Some error occurred when executing this function.
+
+**/
+STATIC
+VOID
+EFIAPI
+VirtMmNotifySetVirtualAddressMap (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EFI_STATUS  Status;
+
+  DEBUG ((DEBUG_VERBOSE, "%a: << %p\n", __func__, mCommunicateBuffer));
+  Status = gRT->ConvertPointer (EFI_OPTIONAL_PTR, &mCommunicateBuffer);
+  DEBUG ((DEBUG_VERBOSE, "%a: >> %p\n", __func__, mCommunicateBuffer));
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Unable to convert MM runtime pointer. Status: %r\n",
+      __func__,
+      Status
+      ));
+  }
+
+  Status = VirtMmHwVirtMap ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: VirtMmHwVirtMap failed. Status: %r\n",
+      __func__,
+      Status
+      ));
+  }
+}
+
+STATIC EFI_GUID *CONST  mGuidedEventGuid[] = {
+  &gEfiEndOfDxeEventGroupGuid,
+  &gEfiEventExitBootServicesGuid,
+  &gEfiEventReadyToBootGuid,
+};
+
+STATIC EFI_EVENT  mGuidedEvent[ARRAY_SIZE (mGuidedEventGuid)];
+
+/**
+  Event notification that is fired when GUIDed Event Group is signaled.
+
+  @param  Event                 The Event that is being processed, not used.
+  @param  Context               Event Context, not used.
+
+**/
+STATIC
+VOID
+EFIAPI
+VirtMmGuidedEventNotify (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EFI_MM_COMMUNICATE_HEADER  Header;
+  UINTN                      Size;
+
+  //
+  // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
+  //
+  CopyGuid (&Header.HeaderGuid, Context);
+  Header.MessageLength = 1;
+  Header.Data[0]       = 0;
+
+  Size = sizeof (Header);
+  VirtMmCommunication2Communicate (&mMmCommunication2, &Header, &Header, 
&Size);
+}
+
+/**
+  The Entry Point for MM Communication
+
+  This function installs the MM communication protocol interface and finds out
+  what type of buffer management will be required prior to invoking the
+  communication SMC.
+
+  @param  ImageHandle    The firmware allocated handle for the EFI image.
+  @param  SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS    The entry point is executed successfully.
+  @retval Other          Some error occurred when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtMmCommunication2Initialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Index;
+
+  if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) {
+    ASSERT (!"Variable driver runtime cache is not supported.\n");
+    CpuDeadLoop ();
+  }
+
+  if (FeaturePcdGet (PcdVariableCollectStatistics)) {
+    ASSERT (!"Variable driver statistics are not supported.\n");
+    CpuDeadLoop ();
+  }
+
+  mCommunicateBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES 
(MAX_BUFFER_SIZE));
+  if (!mCommunicateBuffer) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ReturnErrorStatus;
+  }
+
+  mCommunicateBufferPhys = (EFI_PHYSICAL_ADDRESS)(UINTN)(mCommunicateBuffer);
+  Status                 = VirtMmHwInit ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Failed to init HW: %r\n",
+      __func__,
+      Status
+      ));
+    goto FreeBufferPages;
+  }
+
+  // Install the communication protocol
+  Status = gBS->InstallProtocolInterface (
+                  &mMmCommunicateHandle,
+                  &gEfiMmCommunication2ProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &mMmCommunication2
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Failed to install MM communication protocol\n",
+      __func__
+      ));
+    goto FreeBufferPages;
+  }
+
+  Status = gBS->InstallProtocolInterface (
+                  &mSmmVariableHandle,
+                  &gEfiSmmVariableProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->InstallProtocolInterface (
+                  &mSmmVariableWriteHandle,
+                  &gSmmVariableWriteGuid,
+                  EFI_NATIVE_INTERFACE,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  // Register notification callback when virtual address is associated
+  // with the physical address.
+  // Create a Set Virtual Address Map event.
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
+                  TPL_NOTIFY,
+                  VirtMmNotifySetVirtualAddressMap,
+                  NULL,
+                  &mSetVirtualAddressMapEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  for (Index = 0; Index < ARRAY_SIZE (mGuidedEventGuid); Index++) {
+    Status = gBS->CreateEventEx (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_CALLBACK,
+                    VirtMmGuidedEventNotify,
+                    mGuidedEventGuid[Index],
+                    mGuidedEventGuid[Index],
+                    &mGuidedEvent[Index]
+                    );
+    ASSERT_EFI_ERROR (Status);
+    if (EFI_ERROR (Status)) {
+      while (Index-- > 0) {
+        gBS->CloseEvent (mGuidedEvent[Index]);
+      }
+
+      goto UninstallProtocol;
+    }
+  }
+
+  return EFI_SUCCESS;
+
+UninstallProtocol:
+  gBS->UninstallProtocolInterface (
+         mMmCommunicateHandle,
+         &gEfiMmCommunication2ProtocolGuid,
+         &mMmCommunication2
+         );
+
+FreeBufferPages:
+  FreePages (mCommunicateBuffer, EFI_SIZE_TO_PAGES (MAX_BUFFER_SIZE));
+
+ReturnErrorStatus:
+  return EFI_INVALID_PARAMETER;
+}
-- 
2.42.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#111679): https://edk2.groups.io/g/devel/message/111679
Mute This Topic: https://groups.io/mt/102767936/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to