]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c
ArmPkg/Drivers: Add EFI_MM_COMMUNICATION_PROTOCOL DXE driver.
[mirror_edk2.git] / ArmPkg / Drivers / MmCommunicationDxe / MmCommunication.c
diff --git a/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c
new file mode 100644 (file)
index 0000000..feb9fa9
--- /dev/null
@@ -0,0 +1,372 @@
+/** @file\r
+\r
+  Copyright (c) 2016-2018, ARM Limited. All rights reserved.\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/ArmLib.h>\r
+#include <Library/ArmSmcLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/DxeServicesTableLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+\r
+#include <Protocol/MmCommunication.h>\r
+\r
+#include <IndustryStandard/ArmStdSmc.h>\r
+\r
+#include "MmCommunicate.h"\r
+\r
+//\r
+// Address, Length of the pre-allocated buffer for communication with the secure\r
+// world.\r
+//\r
+STATIC ARM_MEMORY_REGION_DESCRIPTOR  mNsCommBuffMemRegion;\r
+\r
+// Notification event when virtual address map is set.\r
+STATIC EFI_EVENT  mSetVirtualAddressMapEvent;\r
+\r
+//\r
+// Handle to install the MM Communication Protocol\r
+//\r
+STATIC EFI_HANDLE  mMmCommunicateHandle;\r
+\r
+/**\r
+  Communicates with a registered handler.\r
+\r
+  This function provides an interface to send and receive messages to the\r
+  Standalone MM environment on behalf of UEFI services.  This function is part\r
+  of the MM Communication Protocol that may be called in physical mode prior to\r
+  SetVirtualAddressMap() and in virtual mode after SetVirtualAddressMap().\r
+\r
+  @param[in]      This                The EFI_MM_COMMUNICATION_PROTOCOL\r
+                                      instance.\r
+  @param[in, out] CommBuffer          A pointer to the buffer to convey\r
+                                      into MMRAM.\r
+  @param[in, out] CommSize            The size of the data buffer being\r
+                                      passed in. This is optional.\r
+\r
+  @retval EFI_SUCCESS                 The message was successfully posted.\r
+  @retval EFI_INVALID_PARAMETER       The CommBuffer was NULL.\r
+  @retval EFI_BAD_BUFFER_SIZE         The buffer size is incorrect for the MM\r
+                                      implementation. If this error is\r
+                                      returned, the MessageLength field in\r
+                                      the CommBuffer header or the integer\r
+                                      pointed by CommSize are updated to reflect\r
+                                      the maximum payload size the\r
+                                      implementation can accommodate.\r
+  @retval EFI_ACCESS_DENIED           The CommunicateBuffer parameter\r
+                                      or CommSize parameter, if not omitted,\r
+                                      are in address range that cannot be\r
+                                      accessed by the MM environment\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+MmCommunicationCommunicate (\r
+  IN CONST EFI_MM_COMMUNICATION_PROTOCOL  *This,\r
+  IN OUT VOID                             *CommBuffer,\r
+  IN OUT UINTN                            *CommSize OPTIONAL\r
+  )\r
+{\r
+  EFI_MM_COMMUNICATE_HEADER   *CommunicateHeader;\r
+  ARM_SMC_ARGS                CommunicateSmcArgs;\r
+  EFI_STATUS                  Status;\r
+  UINTN                       BufferSize;\r
+\r
+  Status = EFI_ACCESS_DENIED;\r
+  BufferSize = 0;\r
+\r
+  ZeroMem (&CommunicateSmcArgs, sizeof (ARM_SMC_ARGS));\r
+\r
+  //\r
+  // Check parameters\r
+  //\r
+  if (CommBuffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  CommunicateHeader = CommBuffer;\r
+  // CommBuffer is a mandatory parameter. Hence, Rely on\r
+  // MessageLength + Header to ascertain the\r
+  // total size of the communication payload rather than\r
+  // rely on optional CommSize parameter\r
+  BufferSize = CommunicateHeader->MessageLength +\r
+               sizeof (CommunicateHeader->HeaderGuid) +\r
+               sizeof (CommunicateHeader->MessageLength);\r
+\r
+  // If the length of the CommBuffer is 0 then return the expected length.\r
+  if (CommSize) {\r
+    // This case can be used by the consumer of this driver to find out the\r
+    // max size that can be used for allocating CommBuffer.\r
+    if ((*CommSize == 0) ||\r
+        (*CommSize > mNsCommBuffMemRegion.Length)) {\r
+      *CommSize = mNsCommBuffMemRegion.Length;\r
+      return EFI_BAD_BUFFER_SIZE;\r
+    }\r
+    //\r
+    // CommSize must match MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);\r
+    //\r
+    if (*CommSize != BufferSize) {\r
+        return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If the buffer size is 0 or greater than what can be tolerated by the MM\r
+  // environment then return the expected size.\r
+  //\r
+  if ((BufferSize == 0) ||\r
+      (BufferSize > mNsCommBuffMemRegion.Length)) {\r
+    CommunicateHeader->MessageLength = mNsCommBuffMemRegion.Length -\r
+                                       sizeof (CommunicateHeader->HeaderGuid) -\r
+                                       sizeof (CommunicateHeader->MessageLength);\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  // SMC Function ID\r
+  CommunicateSmcArgs.Arg0 = ARM_SMC_ID_MM_COMMUNICATE_AARCH64;\r
+\r
+  // Cookie\r
+  CommunicateSmcArgs.Arg1 = 0;\r
+\r
+  // Copy Communication Payload\r
+  CopyMem ((VOID *)mNsCommBuffMemRegion.VirtualBase, CommBuffer, BufferSize);\r
+\r
+  // comm_buffer_address (64-bit physical address)\r
+  CommunicateSmcArgs.Arg2 = (UINTN)mNsCommBuffMemRegion.PhysicalBase;\r
+\r
+  // comm_size_address (not used, indicated by setting to zero)\r
+  CommunicateSmcArgs.Arg3 = 0;\r
+\r
+  // Call the Standalone MM environment.\r
+  ArmCallSmc (&CommunicateSmcArgs);\r
+\r
+  switch (CommunicateSmcArgs.Arg0) {\r
+  case ARM_SMC_MM_RET_SUCCESS:\r
+    ZeroMem (CommBuffer, BufferSize);\r
+    // On successful return, the size of data being returned is inferred from\r
+    // MessageLength + Header.\r
+    CommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)mNsCommBuffMemRegion.VirtualBase;\r
+    BufferSize = CommunicateHeader->MessageLength +\r
+                 sizeof (CommunicateHeader->HeaderGuid) +\r
+                 sizeof (CommunicateHeader->MessageLength);\r
+\r
+    CopyMem (\r
+      CommBuffer,\r
+      (VOID *)mNsCommBuffMemRegion.VirtualBase,\r
+      BufferSize\r
+      );\r
+    Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  case ARM_SMC_MM_RET_INVALID_PARAMS:\r
+    Status = EFI_INVALID_PARAMETER;\r
+    break;\r
+\r
+  case ARM_SMC_MM_RET_DENIED:\r
+    Status = EFI_ACCESS_DENIED;\r
+    break;\r
+\r
+  case ARM_SMC_MM_RET_NO_MEMORY:\r
+    // Unexpected error since the CommSize was checked for zero length\r
+    // prior to issuing the SMC\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    ASSERT (0);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_ACCESS_DENIED;\r
+    ASSERT (0);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+//\r
+// MM Communication Protocol instance\r
+//\r
+EFI_MM_COMMUNICATION_PROTOCOL  mMmCommunication = {\r
+  MmCommunicationCommunicate\r
+};\r
+\r
+/**\r
+  Notification callback on SetVirtualAddressMap event.\r
+\r
+  This function notifies the MM communication protocol interface on\r
+  SetVirtualAddressMap event and converts pointers used in this driver\r
+  from physical to virtual address.\r
+\r
+  @param  Event          SetVirtualAddressMap event.\r
+  @param  Context        A context when the SetVirtualAddressMap triggered.\r
+\r
+  @retval EFI_SUCCESS    The function executed successfully.\r
+  @retval Other          Some error occurred when executing this function.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+NotifySetVirtualAddressMap (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = gRT->ConvertPointer (\r
+                  EFI_OPTIONAL_PTR,\r
+                  (VOID **)&mNsCommBuffMemRegion.VirtualBase\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NotifySetVirtualAddressMap():"\r
+            " Unable to convert MM runtime pointer. Status:0x%r\n", Status));\r
+  }\r
+\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+GetMmCompatibility ()\r
+{\r
+  EFI_STATUS   Status;\r
+  UINT32       MmVersion;\r
+  ARM_SMC_ARGS MmVersionArgs;\r
+\r
+  // MM_VERSION uses SMC32 calling conventions\r
+  MmVersionArgs.Arg0 = ARM_SMC_ID_MM_VERSION_AARCH32;\r
+\r
+  ArmCallSmc (&MmVersionArgs);\r
+\r
+  MmVersion = MmVersionArgs.Arg0;\r
+\r
+  if ((MM_MAJOR_VER(MmVersion) == MM_CALLER_MAJOR_VER) &&\r
+      (MM_MINOR_VER(MmVersion) >= MM_CALLER_MINOR_VER)) {\r
+    DEBUG ((DEBUG_INFO, "MM Version: Major=0x%x, Minor=0x%x\n",\r
+            MM_MAJOR_VER(MmVersion), MM_MINOR_VER(MmVersion)));\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+    DEBUG ((DEBUG_ERROR, "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n",\r
+            MM_MAJOR_VER(MmVersion), MM_MINOR_VER(MmVersion), MM_CALLER_MAJOR_VER, MM_CALLER_MINOR_VER));\r
+    Status = EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The Entry Point for MM Communication\r
+\r
+  This function installs the MM communication protocol interface and finds out\r
+  what type of buffer management will be required prior to invoking the\r
+  communication SMC.\r
+\r
+  @param  ImageHandle    The firmware allocated handle for the EFI image.\r
+  @param  SystemTable    A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS    The entry point is executed successfully.\r
+  @retval Other          Some error occurred when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+MmCommunicationInitialize (\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                 Status;\r
+\r
+  // Check if we can make the MM call\r
+  Status = GetMmCompatibility ();\r
+  if (EFI_ERROR(Status)) {\r
+    goto ReturnErrorStatus;\r
+  }\r
+\r
+  mNsCommBuffMemRegion.PhysicalBase = PcdGet64 (PcdMmBufferBase);\r
+  // During boot , Virtual and Physical are same\r
+  mNsCommBuffMemRegion.VirtualBase = mNsCommBuffMemRegion.PhysicalBase;\r
+  mNsCommBuffMemRegion.Length = PcdGet64 (PcdMmBufferSize);\r
+\r
+  ASSERT (mNsCommBuffMemRegion.PhysicalBase != 0);\r
+\r
+  ASSERT (mNsCommBuffMemRegion.Length != 0);\r
+\r
+  Status = gDS->AddMemorySpace (\r
+                  EfiGcdMemoryTypeReserved,\r
+                  mNsCommBuffMemRegion.PhysicalBase,\r
+                  mNsCommBuffMemRegion.Length,\r
+                  EFI_MEMORY_WB |\r
+                  EFI_MEMORY_XP |\r
+                  EFI_MEMORY_RUNTIME\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "\r
+            "Failed to add MM-NS Buffer Memory Space\n"));\r
+    goto ReturnErrorStatus;\r
+  }\r
+\r
+  Status = gDS->SetMemorySpaceAttributes (\r
+                  mNsCommBuffMemRegion.PhysicalBase,\r
+                  mNsCommBuffMemRegion.Length,\r
+                  EFI_MEMORY_WB | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "\r
+            "Failed to set MM-NS Buffer Memory attributes\n"));\r
+    goto CleanAddedMemorySpace;\r
+  }\r
+\r
+  // Install the communication protocol\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &mMmCommunicateHandle,\r
+                  &gEfiMmCommunicationProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &mMmCommunication\r
+                  );\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "MmCommunicationInitialize: "\r
+            "Failed to install MM communication protocol\n"));\r
+    goto CleanAddedMemorySpace;\r
+  }\r
+\r
+  // Register notification callback when virtual address is associated\r
+  // with the physical address.\r
+  // Create a Set Virtual Address Map event.\r
+  Status = gBS->CreateEvent (\r
+                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,\r
+                  TPL_NOTIFY,\r
+                  NotifySetVirtualAddressMap,\r
+                  NULL,\r
+                  &mSetVirtualAddressMapEvent\r
+                  );\r
+  if (Status == EFI_SUCCESS) {\r
+    return Status;\r
+  }\r
+\r
+  gBS->UninstallProtocolInterface (\r
+         mMmCommunicateHandle,\r
+         &gEfiMmCommunicationProtocolGuid,\r
+         &mMmCommunication\r
+         );\r
+\r
+CleanAddedMemorySpace:\r
+  gDS->RemoveMemorySpace (\r
+         mNsCommBuffMemRegion.PhysicalBase,\r
+         mNsCommBuffMemRegion.Length\r
+         );\r
+\r
+ReturnErrorStatus:\r
+  return EFI_INVALID_PARAMETER;\r
+}\r