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