| /*++ | |
| Copyright (c) 2004, 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. | |
| Module Name: | |
| FvLib.c | |
| Abstract: | |
| These functions assist in parsing and manipulating a Firmware Volume. | |
| --*/ | |
| // | |
| // Include files | |
| // | |
| #include "FvLib.h" | |
| #include "CommonLib.h" | |
| #include "EfiUtilityMsgs.h" | |
| // | |
| // Module global variables | |
| // | |
| EFI_FIRMWARE_VOLUME_HEADER *mFvHeader = NULL; | |
| UINT32 mFvLength = 0; | |
| // | |
| // External function implementations | |
| // | |
| EFI_STATUS | |
| InitializeFvLib ( | |
| IN VOID *Fv, | |
| IN UINT32 FvLength | |
| ) | |
| /*++ | |
| Routine Description: | |
| This initializes the FV lib with a pointer to the FV and length. It does not | |
| verify the FV in any way. | |
| Arguments: | |
| Fv Buffer containing the FV. | |
| FvLength Length of the FV | |
| Returns: | |
| EFI_SUCCESS Function Completed successfully. | |
| EFI_INVALID_PARAMETER A required parameter was NULL. | |
| --*/ | |
| { | |
| // | |
| // Verify input arguments | |
| // | |
| if (Fv == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| mFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) Fv; | |
| mFvLength = FvLength; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetFvHeader ( | |
| OUT EFI_FIRMWARE_VOLUME_HEADER **FvHeader, | |
| OUT UINT32 *FvLength | |
| ) | |
| /*++ | |
| Routine Description: | |
| This function returns a pointer to the current FV and the size. | |
| Arguments: | |
| FvHeader Pointer to the FV buffer. | |
| FvLength Length of the FV | |
| Returns: | |
| EFI_SUCCESS Function Completed successfully. | |
| EFI_INVALID_PARAMETER A required parameter was NULL. | |
| EFI_ABORTED The library needs to be initialized. | |
| --*/ | |
| { | |
| // | |
| // Verify library has been initialized. | |
| // | |
| if (mFvHeader == NULL || mFvLength == 0) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify input arguments | |
| // | |
| if (FvHeader == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *FvHeader = mFvHeader; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetNextFile ( | |
| IN EFI_FFS_FILE_HEADER *CurrentFile, | |
| OUT EFI_FFS_FILE_HEADER **NextFile | |
| ) | |
| /*++ | |
| Routine Description: | |
| This function returns the next file. If the current file is NULL, it returns | |
| the first file in the FV. If the function returns EFI_SUCCESS and the file | |
| pointer is NULL, then there are no more files in the FV. | |
| Arguments: | |
| CurrentFile Pointer to the current file, must be within the current FV. | |
| NextFile Pointer to the next file in the FV. | |
| Returns: | |
| EFI_SUCCESS Function completed successfully. | |
| EFI_INVALID_PARAMETER A required parameter was NULL or is out of range. | |
| EFI_ABORTED The library needs to be initialized. | |
| --*/ | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Verify library has been initialized. | |
| // | |
| if (mFvHeader == NULL || mFvLength == 0) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify input arguments | |
| // | |
| if (NextFile == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Verify FV header | |
| // | |
| Status = VerifyFv (mFvHeader); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Get first file | |
| // | |
| if (CurrentFile == NULL) { | |
| CurrentFile = (EFI_FFS_FILE_HEADER *) ((UINTN) mFvHeader + mFvHeader->HeaderLength); | |
| // | |
| // Verify file is valid | |
| // | |
| Status = VerifyFfsFile (CurrentFile); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // no files in this FV | |
| // | |
| *NextFile = NULL; | |
| return EFI_SUCCESS; | |
| } else { | |
| // | |
| // Verify file is in this FV. | |
| // | |
| if ((UINTN) CurrentFile >= (UINTN) mFvHeader + mFvLength - sizeof (EFI_FFS_FILE_HEADER)) { | |
| *NextFile = NULL; | |
| return EFI_SUCCESS; | |
| } | |
| *NextFile = CurrentFile; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| // | |
| // Verify current file is in range | |
| // | |
| if (((UINTN) CurrentFile < (UINTN) mFvHeader + sizeof (EFI_FIRMWARE_VOLUME_HEADER)) || | |
| ((UINTN) CurrentFile >= (UINTN) mFvHeader + mFvLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER)) | |
| ) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Get next file, compensate for 8 byte alignment if necessary. | |
| // | |
| *NextFile = (EFI_FFS_FILE_HEADER *) (((UINTN) CurrentFile + GetLength (CurrentFile->Size) + 0x07) & (-1 << 3)); | |
| // | |
| // Verify file is in this FV. | |
| // | |
| if ((UINTN) *NextFile >= (UINTN) mFvHeader + mFvLength - sizeof (EFI_FFS_FILE_HEADER)) { | |
| *NextFile = NULL; | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Verify file is valid | |
| // | |
| Status = VerifyFfsFile (*NextFile); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // no more files in this FV | |
| // | |
| *NextFile = NULL; | |
| return EFI_SUCCESS; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetFileByName ( | |
| IN EFI_GUID *FileName, | |
| OUT EFI_FFS_FILE_HEADER **File | |
| ) | |
| /*++ | |
| Routine Description: | |
| Find a file by name. The function will return NULL if the file is not found. | |
| Arguments: | |
| FileName The GUID file name of the file to search for. | |
| File Return pointer. In the case of an error, contents are undefined. | |
| Returns: | |
| EFI_SUCCESS The function completed successfully. | |
| EFI_ABORTED An error was encountered. | |
| EFI_INVALID_PARAMETER One of the parameters was NULL. | |
| --*/ | |
| { | |
| EFI_FFS_FILE_HEADER *CurrentFile; | |
| EFI_STATUS Status; | |
| // | |
| // Verify library has been initialized. | |
| // | |
| if (mFvHeader == NULL || mFvLength == 0) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify input parameters | |
| // | |
| if (FileName == NULL || File == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Verify FV header | |
| // | |
| Status = VerifyFv (mFvHeader); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Get the first file | |
| // | |
| Status = GetNextFile (NULL, &CurrentFile); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 0, "error parsing the FV", NULL); | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Loop as long as we have a valid file | |
| // | |
| while (CurrentFile) { | |
| if (!CompareGuid (&CurrentFile->Name, FileName)) { | |
| *File = CurrentFile; | |
| return EFI_SUCCESS; | |
| } | |
| Status = GetNextFile (CurrentFile, &CurrentFile); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 0, "error parsing the FV", NULL); | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // File not found in this FV. | |
| // | |
| *File = NULL; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetFileByType ( | |
| IN EFI_FV_FILETYPE FileType, | |
| IN UINTN Instance, | |
| OUT EFI_FFS_FILE_HEADER **File | |
| ) | |
| /*++ | |
| Routine Description: | |
| Find a file by type and instance. An instance of 1 is the first instance. | |
| The function will return NULL if a matching file cannot be found. | |
| File type EFI_FV_FILETYPE_ALL means any file type is valid. | |
| Arguments: | |
| FileType Type of file to search for. | |
| Instance Instace of the file type to return. | |
| File Return pointer. In the case of an error, contents are undefined. | |
| Returns: | |
| EFI_SUCCESS The function completed successfully. | |
| EFI_ABORTED An error was encountered. | |
| EFI_INVALID_PARAMETER One of the parameters was NULL. | |
| --*/ | |
| { | |
| EFI_FFS_FILE_HEADER *CurrentFile; | |
| EFI_STATUS Status; | |
| UINTN FileCount; | |
| // | |
| // Verify library has been initialized. | |
| // | |
| if (mFvHeader == NULL || mFvLength == 0) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify input parameters | |
| // | |
| if (File == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Verify FV header | |
| // | |
| Status = VerifyFv (mFvHeader); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Initialize the number of matching files found. | |
| // | |
| FileCount = 0; | |
| // | |
| // Get the first file | |
| // | |
| Status = GetNextFile (NULL, &CurrentFile); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 0, "error parsing FV", NULL); | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Loop as long as we have a valid file | |
| // | |
| while (CurrentFile) { | |
| if (FileType == EFI_FV_FILETYPE_ALL || CurrentFile->Type == FileType) { | |
| FileCount++; | |
| } | |
| if (FileCount == Instance) { | |
| *File = CurrentFile; | |
| return EFI_SUCCESS; | |
| } | |
| Status = GetNextFile (CurrentFile, &CurrentFile); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 0, "error parsing the FV", NULL); | |
| return EFI_ABORTED; | |
| } | |
| } | |
| *File = NULL; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetSectionByType ( | |
| IN EFI_FFS_FILE_HEADER *File, | |
| IN EFI_SECTION_TYPE SectionType, | |
| IN UINTN Instance, | |
| OUT EFI_FILE_SECTION_POINTER *Section | |
| ) | |
| /*++ | |
| Routine Description: | |
| Find a section in a file by type and instance. An instance of 1 is the first | |
| instance. The function will return NULL if a matching section cannot be found. | |
| The function will not handle encapsulating sections. | |
| Arguments: | |
| File The file to search. | |
| SectionType Type of file to search for. | |
| Instance Instace of the section to return. | |
| Section Return pointer. In the case of an error, contents are undefined. | |
| Returns: | |
| EFI_SUCCESS The function completed successfully. | |
| EFI_ABORTED An error was encountered. | |
| EFI_INVALID_PARAMETER One of the parameters was NULL. | |
| EFI_NOT_FOUND No found. | |
| --*/ | |
| { | |
| EFI_FILE_SECTION_POINTER CurrentSection; | |
| EFI_STATUS Status; | |
| UINTN SectionCount; | |
| // | |
| // Verify input parameters | |
| // | |
| if (File == NULL || Instance == 0) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Verify FFS header | |
| // | |
| Status = VerifyFfsFile (File); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 0, "invalid FFS file", NULL); | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Initialize the number of matching sections found. | |
| // | |
| SectionCount = 0; | |
| // | |
| // Get the first section | |
| // | |
| CurrentSection.CommonHeader = (EFI_COMMON_SECTION_HEADER *) ((UINTN) File + sizeof (EFI_FFS_FILE_HEADER)); | |
| // | |
| // Loop as long as we have a valid file | |
| // | |
| while ((UINTN) CurrentSection.CommonHeader < (UINTN) File + GetLength (File->Size)) { | |
| if (CurrentSection.CommonHeader->Type == SectionType) { | |
| SectionCount++; | |
| } | |
| if (SectionCount == Instance) { | |
| *Section = CurrentSection; | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Find next section (including compensating for alignment issues. | |
| // | |
| CurrentSection.CommonHeader = (EFI_COMMON_SECTION_HEADER *) ((((UINTN) CurrentSection.CommonHeader) + GetLength (CurrentSection.CommonHeader->Size) + 0x03) & (-1 << 2)); | |
| } | |
| // | |
| // Section not found | |
| // | |
| (*Section).Code16Section = NULL; | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // will not parse compressed sections | |
| // | |
| EFI_STATUS | |
| VerifyFv ( | |
| IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader | |
| ) | |
| /*++ | |
| Routine Description: | |
| Verify the current pointer points to a valid FV header. | |
| Arguments: | |
| FvHeader Pointer to an alleged FV file. | |
| Returns: | |
| EFI_SUCCESS The FV header is valid. | |
| EFI_VOLUME_CORRUPTED The FV header is not valid. | |
| EFI_INVALID_PARAMETER A required parameter was NULL. | |
| EFI_ABORTED Operation aborted. | |
| --*/ | |
| { | |
| UINT16 Checksum; | |
| // | |
| // Verify input parameters | |
| // | |
| if (FvHeader == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (FvHeader->Signature != EFI_FVH_SIGNATURE) { | |
| Error (NULL, 0, 0, "invalid FV header signature", NULL); | |
| return EFI_VOLUME_CORRUPTED; | |
| } | |
| // | |
| // Verify header checksum | |
| // | |
| Checksum = CalculateSum16 ((UINT16 *) FvHeader, FvHeader->HeaderLength / sizeof (UINT16)); | |
| if (Checksum != 0) { | |
| Error (NULL, 0, 0, "invalid FV header checksum", NULL); | |
| return EFI_ABORTED; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| VerifyFfsFile ( | |
| IN EFI_FFS_FILE_HEADER *FfsHeader | |
| ) | |
| /*++ | |
| Routine Description: | |
| Verify the current pointer points to a FFS file header. | |
| Arguments: | |
| FfsHeader Pointer to an alleged FFS file. | |
| Returns: | |
| EFI_SUCCESS The Ffs header is valid. | |
| EFI_NOT_FOUND This "file" is the beginning of free space. | |
| EFI_VOLUME_CORRUPTED The Ffs header is not valid. | |
| EFI_ABORTED The erase polarity is not known. | |
| --*/ | |
| { | |
| BOOLEAN ErasePolarity; | |
| EFI_STATUS Status; | |
| EFI_FFS_FILE_HEADER BlankHeader; | |
| UINT8 Checksum; | |
| UINT32 FileLength; | |
| UINT32 OccupiedFileLength; | |
| EFI_FFS_FILE_TAIL *Tail; | |
| UINT8 SavedChecksum; | |
| UINT8 SavedState; | |
| UINT8 FileGuidString[80]; | |
| UINT32 TailSize; | |
| // | |
| // Verify library has been initialized. | |
| // | |
| if (mFvHeader == NULL || mFvLength == 0) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify FV header | |
| // | |
| Status = VerifyFv (mFvHeader); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Get the erase polarity. | |
| // | |
| Status = GetErasePolarity (&ErasePolarity); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Check if we have free space | |
| // | |
| if (ErasePolarity) { | |
| memset (&BlankHeader, -1, sizeof (EFI_FFS_FILE_HEADER)); | |
| } else { | |
| memset (&BlankHeader, 0, sizeof (EFI_FFS_FILE_HEADER)); | |
| } | |
| if (memcmp (&BlankHeader, FfsHeader, sizeof (EFI_FFS_FILE_HEADER)) == 0) { | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Convert the GUID to a string so we can at least report which file | |
| // if we find an error. | |
| // | |
| PrintGuidToBuffer (&FfsHeader->Name, FileGuidString, sizeof (FileGuidString), TRUE); | |
| if (FfsHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { | |
| TailSize = sizeof (EFI_FFS_FILE_TAIL); | |
| } else { | |
| TailSize = 0; | |
| } | |
| // | |
| // Verify file header checksum | |
| // | |
| SavedState = FfsHeader->State; | |
| FfsHeader->State = 0; | |
| SavedChecksum = FfsHeader->IntegrityCheck.Checksum.File; | |
| FfsHeader->IntegrityCheck.Checksum.File = 0; | |
| Checksum = CalculateSum8 ((UINT8 *) FfsHeader, sizeof (EFI_FFS_FILE_HEADER)); | |
| FfsHeader->State = SavedState; | |
| FfsHeader->IntegrityCheck.Checksum.File = SavedChecksum; | |
| if (Checksum != 0) { | |
| Error (NULL, 0, 0, FileGuidString, "invalid FFS file header checksum"); | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify file checksum | |
| // | |
| if (FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) { | |
| // | |
| // Verify file data checksum | |
| // | |
| FileLength = GetLength (FfsHeader->Size); | |
| OccupiedFileLength = (FileLength + 0x07) & (-1 << 3); | |
| Checksum = CalculateSum8 ((UINT8 *) FfsHeader, FileLength - TailSize); | |
| Checksum = (UINT8) (Checksum - FfsHeader->State); | |
| if (Checksum != 0) { | |
| Error (NULL, 0, 0, FileGuidString, "invalid FFS file checksum"); | |
| return EFI_ABORTED; | |
| } | |
| } else { | |
| // | |
| // File does not have a checksum | |
| // Verify contents are 0x5A as spec'd | |
| // | |
| if (FfsHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) { | |
| Error (NULL, 0, 0, FileGuidString, "invalid fixed FFS file header checksum"); | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Check if the tail is present and verify it if it is. | |
| // | |
| if (FfsHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { | |
| // | |
| // Verify tail is complement of integrity check field in the header. | |
| // | |
| Tail = (EFI_FFS_FILE_TAIL *) ((UINTN) FfsHeader + GetLength (FfsHeader->Size) - sizeof (EFI_FFS_FILE_TAIL)); | |
| if (FfsHeader->IntegrityCheck.TailReference != (EFI_FFS_FILE_TAIL)~(*Tail)) { | |
| Error (NULL, 0, 0, FileGuidString, "invalid FFS file tail"); | |
| return EFI_ABORTED; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| UINT32 | |
| GetLength ( | |
| UINT8 *ThreeByteLength | |
| ) | |
| /*++ | |
| Routine Description: | |
| Converts a three byte length value into a UINT32. | |
| Arguments: | |
| ThreeByteLength Pointer to the first of the 3 byte length. | |
| Returns: | |
| UINT32 Size of the section | |
| --*/ | |
| { | |
| UINT32 Length; | |
| if (ThreeByteLength == NULL) { | |
| return 0; | |
| } | |
| Length = *((UINT32 *) ThreeByteLength); | |
| Length = Length & 0x00FFFFFF; | |
| return Length; | |
| } | |
| EFI_STATUS | |
| GetErasePolarity ( | |
| OUT BOOLEAN *ErasePolarity | |
| ) | |
| /*++ | |
| Routine Description: | |
| This function returns with the FV erase polarity. If the erase polarity | |
| for a bit is 1, the function return TRUE. | |
| Arguments: | |
| ErasePolarity A pointer to the erase polarity. | |
| Returns: | |
| EFI_SUCCESS The function completed successfully. | |
| EFI_INVALID_PARAMETER One of the input parameters was invalid. | |
| EFI_ABORTED Operation aborted. | |
| --*/ | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Verify library has been initialized. | |
| // | |
| if (mFvHeader == NULL || mFvLength == 0) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify FV header | |
| // | |
| Status = VerifyFv (mFvHeader); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_ABORTED; | |
| } | |
| // | |
| // Verify input parameters. | |
| // | |
| if (ErasePolarity == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (mFvHeader->Attributes & EFI_FVB_ERASE_POLARITY) { | |
| *ErasePolarity = TRUE; | |
| } else { | |
| *ErasePolarity = FALSE; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| UINT8 | |
| GetFileState ( | |
| IN BOOLEAN ErasePolarity, | |
| IN EFI_FFS_FILE_HEADER *FfsHeader | |
| ) | |
| /*++ | |
| Routine Description: | |
| This function returns a the highest state bit in the FFS that is set. | |
| It in no way validate the FFS file. | |
| Arguments: | |
| ErasePolarity The erase polarity for the file state bits. | |
| FfsHeader Pointer to a FFS file. | |
| Returns: | |
| UINT8 The hightest set state of the file. | |
| --*/ | |
| { | |
| UINT8 FileState; | |
| UINT8 HighestBit; | |
| FileState = FfsHeader->State; | |
| if (ErasePolarity) { | |
| FileState = (UINT8)~FileState; | |
| } | |
| HighestBit = 0x80; | |
| while (HighestBit != 0 && (HighestBit & FileState) == 0) { | |
| HighestBit >>= 1; | |
| } | |
| return HighestBit; | |
| } |