1## @file
2# process FV generation
3#
4#  Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5#
6#  SPDX-License-Identifier: BSD-2-Clause-Patent
7#
8
9##
10# Import Modules
11#
12from __future__ import absolute_import
13import Common.LongFilePathOs as os
14import subprocess
15from io import BytesIO
16from struct import *
17from . import FfsFileStatement
18from .GenFdsGlobalVariable import GenFdsGlobalVariable
19from Common.Misc import SaveFileOnChange, PackGUID
20from Common.LongFilePathSupport import CopyLongFilePath
21from Common.LongFilePathSupport import OpenLongFilePath as open
22from Common.DataType import *
23
24FV_UI_EXT_ENTY_GUID = 'A67DF1FA-8DE8-4E98-AF09-4BDF2EFFBC7C'
25
26## generate FV
27#
28#
29class FV (object):
30    ## The constructor
31    #
32    #   @param  self        The object pointer
33    #
34    def __init__(self, Name=None):
35        self.UiFvName = Name
36        self.CreateFileName = None
37        self.BlockSizeList = []
38        self.DefineVarDict = {}
39        self.SetVarDict = {}
40        self.FvAlignment = None
41        self.FvAttributeDict = {}
42        self.FvNameGuid = None
43        self.FvNameString = None
44        self.AprioriSectionList = []
45        self.FfsList = []
46        self.BsBaseAddress = None
47        self.RtBaseAddress = None
48        self.FvInfFile = None
49        self.FvAddressFile = None
50        self.BaseAddress = None
51        self.InfFileName = None
52        self.FvAddressFileName = None
53        self.CapsuleName = None
54        self.FvBaseAddress = None
55        self.FvForceRebase = None
56        self.FvRegionInFD = None
57        self.UsedSizeEnable = False
58        self.FvExtEntryTypeValue = []
59        self.FvExtEntryType = []
60        self.FvExtEntryData = []
61    ## AddToBuffer()
62    #
63    #   Generate Fv and add it to the Buffer
64    #
65    #   @param  self        The object pointer
66    #   @param  Buffer      The buffer generated FV data will be put
67    #   @param  BaseAddress base address of FV
68    #   @param  BlockSize   block size of FV
69    #   @param  BlockNum    How many blocks in FV
70    #   @param  ErasePolarity      Flash erase polarity
71    #   @param  MacroDict   macro value pair
72    #   @retval string      Generated FV file path
73    #
74    def AddToBuffer (self, Buffer, BaseAddress=None, BlockSize= None, BlockNum=None, ErasePloarity='1',  MacroDict = None, Flag=False):
75        if BaseAddress is None and self.UiFvName.upper() + 'fv' in GenFdsGlobalVariable.ImageBinDict:
76            return GenFdsGlobalVariable.ImageBinDict[self.UiFvName.upper() + 'fv']
77        if MacroDict is None:
78            MacroDict = {}
79
80        #
81        # Check whether FV in Capsule is in FD flash region.
82        # If yes, return error. Doesn't support FV in Capsule image is also in FD flash region.
83        #
84        if self.CapsuleName is not None:
85            for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
86                for RegionObj in FdObj.RegionList:
87                    if RegionObj.RegionType == BINARY_FILE_TYPE_FV:
88                        for RegionData in RegionObj.RegionDataList:
89                            if RegionData.endswith(".fv"):
90                                continue
91                            elif RegionData.upper() + 'fv' in GenFdsGlobalVariable.ImageBinDict:
92                                continue
93                            elif self.UiFvName.upper() == RegionData.upper():
94                                GenFdsGlobalVariable.ErrorLogger("Capsule %s in FD region can't contain a FV %s in FD region." % (self.CapsuleName, self.UiFvName.upper()))
95        if not Flag:
96            GenFdsGlobalVariable.InfLogger( "\nGenerating %s FV" %self.UiFvName)
97        GenFdsGlobalVariable.LargeFileInFvFlags.append(False)
98        FFSGuid = None
99
100        if self.FvBaseAddress is not None:
101            BaseAddress = self.FvBaseAddress
102        if not Flag:
103            self._InitializeInf(BaseAddress, BlockSize, BlockNum, ErasePloarity)
104        #
105        # First Process the Apriori section
106        #
107        MacroDict.update(self.DefineVarDict)
108
109        GenFdsGlobalVariable.VerboseLogger('First generate Apriori file !')
110        FfsFileList = []
111        for AprSection in self.AprioriSectionList:
112            FileName = AprSection.GenFfs (self.UiFvName, MacroDict, IsMakefile=Flag)
113            FfsFileList.append(FileName)
114            # Add Apriori file name to Inf file
115            if not Flag:
116                self.FvInfFile.append("EFI_FILE_NAME = " + \
117                                            FileName          + \
118                                            TAB_LINE_BREAK)
119
120        # Process Modules in FfsList
121        for FfsFile in self.FfsList:
122            if Flag:
123                if isinstance(FfsFile, FfsFileStatement.FileStatement):
124                    continue
125            if GenFdsGlobalVariable.EnableGenfdsMultiThread and GenFdsGlobalVariable.ModuleFile and GenFdsGlobalVariable.ModuleFile.Path.find(os.path.normpath(FfsFile.InfFileName)) == -1:
126                continue
127            FileName = FfsFile.GenFfs(MacroDict, FvParentAddr=BaseAddress, IsMakefile=Flag, FvName=self.UiFvName)
128            FfsFileList.append(FileName)
129            if not Flag:
130                self.FvInfFile.append("EFI_FILE_NAME = " + \
131                                            FileName          + \
132                                            TAB_LINE_BREAK)
133        if not Flag:
134            FvInfFile = ''.join(self.FvInfFile)
135            SaveFileOnChange(self.InfFileName, FvInfFile, False)
136        #
137        # Call GenFv tool
138        #
139        FvOutputFile = os.path.join(GenFdsGlobalVariable.FvDir, self.UiFvName)
140        FvOutputFile = FvOutputFile + '.Fv'
141        # BUGBUG: FvOutputFile could be specified from FDF file (FV section, CreateFile statement)
142        if self.CreateFileName is not None:
143            FvOutputFile = self.CreateFileName
144
145        if Flag:
146            GenFdsGlobalVariable.ImageBinDict[self.UiFvName.upper() + 'fv'] = FvOutputFile
147            return FvOutputFile
148
149        FvInfoFileName = os.path.join(GenFdsGlobalVariable.FfsDir, self.UiFvName + '.inf')
150        if not Flag:
151            CopyLongFilePath(GenFdsGlobalVariable.FvAddressFileName, FvInfoFileName)
152            OrigFvInfo = None
153            if os.path.exists (FvInfoFileName):
154                OrigFvInfo = open(FvInfoFileName, 'r').read()
155            if GenFdsGlobalVariable.LargeFileInFvFlags[-1]:
156                FFSGuid = GenFdsGlobalVariable.EFI_FIRMWARE_FILE_SYSTEM3_GUID
157            GenFdsGlobalVariable.GenerateFirmwareVolume(
158                                    FvOutputFile,
159                                    [self.InfFileName],
160                                    AddressFile=FvInfoFileName,
161                                    FfsList=FfsFileList,
162                                    ForceRebase=self.FvForceRebase,
163                                    FileSystemGuid=FFSGuid
164                                    )
165
166            NewFvInfo = None
167            if os.path.exists (FvInfoFileName):
168                NewFvInfo = open(FvInfoFileName, 'r').read()
169            if NewFvInfo is not None and NewFvInfo != OrigFvInfo:
170                FvChildAddr = []
171                AddFileObj = open(FvInfoFileName, 'r')
172                AddrStrings = AddFileObj.readlines()
173                AddrKeyFound = False
174                for AddrString in AddrStrings:
175                    if AddrKeyFound:
176                        #get base address for the inside FvImage
177                        FvChildAddr.append (AddrString)
178                    elif AddrString.find ("[FV_BASE_ADDRESS]") != -1:
179                        AddrKeyFound = True
180                AddFileObj.close()
181
182                if FvChildAddr != []:
183                    # Update Ffs again
184                    for FfsFile in self.FfsList:
185                        FileName = FfsFile.GenFfs(MacroDict, FvChildAddr, BaseAddress, IsMakefile=Flag, FvName=self.UiFvName)
186
187                    if GenFdsGlobalVariable.LargeFileInFvFlags[-1]:
188                        FFSGuid = GenFdsGlobalVariable.EFI_FIRMWARE_FILE_SYSTEM3_GUID;
189                    #Update GenFv again
190                    GenFdsGlobalVariable.GenerateFirmwareVolume(
191                                                FvOutputFile,
192                                                [self.InfFileName],
193                                                AddressFile=FvInfoFileName,
194                                                FfsList=FfsFileList,
195                                                ForceRebase=self.FvForceRebase,
196                                                FileSystemGuid=FFSGuid
197                                                )
198
199            #
200            # Write the Fv contents to Buffer
201            #
202            if os.path.isfile(FvOutputFile) and os.path.getsize(FvOutputFile) >= 0x48:
203                FvFileObj = open(FvOutputFile, 'rb')
204                # PI FvHeader is 0x48 byte
205                FvHeaderBuffer = FvFileObj.read(0x48)
206                Signature = FvHeaderBuffer[0x28:0x32]
207                if Signature and Signature.startswith(b'_FVH'):
208                    GenFdsGlobalVariable.VerboseLogger("\nGenerate %s FV Successfully" % self.UiFvName)
209                    GenFdsGlobalVariable.SharpCounter = 0
210
211                    FvFileObj.seek(0)
212                    Buffer.write(FvFileObj.read())
213                    # FV alignment position.
214                    FvAlignmentValue = 1 << (ord(FvHeaderBuffer[0x2E:0x2F]) & 0x1F)
215                    if FvAlignmentValue >= 0x400:
216                        if FvAlignmentValue >= 0x100000:
217                            if FvAlignmentValue >= 0x1000000:
218                            #The max alignment supported by FFS is 16M.
219                                self.FvAlignment = "16M"
220                            else:
221                                self.FvAlignment = str(FvAlignmentValue // 0x100000) + "M"
222                        else:
223                            self.FvAlignment = str(FvAlignmentValue // 0x400) + "K"
224                    else:
225                        # FvAlignmentValue is less than 1K
226                        self.FvAlignment = str (FvAlignmentValue)
227                    FvFileObj.close()
228                    GenFdsGlobalVariable.ImageBinDict[self.UiFvName.upper() + 'fv'] = FvOutputFile
229                    GenFdsGlobalVariable.LargeFileInFvFlags.pop()
230                else:
231                    GenFdsGlobalVariable.ErrorLogger("Invalid FV file %s." % self.UiFvName)
232            else:
233                GenFdsGlobalVariable.ErrorLogger("Failed to generate %s FV file." %self.UiFvName)
234        return FvOutputFile
235
236    ## _GetBlockSize()
237    #
238    #   Calculate FV's block size
239    #   Inherit block size from FD if no block size specified in FV
240    #
241    def _GetBlockSize(self):
242        if self.BlockSizeList:
243            return True
244
245        for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
246            for RegionObj in FdObj.RegionList:
247                if RegionObj.RegionType != BINARY_FILE_TYPE_FV:
248                    continue
249                for RegionData in RegionObj.RegionDataList:
250                    #
251                    # Found the FD and region that contain this FV
252                    #
253                    if self.UiFvName.upper() == RegionData.upper():
254                        RegionObj.BlockInfoOfRegion(FdObj.BlockSizeList, self)
255                        if self.BlockSizeList:
256                            return True
257        return False
258
259    ## _InitializeInf()
260    #
261    #   Initialize the inf file to create FV
262    #
263    #   @param  self        The object pointer
264    #   @param  BaseAddress base address of FV
265    #   @param  BlockSize   block size of FV
266    #   @param  BlockNum    How many blocks in FV
267    #   @param  ErasePolarity      Flash erase polarity
268    #
269    def _InitializeInf (self, BaseAddress = None, BlockSize= None, BlockNum = None, ErasePloarity='1'):
270        #
271        # Create FV inf file
272        #
273        self.InfFileName = os.path.join(GenFdsGlobalVariable.FvDir,
274                                   self.UiFvName + '.inf')
275        self.FvInfFile = []
276
277        #
278        # Add [Options]
279        #
280        self.FvInfFile.append("[options]" + TAB_LINE_BREAK)
281        if BaseAddress is not None:
282            self.FvInfFile.append("EFI_BASE_ADDRESS = " + \
283                                       BaseAddress          + \
284                                       TAB_LINE_BREAK)
285
286        if BlockSize is not None:
287            self.FvInfFile.append("EFI_BLOCK_SIZE = " + \
288                                      '0x%X' %BlockSize    + \
289                                      TAB_LINE_BREAK)
290            if BlockNum is not None:
291                self.FvInfFile.append("EFI_NUM_BLOCKS   = "  + \
292                                      ' 0x%X' %BlockNum    + \
293                                      TAB_LINE_BREAK)
294        else:
295            if self.BlockSizeList == []:
296                if not self._GetBlockSize():
297                    #set default block size is 1
298                    self.FvInfFile.append("EFI_BLOCK_SIZE  = 0x1" + TAB_LINE_BREAK)
299
300            for BlockSize in self.BlockSizeList:
301                if BlockSize[0] is not None:
302                    self.FvInfFile.append("EFI_BLOCK_SIZE  = "  + \
303                                          '0x%X' %BlockSize[0]    + \
304                                          TAB_LINE_BREAK)
305
306                if BlockSize[1] is not None:
307                    self.FvInfFile.append("EFI_NUM_BLOCKS   = "  + \
308                                          ' 0x%X' %BlockSize[1]    + \
309                                          TAB_LINE_BREAK)
310
311        if self.BsBaseAddress is not None:
312            self.FvInfFile.append('EFI_BOOT_DRIVER_BASE_ADDRESS = ' + \
313                                       '0x%X' %self.BsBaseAddress)
314        if self.RtBaseAddress is not None:
315            self.FvInfFile.append('EFI_RUNTIME_DRIVER_BASE_ADDRESS = ' + \
316                                      '0x%X' %self.RtBaseAddress)
317        #
318        # Add attribute
319        #
320        self.FvInfFile.append("[attributes]" + TAB_LINE_BREAK)
321
322        self.FvInfFile.append("EFI_ERASE_POLARITY   = "       + \
323                                          ' %s' %ErasePloarity    + \
324                                          TAB_LINE_BREAK)
325        if not (self.FvAttributeDict is None):
326            for FvAttribute in self.FvAttributeDict.keys():
327                if FvAttribute == "FvUsedSizeEnable":
328                    if self.FvAttributeDict[FvAttribute].upper() in ('TRUE', '1'):
329                        self.UsedSizeEnable = True
330                    continue
331                self.FvInfFile.append("EFI_"            + \
332                                          FvAttribute       + \
333                                          ' = '             + \
334                                          self.FvAttributeDict[FvAttribute] + \
335                                          TAB_LINE_BREAK )
336        if self.FvAlignment is not None:
337            self.FvInfFile.append("EFI_FVB2_ALIGNMENT_"     + \
338                                       self.FvAlignment.strip() + \
339                                       " = TRUE"                + \
340                                       TAB_LINE_BREAK)
341
342        #
343        # Generate FV extension header file
344        #
345        if not self.FvNameGuid:
346            if len(self.FvExtEntryType) > 0 or self.UsedSizeEnable:
347                GenFdsGlobalVariable.ErrorLogger("FV Extension Header Entries declared for %s with no FvNameGuid declaration." % (self.UiFvName))
348        else:
349            TotalSize = 16 + 4
350            Buffer = bytearray()
351            if self.UsedSizeEnable:
352                TotalSize += (4 + 4)
353                ## define EFI_FV_EXT_TYPE_USED_SIZE_TYPE 0x03
354                #typedef  struct
355                # {
356                #    EFI_FIRMWARE_VOLUME_EXT_ENTRY Hdr;
357                #    UINT32 UsedSize;
358                # } EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE;
359                Buffer += pack('HHL', 8, 3, 0)
360
361            if self.FvNameString == 'TRUE':
362                #
363                # Create EXT entry for FV UI name
364                # This GUID is used: A67DF1FA-8DE8-4E98-AF09-4BDF2EFFBC7C
365                #
366                FvUiLen = len(self.UiFvName)
367                TotalSize += (FvUiLen + 16 + 4)
368                Guid = FV_UI_EXT_ENTY_GUID.split('-')
369                #
370                # Layout:
371                #   EFI_FIRMWARE_VOLUME_EXT_ENTRY: size 4
372                #   GUID: size 16
373                #   FV UI name
374                #
375                Buffer += (pack('HH', (FvUiLen + 16 + 4), 0x0002)
376                           + PackGUID(Guid)
377                           + self.UiFvName.encode('utf-8'))
378
379            for Index in range (0, len(self.FvExtEntryType)):
380                if self.FvExtEntryType[Index] == 'FILE':
381                    # check if the path is absolute or relative
382                    if os.path.isabs(self.FvExtEntryData[Index]):
383                        FileFullPath = os.path.normpath(self.FvExtEntryData[Index])
384                    else:
385                        FileFullPath = os.path.normpath(os.path.join(GenFdsGlobalVariable.WorkSpaceDir, self.FvExtEntryData[Index]))
386                    # check if the file path exists or not
387                    if not os.path.isfile(FileFullPath):
388                        GenFdsGlobalVariable.ErrorLogger("Error opening FV Extension Header Entry file %s." % (self.FvExtEntryData[Index]))
389                    FvExtFile = open (FileFullPath, 'rb')
390                    FvExtFile.seek(0, 2)
391                    Size = FvExtFile.tell()
392                    if Size >= 0x10000:
393                        GenFdsGlobalVariable.ErrorLogger("The size of FV Extension Header Entry file %s exceeds 0x10000." % (self.FvExtEntryData[Index]))
394                    TotalSize += (Size + 4)
395                    FvExtFile.seek(0)
396                    Buffer += pack('HH', (Size + 4), int(self.FvExtEntryTypeValue[Index], 16))
397                    Buffer += FvExtFile.read()
398                    FvExtFile.close()
399                if self.FvExtEntryType[Index] == 'DATA':
400                    ByteList = self.FvExtEntryData[Index].split(',')
401                    Size = len (ByteList)
402                    if Size >= 0x10000:
403                        GenFdsGlobalVariable.ErrorLogger("The size of FV Extension Header Entry data %s exceeds 0x10000." % (self.FvExtEntryData[Index]))
404                    TotalSize += (Size + 4)
405                    Buffer += pack('HH', (Size + 4), int(self.FvExtEntryTypeValue[Index], 16))
406                    for Index1 in range (0, Size):
407                        Buffer += pack('B', int(ByteList[Index1], 16))
408
409            Guid = self.FvNameGuid.split('-')
410            Buffer = PackGUID(Guid) + pack('=L', TotalSize) + Buffer
411
412            #
413            # Generate FV extension header file if the total size is not zero
414            #
415            if TotalSize > 0:
416                FvExtHeaderFileName = os.path.join(GenFdsGlobalVariable.FvDir, self.UiFvName + '.ext')
417                FvExtHeaderFile = BytesIO()
418                FvExtHeaderFile.write(Buffer)
419                Changed = SaveFileOnChange(FvExtHeaderFileName, FvExtHeaderFile.getvalue(), True)
420                FvExtHeaderFile.close()
421                if Changed:
422                  if os.path.exists (self.InfFileName):
423                    os.remove (self.InfFileName)
424                self.FvInfFile.append("EFI_FV_EXT_HEADER_FILE_NAME = "      + \
425                                           FvExtHeaderFileName                  + \
426                                           TAB_LINE_BREAK)
427
428        #
429        # Add [Files]
430        #
431        self.FvInfFile.append("[files]" + TAB_LINE_BREAK)
432