diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py')
-rwxr-xr-x | src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py | 1791 |
1 files changed, 1791 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py new file mode 100755 index 00000000..34484c16 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py @@ -0,0 +1,1791 @@ +## @ GenCfgOpt.py +# +# Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +import os +import re +import sys +import struct +from datetime import date +from functools import reduce + +# Generated file copyright header + +__copyright_txt__ = """## @file +# +# THIS IS AUTO-GENERATED FILE BY BUILD TOOLS AND PLEASE DO NOT MAKE MODIFICATION. +# +# This file lists all VPD informations for a platform collected by build.exe. +# +# Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +""" + +__copyright_bsf__ = """/** @file + + Boot Setting File for Platform Configuration. + + Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + This file is automatically generated. Please do NOT modify !!! + +**/ + +""" + +__copyright_h__ = """/** @file + +Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of Intel Corporation nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + This file is automatically generated. Please do NOT modify !!! + +**/ +""" + +BuildOptionPcd = [] + +class CLogicalExpression: + def __init__(self): + self.index = 0 + self.string = '' + + def errExit(self, err = ''): + print ("ERROR: Express parsing for:") + print (" %s" % self.string) + print (" %s^" % (' ' * self.index)) + if err: + print ("INFO : %s" % err) + raise SystemExit + + def getNonNumber (self, n1, n2): + if not n1.isdigit(): + return n1 + if not n2.isdigit(): + return n2 + return None + + def getCurr(self, lens = 1): + try: + if lens == -1: + return self.string[self.index :] + else: + if self.index + lens > len(self.string): + lens = len(self.string) - self.index + return self.string[self.index : self.index + lens] + except Exception: + return '' + + def isLast(self): + return self.index == len(self.string) + + def moveNext(self, len = 1): + self.index += len + + def skipSpace(self): + while not self.isLast(): + if self.getCurr() in ' \t': + self.moveNext() + else: + return + + def normNumber (self, val): + return True if val else False + + def getNumber(self, var): + var = var.strip() + if re.match('^0x[a-fA-F0-9]+$', var): + value = int(var, 16) + elif re.match('^[+-]?\d+$', var): + value = int(var, 10) + else: + value = None + return value + + def parseValue(self): + self.skipSpace() + var = '' + while not self.isLast(): + char = self.getCurr() + if re.match('^[\w.]', char): + var += char + self.moveNext() + else: + break + val = self.getNumber(var) + if val is None: + value = var + else: + value = "%d" % val + return value + + def parseSingleOp(self): + self.skipSpace() + if re.match('^NOT\W', self.getCurr(-1)): + self.moveNext(3) + op = self.parseBrace() + val = self.getNumber (op) + if val is None: + self.errExit ("'%s' is not a number" % op) + return "%d" % (not self.normNumber(int(op))) + else: + return self.parseValue() + + def parseBrace(self): + self.skipSpace() + char = self.getCurr() + if char == '(': + self.moveNext() + value = self.parseExpr() + self.skipSpace() + if self.getCurr() != ')': + self.errExit ("Expecting closing brace or operator") + self.moveNext() + return value + else: + value = self.parseSingleOp() + return value + + def parseCompare(self): + value = self.parseBrace() + while True: + self.skipSpace() + char = self.getCurr() + if char in ['<', '>']: + self.moveNext() + next = self.getCurr() + if next == '=': + op = char + next + self.moveNext() + else: + op = char + result = self.parseBrace() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber(eval (value + op + result)) + else: + self.errExit ("'%s' is not a valid number for comparision" % test) + elif char in ['=', '!']: + op = self.getCurr(2) + if op in ['==', '!=']: + self.moveNext(2) + result = self.parseBrace() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber((eval (value + op + result))) + else: + value = "%d" % self.normNumber(eval ("'" + value + "'" + op + "'" + result + "'")) + else: + break + else: + break + return value + + def parseAnd(self): + value = self.parseCompare() + while True: + self.skipSpace() + if re.match('^AND\W', self.getCurr(-1)): + self.moveNext(3) + result = self.parseCompare() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber(int(value) & int(result)) + else: + self.errExit ("'%s' is not a valid op number for AND" % test) + else: + break + return value + + def parseOrXor(self): + value = self.parseAnd() + op = None + while True: + self.skipSpace() + op = None + if re.match('^XOR\W', self.getCurr(-1)): + self.moveNext(3) + op = '^' + elif re.match('^OR\W', self.getCurr(-1)): + self.moveNext(2) + op = '|' + else: + break + if op: + result = self.parseAnd() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber(eval (value + op + result)) + else: + self.errExit ("'%s' is not a valid op number for XOR/OR" % test) + return value + + def parseExpr(self): + return self.parseOrXor() + + def getResult(self): + value = self.parseExpr() + self.skipSpace() + if not self.isLast(): + self.errExit ("Unexpected character found '%s'" % self.getCurr()) + test = self.getNumber(value) + if test is None: + self.errExit ("Result '%s' is not a number" % value) + return int(value) + + def evaluateExpress (self, Expr): + self.index = 0 + self.string = Expr + if self.getResult(): + Result = True + else: + Result = False + return Result + +class CGenCfgOpt: + def __init__(self, Mode = ''): + self.Debug = False + self.Error = '' + self.Mode = Mode + self._GlobalDataDef = """ +GlobalDataDef + SKUID = 0, "DEFAULT" +EndGlobalData + +""" + self._BuidinOptionTxt = """ +List &EN_DIS + Selection 0x1 , "Enabled" + Selection 0x0 , "Disabled" +EndList + +""" + self._BsfKeyList = ['FIND','NAME','HELP','TYPE','PAGE', 'PAGES', 'BLOCK', 'OPTION','CONDITION','ORDER', 'MARKER', 'SUBT'] + self._HdrKeyList = ['HEADER','STRUCT', 'EMBED', 'COMMENT'] + self._BuidinOption = {'$EN_DIS' : 'EN_DIS'} + + self._MacroDict = {} + self._VarDict = {} + self._PcdsDict = {} + self._CfgBlkDict = {} + self._CfgPageDict = {} + self._BsfTempDict = {} + self._CfgItemList = [] + self._DscLines = [] + self._DscFile = '' + + self._MapVer = 0 + self._DscTime = 0 + + def ParseMacros (self, MacroDefStr): + # ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build'] + self._MacroDict = {} + IsExpression = False + for Macro in MacroDefStr: + if Macro.startswith('-D'): + IsExpression = True + if len(Macro) > 2: + Macro = Macro[2:] + else : + continue + if IsExpression: + IsExpression = False + Match = re.match("(\w+)=(.+)", Macro) + if Match: + self._MacroDict[Match.group(1)] = Match.group(2) + else: + Match = re.match("(\w+)", Macro) + if Match: + self._MacroDict[Match.group(1)] = '' + if len(self._MacroDict) == 0: + Error = 1 + else: + Error = 0 + if self.Debug: + print ("INFO : Macro dictionary:") + for Each in self._MacroDict: + print (" $(%s) = [ %s ]" % (Each , self._MacroDict[Each])) + return Error + + def EvaulateIfdef (self, Macro): + Result = Macro in self._MacroDict + if self.Debug: + print ("INFO : Eval Ifdef [%s] : %s" % (Macro, Result)) + return Result + + def ExpandMacros (self, Input, Preserve = False): + Line = Input + Match = re.findall("\$\(\w+\)", Input) + if Match: + for Each in Match: + Variable = Each[2:-1] + if Variable in self._MacroDict: + Line = Line.replace(Each, self._MacroDict[Variable]) + else: + if self.Debug: + print ("WARN : %s is not defined" % Each) + if not Preserve: + Line = Line.replace(Each, Each[2:-1]) + return Line + + def ExpandPcds (self, Input): + Line = Input + Match = re.findall("(\w+\.\w+)", Input) + if Match: + for PcdName in Match: + if PcdName in self._PcdsDict: + Line = Line.replace(PcdName, self._PcdsDict[PcdName]) + else: + if self.Debug: + print ("WARN : %s is not defined" % PcdName) + return Line + + def EvaluateExpress (self, Expr): + ExpExpr = self.ExpandPcds(Expr) + ExpExpr = self.ExpandMacros(ExpExpr) + LogExpr = CLogicalExpression() + Result = LogExpr.evaluateExpress (ExpExpr) + if self.Debug: + print ("INFO : Eval Express [%s] : %s" % (Expr, Result)) + return Result + + def ValueToByteArray (self, ValueStr, Length): + Match = re.match("\{\s*FILE:(.+)\}", ValueStr) + if Match: + FileList = Match.group(1).split(',') + Result = bytearray() + for File in FileList: + File = File.strip() + BinPath = os.path.join(os.path.dirname(self._DscFile), File) + Result.extend(bytearray(open(BinPath, 'rb').read())) + else: + try: + Result = bytearray(self.ValueToList(ValueStr, Length)) + except ValueError as e: + raise Exception ("Bytes in '%s' must be in range 0~255 !" % ValueStr) + if len(Result) < Length: + Result.extend(b'\x00' * (Length - len(Result))) + elif len(Result) > Length: + raise Exception ("Value '%s' is too big to fit into %d bytes !" % (ValueStr, Length)) + + return Result[:Length] + + def ValueToList (self, ValueStr, Length): + if ValueStr[0] == '{': + Result = [] + BinList = ValueStr[1:-1].split(',') + InBitField = False + LastInBitField = False + Value = 0 + BitLen = 0 + for Element in BinList: + InBitField = False + Each = Element.strip() + if len(Each) == 0: + pass + else: + if Each[0] in ['"', "'"]: + Result.extend(list(bytearray(Each[1:-1], 'utf-8'))) + elif ':' in Each: + Match = re.match("(.+):(\d+)b", Each) + if Match is None: + raise Exception("Invald value list format '%s' !" % Each) + InBitField = True + CurrentBitLen = int(Match.group(2)) + CurrentValue = ((self.EvaluateExpress(Match.group(1)) & (1<<CurrentBitLen) - 1)) << BitLen + else: + Result.append(self.EvaluateExpress(Each.strip())) + if InBitField: + Value += CurrentValue + BitLen += CurrentBitLen + if LastInBitField and ((not InBitField) or (Element == BinList[-1])): + if BitLen % 8 != 0: + raise Exception("Invald bit field length!") + Result.extend(Val2Bytes(Value, BitLen // 8)) + Value = 0 + BitLen = 0 + LastInBitField = InBitField + elif ValueStr.startswith("'") and ValueStr.endswith("'"): + Result = Str2Bytes (ValueStr, Length) + elif ValueStr.startswith('"') and ValueStr.endswith('"'): + Result = Str2Bytes (ValueStr, Length) + else: + Result = Val2Bytes (self.EvaluateExpress(ValueStr), Length) + return Result + + def FormatListValue(self, ConfigDict): + Struct = ConfigDict['struct'] + if Struct not in ['UINT8','UINT16','UINT32','UINT64']: + return + + dataarray = [] + binlist = ConfigDict['value'][1:-1].split(',') + for each in binlist: + each = each.strip() + if each.startswith('0x'): + value = int(each, 16) + else: + value = int(each) + dataarray.append(value) + + unit = int(Struct[4:]) / 8 + if int(ConfigDict['length']) != unit * len(dataarray): + raise Exception("Array size is not proper for '%s' !" % ConfigDict['cname']) + + bytearray = [] + for each in dataarray: + value = each + for loop in range(int(unit)): + bytearray.append("0x%02X" % (value & 0xFF)) + value = value >> 8 + newvalue = '{' + ','.join(bytearray) + '}' + ConfigDict['value'] = newvalue + return "" + + def ParseDscFile (self, DscFile, FvDir): + Hardcode = False + AutoAlign = False + self._CfgItemList = [] + self._CfgPageDict = {} + self._CfgBlkDict = {} + self._DscFile = DscFile + self._FvDir = FvDir + + self._DscLines = [] + self._BsfTempDict = {} + + # Initial DSC time is parent DSC time. + self._DscTime = os.path.getmtime(DscFile) + + CfgDict = {} + + IsDefSect = False + IsPcdSect = False + IsUpdSect = False + IsVpdSect = False + IsTmpSect = False + + TemplateName = '' + + IfStack = [] + ElifStack = [] + Error = 0 + ConfigDict = {} + + + if type(DscFile) is list: + # it is DSC lines already + DscLines = DscFile + self._DscFile = '.' + else: + DscFd = open(DscFile, "r") + DscLines = DscFd.readlines() + DscFd.close() + self._DscFile = DscFile + + SkipLines = 0 + + MaxAlign = 32 #Default align to 32, but if there are 64 bit unit, align to 64 + SizeAlign = 0 #record the struct max align + Base = 0 #Starting offset of sub-structure. + + while len(DscLines): + DscLine = DscLines.pop(0).strip() + if SkipLines == 0: + self._DscLines.append (DscLine) + else: + SkipLines = SkipLines - 1 + if len(DscLine) == 0: + continue + + Handle = False + Match = re.match("^\[(.+)\]", DscLine) + if Match is not None: + IsDefSect = False + IsPcdSect = False + IsVpdSect = False + IsUpdSect = False + IsTmpSect = False + SectionName = Match.group(1).lower() + if SectionName == "Defines".lower(): + IsDefSect = True + if (SectionName == "PcdsFeatureFlag".lower() or SectionName == "PcdsFixedAtBuild".lower()): + IsPcdSect = True + elif SectionName == "PcdsDynamicVpd.Tmp".lower(): + IsTmpSect = True + elif SectionName == "PcdsDynamicVpd.Upd".lower(): + ConfigDict = {} + ConfigDict['header'] = 'ON' + ConfigDict['region'] = 'UPD' + ConfigDict['order'] = -1 + ConfigDict['page'] = '' + ConfigDict['name'] = '' + ConfigDict['find'] = '' + ConfigDict['marker'] = '' + ConfigDict['struct'] = '' + ConfigDict['embed'] = '' + ConfigDict['comment'] = '' + ConfigDict['subreg'] = [] + ConfigDict['condition'] = '' + ConfigDict['option'] = '' + IsUpdSect = True + Offset = 0 + else: + if IsDefSect or IsPcdSect or IsUpdSect or IsVpdSect or IsTmpSect: + + Match = False if DscLine[0] != '!' else True + if Match: + Match = re.match("^!(else|endif|ifdef|ifndef|if|elseif|include)\s*(.+)?$", DscLine.split("#")[0]) + Keyword = Match.group(1) if Match else '' + Remaining = Match.group(2) if Match else '' + Remaining = '' if Remaining is None else Remaining.strip() + + if Keyword in ['if', 'elseif', 'ifdef', 'ifndef', 'include'] and not Remaining: + raise Exception ("ERROR: Expression is expected after '!if' or !elseif' for line '%s'" % DscLine) + + if Keyword == 'else': + if IfStack: + IfStack[-1] = not IfStack[-1] + else: + raise Exception ("ERROR: No paired '!if' found for '!else' for line '%s'" % DscLine) + elif Keyword == 'endif': + if IfStack: + IfStack.pop() + Level = ElifStack.pop() + if Level > 0: + del IfStack[-Level:] + else: + raise Exception ("ERROR: No paired '!if' found for '!endif' for line '%s'" % DscLine) + elif Keyword == 'ifdef' or Keyword == 'ifndef': + Result = self.EvaulateIfdef (Remaining) + if Keyword == 'ifndef': + Result = not Result + IfStack.append(Result) + ElifStack.append(0) + elif Keyword == 'if' or Keyword == 'elseif': + Result = self.EvaluateExpress(Remaining) + if Keyword == "if": + ElifStack.append(0) + IfStack.append(Result) + else: #elseif + if IfStack: + IfStack[-1] = not IfStack[-1] + IfStack.append(Result) + ElifStack[-1] = ElifStack[-1] + 1 + else: + raise Exception ("ERROR: No paired '!if' found for '!elif' for line '%s'" % DscLine) + else: + if IfStack: + Handle = reduce(lambda x,y: x and y, IfStack) + else: + Handle = True + if Handle: + Match = re.match("!include\s+(.+)", DscLine) + if Match: + IncludeFilePath = Match.group(1) + IncludeFilePath = self.ExpandMacros(IncludeFilePath) + PackagesPath = os.getenv("PACKAGES_PATH") + if PackagesPath: + for PackagePath in PackagesPath.split(os.pathsep): + IncludeFilePathAbs = os.path.join(os.path.normpath(PackagePath), os.path.normpath(IncludeFilePath)) + if os.path.exists(IncludeFilePathAbs): + IncludeDsc = open(IncludeFilePathAbs, "r") + break + else: + IncludeDsc = open(IncludeFilePath, "r") + if IncludeDsc == None: + print("ERROR: Cannot open file '%s'" % IncludeFilePath) + raise SystemExit + + # Update DscTime when newer DSC time found. + CurrentDscTime = os.path.getmtime(os.path.realpath(IncludeDsc.name)) + if CurrentDscTime > self._DscTime: + self._DscTime = CurrentDscTime + + NewDscLines = IncludeDsc.readlines() + IncludeDsc.close() + DscLines = NewDscLines + DscLines + del self._DscLines[-1] + Offset = 0 + else: + if DscLine.startswith('!'): + print("ERROR: Unrecognized directive for line '%s'" % DscLine) + raise SystemExit + if not Handle: + del self._DscLines[-1] + continue + + if IsDefSect: + #DEFINE UPD_TOOL_GUID = 8C3D856A-9BE6-468E-850A-24F7A8D38E09 + #DEFINE FSP_T_UPD_TOOL_GUID = 34686CA3-34F9-4901-B82A-BA630F0714C6 + #DEFINE FSP_M_UPD_TOOL_GUID = 39A250DB-E465-4DD1-A2AC-E2BD3C0E2385 + #DEFINE FSP_S_UPD_TOOL_GUID = CAE3605B-5B34-4C85-B3D7-27D54273C40F + Match = re.match("^\s*(?:DEFINE\s+)*(\w+)\s*=\s*(.+)", DscLine) + if Match: + self._MacroDict[Match.group(1)] = self.ExpandMacros(Match.group(2)) + if self.Debug: + print ("INFO : DEFINE %s = [ %s ]" % (Match.group(1), self.ExpandMacros(Match.group(2)))) + elif IsPcdSect: + #gSiPkgTokenSpaceGuid.PcdTxtEnable|FALSE + #gSiPkgTokenSpaceGuid.PcdOverclockEnable|TRUE + Match = re.match("^\s*([\w\.]+)\s*\|\s*(\w+)", DscLine) + if Match: + self._PcdsDict[Match.group(1)] = Match.group(2) + if self.Debug: + print ("INFO : PCD %s = [ %s ]" % (Match.group(1), Match.group(2))) + i = 0 + while i < len(BuildOptionPcd): + Match = re.match("\s*([\w\.]+)\s*\=\s*(\w+)", BuildOptionPcd[i]) + if Match: + self._PcdsDict[Match.group(1)] = Match.group(2) + i += 1 + + elif IsTmpSect: + # !BSF DEFT:{GPIO_TMPL:START} + Match = re.match("^\s*#\s+(!BSF)\s+DEFT:{(.+?):(START|END)}", DscLine) + if Match: + if Match.group(3) == 'START' and not TemplateName: + TemplateName = Match.group(2).strip() + self._BsfTempDict[TemplateName] = [] + if Match.group(3) == 'END' and (TemplateName == Match.group(2).strip()) and TemplateName: + TemplateName = '' + else: + if TemplateName: + Match = re.match("^!include\s*(.+)?$", DscLine) + if Match: + continue + self._BsfTempDict[TemplateName].append(DscLine) + + else: + Match = re.match("^\s*#\s+(!BSF|@Bsf|!HDR)\s+(.+)", DscLine) + if Match: + Remaining = Match.group(2) + if Match.group(1) == '!BSF' or Match.group(1) == '@Bsf': + Match = re.match("(?:^|.+\s+)PAGES:{(.+?)}", Remaining) + if Match: + # !BSF PAGES:{HSW:"Haswell System Agent", LPT:"Lynx Point PCH"} + PageList = Match.group(1).split(',') + for Page in PageList: + Page = Page.strip() + Match = re.match("(\w+):\"(.+)\"", Page) + if Match != None: + self._CfgPageDict[Match.group(1)] = Match.group(2) + + Match = re.match("(?:^|.+\s+)BLOCK:{NAME:\"(.+)\"\s*,\s*VER:\"(.+)\"\s*}", Remaining) + if Match: + self._CfgBlkDict['name'] = Match.group(1) + self._CfgBlkDict['ver'] = Match.group(2) + + for Key in self._BsfKeyList: + Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining) + if Match: + if Key in ['NAME', 'HELP', 'OPTION'] and Match.group(1).startswith('+'): + ConfigDict[Key.lower()] += Match.group(1)[1:] + else: + ConfigDict[Key.lower()] = Match.group(1) + else: + for Key in self._HdrKeyList: + Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining) + if Match: + ConfigDict[Key.lower()] = Match.group(1) + + Match = re.match("^\s*#\s+@Prompt\s+(.+)", DscLine) + if Match: + ConfigDict['name'] = Match.group(1) + + Match = re.match("^\s*#\s*@ValidList\s*(.+)\s*\|\s*(.+)\s*\|\s*(.+)\s*", DscLine) + if Match: + if Match.group(2).strip() in self._BuidinOption: + ConfigDict['option'] = Match.group(2).strip() + else: + OptionValueList = Match.group(2).split(',') + OptionStringList = Match.group(3).split(',') + Index = 0 + for Option in OptionValueList: + Option = Option.strip() + ConfigDict['option'] = ConfigDict['option'] + str(Option) + ':' + OptionStringList[Index].strip() + Index += 1 + if Index in range(len(OptionValueList)): + ConfigDict['option'] += ', ' + ConfigDict['type'] = "Combo" + + Match = re.match("^\s*#\s*@ValidRange\s*(.+)\s*\|\s*(.+)\s*-\s*(.+)\s*", DscLine) + if Match: + if "0x" in Match.group(2) or "0x" in Match.group(3): + ConfigDict['type'] = "EditNum, HEX, (%s,%s)" % (Match.group(2), Match.group(3)) + else: + ConfigDict['type'] = "EditNum, DEC, (%s,%s)" % (Match.group(2), Match.group(3)) + + Match = re.match("^\s*##\s+(.+)", DscLine) + if Match: + ConfigDict['help'] = Match.group(1) + + # Check VPD/UPD + if IsUpdSect: + 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) + else: + Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+)(?:\s*\|\s*(.+))?", DscLine) + if Match: + ConfigDict['space'] = Match.group(1) + ConfigDict['cname'] = Match.group(2) + if Match.group(3) != '*': + Hardcode = True + Offset = int (Match.group(3), 16) + else: + AutoAlign = True + + if Hardcode and AutoAlign: + print("Hardcode and auto-align mixed mode is not supported by GenCfgOpt") + raise SystemExit + ConfigDict['offset'] = Offset + if ConfigDict['order'] == -1: + ConfigDict['order'] = ConfigDict['offset'] << 8 + else: + (Major, Minor) = ConfigDict['order'].split('.') + ConfigDict['order'] = (int (Major, 16) << 8 ) + int (Minor, 16) + if IsUpdSect: + Value = Match.group(5).strip() + if Match.group(4).startswith("0x"): + Length = int (Match.group(4), 16) + else : + Length = int (Match.group(4)) + Offset += Length + else: + Value = Match.group(4) + if Value is None: + Value = '' + Value = Value.strip() + if '|' in Value: + Match = re.match("^.+\s*\|\s*(.+)", Value) + if Match: + Value = Match.group(1) + Length = -1 + + ConfigDict['length'] = Length + Match = re.match("\$\((\w+)\)", Value) + if Match: + if Match.group(1) in self._MacroDict: + Value = self._MacroDict[Match.group(1)] + + ConfigDict['value'] = Value + if (len(Value) > 0) and (Value[0] == '{'): + Value = self.FormatListValue(ConfigDict) + + if ConfigDict['name'] == '': + # Clear BSF specific items + ConfigDict['bsfname'] = '' + ConfigDict['help'] = '' + ConfigDict['type'] = '' + ConfigDict['option'] = '' + if IsUpdSect and AutoAlign: + ItemLength = int(ConfigDict['length']) + ItemOffset = int(ConfigDict['offset']) + ItemStruct = ConfigDict['struct'] + Unit = 1 + if ItemLength in [1, 2, 4, 8] and not ConfigDict['value'].startswith('{'): + Unit = ItemLength + # If there are 64 bit unit, align to 64 + if Unit == 8: + MaxAlign = 64 + SizeAlign = 8 + if ItemStruct != '': + UnitDict = {'UINT8':1, 'UINT16':2, 'UINT32':4, 'UINT64':8} + if ItemStruct in ['UINT8', 'UINT16', 'UINT32', 'UINT64']: + Unit = UnitDict[ItemStruct] + # If there are 64 bit unit, align to 64 + if Unit == 8: + MaxAlign = 64 + SizeAlign = max(SizeAlign, Unit) + if (ConfigDict['embed'].find(':START') != -1): + Base = ItemOffset + SubOffset = ItemOffset - Base + SubRemainder = SubOffset % Unit + if SubRemainder: + Diff = Unit - SubRemainder + Offset = Offset + Diff + ItemOffset = ItemOffset + Diff + + if (ConfigDict['embed'].find(':END') != -1): + Remainder = Offset % (MaxAlign/8) # MaxAlign is either 32 or 64 + if Remainder: + Diff = int((MaxAlign/8) - Remainder) + Offset = Offset + Diff + ItemOffset = ItemOffset + Diff + MaxAlign = 32 # Reset to default 32 align when struct end + if (ConfigDict['cname'] == 'UpdTerminator'): + # ItemLength is the size of UpdTerminator + # Itemlength might be 16, 32, or 64 + # Struct align to 64 if UpdTerminator + # or struct size is 64 bit, else align to 32 + Remainder = Offset % max(ItemLength/8, 4, SizeAlign) + Offset = Offset + ItemLength + if Remainder: + Diff = int(max(ItemLength/8, 4, SizeAlign) - Remainder) + ItemOffset = ItemOffset + Diff + ConfigDict['offset'] = ItemOffset + + self._CfgItemList.append(ConfigDict.copy()) + ConfigDict['name'] = '' + ConfigDict['find'] = '' + ConfigDict['struct'] = '' + ConfigDict['embed'] = '' + ConfigDict['comment'] = '' + ConfigDict['marker'] = '' + ConfigDict['order'] = -1 + ConfigDict['subreg'] = [] + ConfigDict['option'] = '' + else: + # It could be a virtual item as below + # !BSF FIELD:{SerialDebugPortAddress0:1} + # or + # @Bsf FIELD:{SerialDebugPortAddress0:1b} + Match = re.match("^\s*#\s+(!BSF|@Bsf)\s+FIELD:{(.+):(\d+)([Bb])?}", DscLine) + if Match: + SubCfgDict = ConfigDict.copy() + if (Match.group(4) == None) or (Match.group(4) == 'B'): + UnitBitLen = 8 + elif Match.group(4) == 'b': + UnitBitLen = 1 + else: + print("ERROR: Invalide BSF FIELD length for line '%s'" % DscLine) + raise SystemExit + SubCfgDict['cname'] = Match.group(2) + SubCfgDict['bitlength'] = int (Match.group(3)) * UnitBitLen + if SubCfgDict['bitlength'] > 0: + LastItem = self._CfgItemList[-1] + if len(LastItem['subreg']) == 0: + SubOffset = 0 + else: + SubOffset = LastItem['subreg'][-1]['bitoffset'] + LastItem['subreg'][-1]['bitlength'] + SubCfgDict['bitoffset'] = SubOffset + LastItem['subreg'].append (SubCfgDict.copy()) + ConfigDict['name'] = '' + return Error + + def GetBsfBitFields (self, subitem, bytes): + start = subitem['bitoffset'] + end = start + subitem['bitlength'] + bitsvalue = ''.join('{0:08b}'.format(i) for i in bytes[::-1]) + bitsvalue = bitsvalue[::-1] + bitslen = len(bitsvalue) + if start > bitslen or end > bitslen: + raise Exception ("Invalid bits offset [%d,%d] %d for %s" % (start, end, bitslen, subitem['name'])) + return '0x%X' % (int(bitsvalue[start:end][::-1], 2)) + + def UpdateSubRegionDefaultValue (self): + Error = 0 + for Item in self._CfgItemList: + if len(Item['subreg']) == 0: + continue + bytearray = [] + if Item['value'][0] == '{': + binlist = Item['value'][1:-1].split(',') + for each in binlist: + each = each.strip() + if each.startswith('0x'): + value = int(each, 16) + else: + value = int(each) + bytearray.append(value) + else: + if Item['value'].startswith('0x'): + value = int(Item['value'], 16) + else: + value = int(Item['value']) + idx = 0 + while idx < Item['length']: + bytearray.append(value & 0xFF) + value = value >> 8 + idx = idx + 1 + for SubItem in Item['subreg']: + valuestr = self.GetBsfBitFields(SubItem, bytearray) + SubItem['value'] = valuestr + return Error + + def NoDscFileChange (self, OutPutFile): + NoFileChange = True + if not os.path.exists(OutPutFile): + NoFileChange = False + else: + OutputTime = os.path.getmtime(OutPutFile) + if self._DscTime > OutputTime: + NoFileChange = False + return NoFileChange + + def CreateSplitUpdTxt (self, UpdTxtFile): + GuidList = ['FSP_T_UPD_TOOL_GUID','FSP_M_UPD_TOOL_GUID','FSP_S_UPD_TOOL_GUID'] + SignatureList = ['0x545F', '0x4D5F','0x535F'] # _T, _M, and _S signature for FSPT, FSPM, FSPS + for Index in range(len(GuidList)): + UpdTxtFile = '' + FvDir = self._FvDir + if GuidList[Index] not in self._MacroDict: + self.Error = "%s definition is missing in DSC file" % (GuidList[Index]) + return 1 + + if UpdTxtFile == '': + UpdTxtFile = os.path.join(FvDir, self._MacroDict[GuidList[Index]] + '.txt') + + if (self.NoDscFileChange (UpdTxtFile)): + # DSC has not been modified yet + # So don't have to re-generate other files + self.Error = 'No DSC file change, skip to create UPD TXT file' + return 256 + + TxtFd = open(UpdTxtFile, "w") + TxtFd.write("%s\n" % (__copyright_txt__ % date.today().year)) + + NextOffset = 0 + SpaceIdx = 0 + StartAddr = 0 + EndAddr = 0 + Default = 'DEFAULT|' + InRange = False + for Item in self._CfgItemList: + if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]: + StartAddr = Item['offset'] + NextOffset = StartAddr + InRange = True + if Item['cname'] == 'UpdTerminator' and InRange == True: + EndAddr = Item['offset'] + InRange = False + InRange = False + for Item in self._CfgItemList: + if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]: + InRange = True + if InRange != True: + continue + if Item['cname'] == 'UpdTerminator': + InRange = False + if Item['region'] != 'UPD': + continue + Offset = Item['offset'] + if StartAddr > Offset or EndAddr < Offset: + continue + if NextOffset < Offset: + # insert one line + TxtFd.write("%s.UnusedUpdSpace%d|%s0x%04X|0x%04X|{0}\n" % (Item['space'], SpaceIdx, Default, NextOffset - StartAddr, Offset - NextOffset)) + SpaceIdx = SpaceIdx + 1 + NextOffset = Offset + Item['length'] + TxtFd.write("%s.%s|%s0x%04X|%s|%s\n" % (Item['space'],Item['cname'],Default,Item['offset'] - StartAddr,Item['length'],Item['value'])) + TxtFd.close() + return 0 + + def CreateVarDict (self): + Error = 0 + self._VarDict = {} + if len(self._CfgItemList) > 0: + Item = self._CfgItemList[-1] + self._VarDict['_LENGTH_'] = '%d' % (Item['offset'] + Item['length']) + for Item in self._CfgItemList: + Embed = Item['embed'] + Match = re.match("^(\w+):(\w+):(START|END)", Embed) + if Match: + StructName = Match.group(1) + VarName = '_%s_%s_' % (Match.group(3), StructName) + if Match.group(3) == 'END': + self._VarDict[VarName] = Item['offset'] + Item['length'] + self._VarDict['_LENGTH_%s_' % StructName] = \ + self._VarDict['_END_%s_' % StructName] - self._VarDict['_START_%s_' % StructName] + if Match.group(2).startswith('TAG_'): + if (self.Mode != 'FSP') and (self._VarDict['_LENGTH_%s_' % StructName] % 4): + raise Exception("Size of structure '%s' is %d, not DWORD aligned !" % (StructName, self._VarDict['_LENGTH_%s_' % StructName])) + self._VarDict['_TAG_%s_' % StructName] = int (Match.group(2)[4:], 16) & 0xFFF + else: + self._VarDict[VarName] = Item['offset'] + if Item['marker']: + self._VarDict['_OFFSET_%s_' % Item['marker'].strip()] = Item['offset'] + return Error + + def UpdateBsfBitUnit (self, Item): + BitTotal = 0 + BitOffset = 0 + StartIdx = 0 + Unit = None + UnitDec = {1:'BYTE', 2:'WORD', 4:'DWORD', 8:'QWORD'} + for Idx, SubItem in enumerate(Item['subreg']): + if Unit is None: + Unit = SubItem['bitunit'] + BitLength = SubItem['bitlength'] + BitTotal += BitLength + BitOffset += BitLength + + if BitOffset > 64 or BitOffset > Unit * 8: + break + + if BitOffset == Unit * 8: + for SubIdx in range (StartIdx, Idx + 1): + Item['subreg'][SubIdx]['bitunit'] = Unit + BitOffset = 0 + StartIdx = Idx + 1 + Unit = None + + if BitOffset > 0: + raise Exception ("Bit fields cannot fit into %s for '%s.%s' !" % (UnitDec[Unit], Item['cname'], SubItem['cname'])) + + ExpectedTotal = Item['length'] * 8 + if Item['length'] * 8 != BitTotal: + raise Exception ("Bit fields total length (%d) does not match length (%d) of '%s' !" % (BitTotal, ExpectedTotal, Item['cname'])) + + def UpdateDefaultValue (self): + Error = 0 + for Idx, Item in enumerate(self._CfgItemList): + if len(Item['subreg']) == 0: + Value = Item['value'] + if (len(Value) > 0) and (Value[0] == '{' or Value[0] == "'" or Value[0] == '"'): + # {XXX} or 'XXX' strings + self.FormatListValue(self._CfgItemList[Idx]) + else: + Match = re.match("(0x[0-9a-fA-F]+|[0-9]+)", Value) + if not Match: + NumValue = self.EvaluateExpress (Value) + Item['value'] = '0x%X' % NumValue + else: + ValArray = self.ValueToByteArray (Item['value'], Item['length']) + for SubItem in Item['subreg']: + SubItem['value'] = self.GetBsfBitFields(SubItem, ValArray) + self.UpdateBsfBitUnit (Item) + return Error + + def ProcessMultilines (self, String, MaxCharLength): + Multilines = '' + StringLength = len(String) + CurrentStringStart = 0 + StringOffset = 0 + BreakLineDict = [] + if len(String) <= MaxCharLength: + while (StringOffset < StringLength): + if StringOffset >= 1: + if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n': + BreakLineDict.append (StringOffset + 1) + StringOffset += 1 + if BreakLineDict != []: + for Each in BreakLineDict: + Multilines += " %s\n" % String[CurrentStringStart:Each].lstrip() + CurrentStringStart = Each + if StringLength - CurrentStringStart > 0: + Multilines += " %s\n" % String[CurrentStringStart:].lstrip() + else: + Multilines = " %s\n" % String + else: + NewLineStart = 0 + NewLineCount = 0 + FoundSpaceChar = False + while (StringOffset < StringLength): + if StringOffset >= 1: + if NewLineCount >= MaxCharLength - 1: + if String[StringOffset] == ' ' and StringLength - StringOffset > 10: + BreakLineDict.append (NewLineStart + NewLineCount) + NewLineStart = NewLineStart + NewLineCount + NewLineCount = 0 + FoundSpaceChar = True + elif StringOffset == StringLength - 1 and FoundSpaceChar == False: + BreakLineDict.append (0) + if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n': + BreakLineDict.append (StringOffset + 1) + NewLineStart = StringOffset + 1 + NewLineCount = 0 + StringOffset += 1 + NewLineCount += 1 + if BreakLineDict != []: + BreakLineDict.sort () + for Each in BreakLineDict: + if Each > 0: + Multilines += " %s\n" % String[CurrentStringStart:Each].lstrip() + CurrentStringStart = Each + if StringLength - CurrentStringStart > 0: + Multilines += " %s\n" % String[CurrentStringStart:].lstrip() + return Multilines + + def CreateField (self, Item, Name, Length, Offset, Struct, BsfName, Help, Option, BitsLength = None): + PosName = 28 + PosComment = 30 + NameLine='' + HelpLine='' + OptionLine='' + + if Length == 0 and Name == 'Dummy': + return '\n' + + IsArray = False + if Length in [1,2,4,8]: + Type = "UINT%d" % (Length * 8) + if Name.startswith("UnusedUpdSpace") and Length != 1: + IsArray = True + Type = "UINT8" + else: + IsArray = True + Type = "UINT8" + + if Item and Item['value'].startswith('{'): + Type = "UINT8" + IsArray = True + + if Struct != '': + Type = Struct + if Struct in ['UINT8','UINT16','UINT32','UINT64']: + IsArray = True + Unit = int(Type[4:]) / 8 + Length = Length / Unit + else: + IsArray = False + + if IsArray: + Name = Name + '[%d]' % Length + + if len(Type) < PosName: + Space1 = PosName - len(Type) + else: + Space1 = 1 + + if BsfName != '': + NameLine=" - %s\n" % BsfName + else: + NameLine="\n" + + if Help != '': + HelpLine = self.ProcessMultilines (Help, 80) + + if Option != '': + OptionLine = self.ProcessMultilines (Option, 80) + + if Offset is None: + OffsetStr = '????' + else: + OffsetStr = '0x%04X' % Offset + + if BitsLength is None: + BitsLength = '' + else: + BitsLength = ' : %d' % BitsLength + + return "\n/** Offset %s%s%s%s**/\n %s%s%s%s;\n" % (OffsetStr, NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name, BitsLength) + + def PostProcessBody (self, TextBody): + NewTextBody = [] + OldTextBody = [] + IncludeLine = False + StructName = '' + VariableName = '' + IsUpdHdrDefined = False + IsUpdHeader = False + for Line in TextBody: + SplitToLines = Line.splitlines() + MatchComment = re.match("^/\*\sCOMMENT:(\w+):([\w|\W|\s]+)\s\*/\s([\s\S]*)", SplitToLines[0]) + if MatchComment: + if MatchComment.group(1) == 'FSP_UPD_HEADER': + IsUpdHeader = True + else: + IsUpdHeader = False + if IsUpdHdrDefined != True or IsUpdHeader != True: + CommentLine = " " + MatchComment.group(2) + "\n" + NewTextBody.append("/**" + CommentLine + "**/\n") + Line = Line[(len(SplitToLines[0]) + 1):] + + Match = re.match("^/\*\sEMBED_STRUCT:(\w+):(\w+):(START|END)\s\*/\s([\s\S]*)", Line) + if Match: + Line = Match.group(4) + if Match.group(1) == 'FSP_UPD_HEADER': + IsUpdHeader = True + else: + IsUpdHeader = False + + if Match and Match.group(3) == 'START': + if IsUpdHdrDefined != True or IsUpdHeader != True: + NewTextBody.append ('typedef struct {\n') + StructName = Match.group(1) + VariableName = Match.group(2) + MatchOffset = re.search('/\*\*\sOffset\s0x([a-fA-F0-9]+)', Line) + if MatchOffset: + Offset = int(MatchOffset.group(1), 16) + else: + Offset = None + Line + IncludeLine = True + OldTextBody.append (self.CreateField (None, VariableName, 0, Offset, StructName, '', '', '')) + if IncludeLine: + if IsUpdHdrDefined != True or IsUpdHeader != True: + NewTextBody.append (Line) + else: + OldTextBody.append (Line) + + if Match and Match.group(3) == 'END': + if (StructName != Match.group(1)) or (VariableName != Match.group(2)): + print ("Unmatched struct name '%s' and '%s' !" % (StructName, Match.group(1))) + else: + if IsUpdHdrDefined != True or IsUpdHeader != True: + NewTextBody.append ('} %s;\n\n' % StructName) + IsUpdHdrDefined = True + IncludeLine = False + NewTextBody.extend(OldTextBody) + return NewTextBody + + def WriteLinesWithoutTailingSpace (self, HeaderFd, Line): + TxtBody2 = Line.splitlines(True) + for Line2 in TxtBody2: + Line2 = Line2.rstrip() + Line2 += '\n' + HeaderFd.write (Line2) + return 0 + def CreateHeaderFile (self, InputHeaderFile): + FvDir = self._FvDir + + HeaderFileName = 'FspUpd.h' + HeaderFile = os.path.join(FvDir, HeaderFileName) + + # Check if header needs to be recreated + if (self.NoDscFileChange (HeaderFile)): + # DSC has not been modified yet + # So don't have to re-generate other files + self.Error = 'No DSC file change, skip to create UPD header file' + return 256 + + TxtBody = [] + for Item in self._CfgItemList: + if str(Item['cname']) == 'Signature' and Item['length'] == 8: + Value = int(Item['value'], 16) + Chars = [] + while Value != 0x0: + Chars.append(chr(Value & 0xFF)) + Value = Value >> 8 + SignatureStr = ''.join(Chars) + # Signature will be _T / _M / _S for FSPT / FSPM / FSPS accordingly + if '_T' in SignatureStr[6:6+2]: + TxtBody.append("#define FSPT_UPD_SIGNATURE %s /* '%s' */\n\n" % (Item['value'], SignatureStr)) + elif '_M' in SignatureStr[6:6+2]: + TxtBody.append("#define FSPM_UPD_SIGNATURE %s /* '%s' */\n\n" % (Item['value'], SignatureStr)) + elif '_S' in SignatureStr[6:6+2]: + TxtBody.append("#define FSPS_UPD_SIGNATURE %s /* '%s' */\n\n" % (Item['value'], SignatureStr)) + TxtBody.append("\n") + + for Region in ['UPD']: + UpdOffsetTable = [] + UpdSignature = ['0x545F', '0x4D5F', '0x535F'] #['_T', '_M', '_S'] signature for FSPT, FSPM, FSPS + UpdStructure = ['FSPT_UPD', 'FSPM_UPD', 'FSPS_UPD'] + for Item in self._CfgItemList: + if Item["cname"] == 'Signature' and Item["value"][0:6] in UpdSignature: + Item["offset"] = 0 # re-initialize offset to 0 when new UPD structure starting + UpdOffsetTable.append (Item["offset"]) + + for UpdIdx in range(len(UpdOffsetTable)): + CommentLine = "" + for Item in self._CfgItemList: + if Item["comment"] != '' and Item["offset"] >= UpdOffsetTable[UpdIdx]: + MatchComment = re.match("^(U|V)PD_DATA_REGION:([\w|\W|\s]+)", Item["comment"]) + if MatchComment and MatchComment.group(1) == Region[0]: + CommentLine = " " + MatchComment.group(2) + "\n" + TxtBody.append("/**" + CommentLine + "**/\n") + elif Item["offset"] >= UpdOffsetTable[UpdIdx] and Item["comment"] == '': + Match = re.match("^FSP([\w|\W|\s])_UPD", UpdStructure[UpdIdx]) + if Match: + TxtBody.append("/** Fsp " + Match.group(1) + " UPD Configuration\n**/\n") + TxtBody.append("typedef struct {\n") + NextOffset = 0 + SpaceIdx = 0 + Offset = 0 + + LastVisible = True + ResvOffset = 0 + ResvIdx = 0 + LineBuffer = [] + InRange = False + for Item in self._CfgItemList: + if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == UpdSignature[UpdIdx] or Region[0] == 'V': + InRange = True + if InRange != True: + continue + if Item['cname'] == 'UpdTerminator': + InRange = False + + if Item['region'] != Region: + continue + + if Item["offset"] < UpdOffsetTable[UpdIdx]: + continue + + NextVisible = LastVisible + + if LastVisible and (Item['header'] == 'OFF'): + NextVisible = False + ResvOffset = Item['offset'] + elif (not LastVisible) and Item['header'] == 'ON': + NextVisible = True + Name = "Reserved" + Region[0] + "pdSpace%d" % ResvIdx + ResvIdx = ResvIdx + 1 + TxtBody.append(self.CreateField (Item, Name, Item["offset"] - ResvOffset, ResvOffset, '', '', '', '')) + + if Offset < Item["offset"]: + if LastVisible: + Name = "Unused" + Region[0] + "pdSpace%d" % SpaceIdx + LineBuffer.append(self.CreateField (Item, Name, Item["offset"] - Offset, Offset, '', '', '', '')) + SpaceIdx = SpaceIdx + 1 + Offset = Item["offset"] + + LastVisible = NextVisible + + Offset = Offset + Item["length"] + if LastVisible: + for Each in LineBuffer: + TxtBody.append (Each) + LineBuffer = [] + Comment = Item["comment"] + Embed = Item["embed"].upper() + if Embed.endswith(':START') or Embed.endswith(':END'): + if not Comment == '' and Embed.endswith(':START'): + Marker = '/* COMMENT:%s */ \n' % Item["comment"] + Marker = Marker + '/* EMBED_STRUCT:%s */ ' % Item["embed"] + else: + Marker = '/* EMBED_STRUCT:%s */ ' % Item["embed"] + else: + if Embed == '': + Marker = '' + else: + self.Error = "Invalid embedded structure format '%s'!\n" % Item["embed"] + return 4 + Line = Marker + self.CreateField (Item, Item["cname"], Item["length"], Item["offset"], Item['struct'], Item['name'], Item['help'], Item['option']) + TxtBody.append(Line) + if Item['cname'] == 'UpdTerminator': + break + TxtBody.append("} " + UpdStructure[UpdIdx] + ";\n\n") + + # Handle the embedded data structure + TxtBody = self.PostProcessBody (TxtBody) + + HeaderTFileName = 'FsptUpd.h' + HeaderMFileName = 'FspmUpd.h' + HeaderSFileName = 'FspsUpd.h' + + UpdRegionCheck = ['FSPT', 'FSPM', 'FSPS'] # FSPX_UPD_REGION + UpdConfigCheck = ['FSP_T', 'FSP_M', 'FSP_S'] # FSP_X_CONFIG, FSP_X_TEST_CONFIG, FSP_X_RESTRICTED_CONFIG + UpdSignatureCheck = ['FSPT_UPD_SIGNATURE', 'FSPM_UPD_SIGNATURE', 'FSPS_UPD_SIGNATURE'] + ExcludedSpecificUpd = ['FSPT_ARCH_UPD', 'FSPM_ARCH_UPD', 'FSPS_ARCH_UPD'] + + IncLines = [] + if InputHeaderFile != '': + if not os.path.exists(InputHeaderFile): + self.Error = "Input header file '%s' does not exist" % InputHeaderFile + return 6 + + InFd = open(InputHeaderFile, "r") + IncLines = InFd.readlines() + InFd.close() + + for item in range(len(UpdRegionCheck)): + if UpdRegionCheck[item] == 'FSPT': + HeaderFd = open(os.path.join(FvDir, HeaderTFileName), "w") + FileBase = os.path.basename(os.path.join(FvDir, HeaderTFileName)) + elif UpdRegionCheck[item] == 'FSPM': + HeaderFd = open(os.path.join(FvDir, HeaderMFileName), "w") + FileBase = os.path.basename(os.path.join(FvDir, HeaderMFileName)) + elif UpdRegionCheck[item] == 'FSPS': + HeaderFd = open(os.path.join(FvDir, HeaderSFileName), "w") + FileBase = os.path.basename(os.path.join(FvDir, HeaderSFileName)) + FileName = FileBase.replace(".", "_").upper() + HeaderFd.write("%s\n" % (__copyright_h__ % date.today().year)) + HeaderFd.write("#ifndef __%s__\n" % FileName) + HeaderFd.write("#define __%s__\n\n" % FileName) + HeaderFd.write("#include <%s>\n\n" % HeaderFileName) + HeaderFd.write("#pragma pack(1)\n\n") + + Export = False + for Line in IncLines: + Match = re.search ("!EXPORT\s+([A-Z]+)\s+EXTERNAL_BOOTLOADER_STRUCT_(BEGIN|END)\s+", Line) + if Match: + if Match.group(2) == "BEGIN" and Match.group(1) == UpdRegionCheck[item]: + Export = True + continue + else: + Export = False + continue + if Export: + HeaderFd.write(Line) + HeaderFd.write("\n") + + Index = 0 + StartIndex = 0 + EndIndex = 0 + StructStart = [] + StructStartWithComment = [] + StructEnd = [] + for Line in TxtBody: + Index += 1 + Match = re.match("(typedef struct {)", Line) + if Match: + StartIndex = Index - 1 + Match = re.match("}\s([_A-Z0-9]+);", Line) + if Match and (UpdRegionCheck[item] in Match.group(1) or UpdConfigCheck[item] in Match.group(1)) and (ExcludedSpecificUpd[item] not in Match.group(1)): + EndIndex = Index + StructStart.append(StartIndex) + StructEnd.append(EndIndex) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index == StructStart[Item]: + Match = re.match("^(/\*\*\s*)", Line) + if Match: + StructStartWithComment.append(StructStart[Item]) + else: + StructStartWithComment.append(StructStart[Item] + 1) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]: + self.WriteLinesWithoutTailingSpace(HeaderFd, Line) + HeaderFd.write("#pragma pack()\n\n") + HeaderFd.write("#endif\n") + HeaderFd.close() + + HeaderFd = open(HeaderFile, "w") + FileBase = os.path.basename(HeaderFile) + FileName = FileBase.replace(".", "_").upper() + HeaderFd.write("%s\n" % (__copyright_h__ % date.today().year)) + HeaderFd.write("#ifndef __%s__\n" % FileName) + HeaderFd.write("#define __%s__\n\n" % FileName) + HeaderFd.write("#include <FspEas.h>\n\n") + HeaderFd.write("#pragma pack(1)\n\n") + + for item in range(len(UpdRegionCheck)): + Index = 0 + StartIndex = 0 + EndIndex = 0 + StructStart = [] + StructStartWithComment = [] + StructEnd = [] + for Line in TxtBody: + Index += 1 + Match = re.match("(typedef struct {)", Line) + if Match: + StartIndex = Index - 1 + Match = re.match("#define\s([_A-Z0-9]+)\s*", Line) + if Match and (UpdSignatureCheck[item] in Match.group(1) or UpdSignatureCheck[item] in Match.group(1)): + StructStart.append(Index - 1) + StructEnd.append(Index) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index == StructStart[Item]: + Match = re.match("^(/\*\*\s*)", Line) + if Match: + StructStartWithComment.append(StructStart[Item]) + else: + StructStartWithComment.append(StructStart[Item] + 1) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]: + self.WriteLinesWithoutTailingSpace(HeaderFd, Line) + HeaderFd.write("#pragma pack()\n\n") + HeaderFd.write("#endif\n") + HeaderFd.close() + + return 0 + + def WriteBsfStruct (self, BsfFd, Item): + LogExpr = CLogicalExpression() + if Item['type'] == "None": + Space = "gPlatformFspPkgTokenSpaceGuid" + else: + Space = Item['space'] + Line = " $%s_%s" % (Space, Item['cname']) + Match = re.match("\s*\{([x0-9a-fA-F,\s]+)\}\s*", Item['value']) + if Match: + DefaultValue = Match.group(1).strip() + else: + DefaultValue = Item['value'].strip() + if 'bitlength' in Item: + BsfFd.write(" %s%s%4d bits $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['bitlength'], DefaultValue)) + else: + BsfFd.write(" %s%s%4d bytes $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['length'], DefaultValue)) + TmpList = [] + if Item['type'] == "Combo": + if not Item['option'] in self._BuidinOption: + OptList = Item['option'].split(',') + for Option in OptList: + Option = Option.strip() + (OpVal, OpStr) = Option.split(':') + test = LogExpr.getNumber (OpVal) + if test is None: + raise Exception("Selection Index '%s' is not a number" % OpVal) + TmpList.append((OpVal, OpStr)) + return TmpList + + def WriteBsfOption (self, BsfFd, Item): + PcdName = Item['space'] + '_' + Item['cname'] + WriteHelp = 0 + if Item['type'] == "Combo": + if Item['option'] in self._BuidinOption: + Options = self._BuidinOption[Item['option']] + else: + Options = PcdName + BsfFd.write(' %s $%s, "%s", &%s,\n' % (Item['type'], PcdName, Item['name'], Options)) + WriteHelp = 1 + elif Item['type'].startswith("EditNum"): + 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']) + if Match: + BsfFd.write(' EditNum $%s, "%s", %s,\n' % (PcdName, Item['name'], Match.group(1))) + WriteHelp = 2 + elif Item['type'].startswith("EditText"): + BsfFd.write(' %s $%s, "%s",\n' % (Item['type'], PcdName, Item['name'])) + WriteHelp = 1 + elif Item['type'] == "Table": + Columns = Item['option'].split(',') + if len(Columns) != 0: + BsfFd.write(' %s $%s "%s",' % (Item['type'], PcdName, Item['name'])) + for Col in Columns: + Fmt = Col.split(':') + if len(Fmt) != 3: + raise Exception("Column format '%s' is invalid !" % Fmt) + try: + Dtype = int(Fmt[1].strip()) + except: + raise Exception("Column size '%s' is invalid !" % Fmt[1]) + BsfFd.write('\n Column "%s", %d bytes, %s' % (Fmt[0].strip(), Dtype, Fmt[2].strip())) + BsfFd.write(',\n') + WriteHelp = 1 + + if WriteHelp > 0: + HelpLines = Item['help'].split('\\n\\r') + FirstLine = True + for HelpLine in HelpLines: + if FirstLine: + FirstLine = False + BsfFd.write(' Help "%s"\n' % (HelpLine)) + else: + BsfFd.write(' "%s"\n' % (HelpLine)) + if WriteHelp == 2: + BsfFd.write(' "Valid range: %s ~ %s"\n' % (Match.group(2), Match.group(3))) + + def GenerateBsfFile (self, BsfFile): + + if BsfFile == '': + self.Error = "BSF output file '%s' is invalid" % BsfFile + return 1 + + if (self.NoDscFileChange (BsfFile)): + # DSC has not been modified yet + # So don't have to re-generate other files + self.Error = 'No DSC file change, skip to create UPD BSF file' + return 256 + + Error = 0 + OptionDict = {} + BsfFd = open(BsfFile, "w") + BsfFd.write("%s\n" % (__copyright_bsf__ % date.today().year)) + BsfFd.write("%s\n" % self._GlobalDataDef) + BsfFd.write("StructDef\n") + NextOffset = -1 + for Item in self._CfgItemList: + if Item['find'] != '': + BsfFd.write('\n Find "%s"\n' % Item['find']) + NextOffset = Item['offset'] + Item['length'] + if Item['name'] != '': + if NextOffset != Item['offset']: + BsfFd.write(" Skip %d bytes\n" % (Item['offset'] - NextOffset)) + if len(Item['subreg']) > 0: + NextOffset = Item['offset'] + BitsOffset = NextOffset * 8 + for SubItem in Item['subreg']: + BitsOffset += SubItem['bitlength'] + if SubItem['name'] == '': + if 'bitlength' in SubItem: + BsfFd.write(" Skip %d bits\n" % (SubItem['bitlength'])) + else: + BsfFd.write(" Skip %d bytes\n" % (SubItem['length'])) + else: + Options = self.WriteBsfStruct(BsfFd, SubItem) + if len(Options) > 0: + OptionDict[SubItem['space']+'_'+SubItem['cname']] = Options + + NextBitsOffset = (Item['offset'] + Item['length']) * 8 + if NextBitsOffset > BitsOffset: + BitsGap = NextBitsOffset - BitsOffset + BitsRemain = BitsGap % 8 + if BitsRemain: + BsfFd.write(" Skip %d bits\n" % BitsRemain) + BitsGap -= BitsRemain + BytesRemain = int(BitsGap / 8) + if BytesRemain: + BsfFd.write(" Skip %d bytes\n" % BytesRemain) + NextOffset = Item['offset'] + Item['length'] + else: + NextOffset = Item['offset'] + Item['length'] + Options = self.WriteBsfStruct(BsfFd, Item) + if len(Options) > 0: + OptionDict[Item['space']+'_'+Item['cname']] = Options + BsfFd.write("\nEndStruct\n\n") + + BsfFd.write("%s" % self._BuidinOptionTxt) + + for Each in OptionDict: + BsfFd.write("List &%s\n" % Each) + for Item in OptionDict[Each]: + BsfFd.write(' Selection %s , "%s"\n' % (Item[0], Item[1])) + BsfFd.write("EndList\n\n") + + BsfFd.write("BeginInfoBlock\n") + BsfFd.write(' PPVer "%s"\n' % (self._CfgBlkDict['ver'])) + BsfFd.write(' Description "%s"\n' % (self._CfgBlkDict['name'])) + BsfFd.write("EndInfoBlock\n\n") + + for Each in self._CfgPageDict: + BsfFd.write('Page "%s"\n' % self._CfgPageDict[Each]) + BsfItems = [] + for Item in self._CfgItemList: + if Item['name'] != '': + if Item['page'] != Each: + continue + if len(Item['subreg']) > 0: + for SubItem in Item['subreg']: + if SubItem['name'] != '': + BsfItems.append(SubItem) + else: + BsfItems.append(Item) + + BsfItems.sort(key=lambda x: x['order']) + + for Item in BsfItems: + self.WriteBsfOption (BsfFd, Item) + BsfFd.write("EndPage\n\n") + + BsfFd.close() + return Error + + +def Usage(): + print ("GenCfgOpt Version 0.56") + print ("Usage:") + print (" GenCfgOpt UPDTXT PlatformDscFile BuildFvDir [-D Macros]") + print (" GenCfgOpt HEADER PlatformDscFile BuildFvDir InputHFile [-D Macros]") + print (" GenCfgOpt GENBSF PlatformDscFile BuildFvDir BsfOutFile [-D Macros]") + +def Main(): + # + # Parse the options and args + # + i = 1 + + GenCfgOpt = CGenCfgOpt() + while i < len(sys.argv): + if sys.argv[i].strip().lower() == "--pcd": + BuildOptionPcd.append(sys.argv[i+1]) + i += 1 + i += 1 + argc = len(sys.argv) + if argc < 4: + Usage() + return 1 + else: + DscFile = sys.argv[2] + if not os.path.exists(DscFile): + print ("ERROR: Cannot open DSC file '%s' !" % DscFile) + return 2 + + OutFile = '' + if argc > 4: + if sys.argv[4][0] == '-': + Start = 4 + else: + OutFile = sys.argv[4] + Start = 5 + if argc > Start: + if GenCfgOpt.ParseMacros(sys.argv[Start:]) != 0: + print ("ERROR: Macro parsing failed !") + return 3 + + FvDir = sys.argv[3] + if not os.path.exists(FvDir): + os.makedirs(FvDir) + + if GenCfgOpt.ParseDscFile(DscFile, FvDir) != 0: + print ("ERROR: %s !" % GenCfgOpt.Error) + return 5 + + if GenCfgOpt.UpdateSubRegionDefaultValue() != 0: + print ("ERROR: %s !" % GenCfgOpt.Error) + return 7 + + if sys.argv[1] == "UPDTXT": + Ret = GenCfgOpt.CreateSplitUpdTxt(OutFile) + if Ret != 0: + # No change is detected + if Ret == 256: + print ("INFO: %s !" % (GenCfgOpt.Error)) + else : + print ("ERROR: %s !" % (GenCfgOpt.Error)) + return Ret + elif sys.argv[1] == "HEADER": + Ret = GenCfgOpt.CreateHeaderFile(OutFile) + if Ret != 0: + # No change is detected + if Ret == 256: + print ("INFO: %s !" % (GenCfgOpt.Error)) + else : + print ("ERROR: %s !" % (GenCfgOpt.Error)) + return 8 + return Ret + elif sys.argv[1] == "GENBSF": + Ret = GenCfgOpt.GenerateBsfFile(OutFile) + if Ret != 0: + # No change is detected + if Ret == 256: + print ("INFO: %s !" % (GenCfgOpt.Error)) + else : + print ("ERROR: %s !" % (GenCfgOpt.Error)) + return 9 + return Ret + else: + if argc < 5: + Usage() + return 1 + print ("ERROR: Unknown command '%s' !" % sys.argv[1]) + Usage() + return 1 + return 0 + return 0 + + +if __name__ == '__main__': + sys.exit(Main()) |