mirror of https://github.com/acidanthera/audk.git
503 lines
15 KiB
C
503 lines
15 KiB
C
/** @file
|
|
|
|
Internal functions to operate Working Block Space.
|
|
|
|
Copyright (c) 2006 - 2008, 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 "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 = gBS->CalculateCrc32 (
|
|
(UINT8 *) &WorkingBlockHeader,
|
|
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
|
|
&WorkingBlockHeader.Crc
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {
|
|
DEBUG ((EFI_D_FTW_LITE, "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 = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - 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 = gBS->CalculateCrc32 (
|
|
(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 FvBlock FVB Protocol interface to access SrcBlock and DestBlock
|
|
@param Lba Lba of a block
|
|
@param Offset Offset on the Lba
|
|
@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 Others Access block device error.
|
|
Notes:
|
|
Assume all bits of State are inside the same BYTE.
|
|
@retval EFI_ABORTED Read block fail
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FtwUpdateFvState (
|
|
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN Offset,
|
|
IN UINT8 NewBit
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 State;
|
|
UINTN Length;
|
|
|
|
//
|
|
// Read state from device, assume State is only one byte.
|
|
//
|
|
Length = sizeof (UINT8);
|
|
Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
State ^= FTW_POLARITY_REVERT;
|
|
State = (UINT8) (State | NewBit);
|
|
State ^= FTW_POLARITY_REVERT;
|
|
|
|
//
|
|
// Write state back to device
|
|
//
|
|
Length = sizeof (UINT8);
|
|
Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
|
|
|
|
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;
|
|
|
|
*FtwLastRecord = NULL;
|
|
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;
|
|
|
|
//
|
|
// Initialize WorkSpace as FTW_ERASED_BYTE
|
|
//
|
|
SetMem (
|
|
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 + FTW_LITE_RECORD_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;
|
|
}
|
|
|
|
/**
|
|
Reclaim the work space on the working block.
|
|
|
|
|
|
@param FtwLiteDevice Point to private data of FTW driver
|
|
@param PreserveRecord Whether get the last record or not
|
|
|
|
@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,
|
|
IN BOOLEAN PreserveRecord
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *TempBuffer;
|
|
UINTN TempBufferSize;
|
|
UINT8 *Ptr;
|
|
UINTN Length;
|
|
UINTN Index;
|
|
UINTN SpareBufferSize;
|
|
UINT8 *SpareBuffer;
|
|
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
|
|
EFI_FTW_LITE_RECORD *Record;
|
|
|
|
DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));
|
|
|
|
//
|
|
// Read all original data from working block to a memory buffer
|
|
//
|
|
TempBufferSize = FtwLiteDevice->SpareAreaLength;
|
|
TempBuffer = AllocateZeroPool (TempBufferSize);
|
|
if (TempBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Ptr = TempBuffer;
|
|
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
|
|
Length = FtwLiteDevice->SizeOfSpareBlock;
|
|
Status = FtwLiteDevice->FtwFvBlock->Read (
|
|
FtwLiteDevice->FtwFvBlock,
|
|
FtwLiteDevice->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) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
|
|
FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase;
|
|
|
|
//
|
|
// Clear the content of buffer that will save the new work space data
|
|
//
|
|
SetMem (Ptr, FtwLiteDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
|
|
|
|
//
|
|
// Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
|
|
//
|
|
CopyMem (
|
|
Ptr,
|
|
FtwLiteDevice->FtwWorkSpaceHeader,
|
|
sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
|
|
);
|
|
if (PreserveRecord) {
|
|
//
|
|
// Get the last record
|
|
//
|
|
Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
|
|
Record = FtwLiteDevice->FtwLastRecord;
|
|
if (!EFI_ERROR (Status) &&
|
|
Record != NULL &&
|
|
Record->WriteAllocated == FTW_VALID_STATE &&
|
|
Record->WriteCompleted != FTW_VALID_STATE) {
|
|
CopyMem (
|
|
(UINT8 *) Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
|
|
Record,
|
|
FTW_LITE_RECORD_SIZE
|
|
);
|
|
}
|
|
}
|
|
|
|
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 = AllocatePool (SpareBufferSize);
|
|
if (SpareBuffer == NULL) {
|
|
FreePool (TempBuffer);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Ptr = SpareBuffer;
|
|
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
|
|
Length = FtwLiteDevice->SizeOfSpareBlock;
|
|
Status = FtwLiteDevice->FtwBackupFvb->Read (
|
|
FtwLiteDevice->FtwBackupFvb,
|
|
FtwLiteDevice->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 (FtwLiteDevice);
|
|
Ptr = TempBuffer;
|
|
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
|
|
Length = FtwLiteDevice->SizeOfSpareBlock;
|
|
Status = FtwLiteDevice->FtwBackupFvb->Write (
|
|
FtwLiteDevice->FtwBackupFvb,
|
|
FtwLiteDevice->FtwSpareLba + Index,
|
|
0,
|
|
&Length,
|
|
Ptr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (TempBuffer);
|
|
FreePool (SpareBuffer);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
Ptr += Length;
|
|
}
|
|
//
|
|
// Free TempBuffer
|
|
//
|
|
FreePool (TempBuffer);
|
|
|
|
//
|
|
// Write the spare block to working block
|
|
//
|
|
Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (SpareBuffer);
|
|
return Status;
|
|
}
|
|
//
|
|
// Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
|
|
//
|
|
Status = FtwEraseSpareBlock (FtwLiteDevice);
|
|
Ptr = SpareBuffer;
|
|
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
|
|
Length = FtwLiteDevice->SizeOfSpareBlock;
|
|
Status = FtwLiteDevice->FtwBackupFvb->Write (
|
|
FtwLiteDevice->FtwBackupFvb,
|
|
FtwLiteDevice->FtwSpareLba + Index,
|
|
0,
|
|
&Length,
|
|
Ptr
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (SpareBuffer);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
Ptr += Length;
|
|
}
|
|
|
|
FreePool (SpareBuffer);
|
|
|
|
DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|