/** 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; }
/** Main entry for Fault Tolerant Write PEIM. @param[in] FileHandle Handle of the file being invoked. @param[in] PeiServices Pointer to PEI Services table. @retval EFI_SUCCESS If the interface could be successfully installed @retval Others Returned from PeiServicesInstallPpi() **/ EFI_STATUS EFIAPI PeimFaultTolerantWriteInitialize ( IN EFI_PEI_FILE_HANDLE FileHandle, IN CONST EFI_PEI_SERVICES **PeiServices ) { EFI_STATUS Status; EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkingBlockHeader; EFI_FAULT_TOLERANT_WRITE_HEADER *FtwLastWriteHeader; EFI_FAULT_TOLERANT_WRITE_RECORD *FtwLastWriteRecord; EFI_PHYSICAL_ADDRESS WorkSpaceAddress; UINTN WorkSpaceLength; EFI_PHYSICAL_ADDRESS SpareAreaAddress; UINTN SpareAreaLength; EFI_PHYSICAL_ADDRESS WorkSpaceInSpareArea; FAULT_TOLERANT_WRITE_LAST_WRITE_DATA FtwLastWrite; FtwWorkingBlockHeader = NULL; FtwLastWriteHeader = NULL; FtwLastWriteRecord = NULL; WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64); if (WorkSpaceAddress == 0) { WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase); } WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize); SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64); if (SpareAreaAddress == 0) { SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase); } SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize); // // The address of FTW working base and spare base must not be 0. // ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0)); FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceAddress; if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) { Status = FtwGetLastWriteHeader ( FtwWorkingBlockHeader, WorkSpaceLength, &FtwLastWriteHeader ); if (!EFI_ERROR (Status)) { Status = FtwGetLastWriteRecord ( FtwLastWriteHeader, &FtwLastWriteRecord ); } if (!EFI_ERROR (Status)) { ASSERT (FtwLastWriteRecord != NULL); if ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE)) { // // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set. // It means the target buffer has been backed up in spare block, then target block has been erased, // but the target buffer has not been writen in target block from spare block, we need to build // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data. // FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) SpareAreaAddress + FtwLastWriteRecord->RelativeOffset); FtwLastWrite.SpareAddress = SpareAreaAddress; FtwLastWrite.Length = SpareAreaLength; DEBUG (( EFI_D_INFO, "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n", (UINTN) FtwLastWrite.TargetAddress, (UINTN) FtwLastWrite.SpareAddress, (UINTN) FtwLastWrite.Length)); BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA)); } } } else { FtwWorkingBlockHeader = NULL; // // If the working block workspace is not valid, try to find workspace in the spare block. // WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength; while (WorkSpaceInSpareArea >= SpareAreaAddress) { if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *) (UINTN) WorkSpaceInSpareArea)) { // // Found the workspace. // DEBUG ((EFI_D_INFO, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN) WorkSpaceInSpareArea)); FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceInSpareArea; break; } WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID); } if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) { // // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it. // FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress); FtwLastWrite.SpareAddress = SpareAreaAddress; FtwLastWrite.Length = SpareAreaLength; DEBUG (( EFI_D_INFO, "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n", (UINTN) FtwLastWrite.TargetAddress, (UINTN) FtwLastWrite.SpareAddress, (UINTN) FtwLastWrite.Length)); BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA)); } else { // // Both are invalid. // DEBUG ((EFI_D_ERROR, "FtwPei: Both working and spare block are invalid.\n")); } } // // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done. // return PeiServicesInstallPpi (&mPpiListVariable); }