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