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 TargetTxt
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 = True
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            else:
144                GenFdsGlobalVariable.EnableGenfdsMultiThread = False
145        os.chdir(GenFdsGlobalVariable.WorkSpaceDir)
146
147        # set multiple workspace
148        PackagesPath = os.getenv("PACKAGES_PATH")
149        mws.setWs(GenFdsGlobalVariable.WorkSpaceDir, PackagesPath)
150
151        if FdsCommandDict.get("fdf_file"):
152            FdfFilename = FdsCommandDict.get("fdf_file")[0].Path
153            FdfFilename = GenFdsGlobalVariable.ReplaceWorkspaceMacro(FdfFilename)
154
155            if FdfFilename[0:2] == '..':
156                FdfFilename = os.path.realpath(FdfFilename)
157            if not os.path.isabs(FdfFilename):
158                FdfFilename = mws.join(GenFdsGlobalVariable.WorkSpaceDir, FdfFilename)
159            if not os.path.exists(FdfFilename):
160                EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=FdfFilename)
161
162            GenFdsGlobalVariable.FdfFile = FdfFilename
163            GenFdsGlobalVariable.FdfFileTimeStamp = os.path.getmtime(FdfFilename)
164        else:
165            EdkLogger.error("GenFds", OPTION_MISSING, "Missing FDF filename")
166
167        if FdsCommandDict.get("build_target"):
168            GenFdsGlobalVariable.TargetName = FdsCommandDict.get("build_target")
169
170        if FdsCommandDict.get("toolchain_tag"):
171            GenFdsGlobalVariable.ToolChainTag = FdsCommandDict.get("toolchain_tag")
172
173        if FdsCommandDict.get("active_platform"):
174            ActivePlatform = FdsCommandDict.get("active_platform")
175            ActivePlatform = GenFdsGlobalVariable.ReplaceWorkspaceMacro(ActivePlatform)
176
177            if ActivePlatform[0:2] == '..':
178                ActivePlatform = os.path.realpath(ActivePlatform)
179
180            if not os.path.isabs (ActivePlatform):
181                ActivePlatform = mws.join(GenFdsGlobalVariable.WorkSpaceDir, ActivePlatform)
182
183            if not os.path.exists(ActivePlatform):
184                EdkLogger.error("GenFds", FILE_NOT_FOUND, "ActivePlatform doesn't exist!")
185        else:
186            EdkLogger.error("GenFds", OPTION_MISSING, "Missing active platform")
187
188        GenFdsGlobalVariable.ActivePlatform = PathClass(NormPath(ActivePlatform))
189
190        if FdsCommandDict.get("conf_directory"):
191            # Get alternate Conf location, if it is absolute, then just use the absolute directory name
192            ConfDirectoryPath = os.path.normpath(FdsCommandDict.get("conf_directory"))
193            if ConfDirectoryPath.startswith('"'):
194                ConfDirectoryPath = ConfDirectoryPath[1:]
195            if ConfDirectoryPath.endswith('"'):
196                ConfDirectoryPath = ConfDirectoryPath[:-1]
197            if not os.path.isabs(ConfDirectoryPath):
198                # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE
199                # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf
200                ConfDirectoryPath = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, ConfDirectoryPath)
201        else:
202            if "CONF_PATH" in os.environ:
203                ConfDirectoryPath = os.path.normcase(os.environ["CONF_PATH"])
204            else:
205                # Get standard WORKSPACE/Conf, use the absolute path to the WORKSPACE/Conf
206                ConfDirectoryPath = mws.join(GenFdsGlobalVariable.WorkSpaceDir, 'Conf')
207        GenFdsGlobalVariable.ConfDir = ConfDirectoryPath
208        if not GlobalData.gConfDirectory:
209            GlobalData.gConfDirectory = GenFdsGlobalVariable.ConfDir
210        BuildConfigurationFile = os.path.normpath(os.path.join(ConfDirectoryPath, "target.txt"))
211        if os.path.isfile(BuildConfigurationFile) == True:
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"] = not Options.NoGenfdsMultiThread
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=True, help="Enable GenFds multi thread to generate ffs file.")
465    Parser.add_option("--no-genfds-multi-thread", action="store_true", dest="NoGenfdsMultiThread", default=False, help="Disable GenFds multi thread to generate ffs file.")
466
467    Options, _ = Parser.parse_args()
468    return Options
469
470## The class implementing the EDK2 flash image generation process
471#
472#   This process includes:
473#       1. Collect workspace information, includes platform and module information
474#       2. Call methods of Fd class to generate FD
475#       3. Call methods of Fv class to generate FV that not belong to FD
476#
477class GenFds(object):
478    FdfParsef = None
479    OnlyGenerateThisFd = None
480    OnlyGenerateThisFv = None
481    OnlyGenerateThisCap = None
482
483    ## GenFd()
484    #
485    #   @param  OutputDir           Output directory
486    #   @param  FdfParserObject     FDF contents parser
487    #   @param  Workspace           The directory of workspace
488    #   @param  ArchList            The Arch list of platform
489    #
490    @staticmethod
491    def GenFd (OutputDir, FdfParserObject, WorkSpace, ArchList):
492        GenFdsGlobalVariable.SetDir ('', FdfParserObject, WorkSpace, ArchList)
493
494        GenFdsGlobalVariable.VerboseLogger(" Generate all Fd images and their required FV and Capsule images!")
495        if GenFds.OnlyGenerateThisCap is not None and GenFds.OnlyGenerateThisCap.upper() in GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict:
496            CapsuleObj = GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict[GenFds.OnlyGenerateThisCap.upper()]
497            if CapsuleObj is not None:
498                CapsuleObj.GenCapsule()
499                return
500
501        if GenFds.OnlyGenerateThisFd is not None and GenFds.OnlyGenerateThisFd.upper() in GenFdsGlobalVariable.FdfParser.Profile.FdDict:
502            FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict[GenFds.OnlyGenerateThisFd.upper()]
503            if FdObj is not None:
504                FdObj.GenFd()
505                return
506        elif GenFds.OnlyGenerateThisFd is None and GenFds.OnlyGenerateThisFv is None:
507            for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
508                FdObj.GenFd()
509
510        GenFdsGlobalVariable.VerboseLogger("\n Generate other FV images! ")
511        if GenFds.OnlyGenerateThisFv is not None and GenFds.OnlyGenerateThisFv.upper() in GenFdsGlobalVariable.FdfParser.Profile.FvDict:
512            FvObj = GenFdsGlobalVariable.FdfParser.Profile.FvDict[GenFds.OnlyGenerateThisFv.upper()]
513            if FvObj is not None:
514                Buffer = BytesIO()
515                FvObj.AddToBuffer(Buffer)
516                Buffer.close()
517                return
518        elif GenFds.OnlyGenerateThisFv is None:
519            for FvObj in GenFdsGlobalVariable.FdfParser.Profile.FvDict.values():
520                Buffer = BytesIO()
521                FvObj.AddToBuffer(Buffer)
522                Buffer.close()
523
524        if GenFds.OnlyGenerateThisFv is None and GenFds.OnlyGenerateThisFd is None and GenFds.OnlyGenerateThisCap is None:
525            if GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict != {}:
526                GenFdsGlobalVariable.VerboseLogger("\n Generate other Capsule images!")
527                for CapsuleObj in GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict.values():
528                    CapsuleObj.GenCapsule()
529
530            if GenFdsGlobalVariable.FdfParser.Profile.OptRomDict != {}:
531                GenFdsGlobalVariable.VerboseLogger("\n Generate all Option ROM!")
532                for OptRomObj in GenFdsGlobalVariable.FdfParser.Profile.OptRomDict.values():
533                    OptRomObj.AddToBuffer(None)
534
535    @staticmethod
536    def GenFfsMakefile(OutputDir, FdfParserObject, WorkSpace, ArchList, GlobalData):
537        GenFdsGlobalVariable.SetEnv(FdfParserObject, WorkSpace, ArchList, GlobalData)
538        for FdObj in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
539            FdObj.GenFd(Flag=True)
540
541        for FvObj in GenFdsGlobalVariable.FdfParser.Profile.FvDict.values():
542            FvObj.AddToBuffer(Buffer=None, Flag=True)
543
544        if GenFdsGlobalVariable.FdfParser.Profile.OptRomDict != {}:
545            for OptRomObj in GenFdsGlobalVariable.FdfParser.Profile.OptRomDict.values():
546                OptRomObj.AddToBuffer(Buffer=None, Flag=True)
547
548        return GenFdsGlobalVariable.FfsCmdDict
549
550    ## GetFvBlockSize()
551    #
552    #   @param  FvObj           Whose block size to get
553    #   @retval int             Block size value
554    #
555    @staticmethod
556    def GetFvBlockSize(FvObj):
557        DefaultBlockSize = 0x1
558        FdObj = None
559        if GenFds.OnlyGenerateThisFd is not None and GenFds.OnlyGenerateThisFd.upper() in GenFdsGlobalVariable.FdfParser.Profile.FdDict:
560            FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict[GenFds.OnlyGenerateThisFd.upper()]
561        if FdObj is None:
562            for ElementFd in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():
563                for ElementRegion in ElementFd.RegionList:
564                    if ElementRegion.RegionType == BINARY_FILE_TYPE_FV:
565                        for ElementRegionData in ElementRegion.RegionDataList:
566                            if ElementRegionData is not None and ElementRegionData.upper() == FvObj.UiFvName:
567                                if FvObj.BlockSizeList != []:
568                                    return FvObj.BlockSizeList[0][0]
569                                else:
570                                    return ElementRegion.BlockSizeOfRegion(ElementFd.BlockSizeList)
571            if FvObj.BlockSizeList != []:
572                return FvObj.BlockSizeList[0][0]
573            return DefaultBlockSize
574        else:
575            for ElementRegion in FdObj.RegionList:
576                    if ElementRegion.RegionType == BINARY_FILE_TYPE_FV:
577                        for ElementRegionData in ElementRegion.RegionDataList:
578                            if ElementRegionData is not None and ElementRegionData.upper() == FvObj.UiFvName:
579                                if FvObj.BlockSizeList != []:
580                                    return FvObj.BlockSizeList[0][0]
581                                else:
582                                    return ElementRegion.BlockSizeOfRegion(ElementFd.BlockSizeList)
583            return DefaultBlockSize
584
585    ## DisplayFvSpaceInfo()
586    #
587    #   @param  FvObj           Whose block size to get
588    #   @retval None
589    #
590    @staticmethod
591    def DisplayFvSpaceInfo(FdfParserObject):
592
593        FvSpaceInfoList = []
594        MaxFvNameLength = 0
595        for FvName in FdfParserObject.Profile.FvDict:
596            if len(FvName) > MaxFvNameLength:
597                MaxFvNameLength = len(FvName)
598            FvSpaceInfoFileName = os.path.join(GenFdsGlobalVariable.FvDir, FvName.upper() + '.Fv.map')
599            if os.path.exists(FvSpaceInfoFileName):
600                FileLinesList = getlines(FvSpaceInfoFileName)
601                TotalFound = False
602                Total = ''
603                UsedFound = False
604                Used = ''
605                FreeFound = False
606                Free = ''
607                for Line in FileLinesList:
608                    NameValue = Line.split('=')
609                    if len(NameValue) == 2:
610                        if NameValue[0].strip() == 'EFI_FV_TOTAL_SIZE':
611                            TotalFound = True
612                            Total = NameValue[1].strip()
613                        if NameValue[0].strip() == 'EFI_FV_TAKEN_SIZE':
614                            UsedFound = True
615                            Used = NameValue[1].strip()
616                        if NameValue[0].strip() == 'EFI_FV_SPACE_SIZE':
617                            FreeFound = True
618                            Free = NameValue[1].strip()
619
620                if TotalFound and UsedFound and FreeFound:
621                    FvSpaceInfoList.append((FvName, Total, Used, Free))
622
623        GenFdsGlobalVariable.InfLogger('\nFV Space Information')
624        for FvSpaceInfo in FvSpaceInfoList:
625            Name = FvSpaceInfo[0]
626            TotalSizeValue = int(FvSpaceInfo[1], 0)
627            UsedSizeValue = int(FvSpaceInfo[2], 0)
628            FreeSizeValue = int(FvSpaceInfo[3], 0)
629            if UsedSizeValue == TotalSizeValue:
630                Percentage = '100'
631            else:
632                Percentage = str((UsedSizeValue + 0.0) / TotalSizeValue)[0:4].lstrip('0.')
633
634            GenFdsGlobalVariable.InfLogger(Name + ' ' + '[' + Percentage + '%Full] ' + str(TotalSizeValue) + ' total, ' + str(UsedSizeValue) + ' used, ' + str(FreeSizeValue) + ' free')
635
636    ## PreprocessImage()
637    #
638    #   @param  BuildDb         Database from build meta data files
639    #   @param  DscFile         modules from dsc file will be preprocessed
640    #   @retval None
641    #
642    @staticmethod
643    def PreprocessImage(BuildDb, DscFile):
644        PcdDict = BuildDb.BuildObject[DscFile, TAB_COMMON, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].Pcds
645        PcdValue = ''
646        for Key in PcdDict:
647            PcdObj = PcdDict[Key]
648            if PcdObj.TokenCName == 'PcdBsBaseAddress':
649                PcdValue = PcdObj.DefaultValue
650                break
651
652        if PcdValue == '':
653            return
654
655        Int64PcdValue = int(PcdValue, 0)
656        if Int64PcdValue == 0 or Int64PcdValue < -1:
657            return
658
659        TopAddress = 0
660        if Int64PcdValue > 0:
661            TopAddress = Int64PcdValue
662
663        ModuleDict = BuildDb.BuildObject[DscFile, TAB_COMMON, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].Modules
664        for Key in ModuleDict:
665            ModuleObj = BuildDb.BuildObject[Key, TAB_COMMON, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
666            print(ModuleObj.BaseName + ' ' + ModuleObj.ModuleType)
667
668    @staticmethod
669    def GenerateGuidXRefFile(BuildDb, ArchList, FdfParserObj):
670        GuidXRefFileName = os.path.join(GenFdsGlobalVariable.FvDir, "Guid.xref")
671        GuidXRefFile = []
672        PkgGuidDict = {}
673        GuidDict = {}
674        ModuleList = []
675        FileGuidList = []
676        VariableGuidSet = set()
677        for Arch in ArchList:
678            PlatformDataBase = BuildDb.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
679            PkgList = GenFdsGlobalVariable.WorkSpace.GetPackageList(GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag)
680            for P in PkgList:
681                PkgGuidDict.update(P.Guids)
682            for Name, Guid in PlatformDataBase.Pcds:
683                Pcd = PlatformDataBase.Pcds[Name, Guid]
684                if Pcd.Type in [TAB_PCDS_DYNAMIC_HII, TAB_PCDS_DYNAMIC_EX_HII]:
685                    for SkuId in Pcd.SkuInfoList:
686                        Sku = Pcd.SkuInfoList[SkuId]
687                        if Sku.VariableGuid in VariableGuidSet:continue
688                        VariableGuidSet.add(Sku.VariableGuid)
689                        if Sku.VariableGuid and Sku.VariableGuid in PkgGuidDict.keys():
690                            GuidDict[Sku.VariableGuid] = PkgGuidDict[Sku.VariableGuid]
691            for ModuleFile in PlatformDataBase.Modules:
692                Module = BuildDb.BuildObject[ModuleFile, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
693                if Module in ModuleList:
694                    continue
695                else:
696                    ModuleList.append(Module)
697                if GlobalData.gGuidPattern.match(ModuleFile.BaseName):
698                    GuidXRefFile.append("%s %s\n" % (ModuleFile.BaseName, Module.BaseName))
699                else:
700                    GuidXRefFile.append("%s %s\n" % (Module.Guid, Module.BaseName))
701                GuidDict.update(Module.Protocols)
702                GuidDict.update(Module.Guids)
703                GuidDict.update(Module.Ppis)
704            for FvName in FdfParserObj.Profile.FvDict:
705                for FfsObj in FdfParserObj.Profile.FvDict[FvName].FfsList:
706                    if not isinstance(FfsObj, FileStatement):
707                        InfPath = PathClass(NormPath(mws.join(GenFdsGlobalVariable.WorkSpaceDir, FfsObj.InfFileName)))
708                        FdfModule = BuildDb.BuildObject[InfPath, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
709                        if FdfModule in ModuleList:
710                            continue
711                        else:
712                            ModuleList.append(FdfModule)
713                        GuidXRefFile.append("%s %s\n" % (FdfModule.Guid, FdfModule.BaseName))
714                        GuidDict.update(FdfModule.Protocols)
715                        GuidDict.update(FdfModule.Guids)
716                        GuidDict.update(FdfModule.Ppis)
717                    else:
718                        FileStatementGuid = FfsObj.NameGuid
719                        if FileStatementGuid in FileGuidList:
720                            continue
721                        else:
722                            FileGuidList.append(FileStatementGuid)
723                        Name = []
724                        FfsPath = os.path.join(GenFdsGlobalVariable.FvDir, 'Ffs')
725                        FfsPath = glob(os.path.join(FfsPath, FileStatementGuid) + TAB_STAR)
726                        if not FfsPath:
727                            continue
728                        if not os.path.exists(FfsPath[0]):
729                            continue
730                        MatchDict = {}
731                        ReFileEnds = compile('\S+(.ui)$|\S+(fv.sec.txt)$|\S+(.pe32.txt)$|\S+(.te.txt)$|\S+(.pic.txt)$|\S+(.raw.txt)$|\S+(.ffs.txt)$')
732                        FileList = os.listdir(FfsPath[0])
733                        for File in FileList:
734                            Match = ReFileEnds.search(File)
735                            if Match:
736                                for Index in range(1, 8):
737                                    if Match.group(Index) and Match.group(Index) in MatchDict:
738                                        MatchDict[Match.group(Index)].append(File)
739                                    elif Match.group(Index):
740                                        MatchDict[Match.group(Index)] = [File]
741                        if not MatchDict:
742                            continue
743                        if '.ui' in MatchDict:
744                            for File in MatchDict['.ui']:
745                                with open(os.path.join(FfsPath[0], File), 'rb') as F:
746                                    F.read()
747                                    length = F.tell()
748                                    F.seek(4)
749                                    TmpStr = unpack('%dh' % ((length - 4) // 2), F.read())
750                                    Name = ''.join(chr(c) for c in TmpStr[:-1])
751                        else:
752                            FileList = []
753                            if 'fv.sec.txt' in MatchDict:
754                                FileList = MatchDict['fv.sec.txt']
755                            elif '.pe32.txt' in MatchDict:
756                                FileList = MatchDict['.pe32.txt']
757                            elif '.te.txt' in MatchDict:
758                                FileList = MatchDict['.te.txt']
759                            elif '.pic.txt' in MatchDict:
760                                FileList = MatchDict['.pic.txt']
761                            elif '.raw.txt' in MatchDict:
762                                FileList = MatchDict['.raw.txt']
763                            elif '.ffs.txt' in MatchDict:
764                                FileList = MatchDict['.ffs.txt']
765                            else:
766                                pass
767                            for File in FileList:
768                                with open(os.path.join(FfsPath[0], File), 'r') as F:
769                                    Name.append((F.read().split()[-1]))
770                        if not Name:
771                            continue
772
773                        Name = ' '.join(Name) if isinstance(Name, type([])) else Name
774                        GuidXRefFile.append("%s %s\n" %(FileStatementGuid, Name))
775
776       # Append GUIDs, Protocols, and PPIs to the Xref file
777        GuidXRefFile.append("\n")
778        for key, item in GuidDict.items():
779            GuidXRefFile.append("%s %s\n" % (GuidStructureStringToGuidString(item).upper(), key))
780
781        if GuidXRefFile:
782            GuidXRefFile = ''.join(GuidXRefFile)
783            SaveFileOnChange(GuidXRefFileName, GuidXRefFile, False)
784            GenFdsGlobalVariable.InfLogger("\nGUID cross reference file can be found at %s" % GuidXRefFileName)
785        elif os.path.exists(GuidXRefFileName):
786            os.remove(GuidXRefFileName)
787
788
789if __name__ == '__main__':
790    r = main()
791    ## 0-127 is a safe return range, and 1 is a standard default error
792    if r < 0 or r > 127:
793        r = 1
794    exit(r)
795
796