From f3b80a8eaba197ba34e9fea934dfbc59ce89d837 Mon Sep 17 00:00:00 2001 From: gdong1 Date: Wed, 12 Jan 2011 09:05:27 +0000 Subject: Add SMM FTW wrapper driver since non-SMM FTW protocol can be used by some consumers (Such as capsule update) when SMM FTW driver is applied. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11246 6f19259b-4bc3-4df7-8a09-765794883524 --- .../FaultTolerantWriteSmmDxe.c | 558 +++++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c (limited to 'MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c') diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c new file mode 100644 index 0000000..89c7d33 --- /dev/null +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c @@ -0,0 +1,558 @@ +/** @file + + Implement the Fault Tolerant Write (FTW) protocol based on SMM FTW + module. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "FaultTolerantWriteSmmDxe.h" + +EFI_HANDLE mHandle = NULL; +EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL; +UINTN mPrivateDataSize = 0; + +EFI_FAULT_TOLERANT_WRITE_PROTOCOL mFaultTolerantWriteDriver = { + FtwGetMaxBlockSize, + FtwAllocate, + FtwWrite, + FtwRestart, + FtwAbort, + FtwGetLastWrite +}; + +/** + Initialize the communicate buffer using DataSize and Function number. + + @param[out] CommunicateBuffer The communicate buffer. Caller should free it after use. + @param[out] DataPtr Points to the data in the communicate buffer. Caller should not free it. + @param[in] DataSize The payload size. + @param[in] Function The function number used to initialize the communicate header. + +**/ +VOID +InitCommunicateBuffer ( + OUT VOID **CommunicateBuffer, + OUT VOID **DataPtr, + IN UINTN DataSize, + IN UINTN Function + ) +{ + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader; + + // + // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE + DataSize. + // + SmmCommunicateHeader = AllocateZeroPool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE); + ASSERT (SmmCommunicateHeader != NULL); + + // + // Prepare data buffer. + // + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFaultTolerantWriteProtocolGuid); + SmmCommunicateHeader->MessageLength = DataSize + SMM_FTW_COMMUNICATE_HEADER_SIZE; + + SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data; + SmmFtwFunctionHeader->Function = Function; + + *CommunicateBuffer = SmmCommunicateHeader; + if (DataPtr != NULL) { + *DataPtr = SmmFtwFunctionHeader->Data; + } +} + + +/** + Send the data in communicate buffer to SMI handler and get response. + + @param[out] SmmCommunicateHeader The communicate buffer. + @param[in] DataSize The payload size. + +**/ +EFI_STATUS +SendCommunicateBuffer ( + IN EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader, + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN CommSize; + SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader; + + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE; + Status = mSmmCommunication->Communicate (mSmmCommunication, SmmCommunicateHeader, &CommSize); + ASSERT_EFI_ERROR (Status); + + SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data; + return SmmFtwFunctionHeader->ReturnStatus; +} + + +/** + Get the FvbBaseAddress and FvbAttributes from the FVB handle FvbHandle. + + @param[in] FvBlockHandle The handle of FVB protocol that provides services. + @param[in] FvbBaseAddress The base address of the FVB attached with FvBlockHandle. + @param[out] FvbAttributes The attributes of the FVB attached with FvBlockHandle. + + @retval EFI_SUCCESS The function completed successfully. + @retval Others The function could not complete successfully. + +**/ +EFI_STATUS +ConvertFvbHandle ( + IN EFI_HANDLE FvbHandle, + OUT EFI_PHYSICAL_ADDRESS *FvbBaseAddress, + OUT EFI_FVB_ATTRIBUTES_2 *FvbAttributes + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + + Status = gBS->HandleProtocol (FvbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **) &Fvb); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Fvb->GetPhysicalAddress (Fvb, FvbBaseAddress); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Fvb->GetAttributes (Fvb, FvbAttributes); + return Status; +} + + +/** + Get the size of the largest block that can be updated in a fault-tolerant manner. + + @param[in] This Indicates a pointer to the calling context. + @param[out] BlockSize A pointer to a caller-allocated UINTN that is + updated to indicate the size of the largest block + that can be updated. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +EFIAPI +FtwGetMaxBlockSize ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT UINTN *BlockSize + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *SmmFtwBlockSizeHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER); + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwBlockSizeHeader, PayloadSize, FTW_FUNCTION_GET_MAX_BLOCK_SIZE); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + + // + // Get data from SMM + // + *BlockSize = SmmFtwBlockSizeHeader->BlockSize; + FreePool (SmmCommunicateHeader); + + return Status; +} + + +/** + Allocates space for the protocol to maintain information about writes. + Since writes must be completed in a fault-tolerant manner and multiple + writes require more resources to be successful, this function + enables the protocol to ensure that enough space exists to track + information about upcoming writes. + + @param[in] This A pointer to the calling context. + @param[in] CallerId The GUID identifying the write. + @param[in] PrivateDataSize The size of the caller's private data that must be + recorded for each write. + @param[in] NumberOfWrites The number of fault tolerant block writes that will + need to occur. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED Not all allocated writes have been completed. All + writes must be completed or aborted before another + fault tolerant write can occur. + +**/ +EFI_STATUS +EFIAPI +FtwAllocate ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_GUID *CallerId, + IN UINTN PrivateDataSize, + IN UINTN NumberOfWrites + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_ALLOCATE_HEADER *SmmFtwAllocateHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = sizeof (SMM_FTW_ALLOCATE_HEADER); + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwAllocateHeader, PayloadSize, FTW_FUNCTION_ALLOCATE); + CopyGuid (&SmmFtwAllocateHeader->CallerId, CallerId); + SmmFtwAllocateHeader->PrivateDataSize = PrivateDataSize; + SmmFtwAllocateHeader->NumberOfWrites = NumberOfWrites; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + if (!EFI_ERROR( Status)) { + mPrivateDataSize = PrivateDataSize; + } + + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Starts a target block update. This records information about the write + in fault tolerant storage, and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param[in] This The calling context. + @param[in] Lba The logical block address of the target block. + @param[in] Offset The offset within the target block to place the + data. + @param[in] Length The number of bytes to write to the target block. + @param[in] PrivateData A pointer to private data that the caller requires + to complete any pending writes in the event of a + fault. + @param[in] FvBlockHandle The handle of FVB protocol that provides services + for reading, writing, and erasing the target block. + @param[in] Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_BAD_BUFFER_SIZE The write would span a block boundary, which is not + a valid action. + @retval EFI_ACCESS_DENIED No writes have been allocated. + @retval EFI_NOT_READY The last write has not been completed. Restart() + must be called to complete it. + +**/ +EFI_STATUS +EFIAPI +FtwWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN VOID *PrivateData, + IN EFI_HANDLE FvBlockHandle, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_WRITE_HEADER *SmmFtwWriteHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length; + if (PrivateData != NULL) { + // + // The private data buffer size should be the same one in FtwAllocate API. + // + PayloadSize += mPrivateDataSize; + } + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwWriteHeader, PayloadSize, FTW_FUNCTION_WRITE); + + // + // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address + // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data. + // + Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwWriteHeader->FvbBaseAddress, &SmmFtwWriteHeader->FvbAttributes); + if (EFI_ERROR (Status)) { + FreePool (SmmCommunicateHeader); + return EFI_ABORTED; + } + + SmmFtwWriteHeader->Lba = Lba; + SmmFtwWriteHeader->Offset = Offset; + SmmFtwWriteHeader->Length = Length; + CopyMem (SmmFtwWriteHeader->Data, Buffer, Length); + if (PrivateData == NULL) { + SmmFtwWriteHeader->PrivateDataSize = 0; + } else { + SmmFtwWriteHeader->PrivateDataSize = mPrivateDataSize; + CopyMem (&SmmFtwWriteHeader->Data[Length], PrivateData, mPrivateDataSize); + } + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Restarts a previously interrupted write. The caller must provide the + block protocol needed to complete the interrupted write. + + @param[in] This The calling context. + @param[in] FvBlockHandle The handle of FVB protocol that provides services. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED No pending writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwRestart ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_HANDLE FvBlockHandle + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_RESTART_HEADER *SmmFtwRestartHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = sizeof (SMM_FTW_RESTART_HEADER); + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwRestartHeader, PayloadSize, FTW_FUNCTION_RESTART); + + // + // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address + // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data. + // + Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwRestartHeader->FvbBaseAddress, &SmmFtwRestartHeader->FvbAttributes); + if (EFI_ERROR (Status)) { + FreePool (SmmCommunicateHeader); + return EFI_ABORTED; + } + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Aborts all previously allocated writes. + + @param[in] This The calling context. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwAbort ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + + // + // Initialize the communicate buffer. + // + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, NULL, 0, FTW_FUNCTION_ABORT); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, 0); + + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Starts a target block update. This function records information about the write + in fault-tolerant storage and completes the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param[in] This Indicates a pointer to the calling context. + @param[out] CallerId The GUID identifying the last write. + @param[out] Lba The logical block address of the last write. + @param[out] Offset The offset within the block of the last write. + @param[out] Length The length of the last write. + @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On + output, the size of the private data stored for + this write. + @param[out] PrivateData A pointer to a buffer. The function will copy + PrivateDataSize bytes from the private data stored + for this write. + @param[out] Complete A Boolean value with TRUE indicating that the write + was completed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwGetLastWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT EFI_GUID *CallerId, + OUT EFI_LBA *Lba, + OUT UINTN *Offset, + OUT UINTN *Length, + IN OUT UINTN *PrivateDataSize, + OUT VOID *PrivateData, + OUT BOOLEAN *Complete + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_GET_LAST_WRITE_HEADER *SmmFtwGetLastWriteHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + *PrivateDataSize; + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwGetLastWriteHeader, PayloadSize, FTW_FUNCTION_GET_LAST_WRITE); + SmmFtwGetLastWriteHeader->PrivateDataSize = *PrivateDataSize; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + + // + // Get data from SMM + // + *PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize; + if (!EFI_ERROR (Status)) { + *Lba = SmmFtwGetLastWriteHeader->Lba; + *Offset = SmmFtwGetLastWriteHeader->Offset; + *Length = SmmFtwGetLastWriteHeader->Length; + *Complete = SmmFtwGetLastWriteHeader->Complete; + CopyGuid (CallerId, &SmmFtwGetLastWriteHeader->CallerId); + CopyMem (PrivateData, SmmFtwGetLastWriteHeader->Data, *PrivateDataSize); + } + + FreePool (SmmCommunicateHeader); + return Status; +} + +/** + SMM Fault Tolerant Write Protocol notification event handler. + + Install Fault Tolerant Write Protocol. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +SmmFtwReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + + // + // Just return to avoid install SMM FaultTolerantWriteProtocol again + // if Fault Tolerant Write protocol had been installed. + // + Status = gBS->LocateProtocol (&gEfiFaultTolerantWriteProtocolGuid, NULL, (VOID **)&FtwProtocol); + if (!EFI_ERROR (Status)) { + return; + } + + Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication); + ASSERT_EFI_ERROR (Status); + + // + // Install protocol interface + // + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + &mFaultTolerantWriteDriver + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); +} + + +/** + The driver entry point for Fault Tolerant Write driver. + + The function does the necessary initialization work. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI system table. + + @retval EFI_SUCCESS This funtion always return EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +FaultTolerantWriteSmmInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *SmmFtwRegistration; + + // + // Smm FTW driver is ready + // + EfiCreateProtocolNotifyEvent ( + &gEfiSmmFaultTolerantWriteProtocolGuid, + TPL_CALLBACK, + SmmFtwReady, + NULL, + &SmmFtwRegistration + ); + + return EFI_SUCCESS; +} + -- cgit v1.1