96af3193b4b50b9280fac3a0b08721833632a3b1
[efi/basetools/.git] / Source / Python / GenFds / GenFdsGlobalVariable.py
1 ## @file
2 # Global variables for GenFds
3 #
4 #  Copyright (c) 2007 - 2010, Intel Corporation
5 #
6 #  All rights reserved. This program and the accompanying materials
7 #  are licensed and made available under the terms and conditions of the BSD License
8 #  which accompanies this distribution.  The full text of the license may be found at
9 #  http://opensource.org/licenses/bsd-license.php
10 #
11 #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 #
14
15 ##
16 # Import Modules
17 #
18 import os
19 import sys
20 import subprocess
21 import struct
22 import array
23
24 from Common.BuildToolError import *
25 from Common import EdkLogger
26 from Common.Misc import SaveFileOnChange
27
28 ## Global variables
29 #
30 #
31 class GenFdsGlobalVariable:
32     FvDir = ''
33     OutputDirDict = {}
34     BinDir = ''
35     # will be FvDir + os.sep + 'Ffs'
36     FfsDir = ''
37     FdfParser = None
38     LibDir = ''
39     WorkSpace = None
40     WorkSpaceDir = ''
41     EdkSourceDir = ''
42     OutputDirFromDscDict = {}
43     TargetName = ''
44     ToolChainTag = ''
45     RuleDict = {}
46     ArchList = None
47     VtfDict = {}
48     ActivePlatform = None
49     FvAddressFileName = ''
50     VerboseMode = False
51     DebugLevel = -1
52     SharpCounter = 0
53     SharpNumberPerLine = 40
54     FdfFile = ''
55     FdfFileTimeStamp = 0
56     FixedLoadAddress = False
57
58     SectionHeader = struct.Struct("3B 1B")
59
60     ## SetDir()
61     #
62     #   @param  OutputDir           Output directory
63     #   @param  FdfParser           FDF contents parser
64     #   @param  Workspace           The directory of workspace
65     #   @param  ArchList            The Arch list of platform
66     #
67     def SetDir (OutputDir, FdfParser, WorkSpace, ArchList):
68         GenFdsGlobalVariable.VerboseLogger( "GenFdsGlobalVariable.OutputDir :%s" %OutputDir)
69 #        GenFdsGlobalVariable.OutputDirDict = OutputDir
70         GenFdsGlobalVariable.FdfParser = FdfParser
71         GenFdsGlobalVariable.WorkSpace = WorkSpace
72         GenFdsGlobalVariable.FvDir = os.path.join(GenFdsGlobalVariable.OutputDirDict[ArchList[0]], 'FV')
73         if not os.path.exists(GenFdsGlobalVariable.FvDir) :
74             os.makedirs(GenFdsGlobalVariable.FvDir)
75         GenFdsGlobalVariable.FfsDir = os.path.join(GenFdsGlobalVariable.FvDir, 'Ffs')
76         if not os.path.exists(GenFdsGlobalVariable.FfsDir) :
77             os.makedirs(GenFdsGlobalVariable.FfsDir)
78         if ArchList != None:
79             GenFdsGlobalVariable.ArchList = ArchList
80
81         T_CHAR_LF = '\n'
82         #
83         # Create FV Address inf file
84         #
85         GenFdsGlobalVariable.FvAddressFileName = os.path.join(GenFdsGlobalVariable.FfsDir, 'FvAddress.inf')
86         FvAddressFile = open (GenFdsGlobalVariable.FvAddressFileName, 'w')
87         #
88         # Add [Options]
89         #
90         FvAddressFile.writelines("[options]" + T_CHAR_LF)
91         BsAddress = '0'
92         for Arch in ArchList:
93             if GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch].BsBaseAddress:
94                 BsAddress = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch].BsBaseAddress
95                 break
96
97         FvAddressFile.writelines("EFI_BOOT_DRIVER_BASE_ADDRESS = " + \
98                                        BsAddress          + \
99                                        T_CHAR_LF)
100
101         RtAddress = '0'
102         for Arch in ArchList:
103             if GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch].RtBaseAddress:
104                 RtAddress = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch].RtBaseAddress
105
106         FvAddressFile.writelines("EFI_RUNTIME_DRIVER_BASE_ADDRESS = " + \
107                                        RtAddress          + \
108                                        T_CHAR_LF)
109
110         FvAddressFile.close()
111
112     ## ReplaceWorkspaceMacro()
113     #
114     #   @param  String           String that may contain macro
115     #
116     def ReplaceWorkspaceMacro(String):
117         Str = String.replace('$(WORKSPACE)', GenFdsGlobalVariable.WorkSpaceDir)
118         if os.path.exists(Str):
119             if not os.path.isabs(Str):
120                 Str = os.path.abspath(Str)
121         else:
122             Str = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, String)
123         return os.path.normpath(Str)
124
125     ## Check if the input files are newer than output files
126     #
127     #   @param  Output          Path of output file
128     #   @param  Input           Path list of input files
129     #
130     #   @retval True            if Output doesn't exist, or any Input is newer
131     #   @retval False           if all Input is older than Output
132     #
133     @staticmethod
134     def NeedsUpdate(Output, Input):
135         if not os.path.exists(Output):
136             return True
137         # always update "Output" if no "Input" given
138         if Input == None or len(Input) == 0:
139             return True
140
141         # if fdf file is changed after the 'Output" is generated, update the 'Output'
142         OutputTime = os.path.getmtime(Output)
143         if GenFdsGlobalVariable.FdfFileTimeStamp > OutputTime:
144             return True
145
146         for F in Input:
147             # always update "Output" if any "Input" doesn't exist
148             if not os.path.exists(F):
149                 return True
150             # always update "Output" if any "Input" is newer than "Output"
151             if os.path.getmtime(F) > OutputTime:
152                 return True
153         return False
154
155     @staticmethod
156     def GenerateSection(Output, Input, Type=None, CompressionType=None, Guid=None,
157                         GuidHdrLen=None, GuidAttr=[], Ui=None, Ver=None, InputAlign=None):
158         if not GenFdsGlobalVariable.NeedsUpdate(Output, Input):
159             return
160         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
161
162         Cmd = ["GenSec"]
163         if Type not in [None, '']:
164             Cmd += ["-s", Type]
165         if CompressionType not in [None, '']:
166             Cmd += ["-c", CompressionType]
167         if Guid != None:
168             Cmd += ["-g", Guid]
169         if GuidHdrLen not in [None, '']:
170             Cmd += ["-l", GuidHdrLen]
171         if len(GuidAttr) != 0:
172             #Add each guided attribute
173             for Attr in GuidAttr:
174                 Cmd += ["-r", Attr]
175         if InputAlign != None:
176             #Section Align is only for dummy section without section type
177             for SecAlign in InputAlign:
178                 Cmd += ["--sectionalign", SecAlign]
179
180         if Ui not in [None, '']:
181             #Cmd += ["-n", '"' + Ui + '"']
182             SectionData = array.array('B', [0,0,0,0])
183             SectionData.fromstring(Ui.encode("utf_16_le"))
184             SectionData.append(0)
185             SectionData.append(0)
186             Len = len(SectionData)
187             GenFdsGlobalVariable.SectionHeader.pack_into(SectionData, 0, Len & 0xff, (Len >> 8) & 0xff, (Len >> 16) & 0xff, 0x15)
188             SaveFileOnChange(Output,  SectionData.tostring())
189         elif Ver not in [None, '']:
190             #Cmd += ["-j", Ver]
191             SectionData = array.array('B', [0,0,0,0])
192             SectionData.fromstring(Ver.encode("utf_16_le"))
193             SectionData.append(0)
194             SectionData.append(0)
195             Len = len(SectionData)
196             GenFdsGlobalVariable.SectionHeader.pack_into(SectionData, 0, Len & 0xff, (Len >> 8) & 0xff, (Len >> 16) & 0xff, 0x14)
197             SaveFileOnChange(Output,  SectionData.tostring())
198         else:
199             Cmd += ["-o", Output]
200             Cmd += Input
201             GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate section")
202
203     @staticmethod
204     def GetAlignment (AlignString):
205         if AlignString == None:
206             return 0
207         if AlignString in ("1K", "2K", "4K", "8K", "16K", "32K", "64K"):\r
208             return int (AlignString.rstrip('K')) * 1024\r
209         else:\r
210             return int (AlignString)\r
211
212     @staticmethod
213     def GenerateFfs(Output, Input, Type, Guid, Fixed=False, CheckSum=False, Align=None,
214                     SectionAlign=None):
215         if not GenFdsGlobalVariable.NeedsUpdate(Output, Input):
216             return
217         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
218
219         Cmd = ["GenFfs", "-t", Type, "-g", Guid]
220         if Fixed == True:
221             Cmd += ["-x"]
222         if CheckSum:
223             Cmd += ["-s"]
224         if Align not in [None, '']:
225             Cmd += ["-a", Align]
226
227         Cmd += ["-o", Output]
228         for I in range(0, len(Input)):
229             Cmd += ("-i", Input[I])
230             if SectionAlign not in [None, '', []] and SectionAlign[I] not in [None, '']:
231                 Cmd += ("-n", SectionAlign[I])
232         GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate FFS")
233
234     @staticmethod
235     def GenerateFirmwareVolume(Output, Input, BaseAddress=None, Capsule=False, Dump=False,
236                                AddressFile=None, MapFile=None, FfsList=[]):
237         if not GenFdsGlobalVariable.NeedsUpdate(Output, Input+FfsList):
238             return
239         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
240
241         Cmd = ["GenFv"]
242         if BaseAddress not in [None, '']:
243             Cmd += ["-r", BaseAddress]
244         if Capsule:
245             Cmd += ["-c"]
246         if Dump:
247             Cmd += ["-p"]
248         if AddressFile not in [None, '']:
249             Cmd += ["-a", AddressFile]
250         if MapFile not in [None, '']:
251             Cmd += ["-m", MapFile]
252         Cmd += ["-o", Output]
253         for I in Input:
254             Cmd += ["-i", I]
255
256         GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate FV")
257
258     @staticmethod
259     def GenerateVtf(Output, Input, BaseAddress=None, FvSize=None):
260         if not GenFdsGlobalVariable.NeedsUpdate(Output, Input):
261             return
262         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
263
264         Cmd = ["GenVtf"]
265         if BaseAddress not in [None, ''] and FvSize not in [None, ''] \
266             and len(BaseAddress) == len(FvSize):
267             for I in range(0, len(BaseAddress)):
268                 Cmd += ["-r", BaseAddress[I], "-s", FvSize[I]]
269         Cmd += ["-o", Output]
270         for F in Input:
271             Cmd += ["-f", F]
272
273         GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate VTF")
274
275     @staticmethod
276     def GenerateFirmwareImage(Output, Input, Type="efi", SubType=None, Zero=False,
277                               Strip=False, Replace=False, TimeStamp=None, Join=False,
278                               Align=None, Padding=None, Convert=False):
279         if not GenFdsGlobalVariable.NeedsUpdate(Output, Input):
280             return
281         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
282
283         Cmd = ["GenFw"]
284         if Type.lower() == "te":
285             Cmd += ["-t"]
286         if SubType not in [None, '']:
287             Cmd += ["-e", SubType]
288         if TimeStamp not in [None, '']:
289             Cmd += ["-s", TimeStamp]
290         if Align not in [None, '']:
291             Cmd += ["-a", Align]
292         if Padding not in [None, '']:
293             Cmd += ["-p", Padding]
294         if Zero:
295             Cmd += ["-z"]
296         if Strip:
297             Cmd += ["-l"]
298         if Replace:
299             Cmd += ["-r"]
300         if Join:
301             Cmd += ["-j"]
302         if Convert:
303             Cmd += ["-m"]
304         Cmd += ["-o", Output]
305         Cmd += Input
306
307         GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate firmware image")
308
309     @staticmethod
310     def GenerateOptionRom(Output, EfiInput, BinaryInput, Compress=False, ClassCode=None,
311                         Revision=None, DeviceId=None, VendorId=None):
312         InputList = []   
313         Cmd = ["EfiRom"]
314         if len(EfiInput) > 0:
315             
316             if Compress:
317                 Cmd += ["-ec"]
318             else:
319                 Cmd += ["-e"]
320                 
321             for EfiFile in EfiInput:
322                 Cmd += [EfiFile]
323                 InputList.append (EfiFile)
324         
325         if len(BinaryInput) > 0:
326             Cmd += ["-b"]
327             for BinFile in BinaryInput:
328                 Cmd += [BinFile]
329                 InputList.append (BinFile)
330
331         # Check List
332         if not GenFdsGlobalVariable.NeedsUpdate(Output, InputList):
333             return
334         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, InputList))
335                         
336         if ClassCode != None:
337             Cmd += ["-l", ClassCode]
338         if Revision != None:
339             Cmd += ["-r", Revision]
340         if DeviceId != None:
341             Cmd += ["-i", DeviceId]
342         if VendorId != None:
343             Cmd += ["-f", VendorId]
344
345         Cmd += ["-o", Output]    
346         GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate option rom")
347
348     @staticmethod
349     def GuidTool(Output, Input, ToolPath, Options='', returnValue=[]):
350         if not GenFdsGlobalVariable.NeedsUpdate(Output, Input):
351             return
352         GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
353
354         Cmd = [ToolPath, ]
355         Cmd += Options.split(' ')
356         Cmd += ["-o", Output]
357         Cmd += Input
358
359         GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to call " + ToolPath, returnValue)
360
361     def CallExternalTool (cmd, errorMess, returnValue=[]):
362
363         if type(cmd) not in (tuple, list):
364             GenFdsGlobalVariable.ErrorLogger("ToolError!  Invalid parameter type in call to CallExternalTool")
365
366         if GenFdsGlobalVariable.DebugLevel != -1:
367             cmd += ('--debug', str(GenFdsGlobalVariable.DebugLevel))
368             GenFdsGlobalVariable.InfLogger (cmd)
369
370         if GenFdsGlobalVariable.VerboseMode:
371             cmd += ('-v',)
372             GenFdsGlobalVariable.InfLogger (cmd)
373         else:
374             sys.stdout.write ('#')
375             sys.stdout.flush()
376             GenFdsGlobalVariable.SharpCounter = GenFdsGlobalVariable.SharpCounter + 1
377             if GenFdsGlobalVariable.SharpCounter % GenFdsGlobalVariable.SharpNumberPerLine == 0:
378                 sys.stdout.write('\n')
379
380         try:
381             PopenObject = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr= subprocess.PIPE)
382         except Exception, X:
383             EdkLogger.error("GenFds", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
384         (out, error) = PopenObject.communicate()
385
386         while PopenObject.returncode == None :
387             PopenObject.wait()
388         if returnValue != [] and returnValue[0] != 0:
389             #get command return value
390             returnValue[0] = PopenObject.returncode
391             return
392         if PopenObject.returncode != 0 or GenFdsGlobalVariable.VerboseMode or GenFdsGlobalVariable.DebugLevel != -1:
393             GenFdsGlobalVariable.InfLogger ("Return Value = %d" %PopenObject.returncode)
394             GenFdsGlobalVariable.InfLogger (out)
395             GenFdsGlobalVariable.InfLogger (error)
396             if PopenObject.returncode != 0:
397                 print "###", cmd
398                 EdkLogger.error("GenFds", COMMAND_FAILURE, errorMess)
399
400     def VerboseLogger (msg):
401         EdkLogger.verbose(msg)
402
403     def InfLogger (msg):
404         EdkLogger.info(msg)
405
406     def ErrorLogger (msg, File = None, Line = None, ExtraData = None):
407         EdkLogger.error('GenFds', GENFDS_ERROR, msg, File, Line, ExtraData)
408
409     def DebugLogger (Level, msg):
410         EdkLogger.debug(Level, msg)
411
412     ## ReplaceWorkspaceMacro()
413     #
414     #   @param  Str           String that may contain macro
415     #   @param  MacroDict     Dictionary that contains macro value pair
416     #
417     def MacroExtend (Str, MacroDict = {}, Arch = 'COMMON'):
418         if Str == None :
419             return None
420
421         Dict = {'$(WORKSPACE)'   : GenFdsGlobalVariable.WorkSpaceDir,
422                 '$(EDK_SOURCE)'  : GenFdsGlobalVariable.EdkSourceDir,
423 #                '$(OUTPUT_DIRECTORY)': GenFdsGlobalVariable.OutputDirFromDsc,
424                 '$(TARGET)' : GenFdsGlobalVariable.TargetName,
425                 '$(TOOL_CHAIN_TAG)' : GenFdsGlobalVariable.ToolChainTag
426                }
427         OutputDir = GenFdsGlobalVariable.OutputDirFromDscDict[GenFdsGlobalVariable.ArchList[0]]
428         if Arch != 'COMMON' and Arch in GenFdsGlobalVariable.ArchList:
429             OutputDir = GenFdsGlobalVariable.OutputDirFromDscDict[Arch]
430
431         Dict['$(OUTPUT_DIRECTORY)'] = OutputDir
432
433         if MacroDict != None  and len (MacroDict) != 0:
434             Dict.update(MacroDict)
435
436         for key in Dict.keys():
437             if Str.find(key) >= 0 :
438                 Str = Str.replace (key, Dict[key])
439
440         if Str.find('$(ARCH)') >= 0:
441             if len(GenFdsGlobalVariable.ArchList) == 1:
442                 Str = Str.replace('$(ARCH)', GenFdsGlobalVariable.ArchList[0])
443             else:
444                 EdkLogger.error("GenFds", GENFDS_ERROR, "No way to determine $(ARCH) for %s" % Str)
445
446         return Str
447
448     ## GetPcdValue()
449     #
450     #   @param  PcdPattern           pattern that labels a PCD.
451     #
452     def GetPcdValue (PcdPattern):
453         if PcdPattern == None :
454             return None
455         PcdPair = PcdPattern.lstrip('PCD(').rstrip(')').strip().split('.')
456         TokenSpace = PcdPair[0]
457         TokenCName = PcdPair[1]
458
459         PcdValue = ''
460         for Platform in GenFdsGlobalVariable.WorkSpace.PlatformList:
461             PcdDict = Platform.Pcds
462             for Key in PcdDict:
463                 PcdObj = PcdDict[Key]
464                 if (PcdObj.TokenCName == TokenCName) and (PcdObj.TokenSpaceGuidCName == TokenSpace):
465                     if PcdObj.Type != 'FixedAtBuild':
466                         EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not FixedAtBuild type." % PcdPattern)
467                     if PcdObj.DatumType != 'VOID*':
468                         EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not VOID* datum type." % PcdPattern)
469                         
470                     PcdValue = PcdObj.DefaultValue
471                     return PcdValue
472
473         for Package in GenFdsGlobalVariable.WorkSpace.PackageList:
474             PcdDict = Package.Pcds
475             for Key in PcdDict:
476                 PcdObj = PcdDict[Key]
477                 if (PcdObj.TokenCName == TokenCName) and (PcdObj.TokenSpaceGuidCName == TokenSpace):
478                     if PcdObj.Type != 'FixedAtBuild':
479                         EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not FixedAtBuild type." % PcdPattern)
480                     if PcdObj.DatumType != 'VOID*':
481                         EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not VOID* datum type." % PcdPattern)
482                         
483                     PcdValue = PcdObj.DefaultValue
484                     return PcdValue
485
486         return PcdValue
487
488     SetDir = staticmethod(SetDir)
489     ReplaceWorkspaceMacro = staticmethod(ReplaceWorkspaceMacro)
490     CallExternalTool = staticmethod(CallExternalTool)
491     VerboseLogger = staticmethod(VerboseLogger)
492     InfLogger = staticmethod(InfLogger)
493     ErrorLogger = staticmethod(ErrorLogger)
494     DebugLogger = staticmethod(DebugLogger)
495     MacroExtend = staticmethod (MacroExtend)
496     GetPcdValue = staticmethod(GetPcdValue)