1## @file
2# generate flash image
3#
4#  Copyright (c) 2007 - 2019, 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 print_function
13from __future__ import absolute_import
14from re import compile
15from optparse import OptionParser
16from sys import exit
17from glob import glob
18from struct import unpack
19from linecache import getlines
20from io import BytesIO
21
22import Common.LongFilePathOs as os
23from Common.TargetTxtClassObject import TargetTxtClassObject
24from Common.DataType import *
25import Common.GlobalData as GlobalData
26from Common import EdkLogger
27from Common.StringUtils import NormPath
28from Common.Misc import DirCache, PathClass, GuidStructureStringToGuidString
29from Common.Misc import SaveFileOnChange, ClearDuplicatedInf
30from Common.BuildVersion import gBUILD_VERSION
31from Common.MultipleWorkspace import MultipleWorkspace as mws
32from Common.BuildToolError import FatalError, GENFDS_ERROR, CODE_ERROR, FORMAT_INVALID, RESOURCE_NOT_AVAILABLE, FILE_NOT_FOUND, OPTION_MISSING, FORMAT_NOT_SUPPORTED, OPTION_VALUE_INVALID, PARAMETER_INVALID
33from Workspace.WorkspaceDatabase import WorkspaceDatabase
34
35from .FdfParser import FdfParser, Warning
36from .GenFdsGlobalVariable import GenFdsGlobalVariable
37from .FfsFileStatement import FileStatement
38import Common.DataType as DataType
39from struct import Struct
40
41## Version and Copyright
42versionNumber = "1.0" + ' ' + gBUILD_VERSION
43__version__ = "%prog Version " + versionNumber
44__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation  All rights reserved."
45
46## Tool entrance method
47#
48# This method mainly dispatch specific methods per the command line options.
49# If no error found, return zero value so the caller of this tool can know
50# if it's executed successfully or not.
51#
52#   @retval 0     Tool was successful
53#   @retval 1     Tool failed
54#
55def main():
56    global Options
57    Options = myOptionParser()
58    EdkLogger.Initialize()
59    return GenFdsApi(OptionsToCommandDict(Options))
60
61def resetFdsGlobalVariable():
62    GenFdsGlobalVariable.FvDir = ''
63    GenFdsGlobalVariable.OutputDirDict = {}
64    GenFdsGlobalVariable.BinDir = ''
65    # will be FvDir + os.sep + 'Ffs'
66    GenFdsGlobalVariable.FfsDir = ''
67    GenFdsGlobalVariable.FdfParser = None
68    GenFdsGlobalVariable.LibDir = ''
69    GenFdsGlobalVariable.WorkSpace = None
70    GenFdsGlobalVariable.WorkSpaceDir = ''
71    GenFdsGlobalVariable.ConfDir = ''
72    GenFdsGlobalVariable.OutputDirFromDscDict = {}
73    GenFdsGlobalVariable.TargetName = ''
74    GenFdsGlobalVariable.ToolChainTag = ''
75    GenFdsGlobalVariable.RuleDict = {}
76    GenFdsGlobalVariable.ArchList = None
77    GenFdsGlobalVariable.ActivePlatform = None
78    GenFdsGlobalVariable.FvAddressFileName = ''
79    GenFdsGlobalVariable.VerboseMode = False
80    GenFdsGlobalVariable.DebugLevel = -1
81    GenFdsGlobalVariable.SharpCounter = 0
82    GenFdsGlobalVariable.SharpNumberPerLine = 40
83    GenFdsGlobalVariable.FdfFile = ''
84    GenFdsGlobalVariable.FdfFileTimeStamp = 0
85    GenFdsGlobalVariable.FixedLoadAddress = False
86    GenFdsGlobalVariable.PlatformName = ''
87
88    GenFdsGlobalVariable.BuildRuleFamily = DataType.TAB_COMPILER_MSFT
89    GenFdsGlobalVariable.ToolChainFamily = DataType.TAB_COMPILER_MSFT
90    GenFdsGlobalVariable.__BuildRuleDatabase = None
91    GenFdsGlobalVariable.GuidToolDefinition = {}
92    GenFdsGlobalVariable.FfsCmdDict = {}
93    GenFdsGlobalVariable.SecCmdList = []
94    GenFdsGlobalVariable.CopyList   = []
95    GenFdsGlobalVariable.ModuleFile = ''
96    GenFdsGlobalVariable.EnableGenfdsMultiThread = False
97
98    GenFdsGlobalVariable.LargeFileInFvFlags = []
99    GenFdsGlobalVariable.EFI_FIRMWARE_FILE_SYSTEM3_GUID = '5473C07A-3DCB-4dca-BD6F-1E9689E7349A'
100    GenFdsGlobalVariable.LARGE_FILE_SIZE = 0x1000000
101
102    GenFdsGlobalVariable.SectionHeader = Struct("3B 1B")
103
104    # FvName, FdName, CapName in FDF, Image file name
105    GenFdsGlobalVariable.ImageBinDict = {}
106
107def GenFdsApi(FdsCommandDict, WorkSpaceDataBase=None):
108    global Workspace
109    Workspace = ""
110    ArchList = None
111    ReturnCode = 0
112    resetFdsGlobalVariable()
113
114    try:
115        if FdsCommandDict.get("verbose"):
116            EdkLogger.SetLevel(EdkLogger.VERBOSE)
117            GenFdsGlobalVariable.VerboseMode = True
118
119        if FdsCommandDict.get("FixedAddress"):
120            GenFdsGlobalVariable.FixedLoadAddress = True
121
122        if FdsCommandDict.get("quiet"):
123            EdkLogger.SetLevel(EdkLogger.QUIET)
124        if FdsCommandDict.get("debug"):
125            EdkLogger.SetLevel(FdsCommandDict.get("debug") + 1)
126            GenFdsGlobalVariable.DebugLevel = FdsCommandDict.get("debug")
127        else:
128            EdkLogger.SetLevel(EdkLogger.INFO)
129
130        if not FdsCommandDict.get("Workspace",os.environ.get('WORKSPACE')):
131            EdkLogger.error("GenFds", OPTION_MISSING, "WORKSPACE not defined",
132                            ExtraData="Please use '-w' switch to pass it or set the WORKSPACE environment variable.")
133        elif not os.path.exists(FdsCommandDict.get("Workspace",os.environ.get('WORKSPACE'))):
134            EdkLogger.error("GenFds", PARAMETER_INVALID, "WORKSPACE is invalid",
135                            ExtraData="Please use '-w' switch to pass it or set the WORKSPACE environment variable.")
136        else:
137            Workspace = os.path.normcase(FdsCommandDict.get("Workspace",os.environ.get('WORKSPACE')))
138            GenFdsGlobalVariable.WorkSpaceDir = Workspace
139            if FdsCommandDict.get("debug"):
140                GenFdsGlobalVariable.VerboseLogger("Using Workspace:" + Workspace)
141            if FdsCommandDict.get("GenfdsMultiThread"):
142                GenFdsGlobalVariable.EnableGenfdsMultiThread = True
143        os.chdir(GenFdsGlobalVariable.WorkSpaceDir)
144
145        # set multiple workspace
146        PackagesPath = os.getenv("PACKAGES_PATH")
147        mws.setWs(GenFdsGlobalVariable.WorkSpaceDir, PackagesPath)
148
149        if FdsCommandDict.get("fdf_file"):
150            FdfFilename = FdsCommandDict.get("fdf_file")[0].Path
151            FdfFilename = GenFdsGlobalVariable.ReplaceWorkspaceMacro(FdfFilename)
152
153            if FdfFilename[0:2] == '..':
154                FdfFilename = os.path.realpath(FdfFilename)
155            if not os.path.isabs(FdfFilename):
156                FdfFilename = mws.join(GenFdsGlobalVariable.WorkSpaceDir, FdfFilename)
157            if not os.path.exists(FdfFilename):
158                EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=FdfFilename)
159
160            GenFdsGlobalVariable.FdfFile = FdfFilename
161            GenFdsGlobalVariable.FdfFileTimeStamp = os.path.getmtime(FdfFilename)
162        else:
163            EdkLogger.error("GenFds", OPTION_MISSING, "Missing FDF filename")
164
165        if FdsCommandDict.get("build_target"):
166            GenFdsGlobalVariable.TargetName = FdsCommandDict.get("build_target")
167
168        if FdsCommandDict.get("toolchain_tag"):
169            GenFdsGlobalVariable.ToolChainTag = FdsCommandDict.get("toolchain_tag")
170
171        if FdsCommandDict.get("active_platform"):
172            ActivePlatform = FdsCommandDict.get("active_platform")
173            ActivePlatform = GenFdsGlobalVariable.ReplaceWorkspaceMacro(ActivePlatform)
174
175            if ActivePlatform[0:2] == '..':
176                ActivePlatform = os.path.realpath(ActivePlatform)
177
178            if not os.path.isabs (ActivePlatform):
179                ActivePlatform = mws.join(GenFdsGlobalVariable.WorkSpaceDir, ActivePlatform)
180
181            if not os.path.exists(ActivePlatform):
182                EdkLogger.error("GenFds", FILE_NOT_FOUND, "ActivePlatform doesn't exist!")
183        else:
184            EdkLogger.error("GenFds", OPTION_MISSING, "Missing active platform")
185
186        GenFdsGlobalVariable.ActivePlatform = PathClass(NormPath(ActivePlatform))
187
188        if FdsCommandDict.get("conf_directory"):
189            # Get alternate Conf location, if it is absolute, then just use the absolute directory name
190            ConfDirectoryPath = os.path.normpath(FdsCommandDict.get("conf_directory"))
191            if ConfDirectoryPath.startswith('"'):
192                ConfDirectoryPath = ConfDirectoryPath[1:]
193            if ConfDirectoryPath.endswith('"'):
194                ConfDirectoryPath = ConfDirectoryPath[:-1]
195            if not os.path.isabs(ConfDirectoryPath):
196                # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE
197                # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf
198                ConfDirectoryPath = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, ConfDirectoryPath)
199        else:
200            if "CONF_PATH" in os.environ:
201                ConfDirectoryPath = os.path.normcase(os.environ["CONF_PATH"])
202            else:
203                # Get standard WORKSPACE/Conf, use the absolute path to the WORKSPACE/Conf
204                ConfDirectoryPath = mws.join(GenFdsGlobalVariable.WorkSpaceDir, 'Conf')
205        GenFdsGlobalVariable.ConfDir = ConfDirectoryPath
206        if not GlobalData.gConfDirectory:
207            GlobalData.gConfDirectory = GenFdsGlobalVariable.ConfDir
208        BuildConfigurationFile = os.path.normpath(os.path.join(ConfDirectoryPath, "target.txt"))
209        if os.path.isfile(BuildConfigurationFile) == True:
210            TargetTxt = TargetTxtClassObject()
211            TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)
212            # if no build target given in command line, get it from target.txt
213            if not GenFdsGlobalVariable.TargetName:
214                BuildTargetList = TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET]
215                if len(BuildTargetList) != 1:
216                    EdkLogger.error("GenFds", OPTION_VALUE_INVALID, ExtraData="Only allows one instance for Target.")
217                GenFdsGlobalVariable.TargetName = BuildTargetList[0]
218
219            # if no tool chain given in command line, get it from target.txt
220            if not GenFdsGlobalVariable.ToolChainTag:
221                ToolChainList = TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG]
222                if ToolChainList is None or len(ToolChainList) == 0:
223                    EdkLogger.error("GenFds", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.")
224                if len(ToolChainList) != 1:
225                    EdkLogger.error("GenFds", OPTION_VALUE_INVALID, ExtraData="Only allows one instance for ToolChain.")
226                GenFdsGlobalVariable.ToolChainTag = ToolChainList[0]
227        else:
228            EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)
229
230        #Set global flag for build mode
231        GlobalData.gIgnoreSource = FdsCommandDict.get("IgnoreSources")
232
233        if FdsCommandDict.get("macro"):
234            for Pair in FdsCommandDict.get("macro"):
235                if Pair.startswith('"'):
236                    Pair = Pair[1:]
237                if Pair.endswith('"'):
238                    Pair = Pair[:-1]
239                List = Pair.split('=')
240                if len(List) == 2:
241                    if not List[1].strip():
242                        EdkLogger.error("GenFds", OPTION_VALUE_INVALID, ExtraData="No Value given for Macro %s" %List[0])
243                    if List[0].strip() in ["WORKSPACE", "TARGET", "TOOLCHAIN"]:
244                        GlobalData.gGlobalDefines[List[0].strip()] = List[1].strip()
245                    else:
246                        GlobalData.gCommandLineDefines[List[0].strip()] = List[1].strip()
247                else:
248                    GlobalData.gCommandLineDefines[List[0].strip()] = "TRUE"
249        os.environ["WORKSPACE"] = Workspace
250
251        # Use the -t and -b option as gGlobalDefines's TOOLCHAIN and TARGET if they are not defined
252        if "TARGET" not in GlobalData.gGlobalDefines:
253            GlobalData.gGlobalDefines["TARGET"] = GenFdsGlobalVariable.TargetName
254        if "TOOLCHAIN" not in GlobalData.gGlobalDefines:
255            GlobalData.gGlobalDefines["TOOLCHAIN"] = GenFdsGlobalVariable.ToolChainTag
256        if "TOOL_CHAIN_TAG" not in GlobalData.gGlobalDefines:
257            GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = GenFdsGlobalVariable.ToolChainTag
258
259        """call Workspace build create database"""
260        GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))
261
262        if WorkSpaceDataBase:
263            BuildWorkSpace = WorkSpaceDataBase
264        else:
265            BuildWorkSpace = WorkspaceDatabase()
266        #
267        # Get files real name in workspace dir
268        #
269        GlobalData.gAllFiles = DirCache(Workspace)
270        GlobalData.gWorkspace = Workspace
271
272        if FdsCommandDict.get("build_architecture_list"):
273            ArchList = FdsCommandDict.get("build_architecture_list").split(',')
274        else:
275            ArchList = BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, TAB_COMMON, FdsCommandDict.get("build_target"), FdsCommandDict.get("toolchain_tag")].SupArchList
276
277        TargetArchList = set(BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, TAB_COMMON, FdsCommandDict.get("build_target"), FdsCommandDict.get("toolchain_tag")].SupArchList) & set(ArchList)
278        if len(TargetArchList) == 0:
279            EdkLogger.error("GenFds", GENFDS_ERROR, "Target ARCH %s not in platform supported ARCH %s" % (str(ArchList), str(BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, TAB_COMMON].SupArchList)))
280
281        for Arch in ArchList:
282            GenFdsGlobalVariable.OutputDirFromDscDict[Arch] = NormPath(BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, FdsCommandDict.get("build_target"), FdsCommandDict.get("toolchain_tag")].OutputDirectory)
283
284        # assign platform name based on last entry in ArchList
285        GenFdsGlobalVariable.PlatformName = BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, ArchList[-1], FdsCommandDict.get("build_target"), FdsCommandDict.get("toolchain_tag")].PlatformName
286
287        if FdsCommandDict.get("platform_build_directory"):
288            OutputDirFromCommandLine = GenFdsGlobalVariable.ReplaceWorkspaceMacro(FdsCommandDict.get("platform_build_directory"))
289            if not os.path.isabs (OutputDirFromCommandLine):
290                OutputDirFromCommandLine = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, OutputDirFromCommandLine)
291            for Arch in ArchList:
292                GenFdsGlobalVariable.OutputDirDict[Arch] = OutputDirFromCommandLine
293        else:
294            for Arch in ArchList:
295                GenFdsGlobalVariable.OutputDirDict[Arch] = os.path.join(GenFdsGlobalVariable.OutputDirFromDscDict[Arch], GenFdsGlobalVariable.TargetName + '_' + GenFdsGlobalVariable.ToolChainTag)
296
297        for Key in GenFdsGlobalVariable.OutputDirDict:
298            OutputDir = GenFdsGlobalVariable.OutputDirDict[Key]
299            if OutputDir[0:2] == '..':
300                OutputDir = os.path.realpath(OutputDir)
301
302            if OutputDir[1] != ':':
303                OutputDir = os.path.join (GenFdsGlobalVariable.WorkSpaceDir, OutputDir)
304
305            if not os.path.exists(OutputDir):
306                EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=OutputDir)
307            GenFdsGlobalVariable.OutputDirDict[Key] = OutputDir
308
309        """ Parse Fdf file, has to place after build Workspace as FDF may contain macros from DSC file """
310        if WorkSpaceDataBase:
311            FdfParserObj = GlobalData.gFdfParser
312        else:
313            FdfParserObj = FdfParser(FdfFilename)
314            FdfParserObj.ParseFile()
315
316        if FdfParserObj.CycleReferenceCheck():
317            EdkLogger.error("GenFds", FORMAT_NOT_SUPPORTED, "Cycle Reference Detected in FDF file")
318
319        if FdsCommandDict.get("fd"):
320            if FdsCommandDict.get("fd")[0].upper() in FdfParserObj.Profile.FdDict:
321                GenFds.OnlyGenerateThisFd = FdsCommandDict.get("fd")[0]
322            else:
323                EdkLogger.error("GenFds", OPTION_VALUE_INVALID,
324                                "No such an FD in FDF file: %s" % FdsCommandDict.get("fd")[0])
325
326        if FdsCommandDict.get("fv"):
327            if FdsCommandDict.get("fv")[0].upper() in FdfParserObj.Profile.FvDict:
328                GenFds.OnlyGenerateThisFv = FdsCommandDict.get("fv")[0]
329            else:
330                EdkLogger.error("GenFds", OPTION_VALUE_INVALID,
331                                "No such an FV in FDF file: %s" % FdsCommandDict.get("fv")[0])
332
333        if FdsCommandDict.get("cap"):
334            if FdsCommandDict.get("cap")[0].upper() in FdfParserObj.Profile.CapsuleDict:
335                GenFds.OnlyGenerateThisCap = FdsCommandDict.get("cap")[0]
336            else:
337                EdkLogger.error("GenFds", OPTION_VALUE_INVALID,
338                                "No such a Capsule in FDF file: %s" % FdsCommandDict.get("cap")[0])
339
340        GenFdsGlobalVariable.WorkSpace = BuildWorkSpace
341        if ArchList:
342            GenFdsGlobalVariable.ArchList = ArchList
343
344        # Dsc Build Data will handle Pcd Settings from CommandLine.
345
346        """Modify images from build output if the feature of loading driver at fixed address is on."""
347        if GenFdsGlobalVariable.FixedLoadAddress:
348            GenFds.PreprocessImage(BuildWorkSpace, GenFdsGlobalVariable.ActivePlatform)
349
350        # Record the FV Region info that may specific in the FD
351        if FdfParserObj.Profile.FvDict and FdfParserObj.Profile.FdDict:
352            for FvObj in FdfParserObj.Profile.FvDict.values():
353                for FdObj in FdfParserObj.Profile.FdDict.values():
354                    for RegionObj in FdObj.RegionList:
355                        if RegionObj.RegionType != BINARY_FILE_TYPE_FV:
356                            continue
357                        for RegionData in RegionObj.RegionDataList:
358                            if FvObj.UiFvName.upper() == RegionData.upper():
359                                if not FvObj.BaseAddress:
360                                    FvObj.BaseAddress = '0x%x' % (int(FdObj.BaseAddress, 0) + RegionObj.Offset)
361                                if FvObj.FvRegionInFD:
362                                    if FvObj.FvRegionInFD != RegionObj.Size:
363                                        EdkLogger.error("GenFds", FORMAT_INVALID, "The FV %s's region is specified in multiple FD with different value." %FvObj.UiFvName)
364                                else:
365                                    FvObj.FvRegionInFD = RegionObj.Size
366                                    RegionObj.BlockInfoOfRegion(FdObj.BlockSizeList, FvObj)
367
368        """Call GenFds"""
369        GenFds.GenFd('', FdfParserObj, BuildWorkSpace, ArchList)
370
371        """Generate GUID cross reference file"""
372        GenFds.GenerateGuidXRefFile(BuildWorkSpace, ArchList, FdfParserObj)
373
374        """Display FV space info."""
375        GenFds.DisplayFvSpaceInfo(FdfParserObj)
376
377    except Warning as X:
378        EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)
379        ReturnCode = FORMAT_INVALID
380    except FatalError as X:
381        if FdsCommandDict.get("debug") is not None:
382            import traceback
383            EdkLogger.quiet(traceback.format_exc())
384        ReturnCode = X.args[0]
385    except:
386        import traceback
387        EdkLogger.error(
388                    "\nPython",
389                    CODE_ERROR,
390                    "Tools code failure",
391                    ExtraData="Please send email to %s for help, attaching following call stack trace!\n" % MSG_EDKII_MAIL_ADDR,
392                    RaiseError=False
393                    )
394        EdkLogger.quiet(traceback.format_exc())
395        ReturnCode = CODE_ERROR
396    finally:
397        ClearDuplicatedInf()
398    return ReturnCode
399
400def OptionsToCommandDict(Options):
401    FdsCommandDict = {}
402    FdsCommandDict["verbose"] = Options.verbose
403    FdsCommandDict["FixedAddress"] = Options.FixedAddress
404    FdsCommandDict["quiet"] = Options.quiet
405    FdsCommandDict["debug"] = Options.debug
406    FdsCommandDict["Workspace"] = Options.Workspace
407    FdsCommandDict["GenfdsMultiThread"] = Options.GenfdsMultiThread
408    FdsCommandDict["fdf_file"] = [PathClass(Options.filename)] if Options.filename else []
409    FdsCommandDict["build_target"] = Options.BuildTarget
410    FdsCommandDict["toolchain_tag"] = Options.ToolChain
411    FdsCommandDict["active_platform"] = Options.activePlatform
412    FdsCommandDict["OptionPcd"] = Options.OptionPcd
413    FdsCommandDict["conf_directory"] = Options.ConfDirectory
414    FdsCommandDict["IgnoreSources"] = Options.IgnoreSources
415    FdsCommandDict["macro"] = Options.Macros
416    FdsCommandDict["build_architecture_list"] = Options.archList
417    FdsCommandDict["platform_build_directory"] = Options.outputDir
418    FdsCommandDict["fd"] = [Options.uiFdName] if Options.uiFdName else []
419    FdsCommandDict["fv"] = [Options.uiFvName] if Options.uiFvName else []
420    FdsCommandDict["cap"] = [Options.uiCapName] if Options.uiCapName else []
421    return FdsCommandDict
422
423
424gParamCheck = []
425def SingleCheckCallback(option, opt_str, value, parser):
426    if option not in gParamCheck:
427        setattr(parser.values, option.dest, value)
428        gParamCheck.append(option)
429    else:
430        parser.error("Option %s only allows one instance in command line!" % option)
431
432## Parse command line options
433#
434# Using standard Python module optparse to parse command line option of this tool.
435#
436#   @retval Opt   A optparse.Values object containing the parsed options
437#
438def myOptionParser():
439    usage = "%prog [options] -f input_file -a arch_list -b build_target -p active_platform -t tool_chain_tag -D \"MacroName [= MacroValue]\""
440    Parser = OptionParser(usage=usage, description=__copyright__, version="%prog " + str(versionNumber))
441    Parser.add_option("-f", "--file", dest="filename", type="string", help="Name of FDF file to convert", action="callback", callback=SingleCheckCallback)
442    Parser.add_option("-a", "--arch", dest="archList", help="comma separated list containing one or more of: IA32, X64, IPF, ARM, AARCH64 or EBC which should be built, overrides target.txt?s TARGET_ARCH")
443    Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
444    Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed.")
445    Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
446    Parser.add_option("-p", "--platform", type="string", dest="activePlatform", help="Set the ACTIVE_PLATFORM, overrides target.txt ACTIVE_PLATFORM setting.",
447                      action="callback", callback=SingleCheckCallback)
448    Parser.add_option("-w", "--workspace", type="string", dest="Workspace", default=os.environ.get('WORKSPACE'), help="Set the WORKSPACE",
449                      action="callback", callback=SingleCheckCallback)
450    Parser.add_option("-o", "--outputDir", type="string", dest="outputDir", help="Name of Build Output directory",
451                      action="callback", callback=SingleCheckCallback)
452    Parser.add_option("-r", "--rom_image", dest="uiFdName", help="Build the image using the [FD] section named by FdUiName.")
453    Parser.add_option("-i", "--FvImage", dest="uiFvName", help="Build the FV image using the [FV] section named by UiFvName")
454    Parser.add_option("-C", "--CapsuleImage", dest="uiCapName", help="Build the Capsule image using the [Capsule] section named by UiCapName")
455    Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Set the build TARGET, overrides target.txt TARGET setting.",
456                      action="callback", callback=SingleCheckCallback)
457    Parser.add_option("-t", "--tagname", type="string", dest="ToolChain", help="Using the tools: TOOL_CHAIN_TAG name to build the platform.",
458                      action="callback", callback=SingleCheckCallback)
459    Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")
460    Parser.add_option("-s", "--specifyaddress", dest="FixedAddress", action="store_true", type=None, help="Specify driver load address.")
461    Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")
462    Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")
463    Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")
464    Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")
465
466    Options, _ = Parser.parse_args()
467    return Options
468
469## The class implementing the EDK2 flash image generation process
470#
471#   This process includes:
472#       1. Collect workspace information, includes platform and module information
473#       2. Call methods of Fd class to generate FD
474#       3. Call methods of Fv class to generate FV that not belong to FD
475#
476class GenFds(object):
477    FdfParsef = None
478    OnlyGenerateThisFd = None
479    OnlyGenerateThisFv = None
480    OnlyGenerateThisCap = None
481
482    ## GenFd()
483    #
484    #   @param  OutputDir           Output directory
485    #   @param  FdfParserObject     FDF contents parser
486    #   @param  Workspace           The directory of workspace
487    #   @param  ArchList            The Arch list of platform
488    #
489    @staticmethod
490    def GenFd (OutputDir, FdfParserObject, WorkSpace, ArchList):
491        GenFdsGlobalVariable.SetDir ('', FdfParserObject, WorkSpace, ArchList)
492
493        GenFdsGlobalVariable.VerboseLogger(" Generate all Fd images and their required FV and Capsule images!")
494        if GenFds.OnlyGenerateThisCap is not None and GenFds.OnlyGenerateThisCap.upper() in GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict:
495            CapsuleObj = GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict[GenFds.OnlyGenerateThisCap.upper()]
496            if CapsuleObj is not None:
497                CapsuleObj.GenCapsule()
498                return
499
500        if GenFds.OnlyGenerateThisFd is not None and GenFds.OnlyGenerateThisFd.upper() in GenFdsGlobalVariable.FdfParser.Profile.FdDict:
501            FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict[GenFds.OnlyGenerateThisFd.upper()]
502            if FdObj is not None:
503                FdObj.GenFd()
504                return
505        elif GenFds.OnlyGenerateThisFd is None and GenFds.OnlyGenerateThisFv is None:
506            for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
507                FdObj.GenFd()
508
509        GenFdsGlobalVariable.VerboseLogger("\n Generate other FV images! ")
510        if GenFds.OnlyGenerateThisFv is not None and GenFds.OnlyGenerateThisFv.upper() in GenFdsGlobalVariable.FdfParser.Profile.FvDict:
511            FvObj = GenFdsGlobalVariable.FdfParser.Profile.FvDict[GenFds.OnlyGenerateThisFv.upper()]
512            if FvObj is not None:
513                Buffer = BytesIO()
514                FvObj.AddToBuffer(Buffer)
515                Buffer.close()
516                return
517        elif GenFds.OnlyGenerateThisFv is None:
518            for FvObj in GenFdsGlobalVariable.FdfParser.Profile.FvDict.values():
519                Buffer = BytesIO()
520                FvObj.AddToBuffer(Buffer)
521                Buffer.close()
522
523        if GenFds.OnlyGenerateThisFv is None and GenFds.OnlyGenerateThisFd is None and GenFds.OnlyGenerateThisCap is None:
524            if GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict != {}:
525                GenFdsGlobalVariable.VerboseLogger("\n Generate other Capsule images!")
526                for CapsuleObj in GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict.values():
527                    CapsuleObj.GenCapsule()
528
529            if GenFdsGlobalVariable.FdfParser.Profile.OptRomDict != {}:
530                GenFdsGlobalVariable.VerboseLogger("\n Generate all Option ROM!")
531                for OptRomObj in GenFdsGlobalVariable.FdfParser.Profile.OptRomDict.values():
532                    OptRomObj.AddToBuffer(None)
533
534    @staticmethod
535    def GenFfsMakefile(OutputDir, FdfParserObject, WorkSpace, ArchList, GlobalData):
536        GenFdsGlobalVariable.SetEnv(FdfParserObject, WorkSpace, ArchList, GlobalData)
537        for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
538            FdObj.GenFd(Flag=True)
539
540        for FvObj in GenFdsGlobalVariable.FdfParser.Profile.FvDict.values():
541            FvObj.AddToBuffer(Buffer=None, Flag=True)
542
543        if GenFdsGlobalVariable.FdfParser.Profile.OptRomDict != {}:
544            for OptRomObj in GenFdsGlobalVariable.FdfParser.Profile.OptRomDict.values():
545                OptRomObj.AddToBuffer(Buffer=None, Flag=True)
546
547        return GenFdsGlobalVariable.FfsCmdDict
548
549    ## GetFvBlockSize()
550    #
551    #   @param  FvObj           Whose block size to get
552    #   @retval int             Block size value
553    #
554    @staticmethod
555    def GetFvBlockSize(FvObj):
556        DefaultBlockSize = 0x1
557        FdObj = None
558        if GenFds.OnlyGenerateThisFd is not None and GenFds.OnlyGenerateThisFd.upper() in GenFdsGlobalVariable.FdfParser.Profile.FdDict:
559            FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict[GenFds.OnlyGenerateThisFd.upper()]
560        if FdObj is None:
561            for ElementFd in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
562                for ElementRegion in ElementFd.RegionList:
563                    if ElementRegion.RegionType == BINARY_FILE_TYPE_FV:
564                        for ElementRegionData in ElementRegion.RegionDataList:
565                            if ElementRegionData is not None and ElementRegionData.upper() == FvObj.UiFvName:
566                                if FvObj.BlockSizeList != []:
567                                    return FvObj.BlockSizeList[0][0]
568                                else:
569                                    return ElementRegion.BlockSizeOfRegion(ElementFd.BlockSizeList)
570            if FvObj.BlockSizeList != []:
571                return FvObj.BlockSizeList[0][0]
572            return DefaultBlockSize
573        else:
574            for ElementRegion in FdObj.RegionList:
575                    if ElementRegion.RegionType == BINARY_FILE_TYPE_FV:
576                        for ElementRegionData in ElementRegion.RegionDataList:
577                            if ElementRegionData is not None and ElementRegionData.upper() == FvObj.UiFvName:
578                                if FvObj.BlockSizeList != []:
579                                    return FvObj.BlockSizeList[0][0]
580                                else:
581                                    return ElementRegion.BlockSizeOfRegion(ElementFd.BlockSizeList)
582            return DefaultBlockSize
583
584    ## DisplayFvSpaceInfo()
585    #
586    #   @param  FvObj           Whose block size to get
587    #   @retval None
588    #
589    @staticmethod
590    def DisplayFvSpaceInfo(FdfParserObject):
591
592        FvSpaceInfoList = []
593        MaxFvNameLength = 0
594        for FvName in FdfParserObject.Profile.FvDict:
595            if len(FvName) > MaxFvNameLength:
596                MaxFvNameLength = len(FvName)
597            FvSpaceInfoFileName = os.path.join(GenFdsGlobalVariable.FvDir, FvName.upper() + '.Fv.map')
598            if os.path.exists(FvSpaceInfoFileName):
599                FileLinesList = getlines(FvSpaceInfoFileName)
600                TotalFound = False
601                Total = ''
602                UsedFound = False
603                Used = ''
604                FreeFound = False
605                Free = ''
606                for Line in FileLinesList:
607                    NameValue = Line.split('=')
608                    if len(NameValue) == 2:
609                        if NameValue[0].strip() == 'EFI_FV_TOTAL_SIZE':
610                            TotalFound = True
611                            Total = NameValue[1].strip()
612                        if NameValue[0].strip() == 'EFI_FV_TAKEN_SIZE':
613                            UsedFound = True
614                            Used = NameValue[1].strip()
615                        if NameValue[0].strip() == 'EFI_FV_SPACE_SIZE':
616                            FreeFound = True
617                            Free = NameValue[1].strip()
618
619                if TotalFound and UsedFound and FreeFound:
620                    FvSpaceInfoList.append((FvName, Total, Used, Free))
621
622        GenFdsGlobalVariable.InfLogger('\nFV Space Information')
623        for FvSpaceInfo in FvSpaceInfoList:
624            Name = FvSpaceInfo[0]
625            TotalSizeValue = int(FvSpaceInfo[1], 0)
626            UsedSizeValue = int(FvSpaceInfo[2], 0)
627            FreeSizeValue = int(FvSpaceInfo[3], 0)
628            if UsedSizeValue == TotalSizeValue:
629                Percentage = '100'
630            else:
631                Percentage = str((UsedSizeValue + 0.0) / TotalSizeValue)[0:4].lstrip('0.')
632
633            GenFdsGlobalVariable.InfLogger(Name + ' ' + '[' + Percentage + '%Full] ' + str(TotalSizeValue) + ' total, ' + str(UsedSizeValue) + ' used, ' + str(FreeSizeValue) + ' free')
634
635    ## PreprocessImage()
636    #
637    #   @param  BuildDb         Database from build meta data files
638    #   @param  DscFile         modules from dsc file will be preprocessed
639    #   @retval None
640    #
641    @staticmethod
642    def PreprocessImage(BuildDb, DscFile):
643        PcdDict = BuildDb.BuildObject[DscFile, TAB_COMMON, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].Pcds
644        PcdValue = ''
645        for Key in PcdDict:
646            PcdObj = PcdDict[Key]
647            if PcdObj.TokenCName == 'PcdBsBaseAddress':
648                PcdValue = PcdObj.DefaultValue
649                break
650
651        if PcdValue == '':
652            return
653
654        Int64PcdValue = int(PcdValue, 0)
655        if Int64PcdValue == 0 or Int64PcdValue < -1:
656            return
657
658        TopAddress = 0
659        if Int64PcdValue > 0:
660            TopAddress = Int64PcdValue
661
662        ModuleDict = BuildDb.BuildObject[DscFile, TAB_COMMON, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].Modules
663        for Key in ModuleDict:
664            ModuleObj = BuildDb.BuildObject[Key, TAB_COMMON, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
665            print(ModuleObj.BaseName + ' ' + ModuleObj.ModuleType)
666
667    @staticmethod
668    def GenerateGuidXRefFile(BuildDb, ArchList, FdfParserObj):
669        GuidXRefFileName = os.path.join(GenFdsGlobalVariable.FvDir, "Guid.xref")
670        GuidXRefFile = []
671        PkgGuidDict = {}
672        GuidDict = {}
673        ModuleList = []
674        FileGuidList = []
675        VariableGuidSet = set()
676        for Arch in ArchList:
677            PlatformDataBase = BuildDb.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
678            PkgList = GenFdsGlobalVariable.WorkSpace.GetPackageList(GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag)
679            for P in PkgList:
680                PkgGuidDict.update(P.Guids)
681            for Name, Guid in PlatformDataBase.Pcds:
682                Pcd = PlatformDataBase.Pcds[Name, Guid]
683                if Pcd.Type in [TAB_PCDS_DYNAMIC_HII, TAB_PCDS_DYNAMIC_EX_HII]:
684                    for SkuId in Pcd.SkuInfoList:
685                        Sku = Pcd.SkuInfoList[SkuId]
686                        if Sku.VariableGuid in VariableGuidSet:continue
687                        VariableGuidSet.add(Sku.VariableGuid)
688                        if Sku.VariableGuid and Sku.VariableGuid in PkgGuidDict.keys():
689                            GuidDict[Sku.VariableGuid] = PkgGuidDict[Sku.VariableGuid]
690            for ModuleFile in PlatformDataBase.Modules:
691                Module = BuildDb.BuildObject[ModuleFile, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
692                if Module in ModuleList:
693                    continue
694                else:
695                    ModuleList.append(Module)
696                if GlobalData.gGuidPattern.match(ModuleFile.BaseName):
697                    GuidXRefFile.append("%s %s\n" % (ModuleFile.BaseName, Module.BaseName))
698                else:
699                    GuidXRefFile.append("%s %s\n" % (Module.Guid, Module.BaseName))
700                GuidDict.update(Module.Protocols)
701                GuidDict.update(Module.Guids)
702                GuidDict.update(Module.Ppis)
703            for FvName in FdfParserObj.Profile.FvDict:
704                for FfsObj in FdfParserObj.Profile.FvDict[FvName].FfsList:
705                    if not isinstance(FfsObj, FileStatement):
706                        InfPath = PathClass(NormPath(mws.join(GenFdsGlobalVariable.WorkSpaceDir, FfsObj.InfFileName)))
707                        FdfModule = BuildDb.BuildObject[InfPath, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
708                        if FdfModule in ModuleList:
709                            continue
710                        else:
711                            ModuleList.append(FdfModule)
712                        GuidXRefFile.append("%s %s\n" % (FdfModule.Guid, FdfModule.BaseName))
713                        GuidDict.update(FdfModule.Protocols)
714                        GuidDict.update(FdfModule.Guids)
715                        GuidDict.update(FdfModule.Ppis)
716                    else:
717                        FileStatementGuid = FfsObj.NameGuid
718                        if FileStatementGuid in FileGuidList:
719                            continue
720                        else:
721                            FileGuidList.append(FileStatementGuid)
722                        Name = []
723                        FfsPath = os.path.join(GenFdsGlobalVariable.FvDir, 'Ffs')
724                        FfsPath = glob(os.path.join(FfsPath, FileStatementGuid) + TAB_STAR)
725                        if not FfsPath:
726                            continue
727                        if not os.path.exists(FfsPath[0]):
728                            continue
729                        MatchDict = {}
730                        ReFileEnds = compile('\S+(.ui)$|\S+(fv.sec.txt)$|\S+(.pe32.txt)$|\S+(.te.txt)$|\S+(.pic.txt)$|\S+(.raw.txt)$|\S+(.ffs.txt)$')
731                        FileList = os.listdir(FfsPath[0])
732                        for File in FileList:
733                            Match = ReFileEnds.search(File)
734                            if Match:
735                                for Index in range(1, 8):
736                                    if Match.group(Index) and Match.group(Index) in MatchDict:
737                                        MatchDict[Match.group(Index)].append(File)
738                                    elif Match.group(Index):
739                                        MatchDict[Match.group(Index)] = [File]
740                        if not MatchDict:
741                            continue
742                        if '.ui' in MatchDict:
743                            for File in MatchDict['.ui']:
744                                with open(os.path.join(FfsPath[0], File), 'rb') as F:
745                                    F.read()
746                                    length = F.tell()
747                                    F.seek(4)
748                                    TmpStr = unpack('%dh' % ((length - 4) // 2), F.read())
749                                    Name = ''.join(chr(c) for c in TmpStr[:-1])
750                        else:
751                            FileList = []
752                            if 'fv.sec.txt' in MatchDict:
753                                FileList = MatchDict['fv.sec.txt']
754                            elif '.pe32.txt' in MatchDict:
755                                FileList = MatchDict['.pe32.txt']
756                            elif '.te.txt' in MatchDict:
757                                FileList = MatchDict['.te.txt']
758                            elif '.pic.txt' in MatchDict:
759                                FileList = MatchDict['.pic.txt']
760                            elif '.raw.txt' in MatchDict:
761                                FileList = MatchDict['.raw.txt']
762                            elif '.ffs.txt' in MatchDict:
763                                FileList = MatchDict['.ffs.txt']
764                            else:
765                                pass
766                            for File in FileList:
767                                with open(os.path.join(FfsPath[0], File), 'r') as F:
768                                    Name.append((F.read().split()[-1]))
769                        if not Name:
770                            continue
771
772                        Name = ' '.join(Name) if isinstance(Name, type([])) else Name
773                        GuidXRefFile.append("%s %s\n" %(FileStatementGuid, Name))
774
775       # Append GUIDs, Protocols, and PPIs to the Xref file
776        GuidXRefFile.append("\n")
777        for key, item in GuidDict.items():
778            GuidXRefFile.append("%s %s\n" % (GuidStructureStringToGuidString(item).upper(), key))
779
780        if GuidXRefFile:
781            GuidXRefFile = ''.join(GuidXRefFile)
782            SaveFileOnChange(GuidXRefFileName, GuidXRefFile, False)
783            GenFdsGlobalVariable.InfLogger("\nGUID cross reference file can be found at %s" % GuidXRefFileName)
784        elif os.path.exists(GuidXRefFileName):
785            os.remove(GuidXRefFileName)
786
787
788if __name__ == '__main__':
789    r = main()
790    ## 0-127 is a safe return range, and 1 is a standard default error
791    if r < 0 or r > 127:
792        r = 1
793    exit(r)
794
795