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