/** 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 Length; UINTN RemainingSpaceSize; // // Initialize WorkSpace as FTW_ERASED_BYTE // SetMem ( FtwDevice->FtwWorkSpace, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE ); // // Read from working block // Length = FtwDevice->FtwWorkSpaceSize; Status = FtwDevice->FtwFvBlock->Read ( FtwDevice->FtwFvBlock, FtwDevice->FtwWorkSpaceLba, FtwDevice->FtwWorkSpaceBase, &Length, 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 // Length = FtwDevice->FtwWorkSpaceSize; Status = FtwDevice->FtwFvBlock->Read ( FtwDevice->FtwFvBlock, FtwDevice->FtwWorkSpaceLba, FtwDevice->FtwWorkSpaceBase, &Length, 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; }
/** Allocates space for the protocol to maintain information about writes. Since writes must be completed in a fault tolerant manner and multiple updates will require more resources to be successful, this function enables the protocol to ensure that enough space exists to track information about the upcoming writes. All writes must be completed or aborted before another fault tolerant write can occur. @param This The pointer to this protocol instance. @param CallerId The GUID identifying the write. @param PrivateDataSize The size of the caller's private data that must be recorded for each write. @param NumberOfWrites The number of fault tolerant block writes that will need to occur. @return EFI_SUCCESS The function completed successfully @retval EFI_ABORTED The function could not complete successfully. @retval EFI_ACCESS_DENIED All allocated writes have not been completed. **/ 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 Length; UINTN Offset; EFI_FTW_DEVICE *FtwDevice; EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; FtwDevice = FTW_CONTEXT_FROM_THIS (This); Status = WorkSpaceRefresh (FtwDevice); if (EFI_ERROR (Status)) { return EFI_ABORTED; } // // Check if there is enough space for the coming allocation // if (WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) { DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId)); return EFI_BUFFER_TOO_SMALL; } // // Find the last write header and record. // If the FtwHeader is complete, skip the completed last write header/records // FtwHeader = FtwDevice->FtwLastWriteHeader; // // Previous write has not completed, access denied. // if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) { return EFI_ACCESS_DENIED; } // // If workspace is not enough, then reclaim workspace // Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace; if (Offset + WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) { Status = FtwReclaimWorkSpace (FtwDevice, TRUE); if (EFI_ERROR (Status)) { return EFI_ABORTED; } FtwHeader = FtwDevice->FtwLastWriteHeader; } // // Prepare FTW write header, // overwrite the buffer and write to workspace. // FtwHeader->WritesAllocated = FTW_INVALID_STATE; FtwHeader->Complete = FTW_INVALID_STATE; CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID)); FtwHeader->NumberOfWrites = NumberOfWrites; FtwHeader->PrivateDataSize = PrivateDataSize; FtwHeader->HeaderAllocated = FTW_VALID_STATE; Length = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER); Status = FtwDevice->FtwFvBlock->Write ( FtwDevice->FtwFvBlock, FtwDevice->FtwWorkSpaceLba, FtwDevice->FtwWorkSpaceBase + Offset, &Length, (UINT8 *) FtwHeader ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } // // Update Header->WriteAllocated as VALID // Status = FtwUpdateFvState ( FtwDevice->FtwFvBlock, FtwDevice->FtwWorkSpaceLba, FtwDevice->FtwWorkSpaceBase + Offset, WRITES_ALLOCATED ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } DEBUG ( (EFI_D_ERROR, "Ftw: Allocate() success, Caller:%g, # %d\n", CallerId, NumberOfWrites) ); return EFI_SUCCESS; }
EFI_STATUS WorkSpaceRefresh ( IN EFI_FTW_LITE_DEVICE *FtwLiteDevice ) /*++ Routine Description: Read from working block to refresh the work space in memory. Arguments: FtwLiteDevice - Point to private data of FTW driver Returns: EFI_SUCCESS - The function completed successfully EFI_ABORTED - The function could not complete successfully. --*/ { EFI_STATUS Status; UINTN Length; UINTN Offset; EFI_FTW_LITE_RECORD *Record; // // Initialize WorkSpace as FTW_ERASED_BYTE // EfiSetMem ( FtwLiteDevice->FtwWorkSpace, FtwLiteDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE ); // // Read from working block // Length = FtwLiteDevice->FtwWorkSpaceSize; Status = FtwLiteDevice->FtwFvBlock->Read ( FtwLiteDevice->FtwFvBlock, FtwLiteDevice->FtwWorkSpaceLba, FtwLiteDevice->FtwWorkSpaceBase, &Length, FtwLiteDevice->FtwWorkSpace ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } // // Refresh the FtwLastRecord // Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord); Record = FtwLiteDevice->FtwLastRecord; Offset = (UINTN) (UINT8 *) Record - (UINTN) FtwLiteDevice->FtwWorkSpace; // // IF work space has error or Record is out of the workspace limit, THEN // call reclaim. // if (EFI_ERROR (Status) || (Offset + WRITE_TOTAL_SIZE >= FtwLiteDevice->FtwWorkSpaceSize)) { // // reclaim work space in working block. // Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_FTW_LITE, "FtwLite: Reclaim workspace - %r\n", Status)); return EFI_ABORTED; } } return EFI_SUCCESS; }