From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../FaultTolerantWriteDxe/UpdateWorkingBlock.c | 607 +++++++++++++++++++++ 1 file changed, 607 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c (limited to 'roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c') diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c new file mode 100644 index 000000000..04cb38afd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c @@ -0,0 +1,607 @@ +/** @file + + Internal functions to operate Working Block Space. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "FaultTolerantWrite.h" + +EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0}; + +/** + Initialize a local work space header. + + Since Signature and WriteQueueSize have been known, Crc can be calculated out, + then the work space header will be fixed. +**/ +VOID +InitializeLocalWorkSpaceHeader ( + VOID + ) +{ + // + // Check signature with gEdkiiWorkingBlockSignatureGuid. + // + if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) { + // + // The local work space header has been initialized. + // + return; + } + + SetMem ( + &mWorkingBlockHeader, + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), + FTW_ERASED_BYTE + ); + + // + // Here using gEdkiiWorkingBlockSignatureGuid as the signature. + // + CopyMem ( + &mWorkingBlockHeader.Signature, + &gEdkiiWorkingBlockSignatureGuid, + sizeof (EFI_GUID) + ); + mWorkingBlockHeader.WriteQueueSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER); + + // + // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE. + // + + // + // Calculate the Crc of woking block header + // + mWorkingBlockHeader.Crc = FtwCalculateCrc32 (&mWorkingBlockHeader, + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)); + + mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE; + mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE; +} + +/** + Check to see if it is a valid work space. + + + @param WorkingHeader Pointer of working block header + + @retval TRUE The work space is valid. + @retval FALSE The work space is invalid. + +**/ +BOOLEAN +IsValidWorkSpace ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader + ) +{ + if (WorkingHeader == NULL) { + return FALSE; + } + + if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) { + return TRUE; + } + + DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n")); + return FALSE; +} + +/** + Initialize a work space when there is no work space. + + @param WorkingHeader Pointer of working block header + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +InitWorkSpaceHeader ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader + ) +{ + if (WorkingHeader == NULL) { + return EFI_INVALID_PARAMETER; + } + + CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)); + + return EFI_SUCCESS; +} + +/** + Read work space data from work block or spare block. + + @param FvBlock FVB Protocol interface to access the block. + @param BlockSize The size of the block. + @param Lba Lba of the block. + @param Offset The offset within the block. + @param Length The number of bytes to read from the block. + @param Buffer The data is read. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +ReadWorkSpaceData ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + OUT UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINTN MyLength; + + // + // Calculate the real Offset and Lba to write. + // + while (Offset >= BlockSize) { + Offset -= BlockSize; + Lba++; + } + + Ptr = Buffer; + while (Length > 0) { + if ((Offset + Length) > BlockSize) { + MyLength = BlockSize - Offset; + } else { + MyLength = Length; + } + + Status = FvBlock->Read ( + FvBlock, + Lba, + Offset, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + Offset = 0; + Length -= MyLength; + Ptr += MyLength; + Lba++; + } + + return EFI_SUCCESS; +} + +/** + Write work space data to work block. + + @param FvBlock FVB Protocol interface to access the block. + @param BlockSize The size of the block. + @param Lba Lba of the block. + @param Offset The offset within the block to place the data. + @param Length The number of bytes to write to the block. + @param Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +WriteWorkSpaceData ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINTN MyLength; + + // + // Calculate the real Offset and Lba to write. + // + while (Offset >= BlockSize) { + Offset -= BlockSize; + Lba++; + } + + Ptr = Buffer; + while (Length > 0) { + if ((Offset + Length) > BlockSize) { + MyLength = BlockSize - Offset; + } else { + MyLength = Length; + } + + Status = FvBlock->Write ( + FvBlock, + Lba, + Offset, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + Offset = 0; + Length -= MyLength; + Ptr += MyLength; + Lba++; + } + return EFI_SUCCESS; +} + +/** + Read from working block to refresh the work space in memory. + + @param FtwDevice Point to private data of FTW driver + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +WorkSpaceRefresh ( + IN EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + UINTN RemainingSpaceSize; + + // + // Initialize WorkSpace as FTW_ERASED_BYTE + // + SetMem ( + FtwDevice->FtwWorkSpace, + FtwDevice->FtwWorkSpaceSize, + FTW_ERASED_BYTE + ); + + // + // Read from working block + // + Status = ReadWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase, + FtwDevice->FtwWorkSpaceSize, + FtwDevice->FtwWorkSpace + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Refresh the FtwLastWriteHeader + // + Status = FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace); + DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize)); + // + // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain + // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header + // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data), + // it needs to reclaim work space. + // + if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) { + // + // reclaim work space in working block. + // + Status = FtwReclaimWorkSpace (FtwDevice, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status)); + return EFI_ABORTED; + } + // + // Read from working block again + // + Status = ReadWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase, + FtwDevice->FtwWorkSpaceSize, + FtwDevice->FtwWorkSpace + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Status = FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + // + // Refresh the FtwLastWriteRecord + // + Status = FtwGetLastWriteRecord ( + FtwDevice->FtwLastWriteHeader, + &FtwDevice->FtwLastWriteRecord + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Reclaim the work space on the working block. + + @param FtwDevice Point to private data of FTW driver + @param PreserveRecord Whether to preserve the working record is needed + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FtwReclaimWorkSpace ( + IN EFI_FTW_DEVICE *FtwDevice, + IN BOOLEAN PreserveRecord + ) +{ + EFI_STATUS Status; + UINTN Length; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + UINT8 *TempBuffer; + UINTN TempBufferSize; + UINTN SpareBufferSize; + UINT8 *SpareBuffer; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader; + UINTN Index; + UINT8 *Ptr; + EFI_LBA WorkSpaceLbaOffset; + + DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n")); + + WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba; + + // + // Read all original data from working block to a memory buffer + // + TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize; + TempBuffer = AllocateZeroPool (TempBufferSize); + if (TempBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = TempBuffer; + for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) { + Length = FtwDevice->WorkBlockSize; + Status = FtwDevice->FtwFvBlock->Read ( + FtwDevice->FtwFvBlock, + FtwDevice->FtwWorkBlockLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + } + // + // Clean up the workspace, remove all the completed records. + // + Ptr = TempBuffer + + (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + + FtwDevice->FtwWorkSpaceBase; + + // + // Clear the content of buffer that will save the new work space data + // + SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE); + + // + // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer + // + CopyMem ( + Ptr, + FtwDevice->FtwWorkSpaceHeader, + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER) + ); + if (PreserveRecord) { + // + // Get the last record following the header, + // + Status = FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + Header = FtwDevice->FtwLastWriteHeader; + if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) { + CopyMem ( + Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), + FtwDevice->FtwLastWriteHeader, + FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize) + ); + } + } + + CopyMem ( + FtwDevice->FtwWorkSpace, + Ptr, + FtwDevice->FtwWorkSpaceSize + ); + + FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + + FtwGetLastWriteRecord ( + FtwDevice->FtwLastWriteHeader, + &FtwDevice->FtwLastWriteRecord + ); + + // + // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID + // + WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer + + (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + + FtwDevice->FtwWorkSpaceBase); + WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE; + WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE; + + // + // Try to keep the content of spare block + // Save spare block into a spare backup memory buffer (Sparebuffer) + // + SpareBufferSize = FtwDevice->SpareAreaLength; + SpareBuffer = AllocatePool (SpareBufferSize); + if (SpareBuffer == NULL) { + FreePool (TempBuffer); + return EFI_OUT_OF_RESOURCES; + } + + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Length = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + } + // + // Write the memory buffer to spare block + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = TempBuffer; + for (Index = 0; TempBufferSize > 0; Index += 1) { + if (TempBufferSize > FtwDevice->SpareBlockSize) { + Length = FtwDevice->SpareBlockSize; + } else { + Length = TempBufferSize; + } + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + TempBufferSize -= Length; + } + // + // Free TempBuffer + // + FreePool (TempBuffer); + + // + // Set the WorkingBlockValid in spare block + // + Status = FtwUpdateFvState ( + FtwDevice->FtwBackupFvb, + FtwDevice->SpareBlockSize, + FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, + FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_VALID + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + // + // Before erase the working block, set WorkingBlockInvalid in working block. + // + // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, + // WorkingBlockInvalid); + // + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_INVALID + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE; + + // + // Write the spare block to working block + // + Status = FlushSpareBlockToWorkingBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return Status; + } + // + // Restore spare backup buffer into spare block , if no failure happened during FtwWrite. + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Length = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + } + + FreePool (SpareBuffer); + + DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n")); + + return EFI_SUCCESS; +} -- cgit