]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c
ArmPkg: Replace BSD License with BSD+Patent License
[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
262/**\r
263 The Entry Point for MM Communication\r
264\r
265 This function installs the MM communication protocol interface and finds out\r
266 what type of buffer management will be required prior to invoking the\r
267 communication SMC.\r
268\r
269 @param ImageHandle The firmware allocated handle for the EFI image.\r
270 @param SystemTable A pointer to the EFI System Table.\r
271\r
272 @retval EFI_SUCCESS The entry point is executed successfully.\r
273 @retval Other Some error occurred when executing this entry point.\r
274\r
275**/\r
276EFI_STATUS\r
277EFIAPI\r
278MmCommunicationInitialize (\r
279 IN EFI_HANDLE ImageHandle,\r
280 IN EFI_SYSTEM_TABLE *SystemTable\r
281 )\r
282{\r
283 EFI_STATUS Status;\r
284\r
285 // Check if we can make the MM call\r
286 Status = GetMmCompatibility ();\r
287 if (EFI_ERROR(Status)) {\r
288 goto ReturnErrorStatus;\r
289 }\r
290\r
291 mNsCommBuffMemRegion.PhysicalBase = PcdGet64 (PcdMmBufferBase);\r
292 // During boot , Virtual and Physical are same\r
293 mNsCommBuffMemRegion.VirtualBase = mNsCommBuffMemRegion.PhysicalBase;\r
294 mNsCommBuffMemRegion.Length = PcdGet64 (PcdMmBufferSize);\r
295\r
296 ASSERT (mNsCommBuffMemRegion.PhysicalBase != 0);\r
297\r
298 ASSERT (mNsCommBuffMemRegion.Length != 0);\r
299\r
300 Status = gDS->AddMemorySpace (\r
301 EfiGcdMemoryTypeReserved,\r
302 mNsCommBuffMemRegion.PhysicalBase,\r
303 mNsCommBuffMemRegion.Length,\r
304 EFI_MEMORY_WB |\r
305 EFI_MEMORY_XP |\r
306 EFI_MEMORY_RUNTIME\r
307 );\r
308 if (EFI_ERROR (Status)) {\r
309 DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "\r
310 "Failed to add MM-NS Buffer Memory Space\n"));\r
311 goto ReturnErrorStatus;\r
312 }\r
313\r
314 Status = gDS->SetMemorySpaceAttributes (\r
315 mNsCommBuffMemRegion.PhysicalBase,\r
316 mNsCommBuffMemRegion.Length,\r
317 EFI_MEMORY_WB | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME\r
318 );\r
319 if (EFI_ERROR (Status)) {\r
320 DEBUG ((DEBUG_ERROR, "MmCommunicateInitialize: "\r
321 "Failed to set MM-NS Buffer Memory attributes\n"));\r
322 goto CleanAddedMemorySpace;\r
323 }\r
324\r
325 // Install the communication protocol\r
326 Status = gBS->InstallProtocolInterface (\r
327 &mMmCommunicateHandle,\r
328 &gEfiMmCommunicationProtocolGuid,\r
329 EFI_NATIVE_INTERFACE,\r
330 &mMmCommunication\r
331 );\r
332 if (EFI_ERROR(Status)) {\r
333 DEBUG ((DEBUG_ERROR, "MmCommunicationInitialize: "\r
334 "Failed to install MM communication protocol\n"));\r
335 goto CleanAddedMemorySpace;\r
336 }\r
337\r
338 // Register notification callback when virtual address is associated\r
339 // with the physical address.\r
340 // Create a Set Virtual Address Map event.\r
341 Status = gBS->CreateEvent (\r
342 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,\r
343 TPL_NOTIFY,\r
344 NotifySetVirtualAddressMap,\r
345 NULL,\r
346 &mSetVirtualAddressMapEvent\r
347 );\r
348 if (Status == EFI_SUCCESS) {\r
349 return Status;\r
350 }\r
351\r
352 gBS->UninstallProtocolInterface (\r
353 mMmCommunicateHandle,\r
354 &gEfiMmCommunicationProtocolGuid,\r
355 &mMmCommunication\r
356 );\r
357\r
358CleanAddedMemorySpace:\r
359 gDS->RemoveMemorySpace (\r
360 mNsCommBuffMemRegion.PhysicalBase,\r
361 mNsCommBuffMemRegion.Length\r
362 );\r
363\r
364ReturnErrorStatus:\r
365 return EFI_INVALID_PARAMETER;\r
366}\r