summaryrefslogtreecommitdiffstats
path: root/ArmPkg
diff options
context:
space:
mode:
Diffstat (limited to 'ArmPkg')
-rw-r--r--ArmPkg/ArmPkg.ci.yaml3
-rw-r--r--ArmPkg/ArmPkg.dec6
-rw-r--r--ArmPkg/ArmPkg.dsc15
-rw-r--r--ArmPkg/Drivers/StandaloneMmCpu/EventHandle.c210
-rw-r--r--ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.c99
-rw-r--r--ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.h79
-rw-r--r--ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf51
-rw-r--r--ArmPkg/Include/Library/ArmStandaloneMmCoreEntryPoint.h294
-rw-r--r--ArmPkg/Include/Protocol/PiMmCpuDriverEp.h60
-rw-r--r--ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/AArch64/ModuleEntryPoint.S231
-rw-r--r--ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/Arm/ModuleEntryPoint.S220
-rw-r--r--ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.c1223
-rw-r--r--ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.inf72
-rw-r--r--ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/SetPermissions.c383
14 files changed, 2945 insertions, 1 deletions
diff --git a/ArmPkg/ArmPkg.ci.yaml b/ArmPkg/ArmPkg.ci.yaml
index a8f179243b..2f0766a1cc 100644
--- a/ArmPkg/ArmPkg.ci.yaml
+++ b/ArmPkg/ArmPkg.ci.yaml
@@ -49,7 +49,8 @@
"EmbeddedPkg/EmbeddedPkg.dec",
"MdeModulePkg/MdeModulePkg.dec",
"MdePkg/MdePkg.dec",
- "ShellPkg/ShellPkg.dec"
+ "ShellPkg/ShellPkg.dec",
+ "StandaloneMmPkg/StandaloneMmPkg.dec"
],
# For host based unit tests
"AcceptableDependencies-HOST_APPLICATION":[
diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
index df56e6326d..c65660658d 100644
--- a/ArmPkg/ArmPkg.dec
+++ b/ArmPkg/ArmPkg.dec
@@ -101,6 +101,10 @@
#
ArmFfaLib|Include/Library/ArmFfaLib.h
+ ## @libraryclass Defines a set of interfaces for the MM core entrypoint for
+ ## AArch64 and ARM.
+ StandaloneMmCoreEntryPoint|Include/Library/ArmStandaloneMmCoreEntryPoint.h
+
[Guids.common]
gArmTokenSpaceGuid = { 0xBB11ECFE, 0x820F, 0x4968, { 0xBB, 0xA6, 0xF7, 0x6A, 0xFE, 0x30, 0x25, 0x96 } }
@@ -126,6 +130,8 @@
## ArmPkg/Include/Protocol/ArmScmiPerformanceProtocol.h
gArmScmiPerformanceProtocolGuid = { 0x9b8ba84, 0x3dd3, 0x49a6, { 0xa0, 0x5a, 0x31, 0x34, 0xa5, 0xf0, 0x7b, 0xad } }
+ gEdkiiPiMmCpuDriverEpProtocolGuid = { 0x6ecbd5a1, 0xc0f8, 0x4702, { 0x83, 0x01, 0x4f, 0xc2, 0xc5, 0x47, 0x0a, 0x51 }}
+
[Ppis]
## Include/Ppi/ArmMpCoreInfo.h
gArmMpCoreInfoPpiGuid = { 0x6847cc74, 0xe9ec, 0x4f8f, {0xa2, 0x9d, 0xab, 0x44, 0xe7, 0x54, 0xa8, 0xfc} }
diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
index 330effdfe3..e602fc9340 100644
--- a/ArmPkg/ArmPkg.dsc
+++ b/ArmPkg/ArmPkg.dsc
@@ -102,6 +102,15 @@
PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
ArmFfaLib|ArmPkg/Library/ArmFfaLib/ArmFfaPeiLib.inf
+[LibraryClasses.common.MM_CORE_STANDALONE]
+ StandaloneMmCoreEntryPoint|ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.inf
+ HobLib|StandaloneMmPkg/Library/StandaloneMmHobLib/StandaloneMmHobLib.inf
+
+[LibraryClasses.common.MM_STANDALONE]
+ StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf
+ HobLib|StandaloneMmPkg/Library/StandaloneMmHobLib/StandaloneMmHobLib.inf
+ MmServicesTableLib|MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf
+
[Components.common]
ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf
ArmPkg/Library/ArmDisassemblerLib/ArmDisassemblerLib.inf
@@ -161,6 +170,12 @@
ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf
+[Components.common.MM_CORE_STANALONE]
+ ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.inf
+
+[Components.common.MM_STANDALONE]
+ ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf
+
[Components.AARCH64]
ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf
ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf
diff --git a/ArmPkg/Drivers/StandaloneMmCpu/EventHandle.c b/ArmPkg/Drivers/StandaloneMmCpu/EventHandle.c
new file mode 100644
index 0000000000..b6cecd1336
--- /dev/null
+++ b/ArmPkg/Drivers/StandaloneMmCpu/EventHandle.c
@@ -0,0 +1,210 @@
+/** @file
+
+ Copyright (c) 2016 HP Development Company, L.P.
+ Copyright (c) 2016 - 2024, Arm Limited. All rights reserved.
+ Copyright (c) 2021, Linaro Limited
+ Copyright (c) 2023, Ventana Micro System Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Pi/PiMmCis.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+
+#include <Protocol/DebugSupport.h> // for EFI_SYSTEM_CONTEXT
+
+#include <Guid/ZeroGuid.h>
+#include <Guid/MmramMemoryReserve.h>
+
+#include <StandaloneMmCpu.h>
+
+EFI_STATUS
+EFIAPI
+MmFoundationEntryRegister (
+ IN CONST EFI_MM_CONFIGURATION_PROTOCOL *This,
+ IN EFI_MM_ENTRY_POINT MmEntryPoint
+ );
+
+//
+// On ARM platforms every event is expected to have a GUID associated with
+// it. It will be used by the MM Entry point to find the handler for the
+// event. It will either be populated in a EFI_MM_COMMUNICATE_HEADER by the
+// caller of the event (e.g. MM_COMMUNICATE SMC) or by the CPU driver
+// (e.g. during an asynchronous event). In either case, this context is
+// maintained in single global variable because StandaloneMm is UP-migratable
+// (which means it cannot run concurrently)
+//
+EFI_MM_COMMUNICATE_HEADER *gGuidedEventContext = NULL;
+
+EFI_MM_CONFIGURATION_PROTOCOL mMmConfig = {
+ 0,
+ MmFoundationEntryRegister
+};
+
+EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL mPiMmCpuDriverEpProtocol = {
+ PiMmStandaloneMmCpuDriverEntry
+};
+
+STATIC EFI_MM_ENTRY_POINT mMmEntryPoint = NULL;
+
+/**
+ The PI Standalone MM entry point for the CPU driver.
+
+ @param [in] EventId The event Id.
+ @param [in] CommBufferAddr Address of the communication buffer.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter was invalid.
+ @retval EFI_ACCESS_DENIED Access not permitted.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Operation not supported.
+**/
+EFI_STATUS
+PiMmStandaloneMmCpuDriverEntry (
+ IN UINTN EventId,
+ IN UINTN CommBufferAddr
+ )
+{
+ EFI_MM_ENTRY_CONTEXT MmEntryPointContext;
+ EFI_STATUS Status;
+ UINTN CommBufferSize;
+
+ DEBUG ((DEBUG_INFO, "Received event - 0x%x\n", EventId));
+
+ Status = EFI_SUCCESS;
+
+ // Perform parameter validation of NsCommBufferAddr
+ if (CommBufferAddr == (UINTN)NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (mMmEntryPoint == NULL) {
+ DEBUG ((DEBUG_ERROR, "Mm Entry point Not Found\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ // Find out the size of the buffer passed
+ CommBufferSize = ((EFI_MM_COMMUNICATE_HEADER *)CommBufferAddr)->MessageLength +
+ sizeof (EFI_MM_COMMUNICATE_HEADER);
+
+ // Now that the secure world can see the normal world buffer, allocate
+ // memory to copy the communication buffer to the secure world.
+ Status = mMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ CommBufferSize,
+ (VOID **)&gGuidedEventContext
+ );
+
+ if (EFI_ERROR (Status)) {
+ gGuidedEventContext = NULL;
+ DEBUG ((DEBUG_ERROR, "Mem alloc failed - 0x%x\n", EventId));
+ return Status;
+ }
+
+ CopyMem (gGuidedEventContext, (CONST VOID *)CommBufferAddr, CommBufferSize);
+
+ ZeroMem (&MmEntryPointContext, sizeof (EFI_MM_ENTRY_CONTEXT));
+
+ // StandaloneMm UP-migratable which means it cannot run concurrently.
+ // Therefore, set number of cpus as 1 and cpu number as 0.
+ MmEntryPointContext.CurrentlyExecutingCpu = 0;
+ MmEntryPointContext.NumberOfCpus = 1;
+
+ // Populate the MM system table with MP and state information
+ mMmst->CurrentlyExecutingCpu = 0;
+ mMmst->NumberOfCpus = 1;
+ mMmst->CpuSaveStateSize = 0;
+ mMmst->CpuSaveState = NULL;
+
+ mMmEntryPoint (&MmEntryPointContext);
+
+ // Free the memory allocation done earlier and reset the per-cpu context
+ CopyMem ((VOID *)CommBufferAddr, (CONST VOID *)gGuidedEventContext, CommBufferSize);
+
+ Status = mMmst->MmFreePool ((VOID *)gGuidedEventContext);
+ ASSERT_EFI_ERROR (Status);
+ gGuidedEventContext = NULL;
+
+ return Status;
+}
+
+/**
+ Registers the MM foundation entry point.
+
+ @param [in] This Pointer to the MM Configuration protocol.
+ @param [in] MmEntryPoint Function pointer to the MM Entry point.
+
+ @retval EFI_SUCCESS Success.
+**/
+EFI_STATUS
+EFIAPI
+MmFoundationEntryRegister (
+ IN CONST EFI_MM_CONFIGURATION_PROTOCOL *This,
+ IN EFI_MM_ENTRY_POINT MmEntryPoint
+ )
+{
+ // store the entry point in a global
+ mMmEntryPoint = MmEntryPoint;
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is the main entry point for an MM handler dispatch
+ or communicate-based callback.
+
+ @param DispatchHandle The unique handle assigned to this handler by
+ MmiHandlerRegister().
+ @param Context 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-MM environment into an
+ MM environment.
+ @param CommBufferSize The size of the CommBuffer.
+
+ @return Status Code
+
+**/
+EFI_STATUS
+EFIAPI
+PiMmCpuTpFwRootMmiHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *Context OPTIONAL,
+ IN OUT VOID *CommBuffer OPTIONAL,
+ IN OUT UINTN *CommBufferSize OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Context == NULL);
+ ASSERT (CommBuffer == NULL);
+ ASSERT (CommBufferSize == NULL);
+
+ if (gGuidedEventContext == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "CommBuffer - 0x%x, CommBufferSize - 0x%x\n",
+ gGuidedEventContext,
+ gGuidedEventContext->MessageLength
+ ));
+
+ Status = mMmst->MmiManage (
+ &gGuidedEventContext->HeaderGuid,
+ NULL,
+ gGuidedEventContext->Data,
+ &gGuidedEventContext->MessageLength
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((DEBUG_WARN, "Unable to manage Guided Event - %d\n", Status));
+ }
+
+ return Status;
+}
diff --git a/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.c b/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.c
new file mode 100644
index 0000000000..f2b566b108
--- /dev/null
+++ b/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.c
@@ -0,0 +1,99 @@
+/** @file
+
+ Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
+ Copyright (c) 2016 HP Development Company, L.P.
+ Copyright (c) 2016 - 2024, Arm Limited. All rights reserved.
+ Copyright (c) 2023, Ventana Micro System Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Pi/PiMmCis.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/HobLib.h>
+
+#include <Protocol/DebugSupport.h> // for EFI_SYSTEM_CONTEXT
+
+#include <Guid/ZeroGuid.h>
+#include <Guid/MmramMemoryReserve.h>
+
+#include <StandaloneMmCpu.h>
+
+//
+// mPiMmCpuDriverEpProtocol for Cpu driver entry point to handle
+// mm communication event.
+//
+extern EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL mPiMmCpuDriverEpProtocol;
+
+//
+// Private copy of the MM system table for future use
+//
+EFI_MM_SYSTEM_TABLE *mMmst = NULL;
+
+//
+// Globals used to initialize the protocol
+//
+STATIC EFI_HANDLE mMmCpuHandle = NULL;
+
+/** Entry point for the Standalone MM CPU driver.
+
+ @param [in] ImageHandle Unused. Not actual image handle.
+ @param [in] SystemTable Pointer to MM System table.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_NOT_FOUND Failed to find the HOB for the CPU
+ driver endpoint descriptor.
+**/
+EFI_STATUS
+StandaloneMmCpuInitialize (
+ IN EFI_HANDLE ImageHandle, // not actual imagehandle
+ IN EFI_MM_SYSTEM_TABLE *SystemTable // not actual systemtable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE DispatchHandle;
+
+ ASSERT (SystemTable != NULL);
+ mMmst = SystemTable;
+
+ // publish the MM config protocol so the MM core can register its entry point
+ Status = mMmst->MmInstallProtocolInterface (
+ &mMmCpuHandle,
+ &gEfiMmConfigurationProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mMmConfig
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Install entry point of this CPU driver to allow
+ // the entry point driver to be invoked upon receipt of an event in
+ // DelegatedEventLoop.
+ Status = mMmst->MmInstallProtocolInterface (
+ &mMmCpuHandle,
+ &gEdkiiPiMmCpuDriverEpProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mPiMmCpuDriverEpProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // register the root MMI handler
+ Status = mMmst->MmiHandlerRegister (
+ PiMmCpuTpFwRootMmiHandler,
+ NULL,
+ &DispatchHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
diff --git a/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.h b/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.h
new file mode 100644
index 0000000000..fc3d81b6e6
--- /dev/null
+++ b/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.h
@@ -0,0 +1,79 @@
+/** @file
+ Private header with declarations and definitions specific to the MM Standalone
+ CPU driver
+
+ Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MM_CPU_DRIVER_H_
+#define MM_CPU_DRIVER_H_
+
+#include <Protocol/MmCommunication2.h>
+#include <Protocol/MmConfiguration.h>
+#include <Protocol/MmCpu.h>
+#include <Protocol/PiMmCpuDriverEp.h>
+
+#include <Guid/MpInformation.h>
+
+//
+// CPU driver initialization specific declarations
+//
+extern EFI_MM_SYSTEM_TABLE *mMmst;
+
+//
+// CPU State Save protocol specific declarations
+//
+extern EFI_MM_CPU_PROTOCOL mMmCpuState;
+
+//
+// MM event handling specific declarations
+//
+extern EFI_MM_COMMUNICATE_HEADER *gGuidedEventContext;
+extern EFI_MM_CONFIGURATION_PROTOCOL mMmConfig;
+
+/**
+ The PI Standalone MM entry point for the CPU driver.
+
+ @param [in] EventId The event Id.
+ @param [in] CommBufferAddr Address of the communication buffer.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter was invalid.
+ @retval EFI_ACCESS_DENIED Access not permitted.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Operation not supported.
+**/
+EFI_STATUS
+PiMmStandaloneMmCpuDriverEntry (
+ IN UINTN EventId,
+ IN UINTN CommBufferAddr
+ );
+
+/**
+ This function is the main entry point for an MM handler dispatch
+ or communicate-based callback.
+
+ @param DispatchHandle The unique handle assigned to this handler by
+ MmiHandlerRegister().
+ @param Context 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-MM environment into an
+ MM environment.
+ @param CommBufferSize The size of the CommBuffer.
+
+ @return Status Code
+
+**/
+EFI_STATUS
+EFIAPI
+PiMmCpuTpFwRootMmiHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *Context OPTIONAL,
+ IN OUT VOID *CommBuffer OPTIONAL,
+ IN OUT UINTN *CommBufferSize OPTIONAL
+ );
+
+#endif /* MM_CPU_DRIVER_H_ */
diff --git a/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf b/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf
new file mode 100644
index 0000000000..dcf94d5778
--- /dev/null
+++ b/ArmPkg/Drivers/StandaloneMmCpu/StandaloneMmCpu.inf
@@ -0,0 +1,51 @@
+## @file
+# Standalone MM CPU driver
+#
+# Copyright (c) 2009, Apple Inc. All rights reserved.<BR>
+# Copyright (c) 2016 HP Development Company, L.P.
+# Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.
+# Copyright (c) 2023, Ventana Micro System Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = StandaloneMmCpu
+ FILE_GUID = 58F7A62B-6280-42A7-BC38-10535A64A92C
+ MODULE_TYPE = MM_STANDALONE
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x00010032
+ ENTRY_POINT = StandaloneMmCpuInitialize
+
+[Sources]
+ EventHandle.c
+ StandaloneMmCpu.c
+ StandaloneMmCpu.h
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ StandaloneMmPkg/StandaloneMmPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ HobLib
+ StandaloneMmDriverEntryPoint
+
+[Protocols]
+ gEfiMmConfigurationProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEfiMmCpuProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEdkiiPiMmCpuDriverEpProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+
+[Guids]
+ gEfiHobListGuid
+ gEfiMmPeiMmramMemoryReserveGuid
+ gZeroGuid
+ gMpInformationHobGuid
+ gEfiStandaloneMmNonSecureBufferGuid
+
+[Depex]
+ TRUE
diff --git a/ArmPkg/Include/Library/ArmStandaloneMmCoreEntryPoint.h b/ArmPkg/Include/Library/ArmStandaloneMmCoreEntryPoint.h
new file mode 100644
index 0000000000..1549211613
--- /dev/null
+++ b/ArmPkg/Include/Library/ArmStandaloneMmCoreEntryPoint.h
@@ -0,0 +1,294 @@
+/** @file
+ Entry point to the Standalone MM Foundation when initialized during the SEC
+ phase on ARM platforms
+
+ Copyright (c) 2017 - 2024, Arm Ltd. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - SPM_MM - An implementation where the Secure Partition Manager resides at EL3
+ with management services running from an isolated Secure Partitions
+ at S-EL0, and the communication protocol is the Management Mode(MM)
+ interface.
+ - FF-A - Firmware Framework for Arm A-profile
+ - TL - Transfer List
+
+ @par Reference(s):
+ - Transfer List [https://github.com/FirmwareHandoff/firmware_handoff]
+ - Secure Partition Manager [https://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager-mm.html].
+ - Arm Firmware Framework for Arm A-Profile [https://developer.arm.com/documentation/den0077/latest]
+
+**/
+
+#ifndef __STANDALONEMMCORE_ENTRY_POINT_H__
+#define __STANDALONEMMCORE_ENTRY_POINT_H__
+
+#include <Library/ArmSvcLib.h>
+#include <Library/ArmFfaLib.h>
+#include <Library/PeCoffLib.h>
+#include <Library/FvLib.h>
+
+#define CPU_INFO_FLAG_PRIMARY_CPU 0x00000001
+
+/*
+ * Index information for mm range descriptors in
+ * gEfiMmPeiMmramMemoryReserveGuid Guid Hob.
+ */
+#define MMRAM_DESC_IDX_IMAGE 0
+#define MMRAM_DESC_IDX_SECURE_SHARED_BUFFER 1
+#define MMRAM_DESC_IDX_NORMAL_SHARED_BUFFER 2
+#define MMRAM_DESC_IDX_HEAP 3
+#define MMRAM_DESC_MIN_COUNT 4
+
+/*
+ * Communication ABI protocol to communicate between normal/secure partition.
+ */
+typedef enum {
+ /// Unknown Communication ABI protocol
+ CommProtocolUnknown,
+
+ /// Communicate via SPM_MM ABI protocol
+ CommProtocolSpmMm,
+
+ /// Communicate via FF-A ABI protocol
+ CommProtocolFfa,
+
+ CommProtocolMax,
+} COMM_PROTOCOL;
+
+/** When using FF-A ABI, there're ways to request service to StandaloneMm
+ - FF-A with MmCommunication protocol.
+ - FF-A service with each specification.
+ MmCommunication Protocol can use FFA_MSG_SEND_DIRECT_REQ or REQ2,
+ Other FF-A services should use FFA_MSG_SEND_DIRECT_REQ2.
+ In case of FF-A with MmCommunication protocol via FFA_MSG_SEND_DIRECT_REQ,
+ register x3 saves Communication Buffer with gEfiMmCommunication2ProtocolGuid.
+ In case of FF-A with MmCommunication protocol via FFA_MSG_SEND_DIRECT_REQ2,
+ register x2/x3 save gEfiMmCommunication2ProtocolGuid and
+ register x4 saves Communication Buffer with Service Guid.
+
+ Other FF-A services (ServiceTypeMisc) delivers register values according to
+ there own service specification.
+ That means it doesn't use MmCommunication Buffer with MmCommunication Header
+ format.
+ (i.e) Tpm service via FF-A or Firmware Update service via FF-A.
+ To support latter services by StandaloneMm, it defines SERVICE_TYPE_MISC.
+ So that StandaloneMmEntryPointCore.c generates MmCommunication Header
+ with delivered register values to dispatch service provided StandaloneMmCore.
+ So that service handler can get proper information from delivered register.
+
+ In case of SPM_MM Abi, it only supports MmCommunication service.
+ */
+typedef enum {
+ /// Unknown
+ ServiceTypeUnknown,
+
+ /// MmCommunication services
+ ServiceTypeMmCommunication,
+
+ /// Misc services
+ ServiceTypeMisc,
+
+ ServiceTypeMax,
+} SERVICE_TYPE;
+
+/** Direct message request/response version
+ */
+typedef enum {
+ /// Direct message version 1. Use FFA_DIRECT_MSG_REQ/RESP
+ DirectMsgV1,
+
+ /// Direct message version 2. Use FFA_DIRECT_MSG_REQ2/RESP2
+ DirectMsgV2,
+
+ DirectMsgMax,
+} DIRECT_MSG_VERSION;
+
+/** Service table entry to return service type matched with service guid
+ */
+typedef struct ServiceTableEntry {
+ /// Service Guid
+ EFI_GUID *ServiceGuid;
+
+ /// Service Type
+ SERVICE_TYPE ServiceType;
+} SERVICE_TABLE_ENTRY;
+
+/** Ffa Abi data used in FFA_MSG_SEND_DIRECT_RESP/RESP2.
+ */
+typedef struct FfaMsgInfo {
+ /// Source partition id
+ UINT16 SourcePartId;
+
+ /// Destination partition id
+ UINT16 DestPartId;
+
+ /// Direct Message version
+ DIRECT_MSG_VERSION DirectMsgVersion;
+
+ /// Service Type
+ SERVICE_TYPE ServiceType;
+} FFA_MSG_INFO;
+
+/** MmCommunication Header for Misc service.
+ Misc service doesn't use MmCommunication Buffer.
+ This structure is used to dispatch Misc services by StandaloneMm.
+ */
+typedef struct {
+ /// Service guid
+ EFI_GUID HeaderGuid;
+
+ /// Length of Message. In case of misc service, sizeof (EventSvcArgs)
+ UINTN MessageLength;
+
+ /// Delivered register values.
+ DIRECT_MSG_ARGS DirectMsgArgs;
+} MISC_MM_COMMUNICATE_BUFFER;
+
+typedef struct {
+ UINT8 Type; /* type of the structure */
+ UINT8 Version; /* version of this structure */
+ UINT16 Size; /* size of this structure in bytes */
+ UINT32 Attr; /* attributes: unused bits SBZ */
+} EFI_PARAM_HEADER;
+
+typedef RETURN_STATUS (*REGION_PERMISSION_UPDATE_FUNC) (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length
+ );
+
+/**
+ Privileged firmware assigns RO & Executable attributes to all memory occupied
+ by the Boot Firmware Volume. This function sets the correct permissions of
+ sections in the Standalone MM Core module to be able to access RO and RW data
+ and make further progress in the boot process.
+
+ @param [in] ImageContext Pointer to PE/COFF image context
+ @param [in] ImageBase Base of image in memory
+ @param [in] SectionHeaderOffset Offset of PE/COFF image section header
+ @param [in] NumberOfSections Number of Sections
+ @param [in] TextUpdater Function to change code permissions
+ @param [in] ReadOnlyUpdater Function to change RO permissions
+ @param [in] ReadWriteUpdater Function to change RW permissions
+
+**/
+EFI_STATUS
+EFIAPI
+UpdateMmFoundationPeCoffPermissions (
+ IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
+ IN EFI_PHYSICAL_ADDRESS ImageBase,
+ IN UINT32 SectionHeaderOffset,
+ IN CONST UINT16 NumberOfSections,
+ IN REGION_PERMISSION_UPDATE_FUNC TextUpdater,
+ IN REGION_PERMISSION_UPDATE_FUNC ReadOnlyUpdater,
+ IN REGION_PERMISSION_UPDATE_FUNC ReadWriteUpdater
+ );
+
+/**
+ Privileged firmware assigns RO & Executable attributes to all memory occupied
+ by the Boot Firmware Volume. This function locates the section information of
+ the Standalone MM Core module to be able to change permissions of the
+ individual sections later in the boot process.
+
+ @param [in] TeData Pointer to PE/COFF image data
+ @param [in, out] ImageContext Pointer to PE/COFF image context
+ @param [out] ImageBase Pointer to ImageBase variable
+ @param [in, out] SectionHeaderOffset Offset of PE/COFF image section header
+ @param [in, out] NumberOfSections Number of Sections
+
+**/
+EFI_STATUS
+EFIAPI
+GetStandaloneMmCorePeCoffSections (
+ IN VOID *TeData,
+ IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
+ OUT EFI_PHYSICAL_ADDRESS *ImageBase,
+ IN OUT UINT32 *SectionHeaderOffset,
+ IN OUT UINT16 *NumberOfSections
+ );
+
+/**
+ Privileged firmware assigns RO & Executable attributes to all memory occupied
+ by the Boot Firmware Volume. This function locates the Standalone MM Core
+ module PE/COFF image in the BFV and returns this information.
+
+ @param [in] BfvAddress Base Address of Boot Firmware Volume
+ @param [in, out] TeData Pointer to address for allocating memory
+ for PE/COFF image data
+ @param [in, out] TeDataSize Pointer to size of PE/COFF image data
+
+**/
+EFI_STATUS
+EFIAPI
+LocateStandaloneMmCorePeCoffData (
+ IN EFI_FIRMWARE_VOLUME_HEADER *BfvAddress,
+ IN OUT VOID **TeData,
+ IN OUT UINTN *TeDataSize
+ );
+
+/**
+ The handoff between the SPMC to StandaloneMM depends on the
+ communication interface between the SPMC and StandaloneMM.
+ When SpmMM is used, the handoff is implemented using the
+ Firmware Handoff protocol. When FF-A is used the FF-A boot
+ protocol is used.
+
+ @param [in] Arg0 In case of FF-A, address of FF-A boot information
+ In case of SPM_MM, this parameter must be zero
+ @param [in] Arg1 In case of FF-A, this parameter must be zero
+ In case of SPM_MM, Signature and register convention version
+ @param [in] Arg2 Must be zero
+ @param [in] Arg3 In case of FF-A, this parameter must be zero
+ In case of SPM_MM, address of transfer list
+
+**/
+VOID
+EFIAPI
+CEntryPoint (
+ IN UINTN Arg0,
+ IN UINTN Arg1,
+ IN UINTN Arg2,
+ IN UINTN Arg3
+ );
+
+/**
+ Auto generated function that calls the library constructors for all of the module's dependent libraries.
+
+ This function must be called by CEntryPoint().
+ This function calls the set of library constructors for the set of library instances
+ that a module depends on. This includes library instances that a module depends on
+ directly and library instances that a module depends on indirectly through other
+ libraries. This function is auto generated by build tools and those build tools are
+ responsible for collecting the set of library instances, determine which ones have
+ constructors, and calling the library constructors in the proper order based upon
+ each of the library instances own dependencies.
+
+ @param ImageHandle The image handle of the DXE Core.
+ @param SystemTable A pointer to the EFI System Table.
+
+**/
+VOID
+EFIAPI
+ProcessLibraryConstructorList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_MM_SYSTEM_TABLE *MmSystemTable
+ );
+
+/**
+ Auto generated function that calls a set of module entry points.
+
+ This function must be called by CEntryPoint().
+ This function calls the set of module entry points.
+ This function is auto generated by build tools and those build tools are responsible
+ for collecting the module entry points and calling them in a specified order.
+
+ @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase.
+
+**/
+VOID
+EFIAPI
+ProcessModuleEntryPointList (
+ IN VOID *HobStart
+ );
+
+#endif
diff --git a/ArmPkg/Include/Protocol/PiMmCpuDriverEp.h b/ArmPkg/Include/Protocol/PiMmCpuDriverEp.h
new file mode 100644
index 0000000000..4b2f61fa06
--- /dev/null
+++ b/ArmPkg/Include/Protocol/PiMmCpuDriverEp.h
@@ -0,0 +1,60 @@
+/** @file
+ This file describes the PiMm Cpu Driver Entry Point Protocol.
+ the protocol is for defining handler for mm communication request event
+ according to cpu driver.
+
+ Copyright (c) 2024, Arm Limited. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PI_MM_CPU_DRIVER_EP_H_
+#define PI_MM_CPU_DRIVER_EP_H_
+
+#include <PiMm.h>
+
+#define EDKII_PI_MM_CPU_DRIVER_EP_GUID { \
+ 0x6ecbd5a1, 0xc0f8, 0x4702, { 0x83, 0x01, 0x4f, 0xc2, 0xc5, 0x47, 0x0a, 0x51 } \
+ }
+
+/**
+ The PI Standalone MM entry point for handling mm communication request
+ Here is an example of how the EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL
+ is utilized in ARM:
+ 1. StandaloneMmCoreEntryPoint loads StandaloneMmCore.
+ 2. StandaloneMmCore dispatches all MM drivers,
+ including the StandaloneMmCpu driver.
+ 3. The StandaloneMmCpu driver declares its MMI CPU entry point through
+ the PI_MM_CPU_DRIVER_EP_PROTOCOL.
+ 4. After all drivers have been dispatched,
+ StandaloneMmCoreEntryPoint retrieves the MMI CPU entry point
+ by locating the protocol.
+ 5. The DelegatedEventLoop then calls the MM CPU entry point.
+
+ See StandaloneMmPkg/StandaloneMmCoreEntryPoint/Arm/StandaloneMmCoreEntryPoint.c
+
+ @param [in] EventId The event Id based on firmware.
+ @param [in] CommBufferAddr Address of the communication buffer.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter was invalid.
+ @retval EFI_ACCESS_DENIED Access not permitted.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Operation not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_PI_MM_CPU_DRIVER_ENTRYPOINT)(
+ IN UINTN EventId,
+ IN UINTN CommBufferAddr
+ );
+
+typedef struct _EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL;
+
+struct _EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL {
+ EDKII_PI_MM_CPU_DRIVER_ENTRYPOINT PiMmCpuDriverEntryPoint;
+};
+
+extern EFI_GUID gEdkiiPiMmCpuDriverEpProtocolGuid;
+
+#endif
diff --git a/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/AArch64/ModuleEntryPoint.S b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/AArch64/ModuleEntryPoint.S
new file mode 100644
index 0000000000..f8f8877927
--- /dev/null
+++ b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/AArch64/ModuleEntryPoint.S
@@ -0,0 +1,231 @@
+#------------------------------------------------------------------------------
+#
+# Entrypoint of StandaloneMm.
+#
+# Copyright (c) 2024, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# @par Reference(s):
+# - [1] SPM based on the MM interface.
+# (https://trustedfirmware-a.readthedocs.io/en/latest/components/
+# secure-partition-manager-mm.html)
+# - [2] Arm Firmware Framework for Armv8-A, DEN0077, version 1.2
+# (https://developer.arm.com/documentation/den0077/latest/)
+# - [3] FF-A Memory Management Protocol for Armv8-A, DEN0140, version 1.2
+# (https://developer.arm.com/documentation/den0140/latest/)
+#
+#------------------------------------------------------------------------------
+
+#include <AArch64/AsmMacroLib.h>
+#include <IndustryStandard/ArmMmSvc.h>
+#include <IndustryStandard/ArmFfaSvc.h>
+#include <Uefi/UefiBaseType.h>
+
+.data
+.section .data.stmm_stack, "aw"
+.align 12
+// Define a data section to be used for setting up the
+// stack for StandaloneMm
+stmm_stack:
+.zero FixedPcdGet32 (PcdStMmStackSize)
+
+.text
+//
+// Check whether it is possible to use FF-A.
+// If FF-A is supported return TRUE, otherwise return FALSE.
+//
+// BOOLEAN
+// EFIAPI
+// CheckFfaSupport (
+// VOID
+// )
+//
+ASM_FUNC(CheckFfaSupport)
+ //
+ // Try to check FF-A support via FFA_VERSION
+ // See [2], Section 13.2 FFA_VERSION
+ //
+ MOV32 (x0, ARM_FID_FFA_VERSION)
+
+ // Set x1 as request version.
+ MOV32 (x1, ARM_FFA_CREATE_VERSION (
+ ARM_FFA_MAJOR_VERSION,
+ ARM_FFA_MINOR_VERSION))
+
+ svc #0
+
+ MOV64 (x9, ARM_FFA_RET_NOT_SUPPORTED)
+ cmp x0, x9
+ cset x0, ne
+ mov x9, xzr
+ ret
+
+//
+// Set write memory permission on StandaloneMm stack area via FF-A request.
+// If success, return StMmStackBaseAddr. otherwise return 0.
+//
+// UINTN
+// EFIAPI
+// SetStackPermissionFfa (
+// IN UINTN StMmStackTopAddr
+// )
+//
+ASM_FUNC(SetStackPermissionFfa)
+ //
+ // Try to set write permission on stmm_stack with FF-A request
+ // See [3], Section 2.9 FFA_MEM_PERM_SET
+ //
+ MOV32 (x2, FixedPcdGet32 (PcdStMmStackSize))
+
+ // x1 = stmm_stack top
+ mov x1, x0
+
+ // x12 = Compute and save the stack base
+ add x12, x1, x2
+
+ // x2 = Count of pages of stmm_stack
+ lsr x2, x2, #EFI_PAGE_SHIFT
+
+ // x3 = Memory permission
+ MOV32 (x3,
+ ARM_FFA_SET_MEM_ATTR_MAKE_PERM_REQUEST (
+ ARM_FFA_SET_MEM_ATTR_DATA_PERM_RW,
+ ARM_FFA_SET_MEM_ATTR_CODE_PERM_XN))
+
+ MOV32 (x0, ARM_FID_FFA_MEM_PERM_SET)
+
+ // Call FFA_MEM_PERM_SET to set stmm_stack with write permission
+ // See [3], Section 2.9 FFA_MEM_PERM_SET
+ svc #0
+
+ // Check FFA_MEM_PERM_SET operation is success.
+ mov x10, #0x00
+ MOV32 (x11, ARM_FID_FFA_SUCCESS_AARCH64)
+ cmp x0, x11
+ cinc x10, x10, eq
+ MOV32 (x11, ARM_FID_FFA_SUCCESS_AARCH32)
+ cmp x0, x11
+ cinc x10, x10, eq
+ cmp x10, #0x00
+
+ // Set return value as base address of stack.
+ mov x0, x12
+ b.ne .Lout_set_stack_perm_ffa
+ // If failed, set return value as zero.
+ mov x0, #0x00
+
+.Lout_set_stack_perm_ffa:
+ mov x9, xzr
+ mov x10, xzr
+ mov x11, xzr
+ mov x12, xzr
+ ret
+
+//
+// Set write memory permission on StandaloneMm stack area via SpmMm.
+// If success, return StMmStackTopAddr. otherwise return 0.
+//
+// UINTN
+// EFIAPI
+// SetStackPermissionSpmMm (
+// IN UINTN StMmStackTopAddr
+// )
+//
+ASM_FUNC(SetStackPermissionSpmMm)
+ //
+ // Try to set write permission on stmm_stack with SPM_MM request
+ // See [1], Section 4.16.5.5.1 MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64.
+ //
+ MOV32 (x2, FixedPcdGet32 (PcdStMmStackSize))
+
+ // x1 = stmm_stack top
+ mov x1, x0
+
+ // x12 = Compute and save the stack base
+ add x12, x1, x2
+
+ // x2 = Count of pages of stmm_stack
+ lsr x2, x2, #EFI_PAGE_SHIFT
+
+ // x3 = Memory permission
+ MOV32 (x3,
+ ARM_SPM_MM_SET_MEM_ATTR_MAKE_PERM_REQUEST (
+ ARM_SPM_MM_SET_MEM_ATTR_DATA_PERM_RW,
+ ARM_SPM_MM_SET_MEM_ATTR_CODE_PERM_XN))
+
+ MOV32 (x0, ARM_FID_SPM_MM_SP_SET_MEM_ATTRIBUTES)
+
+ // Call SPM_MM_SP_SET_MEM_ATTRIBUTES to set stmm_stack with write permission
+ // See [1], Section 4.16.5.5.1 MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64.
+ svc #0
+
+ cmp x0, #ARM_SPM_MM_RET_SUCCESS
+
+ // Set return value as base address of stack.
+ mov x0, x12
+ b.eq .Lout_set_stack_perm
+ // If failed, set return value as zero.
+ mov x0, #0x00
+
+.Lout_set_stack_perm:
+ mov x9, xzr
+ mov x12, xzr
+ ret
+
+//
+// Entry point of StandaloneMm
+//
+ASM_FUNC(_ModuleEntryPoint)
+ // Stash boot information registers from the SPMC
+ mov x19, x0
+ mov x20, x1
+ mov x21, x2
+ mov x22, x3
+ mov x23, x4
+
+ bl CheckFfaSupport
+ mov x1, x0
+
+ // Get StandaloneMm Stack top address and save in x0
+ adrp x4, stmm_stack
+ mov x0, x4
+
+ // Set stack permission
+ cmp x1, #0x01
+ b.eq .Lset_stack_perm_ffa
+ b.ne .Lset_stack_perm_spm
+
+ // If SetStackPermission* failed, x0 is #0x00.
+ // Otherwise, x0 is base address of stack.
+.Lset_stmm_sp:
+ cmp x0, #0x00
+ b.eq .Lerror
+
+ mov sp, x0
+
+ // Restore boot information registers from the SPMC
+ mov x0, x19
+ mov x1, x20
+ mov x2, x21
+ mov x3, x22
+ mov x4, x23
+ mov x19, xzr
+ mov x20, xzr
+ mov x21, xzr
+ mov x22, xzr
+ mov x23, xzr
+
+ // Invoke the C entrypoint
+ b CEntryPoint
+
+.Lerror:
+ b .
+
+.Lset_stack_perm_ffa:
+ bl SetStackPermissionFfa
+ b .Lset_stmm_sp
+
+.Lset_stack_perm_spm:
+ bl SetStackPermissionSpmMm
+ b .Lset_stmm_sp
diff --git a/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/Arm/ModuleEntryPoint.S b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/Arm/ModuleEntryPoint.S
new file mode 100644
index 0000000000..28a9e72e13
--- /dev/null
+++ b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/Arm/ModuleEntryPoint.S
@@ -0,0 +1,220 @@
+#------------------------------------------------------------------------------
+#
+# Entrypoint of StandaloneMm.
+#
+# Copyright (c) 2024, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# @par Reference(s):
+# - [1] SPM based on the MM interface.
+# (https://trustedfirmware-a.readthedocs.io/en/latest/components/
+# secure-partition-manager-mm.html)
+# - [2] Arm Firmware Framework for Armv8-A, DEN0077, version 1.2
+# (https://developer.arm.com/documentation/den0077/latest/)
+# - [3] FF-A Memory Management Protocol for Armv8-A, DEN0140, version 1.2
+# (https://developer.arm.com/documentation/den0140/latest/)
+#
+#------------------------------------------------------------------------------
+
+#include <Arm/AsmMacroLib.h>
+#include <IndustryStandard/ArmMmSvc.h>
+#include <IndustryStandard/ArmFfaSvc.h>
+#include <Uefi/UefiBaseType.h>
+
+.data
+.section .data.stmm_stack, "aw"
+.align 12
+// Define a data section to be used for setting up the
+// stack for StandaloneMm
+stmm_stack:
+.zero FixedPcdGet32 (PcdStMmStackSize)
+
+.text
+//
+// Check whether it is possible to use FF-A.
+// If FF-A can use, return TRUE. otherwise return FALSE.
+//
+// BOOLEAN
+// EFIAPI
+// CheckFfaSupport (
+// VOID
+// )
+//
+ASM_FUNC(CheckFfaSupport)
+ //
+ // Try to check FF-A support via FFA_VERSION
+ // See [2], Section 13.2 FFA_VERSION
+ //
+ MOV32 (r0, ARM_FID_FFA_VERSION)
+
+ // Set r1 as request version.
+ MOV32 (r1, ARM_FFA_CREATE_VERSION (
+ ARM_FFA_MAJOR_VERSION,
+ ARM_FFA_MINOR_VERSION))
+
+ svc #0
+
+ // Set r4 as ARM_FFA_RET_NOT_SUPPORTED (-1)
+ mvn r4, #0x00
+
+ cmp r0, r4
+ movne r0, #0x01
+ moveq r0, #0x00
+ mov r4, #0x00
+ bx lr
+
+//
+// Set write memory permission on StandaloneMm stack area via FF-A request.
+// If success, return StMmStackBaseAddr. otherwise return 0.
+//
+// UINTN
+// EFIAPI
+// SetStackPermissionFfa (
+// IN UINTN StMmStackTopAddr
+// )
+//
+ASM_FUNC(SetStackPermissionFfa)
+ //
+ // Try to set write permission on stmm_stack with FF-A request
+ // See [3], Section 2.9 FFA_MEM_PERM_SET
+ //
+ MOV32 (r2, FixedPcdGet32 (PcdStMmStackSize))
+
+ // r1 = stmm_stack top
+ mov r1, r0
+
+ // r12 = Compute and save the stack base
+ add r12, r1, r2
+
+ // r2 = Count of pages of stmm_stack
+ lsr r2, r2, #EFI_PAGE_SHIFT
+
+ // r3 = Memory permission
+ MOV32 (r3,
+ ARM_FFA_SET_MEM_ATTR_MAKE_PERM_REQUEST (
+ ARM_FFA_SET_MEM_ATTR_DATA_PERM_RW,
+ ARM_FFA_SET_MEM_ATTR_CODE_PERM_XN))
+
+ MOV32 (r0, ARM_FID_FFA_MEM_PERM_SET)
+
+ // Call FFA_MEM_PERM_SET to set stmm_stack with write permission
+ // See [3], Section 2.9 FFA_MEM_PERM_SET
+ svc #0
+
+ // Check FFA_MEM_PERM_SET operation is success.
+ MOV32 (r5, ARM_FID_FFA_SUCCESS_AARCH32)
+ cmp r0, r5
+
+ // Set return value as base address of stack.
+ mov r0, r12
+ bne .Lout_set_stack_perm_ffa
+ // If failed, set return value as zero.
+ mov r0, #0x00
+
+.Lout_set_stack_perm_ffa:
+ // Initialise SP with temp stack
+ mov r5, #0x00
+ mov r12, #0x00
+ bx lr
+
+//
+// Set write memory permission on StandaloneMm stack area via SpmMm.
+// If success, return StMmStackTopAddr. otherwise return 0.
+//
+// UINTN
+// EFIAPI
+// SetStackPermissionSpmMm (
+// IN UINTN StMmStackTopAddr
+// )
+//
+ASM_FUNC(SetStackPermissionSpmMm)
+ //
+ // Try to set write permission on stmm_stack with SPM_MM request
+ // See [1], Section 4.16.5.5.1 MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64.
+ //
+ MOV32 (r2, FixedPcdGet32 (PcdStMmStackSize))
+
+ // r1 = stmm_stack top
+ mov r1, r0
+
+ // r12 = Compute and save the stack base
+ add r12, r1, r2
+
+ // r2 = Count of pages of stmm_stack
+ lsr r2, r2, #EFI_PAGE_SHIFT
+
+ // r3 = Memory permission
+ MOV32 (r3,
+ ARM_SPM_MM_SET_MEM_ATTR_MAKE_PERM_REQUEST (
+ ARM_SPM_MM_SET_MEM_ATTR_DATA_PERM_RW,
+ ARM_SPM_MM_SET_MEM_ATTR_CODE_PERM_XN))
+
+ MOV32 (r0, ARM_FID_SPM_MM_SP_SET_MEM_ATTRIBUTES)
+
+ // Call SPM_MM_SP_SET_MEM_ATTRIBUTES to set stmm_stack with write permission
+ // See [1], Section 4.16.5.5.1 MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64.
+ svc #0
+
+ MOV32 (r5, ARM_SPM_MM_RET_SUCCESS)
+ cmp r0, r5
+
+ // Set return value as base address of stack.
+ mov r0, r12
+ beq .Lout_set_stack_perm
+ // If failed, set return value as zero.
+ mov r0, #0x00
+
+.Lout_set_stack_perm:
+ mov r5, #0x00
+ mov r12, #0x00
+ bx lr
+
+//
+// Entry point of StandaloneMm
+//
+ASM_FUNC(_ModuleEntryPoint)
+ // Stash boot information registers from the SPMC
+ mov r8, r0
+ mov r9, r1
+ mov r10, r2
+ mov r11, r3
+
+ bl CheckFfaSupport
+ mov r1, r0
+
+ // Get StandaloneMm Stack top address and save in x0
+ LDRL(r0, stmm_stack)
+
+ // Set stack permission
+ cmp r1, #0x01
+ beq .Lset_stack_perm_ffa
+ bne .Lset_stack_perm_spm
+
+ // If SetStackPermission* failed, x0 is #0x00.
+ // Otherwise, x0 is base address of stack.
+.Lset_stmm_sp:
+ cmp r0, #0x00
+ bne .Lerror
+
+ mov sp, r0
+
+ // Restore boot information registers from the SPMC
+ mov r3, r11
+ mov r2, r10
+ mov r1, r9
+ mov r0, r8
+
+ // Invoke the C entrypoint
+ b CEntryPoint
+
+.Lerror:
+ b .
+
+.Lset_stack_perm_ffa:
+ bl SetStackPermissionFfa
+ b .Lset_stmm_sp
+
+.Lset_stack_perm_spm:
+ bl SetStackPermissionSpmMm
+ b .Lset_stmm_sp
diff --git a/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.c b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.c
new file mode 100644
index 0000000000..0d60a491ee
--- /dev/null
+++ b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.c
@@ -0,0 +1,1223 @@
+/** @file
+ Entry point to the Standalone MM Foundation when initialized during the SEC
+ phase on ARM platforms
+
+ Copyright (c) 2017 - 2024, Arm Ltd. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - SpmMM - An implementation where the Secure Partition Manager resides at EL3
+ with management services running from an isolated Secure Partitions
+ at S-EL0, and the communication protocol is the Management Mode(MM)
+ interface.
+ - FF-A - Firmware Framework for Arm A-profile
+
+ @par Reference(s):
+ - Secure Partition Manager
+ [https://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager-mm.html]
+ - Arm Firmware Framework for Arm A-Profile
+ [https://developer.arm.com/documentation/den0077/latest]
+
+**/
+
+#include <PiMm.h>
+
+#include <PiPei.h>
+#include <Guid/MmramMemoryReserve.h>
+
+#include <Library/ArmLib.h>
+#include <Library/ArmStandaloneMmCoreEntryPoint.h>
+#include <Library/ArmSvcLib.h>
+#include <Library/ArmFfaLib.h>
+#include <Library/ArmTransferListLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/StandaloneMmMmuLib.h>
+#include <Library/PcdLib.h>
+
+#include <IndustryStandard/ArmStdSmc.h>
+#include <IndustryStandard/ArmMmSvc.h>
+#include <IndustryStandard/ArmFfaSvc.h>
+#include <IndustryStandard/ArmFfaBootInfo.h>
+
+#include <Protocol/PiMmCpuDriverEp.h>
+#include <Protocol/MmCommunication.h>
+
+extern EFI_MM_SYSTEM_TABLE gMmCoreMmst;
+
+VOID *gHobList = NULL;
+
+STATIC MISC_MM_COMMUNICATE_BUFFER *mMiscMmCommunicateBuffer = NULL;
+STATIC EFI_MMRAM_DESCRIPTOR *mNsCommBuffer = NULL;
+STATIC EFI_MMRAM_DESCRIPTOR *mSCommBuffer = NULL;
+
+/**
+ Get communication ABI protocol.
+
+ @param [out] CommProtocol Communication protocol.
+
+ @retval EFI_SUCCESS
+ @retval EFI_UNSUPPORTED Not supported
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetCommProtocol (
+ OUT COMM_PROTOCOL *CommProtocol
+ )
+{
+ EFI_STATUS Status;
+ UINT16 RequestMajorVersion;
+ UINT16 RequestMinorVersion;
+ UINT16 CurrentMajorVersion;
+ UINT16 CurrentMinorVersion;
+ ARM_SVC_ARGS SvcArgs;
+
+ RequestMajorVersion = ARM_FFA_MAJOR_VERSION;
+ RequestMinorVersion = ARM_FFA_MINOR_VERSION;
+
+ Status = ArmFfaLibGetVersion (
+ RequestMajorVersion,
+ RequestMinorVersion,
+ &CurrentMajorVersion,
+ &CurrentMinorVersion
+ );
+ if (!EFI_ERROR (Status)) {
+ *CommProtocol = CommProtocolFfa;
+ } else {
+ ZeroMem (&SvcArgs, sizeof (ARM_SVC_ARGS));
+ SvcArgs.Arg0 = ARM_FID_SPM_MM_VERSION_AARCH32;
+
+ ArmCallSvc (&SvcArgs);
+
+ if (SvcArgs.Arg0 == ARM_SPM_MM_RET_NOT_SUPPORTED) {
+ *CommProtocol = CommProtocolUnknown;
+ return EFI_UNSUPPORTED;
+ }
+
+ *CommProtocol = CommProtocolSpmMm;
+ RequestMajorVersion = ARM_SPM_MM_SUPPORT_MAJOR_VERSION;
+ RequestMinorVersion = ARM_SPM_MM_SUPPORT_MINOR_VERSION;
+ CurrentMajorVersion =
+ ((SvcArgs.Arg0 >> ARM_SPM_MM_MAJOR_VERSION_SHIFT) & ARM_SPM_MM_VERSION_MASK);
+ CurrentMinorVersion =
+ ((SvcArgs.Arg0 >> ARM_SPM_MM_MINOR_VERSION_SHIFT) & ARM_SPM_MM_VERSION_MASK);
+ }
+
+ // Different major revision values indicate possibly incompatible functions.
+ // For two revisions, A and B, for which the major revision values are
+ // identical, if the minor revision value of revision B is greater than
+ // the minor revision value of revision A, then every function in
+ // revision A must work in a compatible way with revision B.
+ // However, it is possible for revision B to have a higher
+ // function count than revision A
+ if ((RequestMajorVersion != CurrentMajorVersion) ||
+ (RequestMinorVersion > CurrentMinorVersion))
+ {
+ DEBUG ((
+ DEBUG_INFO,
+ "Incompatible %s Versions.\n" \
+ "Request Version: Major=0x%x, Minor>=0x%x.\n" \
+ "Current Version: Major=0x%x, Minor=0x%x.\n",
+ (*CommProtocol == CommProtocolFfa) ? L"FF-A" : L"SPM_MM",
+ RequestMajorVersion,
+ RequestMinorVersion,
+ CurrentMajorVersion,
+ CurrentMinorVersion
+ ));
+
+ return EFI_UNSUPPORTED;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%s Version: Major=0x%x, Minor=0x%x\n",
+ (*CommProtocol == CommProtocolFfa) ? L"FF-A" : L"SPM_MM",
+ CurrentMajorVersion,
+ CurrentMinorVersion
+ ));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validate Boot information when using SpmMm.
+ It should use Transfer list according to firmware handoff specification.
+
+ @param [in] Arg0 Should be 0x00 because we don't use device tree
+ @param [in] Fields Signature and register convention version
+ @param [in] Arg2 Should be 0x00 because we don't use device tree
+ @param [in] TlhAddr Address of transfer list
+
+ @retval EFI_SUCCESS Valid boot information
+ @retval EFI_INVALID_PARAMETER Invalid boot information
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ValidateSpmMmBootInfo (
+ IN UINTN Arg0,
+ IN UINT64 Fields,
+ IN UINTN Arg2,
+ IN UINTN TlhAddr
+ )
+{
+ UINT64 RegVersion;
+
+ /*
+ * The signature value in x1's [23:0] bits is the same regardless of
+ * architecture when using Transfer list.
+ * That's why it need to check signature value in x1 again with [31:0] bits
+ * to discern 32 or 64 bits architecture after checking x1 value in [23:0].
+ * Please see:
+ * https://github.com/FirmwareHandoff/firmware_handoff/blob/main/source/register_conventions.rst
+ */
+ if ((Fields & TRANSFER_LIST_SIGNATURE_MASK_64) == TRANSFER_LIST_SIGNATURE_64) {
+ RegVersion = (Fields >> REGISTER_CONVENTION_VERSION_SHIFT_64) &
+ REGISTER_CONVENTION_VERSION_MASK;
+ if ((RegVersion != 1) || (Arg2 != 0x00) || (TlhAddr == 0x00)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else if ((Fields & TRANSFER_LIST_SIGNATURE_MASK_32) == TRANSFER_LIST_SIGNATURE_32) {
+ RegVersion = (Fields >> REGISTER_CONVENTION_VERSION_SHIFT_32) &
+ REGISTER_CONVENTION_VERSION_MASK;
+ if ((RegVersion != 1) || (Arg0 != 0x00) || (TlhAddr == 0x00)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validate Boot information when using FF-A.
+
+ @param [in] FfaBootInfoHeaderAddr Address of FF-A boot information
+
+ @retval EFI_SUCCESS Valid boot information
+ @retval EFI_INVALID_PARAMETER Invalid boot information
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ValidateFfaBootInfo (
+ IN UINTN FfaBootInfoHeaderAddr
+ )
+{
+ EFI_FFA_BOOT_INFO_HEADER *FfaBootInfoHeader;
+
+ FfaBootInfoHeader = (EFI_FFA_BOOT_INFO_HEADER *)FfaBootInfoHeaderAddr;
+ if (FfaBootInfoHeader == NULL) {
+ DEBUG ((DEBUG_ERROR, "Error: No FF-A boot information...\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((FfaBootInfoHeader->Magic != FFA_BOOT_INFO_SIGNATURE) ||
+ (ARM_FFA_MAJOR_VERSION_GET (FfaBootInfoHeader->Version) <
+ ARM_FFA_MAJOR_VERSION) ||
+ (ARM_FFA_MINOR_VERSION_GET (FfaBootInfoHeader->Version) <
+ ARM_FFA_MINOR_VERSION))
+ {
+ DEBUG ((DEBUG_ERROR, "Error: Invalid FF-A boot information...\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validate Boot information.
+
+ @param [in] CommProtocol Communication ABI protocol
+ @param [in] Arg0 In case of FF-A, address of FF-A boot information
+ In case of SPM_MM, 0x00
+ @param [in] Arg1 In case of FF-A, 0x00
+ In case of SPM_MM, Signature and register convention version
+ @param [in] Arg2 should be 0x00
+ @param [in] Arg3 In case of FF-A, it's 0x00
+ In case of SPM_MM, address of transfer list
+
+ @retval EFI_SUCCESS Valid boot information
+ @retval EFI_INVALID_PARAMETER Invalid boot information
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ValidateBootInfo (
+ IN COMM_PROTOCOL CommProtocol,
+ IN UINTN Arg0,
+ IN UINTN Arg1,
+ IN UINTN Arg2,
+ IN UINTN Arg3
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_INVALID_PARAMETER;
+
+ if (CommProtocol == CommProtocolSpmMm) {
+ Status = ValidateSpmMmBootInfo (Arg0, Arg1, Arg2, Arg3);
+ } else if (CommProtocol == CommProtocolFfa) {
+ /*
+ * In case of FF-A, Arg0 is set as FFA_BOOT_INFO address.
+ */
+ Status = ValidateFfaBootInfo (Arg0);
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Error: Failed to validate boot information!\n"));
+ }
+
+ return Status;
+}
+
+/**
+ Get PHIT hob information from firmware handoff transfer list protocol.
+
+ @param[in] TlhAddr Transfer list header address
+
+ @retval NULL Failed to get PHIT hob
+ @retval Address PHIT hob address
+
+**/
+STATIC
+VOID *
+EFIAPI
+GetPhitHobFromTransferList (
+ IN UINTN TlhAddr
+ )
+{
+ TRANSFER_LIST_HEADER *Tlh;
+ TRANSFER_ENTRY_HEADER *Te;
+ VOID *HobStart;
+
+ Tlh = (TRANSFER_LIST_HEADER *)TlhAddr;
+
+ Te = TlFindFirstEntry (Tlh, TRANSFER_ENTRY_TAG_ID_HOB_LIST);
+ if (Te == NULL) {
+ DEBUG ((DEBUG_ERROR, "Error: No Phit hob is present in transfer list...\n"));
+
+ return NULL;
+ }
+
+ HobStart = TlGetEntryData (Te);
+
+ return HobStart;
+}
+
+/**
+ Get PHIT hob information from FF-A boot information.
+
+ @param[in] FfaBootInfoHeaderAddr FF-A boot information header address
+
+ @retval NULL Failed to get PHIT hob
+ @retval Address PHIT hob address
+
+**/
+STATIC
+VOID *
+EFIAPI
+GetPhitHobFromFfaBootInfo (
+ IN UINTN FfaBootInfoHeaderAddr
+ )
+{
+ EFI_FFA_BOOT_INFO_HEADER *FfaBootInfoHeader;
+ EFI_FFA_BOOT_INFO_DESC *FfaBootInfoDesc;
+ UINT32 Idx;
+
+ FfaBootInfoHeader = (EFI_FFA_BOOT_INFO_HEADER *)FfaBootInfoHeaderAddr;
+
+ for (Idx = 0; Idx < FfaBootInfoHeader->CountBootInfoDesc; Idx++) {
+ FfaBootInfoDesc = (EFI_FFA_BOOT_INFO_DESC *)((FfaBootInfoHeaderAddr +
+ FfaBootInfoHeader->OffsetBootInfoDesc) +
+ (Idx * FfaBootInfoHeader->SizeBootInfoDesc));
+
+ if ((FFA_BOOT_INFO_TYPE (FfaBootInfoDesc->Type) != FFA_BOOT_INFO_TYPE_STD) ||
+ (FFA_BOOT_INFO_TYPE_ID (FfaBootInfoDesc->Type) != FFA_BOOT_INFO_TYPE_ID_HOB) ||
+ (FFA_BOOT_INFO_FLAG_CONTENT (FfaBootInfoDesc->Flags) != FFA_BOOT_INFO_FLAG_CONTENT_ADDR))
+ {
+ continue;
+ }
+
+ return (VOID *)(UINTN)FfaBootInfoDesc->Content;
+ }
+
+ DEBUG ((DEBUG_ERROR, "Error: No Phit hob is present in FfaBootInfo...\n"));
+
+ return NULL;
+}
+
+/**
+ Get PHIT Hob from Boot information.
+
+ @param [in] CommProtocol Communication ABI protocol
+ @param [in] Arg0 In case of FF-A, address of FF-A boot information
+ In case of SPM_MM, 0x00 because we don't use device tree.
+ @param [in] Arg1 In case of FF-A, 0x00
+ In case of SPM_MM, Signature and register convention version
+ @param [in] Arg2 Should be 0x00 because we don't use device tree.
+ @param [in] Arg3 In case of FF-A, it's 0x00
+ In case of SPM_MM, address of transfer list
+
+ @retval NULL Failed to get PHIT hob
+ @retval Address PHIT hob address
+
+**/
+STATIC
+VOID *
+EFIAPI
+GetPhitHobFromBootInfo (
+ IN COMM_PROTOCOL CommProtocol,
+ IN UINTN Arg0,
+ IN UINTN Arg1,
+ IN UINTN Arg2,
+ IN UINTN Arg3
+ )
+{
+ EFI_STATUS Status;
+ VOID *HobStart;
+
+ Status = ValidateBootInfo (CommProtocol, Arg0, Arg1, Arg2, Arg3);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ if (CommProtocol == CommProtocolFfa) {
+ HobStart = GetPhitHobFromFfaBootInfo (Arg0);
+ } else {
+ HobStart = GetPhitHobFromTransferList (Arg3);
+ }
+
+ return HobStart;
+}
+
+/**
+ Get service type.
+ When using FF-A ABI, there're ways to request service to StandaloneMm
+ - FF-A with MmCommunication protocol.
+ - FF-A service with each specification.
+ MmCommunication Protocol can use FFA_MSG_SEND_DIRECT_REQ or REQ2,
+ Other FF-A services should use FFA_MSG_SEND_DIRECT_REQ2.
+ In case of FF-A with MmCommunication protocol via FFA_MSG_SEND_DIRECT_REQ,
+ register x3 saves Communication Buffer with gEfiMmCommunication2ProtocolGuid.
+ In case of FF-A with MmCommunication protocol via FFA_MSG_SEND_DIRECT_REQ2,
+ register x2/x3 save gEfiMmCommunication2ProtocolGuid and
+ register x4 saves Communication Buffer with Service Guid.
+
+ Other FF-A services (ServiceTypeMisc) delivers register values according to
+ there own service specification.
+ That means it doesn't use MmCommunication Buffer with MmCommunication Header
+ format.
+ (i.e) Tpm service via FF-A or Firmware Update service via FF-A.
+ To support latter services by StandaloneMm, it defines SERVICE_TYPE_MISC.
+ So that StandaloneMmEntryPointCore.c generates MmCommunication Header
+ with delivered register values to dispatch service provided StandaloneMmCore.
+ So that service handler can get proper information from delivered register.
+
+ In case of SPM_MM Abi, it only supports MmCommunication service.
+
+
+ @param[in] ServiceGuid Service Guid
+
+ @retval ServiceTypeMmCommunication Mm communication service
+ @retval ServiceTypeMisc Service via implemented defined
+ register ABI.
+ This will generate internal
+ MmCommunication Header
+ to dispatch service implemented
+ in standaloneMm
+ @retval ServiceTypeUnknown Not supported service.
+
+**/
+STATIC
+SERVICE_TYPE
+EFIAPI
+GetServiceType (
+ IN EFI_GUID *ServiceGuid
+ )
+{
+ if (CompareGuid (ServiceGuid, &gEfiMmCommunication2ProtocolGuid)) {
+ return ServiceTypeMmCommunication;
+ }
+
+ return ServiceTypeMisc;
+}
+
+/**
+ Perform bounds check for the Ns and Secure Communication buffer.
+
+ NOTE: We do not need to validate the Misc Communication buffer as
+ we are initialising that in StandaloneMm.
+
+ @param [in] CommBufferAddr Address of the common buffer.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_ACCESS_DENIED Access not permitted.
+**/
+STATIC
+EFI_STATUS
+ValidateMmCommBufferAddr (
+ IN UINTN CommBufferAddr
+ )
+{
+ UINT64 NsCommBufferEnd;
+ UINT64 SCommBufferEnd;
+ UINT64 CommBufferEnd;
+ UINT64 CommBufferRange;
+
+ NsCommBufferEnd = mNsCommBuffer->PhysicalStart + mNsCommBuffer->PhysicalSize;
+ SCommBufferEnd = mSCommBuffer->PhysicalStart + mSCommBuffer->PhysicalSize;
+
+ if ((CommBufferAddr >= mNsCommBuffer->PhysicalStart) &&
+ (CommBufferAddr < NsCommBufferEnd))
+ {
+ CommBufferEnd = NsCommBufferEnd;
+ } else if ((CommBufferAddr >= mSCommBuffer->PhysicalStart) &&
+ (CommBufferAddr < SCommBufferEnd))
+ {
+ CommBufferEnd = SCommBufferEnd;
+ } else {
+ return EFI_ACCESS_DENIED;
+ }
+
+ CommBufferRange = CommBufferEnd - CommBufferAddr;
+
+ if (CommBufferRange < sizeof (EFI_MM_COMMUNICATE_HEADER)) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ // perform bounds check.
+ if (((CommBufferAddr + sizeof (EFI_MM_COMMUNICATE_HEADER) +
+ ((EFI_MM_COMMUNICATE_HEADER *)CommBufferAddr)->MessageLength)) >
+ CommBufferEnd)
+ {
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Dump mmram descriptor.
+
+ @param[in] Name Name
+ @param[in] MmramDesc Mmram descriptor
+
+**/
+STATIC
+VOID
+EFIAPI
+DumpMmramDescriptor (
+ IN CHAR16 *Name,
+ IN EFI_MMRAM_DESCRIPTOR *MmramDesc
+ )
+{
+ if (MmramDesc == NULL) {
+ return;
+ }
+
+ if (Name == NULL) {
+ Name = L"Unknown";
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "MmramDescriptor[%s]: PhysicalStart - 0x%lx\n",
+ Name,
+ MmramDesc->PhysicalStart
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "MmramDescriptors[%s]: CpuStart - 0x%lx\n",
+ Name,
+ MmramDesc->CpuStart
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "MmramDescriptors[%s]: PhysicalSize - %ld\n",
+ Name,
+ MmramDesc->PhysicalSize
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "MmramDescriptors[%s]: RegionState - 0x%lx\n",
+ Name,
+ MmramDesc->RegionState
+ ));
+}
+
+/**
+ Dump PHIT hob information.
+
+ @param[in] HobStart PHIT hob start address.
+
+**/
+STATIC
+VOID
+EFIAPI
+DumpPhitHob (
+ IN VOID *HobStart
+ )
+{
+ EFI_HOB_FIRMWARE_VOLUME *FvHob;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *MmramRangesHobData;
+ EFI_MMRAM_DESCRIPTOR *MmramDesc;
+ UINTN Idx;
+
+ FvHob = GetNextHob (EFI_HOB_TYPE_FV, HobStart);
+ if (FvHob == NULL) {
+ DEBUG ((DEBUG_ERROR, "Error: No Firmware Volume Hob is present.\n"));
+ return;
+ }
+
+ DEBUG ((DEBUG_INFO, "FvHob: BaseAddress - 0x%lx\n", FvHob->BaseAddress));
+ DEBUG ((DEBUG_INFO, "FvHob: Length - %ld\n", FvHob->Length));
+
+ GuidHob = GetNextGuidHob (&gEfiStandaloneMmNonSecureBufferGuid, HobStart);
+ if (GuidHob == NULL) {
+ DEBUG ((DEBUG_ERROR, "Error: No Ns Buffer Guid Hob is present.\n"));
+ return;
+ }
+
+ DumpMmramDescriptor (L"NsBuffer", GET_GUID_HOB_DATA (GuidHob));
+
+ GuidHob = GetNextGuidHob (&gEfiMmPeiMmramMemoryReserveGuid, HobStart);
+ if (GuidHob == NULL) {
+ DEBUG ((DEBUG_ERROR, "Error: No Pei Mmram Memory Reserved Guid Hob is present.\n"));
+ return;
+ }
+
+ MmramRangesHobData = GET_GUID_HOB_DATA (GuidHob);
+ if ((MmramRangesHobData == NULL) ||
+ (MmramRangesHobData->NumberOfMmReservedRegions == 0))
+ {
+ DEBUG ((DEBUG_ERROR, "Error: No Pei Mmram Memory Reserved information is present.\n"));
+ return;
+ }
+
+ for (Idx = 0; Idx < MmramRangesHobData->NumberOfMmReservedRegions; Idx++) {
+ MmramDesc = &MmramRangesHobData->Descriptor[Idx];
+ DumpMmramDescriptor (L"PeiMemReserved", MmramDesc);
+ }
+}
+
+/**
+ Convert EFI_STATUS to MM SPM return code.
+
+ @param [in] Status edk2 status code.
+
+ @retval ARM_SPM_MM_RET_* return value correspond to EFI_STATUS.
+
+**/
+STATIC
+UINTN
+EFIAPI
+EfiStatusToSpmMmStatus (
+ IN EFI_STATUS Status
+ )
+{
+ switch (Status) {
+ case EFI_SUCCESS:
+ return ARM_SPM_MM_RET_SUCCESS;
+ case EFI_INVALID_PARAMETER:
+ return ARM_SPM_MM_RET_INVALID_PARAMS;
+ case EFI_ACCESS_DENIED:
+ return ARM_SPM_MM_RET_DENIED;
+ case EFI_OUT_OF_RESOURCES:
+ return ARM_SPM_MM_RET_NO_MEMORY;
+ default:
+ return ARM_SPM_MM_RET_NOT_SUPPORTED;
+ }
+}
+
+/**
+ Set svc arguments to report initialization status of StandaloneMm.
+
+ @param[in] CommProtocol ABI Protocol.
+ @param[in] Status Result of initializing StandaloneMm.
+ @param[out] EventCompleteSvcArgs Args structure.
+
+**/
+STATIC
+VOID
+ReturnInitStatusToSpmc (
+ IN COMM_PROTOCOL CommProtocol,
+ IN EFI_STATUS Status,
+ OUT ARM_SVC_ARGS *EventCompleteSvcArgs
+ )
+{
+ ZeroMem (EventCompleteSvcArgs, sizeof (ARM_SVC_ARGS));
+
+ if (CommProtocol == CommProtocolFfa) {
+ if (EFI_ERROR (Status)) {
+ EventCompleteSvcArgs->Arg0 = ARM_FID_FFA_ERROR;
+
+ /*
+ * In case SvcConduit, this must be zero.
+ */
+ EventCompleteSvcArgs->Arg1 = 0x00;
+ EventCompleteSvcArgs->Arg2 = EfiStatusToFfaStatus (Status);
+ } else {
+ /*
+ * For completion of initialization, It should use FFA_MSG_WAIT.
+ * See FF-A specification 5.5 Protocol for completing execution context
+ * initialization
+ */
+ EventCompleteSvcArgs->Arg0 = ARM_FID_FFA_WAIT;
+ }
+ } else if (CommProtocol == CommProtocolSpmMm) {
+ EventCompleteSvcArgs->Arg0 = ARM_FID_SPM_MM_SP_EVENT_COMPLETE;
+ EventCompleteSvcArgs->Arg1 = EfiStatusToSpmMmStatus (Status);
+ } else {
+ /*
+ * We don't know what communication abi protocol is using.
+ * Set Arg0 as MAX_UINTN to make SPMC know it's error situation.
+ */
+ EventCompleteSvcArgs->Arg0 = MAX_UINTN;
+ }
+}
+
+/**
+ Set Event Complete arguments to be returned via SVC call.
+
+ @param[in] CommProtocol Communication Protocol.
+ @param[in] CommData Communication Abi specific data.
+ @param[in] Status Result of StandaloneMm service.
+ @param[out] EventCompleteSvcArgs Args structure.
+
+**/
+STATIC
+VOID
+SetEventCompleteSvcArgs (
+ IN COMM_PROTOCOL CommProtocol,
+ IN VOID *CommData,
+ IN EFI_STATUS Status,
+ OUT ARM_SVC_ARGS *EventCompleteSvcArgs
+ )
+{
+ FFA_MSG_INFO *FfaMsgInfo;
+
+ ZeroMem (EventCompleteSvcArgs, sizeof (ARM_SVC_ARGS));
+
+ if (CommProtocol == CommProtocolFfa) {
+ FfaMsgInfo = CommData;
+
+ if (EFI_ERROR (Status)) {
+ EventCompleteSvcArgs->Arg0 = ARM_FID_FFA_ERROR;
+
+ /*
+ * StandaloneMm is secure instance. So set as 0x00.
+ */
+ EventCompleteSvcArgs->Arg1 = 0x00;
+ EventCompleteSvcArgs->Arg2 = EfiStatusToFfaStatus (Status);
+ } else {
+ if (FfaMsgInfo->DirectMsgVersion == DirectMsgV1) {
+ EventCompleteSvcArgs->Arg0 = ARM_FID_FFA_MSG_SEND_DIRECT_RESP;
+ EventCompleteSvcArgs->Arg3 = ARM_FID_SPM_MM_SP_EVENT_COMPLETE;
+ } else {
+ EventCompleteSvcArgs->Arg0 = ARM_FID_FFA_MSG_SEND_DIRECT_RESP2;
+
+ if (FfaMsgInfo->ServiceType == ServiceTypeMisc) {
+ EventCompleteSvcArgs->Arg4 = mMiscMmCommunicateBuffer->DirectMsgArgs.Arg0;
+ EventCompleteSvcArgs->Arg5 = mMiscMmCommunicateBuffer->DirectMsgArgs.Arg1;
+ EventCompleteSvcArgs->Arg6 = mMiscMmCommunicateBuffer->DirectMsgArgs.Arg2;
+ EventCompleteSvcArgs->Arg7 = mMiscMmCommunicateBuffer->DirectMsgArgs.Arg3;
+ }
+ }
+
+ /*
+ * Swap source & dest partition id.
+ */
+ EventCompleteSvcArgs->Arg1 = PACK_PARTITION_ID_INFO (
+ FfaMsgInfo->DestPartId,
+ FfaMsgInfo->SourcePartId
+ );
+ }
+ } else {
+ EventCompleteSvcArgs->Arg0 = ARM_FID_SPM_MM_SP_EVENT_COMPLETE;
+ EventCompleteSvcArgs->Arg1 = EfiStatusToSpmMmStatus (Status);
+ }
+}
+
+/**
+ Wrap Misc service buffer with MmCommunication Header to
+ patch event handler via MmCommunication protocol.
+
+ @param[in] EventSvcArgs Passed arguments
+ @param[in] ServiceGuid Service Guid
+ @param[out] Buffer Misc service data
+ wrapped with MmCommunication Header.
+
+**/
+STATIC
+VOID
+InitializeMiscMmCommunicateBuffer (
+ IN ARM_SVC_ARGS *EventSvcArgs,
+ IN EFI_GUID *ServiceGuid,
+ OUT MISC_MM_COMMUNICATE_BUFFER *Buffer
+ )
+{
+ ZeroMem (Buffer, sizeof (MISC_MM_COMMUNICATE_BUFFER));
+
+ Buffer->MessageLength = sizeof (DIRECT_MSG_ARGS);
+ Buffer->DirectMsgArgs.Arg0 = EventSvcArgs->Arg4;
+ Buffer->DirectMsgArgs.Arg1 = EventSvcArgs->Arg5;
+ Buffer->DirectMsgArgs.Arg2 = EventSvcArgs->Arg6;
+ Buffer->DirectMsgArgs.Arg3 = EventSvcArgs->Arg7;
+ CopyGuid (&Buffer->HeaderGuid, ServiceGuid);
+}
+
+/**
+ Convert UUID to EFI_GUID format.
+ for example, If there is EFI_GUID named
+ "378daedc-f06b-4446-8314-40ab933c87a3",
+
+ EFI_GUID is saved in memory like:
+ dc ae 8d 37
+ 6b f0 46 44
+ 83 14 40 ab
+ 93 3c 87 a3
+
+ However, UUID should be saved like:
+ 37 8d ae dc
+ f0 6b 44 46
+ 83 14 40 ab
+ 93 3c 87 a3
+
+ FF-A and other software components (i.e. linux-kernel)
+ uses below format.
+
+ To patch mm-service properly, the passed uuid should be converted to
+ EFI_GUID format.
+
+ @param [in] Uuid Uuid
+ @param [out] Guid EFI_GUID
+
+**/
+STATIC
+VOID
+EFIAPI
+ConvertUuidToEfiGuid (
+ IN UINT64 *Uuid,
+ OUT EFI_GUID *Guid
+ )
+{
+ UINT32 *Data32;
+ UINT16 *Data16;
+
+ Data32 = (UINT32 *)Uuid;
+ Data32[0] = SwapBytes32 (Data32[0]);
+ Data16 = (UINT16 *)&Data32[1];
+ Data16[0] = SwapBytes16 (Data16[0]);
+ Data16[1] = SwapBytes16 (Data16[1]);
+ CopyGuid (Guid, (EFI_GUID *)Uuid);
+}
+
+/**
+ A loop to delegate events from SPMC.
+ DelegatedEventLoop() calls ArmCallSvc() to exit to SPMC.
+ When an event is delegated to StandaloneMm the SPMC returns control
+ to StandaloneMm by returning from the SVC call.
+
+ @param [in] CommProtocol Abi Protocol.
+ @param [in] CpuDriverEntryPoint Entry point to handle request.
+ @param [in] EventCompleteSvcArgs Pointer to the event completion arguments.
+
+**/
+STATIC
+VOID
+EFIAPI
+DelegatedEventLoop (
+ IN COMM_PROTOCOL CommProtocol,
+ IN EDKII_PI_MM_CPU_DRIVER_ENTRYPOINT CpuDriverEntryPoint,
+ IN ARM_SVC_ARGS *EventCompleteSvcArgs
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Uuid[2];
+ VOID *CommData;
+ FFA_MSG_INFO FfaMsgInfo;
+ EFI_GUID ServiceGuid;
+ SERVICE_TYPE ServiceType;
+ UINTN CommBufferAddr;
+
+ CommData = NULL;
+
+ while (TRUE) {
+ // Exit to SPMC.
+ ArmCallSvc (EventCompleteSvcArgs);
+ // Enter from SPMC.
+
+ DEBUG ((DEBUG_INFO, "Received delegated event\n"));
+ DEBUG ((DEBUG_INFO, "X0 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg0));
+ DEBUG ((DEBUG_INFO, "X1 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg1));
+ DEBUG ((DEBUG_INFO, "X2 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg2));
+ DEBUG ((DEBUG_INFO, "X3 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg3));
+ DEBUG ((DEBUG_INFO, "X4 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg4));
+ DEBUG ((DEBUG_INFO, "X5 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg5));
+ DEBUG ((DEBUG_INFO, "X6 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg6));
+ DEBUG ((DEBUG_INFO, "X7 : 0x%x\n", (UINT32)EventCompleteSvcArgs->Arg7));
+
+ if (CommProtocol == CommProtocolFfa) {
+ /*
+ * Register Convention for FF-A
+ * Arg0: ARM_FID_FFA_MSG_SEND_DIRECT_REQ/REQ2
+ * Arg1: Sender and Receiver endpoint IDs.
+ * Arg2: Message Flags for ARM_FID_FFA_MSG_SEND_DIRECT_REQ
+ * Low 8 bytes of UUID for ARM_FID_FFA_MSG_SEND_DIRECT_REQ2
+ * Arg3: Implementation Defined for ARM_FID_FFA_MSG_SEND_DIRECT_REQ
+ * High 8 bytes of UUID for ARM_FID_FFA_MSG_SEND_DIRECT_REQ2
+ * Others: Implementation Defined.
+ *
+ * See Arm Firmware Framework for Arm A-Profile for detail.
+ */
+ FfaMsgInfo.SourcePartId = GET_SOURCE_PARTITION_ID (EventCompleteSvcArgs->Arg1);
+ FfaMsgInfo.DestPartId = GET_DEST_PARTITION_ID (EventCompleteSvcArgs->Arg1);
+ CommData = &FfaMsgInfo;
+
+ if (EventCompleteSvcArgs->Arg0 == ARM_FID_FFA_MSG_SEND_DIRECT_REQ) {
+ FfaMsgInfo.DirectMsgVersion = DirectMsgV1;
+ ServiceType = ServiceTypeMmCommunication;
+ } else if (EventCompleteSvcArgs->Arg0 == ARM_FID_FFA_MSG_SEND_DIRECT_REQ2) {
+ FfaMsgInfo.DirectMsgVersion = DirectMsgV2;
+ Uuid[0] = EventCompleteSvcArgs->Arg2;
+ Uuid[1] = EventCompleteSvcArgs->Arg3;
+ ConvertUuidToEfiGuid (Uuid, &ServiceGuid);
+ ServiceType = GetServiceType (&ServiceGuid);
+ } else {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "Error: Unrecognized FF-A Id: 0x%x\n",
+ EventCompleteSvcArgs->Arg0
+ ));
+ goto ExitHandler;
+ }
+
+ FfaMsgInfo.ServiceType = ServiceType;
+
+ if (ServiceType == ServiceTypeMmCommunication) {
+ if (FfaMsgInfo.DirectMsgVersion == DirectMsgV1) {
+ CommBufferAddr = EventCompleteSvcArgs->Arg3;
+ } else {
+ CommBufferAddr = EventCompleteSvcArgs->Arg4;
+ }
+ } else if (ServiceType == ServiceTypeMisc) {
+ /*
+ * In case of Misc service, generate mm communication header
+ * to dispatch service via StandaloneMmCore.
+ */
+ InitializeMiscMmCommunicateBuffer (
+ EventCompleteSvcArgs,
+ &ServiceGuid,
+ mMiscMmCommunicateBuffer
+ );
+ CommBufferAddr = (UINTN)mMiscMmCommunicateBuffer;
+ } else {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((DEBUG_ERROR, "Error: Invalid FF-A Service...\n"));
+ goto ExitHandler;
+ }
+ } else if (CommProtocol == CommProtocolSpmMm) {
+ /*
+ * Register Convention for SPM_MM
+ * Arg0: ARM_SMC_ID_MM_COMMUNICATE
+ * Arg1: Communication Buffer
+ * Arg2: Size of Communication Buffer
+ * Arg3: Cpu number where StandaloneMm running on.
+ *
+ * See tf-a/services/std_svc/spm/spm_mm/spm_mm_main.c
+ */
+ if (EventCompleteSvcArgs->Arg0 != ARM_SMC_ID_MM_COMMUNICATE) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "Error: Unrecognized SPM_MM Id: 0x%x\n",
+ EventCompleteSvcArgs->Arg0
+ ));
+ goto ExitHandler;
+ }
+
+ CommBufferAddr = EventCompleteSvcArgs->Arg1;
+ ServiceType = ServiceTypeMmCommunication;
+ }
+
+ if (ServiceType == ServiceTypeMmCommunication) {
+ Status = ValidateMmCommBufferAddr (CommBufferAddr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Error: Failed to validate Communication Buffer address(0x%x)...\n",
+ CommBufferAddr
+ ));
+ goto ExitHandler;
+ }
+ }
+
+ Status = CpuDriverEntryPoint ((UINTN)ServiceType, CommBufferAddr);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Error: Failed delegated event 0x%x, Status 0x%x\n",
+ CommBufferAddr,
+ Status
+ ));
+ }
+
+ExitHandler:
+ SetEventCompleteSvcArgs (
+ CommProtocol,
+ CommData,
+ Status,
+ EventCompleteSvcArgs
+ );
+ } // while
+}
+
+/**
+ The handoff between the SPMC to StandaloneMM depends on the
+ communication interface between the SPMC and StandaloneMM.
+ When SpmMM is used, the handoff is implemented using the
+ Firmware Handoff protocol. When FF-A is used the FF-A boot
+ protocol is used.
+
+ @param [in] Arg0 In case of FF-A, address of FF-A boot information
+ In case of SPM_MM, this parameter must be zero
+ @param [in] Arg1 In case of FF-A, this parameter must be zero
+ In case of SPM_MM, Signature and register convention version
+ @param [in] Arg2 Must be zero
+ @param [in] Arg3 In case of FF-A, this parameter must be zero
+ In case of SPM_MM, address of transfer list
+
+**/
+VOID
+EFIAPI
+CEntryPoint (
+ IN UINTN Arg0,
+ IN UINTN Arg1,
+ IN UINTN Arg2,
+ IN UINTN Arg3
+ )
+{
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
+ ARM_SVC_ARGS EventCompleteSvcArgs;
+ EFI_STATUS Status;
+ UINT32 SectionHeaderOffset;
+ UINT16 NumberOfSections;
+ COMM_PROTOCOL CommProtocol;
+ VOID *HobStart;
+ VOID *TeData;
+ UINTN TeDataSize;
+ EFI_PHYSICAL_ADDRESS ImageBase;
+ EDKII_PI_MM_CPU_DRIVER_EP_PROTOCOL *PiMmCpuDriverEpProtocol;
+ EDKII_PI_MM_CPU_DRIVER_ENTRYPOINT CpuDriverEntryPoint;
+ EFI_HOB_FIRMWARE_VOLUME *FvHob;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ EFI_CONFIGURATION_TABLE *ConfigurationTable;
+ UINTN Idx;
+ EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *MmramRangesHob;
+
+ CpuDriverEntryPoint = NULL;
+
+ Status = GetCommProtocol (&CommProtocol);
+ if (EFI_ERROR (Status)) {
+ goto finish;
+ }
+
+ HobStart = GetPhitHobFromBootInfo (CommProtocol, Arg0, Arg1, Arg2, Arg3);
+ if (HobStart == NULL) {
+ Status = EFI_UNSUPPORTED;
+ goto finish;
+ }
+
+ DEBUG ((DEBUG_INFO, "Start Dump Hob: %lx\n", (unsigned long)HobStart));
+ DumpPhitHob (HobStart);
+ DEBUG ((DEBUG_INFO, "End Dump Hob: %lx\n", (unsigned long)HobStart));
+
+ FvHob = GetNextHob (EFI_HOB_TYPE_FV, HobStart);
+ if (FvHob == NULL) {
+ DEBUG ((DEBUG_ERROR, "Error: No Firmware Volume Hob is present.\n"));
+ Status = EFI_INVALID_PARAMETER;
+
+ goto finish;
+ }
+
+ // Locate PE/COFF File information for the Standalone MM core module
+ Status = LocateStandaloneMmCorePeCoffData (
+ (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvHob->BaseAddress,
+ &TeData,
+ &TeDataSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto finish;
+ }
+
+ // Obtain the PE/COFF Section information for the Standalone MM core module
+ Status = GetStandaloneMmCorePeCoffSections (
+ TeData,
+ &ImageContext,
+ &ImageBase,
+ &SectionHeaderOffset,
+ &NumberOfSections
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto finish;
+ }
+
+ //
+ // ImageBase may deviate from ImageContext.ImageAddress if we are dealing
+ // with a TE image, in which case the latter points to the actual offset
+ // of the image, whereas ImageBase refers to the address where the image
+ // would start if the stripped PE headers were still in place. In either
+ // case, we need to fix up ImageBase so it refers to the actual current
+ // load address.
+ //
+ ImageBase += (UINTN)TeData - ImageContext.ImageAddress;
+
+ // Update the memory access permissions of individual sections in the
+ // Standalone MM core module
+ Status = UpdateMmFoundationPeCoffPermissions (
+ &ImageContext,
+ ImageBase,
+ SectionHeaderOffset,
+ NumberOfSections,
+ ArmSetMemoryRegionNoExec,
+ ArmSetMemoryRegionReadOnly,
+ ArmClearMemoryRegionReadOnly
+ );
+ if (EFI_ERROR (Status)) {
+ goto finish;
+ }
+
+ if (ImageContext.ImageAddress != (UINTN)TeData) {
+ ImageContext.ImageAddress = (UINTN)TeData;
+ ArmSetMemoryRegionNoExec (ImageBase, SIZE_4KB);
+ ArmClearMemoryRegionReadOnly (ImageBase, SIZE_4KB);
+
+ Status = PeCoffLoaderRelocateImage (&ImageContext);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ // Set the gHobList to point to the HOB list passed by TF-A.
+ // This will be used by StandaloneMmCoreHobLib in early stage.
+ gHobList = HobStart;
+
+ //
+ // Call the MM Core entry point
+ //
+ ProcessModuleEntryPointList (HobStart);
+
+ // ProcessModuleEntryPointList() copies the HOB List passed
+ // by TF-A, i.e. HobStart, in the ConfigurationTable[].
+ // Therefore, find the HobList in the ConfigurationTable[] by
+ // searching for the gEfiHobListGuid.
+ // Also update the gHobList to point to the HobList in the
+ // ConfigurationTable[] as the HobList passed by TF-A can
+ // be overwritten by StMM after StMM Core is initialised, i.e.
+ // after the MM Core entry point is called.
+ Status = EFI_NOT_FOUND;
+ ConfigurationTable = gMmCoreMmst.MmConfigurationTable;
+ for (Idx = 0; Idx < gMmCoreMmst.NumberOfTableEntries; Idx++) {
+ if (CompareGuid (&gEfiHobListGuid, &ConfigurationTable[Idx].VendorGuid)) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Error: Hoblist not found in MmConfigurationTable\n"));
+ goto finish;
+ }
+
+ gHobList = ConfigurationTable[Idx].VendorTable;
+
+ // Find the descriptor that contains the whereabouts of the buffer for
+ // communication with the Normal world.
+ GuidHob = GetNextGuidHob (&gEfiStandaloneMmNonSecureBufferGuid, gHobList);
+ if (GuidHob == NULL) {
+ Status = EFI_NOT_FOUND;
+ DEBUG ((DEBUG_ERROR, "Error: No NsCommBuffer hob ...\n"));
+ goto finish;
+ }
+
+ mNsCommBuffer = GET_GUID_HOB_DATA (GuidHob);
+ if (mNsCommBuffer == NULL) {
+ Status = EFI_NOT_FOUND;
+ DEBUG ((DEBUG_ERROR, "Error: No NsCommBuffer hob data...\n"));
+ goto finish;
+ }
+
+ //
+ // The base and size of buffer shared with
+ // privileged Secure world software is in PeiMmramMemoryReservedGuid Hob.
+ //
+ GuidHob = GetNextGuidHob (&gEfiMmPeiMmramMemoryReserveGuid, gHobList);
+ if (GuidHob == NULL) {
+ Status = EFI_NOT_FOUND;
+ DEBUG ((DEBUG_ERROR, "Error: No PeiMmramMemoryReserved hob ...\n"));
+ goto finish;
+ }
+
+ MmramRangesHob = GET_GUID_HOB_DATA (GuidHob);
+ if ((MmramRangesHob == NULL) ||
+ (MmramRangesHob->NumberOfMmReservedRegions < MMRAM_DESC_MIN_COUNT))
+ {
+ Status = EFI_NOT_FOUND;
+ DEBUG ((DEBUG_ERROR, "Error: Failed to get shared comm buffer ...\n"));
+ goto finish;
+ }
+
+ mSCommBuffer = &MmramRangesHob->Descriptor[MMRAM_DESC_IDX_SECURE_SHARED_BUFFER];
+
+ //
+ // Find out cpu driver entry point used in DelegatedEventLoop
+ // to handle MMI request.
+ //
+ Status = gMmCoreMmst.MmLocateProtocol (
+ &gEdkiiPiMmCpuDriverEpProtocolGuid,
+ NULL,
+ (VOID **)&PiMmCpuDriverEpProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ goto finish;
+ }
+
+ CpuDriverEntryPoint = PiMmCpuDriverEpProtocol->PiMmCpuDriverEntryPoint;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "Shared Cpu Driver EP %p\n",
+ CpuDriverEntryPoint
+ ));
+
+ if (CommProtocol == CommProtocolFfa) {
+ Status = gMmCoreMmst.MmAllocatePool (
+ EfiRuntimeServicesData,
+ sizeof (MISC_MM_COMMUNICATE_BUFFER),
+ (VOID **)&mMiscMmCommunicateBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Error: Failed to allocate misc mm communication buffer...\n"
+ ));
+ goto finish;
+ }
+ }
+
+finish:
+ ReturnInitStatusToSpmc (CommProtocol, Status, &EventCompleteSvcArgs);
+
+ // Call DelegateEventLoop(), this function never returns.
+ DelegatedEventLoop (CommProtocol, CpuDriverEntryPoint, &EventCompleteSvcArgs);
+}
diff --git a/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.inf b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.inf
new file mode 100644
index 0000000000..9afd8e9715
--- /dev/null
+++ b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/ArmStandaloneMmCoreEntryPoint.inf
@@ -0,0 +1,72 @@
+## @file
+# Module entry point library for DXE core.
+#
+# Copyright (c) 2024, Arm Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = ArmStandaloneMmCoreEntryPoint
+ FILE_GUID = 16d7b2e4-a025-11ef-8931-6b032fa329a6
+ MODULE_TYPE = MM_CORE_STANDALONE
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x00010032
+ LIBRARY_CLASS = StandaloneMmCoreEntryPoint|MM_CORE_STANDALONE
+
+#
+# VALID_ARCHITECTURES = ARM AARCH64
+#
+
+[Sources]
+ ArmStandaloneMmCoreEntryPoint.c
+ SetPermissions.c
+
+[Sources.AARCH64]
+ AArch64/ModuleEntryPoint.S
+
+[Sources.ARM]
+ Arm/ModuleEntryPoint.S
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ StandaloneMmPkg/StandaloneMmPkg.dec
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ StandaloneMmMmuLib
+ ArmSvcLib
+ ArmTransferListLib
+ ArmFfaLib
+
+[Guids]
+ gMpInformationHobGuid
+ gEfiMmPeiMmramMemoryReserveGuid
+ gEfiStandaloneMmNonSecureBufferGuid
+ gEfiHobListGuid
+
+[Protocols]
+ gEdkiiPiMmCpuDriverEpProtocolGuid
+ gEfiMmCommunication2ProtocolGuid
+
+[Pcd]
+ gArmTokenSpaceGuid.PcdStMmStackSize
+
+#
+# This configuration fails for CLANGPDB, which does not support PIE in the GCC
+# sense. Such however is required for ARM family StandaloneMmCore
+# self-relocation, and thus the CLANGPDB toolchain is unsupported for ARM and
+# AARCH64 for this module.
+#
+[BuildOptions]
+ GCC:*_*_ARM_CC_FLAGS = -fpie
+ GCC:*_*_AARCH64_CC_FLAGS = -fpie
diff --git a/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/SetPermissions.c b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/SetPermissions.c
new file mode 100644
index 0000000000..fd3fdae4f0
--- /dev/null
+++ b/ArmPkg/Library/ArmStandaloneMmCoreEntryPoint/SetPermissions.c
@@ -0,0 +1,383 @@
+/** @file
+ Locate, get and update PE/COFF permissions during Standalone MM
+ Foundation Entry point on ARM platforms.
+
+Copyright (c) 2017 - 2021, Arm Ltd. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiMm.h>
+
+#include <PiPei.h>
+#include <Guid/MmramMemoryReserve.h>
+#include <Guid/MpInformation.h>
+
+#include <Library/ArmStandaloneMmCoreEntryPoint.h>
+#include <Library/ArmMmuLib.h>
+#include <Library/ArmSvcLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SerialPortLib.h>
+
+#include <IndustryStandard/ArmStdSmc.h>
+
+/**
+ Privileged firmware assigns RO & Executable attributes to all memory occupied
+ by the Boot Firmware Volume. This function sets the correct permissions of
+ sections in the Standalone MM Core module to be able to access RO and RW data
+ and make further progress in the boot process.
+
+ @param [in] ImageContext Pointer to PE/COFF image context
+ @param [in] ImageBase Base of image in memory
+ @param [in] SectionHeaderOffset Offset of PE/COFF image section header
+ @param [in] NumberOfSections Number of Sections
+ @param [in] TextUpdater Function to change code permissions
+ @param [in] ReadOnlyUpdater Function to change RO permissions
+ @param [in] ReadWriteUpdater Function to change RW permissions
+
+**/
+EFI_STATUS
+EFIAPI
+UpdateMmFoundationPeCoffPermissions (
+ IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
+ IN EFI_PHYSICAL_ADDRESS ImageBase,
+ IN UINT32 SectionHeaderOffset,
+ IN CONST UINT16 NumberOfSections,
+ IN REGION_PERMISSION_UPDATE_FUNC TextUpdater,
+ IN REGION_PERMISSION_UPDATE_FUNC ReadOnlyUpdater,
+ IN REGION_PERMISSION_UPDATE_FUNC ReadWriteUpdater
+ )
+{
+ EFI_IMAGE_SECTION_HEADER SectionHeader;
+ RETURN_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Base;
+ UINTN Size;
+ UINTN ReadSize;
+ UINTN Index;
+
+ ASSERT (ImageContext != NULL);
+
+ //
+ // Iterate over the sections
+ //
+ for (Index = 0; Index < NumberOfSections; Index++) {
+ //
+ // Read section header from file
+ //
+ Size = sizeof (EFI_IMAGE_SECTION_HEADER);
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ SectionHeaderOffset,
+ &Size,
+ &SectionHeader
+ );
+
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: ImageContext->ImageRead () failed (Status = %r)\n",
+ __func__,
+ Status
+ ));
+ return Status;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Section %d of image at 0x%lx has 0x%x permissions\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress,
+ SectionHeader.Characteristics
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Section %d of image at 0x%lx has %a name\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress,
+ SectionHeader.Name
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Section %d of image at 0x%lx has 0x%x address\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress,
+ ImageContext->ImageAddress + SectionHeader.VirtualAddress
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Section %d of image at 0x%lx has 0x%x data\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress,
+ SectionHeader.PointerToRawData
+ ));
+
+ //
+ // If the section is marked as XN then remove the X attribute. Furthermore,
+ // if it is a writeable section then mark it appropriately as well.
+ //
+ if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) {
+ Base = ImageBase + SectionHeader.VirtualAddress;
+
+ TextUpdater (Base, SectionHeader.Misc.VirtualSize);
+
+ if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_WRITE) != 0) {
+ ReadWriteUpdater (Base, SectionHeader.Misc.VirtualSize);
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Mapping section %d of image at 0x%lx with RW-XN permissions\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress
+ ));
+ } else {
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Mapping section %d of image at 0x%lx with RO-XN permissions\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress
+ ));
+ }
+ } else {
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Ignoring section %d of image at 0x%lx with 0x%x permissions\n",
+ __func__,
+ Index,
+ ImageContext->ImageAddress,
+ SectionHeader.Characteristics
+ ));
+ }
+
+ SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
+ }
+
+ return RETURN_SUCCESS;
+}
+
+/**
+ Privileged firmware assigns RO & Executable attributes to all memory occupied
+ by the Boot Firmware Volume. This function locates the Standalone MM Core
+ module PE/COFF image in the BFV and returns this information.
+
+ @param [in] BfvAddress Base Address of Boot Firmware Volume
+ @param [in, out] TeData Pointer to address for allocating memory
+ for PE/COFF image data
+ @param [in, out] TeDataSize Pointer to size of PE/COFF image data
+
+**/
+EFI_STATUS
+EFIAPI
+LocateStandaloneMmCorePeCoffData (
+ IN EFI_FIRMWARE_VOLUME_HEADER *BfvAddress,
+ IN OUT VOID **TeData,
+ IN OUT UINTN *TeDataSize
+ )
+{
+ EFI_FFS_FILE_HEADER *FileHeader;
+ EFI_STATUS Status;
+
+ FileHeader = NULL;
+ Status = FfsFindNextFile (
+ EFI_FV_FILETYPE_SECURITY_CORE,
+ BfvAddress,
+ &FileHeader
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Unable to locate Standalone MM FFS file - 0x%x\n",
+ Status
+ ));
+ return Status;
+ }
+
+ Status = FfsFindSectionData (EFI_SECTION_PE32, FileHeader, TeData, TeDataSize);
+ if (EFI_ERROR (Status)) {
+ Status = FfsFindSectionData (EFI_SECTION_TE, FileHeader, TeData, TeDataSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Unable to locate Standalone MM Section data - %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "Found Standalone MM PE data - 0x%x\n", *TeData));
+ return Status;
+}
+
+/**
+ Returns the PC COFF section information.
+
+ @param [in, out] ImageContext Pointer to PE/COFF image context
+ @param [out] ImageBase Base of image in memory
+ @param [out] SectionHeaderOffset Offset of PE/COFF image section header
+ @param [out] NumberOfSections Number of Sections
+
+**/
+STATIC
+EFI_STATUS
+GetPeCoffSectionInformation (
+ IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
+ OUT EFI_PHYSICAL_ADDRESS *ImageBase,
+ OUT UINT32 *SectionHeaderOffset,
+ OUT UINT16 *NumberOfSections
+ )
+{
+ RETURN_STATUS Status;
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
+ UINTN Size;
+ UINTN ReadSize;
+
+ ASSERT (ImageContext != NULL);
+ ASSERT (SectionHeaderOffset != NULL);
+ ASSERT (NumberOfSections != NULL);
+
+ Status = PeCoffLoaderGetImageInfo (ImageContext);
+ if (RETURN_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: PeCoffLoaderGetImageInfo () failed (Status == %r)\n",
+ __func__,
+ Status
+ ));
+ return Status;
+ }
+
+ if (ImageContext->SectionAlignment < EFI_PAGE_SIZE) {
+ //
+ // The sections need to be at least 4 KB aligned, since that is the
+ // granularity at which we can tighten permissions.
+ //
+ if (!ImageContext->IsTeImage) {
+ DEBUG ((
+ DEBUG_WARN,
+ "%a: non-TE Image at 0x%lx has SectionAlignment < 4 KB (%lu)\n",
+ __func__,
+ ImageContext->ImageAddress,
+ ImageContext->SectionAlignment
+ ));
+ return RETURN_UNSUPPORTED;
+ }
+
+ ImageContext->SectionAlignment = EFI_PAGE_SIZE;
+ }
+
+ //
+ // Read the PE/COFF Header. For PE32 (32-bit) this will read in too much
+ // data, but that should not hurt anything. Hdr.Pe32->OptionalHeader.Magic
+ // determines if this is a PE32 or PE32+ image. The magic is in the same
+ // location in both images.
+ //
+ Hdr.Union = &HdrData;
+ Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ ImageContext->PeCoffHeaderOffset,
+ &Size,
+ Hdr.Pe32
+ );
+
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: TmpContext->ImageRead () failed (Status = %r)\n",
+ __func__,
+ Status
+ ));
+ return Status;
+ }
+
+ *ImageBase = ImageContext->ImageAddress;
+ if (!ImageContext->IsTeImage) {
+ ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
+
+ *SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER);
+ *NumberOfSections = Hdr.Pe32->FileHeader.NumberOfSections;
+
+ switch (Hdr.Pe32->OptionalHeader.Magic) {
+ case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ *SectionHeaderOffset += Hdr.Pe32->FileHeader.SizeOfOptionalHeader;
+ break;
+ case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ *SectionHeaderOffset += Hdr.Pe32Plus->FileHeader.SizeOfOptionalHeader;
+ break;
+ default:
+ ASSERT (FALSE);
+ }
+ } else {
+ *SectionHeaderOffset = (UINTN)(sizeof (EFI_TE_IMAGE_HEADER));
+ *NumberOfSections = Hdr.Te->NumberOfSections;
+ *ImageBase -= (UINT32)Hdr.Te->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER);
+ }
+
+ return RETURN_SUCCESS;
+}
+
+/**
+ Privileged firmware assigns RO & Executable attributes to all memory occupied
+ by the Boot Firmware Volume. This function locates the section information of
+ the Standalone MM Core module to be able to change permissions of the
+ individual sections later in the boot process.
+
+ @param [in] TeData Pointer to PE/COFF image data
+ @param [in, out] ImageContext Pointer to PE/COFF image context
+ @param [out] ImageBase Pointer to ImageBase variable
+ @param [in, out] SectionHeaderOffset Offset of PE/COFF image section header
+ @param [in, out] NumberOfSections Number of Sections
+
+**/
+EFI_STATUS
+EFIAPI
+GetStandaloneMmCorePeCoffSections (
+ IN VOID *TeData,
+ IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
+ OUT EFI_PHYSICAL_ADDRESS *ImageBase,
+ IN OUT UINT32 *SectionHeaderOffset,
+ IN OUT UINT16 *NumberOfSections
+ )
+{
+ EFI_STATUS Status;
+
+ // Initialize the Image Context
+ ZeroMem (ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
+ ImageContext->Handle = TeData;
+ ImageContext->ImageRead = PeCoffLoaderImageReadFromMemory;
+
+ DEBUG ((DEBUG_INFO, "Found Standalone MM PE data - 0x%x\n", TeData));
+
+ Status = GetPeCoffSectionInformation (
+ ImageContext,
+ ImageBase,
+ SectionHeaderOffset,
+ NumberOfSections
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Unable to locate Standalone MM Core PE-COFF Section information - %r\n", Status));
+ return Status;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "Standalone MM Core PE-COFF SectionHeaderOffset - 0x%x, NumberOfSections - %d\n",
+ *SectionHeaderOffset,
+ *NumberOfSections
+ ));
+
+ return Status;
+}