| /** @file | |
| Implements functions to read firmware file. | |
| Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> | |
| 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 "FwVolDriver.h" | |
| /** | |
| Required Alignment Alignment Value in FFS Alignment Value in | |
| (bytes) Attributes Field Firmware Volume Interfaces | |
| 1 0 0 | |
| 16 1 4 | |
| 128 2 7 | |
| 512 3 9 | |
| 1 KB 4 10 | |
| 4 KB 5 12 | |
| 32 KB 6 15 | |
| 64 KB 7 16 | |
| **/ | |
| UINT8 mFvAttributes[] = {0, 4, 7, 9, 10, 12, 15, 16}; | |
| /** | |
| Convert the FFS File Attributes to FV File Attributes. | |
| @param FfsAttributes The attributes of UINT8 type. | |
| @return The attributes of EFI_FV_FILE_ATTRIBUTES | |
| **/ | |
| EFI_FV_FILE_ATTRIBUTES | |
| FfsAttributes2FvFileAttributes ( | |
| IN EFI_FFS_FILE_ATTRIBUTES FfsAttributes | |
| ) | |
| { | |
| UINT8 DataAlignment; | |
| EFI_FV_FILE_ATTRIBUTES FileAttribute; | |
| DataAlignment = (UINT8) ((FfsAttributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3); | |
| ASSERT (DataAlignment < 8); | |
| FileAttribute = (EFI_FV_FILE_ATTRIBUTES) mFvAttributes[DataAlignment]; | |
| if ((FfsAttributes & FFS_ATTRIB_FIXED) == FFS_ATTRIB_FIXED) { | |
| FileAttribute |= EFI_FV_FILE_ATTRIB_FIXED; | |
| } | |
| return FileAttribute; | |
| } | |
| /** | |
| Given the input key, search for the next matching file in the volume. | |
| @param This Indicates the calling context. | |
| @param Key Key is a pointer to a caller allocated | |
| buffer that contains implementation specific | |
| data that is used to track where to begin | |
| the search for the next file. The size of | |
| the buffer must be at least This->KeySize | |
| bytes long. To reinitialize the search and | |
| begin from the beginning of the firmware | |
| volume, the entire buffer must be cleared to | |
| zero. Other than clearing the buffer to | |
| initiate a new search, the caller must not | |
| modify the data in the buffer between calls | |
| to GetNextFile(). | |
| @param FileType FileType is a pointer to a caller allocated | |
| EFI_FV_FILETYPE. The GetNextFile() API can | |
| filter it's search for files based on the | |
| value of *FileType input. A *FileType input | |
| of 0 causes GetNextFile() to search for | |
| files of all types. If a file is found, the | |
| file's type is returned in *FileType. | |
| *FileType is not modified if no file is | |
| found. | |
| @param NameGuid NameGuid is a pointer to a caller allocated | |
| EFI_GUID. If a file is found, the file's | |
| name is returned in *NameGuid. *NameGuid is | |
| not modified if no file is found. | |
| @param Attributes Attributes is a pointer to a caller | |
| allocated EFI_FV_FILE_ATTRIBUTES. If a file | |
| is found, the file's attributes are returned | |
| in *Attributes. *Attributes is not modified | |
| if no file is found. | |
| @param Size Size is a pointer to a caller allocated | |
| UINTN. If a file is found, the file's size | |
| is returned in *Size. *Size is not modified | |
| if no file is found. | |
| @retval EFI_SUCCESS Successfully find the file. | |
| @retval EFI_DEVICE_ERROR Device error. | |
| @retval EFI_ACCESS_DENIED Fv could not read. | |
| @retval EFI_NOT_FOUND No matching file found. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FvGetNextFile ( | |
| IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL *This, | |
| IN OUT VOID *Key, | |
| IN OUT EFI_FV_FILETYPE *FileType, | |
| OUT EFI_GUID *NameGuid, | |
| OUT EFI_FV_FILE_ATTRIBUTES *Attributes, | |
| OUT UINTN *Size | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FV_DEVICE *FvDevice; | |
| EFI_FV_ATTRIBUTES FvAttributes; | |
| EFI_FFS_FILE_HEADER *FfsFileHeader; | |
| UINTN *KeyValue; | |
| LIST_ENTRY *Link; | |
| FFS_FILE_LIST_ENTRY *FfsFileEntry; | |
| FvDevice = FV_DEVICE_FROM_THIS (This); | |
| Status = This->GetVolumeAttributes (This, &FvAttributes); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| KeyValue = (UINTN *) Key; | |
| FfsFileHeader = NULL; | |
| // | |
| // Check if read operation is enabled | |
| // | |
| if ((FvAttributes & EFI_FV2_READ_STATUS) == 0) { | |
| return EFI_ACCESS_DENIED; | |
| } | |
| if (*FileType > EFI_FV_FILETYPE_SMM_CORE) { | |
| // | |
| // File type needs to be in 0 - 0x0D | |
| // | |
| return EFI_NOT_FOUND; | |
| } | |
| do { | |
| if (*KeyValue == 0) { | |
| // | |
| // Search for 1st matching file | |
| // | |
| Link = &FvDevice->FfsFileListHeader; | |
| if (Link->ForwardLink == &FvDevice->FfsFileListHeader) { | |
| return EFI_NOT_FOUND; | |
| } | |
| FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Link->ForwardLink; | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader; | |
| // | |
| // remember the key | |
| // | |
| *KeyValue = (UINTN) FfsFileEntry; | |
| // | |
| // we ignore pad files | |
| // | |
| if (FfsFileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) { | |
| continue; | |
| } | |
| if (*FileType == 0) { | |
| break; | |
| } | |
| if (*FileType == FfsFileHeader->Type) { | |
| break; | |
| } | |
| } else { | |
| // | |
| // Getting link from last Ffs | |
| // | |
| Link = (LIST_ENTRY *) (*KeyValue); | |
| if (Link->ForwardLink == &FvDevice->FfsFileListHeader) { | |
| return EFI_NOT_FOUND; | |
| } | |
| FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Link->ForwardLink; | |
| FfsFileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader; | |
| // | |
| // remember the key | |
| // | |
| *KeyValue = (UINTN) FfsFileEntry; | |
| // | |
| // we ignore pad files | |
| // | |
| if (FfsFileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) { | |
| continue; | |
| } | |
| if (*FileType == EFI_FV_FILETYPE_ALL) { | |
| break; | |
| } | |
| if (*FileType == FfsFileHeader->Type) { | |
| break; | |
| } | |
| } | |
| } while (Link->ForwardLink != &FvDevice->FfsFileListHeader); | |
| // | |
| // Cache this file entry | |
| // | |
| FvDevice->CurrentFfsFile = FfsFileEntry; | |
| *FileType = FfsFileHeader->Type; | |
| CopyGuid (NameGuid, &FfsFileHeader->Name); | |
| *Attributes = FfsAttributes2FvFileAttributes (FfsFileHeader->Attributes); | |
| if ((FvDevice->FwVolHeader->Attributes & EFI_FVB2_MEMORY_MAPPED) == EFI_FVB2_MEMORY_MAPPED) { | |
| *Attributes |= EFI_FV_FILE_ATTRIB_MEMORY_MAPPED; | |
| } | |
| // | |
| // we need to substract the header size | |
| // | |
| if (IS_FFS_FILE2 (FfsFileHeader)) { | |
| *Size = FFS_FILE2_SIZE (FfsFileHeader) - sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| *Size = FFS_FILE_SIZE (FfsFileHeader) - sizeof (EFI_FFS_FILE_HEADER); | |
| } | |
| if (CompareGuid (&gEfiFirmwareVolumeTopFileGuid, NameGuid)) { | |
| // | |
| // specially deal with VTF file | |
| // | |
| UINT8 *SrcPtr; | |
| UINT32 Tmp; | |
| if (IS_FFS_FILE2 (FfsFileHeader)) { | |
| SrcPtr = ((UINT8 *) FfsFileHeader) + sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| SrcPtr = ((UINT8 *) FfsFileHeader) + sizeof (EFI_FFS_FILE_HEADER); | |
| } | |
| while (*Size >= 4) { | |
| Tmp = *(UINT32 *) SrcPtr; | |
| if (Tmp == 0) { | |
| SrcPtr += 4; | |
| (*Size) -= 4; | |
| } else { | |
| break; | |
| } | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Locates a file in the firmware volume and | |
| copies it to the supplied buffer. | |
| @param This Indicates the calling context. | |
| @param NameGuid Pointer to an EFI_GUID, which is the | |
| filename. | |
| @param Buffer Buffer is a pointer to pointer to a buffer | |
| in which the file or section contents or are | |
| returned. | |
| @param BufferSize BufferSize is a pointer to caller allocated | |
| UINTN. On input *BufferSize indicates the | |
| size in bytes of the memory region pointed | |
| to by Buffer. On output, *BufferSize | |
| contains the number of bytes required to | |
| read the file. | |
| @param FoundType FoundType is a pointer to a caller allocated | |
| EFI_FV_FILETYPE that on successful return | |
| from Read() contains the type of file read. | |
| This output reflects the file type | |
| irrespective of the value of the SectionType | |
| input. | |
| @param FileAttributes FileAttributes is a pointer to a caller | |
| allocated EFI_FV_FILE_ATTRIBUTES. On | |
| successful return from Read(), | |
| *FileAttributes contains the attributes of | |
| the file read. | |
| @param AuthenticationStatus AuthenticationStatus is a pointer to a | |
| caller allocated UINTN in which the | |
| authentication status is returned. | |
| @retval EFI_SUCCESS Successfully read to memory buffer. | |
| @retval EFI_WARN_BUFFER_TOO_SMALL Buffer too small. | |
| @retval EFI_NOT_FOUND Not found. | |
| @retval EFI_DEVICE_ERROR Device error. | |
| @retval EFI_ACCESS_DENIED Could not read. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| @retval EFI_OUT_OF_RESOURCES Not enough buffer to be allocated. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FvReadFile ( | |
| IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL *This, | |
| IN CONST EFI_GUID *NameGuid, | |
| IN OUT VOID **Buffer, | |
| IN OUT UINTN *BufferSize, | |
| OUT EFI_FV_FILETYPE *FoundType, | |
| OUT EFI_FV_FILE_ATTRIBUTES *FileAttributes, | |
| OUT UINT32 *AuthenticationStatus | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FV_DEVICE *FvDevice; | |
| UINTN Key; | |
| EFI_GUID SearchNameGuid; | |
| EFI_FV_ATTRIBUTES FvAttributes; | |
| EFI_FV_FILETYPE LocalFoundType; | |
| EFI_FV_FILE_ATTRIBUTES LocalAttributes; | |
| UINTN FileSize; | |
| UINT8 *SrcPtr; | |
| FFS_FILE_LIST_ENTRY *FfsFileEntry; | |
| EFI_FFS_FILE_HEADER *FfsHeader; | |
| UINT8 *FileBuffer; | |
| if (NULL == This || NULL == NameGuid) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| FvDevice = FV_DEVICE_FROM_THIS (This); | |
| Status = This->GetVolumeAttributes (This, &FvAttributes); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // First check to see that FV is enabled for reads... | |
| // | |
| if (0 == (FvAttributes & EFI_FV2_READ_STATUS)) { | |
| return EFI_ACCESS_DENIED; | |
| } | |
| FfsHeader = NULL; | |
| // | |
| // Check if the file was read last time. | |
| // | |
| FfsFileEntry = FvDevice->CurrentFfsFile; | |
| if (FfsFileEntry != NULL) { | |
| FfsHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader; | |
| } | |
| if ((FfsFileEntry == NULL) || (!CompareGuid (&FfsHeader->Name, NameGuid))) { | |
| // | |
| // If not match or no file cached, search this file | |
| // | |
| Key = 0; | |
| do { | |
| LocalFoundType = 0; | |
| Status = This->GetNextFile ( | |
| This, | |
| &Key, | |
| &LocalFoundType, | |
| &SearchNameGuid, | |
| &LocalAttributes, | |
| &FileSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| } while (!CompareGuid (&SearchNameGuid, NameGuid)); | |
| // | |
| // Get file entry | |
| // | |
| FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key; | |
| // | |
| // Update the cache | |
| // | |
| FvDevice->CurrentFfsFile = FfsFileEntry; | |
| FfsHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader; | |
| } else { | |
| // | |
| // Get File Size of the cached file | |
| // | |
| if (IS_FFS_FILE2 (FfsHeader)) { | |
| FileSize = FFS_FILE2_SIZE (FfsHeader) - sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| FileSize = FFS_FILE_SIZE (FfsHeader) - sizeof (EFI_FFS_FILE_HEADER); | |
| } | |
| } | |
| // | |
| // Get file info | |
| // | |
| *FoundType = FfsHeader->Type; | |
| *FileAttributes = FfsAttributes2FvFileAttributes (FfsHeader->Attributes); | |
| if ((FvDevice->FwVolHeader->Attributes & EFI_FVB2_MEMORY_MAPPED) == EFI_FVB2_MEMORY_MAPPED) { | |
| *FileAttributes |= EFI_FV_FILE_ATTRIB_MEMORY_MAPPED; | |
| } | |
| // | |
| // Inherit the authentication status. | |
| // | |
| *AuthenticationStatus = FvDevice->AuthenticationStatus; | |
| // | |
| // If Buffer is NULL, we only want to get some information | |
| // | |
| if (Buffer == NULL) { | |
| *BufferSize = FileSize; | |
| return EFI_SUCCESS; | |
| } | |
| if (IS_FFS_FILE2 (FfsHeader)) { | |
| SrcPtr = ((UINT8 *) FfsHeader) + sizeof (EFI_FFS_FILE_HEADER2); | |
| } else { | |
| SrcPtr = ((UINT8 *) FfsHeader) + sizeof (EFI_FFS_FILE_HEADER); | |
| } | |
| if (CompareGuid (&gEfiFirmwareVolumeTopFileGuid, NameGuid)) { | |
| // | |
| // specially deal with VTF file | |
| // | |
| UINT32 Tmp; | |
| while (FileSize >= 4) { | |
| Tmp = *(UINT32 *) SrcPtr; | |
| if (Tmp == 0) { | |
| SrcPtr += 4; | |
| FileSize -= 4; | |
| } else { | |
| break; | |
| } | |
| } | |
| } | |
| // | |
| // If we drop out of the above loop, we've found the correct file header... | |
| // | |
| if (*Buffer == NULL) { | |
| FileBuffer = AllocateCopyPool (FileSize, SrcPtr); | |
| if (FileBuffer == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| *BufferSize = FileSize; | |
| *Buffer = FileBuffer; | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // If the user's buffer is smaller than the file size, then copy as much | |
| // as we can and return an appropriate status. | |
| // | |
| if (FileSize > *BufferSize) { | |
| CopyMem (*Buffer, SrcPtr, *BufferSize); | |
| *BufferSize = FileSize; | |
| return EFI_WARN_BUFFER_TOO_SMALL; | |
| } | |
| // | |
| // User's buffer size is ok, so copy the entire file to their buffer. | |
| // | |
| *BufferSize = FileSize; | |
| CopyMem (*Buffer, SrcPtr, *BufferSize); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Locates a section in a given FFS File and | |
| copies it to the supplied buffer (not including section header). | |
| @param This Indicates the calling context. | |
| @param NameGuid Pointer to an EFI_GUID, which is the | |
| filename. | |
| @param SectionType Indicates the section type to return. | |
| @param SectionInstance Indicates which instance of sections with a | |
| type of SectionType to return. | |
| @param Buffer Buffer is a pointer to pointer to a buffer | |
| in which the file or section contents or are | |
| returned. | |
| @param BufferSize BufferSize is a pointer to caller allocated | |
| UINTN. | |
| @param AuthenticationStatus AuthenticationStatus is a pointer to a | |
| caller allocated UINT32 in which the | |
| authentication status is returned. | |
| @retval EFI_SUCCESS Successfully read the file section into | |
| buffer. | |
| @retval EFI_WARN_BUFFER_TOO_SMALL Buffer too small. | |
| @retval EFI_NOT_FOUND Section not found. | |
| @retval EFI_DEVICE_ERROR Device error. | |
| @retval EFI_ACCESS_DENIED Could not read. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FvReadFileSection ( | |
| IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL *This, | |
| IN CONST EFI_GUID *NameGuid, | |
| IN EFI_SECTION_TYPE SectionType, | |
| IN UINTN SectionInstance, | |
| IN OUT VOID **Buffer, | |
| IN OUT UINTN *BufferSize, | |
| OUT UINT32 *AuthenticationStatus | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FV_DEVICE *FvDevice; | |
| EFI_FV_ATTRIBUTES FvAttributes; | |
| EFI_FV_FILETYPE FileType; | |
| EFI_FV_FILE_ATTRIBUTES FileAttributes; | |
| UINTN FileSize; | |
| UINT8 *FileBuffer; | |
| EFI_SECTION_EXTRACTION_PROTOCOL *Sep; | |
| UINTN StreamHandle; | |
| if (NULL == This || NULL == NameGuid || Buffer == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| FvDevice = FV_DEVICE_FROM_THIS (This); | |
| Status = This->GetVolumeAttributes (This, &FvAttributes); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // First check to see that FV is enabled for reads... | |
| // | |
| if (0 == (FvAttributes & EFI_FV2_READ_STATUS)) { | |
| return EFI_ACCESS_DENIED; | |
| } | |
| // | |
| // Read the whole file into buffer | |
| // | |
| FileBuffer = NULL; | |
| Status = This->ReadFile ( | |
| This, | |
| NameGuid, | |
| (VOID **) &FileBuffer, | |
| &FileSize, | |
| &FileType, | |
| &FileAttributes, | |
| AuthenticationStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Check to see that the file actually HAS sections before we go any further. | |
| // | |
| if (FileType == EFI_FV_FILETYPE_RAW) { | |
| FreePool (FileBuffer); | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Located the protocol | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEfiSectionExtractionProtocolGuid, | |
| NULL, | |
| (VOID **) &Sep | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (FileBuffer); | |
| return Status; | |
| } | |
| Status = Sep->OpenSectionStream ( | |
| Sep, | |
| FileSize, | |
| FileBuffer, | |
| &StreamHandle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (FileBuffer); | |
| return Status; | |
| } | |
| if (SectionType == 0) { | |
| // | |
| // We need the whole section stream | |
| // | |
| Status = Sep->GetSection ( | |
| Sep, | |
| StreamHandle, | |
| NULL, | |
| NULL, | |
| 0, | |
| Buffer, | |
| BufferSize, | |
| AuthenticationStatus | |
| ); | |
| } else { | |
| Status = Sep->GetSection ( | |
| Sep, | |
| StreamHandle, | |
| &SectionType, | |
| NULL, | |
| SectionInstance, | |
| Buffer, | |
| BufferSize, | |
| AuthenticationStatus | |
| ); | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Inherit the authentication status. | |
| // | |
| *AuthenticationStatus |= FvDevice->AuthenticationStatus; | |
| } | |
| // | |
| // Handle AuthenticationStatus if necessary | |
| // | |
| Sep->CloseSectionStream (Sep, StreamHandle); | |
| FreePool (FileBuffer); | |
| return Status; | |
| } |