3 Copyright (c) 2016-2019, ARM Limited. All rights reserved.
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/ArmLib.h>
10 #include <Library/ArmSmcLib.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/DebugLib.h>
13 #include <Library/DxeServicesTableLib.h>
14 #include <Library/HobLib.h>
15 #include <Library/PcdLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/UefiRuntimeServicesTableLib.h>
19 #include <Protocol/MmCommunication2.h>
21 #include <IndustryStandard/ArmStdSmc.h>
23 #include "MmCommunicate.h"
26 // Address, Length of the pre-allocated buffer for communication with the secure
29 STATIC ARM_MEMORY_REGION_DESCRIPTOR mNsCommBuffMemRegion
;
31 // Notification event when virtual address map is set.
32 STATIC EFI_EVENT mSetVirtualAddressMapEvent
;
35 // Handle to install the MM Communication Protocol
37 STATIC EFI_HANDLE mMmCommunicateHandle
;
40 Communicates with a registered handler.
42 This function provides a service to send and receive messages from a registered UEFI service.
44 @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL instance.
45 @param[in] CommBufferPhysical Physical address of the MM communication buffer
46 @param[in] CommBufferVirtual Virtual address of the MM communication buffer
47 @param[in] CommSize The size of the data buffer being passed in. On exit, the size of data
48 being returned. Zero if the handler does not wish to reply with any data.
49 This parameter is optional and may be NULL.
51 @retval EFI_SUCCESS The message was successfully posted.
52 @retval EFI_INVALID_PARAMETER CommBufferPhysical was NULL or CommBufferVirtual was NULL.
53 @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation.
54 If this error is returned, the MessageLength field
55 in the CommBuffer header or the integer pointed by
56 CommSize, are updated to reflect the maximum payload
57 size the implementation can accommodate.
58 @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter,
59 if not omitted, are in address range that cannot be
60 accessed by the MM environment.
65 MmCommunication2Communicate (
66 IN CONST EFI_MM_COMMUNICATION2_PROTOCOL
*This
,
67 IN OUT VOID
*CommBufferPhysical
,
68 IN OUT VOID
*CommBufferVirtual
,
69 IN OUT UINTN
*CommSize OPTIONAL
72 EFI_MM_COMMUNICATE_HEADER
*CommunicateHeader
;
73 ARM_SMC_ARGS CommunicateSmcArgs
;
77 Status
= EFI_ACCESS_DENIED
;
80 ZeroMem (&CommunicateSmcArgs
, sizeof (ARM_SMC_ARGS
));
85 if (CommBufferVirtual
== NULL
) {
86 return EFI_INVALID_PARAMETER
;
89 CommunicateHeader
= CommBufferVirtual
;
90 // CommBuffer is a mandatory parameter. Hence, Rely on
91 // MessageLength + Header to ascertain the
92 // total size of the communication payload rather than
93 // rely on optional CommSize parameter
94 BufferSize
= CommunicateHeader
->MessageLength
+
95 sizeof (CommunicateHeader
->HeaderGuid
) +
96 sizeof (CommunicateHeader
->MessageLength
);
98 // If the length of the CommBuffer is 0 then return the expected length.
100 // This case can be used by the consumer of this driver to find out the
101 // max size that can be used for allocating CommBuffer.
102 if ((*CommSize
== 0) ||
103 (*CommSize
> mNsCommBuffMemRegion
.Length
)) {
104 *CommSize
= mNsCommBuffMemRegion
.Length
;
105 return EFI_BAD_BUFFER_SIZE
;
108 // CommSize must match MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
110 if (*CommSize
!= BufferSize
) {
111 return EFI_INVALID_PARAMETER
;
116 // If the buffer size is 0 or greater than what can be tolerated by the MM
117 // environment then return the expected size.
119 if ((BufferSize
== 0) ||
120 (BufferSize
> mNsCommBuffMemRegion
.Length
)) {
121 CommunicateHeader
->MessageLength
= mNsCommBuffMemRegion
.Length
-
122 sizeof (CommunicateHeader
->HeaderGuid
) -
123 sizeof (CommunicateHeader
->MessageLength
);
124 return EFI_BAD_BUFFER_SIZE
;
128 CommunicateSmcArgs
.Arg0
= ARM_SMC_ID_MM_COMMUNICATE_AARCH64
;
131 CommunicateSmcArgs
.Arg1
= 0;
133 // Copy Communication Payload
134 CopyMem ((VOID
*)mNsCommBuffMemRegion
.VirtualBase
, CommBufferVirtual
, BufferSize
);
136 // comm_buffer_address (64-bit physical address)
137 CommunicateSmcArgs
.Arg2
= (UINTN
)mNsCommBuffMemRegion
.PhysicalBase
;
139 // comm_size_address (not used, indicated by setting to zero)
140 CommunicateSmcArgs
.Arg3
= 0;
142 // Call the Standalone MM environment.
143 ArmCallSmc (&CommunicateSmcArgs
);
145 switch (CommunicateSmcArgs
.Arg0
) {
146 case ARM_SMC_MM_RET_SUCCESS
:
147 ZeroMem (CommBufferVirtual
, BufferSize
);
148 // On successful return, the size of data being returned is inferred from
149 // MessageLength + Header.
150 CommunicateHeader
= (EFI_MM_COMMUNICATE_HEADER
*)mNsCommBuffMemRegion
.VirtualBase
;
151 BufferSize
= CommunicateHeader
->MessageLength
+
152 sizeof (CommunicateHeader
->HeaderGuid
) +
153 sizeof (CommunicateHeader
->MessageLength
);
157 (VOID
*)mNsCommBuffMemRegion
.VirtualBase
,
160 Status
= EFI_SUCCESS
;
163 case ARM_SMC_MM_RET_INVALID_PARAMS
:
164 Status
= EFI_INVALID_PARAMETER
;
167 case ARM_SMC_MM_RET_DENIED
:
168 Status
= EFI_ACCESS_DENIED
;
171 case ARM_SMC_MM_RET_NO_MEMORY
:
172 // Unexpected error since the CommSize was checked for zero length
173 // prior to issuing the SMC
174 Status
= EFI_OUT_OF_RESOURCES
;
179 Status
= EFI_ACCESS_DENIED
;
187 // MM Communication Protocol instance
189 STATIC EFI_MM_COMMUNICATION2_PROTOCOL mMmCommunication2
= {
190 MmCommunication2Communicate
194 Notification callback on SetVirtualAddressMap event.
196 This function notifies the MM communication protocol interface on
197 SetVirtualAddressMap event and converts pointers used in this driver
198 from physical to virtual address.
200 @param Event SetVirtualAddressMap event.
201 @param Context A context when the SetVirtualAddressMap triggered.
203 @retval EFI_SUCCESS The function executed successfully.
204 @retval Other Some error occurred when executing this function.
210 NotifySetVirtualAddressMap (
217 Status
= gRT
->ConvertPointer (
219 (VOID
**)&mNsCommBuffMemRegion
.VirtualBase
221 if (EFI_ERROR (Status
)) {
222 DEBUG ((DEBUG_ERROR
, "NotifySetVirtualAddressMap():"
223 " Unable to convert MM runtime pointer. Status:0x%r\n", Status
));
230 GetMmCompatibility ()
234 ARM_SMC_ARGS MmVersionArgs
;
236 // MM_VERSION uses SMC32 calling conventions
237 MmVersionArgs
.Arg0
= ARM_SMC_ID_MM_VERSION_AARCH32
;
239 ArmCallSmc (&MmVersionArgs
);
241 MmVersion
= MmVersionArgs
.Arg0
;
243 if ((MM_MAJOR_VER(MmVersion
) == MM_CALLER_MAJOR_VER
) &&
244 (MM_MINOR_VER(MmVersion
) >= MM_CALLER_MINOR_VER
)) {
245 DEBUG ((DEBUG_INFO
, "MM Version: Major=0x%x, Minor=0x%x\n",
246 MM_MAJOR_VER(MmVersion
), MM_MINOR_VER(MmVersion
)));
247 Status
= EFI_SUCCESS
;
249 DEBUG ((DEBUG_ERROR
, "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n",
250 MM_MAJOR_VER(MmVersion
), MM_MINOR_VER(MmVersion
), MM_CALLER_MAJOR_VER
, MM_CALLER_MINOR_VER
));
251 Status
= EFI_UNSUPPORTED
;
257 STATIC EFI_GUID
* CONST mGuidedEventGuid
[] = {
258 &gEfiEndOfDxeEventGroupGuid
,
259 &gEfiEventExitBootServicesGuid
,
260 &gEfiEventReadyToBootGuid
,
263 STATIC EFI_EVENT mGuidedEvent
[ARRAY_SIZE (mGuidedEventGuid
)];
266 Event notification that is fired when GUIDed Event Group is signaled.
268 @param Event The Event that is being processed, not used.
269 @param Context Event Context, not used.
275 MmGuidedEventNotify (
280 EFI_MM_COMMUNICATE_HEADER Header
;
284 // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
286 CopyGuid (&Header
.HeaderGuid
, Context
);
287 Header
.MessageLength
= 1;
290 Size
= sizeof (Header
);
291 MmCommunication2Communicate (&mMmCommunication2
, &Header
, &Header
, &Size
);
295 The Entry Point for MM Communication
297 This function installs the MM communication protocol interface and finds out
298 what type of buffer management will be required prior to invoking the
301 @param ImageHandle The firmware allocated handle for the EFI image.
302 @param SystemTable A pointer to the EFI System Table.
304 @retval EFI_SUCCESS The entry point is executed successfully.
305 @retval Other Some error occurred when executing this entry point.
310 MmCommunication2Initialize (
311 IN EFI_HANDLE ImageHandle
,
312 IN EFI_SYSTEM_TABLE
*SystemTable
318 // Check if we can make the MM call
319 Status
= GetMmCompatibility ();
320 if (EFI_ERROR(Status
)) {
321 goto ReturnErrorStatus
;
324 mNsCommBuffMemRegion
.PhysicalBase
= PcdGet64 (PcdMmBufferBase
);
325 // During boot , Virtual and Physical are same
326 mNsCommBuffMemRegion
.VirtualBase
= mNsCommBuffMemRegion
.PhysicalBase
;
327 mNsCommBuffMemRegion
.Length
= PcdGet64 (PcdMmBufferSize
);
329 ASSERT (mNsCommBuffMemRegion
.PhysicalBase
!= 0);
331 ASSERT (mNsCommBuffMemRegion
.Length
!= 0);
333 Status
= gDS
->AddMemorySpace (
334 EfiGcdMemoryTypeReserved
,
335 mNsCommBuffMemRegion
.PhysicalBase
,
336 mNsCommBuffMemRegion
.Length
,
341 if (EFI_ERROR (Status
)) {
342 DEBUG ((DEBUG_ERROR
, "MmCommunicateInitialize: "
343 "Failed to add MM-NS Buffer Memory Space\n"));
344 goto ReturnErrorStatus
;
347 Status
= gDS
->SetMemorySpaceAttributes (
348 mNsCommBuffMemRegion
.PhysicalBase
,
349 mNsCommBuffMemRegion
.Length
,
350 EFI_MEMORY_WB
| EFI_MEMORY_XP
| EFI_MEMORY_RUNTIME
352 if (EFI_ERROR (Status
)) {
353 DEBUG ((DEBUG_ERROR
, "MmCommunicateInitialize: "
354 "Failed to set MM-NS Buffer Memory attributes\n"));
355 goto CleanAddedMemorySpace
;
358 // Install the communication protocol
359 Status
= gBS
->InstallProtocolInterface (
360 &mMmCommunicateHandle
,
361 &gEfiMmCommunication2ProtocolGuid
,
362 EFI_NATIVE_INTERFACE
,
365 if (EFI_ERROR(Status
)) {
366 DEBUG ((DEBUG_ERROR
, "MmCommunicationInitialize: "
367 "Failed to install MM communication protocol\n"));
368 goto CleanAddedMemorySpace
;
371 // Register notification callback when virtual address is associated
372 // with the physical address.
373 // Create a Set Virtual Address Map event.
374 Status
= gBS
->CreateEvent (
375 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
,
377 NotifySetVirtualAddressMap
,
379 &mSetVirtualAddressMapEvent
381 ASSERT_EFI_ERROR (Status
);
383 for (Index
= 0; Index
< ARRAY_SIZE (mGuidedEventGuid
); Index
++) {
384 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_CALLBACK
,
385 MmGuidedEventNotify
, mGuidedEventGuid
[Index
],
386 mGuidedEventGuid
[Index
], &mGuidedEvent
[Index
]);
387 ASSERT_EFI_ERROR (Status
);
388 if (EFI_ERROR (Status
)) {
389 while (Index
-- > 0) {
390 gBS
->CloseEvent (mGuidedEvent
[Index
]);
392 goto UninstallProtocol
;
398 gBS
->UninstallProtocolInterface (
399 mMmCommunicateHandle
,
400 &gEfiMmCommunication2ProtocolGuid
,
404 CleanAddedMemorySpace
:
405 gDS
->RemoveMemorySpace (
406 mNsCommBuffMemRegion
.PhysicalBase
,
407 mNsCommBuffMemRegion
.Length
411 return EFI_INVALID_PARAMETER
;