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