/** @file | |
Implements functions to read firmware file | |
Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "DxeMain.h" | |
#include "FwVolDriver.h" | |
/** | |
Required Alignment Alignment Value in FFS FFS_ATTRIB_DATA_ALIGNMENT2 Alignment Value in | |
(bytes) Attributes Field in FFS Attributes Field Firmware Volume Interfaces | |
1 0 0 0 | |
16 1 0 4 | |
128 2 0 7 | |
512 3 0 9 | |
1 KB 4 0 10 | |
4 KB 5 0 12 | |
32 KB 6 0 15 | |
64 KB 7 0 16 | |
128 KB 0 1 17 | |
256 KB 1 1 18 | |
512 KB 2 1 19 | |
1 MB 3 1 20 | |
2 MB 4 1 21 | |
4 MB 5 1 22 | |
8 MB 6 1 23 | |
16 MB 7 1 24 | |
**/ | |
UINT8 mFvAttributes[] = {0, 4, 7, 9, 10, 12, 15, 16}; | |
UINT8 mFvAttributes2[] = {17, 18, 19, 20, 21, 22, 23, 24}; | |
/** | |
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); | |
if ((FfsAttributes & FFS_ATTRIB_DATA_ALIGNMENT_2) != 0) { | |
FileAttribute = (EFI_FV_FILE_ATTRIBUTES) mFvAttributes2[DataAlignment]; | |
} else { | |
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 = FvGetVolumeAttributes (This, &FvAttributes); | |
if (EFI_ERROR (Status)){ | |
return Status; | |
} | |
// | |
// Check if read operation is enabled | |
// | |
if ((FvAttributes & EFI_FV2_READ_STATUS) == 0) { | |
return EFI_ACCESS_DENIED; | |
} | |
if (*FileType > EFI_FV_FILETYPE_MM_CORE_STANDALONE) { | |
// | |
// File type needs to be in 0 - 0x0F | |
// | |
return EFI_NOT_FOUND; | |
} | |
KeyValue = (UINTN *)Key; | |
for (;;) { | |
if (*KeyValue == 0) { | |
// | |
// Search for 1st matching file | |
// | |
Link = &FvDevice->FfsFileListHeader; | |
} else { | |
// | |
// Key is pointer to FFsFileEntry, so get next one | |
// | |
Link = (LIST_ENTRY *)(*KeyValue); | |
} | |
if (Link->ForwardLink == &FvDevice->FfsFileListHeader) { | |
// | |
// Next is end of list so we did not find data | |
// | |
return EFI_NOT_FOUND; | |
} | |
FfsFileEntry = (FFS_FILE_LIST_ENTRY *)Link->ForwardLink; | |
FfsFileHeader = (EFI_FFS_FILE_HEADER *)FfsFileEntry->FfsHeader; | |
// | |
// remember the key | |
// | |
*KeyValue = (UINTN)FfsFileEntry; | |
if (FfsFileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) { | |
// | |
// we ignore pad files | |
// | |
continue; | |
} | |
if (*FileType == EFI_FV_FILETYPE_ALL) { | |
// | |
// Process all file types so we have a match | |
// | |
break; | |
} | |
if (*FileType == FfsFileHeader->Type) { | |
// | |
// Found a matching file type | |
// | |
break; | |
} | |
} | |
// | |
// Return FileType, NameGuid, and Attributes | |
// | |
*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); | |
} | |
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; | |
EFI_GUID SearchNameGuid; | |
EFI_FV_FILETYPE LocalFoundType; | |
EFI_FV_FILE_ATTRIBUTES LocalAttributes; | |
UINTN FileSize; | |
UINT8 *SrcPtr; | |
EFI_FFS_FILE_HEADER *FfsHeader; | |
UINTN InputBufferSize; | |
UINTN WholeFileSize; | |
if (NameGuid == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
FvDevice = FV_DEVICE_FROM_THIS (This); | |
// | |
// Keep looking until we find the matching NameGuid. | |
// The Key is really a FfsFileEntry | |
// | |
FvDevice->LastKey = 0; | |
do { | |
LocalFoundType = 0; | |
Status = FvGetNextFile ( | |
This, | |
&FvDevice->LastKey, | |
&LocalFoundType, | |
&SearchNameGuid, | |
&LocalAttributes, | |
&FileSize | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_FOUND; | |
} | |
} while (!CompareGuid (&SearchNameGuid, NameGuid)); | |
// | |
// Get a pointer to the header | |
// | |
FfsHeader = FvDevice->LastKey->FfsHeader; | |
if (FvDevice->IsMemoryMapped) { | |
// | |
// Memory mapped FV has not been cached, so here is to cache by file. | |
// | |
if (!FvDevice->LastKey->FileCached) { | |
// | |
// Cache FFS file to memory buffer. | |
// | |
WholeFileSize = IS_FFS_FILE2 (FfsHeader) ? FFS_FILE2_SIZE (FfsHeader): FFS_FILE_SIZE (FfsHeader); | |
FfsHeader = AllocateCopyPool (WholeFileSize, FfsHeader); | |
if (FfsHeader == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Let FfsHeader in FfsFileEntry point to the cached file buffer. | |
// | |
FvDevice->LastKey->FfsHeader = FfsHeader; | |
FvDevice->LastKey->FileCached = TRUE; | |
} | |
} | |
// | |
// Remember callers buffer size | |
// | |
InputBufferSize = *BufferSize; | |
// | |
// Calculate return values | |
// | |
*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; | |
*BufferSize = FileSize; | |
if (Buffer == NULL) { | |
// | |
// If Buffer is NULL, we only want to get the information collected so far | |
// | |
return EFI_SUCCESS; | |
} | |
// | |
// Skip over file header | |
// | |
if (IS_FFS_FILE2 (FfsHeader)) { | |
SrcPtr = ((UINT8 *) FfsHeader) + sizeof (EFI_FFS_FILE_HEADER2); | |
} else { | |
SrcPtr = ((UINT8 *) FfsHeader) + sizeof (EFI_FFS_FILE_HEADER); | |
} | |
Status = EFI_SUCCESS; | |
if (*Buffer == NULL) { | |
// | |
// Caller passed in a pointer so allocate buffer for them | |
// | |
*Buffer = AllocatePool (FileSize); | |
if (*Buffer == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} else if (FileSize > InputBufferSize) { | |
// | |
// Callers buffer was not big enough | |
// | |
Status = EFI_WARN_BUFFER_TOO_SMALL; | |
FileSize = InputBufferSize; | |
} | |
// | |
// Copy data into callers buffer | |
// | |
CopyMem (*Buffer, SrcPtr, FileSize); | |
return Status; | |
} | |
/** | |
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_FILETYPE FileType; | |
EFI_FV_FILE_ATTRIBUTES FileAttributes; | |
UINTN FileSize; | |
UINT8 *FileBuffer; | |
FFS_FILE_LIST_ENTRY *FfsEntry; | |
if (NameGuid == NULL || Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
FvDevice = FV_DEVICE_FROM_THIS (This); | |
// | |
// Read the file | |
// | |
Status = FvReadFile ( | |
This, | |
NameGuid, | |
NULL, | |
&FileSize, | |
&FileType, | |
&FileAttributes, | |
AuthenticationStatus | |
); | |
// | |
// Get the last key used by our call to FvReadFile as it is the FfsEntry for this file. | |
// | |
FfsEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->LastKey; | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (IS_FFS_FILE2 (FfsEntry->FfsHeader)) { | |
FileBuffer = ((UINT8 *) FfsEntry->FfsHeader) + sizeof (EFI_FFS_FILE_HEADER2); | |
} else { | |
FileBuffer = ((UINT8 *) FfsEntry->FfsHeader) + sizeof (EFI_FFS_FILE_HEADER); | |
} | |
// | |
// Check to see that the file actually HAS sections before we go any further. | |
// | |
if (FileType == EFI_FV_FILETYPE_RAW) { | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
// | |
// Use FfsEntry to cache Section Extraction Protocol Information | |
// | |
if (FfsEntry->StreamHandle == 0) { | |
Status = OpenSectionStream ( | |
FileSize, | |
FileBuffer, | |
&FfsEntry->StreamHandle | |
); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
} | |
// | |
// If SectionType == 0 We need the whole section stream | |
// | |
Status = GetSection ( | |
FfsEntry->StreamHandle, | |
(SectionType == 0) ? NULL : &SectionType, | |
NULL, | |
(SectionType == 0) ? 0 : SectionInstance, | |
Buffer, | |
BufferSize, | |
AuthenticationStatus, | |
FvDevice->IsFfs3Fv | |
); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Inherit the authentication status. | |
// | |
*AuthenticationStatus |= FvDevice->AuthenticationStatus; | |
} | |
// | |
// Close of stream defered to close of FfsHeader list to allow SEP to cache data | |
// | |
Done: | |
return Status; | |
} | |