3 Copyright (c) 2016-2018, 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/MmCommunication.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 an interface to send and receive messages to the
43 Standalone MM environment on behalf of UEFI services. This function is part
44 of the MM Communication Protocol that may be called in physical mode prior to
45 SetVirtualAddressMap() and in virtual mode after SetVirtualAddressMap().
47 @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL
49 @param[in, out] CommBuffer A pointer to the buffer to convey
51 @param[in, out] CommSize The size of the data buffer being
52 passed in. This is optional.
54 @retval EFI_SUCCESS The message was successfully posted.
55 @retval EFI_INVALID_PARAMETER The CommBuffer was NULL.
56 @retval EFI_BAD_BUFFER_SIZE The buffer size is incorrect for the MM
57 implementation. If this error is
58 returned, the MessageLength field in
59 the CommBuffer header or the integer
60 pointed by CommSize are updated to reflect
61 the maximum payload size the
62 implementation can accommodate.
63 @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter
64 or CommSize parameter, if not omitted,
65 are in address range that cannot be
66 accessed by the MM environment
71 MmCommunicationCommunicate (
72 IN CONST EFI_MM_COMMUNICATION_PROTOCOL
*This
,
73 IN OUT VOID
*CommBuffer
,
74 IN OUT UINTN
*CommSize OPTIONAL
77 EFI_MM_COMMUNICATE_HEADER
*CommunicateHeader
;
78 ARM_SMC_ARGS CommunicateSmcArgs
;
82 Status
= EFI_ACCESS_DENIED
;
85 ZeroMem (&CommunicateSmcArgs
, sizeof (ARM_SMC_ARGS
));
90 if (CommBuffer
== NULL
) {
91 return EFI_INVALID_PARAMETER
;
94 CommunicateHeader
= CommBuffer
;
95 // CommBuffer is a mandatory parameter. Hence, Rely on
96 // MessageLength + Header to ascertain the
97 // total size of the communication payload rather than
98 // rely on optional CommSize parameter
99 BufferSize
= CommunicateHeader
->MessageLength
+
100 sizeof (CommunicateHeader
->HeaderGuid
) +
101 sizeof (CommunicateHeader
->MessageLength
);
103 // If the length of the CommBuffer is 0 then return the expected length.
105 // This case can be used by the consumer of this driver to find out the
106 // max size that can be used for allocating CommBuffer.
107 if ((*CommSize
== 0) ||
108 (*CommSize
> mNsCommBuffMemRegion
.Length
)) {
109 *CommSize
= mNsCommBuffMemRegion
.Length
;
110 return EFI_BAD_BUFFER_SIZE
;
113 // CommSize must match MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
115 if (*CommSize
!= BufferSize
) {
116 return EFI_INVALID_PARAMETER
;
121 // If the buffer size is 0 or greater than what can be tolerated by the MM
122 // environment then return the expected size.
124 if ((BufferSize
== 0) ||
125 (BufferSize
> mNsCommBuffMemRegion
.Length
)) {
126 CommunicateHeader
->MessageLength
= mNsCommBuffMemRegion
.Length
-
127 sizeof (CommunicateHeader
->HeaderGuid
) -
128 sizeof (CommunicateHeader
->MessageLength
);
129 return EFI_BAD_BUFFER_SIZE
;
133 CommunicateSmcArgs
.Arg0
= ARM_SMC_ID_MM_COMMUNICATE_AARCH64
;
136 CommunicateSmcArgs
.Arg1
= 0;
138 // Copy Communication Payload
139 CopyMem ((VOID
*)mNsCommBuffMemRegion
.VirtualBase
, CommBuffer
, BufferSize
);
141 // comm_buffer_address (64-bit physical address)
142 CommunicateSmcArgs
.Arg2
= (UINTN
)mNsCommBuffMemRegion
.PhysicalBase
;
144 // comm_size_address (not used, indicated by setting to zero)
145 CommunicateSmcArgs
.Arg3
= 0;
147 // Call the Standalone MM environment.
148 ArmCallSmc (&CommunicateSmcArgs
);
150 switch (CommunicateSmcArgs
.Arg0
) {
151 case ARM_SMC_MM_RET_SUCCESS
:
152 ZeroMem (CommBuffer
, BufferSize
);
153 // On successful return, the size of data being returned is inferred from
154 // MessageLength + Header.
155 CommunicateHeader
= (EFI_MM_COMMUNICATE_HEADER
*)mNsCommBuffMemRegion
.VirtualBase
;
156 BufferSize
= CommunicateHeader
->MessageLength
+
157 sizeof (CommunicateHeader
->HeaderGuid
) +
158 sizeof (CommunicateHeader
->MessageLength
);
162 (VOID
*)mNsCommBuffMemRegion
.VirtualBase
,
165 Status
= EFI_SUCCESS
;
168 case ARM_SMC_MM_RET_INVALID_PARAMS
:
169 Status
= EFI_INVALID_PARAMETER
;
172 case ARM_SMC_MM_RET_DENIED
:
173 Status
= EFI_ACCESS_DENIED
;
176 case ARM_SMC_MM_RET_NO_MEMORY
:
177 // Unexpected error since the CommSize was checked for zero length
178 // prior to issuing the SMC
179 Status
= EFI_OUT_OF_RESOURCES
;
184 Status
= EFI_ACCESS_DENIED
;
192 // MM Communication Protocol instance
194 EFI_MM_COMMUNICATION_PROTOCOL mMmCommunication
= {
195 MmCommunicationCommunicate
199 Notification callback on SetVirtualAddressMap event.
201 This function notifies the MM communication protocol interface on
202 SetVirtualAddressMap event and converts pointers used in this driver
203 from physical to virtual address.
205 @param Event SetVirtualAddressMap event.
206 @param Context A context when the SetVirtualAddressMap triggered.
208 @retval EFI_SUCCESS The function executed successfully.
209 @retval Other Some error occurred when executing this function.
215 NotifySetVirtualAddressMap (
222 Status
= gRT
->ConvertPointer (
224 (VOID
**)&mNsCommBuffMemRegion
.VirtualBase
226 if (EFI_ERROR (Status
)) {
227 DEBUG ((DEBUG_ERROR
, "NotifySetVirtualAddressMap():"
228 " Unable to convert MM runtime pointer. Status:0x%r\n", Status
));
235 GetMmCompatibility ()
239 ARM_SMC_ARGS MmVersionArgs
;
241 // MM_VERSION uses SMC32 calling conventions
242 MmVersionArgs
.Arg0
= ARM_SMC_ID_MM_VERSION_AARCH32
;
244 ArmCallSmc (&MmVersionArgs
);
246 MmVersion
= MmVersionArgs
.Arg0
;
248 if ((MM_MAJOR_VER(MmVersion
) == MM_CALLER_MAJOR_VER
) &&
249 (MM_MINOR_VER(MmVersion
) >= MM_CALLER_MINOR_VER
)) {
250 DEBUG ((DEBUG_INFO
, "MM Version: Major=0x%x, Minor=0x%x\n",
251 MM_MAJOR_VER(MmVersion
), MM_MINOR_VER(MmVersion
)));
252 Status
= EFI_SUCCESS
;
254 DEBUG ((DEBUG_ERROR
, "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n",
255 MM_MAJOR_VER(MmVersion
), MM_MINOR_VER(MmVersion
), MM_CALLER_MAJOR_VER
, MM_CALLER_MINOR_VER
));
256 Status
= EFI_UNSUPPORTED
;
262 STATIC EFI_GUID
* CONST mGuidedEventGuid
[] = {
263 &gEfiEndOfDxeEventGroupGuid
,
264 &gEfiEventExitBootServicesGuid
,
265 &gEfiEventReadyToBootGuid
,
268 STATIC EFI_EVENT mGuidedEvent
[ARRAY_SIZE (mGuidedEventGuid
)];
271 Event notification that is fired when GUIDed Event Group is signaled.
273 @param Event The Event that is being processed, not used.
274 @param Context Event Context, not used.
280 MmGuidedEventNotify (
285 EFI_MM_COMMUNICATE_HEADER Header
;
289 // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
291 CopyGuid (&Header
.HeaderGuid
, Context
);
292 Header
.MessageLength
= 1;
295 Size
= sizeof (Header
);
296 MmCommunicationCommunicate (&mMmCommunication
, &Header
, &Size
);
300 The Entry Point for MM Communication
302 This function installs the MM communication protocol interface and finds out
303 what type of buffer management will be required prior to invoking the
306 @param ImageHandle The firmware allocated handle for the EFI image.
307 @param SystemTable A pointer to the EFI System Table.
309 @retval EFI_SUCCESS The entry point is executed successfully.
310 @retval Other Some error occurred when executing this entry point.
315 MmCommunicationInitialize (
316 IN EFI_HANDLE ImageHandle
,
317 IN EFI_SYSTEM_TABLE
*SystemTable
323 // Check if we can make the MM call
324 Status
= GetMmCompatibility ();
325 if (EFI_ERROR(Status
)) {
326 goto ReturnErrorStatus
;
329 mNsCommBuffMemRegion
.PhysicalBase
= PcdGet64 (PcdMmBufferBase
);
330 // During boot , Virtual and Physical are same
331 mNsCommBuffMemRegion
.VirtualBase
= mNsCommBuffMemRegion
.PhysicalBase
;
332 mNsCommBuffMemRegion
.Length
= PcdGet64 (PcdMmBufferSize
);
334 ASSERT (mNsCommBuffMemRegion
.PhysicalBase
!= 0);
336 ASSERT (mNsCommBuffMemRegion
.Length
!= 0);
338 Status
= gDS
->AddMemorySpace (
339 EfiGcdMemoryTypeReserved
,
340 mNsCommBuffMemRegion
.PhysicalBase
,
341 mNsCommBuffMemRegion
.Length
,
346 if (EFI_ERROR (Status
)) {
347 DEBUG ((DEBUG_ERROR
, "MmCommunicateInitialize: "
348 "Failed to add MM-NS Buffer Memory Space\n"));
349 goto ReturnErrorStatus
;
352 Status
= gDS
->SetMemorySpaceAttributes (
353 mNsCommBuffMemRegion
.PhysicalBase
,
354 mNsCommBuffMemRegion
.Length
,
355 EFI_MEMORY_WB
| EFI_MEMORY_XP
| EFI_MEMORY_RUNTIME
357 if (EFI_ERROR (Status
)) {
358 DEBUG ((DEBUG_ERROR
, "MmCommunicateInitialize: "
359 "Failed to set MM-NS Buffer Memory attributes\n"));
360 goto CleanAddedMemorySpace
;
363 // Install the communication protocol
364 Status
= gBS
->InstallProtocolInterface (
365 &mMmCommunicateHandle
,
366 &gEfiMmCommunicationProtocolGuid
,
367 EFI_NATIVE_INTERFACE
,
370 if (EFI_ERROR(Status
)) {
371 DEBUG ((DEBUG_ERROR
, "MmCommunicationInitialize: "
372 "Failed to install MM communication protocol\n"));
373 goto CleanAddedMemorySpace
;
376 // Register notification callback when virtual address is associated
377 // with the physical address.
378 // Create a Set Virtual Address Map event.
379 Status
= gBS
->CreateEvent (
380 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
,
382 NotifySetVirtualAddressMap
,
384 &mSetVirtualAddressMapEvent
386 ASSERT_EFI_ERROR (Status
);
388 for (Index
= 0; Index
< ARRAY_SIZE (mGuidedEventGuid
); Index
++) {
389 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_CALLBACK
,
390 MmGuidedEventNotify
, mGuidedEventGuid
[Index
],
391 mGuidedEventGuid
[Index
], &mGuidedEvent
[Index
]);
392 ASSERT_EFI_ERROR (Status
);
393 if (EFI_ERROR (Status
)) {
394 while (Index
-- > 0) {
395 gBS
->CloseEvent (mGuidedEvent
[Index
]);
397 goto UninstallProtocol
;
403 gBS
->UninstallProtocolInterface (
404 mMmCommunicateHandle
,
405 &gEfiMmCommunicationProtocolGuid
,
409 CleanAddedMemorySpace
:
410 gDS
->RemoveMemorySpace (
411 mNsCommBuffMemRegion
.PhysicalBase
,
412 mNsCommBuffMemRegion
.Length
416 return EFI_INVALID_PARAMETER
;