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

1108 lines
34 KiB
C

/** @file
Internal functions to support fault tolerant write.
;******************************************************************************
;* Copyright (c) 2012 - 2021, 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"
extern EFI_GUID gEfiAlternateFvBlockGuid;
/**
Check whether a flash buffer is erased.
@param Polarity All 1 or all 0.
@param Buffer Buffer to check.
@param BufferSize Size of the buffer.
@retval TRUE This is an erased buffer.
@retval FALSE This isn't an erased buffer.
**/
BOOLEAN
IsErasedFlashBuffer (
IN BOOLEAN Polarity,
IN UINT8 *Buffer,
IN UINTN BufferSize
)
{
UINT8 ErasedValue;
UINT8 *Ptr;
if (Polarity) {
ErasedValue = 0xFF;
} else {
ErasedValue = 0;
}
Ptr = Buffer;
while (BufferSize--) {
if (*Ptr++ != ErasedValue) {
return FALSE;
}
}
return TRUE;
}
/**
To Erase one block. The size is FTW_BLOCK_SIZE.
@param FtwLiteDevice Calling context
@param FvBlock FVB Protocol interface
@param FvBaseAddr The Firmware Volume BaseAddress of target block.
@param Lba Lba of the firmware block
@retval EFI_SUCCESS Block LBA is Erased successfully.
@retval EFI_ABORTED Error occurs.
**/
EFI_STATUS
FtwEraseBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
IN EFI_PHYSICAL_ADDRESS FvBaseAddr,
EFI_LBA Lba
)
{
EFI_STATUS Status;
UINTN TryErasetimes;
BOOLEAN EraseSuccess;
UINTN TotalLength;
UINT8 *Buffer;
UINTN DataOffset;
UINTN BlockLength;
UINTN Index;
UINT8 *Ptr;
UINTN NumBytes;
UINTN Length;
//
// allocate pool for read data from block
//
TotalLength = FtwLiteDevice->SpareAreaLength;
Buffer = FtwAllocateZeroBuffer (FtwLiteDevice, TotalLength);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Start try to erase whole block and using do-while to make sure
// at lease erase once.
//
EraseSuccess = FALSE;
TryErasetimes = 0;
Status = EFI_SUCCESS;
do {
//
//Erase whole block
//
Status = EFI_SUCCESS;
if (mSmst == NULL) {
Status = FvBlock->EraseBlocks (
FvBlock,
Lba,
FtwLiteDevice->NumberOfSpareBlock,
EFI_LBA_LIST_TERMINATOR
);
} else {
NumBytes = FtwLiteDevice->SpareAreaLength;
Status = FtwLiteDevice->SmmFwbServices->EraseBlocks (
FtwLiteDevice->SmmFwbServices,
(UINTN)(FvBaseAddr + (UINTN)Lba * FtwLiteDevice->SizeOfSpareBlock),
&NumBytes
);
}
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return Status;
}
if (mSmst == NULL) {
//
// Read whole block
//
Ptr = Buffer;
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index++) {
BlockLength = FtwLiteDevice->SizeOfSpareBlock;
Status = FtwLiteDevice->FtwBackupFvb->Read (
FvBlock,
Lba + Index,
0,
&BlockLength,
Ptr
);
if (EFI_ERROR (Status)) {
DEBUG ((mFtwLiteError, "FtwLite: FVB Read spare block - %r\n", Status));
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return Status;
}
Ptr += BlockLength;
}
} else {
Length = FtwLiteDevice->SizeOfSpareBlock * FtwLiteDevice->NumberOfSpareBlock;
CopyMem (
Buffer,
(UINT8 *) (UINTN) (FvBaseAddr + ((UINTN) Lba * FtwLiteDevice->SizeOfSpareBlock)),
Length
);
}
//
//Set Erase success as default
//
EraseSuccess = TRUE;
for (DataOffset = 0; DataOffset < TotalLength; DataOffset++) {
//
// Find any data isn't 0xff indicates erase failed
//
if (Buffer[DataOffset] != 0xff) {
EraseSuccess = FALSE;
break;
}
}
TryErasetimes++;
} while (!EraseSuccess && TryErasetimes < FTW_MAX_TRY_ACCESS_FVB_TIMES);
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
if (!EraseSuccess) {
Status = EFI_ABORTED;
}
return Status;
}
/**
Function uses to erase whole spare block.
@param FtwLiteDevice Calling context
@retval EFI_SUCCESS Whole spare block is Erased successfully.
@retval EFI_ABORTED Error occurs.
**/
EFI_STATUS
FtwEraseSpareBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
)
{
return FtwEraseBlock (
FtwLiteDevice,
FtwLiteDevice->FtwBackupFvb,
FtwLiteDevice->SpareAreaFvBaseAddr,
FtwLiteDevice->FtwSpareLba
);
}
/**
Get firmware block by address.
@param FvBlockHandle Address specified the block.
@param FvBlock The block caller wanted.
@retval EFI_SUCCESS Get Fvb protocol successful.
@retval EFI_NOT_FOUND Block not found
**/
EFI_STATUS
GetFvbByAddress (
IN EFI_PHYSICAL_ADDRESS Address,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
EFI_PHYSICAL_ADDRESS FvbBaseAddress;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
*FvBlock = NULL;
//
// Locate all handles of Fvb protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolumeBlockProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
//
// Search all FVB until find the right one
//
for (Index = 0; Index < HandleCount; Index += 1) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareVolumeBlockProtocolGuid,
(VOID **) &Fvb
);
if (EFI_ERROR (Status)) {
Status = EFI_NOT_FOUND;
break;
}
//
// Compare the address and select the right one
//
Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
if (EFI_ERROR (Status)) {
continue;
}
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + (FwVolHeader->FvLength - 1))) && IsNvStorageHandle (HandleBuffer[Index])) {
*FvBlock = Fvb;
Status = EFI_SUCCESS;
break;
}
}
FreePool (HandleBuffer);
return Status;
}
/**
According physical address or LBA to check this address is whether
located in working block.
@param FtwLiteDevice Calling context.
@param FvBlock Fvb protocol instance.
@param Lba input LBA for proteced mode use.
@param Address input target address for SMM mode use.
@retval TRUE This address or LBA is located in working block
@retval FALSE This address or LBA isn't located in working block
**/
BOOLEAN
IsInWorkingBlock (
EFI_FTW_LITE_DEVICE *FtwLiteDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
EFI_LBA Lba,
EFI_PHYSICAL_ADDRESS Address
)
{
//
// If matching the following condition, the target block is in working block.
// 1. Target block is on the FV of working block (Using the same FVB protocol instance).
// 2. Lba falls into the range of working block.
//
if (mSmst == NULL) {
return (BOOLEAN)
(
(FvBlock == FtwLiteDevice->FtwFvBlock) &&
(Lba >= FtwLiteDevice->FtwWorkBlockLba) &&
(Lba <= FtwLiteDevice->FtwWorkSpaceLba)
);
} else {
return (BOOLEAN)
(
(Address >= FtwLiteDevice->WorkBlockAddr) &&
(Address <= FtwLiteDevice->WorkSpaceAddress)
);
}
}
/**
Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE. Spare block
is accessed by FTW backup FVB protocol interface. LBA is FtwLiteDevice->FtwSpareLba.
Target block is accessed by FvBlock protocol interface. LBA is Lba.
@param FtwLiteDevice The private data of FTW_LITE driver.
@param FvBlock FVB Protocol interface to access target block.
@param FvBaseAddr Frimware volume base address.
@param Lba Lba of the target block.
@retval EFI_SUCCESS Spare block content is copied to target block.
@retval EFI_INVALID_PARAMETER Input parameter error.
@retval EFI_OUT_OF_RESOURCES Allocate memory error.
@retval EFI_ABORTED The function could not complete successfully.
**/
EFI_STATUS
FlushSpareBlockToTargetBlock (
EFI_FTW_LITE_DEVICE *FtwLiteDevice,
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
IN EFI_PHYSICAL_ADDRESS FvBaseAddr,
EFI_LBA Lba
)
{
EFI_STATUS Status;
UINTN Length;
UINT8 *Buffer;
UINTN Count;
UINT8 *Ptr;
UINTN Index;
if ((FtwLiteDevice == NULL) || (FvBlock == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Allocate a memory buffer
//
Length = FtwLiteDevice->SpareAreaLength;
Buffer = FtwAllocateZeroBuffer (FtwLiteDevice, Length);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Read all content of spare block to memory buffer
//
Ptr = Buffer;
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
Count = FtwLiteDevice->SizeOfSpareBlock;
Status = FtwLiteDevice->FtwBackupFvb->Read (
FtwLiteDevice->FtwBackupFvb,
FtwLiteDevice->FtwSpareLba + Index,
0,
&Count,
Ptr
);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return Status;
}
Ptr += Count;
}
//
// Write memory buffer to target block
//
Status = WriteTargetBlock (FtwLiteDevice, FvBlock, FvBaseAddr, Lba, Buffer, Length, FTW_MAX_TRY_ACCESS_FVB_TIMES);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return EFI_ABORTED;
}
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return Status;
}
/**
Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE. Spare block is
accessed by FTW backup FVB protocol interface. LBA is FtwLiteDevice->FtwSpareLba. Working
block is accessed by FTW working FVB protocol interface. LBA is FtwLiteDevice->FtwWorkBlockLba.
@param FtwLiteDevice The private data of FTW_LITE driver.
@retval EFI_SUCCESS Spare block content is copied to target block.
@retval EFI_OUT_OF_RESOURCES Allocate memory error.
@retval EFI_ABORTED The function could not complete successfully.
Notes:
Since the working block header is important when FTW initializes, the state of the operation
should be handled carefully. The Crc value is calculated without STATE element.
**/
EFI_STATUS
FlushSpareBlockToWorkingBlock (
EFI_FTW_LITE_DEVICE *FtwLiteDevice
)
{
EFI_STATUS Status;
UINTN Length;
UINT8 *Buffer;
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
EFI_LBA WorkSpaceLbaOffset;
UINTN WorkSpaceOffset;
VARIABLE_STORE_HEADER *VariableHeader;
ECP_VARIABLE_STORE_HEADER *EcpVariableHeader;
UINTN FvHeaderSize;
BOOLEAN IsVariableState;
UINTN VariableStateOffset;
//
// Allocate a memory buffer
//
Length = FtwLiteDevice->SpareAreaLength;
Buffer = FtwAllocateZeroBuffer (FtwLiteDevice, Length);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// To guarantee that the WorkingBlockValid is set on spare block
//
WorkSpaceLbaOffset = FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba;
WorkSpaceOffset = (UINTN)(FtwLiteDevice->WorkSpaceAddress - FtwLiteDevice->WorkBlockAddr);
Status = FtwUpdateFvState (
FtwLiteDevice,
FtwLiteDevice->FtwBackupFvb,
FtwLiteDevice->FtwSpareLba + WorkSpaceLbaOffset,
FtwLiteDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
(UINTN)(FtwLiteDevice->SpareAreaAddress + WorkSpaceOffset) + (UINTN)(sizeof (EFI_GUID) + sizeof (UINT32)),
WORKING_BLOCK_VALID
);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return EFI_ABORTED;
}
//
// Read from spare block to memory buffer
//
Status = FtwLiteReadSpareBlock (FtwLiteDevice, Buffer);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return Status;
}
//
// Clear the CRC and STATE, copy data from spare to working block.
//
WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) WorkSpaceLbaOffset * FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase);
InitWorkSpaceHeader (WorkingBlockHeader);
WorkingBlockHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
//
// target block is working block, then
// Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
// before erase the working block.
//
// Offset = EFI_FIELD_OFFSET(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
// WorkingBlockInvalid);
// To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32).
//
Status = FtwUpdateFvState (
FtwLiteDevice,
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkSpaceLba,
FtwLiteDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
(UINTN) (FtwLiteDevice->WorkSpaceAddress) + (UINTN) (sizeof (EFI_GUID) + sizeof (UINT32)),
WORKING_BLOCK_INVALID
);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return EFI_ABORTED;
}
FtwLiteDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
//
// Use the state of VARIABLE_STORE_HEADER to check the variable is health or not.
// Setting the state of VARIABLE_STORE_HEADER to VARIABLE_STORE_HEALTHY, after
// whole data has been written to NV storage.
//
FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY);
//
// Clear the state of VARIABLE_STORE_HEADER
//
IsVariableState = FALSE;
if (PcdGetBool (PcdUseEcpVariableStoreHeader)) {
EcpVariableHeader = (ECP_VARIABLE_STORE_HEADER *) (Buffer + FvHeaderSize);
if (EcpVariableHeader->State == VARIABLE_STORE_HEALTHY) {
IsVariableState = TRUE;
EcpVariableHeader->State = 0xFF;
}
} else {
VariableHeader = (VARIABLE_STORE_HEADER *) (Buffer + FvHeaderSize);
if (VariableHeader->State == VARIABLE_STORE_HEALTHY) {
IsVariableState = TRUE;
VariableHeader->State = 0xFF;
}
}
//
// Write memory buffer to working block
//
Status = FtwWriteWorkingBlock (FtwLiteDevice, Buffer, Length, FTW_MAX_TRY_ACCESS_FVB_TIMES);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
return EFI_ABORTED;
}
//
// Since the memory buffer will not be used, free memory Buffer.
//
FtwFreePool (FtwLiteDevice, (VOID **)&Buffer);
//
// update the state of VARIABLE_STORE_HEADER to VARIABLE_STORE_HEALTHY
//
if (IsVariableState) {
if (PcdGetBool (PcdUseEcpVariableStoreHeader)) {
VariableStateOffset = FvHeaderSize + sizeof (UINT32) + sizeof (UINT32) + sizeof (UINT8);
Status = FtwUpdateFvState (
FtwLiteDevice,
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkBlockLba,
VariableStateOffset,
(UINTN) FtwLiteDevice->WorkBlockAddr + VariableStateOffset,
0x01
);
} else {
VariableStateOffset = FvHeaderSize + sizeof (EFI_GUID) + sizeof (UINT32) + sizeof (UINT8);
Status = FtwUpdateFvState (
FtwLiteDevice,
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkBlockLba,
VariableStateOffset,
(UINTN) FtwLiteDevice->WorkBlockAddr + VariableStateOffset,
0x01
);
}
}
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
//
// Update the VALID of the working block
//
// Offset = EFI_FIELD_OFFSET(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
// WorkingBlockValid);
// Hardcode offset sizeof(EFI_GUID)+sizeof(UINT32), to skip Signature and Crc
//
Status = FtwUpdateFvState (
FtwLiteDevice,
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkSpaceLba,
FtwLiteDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
(UINTN) (FtwLiteDevice->WorkSpaceAddress) + (UINTN) (sizeof (EFI_GUID) + sizeof (UINT32)),
WORKING_BLOCK_VALID
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
FtwLiteDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE;
return EFI_SUCCESS;
}
/**
Write spare block and check the data which write to spare block is correct.
@param FtwLiteDevice Calling context.
@param WriteBuffer Write Buffer.
@param WriteLength The size of write data.
@param TryWriteTimes The maximum trying to write times.
@retval EFI_SUCCESS Write data to spare block success.
@retval EFI_OUT_OF_RESOURCE The input value is invalid.
@retval EFI_ABORTED Write data to spare block failed.
**/
EFI_STATUS
FtwWriteSpareBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN UINT8 *WriteBuffer,
IN UINTN WriteLength,
IN UINTN MaxTryWriteTimes
)
{
EFI_STATUS Status;
//
// Check the input value is valid
//
if ((WriteBuffer == NULL) || (WriteLength != FtwLiteDevice->SpareAreaLength)) {
return EFI_INVALID_PARAMETER;
}
//
// Write data to whole target block
//
Status = WriteTargetBlock (
FtwLiteDevice,
FtwLiteDevice->FtwBackupFvb,
FtwLiteDevice->SpareAreaFvBaseAddr,
FtwLiteDevice->FtwSpareLba,
WriteBuffer,
WriteLength,
MaxTryWriteTimes
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
/**
Write working block and check the data which write to working block is correct.
@param FtwLiteDevice Calling context.
@param WriteBuffer Write Buffer.
@param WriteLength The size of write data.
@param TryWriteTimes The maximum trying to write times.
@retval EFI_SUCCESS Write data to working block success.
@retval EFI_OUT_OF_RESOURCE Allocate memory error.
@retval EFI_ABORTED Erase, write, or read error.
@retval EFI_DEVICE_ERROR Cannont write data to working block.
@retval EFI_INVALID_PARAMETER Any input value is invalid
**/
EFI_STATUS
FtwWriteWorkingBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN UINT8 *WriteBuffer,
IN UINTN WriteLength,
IN UINTN MaxTryWriteTimes
)
{
EFI_STATUS Status;
//
// Check the input value is valid
//
if ((WriteBuffer == NULL) || (WriteLength != FtwLiteDevice->SpareAreaLength)) {
return EFI_INVALID_PARAMETER;
}
//
// Write data to whole target block
//
Status = WriteTargetBlock (
FtwLiteDevice,
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->WorkSpaceFvBaseAddr,
FtwLiteDevice->FtwWorkBlockLba,
WriteBuffer,
WriteLength,
MaxTryWriteTimes
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
/**
Write target block and check the data which write to target block is correct.
@param FtwLiteDevice Calling context.
@param FvBlock FVB Protocol interface to access target block.
@param FvBaseAddr The Firmware Volume BaseAddress of target block.
@param StartLba Lba of the target block.
@param WriteBuffer Write Buffer.
@param WriteLength The size of write data.
@param MaxTryWriteTimes The maximum trying to write times.
@retval EFI_SUCCESS Write data to working block success.
@retval EFI_OUT_OF_RESOURCE Allocate memory error.
@retval EFI_ABORTED Erase, write, or read error.
@retval EFI_DEVICE_ERROR Cannont write data to spare block.
@retval EFI_INVALID_PARAMETER Any input value is invalid.
**/
EFI_STATUS
WriteTargetBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
IN EFI_PHYSICAL_ADDRESS FvBaseAddr,
IN EFI_LBA StartLba,
IN UINT8 *WriteBuffer,
IN UINTN WriteLength,
IN UINTN MaxTryWriteTimes
)
{
EFI_STATUS Status;
UINTN TryWritetimes;
BOOLEAN WriteSuccess;
UINTN BlockLength;
UINT8 *ReadBuffer;
UINTN Index;
UINT8 *Ptr;
UINTN Length;
//
// Check the input value is valid
//
if ((WriteBuffer == NULL) || (WriteLength != FtwLiteDevice->SpareAreaLength)) {
return EFI_INVALID_PARAMETER;
}
//
// Allocate pool for ReadBuffer
//
ReadBuffer = FtwAllocateZeroBuffer (FtwLiteDevice, WriteLength);
if (ReadBuffer == NULL) {
return EFI_ABORTED;
}
WriteSuccess = FALSE;
TryWritetimes = 0;
//
// Start try to write memory data to target block and use do-while to make
// sure at least write data once
//
do {
//
// Erase working block
//
Status = FtwEraseBlock (
FtwLiteDevice,
FvBlock,
FvBaseAddr,
StartLba
);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&ReadBuffer);
return EFI_ABORTED;
}
Ptr = WriteBuffer;
//
// Write memory buffer to target block
//
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
BlockLength = FtwLiteDevice->SizeOfSpareBlock;
Status = EFI_SUCCESS;
if (mSmst == NULL) {
Status = FtwLiteDevice->FtwBackupFvb->Write (
FvBlock,
StartLba + Index,
0,
&BlockLength,
Ptr
);
} else {
Status = FtwLiteDevice->SmmFwbServices->Write (
FtwLiteDevice->SmmFwbServices,
(UINTN)(FvBaseAddr + (UINTN)(StartLba + Index) * FtwLiteDevice->SizeOfSpareBlock),
&BlockLength,
Ptr
);
}
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&ReadBuffer);
return Status;
}
Ptr += BlockLength;
}
//
// Read target block from NV storage
//
if (mSmst == NULL) {
Ptr = ReadBuffer;
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
BlockLength = FtwLiteDevice->SizeOfSpareBlock;
Status = FtwLiteDevice->FtwBackupFvb->Read (
FvBlock,
StartLba + Index,
0,
&BlockLength,
Ptr
);
if (EFI_ERROR (Status)) {
FtwFreePool (FtwLiteDevice, (VOID **)&ReadBuffer);
return Status;
}
Ptr += BlockLength;
}
} else {
Length = FtwLiteDevice->SizeOfSpareBlock * FtwLiteDevice->NumberOfSpareBlock;
CopyMem (
ReadBuffer,
(UINT8 *) (UINTN) (FvBaseAddr + (UINTN) StartLba * FtwLiteDevice->SizeOfSpareBlock),
Length
);
}
//
// Check write data is whther correct
//
WriteSuccess = (CompareMem (ReadBuffer, WriteBuffer, WriteLength) == 0) ? TRUE : FALSE;
TryWritetimes++;
} while (!WriteSuccess && TryWritetimes < MaxTryWriteTimes);
FtwFreePool (FtwLiteDevice, (VOID **)&ReadBuffer);
if (!WriteSuccess) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
Clear first FV header size to 0 in spare block.
@param[in] FtwLiteDevice Pointer to EFI_FTW_LITE_DEVICE.
@retval EFI_SUCCESS Write data to working block success.
@retval EFI_DEVICE_ERROR Cannont write data to spare block.
**/
EFI_STATUS
FtwClearSpareHeader (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
)
{
UINT8 DummyData[0x48];
UINTN DataSize;
EFI_STATUS Status;
UINTN WriteAddress;
DataSize = sizeof (DummyData);
ZeroMem (DummyData, DataSize);
if (mSmst == NULL) {
Status = FtwLiteDevice->FtwBackupFvb->Write (
FtwLiteDevice->FtwBackupFvb,
FtwLiteDevice->FtwSpareLba,
0,
&DataSize,
DummyData
);
} else {
WriteAddress = (UINTN)(FtwLiteDevice->SpareAreaFvBaseAddr + (UINTN)FtwLiteDevice->FtwSpareLba * FtwLiteDevice->SizeOfSpareBlock);
Status = FtwLiteDevice->SmmFwbServices->Write (
FtwLiteDevice->SmmFwbServices,
WriteAddress,
&DataSize,
DummyData
);
}
return Status;
}
/**
Read all data from whole work block.
@param FtwLiteDevice Point to private data of FTW driver.
@param Buffer Buffer to save work block data.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_INVALID_PARAMETER Some input parameter is invalid.
**/
EFI_STATUS
FtwLiteReadWorkBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
OUT UINT8 *Buffer
)
{
UINTN Index;
UINTN Length;
EFI_STATUS Status;
if (FtwLiteDevice == NULL || Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
if (mSmst == NULL) {
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index++) {
Length = FtwLiteDevice->SizeOfSpareBlock;
Status = FtwLiteDevice->FtwFvBlock->Read (
FtwLiteDevice->FtwFvBlock,
FtwLiteDevice->FtwWorkBlockLba + Index,
0,
&Length,
Buffer
);
if (EFI_ERROR (Status)) {
break;
}
Buffer += Length;
}
} else {
Length = FtwLiteDevice->SizeOfSpareBlock * FtwLiteDevice->NumberOfSpareBlock;
CopyMem (
Buffer,
(UINT8 *) ((UINTN) FtwLiteDevice->WorkBlockAddr - ((UINTN) FtwLiteDevice->FtwWorkBlockLba * FtwLiteDevice->SizeOfSpareBlock)),
Length
);
}
return Status;
}
/**
Read all data from whole work block.
@param FtwLiteDevice Point to private data of FTW driver.
@param Buffer Buffer to save work block data.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED The function could not complete successfully.
@retval EFI_INVALID_PARAMETER Some input parameter is invalid.
**/
EFI_STATUS
FtwLiteReadSpareBlock (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
OUT UINT8 *Buffer
)
{
UINTN Index;
UINTN Length;
EFI_STATUS Status;
if (FtwLiteDevice == NULL || Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
if (mSmst == NULL) {
for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index++) {
Length = FtwLiteDevice->SizeOfSpareBlock;
Status = FtwLiteDevice->FtwFvBlock->Read (
FtwLiteDevice->FtwBackupFvb,
FtwLiteDevice->FtwSpareLba + Index,
0,
&Length,
Buffer
);
if (EFI_ERROR (Status)) {
break;
}
Buffer += Length;
}
} else {
Length = FtwLiteDevice->SizeOfSpareBlock * FtwLiteDevice->NumberOfSpareBlock;
CopyMem (
Buffer,
(UINT8 *) (UINTN) FtwLiteDevice->SpareAreaAddress,
Length
);
}
return Status;
}
/**
This function uses to allocate runtimeservicesdata memory in protected mode before before exit
boot services event.
@param Size The size of buffer to allocate.
@retval NULL Buffer unsuccessfully allocated.
@return Buffer Successfully allocated.
**/
VOID *
AllocateZeroRuntimeDataBuffer (
IN UINTN Size
)
{
VOID *Buffer;
Buffer = NULL;
if (mSmst == NULL) {
Buffer = AllocateRuntimeZeroPool(Size);
}
return Buffer;
}
/**
According to system mode to allocate pool. Allocate pool from pre-allocate RuntimeServicesData in protect mode.
Allocate EfiRuntimeServicesData (actually this memory is located in the range of SMM RAM)
in SMM mode.
@param FtwLiteDevice Point to private data of FTW driver.
@param Size The size of buffer to allocate.
@retval NULL Allocate buffer failed.
@return EFI_INVALID_PARAMETER Start address of allocated buffer.
**/
VOID *
FtwAllocateZeroBuffer (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN UINTN Size
)
{
UINTN Index;
VOID *Buffer;
for (Index = 0; Index < MAX_MEMORY_NODE; Index++) {
if (FtwLiteDevice->ReclaimMemory[Index].BufferSize >= Size && !FtwLiteDevice->ReclaimMemory[Index].Used) {
FtwLiteDevice->ReclaimMemory[Index].Used = TRUE;
Buffer = mSmst == NULL ? FtwLiteDevice->ReclaimMemory[Index].Buffer : FtwLiteDevice->ReclaimMemory[Index].PhysicalBuffer;
ZeroMem (Buffer, Size);
return Buffer;
}
}
return NULL;
}
/**
This function uses to free memory which allocated by FtwAllocateZeroBuffer ()
@param FtwLiteDevice Point to private data of FTW driver.
@param Buffer [in]:Double pointer saved the pointer of buffer address which want to free
[out]:Double pointer Saved NULL pointer to indicate allocated memory is freed.
@retval EFI_SUCCESS Memory return to system
@return EFI_INVALID_PARAMETER Buffer was invalid.
**/
EFI_STATUS
FtwFreePool (
IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
IN OUT VOID **Buffer
)
{
UINTN Index;
VOID *AllocatedBuffer;
if (Buffer == NULL || *Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
for (Index = 0; Index < MAX_MEMORY_NODE; Index++) {
if (FtwLiteDevice->ReclaimMemory[Index].Used) {
AllocatedBuffer = mSmst == NULL ? FtwLiteDevice->ReclaimMemory[Index].Buffer : FtwLiteDevice->ReclaimMemory[Index].PhysicalBuffer;
if (*Buffer == AllocatedBuffer) {
FtwLiteDevice->ReclaimMemory[Index].Used = FALSE;
*Buffer = NULL;
return EFI_SUCCESS;
}
}
}
return EFI_INVALID_PARAMETER;
}
/**
Check this input handle is whether a NV store FVB handle.
@param[in] Handle Input EFI_HANDLE instance
@retval TRUE This is NV storage FVB handle.
@retval FALSE This isn't NV storage FVB handle.
**/
BOOLEAN
IsNvStorageHandle (
EFI_HANDLE Handle
)
{
EFI_STATUS Status;
Status = gBS->OpenProtocol (
Handle,
&gEfiFirmwareVolumeBlockProtocolGuid,
NULL,
NULL,
NULL,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
return FALSE;
}
Status = gBS->OpenProtocol (
Handle,
&gEfiAlternateFvBlockGuid,
NULL,
NULL,
NULL,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
return (BOOLEAN) (Status == EFI_SUCCESS);
}