3 Copyright (c) 2016-2018, ARM Limited. All rights reserved.
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include <Library/ArmLib.h>
16 #include <Library/ArmSmcLib.h>
17 #include <Library/BaseMemoryLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/DxeServicesTableLib.h>
20 #include <Library/HobLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/UefiRuntimeServicesTableLib.h>
25 #include <Protocol/MmCommunication.h>
27 #include <IndustryStandard/ArmStdSmc.h>
29 #include "MmCommunicate.h"
32 // Address, Length of the pre-allocated buffer for communication with the secure
35 STATIC ARM_MEMORY_REGION_DESCRIPTOR mNsCommBuffMemRegion
;
37 // Notification event when virtual address map is set.
38 STATIC EFI_EVENT mSetVirtualAddressMapEvent
;
41 // Handle to install the MM Communication Protocol
43 STATIC EFI_HANDLE mMmCommunicateHandle
;
46 Communicates with a registered handler.
48 This function provides an interface to send and receive messages to the
49 Standalone MM environment on behalf of UEFI services. This function is part
50 of the MM Communication Protocol that may be called in physical mode prior to
51 SetVirtualAddressMap() and in virtual mode after SetVirtualAddressMap().
53 @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL
55 @param[in, out] CommBuffer A pointer to the buffer to convey
57 @param[in, out] CommSize The size of the data buffer being
58 passed in. This is optional.
60 @retval EFI_SUCCESS The message was successfully posted.
61 @retval EFI_INVALID_PARAMETER The CommBuffer was NULL.
62 @retval EFI_BAD_BUFFER_SIZE The buffer size is incorrect for the MM
63 implementation. If this error is
64 returned, the MessageLength field in
65 the CommBuffer header or the integer
66 pointed by CommSize are updated to reflect
67 the maximum payload size the
68 implementation can accommodate.
69 @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter
70 or CommSize parameter, if not omitted,
71 are in address range that cannot be
72 accessed by the MM environment
77 MmCommunicationCommunicate (
78 IN CONST EFI_MM_COMMUNICATION_PROTOCOL
*This
,
79 IN OUT VOID
*CommBuffer
,
80 IN OUT UINTN
*CommSize OPTIONAL
83 EFI_MM_COMMUNICATE_HEADER
*CommunicateHeader
;
84 ARM_SMC_ARGS CommunicateSmcArgs
;
88 Status
= EFI_ACCESS_DENIED
;
91 ZeroMem (&CommunicateSmcArgs
, sizeof (ARM_SMC_ARGS
));
96 if (CommBuffer
== NULL
) {
97 return EFI_INVALID_PARAMETER
;
100 CommunicateHeader
= CommBuffer
;
101 // CommBuffer is a mandatory parameter. Hence, Rely on
102 // MessageLength + Header to ascertain the
103 // total size of the communication payload rather than
104 // rely on optional CommSize parameter
105 BufferSize
= CommunicateHeader
->MessageLength
+
106 sizeof (CommunicateHeader
->HeaderGuid
) +
107 sizeof (CommunicateHeader
->MessageLength
);
109 // If the length of the CommBuffer is 0 then return the expected length.
111 // This case can be used by the consumer of this driver to find out the
112 // max size that can be used for allocating CommBuffer.
113 if ((*CommSize
== 0) ||
114 (*CommSize
> mNsCommBuffMemRegion
.Length
)) {
115 *CommSize
= mNsCommBuffMemRegion
.Length
;
116 return EFI_BAD_BUFFER_SIZE
;
119 // CommSize must match MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
121 if (*CommSize
!= BufferSize
) {
122 return EFI_INVALID_PARAMETER
;
127 // If the buffer size is 0 or greater than what can be tolerated by the MM
128 // environment then return the expected size.
130 if ((BufferSize
== 0) ||
131 (BufferSize
> mNsCommBuffMemRegion
.Length
)) {
132 CommunicateHeader
->MessageLength
= mNsCommBuffMemRegion
.Length
-
133 sizeof (CommunicateHeader
->HeaderGuid
) -
134 sizeof (CommunicateHeader
->MessageLength
);
135 return EFI_BAD_BUFFER_SIZE
;
139 CommunicateSmcArgs
.Arg0
= ARM_SMC_ID_MM_COMMUNICATE_AARCH64
;
142 CommunicateSmcArgs
.Arg1
= 0;
144 // Copy Communication Payload
145 CopyMem ((VOID
*)mNsCommBuffMemRegion
.VirtualBase
, CommBuffer
, BufferSize
);
147 // comm_buffer_address (64-bit physical address)
148 CommunicateSmcArgs
.Arg2
= (UINTN
)mNsCommBuffMemRegion
.PhysicalBase
;
150 // comm_size_address (not used, indicated by setting to zero)
151 CommunicateSmcArgs
.Arg3
= 0;
153 // Call the Standalone MM environment.
154 ArmCallSmc (&CommunicateSmcArgs
);
156 switch (CommunicateSmcArgs
.Arg0
) {
157 case ARM_SMC_MM_RET_SUCCESS
:
158 ZeroMem (CommBuffer
, BufferSize
);
159 // On successful return, the size of data being returned is inferred from
160 // MessageLength + Header.
161 CommunicateHeader
= (EFI_MM_COMMUNICATE_HEADER
*)mNsCommBuffMemRegion
.VirtualBase
;
162 BufferSize
= CommunicateHeader
->MessageLength
+
163 sizeof (CommunicateHeader
->HeaderGuid
) +
164 sizeof (CommunicateHeader
->MessageLength
);
168 (VOID
*)mNsCommBuffMemRegion
.VirtualBase
,
171 Status
= EFI_SUCCESS
;
174 case ARM_SMC_MM_RET_INVALID_PARAMS
:
175 Status
= EFI_INVALID_PARAMETER
;
178 case ARM_SMC_MM_RET_DENIED
:
179 Status
= EFI_ACCESS_DENIED
;
182 case ARM_SMC_MM_RET_NO_MEMORY
:
183 // Unexpected error since the CommSize was checked for zero length
184 // prior to issuing the SMC
185 Status
= EFI_OUT_OF_RESOURCES
;
190 Status
= EFI_ACCESS_DENIED
;
198 // MM Communication Protocol instance
200 EFI_MM_COMMUNICATION_PROTOCOL mMmCommunication
= {
201 MmCommunicationCommunicate
205 Notification callback on SetVirtualAddressMap event.
207 This function notifies the MM communication protocol interface on
208 SetVirtualAddressMap event and converts pointers used in this driver
209 from physical to virtual address.
211 @param Event SetVirtualAddressMap event.
212 @param Context A context when the SetVirtualAddressMap triggered.
214 @retval EFI_SUCCESS The function executed successfully.
215 @retval Other Some error occurred when executing this function.
221 NotifySetVirtualAddressMap (
228 Status
= gRT
->ConvertPointer (
230 (VOID
**)&mNsCommBuffMemRegion
.VirtualBase
232 if (EFI_ERROR (Status
)) {
233 DEBUG ((DEBUG_ERROR
, "NotifySetVirtualAddressMap():"
234 " Unable to convert MM runtime pointer. Status:0x%r\n", Status
));
241 GetMmCompatibility ()
245 ARM_SMC_ARGS MmVersionArgs
;
247 // MM_VERSION uses SMC32 calling conventions
248 MmVersionArgs
.Arg0
= ARM_SMC_ID_MM_VERSION_AARCH32
;
250 ArmCallSmc (&MmVersionArgs
);
252 MmVersion
= MmVersionArgs
.Arg0
;
254 if ((MM_MAJOR_VER(MmVersion
) == MM_CALLER_MAJOR_VER
) &&
255 (MM_MINOR_VER(MmVersion
) >= MM_CALLER_MINOR_VER
)) {
256 DEBUG ((DEBUG_INFO
, "MM Version: Major=0x%x, Minor=0x%x\n",
257 MM_MAJOR_VER(MmVersion
), MM_MINOR_VER(MmVersion
)));
258 Status
= EFI_SUCCESS
;
260 DEBUG ((DEBUG_ERROR
, "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n",
261 MM_MAJOR_VER(MmVersion
), MM_MINOR_VER(MmVersion
), MM_CALLER_MAJOR_VER
, MM_CALLER_MINOR_VER
));
262 Status
= EFI_UNSUPPORTED
;
269 The Entry Point for MM Communication
271 This function installs the MM communication protocol interface and finds out
272 what type of buffer management will be required prior to invoking the
275 @param ImageHandle The firmware allocated handle for the EFI image.
276 @param SystemTable A pointer to the EFI System Table.
278 @retval EFI_SUCCESS The entry point is executed successfully.
279 @retval Other Some error occurred when executing this entry point.
284 MmCommunicationInitialize (
285 IN EFI_HANDLE ImageHandle
,
286 IN EFI_SYSTEM_TABLE
*SystemTable
291 // Check if we can make the MM call
292 Status
= GetMmCompatibility ();
293 if (EFI_ERROR(Status
)) {
294 goto ReturnErrorStatus
;
297 mNsCommBuffMemRegion
.PhysicalBase
= PcdGet64 (PcdMmBufferBase
);
298 // During boot , Virtual and Physical are same
299 mNsCommBuffMemRegion
.VirtualBase
= mNsCommBuffMemRegion
.PhysicalBase
;
300 mNsCommBuffMemRegion
.Length
= PcdGet64 (PcdMmBufferSize
);
302 ASSERT (mNsCommBuffMemRegion
.PhysicalBase
!= 0);
304 ASSERT (mNsCommBuffMemRegion
.Length
!= 0);
306 Status
= gDS
->AddMemorySpace (
307 EfiGcdMemoryTypeReserved
,
308 mNsCommBuffMemRegion
.PhysicalBase
,
309 mNsCommBuffMemRegion
.Length
,
314 if (EFI_ERROR (Status
)) {
315 DEBUG ((DEBUG_ERROR
, "MmCommunicateInitialize: "
316 "Failed to add MM-NS Buffer Memory Space\n"));
317 goto ReturnErrorStatus
;
320 Status
= gDS
->SetMemorySpaceAttributes (
321 mNsCommBuffMemRegion
.PhysicalBase
,
322 mNsCommBuffMemRegion
.Length
,
323 EFI_MEMORY_WB
| EFI_MEMORY_XP
| EFI_MEMORY_RUNTIME
325 if (EFI_ERROR (Status
)) {
326 DEBUG ((DEBUG_ERROR
, "MmCommunicateInitialize: "
327 "Failed to set MM-NS Buffer Memory attributes\n"));
328 goto CleanAddedMemorySpace
;
331 // Install the communication protocol
332 Status
= gBS
->InstallProtocolInterface (
333 &mMmCommunicateHandle
,
334 &gEfiMmCommunicationProtocolGuid
,
335 EFI_NATIVE_INTERFACE
,
338 if (EFI_ERROR(Status
)) {
339 DEBUG ((DEBUG_ERROR
, "MmCommunicationInitialize: "
340 "Failed to install MM communication protocol\n"));
341 goto CleanAddedMemorySpace
;
344 // Register notification callback when virtual address is associated
345 // with the physical address.
346 // Create a Set Virtual Address Map event.
347 Status
= gBS
->CreateEvent (
348 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
,
350 NotifySetVirtualAddressMap
,
352 &mSetVirtualAddressMapEvent
354 if (Status
== EFI_SUCCESS
) {
358 gBS
->UninstallProtocolInterface (
359 mMmCommunicateHandle
,
360 &gEfiMmCommunicationProtocolGuid
,
364 CleanAddedMemorySpace
:
365 gDS
->RemoveMemorySpace (
366 mNsCommBuffMemRegion
.PhysicalBase
,
367 mNsCommBuffMemRegion
.Length
371 return EFI_INVALID_PARAMETER
;