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