]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c
d06dcc4d2080cbb1db459adea79905d0a350ad3d
[mirror_edk2.git] / ArmPkg / Drivers / MmCommunicationDxe / MmCommunication.c
1 /** @file
2
3 Copyright (c) 2016-2018, ARM Limited. All rights reserved.
4
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
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>
18
19 #include <Protocol/MmCommunication.h>
20
21 #include <IndustryStandard/ArmStdSmc.h>
22
23 #include "MmCommunicate.h"
24
25 //
26 // Address, Length of the pre-allocated buffer for communication with the secure
27 // world.
28 //
29 STATIC ARM_MEMORY_REGION_DESCRIPTOR mNsCommBuffMemRegion;
30
31 // Notification event when virtual address map is set.
32 STATIC EFI_EVENT mSetVirtualAddressMapEvent;
33
34 //
35 // Handle to install the MM Communication Protocol
36 //
37 STATIC EFI_HANDLE mMmCommunicateHandle;
38
39 /**
40 Communicates with a registered handler.
41
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().
46
47 @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL
48 instance.
49 @param[in, out] CommBuffer A pointer to the buffer to convey
50 into MMRAM.
51 @param[in, out] CommSize The size of the data buffer being
52 passed in. This is optional.
53
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
67 **/
68 STATIC
69 EFI_STATUS
70 EFIAPI
71 MmCommunicationCommunicate (
72 IN CONST EFI_MM_COMMUNICATION_PROTOCOL *This,
73 IN OUT VOID *CommBuffer,
74 IN OUT UINTN *CommSize OPTIONAL
75 )
76 {
77 EFI_MM_COMMUNICATE_HEADER *CommunicateHeader;
78 ARM_SMC_ARGS CommunicateSmcArgs;
79 EFI_STATUS Status;
80 UINTN BufferSize;
81
82 Status = EFI_ACCESS_DENIED;
83 BufferSize = 0;
84
85 ZeroMem (&CommunicateSmcArgs, sizeof (ARM_SMC_ARGS));
86
87 //
88 // Check parameters
89 //
90 if (CommBuffer == NULL) {
91 return EFI_INVALID_PARAMETER;
92 }
93
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);
102
103 // If the length of the CommBuffer is 0 then return the expected length.
104 if (CommSize) {
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;
111 }
112 //
113 // CommSize must match MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
114 //
115 if (*CommSize != BufferSize) {
116 return EFI_INVALID_PARAMETER;
117 }
118 }
119
120 //
121 // If the buffer size is 0 or greater than what can be tolerated by the MM
122 // environment then return the expected size.
123 //
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;
130 }
131
132 // SMC Function ID
133 CommunicateSmcArgs.Arg0 = ARM_SMC_ID_MM_COMMUNICATE_AARCH64;
134
135 // Cookie
136 CommunicateSmcArgs.Arg1 = 0;
137
138 // Copy Communication Payload
139 CopyMem ((VOID *)mNsCommBuffMemRegion.VirtualBase, CommBuffer, BufferSize);
140
141 // comm_buffer_address (64-bit physical address)
142 CommunicateSmcArgs.Arg2 = (UINTN)mNsCommBuffMemRegion.PhysicalBase;
143
144 // comm_size_address (not used, indicated by setting to zero)
145 CommunicateSmcArgs.Arg3 = 0;
146
147 // Call the Standalone MM environment.
148 ArmCallSmc (&CommunicateSmcArgs);
149
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);
159
160 CopyMem (
161 CommBuffer,
162 (VOID *)mNsCommBuffMemRegion.VirtualBase,
163 BufferSize
164 );
165 Status = EFI_SUCCESS;
166 break;
167
168 case ARM_SMC_MM_RET_INVALID_PARAMS:
169 Status = EFI_INVALID_PARAMETER;
170 break;
171
172 case ARM_SMC_MM_RET_DENIED:
173 Status = EFI_ACCESS_DENIED;
174 break;
175
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;
180 ASSERT (0);
181 break;
182
183 default:
184 Status = EFI_ACCESS_DENIED;
185 ASSERT (0);
186 }
187
188 return Status;
189 }
190
191 //
192 // MM Communication Protocol instance
193 //
194 EFI_MM_COMMUNICATION_PROTOCOL mMmCommunication = {
195 MmCommunicationCommunicate
196 };
197
198 /**
199 Notification callback on SetVirtualAddressMap event.
200
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.
204
205 @param Event SetVirtualAddressMap event.
206 @param Context A context when the SetVirtualAddressMap triggered.
207
208 @retval EFI_SUCCESS The function executed successfully.
209 @retval Other Some error occurred when executing this function.
210
211 **/
212 STATIC
213 VOID
214 EFIAPI
215 NotifySetVirtualAddressMap (
216 IN EFI_EVENT Event,
217 IN VOID *Context
218 )
219 {
220 EFI_STATUS Status;
221
222 Status = gRT->ConvertPointer (
223 EFI_OPTIONAL_PTR,
224 (VOID **)&mNsCommBuffMemRegion.VirtualBase
225 );
226 if (EFI_ERROR (Status)) {
227 DEBUG ((DEBUG_ERROR, "NotifySetVirtualAddressMap():"
228 " Unable to convert MM runtime pointer. Status:0x%r\n", Status));
229 }
230
231 }
232
233 STATIC
234 EFI_STATUS
235 GetMmCompatibility ()
236 {
237 EFI_STATUS Status;
238 UINT32 MmVersion;
239 ARM_SMC_ARGS MmVersionArgs;
240
241 // MM_VERSION uses SMC32 calling conventions
242 MmVersionArgs.Arg0 = ARM_SMC_ID_MM_VERSION_AARCH32;
243
244 ArmCallSmc (&MmVersionArgs);
245
246 MmVersion = MmVersionArgs.Arg0;
247
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;
253 } else {
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;
257 }
258
259 return Status;
260 }
261
262 /**
263 The Entry Point for MM Communication
264
265 This function installs the MM communication protocol interface and finds out
266 what type of buffer management will be required prior to invoking the
267 communication SMC.
268
269 @param ImageHandle The firmware allocated handle for the EFI image.
270 @param SystemTable A pointer to the EFI System Table.
271
272 @retval EFI_SUCCESS The entry point is executed successfully.
273 @retval Other Some error occurred when executing this entry point.
274
275 **/
276 EFI_STATUS
277 EFIAPI
278 MmCommunicationInitialize (
279 IN EFI_HANDLE ImageHandle,
280 IN EFI_SYSTEM_TABLE *SystemTable
281 )
282 {
283 EFI_STATUS Status;
284
285 // Check if we can make the MM call
286 Status = GetMmCompatibility ();
287 if (EFI_ERROR(Status)) {
288 goto ReturnErrorStatus;
289 }
290
291 mNsCommBuffMemRegion.PhysicalBase = PcdGet64 (PcdMmBufferBase);
292 // During boot , Virtual and Physical are same
293 mNsCommBuffMemRegion.VirtualBase = mNsCommBuffMemRegion.PhysicalBase;
294 mNsCommBuffMemRegion.Length = PcdGet64 (PcdMmBufferSize);
295
296 ASSERT (mNsCommBuffMemRegion.PhysicalBase != 0);
297
298 ASSERT (mNsCommBuffMemRegion.Length != 0);
299
300 Status = gDS->AddMemorySpace (
301 EfiGcdMemoryTypeReserved,
302 mNsCommBuffMemRegion.PhysicalBase,
303 mNsCommBuffMemRegion.Length,
304 EFI_MEMORY_WB |
305 EFI_MEMORY_XP |
306 EFI_MEMORY_RUNTIME
307 );
308 if (EFI_ERROR (Status)) {
309 DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "
310 "Failed to add MM-NS Buffer Memory Space\n"));
311 goto ReturnErrorStatus;
312 }
313
314 Status = gDS->SetMemorySpaceAttributes (
315 mNsCommBuffMemRegion.PhysicalBase,
316 mNsCommBuffMemRegion.Length,
317 EFI_MEMORY_WB | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME
318 );
319 if (EFI_ERROR (Status)) {
320 DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "
321 "Failed to set MM-NS Buffer Memory attributes\n"));
322 goto CleanAddedMemorySpace;
323 }
324
325 // Install the communication protocol
326 Status = gBS->InstallProtocolInterface (
327 &mMmCommunicateHandle,
328 &gEfiMmCommunicationProtocolGuid,
329 EFI_NATIVE_INTERFACE,
330 &mMmCommunication
331 );
332 if (EFI_ERROR(Status)) {
333 DEBUG ((DEBUG_ERROR, "MmCommunicationInitialize: "
334 "Failed to install MM communication protocol\n"));
335 goto CleanAddedMemorySpace;
336 }
337
338 // Register notification callback when virtual address is associated
339 // with the physical address.
340 // Create a Set Virtual Address Map event.
341 Status = gBS->CreateEvent (
342 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
343 TPL_NOTIFY,
344 NotifySetVirtualAddressMap,
345 NULL,
346 &mSetVirtualAddressMapEvent
347 );
348 if (Status == EFI_SUCCESS) {
349 return Status;
350 }
351
352 gBS->UninstallProtocolInterface (
353 mMmCommunicateHandle,
354 &gEfiMmCommunicationProtocolGuid,
355 &mMmCommunication
356 );
357
358 CleanAddedMemorySpace:
359 gDS->RemoveMemorySpace (
360 mNsCommBuffMemRegion.PhysicalBase,
361 mNsCommBuffMemRegion.Length
362 );
363
364 ReturnErrorStatus:
365 return EFI_INVALID_PARAMETER;
366 }