ArmPkg: MmCommunicationDxe: Add MM communicate v3

As MM cores start to support MM communciate v3. This change moves the
MM communicate driver to produce MM communicate v3 protocol.

Signed-off-by: Kun Qin <kun.qin@microsoft.com>
This commit is contained in:
Kun Qin
2025-05-13 14:37:49 -07:00
committed by mergify[bot]
parent 4fa354b116
commit 6eed57a0aa
2 changed files with 209 additions and 100 deletions

View File

@@ -18,6 +18,7 @@
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/MmCommunication2.h>
#include <Protocol/MmCommunication3.h>
#include <IndustryStandard/ArmStdSmc.h>
#include <IndustryStandard/ArmFfaSvc.h>
@@ -143,6 +144,181 @@ SendSpmMmCommunicate (
return SmcMmRetToEfiStatus (CommunicateSmcArgs.Arg0);
}
/**
Communicates with a registered handler.
This function provides a service to send and receive messages from a registered UEFI service.
@param[in, out] CommBufferPhysical Physical address of the MM communication buffer
@param[in, out] CommBufferVirtual Virtual address of the MM communication buffer
@param[in, out] CommSize The size of the data buffer being passed in. On input,
when not omitted, the buffer should cover EFI_MM_COMMUNICATE_HEADER
and the value of MessageLength field. On exit, the size
of data being returned. Zero if the handler does not
wish to reply with any data. This parameter is optional
and may be NULL.
@retval EFI_SUCCESS The message was successfully posted.
@retval EFI_INVALID_PARAMETER CommBufferPhysical or CommBufferVirtual was NULL, or
integer value pointed by CommSize does not cover
EFI_MM_COMMUNICATE_HEADER and the value of MessageLength
field.
@retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation.
If this error is returned, the MessageLength field
in the CommBuffer header or the integer pointed by
CommSize, are updated to reflect the maximum payload
size the implementation can accommodate.
@retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter,
if not omitted, are in address range that cannot be
accessed by the MM environment.
**/
STATIC
EFI_STATUS
EFIAPI
MmCommunicationCommon (
IN OUT VOID *CommBufferPhysical,
IN OUT VOID *CommBufferVirtual,
IN OUT UINTN *CommSize OPTIONAL
)
{
EFI_MM_COMMUNICATE_HEADER *CommunicateHeader;
EFI_MM_COMMUNICATE_HEADER_V3 *CommunicateHeaderV3;
UINTN BufferSize;
UINTN *MessageSize;
UINTN HeaderSize;
EFI_STATUS Status;
Status = EFI_ACCESS_DENIED;
BufferSize = 0;
//
// Check parameters
//
if ((CommBufferVirtual == NULL) || (CommBufferPhysical == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
CommunicateHeader = CommBufferVirtual;
CommunicateHeaderV3 = NULL;
// CommBuffer is a mandatory parameter. Hence, Rely on
// MessageLength + Header to ascertain the
// total size of the communication payload rather than
// rely on optional CommSize parameter
if (CompareGuid (
&CommunicateHeader->HeaderGuid,
&gEfiMmCommunicateHeaderV3Guid
))
{
CommunicateHeaderV3 = (EFI_MM_COMMUNICATE_HEADER_V3 *)CommBufferVirtual;
BufferSize = CommunicateHeaderV3->BufferSize;
MessageSize = &CommunicateHeaderV3->MessageSize;
HeaderSize = sizeof (EFI_MM_COMMUNICATE_HEADER_V3);
} else {
BufferSize = CommunicateHeader->MessageLength +
sizeof (CommunicateHeader->HeaderGuid) +
sizeof (CommunicateHeader->MessageLength);
MessageSize = &CommunicateHeader->MessageLength;
HeaderSize = sizeof (CommunicateHeader->HeaderGuid) +
sizeof (CommunicateHeader->MessageLength);
}
// If CommSize is not omitted, perform size inspection before proceeding.
if (CommSize != NULL) {
// This case can be used by the consumer of this driver to find out the
// max size that can be used for allocating CommBuffer.
if ((*CommSize == 0) ||
(*CommSize > mNsCommBuffMemRegion.Length))
{
*CommSize = mNsCommBuffMemRegion.Length;
Status = EFI_BAD_BUFFER_SIZE;
}
//
// CommSize should cover at least MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
//
if (*CommSize < BufferSize) {
Status = EFI_INVALID_PARAMETER;
}
}
//
// If the message length is 0 or greater than what can be tolerated by the MM
// environment then return the expected size.
//
if ((*MessageSize == 0) ||
(BufferSize > mNsCommBuffMemRegion.Length))
{
*MessageSize = mNsCommBuffMemRegion.Length - HeaderSize;
Status = EFI_BAD_BUFFER_SIZE;
}
// MessageLength or CommSize check has failed, return here.
if (EFI_ERROR (Status)) {
return Status;
}
// Copy Communication Payload
CopyMem ((VOID *)mNsCommBuffMemRegion.VirtualBase, CommBufferVirtual, BufferSize);
if (IsFfaSupported ()) {
Status = SendFfaMmCommunicate ();
} else {
Status = SendSpmMmCommunicate ();
}
if (!EFI_ERROR (Status)) {
ZeroMem (CommBufferVirtual, BufferSize);
// On successful return, the size of data being returned is inferred from
// MessageLength + Header.
CommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)mNsCommBuffMemRegion.VirtualBase;
if ((CommunicateHeaderV3 != NULL) && !CompareGuid (
&CommunicateHeader->HeaderGuid,
&gEfiMmCommunicateHeaderV3Guid
))
{
// Sanity check to make sure we are still using v3
DEBUG ((
DEBUG_ERROR,
"%a Expected v3 header, but got v1 header! %g\n",
__func__,
&CommunicateHeader->HeaderGuid
));
return EFI_INVALID_PARAMETER;
}
if (CommunicateHeaderV3 != NULL) {
CommunicateHeaderV3 = (EFI_MM_COMMUNICATE_HEADER_V3 *)CommunicateHeader;
BufferSize = CommunicateHeaderV3->BufferSize;
} else {
BufferSize = CommunicateHeader->MessageLength +
sizeof (CommunicateHeader->HeaderGuid) +
sizeof (CommunicateHeader->MessageLength);
}
if (BufferSize > mNsCommBuffMemRegion.Length) {
// Something bad has happened, we should have landed in ARM_SMC_MM_RET_NO_MEMORY
Status = EFI_BAD_BUFFER_SIZE;
DEBUG ((
DEBUG_ERROR,
"%a Returned buffer exceeds communication buffer limit. Has: 0x%llx vs. max: 0x%llx!\n",
__func__,
BufferSize,
(UINTN)mNsCommBuffMemRegion.Length
));
} else {
CopyMem (
CommBufferVirtual,
(VOID *)mNsCommBuffMemRegion.VirtualBase,
BufferSize
);
}
}
return Status;
}
/**
Communicates with a registered handler.
@@ -182,104 +358,26 @@ MmCommunication2Communicate (
IN OUT UINTN *CommSize OPTIONAL
)
{
EFI_MM_COMMUNICATE_HEADER *CommunicateHeader;
UINTN BufferSize;
EFI_STATUS Status;
return MmCommunicationCommon (
CommBufferPhysical,
CommBufferVirtual,
CommSize
);
}
Status = EFI_ACCESS_DENIED;
BufferSize = 0;
//
// Check parameters
//
if ((CommBufferVirtual == NULL) || (CommBufferPhysical == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
CommunicateHeader = CommBufferVirtual;
// CommBuffer is a mandatory parameter. Hence, Rely on
// MessageLength + Header to ascertain the
// total size of the communication payload rather than
// rely on optional CommSize parameter
BufferSize = CommunicateHeader->MessageLength +
sizeof (CommunicateHeader->HeaderGuid) +
sizeof (CommunicateHeader->MessageLength);
// If CommSize is not omitted, perform size inspection before proceeding.
if (CommSize != NULL) {
// This case can be used by the consumer of this driver to find out the
// max size that can be used for allocating CommBuffer.
if ((*CommSize == 0) ||
(*CommSize > mNsCommBuffMemRegion.Length))
{
*CommSize = mNsCommBuffMemRegion.Length;
Status = EFI_BAD_BUFFER_SIZE;
}
//
// CommSize should cover at least MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
//
if (*CommSize < BufferSize) {
Status = EFI_INVALID_PARAMETER;
}
}
//
// If the message length is 0 or greater than what can be tolerated by the MM
// environment then return the expected size.
//
if ((CommunicateHeader->MessageLength == 0) ||
(BufferSize > mNsCommBuffMemRegion.Length))
{
CommunicateHeader->MessageLength = mNsCommBuffMemRegion.Length -
sizeof (CommunicateHeader->HeaderGuid) -
sizeof (CommunicateHeader->MessageLength);
Status = EFI_BAD_BUFFER_SIZE;
}
// MessageLength or CommSize check has failed, return here.
if (EFI_ERROR (Status)) {
return Status;
}
// Copy Communication Payload
CopyMem ((VOID *)mNsCommBuffMemRegion.VirtualBase, CommBufferVirtual, BufferSize);
if (IsFfaSupported ()) {
Status = SendFfaMmCommunicate ();
} else {
Status = SendSpmMmCommunicate ();
}
if (!EFI_ERROR (Status)) {
ZeroMem (CommBufferVirtual, BufferSize);
// On successful return, the size of data being returned is inferred from
// MessageLength + Header.
CommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)mNsCommBuffMemRegion.VirtualBase;
BufferSize = CommunicateHeader->MessageLength +
sizeof (CommunicateHeader->HeaderGuid) +
sizeof (CommunicateHeader->MessageLength);
if (BufferSize > mNsCommBuffMemRegion.Length) {
// Something bad has happened, we should have landed in ARM_SMC_MM_RET_NO_MEMORY
Status = EFI_BAD_BUFFER_SIZE;
DEBUG ((
DEBUG_ERROR,
"%a Returned buffer exceeds communication buffer limit. Has: 0x%llx vs. max: 0x%llx!\n",
__func__,
BufferSize,
(UINTN)mNsCommBuffMemRegion.Length
));
} else {
CopyMem (
CommBufferVirtual,
(VOID *)mNsCommBuffMemRegion.VirtualBase,
BufferSize
);
}
}
return Status;
EFI_STATUS
EFIAPI
MmCommunication3Communicate (
IN CONST EFI_MM_COMMUNICATION3_PROTOCOL *This,
IN OUT VOID *CommBufferPhysical,
IN OUT VOID *CommBufferVirtual
)
{
return MmCommunicationCommon (
CommBufferPhysical,
CommBufferVirtual,
NULL
);
}
//
@@ -289,6 +387,13 @@ STATIC EFI_MM_COMMUNICATION2_PROTOCOL mMmCommunication2 = {
MmCommunication2Communicate
};
//
// MM Communication Protocol instance
//
STATIC EFI_MM_COMMUNICATION3_PROTOCOL mMmCommunication3 = {
MmCommunication3Communicate
};
/**
Notification callback on SetVirtualAddressMap event.
@@ -682,11 +787,13 @@ MmCommunication2Initialize (
}
// Install the communication protocol
Status = gBS->InstallProtocolInterface (
Status = gBS->InstallMultipleProtocolInterfaces (
&mMmCommunicateHandle,
&gEfiMmCommunication2ProtocolGuid,
EFI_NATIVE_INTERFACE,
&mMmCommunication2
&mMmCommunication2,
&gEfiMmCommunication3ProtocolGuid,
&mMmCommunication3,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((

View File

@@ -43,11 +43,13 @@
[Protocols]
gEfiDxeMmReadyToLockProtocolGuid ## UNDEFINED # SmiHandlerRegister
gEfiMmCommunication2ProtocolGuid ## PRODUCES
gEfiMmCommunication3ProtocolGuid ## PRODUCES
[Guids]
gEfiEndOfDxeEventGroupGuid
gEfiEventExitBootServicesGuid
gEfiEventReadyToBootGuid
gEfiMmCommunicateHeaderV3Guid
[Pcd.common]
gArmTokenSpaceGuid.PcdMmBufferBase