| ## @file | |
| # process FV generation | |
| # | |
| # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> | |
| # | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent | |
| # | |
| ## | |
| # Import Modules | |
| # | |
| from __future__ import absolute_import | |
| import Common.LongFilePathOs as os | |
| import subprocess | |
| from io import BytesIO | |
| from struct import * | |
| from . import FfsFileStatement | |
| from .GenFdsGlobalVariable import GenFdsGlobalVariable | |
| from Common.Misc import SaveFileOnChange, PackGUID | |
| from Common.LongFilePathSupport import CopyLongFilePath | |
| from Common.LongFilePathSupport import OpenLongFilePath as open | |
| from Common.DataType import * | |
| FV_UI_EXT_ENTY_GUID = 'A67DF1FA-8DE8-4E98-AF09-4BDF2EFFBC7C' | |
| ## generate FV | |
| # | |
| # | |
| class FV (object): | |
| ## The constructor | |
| # | |
| # @param self The object pointer | |
| # | |
| def __init__(self, Name=None): | |
| self.UiFvName = Name | |
| self.CreateFileName = None | |
| self.BlockSizeList = [] | |
| self.DefineVarDict = {} | |
| self.SetVarDict = {} | |
| self.FvAlignment = None | |
| self.FvAttributeDict = {} | |
| self.FvNameGuid = None | |
| self.FvNameString = None | |
| self.AprioriSectionList = [] | |
| self.FfsList = [] | |
| self.BsBaseAddress = None | |
| self.RtBaseAddress = None | |
| self.FvInfFile = None | |
| self.FvAddressFile = None | |
| self.BaseAddress = None | |
| self.InfFileName = None | |
| self.FvAddressFileName = None | |
| self.CapsuleName = None | |
| self.FvBaseAddress = None | |
| self.FvForceRebase = None | |
| self.FvRegionInFD = None | |
| self.UsedSizeEnable = False | |
| self.FvExtEntryTypeValue = [] | |
| self.FvExtEntryType = [] | |
| self.FvExtEntryData = [] | |
| ## AddToBuffer() | |
| # | |
| # Generate Fv and add it to the Buffer | |
| # | |
| # @param self The object pointer | |
| # @param Buffer The buffer generated FV data will be put | |
| # @param BaseAddress base address of FV | |
| # @param BlockSize block size of FV | |
| # @param BlockNum How many blocks in FV | |
| # @param ErasePolarity Flash erase polarity | |
| # @param MacroDict macro value pair | |
| # @retval string Generated FV file path | |
| # | |
| def AddToBuffer (self, Buffer, BaseAddress=None, BlockSize= None, BlockNum=None, ErasePloarity='1', MacroDict = None, Flag=False): | |
| if BaseAddress is None and self.UiFvName.upper() + 'fv' in GenFdsGlobalVariable.ImageBinDict: | |
| return GenFdsGlobalVariable.ImageBinDict[self.UiFvName.upper() + 'fv'] | |
| if MacroDict is None: | |
| MacroDict = {} | |
| # | |
| # Check whether FV in Capsule is in FD flash region. | |
| # If yes, return error. Doesn't support FV in Capsule image is also in FD flash region. | |
| # | |
| if self.CapsuleName is not None: | |
| for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values(): | |
| for RegionObj in FdObj.RegionList: | |
| if RegionObj.RegionType == BINARY_FILE_TYPE_FV: | |
| for RegionData in RegionObj.RegionDataList: | |
| if RegionData.endswith(".fv"): | |
| continue | |
| elif RegionData.upper() + 'fv' in GenFdsGlobalVariable.ImageBinDict: | |
| continue | |
| elif self.UiFvName.upper() == RegionData.upper(): | |
| GenFdsGlobalVariable.ErrorLogger("Capsule %s in FD region can't contain a FV %s in FD region." % (self.CapsuleName, self.UiFvName.upper())) | |
| if not Flag: | |
| GenFdsGlobalVariable.InfLogger( "\nGenerating %s FV" %self.UiFvName) | |
| GenFdsGlobalVariable.LargeFileInFvFlags.append(False) | |
| FFSGuid = None | |
| if self.FvBaseAddress is not None: | |
| BaseAddress = self.FvBaseAddress | |
| if not Flag: | |
| self._InitializeInf(BaseAddress, BlockSize, BlockNum, ErasePloarity) | |
| # | |
| # First Process the Apriori section | |
| # | |
| MacroDict.update(self.DefineVarDict) | |
| GenFdsGlobalVariable.VerboseLogger('First generate Apriori file !') | |
| FfsFileList = [] | |
| for AprSection in self.AprioriSectionList: | |
| FileName = AprSection.GenFfs (self.UiFvName, MacroDict, IsMakefile=Flag) | |
| FfsFileList.append(FileName) | |
| # Add Apriori file name to Inf file | |
| if not Flag: | |
| self.FvInfFile.append("EFI_FILE_NAME = " + \ | |
| FileName + \ | |
| TAB_LINE_BREAK) | |
| # Process Modules in FfsList | |
| for FfsFile in self.FfsList: | |
| if Flag: | |
| if isinstance(FfsFile, FfsFileStatement.FileStatement): | |
| continue | |
| if GenFdsGlobalVariable.EnableGenfdsMultiThread and GenFdsGlobalVariable.ModuleFile and GenFdsGlobalVariable.ModuleFile.Path.find(os.path.normpath(FfsFile.InfFileName)) == -1: | |
| continue | |
| FileName = FfsFile.GenFfs(MacroDict, FvParentAddr=BaseAddress, IsMakefile=Flag, FvName=self.UiFvName) | |
| FfsFileList.append(FileName) | |
| if not Flag: | |
| self.FvInfFile.append("EFI_FILE_NAME = " + \ | |
| FileName + \ | |
| TAB_LINE_BREAK) | |
| if not Flag: | |
| FvInfFile = ''.join(self.FvInfFile) | |
| SaveFileOnChange(self.InfFileName, FvInfFile, False) | |
| # | |
| # Call GenFv tool | |
| # | |
| FvOutputFile = os.path.join(GenFdsGlobalVariable.FvDir, self.UiFvName) | |
| FvOutputFile = FvOutputFile + '.Fv' | |
| # BUGBUG: FvOutputFile could be specified from FDF file (FV section, CreateFile statement) | |
| if self.CreateFileName is not None: | |
| FvOutputFile = self.CreateFileName | |
| if Flag: | |
| GenFdsGlobalVariable.ImageBinDict[self.UiFvName.upper() + 'fv'] = FvOutputFile | |
| return FvOutputFile | |
| FvInfoFileName = os.path.join(GenFdsGlobalVariable.FfsDir, self.UiFvName + '.inf') | |
| if not Flag: | |
| CopyLongFilePath(GenFdsGlobalVariable.FvAddressFileName, FvInfoFileName) | |
| OrigFvInfo = None | |
| if os.path.exists (FvInfoFileName): | |
| OrigFvInfo = open(FvInfoFileName, 'r').read() | |
| if GenFdsGlobalVariable.LargeFileInFvFlags[-1]: | |
| FFSGuid = GenFdsGlobalVariable.EFI_FIRMWARE_FILE_SYSTEM3_GUID | |
| GenFdsGlobalVariable.GenerateFirmwareVolume( | |
| FvOutputFile, | |
| [self.InfFileName], | |
| AddressFile=FvInfoFileName, | |
| FfsList=FfsFileList, | |
| ForceRebase=self.FvForceRebase, | |
| FileSystemGuid=FFSGuid | |
| ) | |
| NewFvInfo = None | |
| if os.path.exists (FvInfoFileName): | |
| NewFvInfo = open(FvInfoFileName, 'r').read() | |
| if NewFvInfo is not None and NewFvInfo != OrigFvInfo: | |
| FvChildAddr = [] | |
| AddFileObj = open(FvInfoFileName, 'r') | |
| AddrStrings = AddFileObj.readlines() | |
| AddrKeyFound = False | |
| for AddrString in AddrStrings: | |
| if AddrKeyFound: | |
| #get base address for the inside FvImage | |
| FvChildAddr.append (AddrString) | |
| elif AddrString.find ("[FV_BASE_ADDRESS]") != -1: | |
| AddrKeyFound = True | |
| AddFileObj.close() | |
| if FvChildAddr != []: | |
| # Update Ffs again | |
| for FfsFile in self.FfsList: | |
| FileName = FfsFile.GenFfs(MacroDict, FvChildAddr, BaseAddress, IsMakefile=Flag, FvName=self.UiFvName) | |
| if GenFdsGlobalVariable.LargeFileInFvFlags[-1]: | |
| FFSGuid = GenFdsGlobalVariable.EFI_FIRMWARE_FILE_SYSTEM3_GUID; | |
| #Update GenFv again | |
| GenFdsGlobalVariable.GenerateFirmwareVolume( | |
| FvOutputFile, | |
| [self.InfFileName], | |
| AddressFile=FvInfoFileName, | |
| FfsList=FfsFileList, | |
| ForceRebase=self.FvForceRebase, | |
| FileSystemGuid=FFSGuid | |
| ) | |
| # | |
| # Write the Fv contents to Buffer | |
| # | |
| if os.path.isfile(FvOutputFile) and os.path.getsize(FvOutputFile) >= 0x48: | |
| FvFileObj = open(FvOutputFile, 'rb') | |
| # PI FvHeader is 0x48 byte | |
| FvHeaderBuffer = FvFileObj.read(0x48) | |
| Signature = FvHeaderBuffer[0x28:0x32] | |
| if Signature and Signature.startswith(b'_FVH'): | |
| GenFdsGlobalVariable.VerboseLogger("\nGenerate %s FV Successfully" % self.UiFvName) | |
| GenFdsGlobalVariable.SharpCounter = 0 | |
| FvFileObj.seek(0) | |
| Buffer.write(FvFileObj.read()) | |
| # FV alignment position. | |
| FvAlignmentValue = 1 << (ord(FvHeaderBuffer[0x2E:0x2F]) & 0x1F) | |
| if FvAlignmentValue >= 0x400: | |
| if FvAlignmentValue >= 0x100000: | |
| if FvAlignmentValue >= 0x1000000: | |
| #The max alignment supported by FFS is 16M. | |
| self.FvAlignment = "16M" | |
| else: | |
| self.FvAlignment = str(FvAlignmentValue // 0x100000) + "M" | |
| else: | |
| self.FvAlignment = str(FvAlignmentValue // 0x400) + "K" | |
| else: | |
| # FvAlignmentValue is less than 1K | |
| self.FvAlignment = str (FvAlignmentValue) | |
| FvFileObj.close() | |
| GenFdsGlobalVariable.ImageBinDict[self.UiFvName.upper() + 'fv'] = FvOutputFile | |
| GenFdsGlobalVariable.LargeFileInFvFlags.pop() | |
| else: | |
| GenFdsGlobalVariable.ErrorLogger("Invalid FV file %s." % self.UiFvName) | |
| else: | |
| GenFdsGlobalVariable.ErrorLogger("Failed to generate %s FV file." %self.UiFvName) | |
| return FvOutputFile | |
| ## _GetBlockSize() | |
| # | |
| # Calculate FV's block size | |
| # Inherit block size from FD if no block size specified in FV | |
| # | |
| def _GetBlockSize(self): | |
| if self.BlockSizeList: | |
| return True | |
| for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values(): | |
| for RegionObj in FdObj.RegionList: | |
| if RegionObj.RegionType != BINARY_FILE_TYPE_FV: | |
| continue | |
| for RegionData in RegionObj.RegionDataList: | |
| # | |
| # Found the FD and region that contain this FV | |
| # | |
| if self.UiFvName.upper() == RegionData.upper(): | |
| RegionObj.BlockInfoOfRegion(FdObj.BlockSizeList, self) | |
| if self.BlockSizeList: | |
| return True | |
| return False | |
| ## _InitializeInf() | |
| # | |
| # Initialize the inf file to create FV | |
| # | |
| # @param self The object pointer | |
| # @param BaseAddress base address of FV | |
| # @param BlockSize block size of FV | |
| # @param BlockNum How many blocks in FV | |
| # @param ErasePolarity Flash erase polarity | |
| # | |
| def _InitializeInf (self, BaseAddress = None, BlockSize= None, BlockNum = None, ErasePloarity='1'): | |
| # | |
| # Create FV inf file | |
| # | |
| self.InfFileName = os.path.join(GenFdsGlobalVariable.FvDir, | |
| self.UiFvName + '.inf') | |
| self.FvInfFile = [] | |
| # | |
| # Add [Options] | |
| # | |
| self.FvInfFile.append("[options]" + TAB_LINE_BREAK) | |
| if BaseAddress is not None: | |
| self.FvInfFile.append("EFI_BASE_ADDRESS = " + \ | |
| BaseAddress + \ | |
| TAB_LINE_BREAK) | |
| if BlockSize is not None: | |
| self.FvInfFile.append("EFI_BLOCK_SIZE = " + \ | |
| '0x%X' %BlockSize + \ | |
| TAB_LINE_BREAK) | |
| if BlockNum is not None: | |
| self.FvInfFile.append("EFI_NUM_BLOCKS = " + \ | |
| ' 0x%X' %BlockNum + \ | |
| TAB_LINE_BREAK) | |
| else: | |
| if self.BlockSizeList == []: | |
| if not self._GetBlockSize(): | |
| #set default block size is 1 | |
| self.FvInfFile.append("EFI_BLOCK_SIZE = 0x1" + TAB_LINE_BREAK) | |
| for BlockSize in self.BlockSizeList: | |
| if BlockSize[0] is not None: | |
| self.FvInfFile.append("EFI_BLOCK_SIZE = " + \ | |
| '0x%X' %BlockSize[0] + \ | |
| TAB_LINE_BREAK) | |
| if BlockSize[1] is not None: | |
| self.FvInfFile.append("EFI_NUM_BLOCKS = " + \ | |
| ' 0x%X' %BlockSize[1] + \ | |
| TAB_LINE_BREAK) | |
| if self.BsBaseAddress is not None: | |
| self.FvInfFile.append('EFI_BOOT_DRIVER_BASE_ADDRESS = ' + \ | |
| '0x%X' %self.BsBaseAddress) | |
| if self.RtBaseAddress is not None: | |
| self.FvInfFile.append('EFI_RUNTIME_DRIVER_BASE_ADDRESS = ' + \ | |
| '0x%X' %self.RtBaseAddress) | |
| # | |
| # Add attribute | |
| # | |
| self.FvInfFile.append("[attributes]" + TAB_LINE_BREAK) | |
| self.FvInfFile.append("EFI_ERASE_POLARITY = " + \ | |
| ' %s' %ErasePloarity + \ | |
| TAB_LINE_BREAK) | |
| if not (self.FvAttributeDict is None): | |
| for FvAttribute in self.FvAttributeDict.keys(): | |
| if FvAttribute == "FvUsedSizeEnable": | |
| if self.FvAttributeDict[FvAttribute].upper() in ('TRUE', '1'): | |
| self.UsedSizeEnable = True | |
| continue | |
| self.FvInfFile.append("EFI_" + \ | |
| FvAttribute + \ | |
| ' = ' + \ | |
| self.FvAttributeDict[FvAttribute] + \ | |
| TAB_LINE_BREAK ) | |
| if self.FvAlignment is not None: | |
| self.FvInfFile.append("EFI_FVB2_ALIGNMENT_" + \ | |
| self.FvAlignment.strip() + \ | |
| " = TRUE" + \ | |
| TAB_LINE_BREAK) | |
| # | |
| # Generate FV extension header file | |
| # | |
| if not self.FvNameGuid: | |
| if len(self.FvExtEntryType) > 0 or self.UsedSizeEnable: | |
| GenFdsGlobalVariable.ErrorLogger("FV Extension Header Entries declared for %s with no FvNameGuid declaration." % (self.UiFvName)) | |
| else: | |
| TotalSize = 16 + 4 | |
| Buffer = bytearray() | |
| if self.UsedSizeEnable: | |
| TotalSize += (4 + 4) | |
| ## define EFI_FV_EXT_TYPE_USED_SIZE_TYPE 0x03 | |
| #typedef struct | |
| # { | |
| # EFI_FIRMWARE_VOLUME_EXT_ENTRY Hdr; | |
| # UINT32 UsedSize; | |
| # } EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE; | |
| Buffer += pack('HHL', 8, 3, 0) | |
| if self.FvNameString == 'TRUE': | |
| # | |
| # Create EXT entry for FV UI name | |
| # This GUID is used: A67DF1FA-8DE8-4E98-AF09-4BDF2EFFBC7C | |
| # | |
| FvUiLen = len(self.UiFvName) | |
| TotalSize += (FvUiLen + 16 + 4) | |
| Guid = FV_UI_EXT_ENTY_GUID.split('-') | |
| # | |
| # Layout: | |
| # EFI_FIRMWARE_VOLUME_EXT_ENTRY: size 4 | |
| # GUID: size 16 | |
| # FV UI name | |
| # | |
| Buffer += (pack('HH', (FvUiLen + 16 + 4), 0x0002) | |
| + PackGUID(Guid) | |
| + self.UiFvName.encode('utf-8')) | |
| for Index in range (0, len(self.FvExtEntryType)): | |
| if self.FvExtEntryType[Index] == 'FILE': | |
| # check if the path is absolute or relative | |
| if os.path.isabs(self.FvExtEntryData[Index]): | |
| FileFullPath = os.path.normpath(self.FvExtEntryData[Index]) | |
| else: | |
| FileFullPath = os.path.normpath(os.path.join(GenFdsGlobalVariable.WorkSpaceDir, self.FvExtEntryData[Index])) | |
| # check if the file path exists or not | |
| if not os.path.isfile(FileFullPath): | |
| GenFdsGlobalVariable.ErrorLogger("Error opening FV Extension Header Entry file %s." % (self.FvExtEntryData[Index])) | |
| FvExtFile = open (FileFullPath, 'rb') | |
| FvExtFile.seek(0, 2) | |
| Size = FvExtFile.tell() | |
| if Size >= 0x10000: | |
| GenFdsGlobalVariable.ErrorLogger("The size of FV Extension Header Entry file %s exceeds 0x10000." % (self.FvExtEntryData[Index])) | |
| TotalSize += (Size + 4) | |
| FvExtFile.seek(0) | |
| Buffer += pack('HH', (Size + 4), int(self.FvExtEntryTypeValue[Index], 16)) | |
| Buffer += FvExtFile.read() | |
| FvExtFile.close() | |
| if self.FvExtEntryType[Index] == 'DATA': | |
| ByteList = self.FvExtEntryData[Index].split(',') | |
| Size = len (ByteList) | |
| if Size >= 0x10000: | |
| GenFdsGlobalVariable.ErrorLogger("The size of FV Extension Header Entry data %s exceeds 0x10000." % (self.FvExtEntryData[Index])) | |
| TotalSize += (Size + 4) | |
| Buffer += pack('HH', (Size + 4), int(self.FvExtEntryTypeValue[Index], 16)) | |
| for Index1 in range (0, Size): | |
| Buffer += pack('B', int(ByteList[Index1], 16)) | |
| Guid = self.FvNameGuid.split('-') | |
| Buffer = PackGUID(Guid) + pack('=L', TotalSize) + Buffer | |
| # | |
| # Generate FV extension header file if the total size is not zero | |
| # | |
| if TotalSize > 0: | |
| FvExtHeaderFileName = os.path.join(GenFdsGlobalVariable.FvDir, self.UiFvName + '.ext') | |
| FvExtHeaderFile = BytesIO() | |
| FvExtHeaderFile.write(Buffer) | |
| Changed = SaveFileOnChange(FvExtHeaderFileName, FvExtHeaderFile.getvalue(), True) | |
| FvExtHeaderFile.close() | |
| if Changed: | |
| if os.path.exists (self.InfFileName): | |
| os.remove (self.InfFileName) | |
| self.FvInfFile.append("EFI_FV_EXT_HEADER_FILE_NAME = " + \ | |
| FvExtHeaderFileName + \ | |
| TAB_LINE_BREAK) | |
| # | |
| # Add [Files] | |
| # | |
| self.FvInfFile.append("[files]" + TAB_LINE_BREAK) |