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