| /** @file | |
| Handles non-volatile variable store garbage collection, using FTW | |
| (Fault Tolerant Write) protocol. | |
| 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 "Variable.h" | |
| /** | |
| Gets firmware volume block handle by given address. | |
| This function gets firmware volume block handle whose | |
| address range contains the parameter Address. | |
| @param Address Address which should be contained | |
| by returned FVB handle | |
| @param FvbHandle Pointer to FVB handle for output | |
| @retval EFI_SUCCESS FVB handle successfully returned | |
| @retval EFI_NOT_FOUND Fail to find FVB handle by address | |
| **/ | |
| EFI_STATUS | |
| GetFvbHandleByAddress ( | |
| IN EFI_PHYSICAL_ADDRESS Address, | |
| OUT EFI_HANDLE *FvbHandle | |
| ) | |
| { | |
| 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; | |
| EFI_FVB_ATTRIBUTES_2 Attributes; | |
| *FvbHandle = NULL; | |
| // | |
| // Locate all handles of Fvb protocol | |
| // | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolumeBlockProtocolGuid, | |
| NULL, | |
| &HandleCount, | |
| &HandleBuffer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Get the FVB to access variable store | |
| // | |
| for (Index = 0; Index < HandleCount; Index += 1) { | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiFirmwareVolumeBlockProtocolGuid, | |
| (VOID **) &Fvb | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_NOT_FOUND; | |
| break; | |
| } | |
| Status = Fvb->GetAttributes (Fvb, &Attributes); | |
| if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) { | |
| continue; | |
| } | |
| // | |
| // 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))) { | |
| *FvbHandle = HandleBuffer[Index]; | |
| Status = EFI_SUCCESS; | |
| break; | |
| } | |
| } | |
| FreePool (HandleBuffer); | |
| return Status; | |
| } | |
| /** | |
| Gets LBA of block and offset by given address. | |
| This function gets the Logical Block Address (LBA) of firmware | |
| volume block containing the given address, and the offset of | |
| address on the block. | |
| @param Address Address which should be contained | |
| by returned FVB handle | |
| @param Lba Pointer to LBA for output | |
| @param Offset Pointer to offset for output | |
| @retval EFI_SUCCESS LBA and offset successfully returned | |
| @retval EFI_NOT_FOUND Fail to find FVB handle by address | |
| @retval EFI_ABORTED Fail to find valid LBA and offset | |
| **/ | |
| EFI_STATUS | |
| GetLbaAndOffsetByAddress ( | |
| IN EFI_PHYSICAL_ADDRESS Address, | |
| OUT EFI_LBA *Lba, | |
| OUT UINTN *Offset | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE FvbHandle; | |
| EFI_PHYSICAL_ADDRESS FvbBaseAddress; | |
| EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; | |
| EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; | |
| EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry; | |
| UINT32 LbaIndex; | |
| *Lba = (EFI_LBA) (-1); | |
| *Offset = 0; | |
| // | |
| // Get the proper FVB | |
| // | |
| Status = GetFvbHandleByAddress (Address, &FvbHandle); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->HandleProtocol ( | |
| FvbHandle, | |
| &gEfiFirmwareVolumeBlockProtocolGuid, | |
| (VOID **) &Fvb | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Get the Base Address of FV | |
| // | |
| Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress); | |
| // | |
| // Get the (LBA, Offset) of Address | |
| // | |
| if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + FwVolHeader->FvLength))) { | |
| if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { | |
| // | |
| // BUGBUG: Assume one FV has one type of BlockLength | |
| // | |
| FvbMapEntry = &FwVolHeader->BlockMap[0]; | |
| for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { | |
| if (Address < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex)) { | |
| // | |
| // Found the (Lba, Offset) | |
| // | |
| *Lba = LbaIndex - 1; | |
| *Offset = (UINTN) (Address - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| } | |
| return EFI_ABORTED; | |
| } | |
| /** | |
| Writes a buffer to variable storage space, in the working block. | |
| This function writes a buffer to variable storage space into firmware | |
| volume block device. The destination is specified by parameter | |
| VariableBase. Fault Tolerant Write protocol is used for writing. | |
| @param VariableBase Base address of variable to write | |
| @param Buffer Point to the data buffer | |
| @param BufferSize The number of bytes of the data Buffer | |
| @retval EFI_SUCCESS The function completed successfully | |
| @retval EFI_NOT_FOUND Fail to locate Fault Tolerant Write protocol | |
| @retval EFI_ABORTED The function could not complete successfully | |
| **/ | |
| EFI_STATUS | |
| FtwVariableSpace ( | |
| IN EFI_PHYSICAL_ADDRESS VariableBase, | |
| IN UINT8 *Buffer, | |
| IN UINTN BufferSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE FvbHandle; | |
| EFI_LBA VarLba; | |
| UINTN VarOffset; | |
| UINT8 *FtwBuffer; | |
| UINTN FtwBufferSize; | |
| EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; | |
| // | |
| // Locate fault tolerant write protocol | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEfiFaultTolerantWriteProtocolGuid, | |
| NULL, | |
| (VOID **) &FtwProtocol | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Locate Fvb handle by address | |
| // | |
| Status = GetFvbHandleByAddress (VariableBase, &FvbHandle); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Get LBA and Offset by address | |
| // | |
| Status = GetLbaAndOffsetByAddress (VariableBase, &VarLba, &VarOffset); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Prepare for the variable data | |
| // | |
| FtwBufferSize = ((VARIABLE_STORE_HEADER *) ((UINTN) VariableBase))->Size; | |
| FtwBuffer = AllocateRuntimePool (FtwBufferSize); | |
| if (FtwBuffer == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| SetMem (FtwBuffer, FtwBufferSize, (UINT8) 0xff); | |
| CopyMem (FtwBuffer, Buffer, BufferSize); | |
| // | |
| // FTW write record | |
| // | |
| Status = FtwProtocol->Write ( | |
| FtwProtocol, | |
| VarLba, // LBA | |
| VarOffset, // Offset | |
| FtwBufferSize, // NumBytes | |
| NULL, // PrivateData NULL | |
| FvbHandle, // Fvb Handle | |
| FtwBuffer // write buffer | |
| ); | |
| FreePool (FtwBuffer); | |
| return Status; | |
| } |