--- /dev/null
+/** @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