+/**\r
+ Get the handle of the SMM FVB protocol by the FVB base address and attributes.\r
+\r
+ @param[in] Address The base address of SMM FVB protocol.\r
+ @param[in] Attributes The attributes of the SMM FVB protocol.\r
+ @param[out] SmmFvbHandle The handle of the SMM FVB protocol.\r
+\r
+ @retval EFI_SUCCESS The FVB handle is found.\r
+ @retval EFI_ABORTED The FVB protocol is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+GetFvbByAddressAndAttribute (\r
+ IN EFI_PHYSICAL_ADDRESS Address,\r
+ IN EFI_FVB_ATTRIBUTES_2 Attributes,\r
+ OUT EFI_HANDLE *SmmFvbHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN HandleCount;\r
+ UINTN Index;\r
+ EFI_PHYSICAL_ADDRESS FvbBaseAddress;\r
+ EFI_FVB_ATTRIBUTES_2 FvbAttributes;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+\r
+ HandleBuffer = NULL;\r
+\r
+ //\r
+ // Locate all handles of SMM Fvb protocol.\r
+ //\r
+ Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ \r
+ //\r
+ // Find the proper SMM Fvb handle by the address and attributes.\r
+ //\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ //\r
+ // Compare the address.\r
+ //\r
+ Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+ if (Address != FvbBaseAddress) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Compare the attribute.\r
+ //\r
+ Status = Fvb->GetAttributes (Fvb, &FvbAttributes);\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+ if (Attributes != FvbAttributes) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Found the proper FVB handle.\r
+ //\r
+ *SmmFvbHandle = HandleBuffer[Index];\r
+ FreePool (HandleBuffer);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ FreePool (HandleBuffer);\r
+ return EFI_ABORTED;\r
+}\r
+\r
+/**\r
+ Communication service SMI Handler entry.\r
+\r
+ This SMI handler provides services for the fault tolerant write wrapper driver.\r
+\r
+ Caution: This function requires additional review when modified.\r
+ This driver need to make sure the CommBuffer is not in the SMRAM range. \r
+ Also in FTW_FUNCTION_GET_LAST_WRITE case, check SmmFtwGetLastWriteHeader->Data + \r
+ SmmFtwGetLastWriteHeader->PrivateDataSize within communication buffer.\r
+\r
+ @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
+ @param[in] RegisterContext Points to an optional handler context which was specified when the\r
+ handler was registered.\r
+ @param[in, out] CommBuffer A pointer to a collection of data in memory that will be conveyed\r
+ from a non-SMM environment into an SMM environment.\r
+ @param[in, out] CommBufferSize The size of the CommBuffer.\r
+\r
+ @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers \r
+ should still be called.\r
+ @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should \r
+ still be called.\r
+ @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still \r
+ be called.\r
+ @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.\r
+ \r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmFaultTolerantWriteHandler (\r
+ IN EFI_HANDLE DispatchHandle,\r
+ IN CONST VOID *RegisterContext,\r
+ IN OUT VOID *CommBuffer,\r
+ IN OUT UINTN *CommBufferSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader;\r
+ SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *SmmGetMaxBlockSizeHeader;\r
+ SMM_FTW_ALLOCATE_HEADER *SmmFtwAllocateHeader;\r
+ SMM_FTW_WRITE_HEADER *SmmFtwWriteHeader;\r
+ SMM_FTW_RESTART_HEADER *SmmFtwRestartHeader;\r
+ SMM_FTW_GET_LAST_WRITE_HEADER *SmmFtwGetLastWriteHeader;\r
+ VOID *PrivateData;\r
+ EFI_HANDLE SmmFvbHandle;\r
+ UINTN InfoSize;\r
+ UINTN CommBufferPayloadSize;\r
+ UINTN PrivateDataSize;\r
+ UINTN Length;\r
+ UINTN TempCommBufferSize;\r
+\r
+ //\r
+ // If input is invalid, stop processing this SMI\r
+ //\r
+ if (CommBuffer == NULL || CommBufferSize == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ TempCommBufferSize = *CommBufferSize;\r
+\r
+ if (TempCommBufferSize < SMM_FTW_COMMUNICATE_HEADER_SIZE) {\r
+ DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer size invalid!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ CommBufferPayloadSize = TempCommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE;\r
+\r
+ if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {\r
+ DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer in SMRAM or overflow!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *)CommBuffer;\r
+\r
+ if (mEndOfDxe) {\r
+ //\r
+ // It will be not safe to expose the operations after End Of Dxe.\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "SmmFtwHandler: Not safe to do the operation: %x after End Of Dxe, so access denied!\n", SmmFtwFunctionHeader->Function));\r
+ SmmFtwFunctionHeader->ReturnStatus = EFI_ACCESS_DENIED;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ switch (SmmFtwFunctionHeader->Function) {\r
+ case FTW_FUNCTION_GET_MAX_BLOCK_SIZE:\r
+ if (CommBufferPayloadSize < sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER)) {\r
+ DEBUG ((EFI_D_ERROR, "GetMaxBlockSize: SMM communication buffer size invalid!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ SmmGetMaxBlockSizeHeader = (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *) SmmFtwFunctionHeader->Data;\r
+\r
+ Status = FtwGetMaxBlockSize (\r
+ &mFtwDevice->FtwInstance,\r
+ &SmmGetMaxBlockSizeHeader->BlockSize\r
+ );\r
+ break;\r
+ \r
+ case FTW_FUNCTION_ALLOCATE:\r
+ if (CommBufferPayloadSize < sizeof (SMM_FTW_ALLOCATE_HEADER)) {\r
+ DEBUG ((EFI_D_ERROR, "Allocate: SMM communication buffer size invalid!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ SmmFtwAllocateHeader = (SMM_FTW_ALLOCATE_HEADER *) SmmFtwFunctionHeader->Data;\r
+ Status = FtwAllocate (\r
+ &mFtwDevice->FtwInstance,\r
+ &SmmFtwAllocateHeader->CallerId,\r
+ SmmFtwAllocateHeader->PrivateDataSize,\r
+ SmmFtwAllocateHeader->NumberOfWrites\r
+ );\r
+ break;\r
+ \r
+ case FTW_FUNCTION_WRITE:\r
+ if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) {\r
+ DEBUG ((EFI_D_ERROR, "Write: SMM communication buffer size invalid!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ SmmFtwWriteHeader = (SMM_FTW_WRITE_HEADER *) SmmFtwFunctionHeader->Data;\r
+ Length = SmmFtwWriteHeader->Length;\r
+ PrivateDataSize = SmmFtwWriteHeader->PrivateDataSize;\r
+ if (((UINTN)(~0) - Length < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) ||\r
+ ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length)) {\r
+ //\r
+ // Prevent InfoSize overflow\r
+ //\r
+ Status = EFI_ACCESS_DENIED;\r
+ break;\r
+ }\r
+ InfoSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length + PrivateDataSize;\r
+\r
+ //\r
+ // SMRAM range check already covered before\r
+ //\r
+ if (InfoSize > CommBufferPayloadSize) {\r
+ DEBUG ((EFI_D_ERROR, "Write: Data size exceed communication buffer size limit!\n"));\r
+ Status = EFI_ACCESS_DENIED;\r
+ break;\r
+ }\r
+\r
+ if (PrivateDataSize == 0) {\r
+ PrivateData = NULL;\r
+ } else {\r
+ PrivateData = (VOID *)&SmmFtwWriteHeader->Data[Length];\r
+ }\r
+ Status = GetFvbByAddressAndAttribute (\r
+ SmmFtwWriteHeader->FvbBaseAddress, \r
+ SmmFtwWriteHeader->FvbAttributes,\r
+ &SmmFvbHandle\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = FtwWrite(\r
+ &mFtwDevice->FtwInstance,\r
+ SmmFtwWriteHeader->Lba,\r
+ SmmFtwWriteHeader->Offset,\r
+ Length,\r
+ PrivateData,\r
+ SmmFvbHandle,\r
+ SmmFtwWriteHeader->Data\r
+ );\r
+ }\r
+ break;\r
+ \r
+ case FTW_FUNCTION_RESTART:\r
+ if (CommBufferPayloadSize < sizeof (SMM_FTW_RESTART_HEADER)) {\r
+ DEBUG ((EFI_D_ERROR, "Restart: SMM communication buffer size invalid!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ SmmFtwRestartHeader = (SMM_FTW_RESTART_HEADER *) SmmFtwFunctionHeader->Data;\r
+ Status = GetFvbByAddressAndAttribute (\r
+ SmmFtwRestartHeader->FvbBaseAddress, \r
+ SmmFtwRestartHeader->FvbAttributes,\r
+ &SmmFvbHandle\r
+ ); \r
+ if (!EFI_ERROR (Status)) {\r
+ Status = FtwRestart (&mFtwDevice->FtwInstance, SmmFvbHandle);\r
+ }\r
+ break;\r
+\r
+ case FTW_FUNCTION_ABORT:\r
+ Status = FtwAbort (&mFtwDevice->FtwInstance);\r
+ break;\r
+ \r
+ case FTW_FUNCTION_GET_LAST_WRITE:\r
+ if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)) {\r
+ DEBUG ((EFI_D_ERROR, "GetLastWrite: SMM communication buffer size invalid!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ SmmFtwGetLastWriteHeader = (SMM_FTW_GET_LAST_WRITE_HEADER *) SmmFtwFunctionHeader->Data;\r
+ PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;\r
+ if ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)){\r
+ //\r
+ // Prevent InfoSize overflow\r
+ //\r
+ Status = EFI_ACCESS_DENIED;\r
+ break;\r
+ }\r
+ InfoSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + PrivateDataSize;\r
+\r
+ //\r
+ // SMRAM range check already covered before\r
+ //\r
+ if (InfoSize > CommBufferPayloadSize) {\r
+ DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));\r
+ Status = EFI_ACCESS_DENIED;\r
+ break;\r
+ }\r
+\r
+ Status = FtwGetLastWrite (\r
+ &mFtwDevice->FtwInstance,\r
+ &SmmFtwGetLastWriteHeader->CallerId,\r
+ &SmmFtwGetLastWriteHeader->Lba,\r
+ &SmmFtwGetLastWriteHeader->Offset,\r
+ &SmmFtwGetLastWriteHeader->Length,\r
+ &PrivateDataSize,\r
+ (VOID *)SmmFtwGetLastWriteHeader->Data,\r
+ &SmmFtwGetLastWriteHeader->Complete\r
+ );\r
+ SmmFtwGetLastWriteHeader->PrivateDataSize = PrivateDataSize;\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+ SmmFtwFunctionHeader->ReturnStatus = Status;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r