]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c
ArmPkg/MmCommunicationDxe: fix logic bug in DXE driver entrypoint
[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 STATIC EFI_GUID* CONST mGuidedEventGuid[] = {
263 &gEfiEndOfDxeEventGroupGuid,
264 &gEfiEventExitBootServicesGuid,
265 &gEfiEventReadyToBootGuid,
266 };
267
268 STATIC EFI_EVENT mGuidedEvent[ARRAY_SIZE (mGuidedEventGuid)];
269
270 /**
271 Event notification that is fired when GUIDed Event Group is signaled.
272
273 @param Event The Event that is being processed, not used.
274 @param Context Event Context, not used.
275
276 **/
277 STATIC
278 VOID
279 EFIAPI
280 MmGuidedEventNotify (
281 IN EFI_EVENT Event,
282 IN VOID *Context
283 )
284 {
285 EFI_MM_COMMUNICATE_HEADER Header;
286 UINTN Size;
287
288 //
289 // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
290 //
291 CopyGuid (&Header.HeaderGuid, Context);
292 Header.MessageLength = 1;
293 Header.Data[0] = 0;
294
295 Size = sizeof (Header);
296 MmCommunicationCommunicate (&mMmCommunication, &Header, &Size);
297 }
298
299 /**
300 The Entry Point for MM Communication
301
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
304 communication SMC.
305
306 @param ImageHandle The firmware allocated handle for the EFI image.
307 @param SystemTable A pointer to the EFI System Table.
308
309 @retval EFI_SUCCESS The entry point is executed successfully.
310 @retval Other Some error occurred when executing this entry point.
311
312 **/
313 EFI_STATUS
314 EFIAPI
315 MmCommunicationInitialize (
316 IN EFI_HANDLE ImageHandle,
317 IN EFI_SYSTEM_TABLE *SystemTable
318 )
319 {
320 EFI_STATUS Status;
321 UINTN Index;
322
323 // Check if we can make the MM call
324 Status = GetMmCompatibility ();
325 if (EFI_ERROR(Status)) {
326 goto ReturnErrorStatus;
327 }
328
329 mNsCommBuffMemRegion.PhysicalBase = PcdGet64 (PcdMmBufferBase);
330 // During boot , Virtual and Physical are same
331 mNsCommBuffMemRegion.VirtualBase = mNsCommBuffMemRegion.PhysicalBase;
332 mNsCommBuffMemRegion.Length = PcdGet64 (PcdMmBufferSize);
333
334 ASSERT (mNsCommBuffMemRegion.PhysicalBase != 0);
335
336 ASSERT (mNsCommBuffMemRegion.Length != 0);
337
338 Status = gDS->AddMemorySpace (
339 EfiGcdMemoryTypeReserved,
340 mNsCommBuffMemRegion.PhysicalBase,
341 mNsCommBuffMemRegion.Length,
342 EFI_MEMORY_WB |
343 EFI_MEMORY_XP |
344 EFI_MEMORY_RUNTIME
345 );
346 if (EFI_ERROR (Status)) {
347 DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "
348 "Failed to add MM-NS Buffer Memory Space\n"));
349 goto ReturnErrorStatus;
350 }
351
352 Status = gDS->SetMemorySpaceAttributes (
353 mNsCommBuffMemRegion.PhysicalBase,
354 mNsCommBuffMemRegion.Length,
355 EFI_MEMORY_WB | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME
356 );
357 if (EFI_ERROR (Status)) {
358 DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "
359 "Failed to set MM-NS Buffer Memory attributes\n"));
360 goto CleanAddedMemorySpace;
361 }
362
363 // Install the communication protocol
364 Status = gBS->InstallProtocolInterface (
365 &mMmCommunicateHandle,
366 &gEfiMmCommunicationProtocolGuid,
367 EFI_NATIVE_INTERFACE,
368 &mMmCommunication
369 );
370 if (EFI_ERROR(Status)) {
371 DEBUG ((DEBUG_ERROR, "MmCommunicationInitialize: "
372 "Failed to install MM communication protocol\n"));
373 goto CleanAddedMemorySpace;
374 }
375
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,
381 TPL_NOTIFY,
382 NotifySetVirtualAddressMap,
383 NULL,
384 &mSetVirtualAddressMapEvent
385 );
386 ASSERT_EFI_ERROR (Status);
387
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]);
396 }
397 goto UninstallProtocol;
398 }
399 }
400 return EFI_SUCCESS;
401
402 UninstallProtocol:
403 gBS->UninstallProtocolInterface (
404 mMmCommunicateHandle,
405 &gEfiMmCommunicationProtocolGuid,
406 &mMmCommunication
407 );
408
409 CleanAddedMemorySpace:
410 gDS->RemoveMemorySpace (
411 mNsCommBuffMemRegion.PhysicalBase,
412 mNsCommBuffMemRegion.Length
413 );
414
415 ReturnErrorStatus:
416 return EFI_INVALID_PARAMETER;
417 }