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