1## @ GenCfgOpt.py
2#
3# Copyright (c) 2014 - 2020, Intel Corporation. All rights reserved.<BR>
4# SPDX-License-Identifier: BSD-2-Clause-Patent
5#
6##
7
8import os
9import re
10import sys
11import struct
12from   datetime import date
13from functools import reduce
14
15# Generated file copyright header
16
17__copyright_txt__ = """## @file
18#
19#  THIS IS AUTO-GENERATED FILE BY BUILD TOOLS AND PLEASE DO NOT MAKE MODIFICATION.
20#
21#  This file lists all VPD informations for a platform collected by build.exe.
22#
23# Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
24# This program and the accompanying materials
25# are licensed and made available under the terms and conditions of the BSD License
26# which accompanies this distribution.  The full text of the license may be found at
27# http://opensource.org/licenses/bsd-license.php
28#
29# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
30# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
31#
32"""
33
34__copyright_bsf__ = """/** @file
35
36  Boot Setting File for Platform Configuration.
37
38  Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
39  This program and the accompanying materials
40  are licensed and made available under the terms and conditions of the BSD License
41  which accompanies this distribution.  The full text of the license may be found at
42  http://opensource.org/licenses/bsd-license.php
43
44  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
45  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
46
47  This file is automatically generated. Please do NOT modify !!!
48
49**/
50
51"""
52
53__copyright_h__ = """/** @file
54
55Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
56
57Redistribution and use in source and binary forms, with or without modification,
58are permitted provided that the following conditions are met:
59
60* Redistributions of source code must retain the above copyright notice, this
61  list of conditions and the following disclaimer.
62* Redistributions in binary form must reproduce the above copyright notice, this
63  list of conditions and the following disclaimer in the documentation and/or
64  other materials provided with the distribution.
65* Neither the name of Intel Corporation nor the names of its contributors may
66  be used to endorse or promote products derived from this software without
67  specific prior written permission.
68
69  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
70  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
71  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
72  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
73  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
74  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
75  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
76  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
77  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
78  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
79  THE POSSIBILITY OF SUCH DAMAGE.
80
81  This file is automatically generated. Please do NOT modify !!!
82
83**/
84"""
85
86BuildOptionPcd = []
87
88class CLogicalExpression:
89    def __init__(self):
90        self.index    = 0
91        self.string   = ''
92
93    def errExit(self, err = ''):
94        print ("ERROR: Express parsing for:")
95        print ("       %s" % self.string)
96        print ("       %s^" % (' ' * self.index))
97        if err:
98            print ("INFO : %s" % err)
99        raise SystemExit
100
101    def getNonNumber (self, n1, n2):
102        if not n1.isdigit():
103            return n1
104        if not n2.isdigit():
105            return n2
106        return None
107
108    def getCurr(self, lens = 1):
109        try:
110            if lens == -1:
111                return self.string[self.index :]
112            else:
113                if self.index + lens > len(self.string):
114                    lens = len(self.string) - self.index
115                return self.string[self.index : self.index + lens]
116        except Exception:
117            return ''
118
119    def isLast(self):
120        return self.index == len(self.string)
121
122    def moveNext(self, len = 1):
123        self.index += len
124
125    def skipSpace(self):
126        while not self.isLast():
127            if self.getCurr() in ' \t':
128                self.moveNext()
129            else:
130                return
131
132    def normNumber (self, val):
133        return True if val else False
134
135    def getNumber(self, var):
136        var = var.strip()
137        if   re.match('^0x[a-fA-F0-9]+$', var):
138            value = int(var, 16)
139        elif re.match('^[+-]?\d+$', var):
140            value = int(var, 10)
141        else:
142            value = None
143        return value
144
145    def parseValue(self):
146        self.skipSpace()
147        var = ''
148        while not self.isLast():
149            char = self.getCurr()
150            if re.match('^[\w.]', char):
151                var += char
152                self.moveNext()
153            else:
154                break
155        val = self.getNumber(var)
156        if val is None:
157            value = var
158        else:
159            value = "%d" % val
160        return value
161
162    def parseSingleOp(self):
163        self.skipSpace()
164        if re.match('^NOT\W', self.getCurr(-1)):
165            self.moveNext(3)
166            op  = self.parseBrace()
167            val = self.getNumber (op)
168            if val is None:
169                self.errExit ("'%s' is not a number" % op)
170            return "%d" % (not self.normNumber(int(op)))
171        else:
172            return self.parseValue()
173
174    def parseBrace(self):
175        self.skipSpace()
176        char = self.getCurr()
177        if char == '(':
178            self.moveNext()
179            value = self.parseExpr()
180            self.skipSpace()
181            if self.getCurr() != ')':
182                self.errExit ("Expecting closing brace or operator")
183            self.moveNext()
184            return value
185        else:
186            value = self.parseSingleOp()
187            return value
188
189    def parseCompare(self):
190        value = self.parseBrace()
191        while True:
192            self.skipSpace()
193            char = self.getCurr()
194            if char in ['<', '>']:
195                self.moveNext()
196                next = self.getCurr()
197                if next == '=':
198                    op = char + next
199                    self.moveNext()
200                else:
201                    op = char
202                result = self.parseBrace()
203                test = self.getNonNumber(result, value)
204                if test is None:
205                    value = "%d" % self.normNumber(eval (value + op + result))
206                else:
207                    self.errExit ("'%s' is not a valid number for comparision" % test)
208            elif char in ['=', '!']:
209                op = self.getCurr(2)
210                if op in ['==', '!=']:
211                    self.moveNext(2)
212                    result = self.parseBrace()
213                    test = self.getNonNumber(result, value)
214                    if test is None:
215                        value = "%d" % self.normNumber((eval (value + op + result)))
216                    else:
217                        value = "%d" % self.normNumber(eval ("'" + value + "'" + op + "'" + result + "'"))
218                else:
219                    break
220            else:
221                break
222        return value
223
224    def parseAnd(self):
225        value = self.parseCompare()
226        while True:
227            self.skipSpace()
228            if re.match('^AND\W', self.getCurr(-1)):
229                self.moveNext(3)
230                result = self.parseCompare()
231                test = self.getNonNumber(result, value)
232                if test is None:
233                    value = "%d" % self.normNumber(int(value) & int(result))
234                else:
235                    self.errExit ("'%s' is not a valid op number for AND" % test)
236            else:
237                break
238        return value
239
240    def parseOrXor(self):
241        value  = self.parseAnd()
242        op     = None
243        while True:
244            self.skipSpace()
245            op = None
246            if re.match('^XOR\W', self.getCurr(-1)):
247                self.moveNext(3)
248                op = '^'
249            elif re.match('^OR\W', self.getCurr(-1)):
250                self.moveNext(2)
251                op = '|'
252            else:
253                break
254            if op:
255                result = self.parseAnd()
256                test = self.getNonNumber(result, value)
257                if test is None:
258                    value = "%d" % self.normNumber(eval (value + op + result))
259                else:
260                    self.errExit ("'%s' is not a valid op number for XOR/OR" % test)
261        return value
262
263    def parseExpr(self):
264        return self.parseOrXor()
265
266    def getResult(self):
267        value = self.parseExpr()
268        self.skipSpace()
269        if not self.isLast():
270            self.errExit ("Unexpected character found '%s'" % self.getCurr())
271        test = self.getNumber(value)
272        if test is None:
273            self.errExit ("Result '%s' is not a number" % value)
274        return int(value)
275
276    def evaluateExpress (self, Expr):
277        self.index     = 0
278        self.string    = Expr
279        if self.getResult():
280            Result = True
281        else:
282            Result = False
283        return Result
284
285class CGenCfgOpt:
286    def __init__(self):
287        self.Debug          = False
288        self.Error          = ''
289
290        self._GlobalDataDef = """
291GlobalDataDef
292    SKUID = 0, "DEFAULT"
293EndGlobalData
294
295"""
296        self._BuidinOptionTxt = """
297List &EN_DIS
298    Selection 0x1 , "Enabled"
299    Selection 0x0 , "Disabled"
300EndList
301
302"""
303
304        self._BsfKeyList    = ['FIND','NAME','HELP','TYPE','PAGE','OPTION','ORDER']
305        self._HdrKeyList    = ['HEADER','STRUCT', 'EMBED', 'COMMENT']
306        self._BuidinOption  = {'$EN_DIS' : 'EN_DIS'}
307
308        self._MacroDict   = {}
309        self._PcdsDict    = {}
310        self._CfgBlkDict  = {}
311        self._CfgPageDict = {}
312        self._CfgItemList = []
313        self._DscFile     = ''
314        self._FvDir       = ''
315        self._MapVer      = 0
316
317    def ParseMacros (self, MacroDefStr):
318        # ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build']
319        self._MacroDict = {}
320        IsExpression = False
321        for Macro in MacroDefStr:
322            if Macro.startswith('-D'):
323                IsExpression = True
324                if len(Macro) > 2:
325                    Macro = Macro[2:]
326                else :
327                    continue
328            if IsExpression:
329                IsExpression = False
330                Match = re.match("(\w+)=(.+)", Macro)
331                if Match:
332                    self._MacroDict[Match.group(1)] = Match.group(2)
333                else:
334                    Match = re.match("(\w+)", Macro)
335                    if Match:
336                        self._MacroDict[Match.group(1)] = ''
337        if len(self._MacroDict) == 0:
338            Error = 1
339        else:
340            Error = 0
341            if self.Debug:
342                print ("INFO : Macro dictionary:")
343                for Each in self._MacroDict:
344                    print ("       $(%s) = [ %s ]" % (Each , self._MacroDict[Each]))
345        return Error
346
347    def EvaulateIfdef   (self, Macro):
348        Result = Macro in self._MacroDict
349        if self.Debug:
350            print ("INFO : Eval Ifdef [%s] : %s" % (Macro, Result))
351        return  Result
352
353    def ExpandMacros (self, Input):
354        Line = Input
355        Match = re.findall("\$\(\w+\)", Input)
356        if Match:
357            for Each in Match:
358              Variable = Each[2:-1]
359              if Variable in self._MacroDict:
360                  Line = Line.replace(Each, self._MacroDict[Variable])
361              else:
362                  if self.Debug:
363                      print ("WARN : %s is not defined" % Each)
364                  Line = Line.replace(Each, Each[2:-1])
365        return Line
366
367    def ExpandPcds (self, Input):
368        Line = Input
369        Match = re.findall("(\w+\.\w+)", Input)
370        if Match:
371            for PcdName in Match:
372              if PcdName in self._PcdsDict:
373                  Line = Line.replace(PcdName, self._PcdsDict[PcdName])
374              else:
375                  if self.Debug:
376                      print ("WARN : %s is not defined" % PcdName)
377        return Line
378
379    def EvaluateExpress (self, Expr):
380        ExpExpr = self.ExpandPcds(Expr)
381        ExpExpr = self.ExpandMacros(ExpExpr)
382        LogExpr = CLogicalExpression()
383        Result  = LogExpr.evaluateExpress (ExpExpr)
384        if self.Debug:
385            print ("INFO : Eval Express [%s] : %s" % (Expr, Result))
386        return Result
387
388    def FormatListValue(self, ConfigDict):
389        Struct = ConfigDict['struct']
390        if Struct not in ['UINT8','UINT16','UINT32','UINT64']:
391            return
392
393        dataarray = []
394        binlist = ConfigDict['value'][1:-1].split(',')
395        for each in binlist:
396            each = each.strip()
397            if each.startswith('0x'):
398                value = int(each, 16)
399            else:
400                value = int(each)
401            dataarray.append(value)
402
403        unit = int(Struct[4:]) / 8
404        if int(ConfigDict['length']) != unit * len(dataarray):
405            raise Exception("Array size is not proper for '%s' !" % ConfigDict['cname'])
406
407        bytearray = []
408        for each in dataarray:
409            value = each
410            for loop in range(int(unit)):
411                bytearray.append("0x%02X" % (value & 0xFF))
412                value = value >> 8
413        newvalue  = '{'  + ','.join(bytearray) + '}'
414        ConfigDict['value'] = newvalue
415        return ""
416
417    def ParseDscFile (self, DscFile, FvDir):
418        Hardcode = False
419        AutoAlign = False
420        self._CfgItemList = []
421        self._CfgPageDict = {}
422        self._CfgBlkDict  = {}
423        self._DscFile     = DscFile
424        self._FvDir       = FvDir
425
426        IsDefSect       = False
427        IsPcdSect       = False
428        IsUpdSect       = False
429        IsVpdSect       = False
430
431        IfStack         = []
432        ElifStack       = []
433        Error           = 0
434        ConfigDict      = {}
435
436        DscFd        = open(DscFile, "r")
437        DscLines     = DscFd.readlines()
438        DscFd.close()
439
440        MaxAlign = 32   #Default align to 32, but if there are 64 bit unit, align to 64
441        SizeAlign = 0   #record the struct max align
442        Base = 0        #Starting offset of sub-structure.
443        while len(DscLines):
444            DscLine  = DscLines.pop(0).strip()
445            Handle   = False
446            Match    = re.match("^\[(.+)\]", DscLine)
447            if Match is not None:
448                IsDefSect = False
449                IsPcdSect = False
450                IsVpdSect = False
451                IsUpdSect = False
452                if  Match.group(1).lower() == "Defines".lower():
453                    IsDefSect = True
454                if  (Match.group(1).lower() == "PcdsFeatureFlag".lower() or Match.group(1).lower() == "PcdsFixedAtBuild".lower()):
455                    IsPcdSect = True
456                elif Match.group(1).lower() == "PcdsDynamicVpd.Upd".lower():
457                    ConfigDict = {}
458                    ConfigDict['header']  = 'ON'
459                    ConfigDict['region']  = 'UPD'
460                    ConfigDict['order']   = -1
461                    ConfigDict['page']    = ''
462                    ConfigDict['name']    = ''
463                    ConfigDict['find']    = ''
464                    ConfigDict['struct']  = ''
465                    ConfigDict['embed']   = ''
466                    ConfigDict['comment'] = ''
467                    ConfigDict['subreg']  = []
468                    IsUpdSect = True
469                    Offset    = 0
470            else:
471                if IsDefSect or IsPcdSect or IsUpdSect or IsVpdSect:
472                    if re.match("^!else($|\s+#.+)", DscLine):
473                        if IfStack:
474                            IfStack[-1] = not IfStack[-1]
475                        else:
476                            print("ERROR: No paired '!if' found for '!else' for line '%s'" % DscLine)
477                            raise SystemExit
478                    elif re.match("^!endif($|\s+#.+)", DscLine):
479                        if IfStack:
480                            IfStack.pop()
481                            Level = ElifStack.pop()
482                            if Level > 0:
483                                del IfStack[-Level:]
484                        else:
485                            print("ERROR: No paired '!if' found for '!endif' for line '%s'" % DscLine)
486                            raise SystemExit
487                    else:
488                        Result = False
489                        Match = re.match("!(ifdef|ifndef)\s+(.+)", DscLine)
490                        if Match:
491                            Result = self.EvaulateIfdef (Match.group(2))
492                            if Match.group(1) == 'ifndef':
493                                Result = not Result
494                            IfStack.append(Result)
495                            ElifStack.append(0)
496                        else:
497                            Match  = re.match("!(if|elseif)\s+(.+)", DscLine.split("#")[0])
498                            if Match:
499                                Result = self.EvaluateExpress(Match.group(2))
500                                if Match.group(1) == "if":
501                                    ElifStack.append(0)
502                                    IfStack.append(Result)
503                                else:   #elseif
504                                    if IfStack:
505                                        IfStack[-1] = not IfStack[-1]
506                                        IfStack.append(Result)
507                                        ElifStack[-1] = ElifStack[-1] + 1
508                                    else:
509                                        print("ERROR: No paired '!if' found for '!elif' for line '%s'" % DscLine)
510                                        raise SystemExit
511                            else:
512                                if IfStack:
513                                    Handle = reduce(lambda x,y: x and y, IfStack)
514                                else:
515                                    Handle = True
516                                if Handle:
517                                    Match = re.match("!include\s+(.+)", DscLine)
518                                    if Match:
519                                        IncludeFilePath = Match.group(1)
520                                        IncludeFilePath = self.ExpandMacros(IncludeFilePath)
521                                        PackagesPath = os.getenv("PACKAGES_PATH")
522                                        if PackagesPath:
523                                          for PackagePath in PackagesPath.split(os.pathsep):
524                                              IncludeFilePathAbs = os.path.join(os.path.normpath(PackagePath), os.path.normpath(IncludeFilePath))
525                                              if os.path.exists(IncludeFilePathAbs):
526                                                  IncludeDsc  = open(IncludeFilePathAbs, "r")
527                                                  break
528                                        else:
529                                          IncludeDsc  = open(IncludeFilePath, "r")
530                                        if IncludeDsc == None:
531                                            print("ERROR: Cannot open file '%s'" % IncludeFilePath)
532                                            raise SystemExit
533                                        NewDscLines = IncludeDsc.readlines()
534                                        IncludeDsc.close()
535                                        DscLines = NewDscLines + DscLines
536                                        Offset = 0
537                                    else:
538                                        if DscLine.startswith('!'):
539                                            print("ERROR: Unrecognized directive for line '%s'" % DscLine)
540                                            raise SystemExit
541            if not Handle:
542                continue
543
544            if IsDefSect:
545                #DEFINE UPD_TOOL_GUID       = 8C3D856A-9BE6-468E-850A-24F7A8D38E09
546                #DEFINE FSP_T_UPD_TOOL_GUID = 34686CA3-34F9-4901-B82A-BA630F0714C6
547                #DEFINE FSP_M_UPD_TOOL_GUID = 39A250DB-E465-4DD1-A2AC-E2BD3C0E2385
548                #DEFINE FSP_S_UPD_TOOL_GUID = CAE3605B-5B34-4C85-B3D7-27D54273C40F
549                Match = re.match("^\s*(?:DEFINE\s+)*(\w+)\s*=\s*([/$()-.\w]+)", DscLine)
550                if Match:
551                    self._MacroDict[Match.group(1)] = self.ExpandMacros(Match.group(2))
552                    if self.Debug:
553                        print ("INFO : DEFINE %s = [ %s ]" % (Match.group(1), self.ExpandMacros(Match.group(2))))
554            elif IsPcdSect:
555                #gSiPkgTokenSpaceGuid.PcdTxtEnable|FALSE
556                #gSiPkgTokenSpaceGuid.PcdOverclockEnable|TRUE
557                Match = re.match("^\s*([\w\.]+)\s*\|\s*(\w+)", DscLine)
558                if Match:
559                    self._PcdsDict[Match.group(1)] = Match.group(2)
560                    if self.Debug:
561                        print ("INFO : PCD %s = [ %s ]" % (Match.group(1), Match.group(2)))
562                    i = 0
563                    while i < len(BuildOptionPcd):
564                        Match = re.match("\s*([\w\.]+)\s*\=\s*(\w+)", BuildOptionPcd[i])
565                        if Match:
566                            self._PcdsDict[Match.group(1)] = Match.group(2)
567                        i += 1
568            else:
569                Match = re.match("^\s*#\s+(!BSF|@Bsf|!HDR)\s+(.+)", DscLine)
570                if Match:
571                    Remaining = Match.group(2)
572                    if Match.group(1) == '!BSF' or Match.group(1) == '@Bsf':
573                        Match = re.match("(?:^|.+\s+)PAGES:{(.+?)}", Remaining)
574                        if Match:
575                            # !BSF PAGES:{HSW:"Haswell System Agent", LPT:"Lynx Point PCH"}
576                            PageList = Match.group(1).split(',')
577                            for Page in PageList:
578                                Page  = Page.strip()
579                                Match = re.match("(\w+):\"(.+)\"", Page)
580                                self._CfgPageDict[Match.group(1)] = Match.group(2)
581
582                        Match = re.match("(?:^|.+\s+)BLOCK:{NAME:\"(.+)\"\s*,\s*VER:\"(.+)\"\s*}", Remaining)
583                        if Match:
584                            self._CfgBlkDict['name'] = Match.group(1)
585                            self._CfgBlkDict['ver']  = Match.group(2)
586
587                        for Key in self._BsfKeyList:
588                            Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining)
589                            if Match:
590                                if Key in ['NAME', 'HELP', 'OPTION'] and Match.group(1).startswith('+'):
591                                    ConfigDict[Key.lower()] += Match.group(1)[1:]
592                                else:
593                                    ConfigDict[Key.lower()]  = Match.group(1)
594                    else:
595                        for Key in self._HdrKeyList:
596                            Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining)
597                            if Match:
598                                ConfigDict[Key.lower()]  = Match.group(1)
599
600                Match = re.match("^\s*#\s+@Prompt\s+(.+)", DscLine)
601                if Match:
602                    ConfigDict['name'] = Match.group(1)
603
604                Match = re.match("^\s*#\s*@ValidList\s*(.+)\s*\|\s*(.+)\s*\|\s*(.+)\s*", DscLine)
605                if Match:
606                    if Match.group(2).strip() in self._BuidinOption:
607                        ConfigDict['option'] = Match.group(2).strip()
608                    else:
609                        OptionValueList = Match.group(2).split(',')
610                        OptionStringList = Match.group(3).split(',')
611                        Index = 0
612                        for Option in OptionValueList:
613                             Option = Option.strip()
614                             ConfigDict['option'] = ConfigDict['option'] + str(Option) + ':' + OptionStringList[Index].strip()
615                             Index += 1
616                             if Index in range(len(OptionValueList)):
617                                 ConfigDict['option'] += ', '
618                    ConfigDict['type'] = "Combo"
619
620                Match = re.match("^\s*#\s*@ValidRange\s*(.+)\s*\|\s*(.+)\s*-\s*(.+)\s*", DscLine)
621                if Match:
622                    if "0x" in Match.group(2) or "0x" in Match.group(3):
623                       ConfigDict['type'] = "EditNum, HEX, (%s,%s)" % (Match.group(2), Match.group(3))
624                    else:
625                       ConfigDict['type'] = "EditNum, DEC, (%s,%s)" % (Match.group(2), Match.group(3))
626
627                Match = re.match("^\s*##\s+(.+)", DscLine)
628                if Match:
629                    ConfigDict['help'] = Match.group(1)
630
631                # Check VPD/UPD
632                if IsUpdSect:
633                    Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+|\*)\s*\|\s*(\d+|0x[0-9a-fA-F]+)\s*\|\s*(.+)",DscLine)
634                else:
635                    Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+)(?:\s*\|\s*(.+))?",  DscLine)
636                if Match:
637                    ConfigDict['space']  = Match.group(1)
638                    ConfigDict['cname']  = Match.group(2)
639                    if Match.group(3) != '*':
640                        Hardcode = True
641                        Offset =  int (Match.group(3), 16)
642                    else:
643                        AutoAlign = True
644
645                    if Hardcode and AutoAlign:
646                        print("Hardcode and auto-align mixed mode is not supported by GenCfgOpt")
647                        raise SystemExit
648                    ConfigDict['offset'] = Offset
649                    if ConfigDict['order'] == -1:
650                        ConfigDict['order'] = ConfigDict['offset'] << 8
651                    else:
652                        (Major, Minor) = ConfigDict['order'].split('.')
653                        ConfigDict['order'] = (int (Major, 16) << 8 ) +  int (Minor, 16)
654                    if IsUpdSect:
655                        Value = Match.group(5).strip()
656                        if Match.group(4).startswith("0x"):
657                            Length  = int (Match.group(4), 16)
658                        else :
659                            Length  = int (Match.group(4))
660                        Offset += Length
661                    else:
662                        Value = Match.group(4)
663                        if Value is None:
664                            Value = ''
665                        Value = Value.strip()
666                        if '|' in Value:
667                            Match = re.match("^.+\s*\|\s*(.+)", Value)
668                            if Match:
669                                Value = Match.group(1)
670                        Length = -1
671
672                    ConfigDict['length'] = Length
673                    Match = re.match("\$\((\w+)\)", Value)
674                    if Match:
675                        if Match.group(1) in self._MacroDict:
676                            Value = self._MacroDict[Match.group(1)]
677
678                    ConfigDict['value']  = Value
679                    if (len(Value) > 0)  and (Value[0] == '{'):
680                        Value = self.FormatListValue(ConfigDict)
681
682                    if ConfigDict['name']  == '':
683                        # Clear BSF specific items
684                        ConfigDict['bsfname']   = ''
685                        ConfigDict['help']   = ''
686                        ConfigDict['type']   = ''
687                        ConfigDict['option'] = ''
688                    if IsUpdSect and AutoAlign:
689                        ItemLength = int(ConfigDict['length'])
690                        ItemOffset = int(ConfigDict['offset'])
691                        ItemStruct = ConfigDict['struct']
692                        Unit = 1
693                        if ItemLength in [1, 2, 4, 8] and not ConfigDict['value'].startswith('{'):
694                            Unit = ItemLength
695                            # If there are 64 bit unit, align to 64
696                            if Unit == 8:
697                                MaxAlign = 64
698                                SizeAlign = 8
699                        if ItemStruct != '':
700                            UnitDict = {'UINT8':1, 'UINT16':2, 'UINT32':4, 'UINT64':8}
701                            if ItemStruct in ['UINT8', 'UINT16', 'UINT32', 'UINT64']:
702                                Unit = UnitDict[ItemStruct]
703                                # If there are 64 bit unit, align to 64
704                                if Unit == 8:
705                                    MaxAlign = 64
706                                SizeAlign = max(SizeAlign, Unit)
707                        if (ConfigDict['embed'].find(':START') != -1):
708                            Base = ItemOffset
709                        SubOffset = ItemOffset - Base
710                        SubRemainder = SubOffset % Unit
711                        if SubRemainder:
712                            Diff = Unit - SubRemainder
713                            Offset = Offset + Diff
714                            ItemOffset = ItemOffset + Diff
715
716                        if (ConfigDict['embed'].find(':END') != -1):
717                            Remainder = Offset % (MaxAlign/8)   # MaxAlign is either 32 or 64
718                            if Remainder:
719                                Diff = int((MaxAlign/8) - Remainder)
720                                Offset = Offset + Diff
721                                ItemOffset = ItemOffset + Diff
722                            MaxAlign = 32                       # Reset to default 32 align when struct end
723                        if (ConfigDict['cname'] == 'UpdTerminator'):
724                            # ItemLength is the size of UpdTerminator
725                            # Itemlength might be 16, 32, or 64
726                            # Struct align to 64 if UpdTerminator
727                            # or struct size is 64 bit, else align to 32
728                            Remainder = Offset % max(ItemLength/8, 4, SizeAlign)
729                            Offset = Offset + ItemLength
730                            if Remainder:
731                                Diff = int(max(ItemLength/8, 4, SizeAlign) - Remainder)
732                                ItemOffset = ItemOffset + Diff
733                        ConfigDict['offset'] = ItemOffset
734
735                    self._CfgItemList.append(ConfigDict.copy())
736                    ConfigDict['name']   = ''
737                    ConfigDict['find']   = ''
738                    ConfigDict['struct'] = ''
739                    ConfigDict['embed']  = ''
740                    ConfigDict['comment'] = ''
741                    ConfigDict['order']  = -1
742                    ConfigDict['subreg'] = []
743                    ConfigDict['option'] = ''
744                else:
745                    # It could be a virtual item as below
746                    # !BSF FIELD:{SerialDebugPortAddress0:1}
747                    # or
748                    # @Bsf FIELD:{SerialDebugPortAddress0:1b}
749                    Match = re.match("^\s*#\s+(!BSF|@Bsf)\s+FIELD:{(.+):(\d+)([Bb])?}", DscLine)
750                    if Match:
751                        SubCfgDict = ConfigDict.copy()
752                        if (Match.group(4) == None) or (Match.group(4) == 'B'):
753                          UnitBitLen = 8
754                        elif Match.group(4) == 'b':
755                          UnitBitLen = 1
756                        else:
757                          print("ERROR: Invalide BSF FIELD length for line '%s'" % DscLine)
758                          raise SystemExit
759                        SubCfgDict['cname']  = Match.group(2)
760                        SubCfgDict['bitlength'] = int (Match.group(3)) * UnitBitLen
761                        if SubCfgDict['bitlength'] > 0:
762                            LastItem =  self._CfgItemList[-1]
763                            if len(LastItem['subreg']) == 0:
764                                SubOffset  = 0
765                            else:
766                                SubOffset  = LastItem['subreg'][-1]['bitoffset'] + LastItem['subreg'][-1]['bitlength']
767                            SubCfgDict['bitoffset'] = SubOffset
768                            LastItem['subreg'].append (SubCfgDict.copy())
769                        ConfigDict['name']   = ''
770        return Error
771
772    def GetBsfBitFields (self, subitem, bytes):
773        start = subitem['bitoffset']
774        end   = start + subitem['bitlength']
775        bitsvalue = ''.join('{0:08b}'.format(i) for i in bytes[::-1])
776        bitsvalue = bitsvalue[::-1]
777        bitslen   = len(bitsvalue)
778        if start > bitslen or end > bitslen:
779            print ("Invalid bits offset [%d,%d] for %s" % (start, end, subitem['name']))
780            raise SystemExit
781        return hex(int(bitsvalue[start:end][::-1], 2))
782
783    def UpdateSubRegionDefaultValue (self):
784        Error = 0
785        for Item in self._CfgItemList:
786            if len(Item['subreg']) == 0:
787                continue
788            bytearray = []
789            if Item['value'][0] == '{':
790                binlist = Item['value'][1:-1].split(',')
791                for each in binlist:
792                    each = each.strip()
793                    if each.startswith('0x'):
794                        value = int(each, 16)
795                    else:
796                        value = int(each)
797                    bytearray.append(value)
798            else:
799                if Item['value'].startswith('0x'):
800                    value = int(Item['value'], 16)
801                else:
802                    value = int(Item['value'])
803                idx = 0
804                while  idx < Item['length']:
805                    bytearray.append(value & 0xFF)
806                    value = value >> 8
807                    idx = idx + 1
808            for SubItem in Item['subreg']:
809                valuestr = self.GetBsfBitFields(SubItem, bytearray)
810                SubItem['value'] = valuestr
811        return Error
812
813    def CreateSplitUpdTxt (self, UpdTxtFile):
814        GuidList = ['FSP_T_UPD_TOOL_GUID','FSP_M_UPD_TOOL_GUID','FSP_S_UPD_TOOL_GUID']
815        SignatureList = ['0x545F', '0x4D5F','0x535F']        #  _T, _M, and _S signature for FSPT, FSPM, FSPS
816        for Index in range(len(GuidList)):
817            UpdTxtFile = ''
818            FvDir = self._FvDir
819            if GuidList[Index] not in self._MacroDict:
820                self.Error = "%s definition is missing in DSC file" % (GuidList[Index])
821                return 1
822
823            if UpdTxtFile == '':
824                UpdTxtFile = os.path.join(FvDir, self._MacroDict[GuidList[Index]] + '.txt')
825
826            ReCreate = False
827            if not os.path.exists(UpdTxtFile):
828                ReCreate = True
829            else:
830                DscTime = os.path.getmtime(self._DscFile)
831                TxtTime = os.path.getmtime(UpdTxtFile)
832                if DscTime > TxtTime:
833                    ReCreate = True
834
835            if not  ReCreate:
836                # DSC has not been modified yet
837                # So don't have to re-generate other files
838                self.Error = 'No DSC file change, skip to create UPD TXT file'
839                return 256
840
841            TxtFd = open(UpdTxtFile, "w")
842            TxtFd.write("%s\n"   % (__copyright_txt__ % date.today().year))
843
844            NextOffset = 0
845            SpaceIdx   = 0
846            StartAddr  = 0
847            EndAddr    = 0
848            Default = 'DEFAULT|'
849            InRange = False
850            for Item in self._CfgItemList:
851                if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]:
852                    StartAddr = Item['offset']
853                    NextOffset = StartAddr
854                    InRange = True
855                if Item['cname'] == 'UpdTerminator' and InRange == True:
856                    EndAddr = Item['offset']
857                    InRange = False
858            InRange = False
859            for Item in self._CfgItemList:
860                if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]:
861                    InRange = True
862                if InRange != True:
863                    continue
864                if Item['cname'] == 'UpdTerminator':
865                    InRange = False
866                if Item['region'] != 'UPD':
867                    continue
868                Offset = Item['offset']
869                if StartAddr > Offset or EndAddr < Offset:
870                    continue
871                if NextOffset < Offset:
872                    # insert one line
873                    TxtFd.write("%s.UnusedUpdSpace%d|%s0x%04X|0x%04X|{0}\n" % (Item['space'], SpaceIdx, Default, NextOffset - StartAddr, Offset - NextOffset))
874                    SpaceIdx = SpaceIdx + 1
875                NextOffset = Offset + Item['length']
876                TxtFd.write("%s.%s|%s0x%04X|%s|%s\n" % (Item['space'],Item['cname'],Default,Item['offset'] - StartAddr,Item['length'],Item['value']))
877            TxtFd.close()
878        return 0
879
880    def ProcessMultilines (self, String, MaxCharLength):
881            Multilines = ''
882            StringLength = len(String)
883            CurrentStringStart = 0
884            StringOffset = 0
885            BreakLineDict = []
886            if len(String) <= MaxCharLength:
887                while (StringOffset < StringLength):
888                    if StringOffset >= 1:
889                        if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n':
890                            BreakLineDict.append (StringOffset + 1)
891                    StringOffset += 1
892                if BreakLineDict != []:
893                    for Each in BreakLineDict:
894                        Multilines += "  %s\n" % String[CurrentStringStart:Each].lstrip()
895                        CurrentStringStart = Each
896                    if StringLength - CurrentStringStart > 0:
897                        Multilines += "  %s\n" % String[CurrentStringStart:].lstrip()
898                else:
899                    Multilines = "  %s\n" % String
900            else:
901                NewLineStart = 0
902                NewLineCount = 0
903                FoundSpaceChar = False
904                while (StringOffset < StringLength):
905                    if StringOffset >= 1:
906                        if NewLineCount >= MaxCharLength - 1:
907                            if String[StringOffset] == ' ' and StringLength - StringOffset > 10:
908                                BreakLineDict.append (NewLineStart + NewLineCount)
909                                NewLineStart = NewLineStart + NewLineCount
910                                NewLineCount = 0
911                                FoundSpaceChar = True
912                            elif StringOffset == StringLength - 1 and FoundSpaceChar == False:
913                                BreakLineDict.append (0)
914                        if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n':
915                            BreakLineDict.append (StringOffset + 1)
916                            NewLineStart = StringOffset + 1
917                            NewLineCount = 0
918                    StringOffset += 1
919                    NewLineCount += 1
920                if BreakLineDict != []:
921                    BreakLineDict.sort ()
922                    for Each in BreakLineDict:
923                        if Each > 0:
924                            Multilines += "  %s\n" % String[CurrentStringStart:Each].lstrip()
925                        CurrentStringStart = Each
926                    if StringLength - CurrentStringStart > 0:
927                        Multilines += "  %s\n" % String[CurrentStringStart:].lstrip()
928            return Multilines
929
930    def CreateField (self, Item, Name, Length, Offset, Struct, BsfName, Help, Option):
931        PosName    = 28
932        PosComment = 30
933        NameLine=''
934        HelpLine=''
935        OptionLine=''
936
937        IsArray = False
938        if Length in [1,2,4,8]:
939            Type = "UINT%d" % (Length * 8)
940            if Name.startswith("UnusedUpdSpace") and Length != 1:
941                IsArray = True
942                Type = "UINT8"
943        else:
944            IsArray = True
945            Type = "UINT8"
946
947        if Item and Item['value'].startswith('{'):
948            Type = "UINT8"
949            IsArray = True
950
951        if Struct != '':
952            Type = Struct
953            if Struct in ['UINT8','UINT16','UINT32','UINT64']:
954                IsArray = True
955                Unit = int(Type[4:]) / 8
956                Length = Length / Unit
957            else:
958                IsArray = False
959
960        if IsArray:
961            Name = Name + '[%d]' % Length
962
963        if len(Type) < PosName:
964            Space1 = PosName - len(Type)
965        else:
966            Space1 = 1
967
968        if BsfName != '':
969            NameLine=" - %s\n" % BsfName
970        else:
971            NameLine="\n"
972
973        if Help != '':
974            HelpLine = self.ProcessMultilines (Help, 80)
975
976        if Option != '':
977            OptionLine = self.ProcessMultilines (Option, 80)
978
979        if Offset is None:
980            OffsetStr = '????'
981        else:
982            OffsetStr = '0x%04X' % Offset
983
984        return "\n/** Offset %s%s%s%s**/\n  %s%s%s;\n" % (OffsetStr, NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name,)
985
986    def PostProcessBody (self, TextBody):
987        NewTextBody = []
988        OldTextBody = []
989        IncludeLine = False
990        StructName  = ''
991        VariableName = ''
992        IsUpdHdrDefined = False
993        IsUpdHeader = False
994        for Line in TextBody:
995           SplitToLines = Line.splitlines()
996           MatchComment = re.match("^/\*\sCOMMENT:(\w+):([\w|\W|\s]+)\s\*/\s([\s\S]*)", SplitToLines[0])
997           if MatchComment:
998              if MatchComment.group(1) == 'FSP_UPD_HEADER':
999                  IsUpdHeader = True
1000              else:
1001                  IsUpdHeader = False
1002              if IsUpdHdrDefined != True or IsUpdHeader != True:
1003                CommentLine = " " + MatchComment.group(2) + "\n"
1004                NewTextBody.append("/**" + CommentLine + "**/\n")
1005              Line = Line[(len(SplitToLines[0]) + 1):]
1006
1007           Match = re.match("^/\*\sEMBED_STRUCT:(\w+):(\w+):(START|END)\s\*/\s([\s\S]*)", Line)
1008           if Match:
1009               Line = Match.group(4)
1010               if Match.group(1) == 'FSP_UPD_HEADER':
1011                   IsUpdHeader = True
1012               else:
1013                   IsUpdHeader = False
1014
1015           if Match and Match.group(3) == 'START':
1016               if IsUpdHdrDefined != True or IsUpdHeader != True:
1017                   NewTextBody.append ('typedef struct {\n')
1018               StructName   = Match.group(1)
1019               VariableName = Match.group(2)
1020               MatchOffset = re.search('/\*\*\sOffset\s0x([a-fA-F0-9]+)', Line)
1021               if MatchOffset:
1022                   Offset = int(MatchOffset.group(1), 16)
1023               else:
1024                   Offset = None
1025               Line
1026               IncludeLine = True
1027               OldTextBody.append (self.CreateField (None, VariableName, 0, Offset, StructName, '', '', ''))
1028           if IncludeLine:
1029               if IsUpdHdrDefined != True or IsUpdHeader != True:
1030                   NewTextBody.append (Line)
1031           else:
1032               OldTextBody.append (Line)
1033
1034           if Match and Match.group(3) == 'END':
1035               if (StructName != Match.group(1)) or (VariableName != Match.group(2)):
1036                   print ("Unmatched struct name '%s' and '%s' !"  % (StructName, Match.group(1)))
1037               else:
1038                   if IsUpdHdrDefined != True or IsUpdHeader != True:
1039                      NewTextBody.append ('} %s;\n\n' %  StructName)
1040                      IsUpdHdrDefined = True
1041               IncludeLine = False
1042        NewTextBody.extend(OldTextBody)
1043        return NewTextBody
1044
1045    def WriteLinesWithoutTailingSpace (self, HeaderFd, Line):
1046        TxtBody2 = Line.splitlines(True)
1047        for Line2 in TxtBody2:
1048            Line2 = Line2.rstrip()
1049            Line2 += '\n'
1050            HeaderFd.write (Line2)
1051        return 0
1052    def CreateHeaderFile (self, InputHeaderFile):
1053        FvDir = self._FvDir
1054
1055        HeaderFileName = 'FspUpd.h'
1056        HeaderFile = os.path.join(FvDir, HeaderFileName)
1057
1058        # Check if header needs to be recreated
1059        ReCreate = False
1060
1061        TxtBody = []
1062        for Item in self._CfgItemList:
1063           if str(Item['cname']) == 'Signature' and Item['length'] == 8:
1064               Value = int(Item['value'], 16)
1065               Chars = []
1066               while Value != 0x0:
1067                   Chars.append(chr(Value & 0xFF))
1068                   Value = Value >> 8
1069               SignatureStr = ''.join(Chars)
1070               # Signature will be _T / _M / _S for FSPT / FSPM / FSPS accordingly
1071               if '_T' in SignatureStr[6:6+2]:
1072                   TxtBody.append("#define FSPT_UPD_SIGNATURE               %s        /* '%s' */\n\n" % (Item['value'], SignatureStr))
1073               elif '_M' in SignatureStr[6:6+2]:
1074                   TxtBody.append("#define FSPM_UPD_SIGNATURE               %s        /* '%s' */\n\n" % (Item['value'], SignatureStr))
1075               elif '_S' in SignatureStr[6:6+2]:
1076                   TxtBody.append("#define FSPS_UPD_SIGNATURE               %s        /* '%s' */\n\n" % (Item['value'], SignatureStr))
1077        TxtBody.append("\n")
1078
1079        for Region in ['UPD']:
1080            UpdOffsetTable = []
1081            UpdSignature = ['0x545F', '0x4D5F', '0x535F']   #['_T', '_M', '_S'] signature for FSPT, FSPM, FSPS
1082            UpdStructure = ['FSPT_UPD', 'FSPM_UPD', 'FSPS_UPD']
1083            for Item in self._CfgItemList:
1084                if Item["cname"] == 'Signature' and Item["value"][0:6] in UpdSignature:
1085                    UpdOffsetTable.append (Item["offset"])
1086
1087            for UpdIdx in range(len(UpdOffsetTable)):
1088                CommentLine = ""
1089                for Item in self._CfgItemList:
1090                    if Item["comment"] != '' and Item["offset"] >= UpdOffsetTable[UpdIdx]:
1091                        MatchComment = re.match("^(U|V)PD_DATA_REGION:([\w|\W|\s]+)", Item["comment"])
1092                        if MatchComment and MatchComment.group(1) == Region[0]:
1093                            CommentLine = " " + MatchComment.group(2) + "\n"
1094                            TxtBody.append("/**" + CommentLine + "**/\n")
1095                    elif Item["offset"] >= UpdOffsetTable[UpdIdx] and Item["comment"] == '':
1096                        Match = re.match("^FSP([\w|\W|\s])_UPD", UpdStructure[UpdIdx])
1097                        if Match:
1098                            TxtBody.append("/** Fsp " + Match.group(1) + " UPD Configuration\n**/\n")
1099                TxtBody.append("typedef struct {\n")
1100                NextOffset  = 0
1101                SpaceIdx    = 0
1102                Offset      = 0
1103
1104                LastVisible = True
1105                ResvOffset  = 0
1106                ResvIdx     = 0
1107                LineBuffer  = []
1108                InRange = False
1109                for Item in self._CfgItemList:
1110                    if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == UpdSignature[UpdIdx] or Region[0] == 'V':
1111                        InRange = True
1112                    if InRange != True:
1113                        continue
1114                    if Item['cname'] == 'UpdTerminator':
1115                        InRange = False
1116
1117                    if Item['region'] != Region:
1118                        continue
1119
1120                    if Item["offset"] < UpdOffsetTable[UpdIdx]:
1121                        continue
1122
1123                    NextVisible = LastVisible
1124
1125                    if LastVisible and (Item['header'] == 'OFF'):
1126                        NextVisible = False
1127                        ResvOffset  = Item['offset']
1128                    elif (not LastVisible) and Item['header'] == 'ON':
1129                        NextVisible = True
1130                        Name = "Reserved" + Region[0] + "pdSpace%d" % ResvIdx
1131                        ResvIdx = ResvIdx + 1
1132                        TxtBody.append(self.CreateField (Item, Name, Item["offset"] - ResvOffset, ResvOffset, '', '', '', ''))
1133
1134                    if  Offset < Item["offset"]:
1135                        if LastVisible:
1136                            Name = "Unused" + Region[0] + "pdSpace%d" % SpaceIdx
1137                            LineBuffer.append(self.CreateField (Item, Name, Item["offset"] - Offset, Offset, '', '', '', ''))
1138                        SpaceIdx = SpaceIdx + 1
1139                        Offset   = Item["offset"]
1140
1141                    LastVisible = NextVisible
1142
1143                    Offset = Offset + Item["length"]
1144                    if LastVisible:
1145                        for Each in LineBuffer:
1146                            TxtBody.append (Each)
1147                        LineBuffer = []
1148                        Comment = Item["comment"]
1149                        Embed = Item["embed"].upper()
1150                        if Embed.endswith(':START') or Embed.endswith(':END'):
1151                            if not Comment == '' and Embed.endswith(':START'):
1152                               Marker = '/* COMMENT:%s */ \n' % Item["comment"]
1153                               Marker = Marker + '/* EMBED_STRUCT:%s */ ' % Item["embed"]
1154                            else:
1155                               Marker = '/* EMBED_STRUCT:%s */ ' % Item["embed"]
1156                        else:
1157                            if Embed == '':
1158                                Marker = ''
1159                            else:
1160                                self.Error = "Invalid embedded structure format '%s'!\n" % Item["embed"]
1161                                return 4
1162                        Line = Marker + self.CreateField (Item, Item["cname"], Item["length"], Item["offset"], Item['struct'], Item['name'], Item['help'], Item['option'])
1163                        TxtBody.append(Line)
1164                    if Item['cname'] == 'UpdTerminator':
1165                        break
1166                TxtBody.append("} " + UpdStructure[UpdIdx] + ";\n\n")
1167
1168        # Handle the embedded data structure
1169        TxtBody = self.PostProcessBody (TxtBody)
1170
1171        HeaderTFileName = 'FsptUpd.h'
1172        HeaderMFileName = 'FspmUpd.h'
1173        HeaderSFileName = 'FspsUpd.h'
1174
1175        UpdRegionCheck = ['FSPT', 'FSPM', 'FSPS']     # FSPX_UPD_REGION
1176        UpdConfigCheck = ['FSP_T', 'FSP_M', 'FSP_S']  # FSP_X_CONFIG, FSP_X_TEST_CONFIG, FSP_X_RESTRICTED_CONFIG
1177        UpdSignatureCheck = ['FSPT_UPD_SIGNATURE', 'FSPM_UPD_SIGNATURE', 'FSPS_UPD_SIGNATURE']
1178        ExcludedSpecificUpd = ['FSPT_ARCH_UPD', 'FSPM_ARCH_UPD', 'FSPS_ARCH_UPD']
1179
1180        if InputHeaderFile != '':
1181            if not os.path.exists(InputHeaderFile):
1182                 self.Error = "Input header file '%s' does not exist" % InputHeaderFile
1183                 return 6
1184
1185            InFd         = open(InputHeaderFile, "r")
1186            IncLines     = InFd.readlines()
1187            InFd.close()
1188
1189        for item in range(len(UpdRegionCheck)):
1190            if UpdRegionCheck[item] == 'FSPT':
1191                HeaderFd = open(os.path.join(FvDir, HeaderTFileName), "w")
1192                FileBase = os.path.basename(os.path.join(FvDir, HeaderTFileName))
1193            elif UpdRegionCheck[item] == 'FSPM':
1194                HeaderFd = open(os.path.join(FvDir, HeaderMFileName), "w")
1195                FileBase = os.path.basename(os.path.join(FvDir, HeaderMFileName))
1196            elif UpdRegionCheck[item] == 'FSPS':
1197                HeaderFd = open(os.path.join(FvDir, HeaderSFileName), "w")
1198                FileBase = os.path.basename(os.path.join(FvDir, HeaderSFileName))
1199            FileName = FileBase.replace(".", "_").upper()
1200            HeaderFd.write("%s\n"   % (__copyright_h__ % date.today().year))
1201            HeaderFd.write("#ifndef __%s__\n"   % FileName)
1202            HeaderFd.write("#define __%s__\n\n" % FileName)
1203            HeaderFd.write("#include <%s>\n\n" % HeaderFileName)
1204            HeaderFd.write("#pragma pack(1)\n\n")
1205
1206            Export = False
1207            for Line in IncLines:
1208                Match = re.search ("!EXPORT\s+([A-Z]+)\s+EXTERNAL_BOOTLOADER_STRUCT_(BEGIN|END)\s+", Line)
1209                if Match:
1210                    if Match.group(2) == "BEGIN" and Match.group(1) == UpdRegionCheck[item]:
1211                        Export = True
1212                        continue
1213                    else:
1214                        Export = False
1215                        continue
1216                if Export:
1217                    HeaderFd.write(Line)
1218            HeaderFd.write("\n")
1219
1220            Index = 0
1221            StartIndex = 0
1222            EndIndex = 0
1223            StructStart = []
1224            StructStartWithComment = []
1225            StructEnd = []
1226            for Line in TxtBody:
1227                Index += 1
1228                Match = re.match("(typedef struct {)", Line)
1229                if Match:
1230                    StartIndex = Index - 1
1231                Match = re.match("}\s([_A-Z0-9]+);", Line)
1232                if Match and (UpdRegionCheck[item] in Match.group(1) or UpdConfigCheck[item] in Match.group(1)) and (ExcludedSpecificUpd[item] not in Match.group(1)):
1233                    EndIndex = Index
1234                    StructStart.append(StartIndex)
1235                    StructEnd.append(EndIndex)
1236            Index = 0
1237            for Line in TxtBody:
1238                Index += 1
1239                for Item in range(len(StructStart)):
1240                    if Index == StructStart[Item]:
1241                        Match = re.match("^(/\*\*\s*)", Line)
1242                        if Match:
1243                            StructStartWithComment.append(StructStart[Item])
1244                        else:
1245                            StructStartWithComment.append(StructStart[Item] + 1)
1246            Index = 0
1247            for Line in TxtBody:
1248                Index += 1
1249                for Item in range(len(StructStart)):
1250                    if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]:
1251                        self.WriteLinesWithoutTailingSpace(HeaderFd, Line)
1252            HeaderFd.write("#pragma pack()\n\n")
1253            HeaderFd.write("#endif\n")
1254            HeaderFd.close()
1255
1256        HeaderFd = open(HeaderFile, "w")
1257        FileBase = os.path.basename(HeaderFile)
1258        FileName = FileBase.replace(".", "_").upper()
1259        HeaderFd.write("%s\n"   % (__copyright_h__ % date.today().year))
1260        HeaderFd.write("#ifndef __%s__\n"   % FileName)
1261        HeaderFd.write("#define __%s__\n\n" % FileName)
1262        HeaderFd.write("#include <FspEas.h>\n\n")
1263        HeaderFd.write("#pragma pack(1)\n\n")
1264
1265        for item in range(len(UpdRegionCheck)):
1266            Index = 0
1267            StartIndex = 0
1268            EndIndex = 0
1269            StructStart = []
1270            StructStartWithComment = []
1271            StructEnd = []
1272            for Line in TxtBody:
1273                Index += 1
1274                Match = re.match("(typedef struct {)", Line)
1275                if Match:
1276                    StartIndex = Index - 1
1277                Match = re.match("#define\s([_A-Z0-9]+)\s*", Line)
1278                if Match and (UpdSignatureCheck[item] in Match.group(1) or UpdSignatureCheck[item] in Match.group(1)):
1279                    StructStart.append(Index - 1)
1280                    StructEnd.append(Index)
1281            Index = 0
1282            for Line in TxtBody:
1283                Index += 1
1284                for Item in range(len(StructStart)):
1285                    if Index == StructStart[Item]:
1286                        Match = re.match("^(/\*\*\s*)", Line)
1287                        if Match:
1288                            StructStartWithComment.append(StructStart[Item])
1289                        else:
1290                            StructStartWithComment.append(StructStart[Item] + 1)
1291            Index = 0
1292            for Line in TxtBody:
1293                Index += 1
1294                for Item in range(len(StructStart)):
1295                    if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]:
1296                        self.WriteLinesWithoutTailingSpace(HeaderFd, Line)
1297        HeaderFd.write("#pragma pack()\n\n")
1298        HeaderFd.write("#endif\n")
1299        HeaderFd.close()
1300
1301        return 0
1302
1303    def WriteBsfStruct  (self, BsfFd, Item):
1304        LogExpr = CLogicalExpression()
1305        if Item['type'] == "None":
1306            Space = "gPlatformFspPkgTokenSpaceGuid"
1307        else:
1308            Space = Item['space']
1309        Line = "    $%s_%s" % (Space, Item['cname'])
1310        Match = re.match("\s*\{([x0-9a-fA-F,\s]+)\}\s*", Item['value'])
1311        if Match:
1312            DefaultValue = Match.group(1).strip()
1313        else:
1314            DefaultValue = Item['value'].strip()
1315        if 'bitlength' in Item:
1316            BsfFd.write("    %s%s%4d bits     $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['bitlength'], DefaultValue))
1317        else:
1318            BsfFd.write("    %s%s%4d bytes    $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['length'], DefaultValue))
1319        TmpList = []
1320        if  Item['type'] == "Combo":
1321            if not Item['option'] in self._BuidinOption:
1322                OptList = Item['option'].split(',')
1323                for Option in OptList:
1324                    Option = Option.strip()
1325                    (OpVal, OpStr) = Option.split(':')
1326                    test = LogExpr.getNumber (OpVal)
1327                    if test is None:
1328                        raise Exception("Selection Index '%s' is not a number" % OpVal)
1329                    TmpList.append((OpVal, OpStr))
1330        return  TmpList
1331
1332    def WriteBsfOption  (self, BsfFd, Item):
1333        PcdName   = Item['space'] + '_' + Item['cname']
1334        WriteHelp = 0
1335        if Item['type'] == "Combo":
1336            if Item['option'] in self._BuidinOption:
1337                Options = self._BuidinOption[Item['option']]
1338            else:
1339                Options = PcdName
1340            BsfFd.write('    %s $%s, "%s", &%s,\n' % (Item['type'], PcdName, Item['name'], Options))
1341            WriteHelp = 1
1342        elif Item['type'].startswith("EditNum"):
1343            Match = re.match("EditNum\s*,\s*(HEX|DEC)\s*,\s*\((\d+|0x[0-9A-Fa-f]+)\s*,\s*(\d+|0x[0-9A-Fa-f]+)\)", Item['type'])
1344            if Match:
1345                BsfFd.write('    EditNum $%s, "%s", %s,\n' % (PcdName, Item['name'], Match.group(1)))
1346                WriteHelp = 2
1347        elif Item['type'].startswith("EditText"):
1348            BsfFd.write('    %s $%s, "%s",\n' % (Item['type'], PcdName, Item['name']))
1349            WriteHelp = 1
1350        elif Item['type'] == "Table":
1351            Columns = Item['option'].split(',')
1352            if len(Columns) != 0:
1353                BsfFd.write('    %s $%s "%s",' % (Item['type'], PcdName, Item['name']))
1354                for Col in Columns:
1355                    Fmt = Col.split(':')
1356                    if len(Fmt) != 3:
1357                        raise Exception("Column format '%s' is invalid !" % Fmt)
1358                    try:
1359                        Dtype = int(Fmt[1].strip())
1360                    except:
1361                        raise Exception("Column size '%s' is invalid !" % Fmt[1])
1362                    BsfFd.write('\n        Column "%s", %d bytes, %s' % (Fmt[0].strip(), Dtype, Fmt[2].strip()))
1363                BsfFd.write(',\n')
1364                WriteHelp = 1
1365
1366        if WriteHelp  > 0:
1367            HelpLines = Item['help'].split('\\n\\r')
1368            FirstLine = True
1369            for HelpLine in HelpLines:
1370                if FirstLine:
1371                    FirstLine = False
1372                    BsfFd.write('        Help "%s"\n' % (HelpLine))
1373                else:
1374                    BsfFd.write('             "%s"\n' % (HelpLine))
1375            if WriteHelp == 2:
1376                    BsfFd.write('             "Valid range: %s ~ %s"\n' % (Match.group(2), Match.group(3)))
1377
1378    def GenerateBsfFile (self, BsfFile):
1379
1380        if BsfFile == '':
1381            self.Error = "BSF output file '%s' is invalid" % BsfFile
1382            return 1
1383
1384        Error = 0
1385        OptionDict = {}
1386        BsfFd      = open(BsfFile, "w")
1387        BsfFd.write("%s\n" % (__copyright_bsf__ % date.today().year))
1388        BsfFd.write("%s\n" % self._GlobalDataDef)
1389        BsfFd.write("StructDef\n")
1390        NextOffset = -1
1391        for Item in self._CfgItemList:
1392            if Item['find'] != '':
1393                BsfFd.write('\n    Find "%s"\n' % Item['find'])
1394                NextOffset = Item['offset'] + Item['length']
1395            if Item['name'] != '':
1396                if NextOffset != Item['offset']:
1397                    BsfFd.write("        Skip %d bytes\n" % (Item['offset'] - NextOffset))
1398                if len(Item['subreg']) > 0:
1399                    NextOffset =  Item['offset']
1400                    BitsOffset =  NextOffset * 8
1401                    for SubItem in Item['subreg']:
1402                        BitsOffset += SubItem['bitlength']
1403                        if SubItem['name'] == '':
1404                            if 'bitlength' in SubItem:
1405                                BsfFd.write("        Skip %d bits\n" % (SubItem['bitlength']))
1406                            else:
1407                                BsfFd.write("        Skip %d bytes\n" % (SubItem['length']))
1408                        else:
1409                            Options = self.WriteBsfStruct(BsfFd, SubItem)
1410                            if len(Options) > 0:
1411                                OptionDict[SubItem['space']+'_'+SubItem['cname']] = Options
1412
1413                    NextBitsOffset = (Item['offset'] + Item['length']) * 8
1414                    if NextBitsOffset > BitsOffset:
1415                        BitsGap     = NextBitsOffset - BitsOffset
1416                        BitsRemain  = BitsGap % 8
1417                        if BitsRemain:
1418                            BsfFd.write("        Skip %d bits\n" % BitsRemain)
1419                            BitsGap -= BitsRemain
1420                        BytesRemain = int(BitsGap / 8)
1421                        if BytesRemain:
1422                            BsfFd.write("        Skip %d bytes\n" % BytesRemain)
1423                    NextOffset = Item['offset'] + Item['length']
1424                else:
1425                    NextOffset = Item['offset'] + Item['length']
1426                    Options = self.WriteBsfStruct(BsfFd, Item)
1427                    if len(Options) > 0:
1428                        OptionDict[Item['space']+'_'+Item['cname']] = Options
1429        BsfFd.write("\nEndStruct\n\n")
1430
1431        BsfFd.write("%s" % self._BuidinOptionTxt)
1432
1433        for Each in OptionDict:
1434            BsfFd.write("List &%s\n" % Each)
1435            for Item in OptionDict[Each]:
1436                BsfFd.write('    Selection %s , "%s"\n' % (Item[0], Item[1]))
1437            BsfFd.write("EndList\n\n")
1438
1439        BsfFd.write("BeginInfoBlock\n")
1440        BsfFd.write('    PPVer       "%s"\n' % (self._CfgBlkDict['ver']))
1441        BsfFd.write('    Description "%s"\n' % (self._CfgBlkDict['name']))
1442        BsfFd.write("EndInfoBlock\n\n")
1443
1444        for Each in self._CfgPageDict:
1445            BsfFd.write('Page "%s"\n' % self._CfgPageDict[Each])
1446            BsfItems = []
1447            for Item in self._CfgItemList:
1448                if Item['name'] != '':
1449                    if Item['page'] != Each:
1450                        continue
1451                    if len(Item['subreg']) > 0:
1452                        for SubItem in Item['subreg']:
1453                            if SubItem['name'] != '':
1454                                BsfItems.append(SubItem)
1455                    else:
1456                        BsfItems.append(Item)
1457
1458            BsfItems.sort(key=lambda x: x['order'])
1459
1460            for Item in BsfItems:
1461                self.WriteBsfOption (BsfFd, Item)
1462            BsfFd.write("EndPage\n\n")
1463
1464        BsfFd.close()
1465        return  Error
1466
1467
1468def Usage():
1469    print ("GenCfgOpt Version 0.55")
1470    print ("Usage:")
1471    print ("    GenCfgOpt  UPDTXT  PlatformDscFile BuildFvDir                 [-D Macros]")
1472    print ("    GenCfgOpt  HEADER  PlatformDscFile BuildFvDir  InputHFile     [-D Macros]")
1473    print ("    GenCfgOpt  GENBSF  PlatformDscFile BuildFvDir  BsfOutFile     [-D Macros]")
1474
1475def Main():
1476    #
1477    # Parse the options and args
1478    #
1479    i = 1
1480
1481    GenCfgOpt = CGenCfgOpt()
1482    while i < len(sys.argv):
1483        if sys.argv[i].strip().lower() == "--pcd":
1484            BuildOptionPcd.append(sys.argv[i+1])
1485            i += 1
1486        i += 1
1487    argc = len(sys.argv)
1488    if argc < 4:
1489        Usage()
1490        return 1
1491    else:
1492        DscFile = sys.argv[2]
1493        if not os.path.exists(DscFile):
1494            print ("ERROR: Cannot open DSC file '%s' !" % DscFile)
1495            return 2
1496
1497        OutFile = ''
1498        if argc > 4:
1499            if sys.argv[4][0] == '-':
1500                Start = 4
1501            else:
1502                OutFile = sys.argv[4]
1503                Start = 5
1504            if argc > Start:
1505                if GenCfgOpt.ParseMacros(sys.argv[Start:]) != 0:
1506                    print ("ERROR: Macro parsing failed !")
1507                    return 3
1508
1509        FvDir = sys.argv[3]
1510        if not os.path.exists(FvDir):
1511            os.makedirs(FvDir)
1512
1513        if GenCfgOpt.ParseDscFile(DscFile, FvDir) != 0:
1514            print ("ERROR: %s !" % GenCfgOpt.Error)
1515            return 5
1516
1517        if GenCfgOpt.UpdateSubRegionDefaultValue() != 0:
1518            print ("ERROR: %s !" % GenCfgOpt.Error)
1519            return 7
1520
1521        if sys.argv[1] == "UPDTXT":
1522            Ret = GenCfgOpt.CreateSplitUpdTxt(OutFile)
1523            if Ret != 0:
1524                # No change is detected
1525                if Ret == 256:
1526                    print ("INFO: %s !" % (GenCfgOpt.Error))
1527                else :
1528                    print ("ERROR: %s !" % (GenCfgOpt.Error))
1529            return Ret
1530        elif sys.argv[1] == "HEADER":
1531            if GenCfgOpt.CreateHeaderFile(OutFile) != 0:
1532                print ("ERROR: %s !" % GenCfgOpt.Error)
1533                return 8
1534        elif sys.argv[1] == "GENBSF":
1535            if GenCfgOpt.GenerateBsfFile(OutFile) != 0:
1536                print ("ERROR: %s !" % GenCfgOpt.Error)
1537                return 9
1538        else:
1539            if argc < 5:
1540                Usage()
1541                return 1
1542            print ("ERROR: Unknown command '%s' !" % sys.argv[1])
1543            Usage()
1544            return 1
1545        return 0
1546    return 0
1547
1548
1549if __name__ == '__main__':
1550    sys.exit(Main())
1551