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