/** @file Read FFS Library @copyright Copyright 2014 - 2021 Intel Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include // //EFI_FIRMWARE_FILE_SYSTEM3_GUID indicates support for FFS_ATTRIB_LARGE_SIZE //And thus support for files 16MB or larger. // UINT8 * PreMemReadFFSFile ( IN EFI_FIRMWARE_VOLUME_HEADER* FwVolHeader, IN EFI_GUID FFSGuid, IN UINT32 FFSDataSize, IN BOOLEAN skipheader ) /*++ Routine Description: Read FFS file from specified FV in PreMem phase Arguments: FwVolHeader - FV Base Address FFSGuid - FFS to find & Read FFSDataSize - Data size to read.If this value equal 0, will read the whole FFS size skipheader - TRUE: skip to read the ffs and first section header,read from data directly, for one data section only ffs FALSE:read from header Returns: None --*/ { EFI_FFS_FILE_HEADER *ffsHdr; UINT64 FvSize; UINT32 FileOccupiedSize; UINT32 FFSDataOffset; EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExtHeader; UINT32 FFSSize = 0; FvSize = 0; FFSDataOffset = 0; if (FwVolHeader->ExtHeaderOffset != 0) { // // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists. // FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) ((UINTN)FwVolHeader + FwVolHeader->ExtHeaderOffset ); ffsHdr = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize); } else { ffsHdr = (EFI_FFS_FILE_HEADER*)(((UINT8 *) FwVolHeader)+ FwVolHeader->HeaderLength); } ffsHdr = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (ffsHdr, 8); if(FwVolHeader->FvLength == 0xFFFFFFFFFFFFFFFF) return NULL; FvSize = (UINTN)ffsHdr-(UINTN)FwVolHeader; while((FvSize < FwVolHeader->FvLength)&&((UINTN)ffsHdr <((UINTN)FwVolHeader+ (UINTN)FwVolHeader->FvLength))){ if(CompareGuid (&ffsHdr->Name, &FFSGuid)) break; if (IS_FFS_FILE2 (ffsHdr)) { FileOccupiedSize = FFS_FILE2_SIZE (ffsHdr) ; } else { FileOccupiedSize = FFS_FILE_SIZE (ffsHdr) ; } FvSize+= FileOccupiedSize; ffsHdr = (EFI_FFS_FILE_HEADER *)((UINT8 *)ffsHdr + FileOccupiedSize); ffsHdr = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (ffsHdr, 8); } if (FvSize < FwVolHeader->FvLength) { if (IS_FFS_FILE2 (ffsHdr)) { FileOccupiedSize = FFS_FILE2_SIZE (ffsHdr) ; } else { FileOccupiedSize = FFS_FILE_SIZE (ffsHdr) ; } FFSSize = FileOccupiedSize; if(FFSDataSize == 0) FFSDataSize= FFSSize; if(skipheader){ if (IS_FFS_FILE2 (ffsHdr)) { FFSDataOffset = sizeof(EFI_FFS_FILE_HEADER2) + sizeof(EFI_COMMON_SECTION_HEADER2); } else { FFSDataOffset = sizeof(EFI_FFS_FILE_HEADER) + sizeof(EFI_COMMON_SECTION_HEADER); } if(FFSDataSize == 0) { if (IS_FFS_FILE2 (ffsHdr)) { FFSDataSize = FFSDataSize - sizeof(EFI_FFS_FILE_HEADER2) - sizeof(EFI_COMMON_SECTION_HEADER2); } else { FFSDataSize = FFSDataSize - sizeof(EFI_FFS_FILE_HEADER) - sizeof(EFI_COMMON_SECTION_HEADER); } } } } return (UINT8 *)ffsHdr + FFSDataOffset; } EFI_STATUS ReadFFSFile ( IN EFI_FIRMWARE_VOLUME_HEADER* FwVolHeader, IN EFI_GUID FFSGuid, IN UINT32 FFSDataSize, IN OUT VOID *FFSData, OUT UINT32 *FFSSize, IN BOOLEAN skipheader ) /*++ Routine Description: Read FFS file from FV Arguments: FwVolHeader - FV Base Address FFSGuid - FFS to find & Read FFSDataSize - Data size to read.If this value equal 0, will read the whole FFS size FFSData - Pointer to buffer for read. FFSSize - FFS file size FYI for caller. skipheader - TRUE: skip to read the ffs and first section header,read from data directly, for one data section only ffs FALSE:read from header Returns: None --*/ { EFI_FFS_FILE_HEADER *ffsHdr; UINT64 FvSize; UINT32 FileOccupiedSize; UINT32 FFSDataOffset; EFI_STATUS Status; EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExtHeader; Status = EFI_NOT_FOUND; *FFSSize = 0; FvSize = 0; FFSDataOffset = 0; if (FwVolHeader->ExtHeaderOffset != 0) { // // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists. // FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) ((UINTN)FwVolHeader + FwVolHeader->ExtHeaderOffset ); ffsHdr = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize); } else { ffsHdr = (EFI_FFS_FILE_HEADER*)(((UINT8 *) FwVolHeader)+ FwVolHeader->HeaderLength); } ffsHdr = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (ffsHdr, 8); if(FwVolHeader->FvLength == 0xFFFFFFFFFFFFFFFF) { return EFI_VOLUME_CORRUPTED; } FvSize = (UINTN)ffsHdr-(UINTN)FwVolHeader; while((FvSize < FwVolHeader->FvLength)&&((UINTN)ffsHdr <((UINTN)FwVolHeader+ (UINTN)FwVolHeader->FvLength))){ if(CompareGuid (&ffsHdr->Name, &FFSGuid)){ Status = EFI_SUCCESS; break; } if (IS_FFS_FILE2 (ffsHdr)) { FileOccupiedSize = FFS_FILE2_SIZE (ffsHdr) ; } else { FileOccupiedSize = FFS_FILE_SIZE (ffsHdr) ; } FvSize+= FileOccupiedSize; ffsHdr = (EFI_FFS_FILE_HEADER *)((UINT8 *)ffsHdr + FileOccupiedSize); ffsHdr = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (ffsHdr, 8); } if (FvSize < FwVolHeader->FvLength) { if (IS_FFS_FILE2 (ffsHdr)) { FileOccupiedSize = FFS_FILE2_SIZE (ffsHdr) ; } else { FileOccupiedSize = FFS_FILE_SIZE (ffsHdr) ; } *FFSSize = FileOccupiedSize; if(FFSDataSize == 0) { FFSDataSize= *FFSSize; } if(skipheader){ if (IS_FFS_FILE2 (ffsHdr)) { FFSDataOffset = sizeof(EFI_FFS_FILE_HEADER2) + sizeof(EFI_COMMON_SECTION_HEADER2); } else { FFSDataOffset = sizeof(EFI_FFS_FILE_HEADER) + sizeof(EFI_COMMON_SECTION_HEADER); } if(FFSDataSize == 0) { if (IS_FFS_FILE2 (ffsHdr)) { FFSDataSize = FFSDataSize - sizeof(EFI_FFS_FILE_HEADER2) - sizeof(EFI_COMMON_SECTION_HEADER2); } else { FFSDataSize = FFSDataSize - sizeof(EFI_FFS_FILE_HEADER) - sizeof(EFI_COMMON_SECTION_HEADER); } } } CopyMem(FFSData,(UINT8*)ffsHdr + FFSDataOffset,FFSDataSize); } return Status; } BOOLEAN NormalHobToCompressHob(IN OUT VOID* hobAddr,IN OUT UINTN* size) { UINTN i,j,k; COMPRESS_HOBO_DATA CompressHob; COMPRESS_ITEM Hobitem[MAX_COMPRESS_ITEM]; UINT16 TempBuffer[MAX_FFS_BUFFER_SIZE/4]; UINTN offset=0; UINTN RemainSize = 0; CompressHob.Signature = 0x5A45524F; CompressHob.Count =0; j=0; i=0; RemainSize = (*size % 2) + 2; if(MAX_COMPRESS_ITEM*sizeof(COMPRESS_ITEM)+MAX_FFS_BUFFER_SIZE/4+sizeof(COMPRESS_HOBO_DATA) > MAX_FFS_BUFFER_SIZE) { return FALSE; } if((*size) > MAX_FFS_BUFFER_SIZE) { return FALSE; } ZeroMem(TempBuffer,MAX_FFS_BUFFER_SIZE/2); while(i < (*size - RemainSize)){ if(j>=MAX_COMPRESS_ITEM) { return FALSE; } //search for duplicate array if(*(UINT16*)((UINTN)hobAddr+i)==*(UINT16*)((UINTN)hobAddr+i+2) ){ for(k=2;(i+k)<(*size- RemainSize);k+=2 ){ if(*(UINT16*)((UINTN)hobAddr+i)!=*(UINT16*)((UINTN)hobAddr+i+k)){ break; } } if(i+k>= *size - (*size % 2)) { k -=2; } Hobitem[j].Value = *(UINT16*)((UINTN)hobAddr+i); Hobitem[j].Length =(UINT16) k; Hobitem[j].Type = COMPRESS_DUPLICATE; Hobitem[j].Offset = 0; j++; CompressHob.Count =(UINT32)j; i+=k; } else{//single array for(k=2;i+k+2 <= (*size- RemainSize);k+=2){ if(offset >= MAX_FFS_BUFFER_SIZE/4) { return FALSE; } TempBuffer[offset]= *(UINT16*)((UINTN)hobAddr+i+k-2); offset +=1; if(*(UINT16*)((UINTN)hobAddr+i+k)==*(UINT16*)((UINTN)hobAddr+i+k+2)){ k += 2; break; } } Hobitem[j].Length = (UINT16) k - 2; Hobitem[j].Type = COMPRESS_SINGLE; Hobitem[j].Offset = (UINT16)(offset - (Hobitem[j].Length/2) ); Hobitem[j].Value =0; j++; CompressHob.Count =(UINT32)j; i+=k - 2; } } if(j>=MAX_COMPRESS_ITEM) { //not worth to compress return FALSE; } //process last one CopyMem((UINT8*)(&TempBuffer[offset]), (UINT8*)((UINTN)hobAddr + *size - RemainSize), RemainSize); Hobitem[j].Length = (UINT16)RemainSize; Hobitem[j].Type = COMPRESS_SINGLE; Hobitem[j].Offset = (UINT16)offset; Hobitem[j].Value =0; j++; CompressHob.Count =(UINT32)j; CopyMem(hobAddr,(VOID*)&CompressHob,sizeof(COMPRESS_HOBO_DATA)); offset = sizeof(COMPRESS_HOBO_DATA); for(i=0; i < CompressHob.Count;i++){ CopyMem((UINT8*)((UINTN)hobAddr+offset),(UINT8*)&Hobitem[i],sizeof(COMPRESS_ITEM)); offset += sizeof(COMPRESS_ITEM); if(Hobitem[i].Type == COMPRESS_SINGLE){ CopyMem((UINT8*)((UINTN)hobAddr+offset),&TempBuffer[Hobitem[i].Offset],Hobitem[i].Length); offset += Hobitem[i].Length; } } *size = offset; return TRUE; } BOOLEAN CompressHobToNormalHob(IN OUT VOID* hobAddr,OUT UINTN* size) { UINTN i; COMPRESS_HOBO_DATA CompressHob ; COMPRESS_ITEM Hobitem[MAX_COMPRESS_ITEM]; UINTN offset=0; UINT16 TempBuffer[MAX_FFS_BUFFER_SIZE/4]; CopyMem((VOID*)&CompressHob,hobAddr,sizeof(COMPRESS_HOBO_DATA)); if(CompressHob.Signature != 0x5A45524F) { return FALSE; } if(CompressHob.Count>MAX_COMPRESS_ITEM) { //not worth to compress return FALSE; } if(*size > MAX_FFS_BUFFER_SIZE) { return FALSE; } if(MAX_COMPRESS_ITEM*sizeof(COMPRESS_ITEM)+MAX_FFS_BUFFER_SIZE/4+sizeof(COMPRESS_HOBO_DATA) > MAX_FFS_BUFFER_SIZE) { return FALSE; } offset = sizeof(COMPRESS_HOBO_DATA); for(i=0; i < CompressHob.Count;i++){ CopyMem((VOID*)&Hobitem[i],(UINT8*)((UINTN)hobAddr+offset),sizeof(COMPRESS_ITEM)); offset += sizeof(COMPRESS_ITEM); if(Hobitem[i].Type == COMPRESS_SINGLE){ CopyMem(&TempBuffer[Hobitem[i].Offset],(UINT8*)((UINTN)hobAddr+offset),Hobitem[i].Length); offset += Hobitem[i].Length; } } offset =0; for(i=0; i < CompressHob.Count;i++){ if(Hobitem[i].Type ==COMPRESS_DUPLICATE){ SetMem16((VOID*)((UINTN)hobAddr+offset),Hobitem[i].Length,Hobitem[i].Value); offset += Hobitem[i].Length; } else if(Hobitem[i].Type ==COMPRESS_SINGLE){ CopyMem((UINT8*)((UINTN)hobAddr+offset),&TempBuffer[Hobitem[i].Offset],Hobitem[i].Length); offset += Hobitem[i].Length; } } *size = offset; return TRUE; } EFI_STATUS ValidateCommonFvHeader ( EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader ) /*++ Routine Description: Check the integrity of firmware volume header Arguments: FwVolHeader - A pointer to a firmware volume header Returns: EFI_SUCCESS - The firmware volume is consistent EFI_NOT_FOUND - The firmware volume has corrupted. So it is not an FV --*/ { UINT16 *Ptr; UINT16 HeaderLength; UINT16 Checksum; // // Verify the header revision, header signature, length // Length of FvBlock cannot be 2**64-1 // HeaderLength cannot be an odd number // if ((FwVolHeader->Revision != EFI_FVH_REVISION) || (FwVolHeader->Signature != EFI_FVH_SIGNATURE) || (FwVolHeader->FvLength == ((UINTN) -1)) || ((FwVolHeader->HeaderLength & 0x01) != 0) ) { return EFI_NOT_FOUND; } // // Verify the header checksum // HeaderLength = (UINT16) (FwVolHeader->HeaderLength / 2); Ptr = (UINT16 *) FwVolHeader; Checksum = 0; while (HeaderLength > 0) { Checksum = Checksum + (*Ptr); Ptr++; HeaderLength--; } if (Checksum != 0) { return EFI_NOT_FOUND; } return EFI_SUCCESS; }