alder_lake_bios/Insyde/InsydeModulePkg/Universal/FirmwareVolume/FtwLiteRuntimeDxe/FtwWorkSpace.c

535 lines
16 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2012 - 2019, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "FtwLite.h"
/**
Check to see if it is a valid 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.
**/
BOOLEAN
IsValidWorkSpace (
IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
)
{
EFI_STATUS Status;
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER WorkingBlockHeader;
ASSERT (WorkingHeader != NULL);
if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {
return FALSE;
}
//
// Check signature with gEfiSystemNvDataFvGuid
//
if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
return FALSE;
}
//
// Check the CRC of header
//
CopyMem (
&WorkingBlockHeader,
WorkingHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
);
//
// Filter out the Crc and State fields
//
SetMem (&WorkingBlockHeader.Crc, sizeof (UINT32), FTW_ERASED_BYTE);
WorkingBlockHeader.WorkingBlockValid = FTW_ERASE_POLARITY;
WorkingBlockHeader.WorkingBlockInvalid = FTW_ERASE_POLARITY;
//
// Calculate the Crc of woking block header
//
Status = RuntimeDriverCalculateCrc32 (
(UINT8 *) &WorkingBlockHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
&WorkingBlockHeader.Crc
);
if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {
DEBUG ((mFtwLiteError, "FtwLite: Work block header CRC check error\n"));
return FALSE;
}
return TRUE;
}
/**
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
)
{
EFI_STATUS Status;
ASSERT (WorkingHeader != NULL);
//
// Here using gEfiSystemNvDataFvGuid as the signature.
//
CopyMem (
&WorkingHeader->Signature,
&gEfiSystemNvDataFvGuid,
sizeof (EFI_GUID)
);
WorkingHeader->WriteQueueSize = (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwStateGuid, 1) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
//
// Crc is calculated with all the fields except Crc and STATE
//
WorkingHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
WorkingHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
SetMem (&WorkingHeader->Crc, sizeof (UINT32), FTW_ERASED_BYTE);
//
// Calculate the CRC value
//
Status = RuntimeDriverCalculateCrc32 (
(UINT8 *) WorkingHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
&WorkingHeader->Crc
);
ASSERT_EFI_ERROR (Status);
//
// Restore the WorkingBlockValid flag to VALID state
//
WorkingHeader->WorkingBlockValid = FTW_VALID_STATE;
WorkingHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
return EFI_SUCCESS;
}
/**
Update a bit of state on a block device. The location of the bit is calculated by the
(Lba, Offset, bit). Here bit is determined by the the name of a certain bit.
@param FtwLiteDevice The private data of FTW_LITE driver.
@param FvBlock FVB Protocol interface to access SrcBlock and DestBlock.
@param Lba Lba of a block.
@param Offset Offset on the Lba.
@param Address The Address of FvState.
@param NewBit New value that will override the old value if it can be change.
@retval EFI_SUCCESS A state bit has been updated successfully
@retval EFI_ABORTED Read block fail.
@return Others Access block device error.
**/
EFI_STATUS
FtwUpdateFvState (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINTN Address,
IN UINT8 NewBit
)
{
EFI_STATUS Status;
UINT8 State;
UINTN Length;
UINT8 ReadState;
UINTN TryWritetimes;
BOOLEAN WriteSuccess;
//
// Read state from device, assume State is only one byte.
//
Length = sizeof (UINT8);
if (mSmst == NULL) {
Lba += Offset / FtwLiteDevice->SizeOfSpareBlock;
Offset = Offset % FtwLiteDevice->SizeOfSpareBlock;
Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
} else {
CopyMem (&State, (UINT8 *) Address, Length);
}
State ^= FTW_POLARITY_REVERT;
State |= NewBit;
State ^= FTW_POLARITY_REVERT;
WriteSuccess = FALSE;
TryWritetimes = 0;
Status = EFI_ABORTED;
//
// Start try write state to device and using do-while to make sure
// at least write state once.
//
do {
//
// Write state back to device
//
Length = sizeof (UINT8);
if (mSmst == NULL) {
Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
} else {
Status = FtwLiteDevice->SmmFwbServices->Write (
FtwLiteDevice->SmmFwbServices,
(UINTN)Address,
&Length,
&State
);
}
Length = sizeof (UINT8);
if (mSmst == NULL) {
Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &ReadState);
} else {
CopyMem (&ReadState, (UINT8 *) Address, Length);
}
//
// Check write data is whther correct
//
WriteSuccess = (ReadState == State) ? TRUE : FALSE;
TryWritetimes++;
} while (!WriteSuccess && TryWritetimes < FTW_MAX_TRY_ACCESS_FVB_TIMES);
//
// if write failed, set status as EFI_ABORT
//
if (!WriteSuccess) {
Status = EFI_ABORTED;
}
return Status;
}
/**
Get the last Write record pointer. The last record is the record whose 'complete' state hasn't
been set. After all, this header may be a EMPTY header entry for next Allocate.
@param FtwLiteDevice Private data of this driver.
@param FtwLastRecord Pointer to retrieve the last write record.
@retval EFI_SUCCESS Get the last write record successfully.
@retval EFI_ABORTED The FTW work space is damaged.
**/
EFI_STATUS
FtwGetLastRecord (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
OUT EFI_FTW_LITE_RECORD **FtwLastRecord
)
{
EFI_FTW_LITE_RECORD *Record;
Record = (EFI_FTW_LITE_RECORD *) (FtwLiteDevice->FtwWorkSpaceHeader + 1);
while (Record->WriteCompleted == FTW_VALID_STATE) {
//
// If Offset exceed the FTW work space boudary, return error.
//
if ((UINTN) ((UINT8 *) Record - FtwLiteDevice->FtwWorkSpace) > FtwLiteDevice->FtwWorkSpaceSize) {
return EFI_ABORTED;
}
Record++;
}
//
// Last write record is found
//
*FtwLastRecord = Record;
return EFI_SUCCESS;
}
/**
Read from working block to refresh the work space in memory.
@param FtwLiteDevice 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_LITE_DEVICE *FtwLiteDevice
)
{
EFI_STATUS Status;
UINTN Length;
UINTN Offset;
EFI_FTW_LITE_RECORD *Record;
UINTN Index;
//
// Initialize WorkSpace as FTW_ERASED_BYTE
//
SetMem (FtwLiteDevice->FtwWorkSpace, FtwLiteDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
//
// Read from working block
//
if (mSmst == NULL) {
if (FtwLiteDevice->NumberOfFtwWorkBlock != 0) {
for (Index = 0; Index < FtwLiteDevice->NumberOfFtwWorkBlock; Index++) {
Length = FtwLiteDevice->FtwWorkSpaceSize / FtwLiteDevice->NumberOfFtwWorkBlock;
Status = FtwLiteDevice->FtwFvBlock->Read (
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkSpaceLba + Index,
FtwLiteDevice->FtwWorkSpaceBase,
&Length,
FtwLiteDevice->FtwWorkSpace + (Length * Index)
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
}
} else {
Length = FtwLiteDevice->FtwWorkSpaceSize;
Status = FtwLiteDevice->FtwFvBlock->Read (
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkSpaceLba,
FtwLiteDevice->FtwWorkSpaceBase,
&Length,
FtwLiteDevice->FtwWorkSpace
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
}
} else {
Length = FtwLiteDevice->FtwWorkSpaceSize;
CopyMem (FtwLiteDevice->FtwWorkSpace, (UINT8 *) (UINTN) FtwLiteDevice->WorkSpaceAddress, Length);
}
//
// 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);
if (EFI_ERROR (Status)) {
DEBUG ((mFtwLiteError, "FtwLite: Reclaim workspace - %r\n", Status));
return EFI_ABORTED;
}
}
return EFI_SUCCESS;
}
/**
Reclaim the work space. Get rid of all the completed write records and write records in
the Fault Tolerant work space.
@param FtwLiteDevice Point to private data of FTW driver.
@param FtwSpaceBuffer Buffer to contain the reclaimed clean data.
@param BufferSize Size of the FtwSpaceBuffer.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_BUFFER_TOO_SMALL The FtwSpaceBuffer is too small.
@retval EFI_ABORTED The function could not complete successfully.
**/
EFI_STATUS
CleanupWorkSpace (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN OUT UINT8 *FtwSpaceBuffer,
IN UINTN BufferSize
)
{
UINTN Length;
EFI_FTW_LITE_RECORD *Record;
//
// To check if the buffer is large enough
//
Length = FtwLiteDevice->FtwWorkSpaceSize;
if (BufferSize < Length) {
return EFI_BUFFER_TOO_SMALL;
}
//
// Clear the content of buffer that will save the new work space data
//
SetMem (FtwSpaceBuffer, Length, FTW_ERASED_BYTE);
//
// Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
//
CopyMem (
FtwSpaceBuffer,
FtwLiteDevice->FtwWorkSpaceHeader,
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
);
//
// Get the last record
//
Record = FtwLiteDevice->FtwLastRecord;
if ((Record != NULL) && (Record->WriteAllocated == FTW_VALID_STATE) && (Record->WriteCompleted != FTW_VALID_STATE)) {
CopyMem (
(UINT8 *) FtwSpaceBuffer + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
Record,
WRITE_TOTAL_SIZE
);
}
return EFI_SUCCESS;
}
/**
Reclaim the work space on the working block.
@param FtwLiteDevice Point to private data of FTW driver.
@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_LITE_DEVICE *FtwLiteDevice
)
{
EFI_STATUS Status;
UINT8 *TempBuffer;
UINTN TempBufferSize;
UINT8 *Ptr;
UINTN SpareBufferSize;
UINT8 *SpareBuffer;
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
DEBUG ((mFtwLiteError, "FtwLite: start to reclaim work space\n"));
//
// Read all original data from working block to a memory buffer
//
TempBufferSize = FtwLiteDevice->SpareAreaLength;
TempBuffer = FtwAllocateZeroBuffer (FtwLiteDevice, TempBufferSize);
if (TempBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (TempBuffer, TempBufferSize);
Ptr = TempBuffer;
Status = FtwLiteReadWorkBlock (FtwLiteDevice, TempBuffer);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&TempBuffer);
return EFI_ABORTED;
}
//
// Clean up the workspace, remove all the completed records.
//
Ptr = TempBuffer +
((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
FtwLiteDevice->SizeOfSpareBlock +
FtwLiteDevice->FtwWorkSpaceBase;
Status = CleanupWorkSpace (
FtwLiteDevice,
Ptr,
FtwLiteDevice->FtwWorkSpaceSize
);
CopyMem (
FtwLiteDevice->FtwWorkSpace,
Ptr,
FtwLiteDevice->FtwWorkSpaceSize
);
Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
//
// Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
//
WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;
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 = FtwLiteDevice->SpareAreaLength;
SpareBuffer = FtwAllocateZeroBuffer (FtwLiteDevice, SpareBufferSize);
if (SpareBuffer == NULL) {
FtwFreePool (FtwLiteDevice, (VOID **)&TempBuffer);
return EFI_OUT_OF_RESOURCES;
}
Ptr = SpareBuffer;
Status = FtwLiteReadSpareBlock (FtwLiteDevice, SpareBuffer);
//
// Write the memory buffer to spare block
//
Status = FtwWriteSpareBlock (FtwLiteDevice, TempBuffer, SpareBufferSize, FTW_MAX_TRY_ACCESS_FVB_TIMES);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&TempBuffer);
FtwFreePool (FtwLiteDevice, (VOID **)&SpareBuffer);
return EFI_ABORTED;
}
FtwFreePool (FtwLiteDevice, (VOID **)&TempBuffer);
//
// Write the spare block to working block
//
Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&SpareBuffer);
return Status;
}
//
// Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
//
Status = FtwWriteSpareBlock (FtwLiteDevice, SpareBuffer, SpareBufferSize, FTW_MAX_TRY_ACCESS_FVB_TIMES);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&SpareBuffer);
return EFI_ABORTED;
}
FtwFreePool (FtwLiteDevice, (VOID **)&SpareBuffer);
DEBUG ((mFtwLiteError, "FtwLite: reclaim work space success\n"));
return EFI_SUCCESS;
}