]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c
ArmPkg: Apply uncrustify changes
[mirror_edk2.git] / ArmPkg / Drivers / MmCommunicationDxe / MmCommunication.c
1 /** @file
2
3 Copyright (c) 2016-2021, Arm Limited. All rights reserved.<BR>
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/MmCommunication2.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 a service to send and receive messages from a registered UEFI service.
43
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.
50
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.
61
62 **/
63 EFI_STATUS
64 EFIAPI
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
70 )
71 {
72 EFI_MM_COMMUNICATE_HEADER *CommunicateHeader;
73 ARM_SMC_ARGS CommunicateSmcArgs;
74 EFI_STATUS Status;
75 UINTN BufferSize;
76
77 Status = EFI_ACCESS_DENIED;
78 BufferSize = 0;
79
80 ZeroMem (&CommunicateSmcArgs, sizeof (ARM_SMC_ARGS));
81
82 //
83 // Check parameters
84 //
85 if (CommBufferVirtual == NULL) {
86 return EFI_INVALID_PARAMETER;
87 }
88
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);
97
98 // If the length of the CommBuffer is 0 then return the expected length.
99 if (CommSize != 0) {
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 {
105 *CommSize = mNsCommBuffMemRegion.Length;
106 return EFI_BAD_BUFFER_SIZE;
107 }
108
109 //
110 // CommSize must match MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
111 //
112 if (*CommSize != BufferSize) {
113 return EFI_INVALID_PARAMETER;
114 }
115 }
116
117 //
118 // If the buffer size is 0 or greater than what can be tolerated by the MM
119 // environment then return the expected size.
120 //
121 if ((BufferSize == 0) ||
122 (BufferSize > mNsCommBuffMemRegion.Length))
123 {
124 CommunicateHeader->MessageLength = mNsCommBuffMemRegion.Length -
125 sizeof (CommunicateHeader->HeaderGuid) -
126 sizeof (CommunicateHeader->MessageLength);
127 return EFI_BAD_BUFFER_SIZE;
128 }
129
130 // SMC Function ID
131 CommunicateSmcArgs.Arg0 = ARM_SMC_ID_MM_COMMUNICATE_AARCH64;
132
133 // Cookie
134 CommunicateSmcArgs.Arg1 = 0;
135
136 // Copy Communication Payload
137 CopyMem ((VOID *)mNsCommBuffMemRegion.VirtualBase, CommBufferVirtual, BufferSize);
138
139 // comm_buffer_address (64-bit physical address)
140 CommunicateSmcArgs.Arg2 = (UINTN)mNsCommBuffMemRegion.PhysicalBase;
141
142 // comm_size_address (not used, indicated by setting to zero)
143 CommunicateSmcArgs.Arg3 = 0;
144
145 // Call the Standalone MM environment.
146 ArmCallSmc (&CommunicateSmcArgs);
147
148 switch (CommunicateSmcArgs.Arg0) {
149 case ARM_SMC_MM_RET_SUCCESS:
150 ZeroMem (CommBufferVirtual, BufferSize);
151 // On successful return, the size of data being returned is inferred from
152 // MessageLength + Header.
153 CommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)mNsCommBuffMemRegion.VirtualBase;
154 BufferSize = CommunicateHeader->MessageLength +
155 sizeof (CommunicateHeader->HeaderGuid) +
156 sizeof (CommunicateHeader->MessageLength);
157
158 CopyMem (
159 CommBufferVirtual,
160 (VOID *)mNsCommBuffMemRegion.VirtualBase,
161 BufferSize
162 );
163 Status = EFI_SUCCESS;
164 break;
165
166 case ARM_SMC_MM_RET_INVALID_PARAMS:
167 Status = EFI_INVALID_PARAMETER;
168 break;
169
170 case ARM_SMC_MM_RET_DENIED:
171 Status = EFI_ACCESS_DENIED;
172 break;
173
174 case ARM_SMC_MM_RET_NO_MEMORY:
175 // Unexpected error since the CommSize was checked for zero length
176 // prior to issuing the SMC
177 Status = EFI_OUT_OF_RESOURCES;
178 ASSERT (0);
179 break;
180
181 default:
182 Status = EFI_ACCESS_DENIED;
183 ASSERT (0);
184 }
185
186 return Status;
187 }
188
189 //
190 // MM Communication Protocol instance
191 //
192 STATIC EFI_MM_COMMUNICATION2_PROTOCOL mMmCommunication2 = {
193 MmCommunication2Communicate
194 };
195
196 /**
197 Notification callback on SetVirtualAddressMap event.
198
199 This function notifies the MM communication protocol interface on
200 SetVirtualAddressMap event and converts pointers used in this driver
201 from physical to virtual address.
202
203 @param Event SetVirtualAddressMap event.
204 @param Context A context when the SetVirtualAddressMap triggered.
205
206 @retval EFI_SUCCESS The function executed successfully.
207 @retval Other Some error occurred when executing this function.
208
209 **/
210 STATIC
211 VOID
212 EFIAPI
213 NotifySetVirtualAddressMap (
214 IN EFI_EVENT Event,
215 IN VOID *Context
216 )
217 {
218 EFI_STATUS Status;
219
220 Status = gRT->ConvertPointer (
221 EFI_OPTIONAL_PTR,
222 (VOID **)&mNsCommBuffMemRegion.VirtualBase
223 );
224 if (EFI_ERROR (Status)) {
225 DEBUG ((
226 DEBUG_ERROR,
227 "NotifySetVirtualAddressMap():"
228 " Unable to convert MM runtime pointer. Status:0x%r\n",
229 Status
230 ));
231 }
232 }
233
234 STATIC
235 EFI_STATUS
236 GetMmCompatibility (
237 )
238 {
239 EFI_STATUS Status;
240 UINT32 MmVersion;
241 ARM_SMC_ARGS MmVersionArgs;
242
243 // MM_VERSION uses SMC32 calling conventions
244 MmVersionArgs.Arg0 = ARM_SMC_ID_MM_VERSION_AARCH32;
245
246 ArmCallSmc (&MmVersionArgs);
247
248 MmVersion = MmVersionArgs.Arg0;
249
250 if ((MM_MAJOR_VER (MmVersion) == MM_CALLER_MAJOR_VER) &&
251 (MM_MINOR_VER (MmVersion) >= MM_CALLER_MINOR_VER))
252 {
253 DEBUG ((
254 DEBUG_INFO,
255 "MM Version: Major=0x%x, Minor=0x%x\n",
256 MM_MAJOR_VER (MmVersion),
257 MM_MINOR_VER (MmVersion)
258 ));
259 Status = EFI_SUCCESS;
260 } else {
261 DEBUG ((
262 DEBUG_ERROR,
263 "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n",
264 MM_MAJOR_VER (MmVersion),
265 MM_MINOR_VER (MmVersion),
266 MM_CALLER_MAJOR_VER,
267 MM_CALLER_MINOR_VER
268 ));
269 Status = EFI_UNSUPPORTED;
270 }
271
272 return Status;
273 }
274
275 STATIC EFI_GUID *CONST mGuidedEventGuid[] = {
276 &gEfiEndOfDxeEventGroupGuid,
277 &gEfiEventExitBootServicesGuid,
278 &gEfiEventReadyToBootGuid,
279 };
280
281 STATIC EFI_EVENT mGuidedEvent[ARRAY_SIZE (mGuidedEventGuid)];
282
283 /**
284 Event notification that is fired when GUIDed Event Group is signaled.
285
286 @param Event The Event that is being processed, not used.
287 @param Context Event Context, not used.
288
289 **/
290 STATIC
291 VOID
292 EFIAPI
293 MmGuidedEventNotify (
294 IN EFI_EVENT Event,
295 IN VOID *Context
296 )
297 {
298 EFI_MM_COMMUNICATE_HEADER Header;
299 UINTN Size;
300
301 //
302 // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
303 //
304 CopyGuid (&Header.HeaderGuid, Context);
305 Header.MessageLength = 1;
306 Header.Data[0] = 0;
307
308 Size = sizeof (Header);
309 MmCommunication2Communicate (&mMmCommunication2, &Header, &Header, &Size);
310 }
311
312 /**
313 The Entry Point for MM Communication
314
315 This function installs the MM communication protocol interface and finds out
316 what type of buffer management will be required prior to invoking the
317 communication SMC.
318
319 @param ImageHandle The firmware allocated handle for the EFI image.
320 @param SystemTable A pointer to the EFI System Table.
321
322 @retval EFI_SUCCESS The entry point is executed successfully.
323 @retval Other Some error occurred when executing this entry point.
324
325 **/
326 EFI_STATUS
327 EFIAPI
328 MmCommunication2Initialize (
329 IN EFI_HANDLE ImageHandle,
330 IN EFI_SYSTEM_TABLE *SystemTable
331 )
332 {
333 EFI_STATUS Status;
334 UINTN Index;
335
336 // Check if we can make the MM call
337 Status = GetMmCompatibility ();
338 if (EFI_ERROR (Status)) {
339 goto ReturnErrorStatus;
340 }
341
342 mNsCommBuffMemRegion.PhysicalBase = PcdGet64 (PcdMmBufferBase);
343 // During boot , Virtual and Physical are same
344 mNsCommBuffMemRegion.VirtualBase = mNsCommBuffMemRegion.PhysicalBase;
345 mNsCommBuffMemRegion.Length = PcdGet64 (PcdMmBufferSize);
346
347 ASSERT (mNsCommBuffMemRegion.PhysicalBase != 0);
348
349 ASSERT (mNsCommBuffMemRegion.Length != 0);
350
351 Status = gDS->AddMemorySpace (
352 EfiGcdMemoryTypeReserved,
353 mNsCommBuffMemRegion.PhysicalBase,
354 mNsCommBuffMemRegion.Length,
355 EFI_MEMORY_WB |
356 EFI_MEMORY_XP |
357 EFI_MEMORY_RUNTIME
358 );
359 if (EFI_ERROR (Status)) {
360 DEBUG ((
361 DEBUG_ERROR,
362 "MmCommunicateInitialize: "
363 "Failed to add MM-NS Buffer Memory Space\n"
364 ));
365 goto ReturnErrorStatus;
366 }
367
368 Status = gDS->SetMemorySpaceAttributes (
369 mNsCommBuffMemRegion.PhysicalBase,
370 mNsCommBuffMemRegion.Length,
371 EFI_MEMORY_WB | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME
372 );
373 if (EFI_ERROR (Status)) {
374 DEBUG ((
375 DEBUG_ERROR,
376 "MmCommunicateInitialize: "
377 "Failed to set MM-NS Buffer Memory attributes\n"
378 ));
379 goto CleanAddedMemorySpace;
380 }
381
382 // Install the communication protocol
383 Status = gBS->InstallProtocolInterface (
384 &mMmCommunicateHandle,
385 &gEfiMmCommunication2ProtocolGuid,
386 EFI_NATIVE_INTERFACE,
387 &mMmCommunication2
388 );
389 if (EFI_ERROR (Status)) {
390 DEBUG ((
391 DEBUG_ERROR,
392 "MmCommunicationInitialize: "
393 "Failed to install MM communication protocol\n"
394 ));
395 goto CleanAddedMemorySpace;
396 }
397
398 // Register notification callback when virtual address is associated
399 // with the physical address.
400 // Create a Set Virtual Address Map event.
401 Status = gBS->CreateEvent (
402 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
403 TPL_NOTIFY,
404 NotifySetVirtualAddressMap,
405 NULL,
406 &mSetVirtualAddressMapEvent
407 );
408 ASSERT_EFI_ERROR (Status);
409
410 for (Index = 0; Index < ARRAY_SIZE (mGuidedEventGuid); Index++) {
411 Status = gBS->CreateEventEx (
412 EVT_NOTIFY_SIGNAL,
413 TPL_CALLBACK,
414 MmGuidedEventNotify,
415 mGuidedEventGuid[Index],
416 mGuidedEventGuid[Index],
417 &mGuidedEvent[Index]
418 );
419 ASSERT_EFI_ERROR (Status);
420 if (EFI_ERROR (Status)) {
421 while (Index-- > 0) {
422 gBS->CloseEvent (mGuidedEvent[Index]);
423 }
424
425 goto UninstallProtocol;
426 }
427 }
428
429 return EFI_SUCCESS;
430
431 UninstallProtocol:
432 gBS->UninstallProtocolInterface (
433 mMmCommunicateHandle,
434 &gEfiMmCommunication2ProtocolGuid,
435 &mMmCommunication2
436 );
437
438 CleanAddedMemorySpace:
439 gDS->RemoveMemorySpace (
440 mNsCommBuffMemRegion.PhysicalBase,
441 mNsCommBuffMemRegion.Length
442 );
443
444 ReturnErrorStatus:
445 return EFI_INVALID_PARAMETER;
446 }