diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/GenDepex.py | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/GenDepex.py')
-rwxr-xr-x | src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/GenDepex.py | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/GenDepex.py b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/GenDepex.py new file mode 100755 index 00000000..901250bb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/GenDepex.py @@ -0,0 +1,464 @@ +## @file +# This file is used to generate DEPEX file for module's dependency expression +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent + +## Import Modules +# +import sys +import Common.LongFilePathOs as os +import re +import traceback +from Common.LongFilePathSupport import OpenLongFilePath as open +from io import BytesIO +from struct import pack +from Common.BuildToolError import * +from Common.Misc import SaveFileOnChange +from Common.Misc import GuidStructureStringToGuidString +from Common.Misc import GuidStructureByteArrayToGuidString +from Common.Misc import GuidStringToGuidStructureString +from Common import EdkLogger as EdkLogger +from Common.BuildVersion import gBUILD_VERSION +from Common.DataType import * + +## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END" +gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S) + +## Mapping between module type and EFI phase +gType2Phase = { + SUP_MODULE_BASE : None, + SUP_MODULE_SEC : "PEI", + SUP_MODULE_PEI_CORE : "PEI", + SUP_MODULE_PEIM : "PEI", + SUP_MODULE_DXE_CORE : "DXE", + SUP_MODULE_DXE_DRIVER : "DXE", + SUP_MODULE_DXE_SMM_DRIVER : "DXE", + SUP_MODULE_DXE_RUNTIME_DRIVER: "DXE", + SUP_MODULE_DXE_SAL_DRIVER : "DXE", + SUP_MODULE_UEFI_DRIVER : "DXE", + SUP_MODULE_UEFI_APPLICATION : "DXE", + SUP_MODULE_SMM_CORE : "DXE", + SUP_MODULE_MM_STANDALONE : "MM", + SUP_MODULE_MM_CORE_STANDALONE : "MM", +} + +## Convert dependency expression string into EFI internal representation +# +# DependencyExpression class is used to parse dependency expression string and +# convert it into its binary form. +# +class DependencyExpression: + + ArchProtocols = { + '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid' + '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid' + '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid' + '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid' + '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid' + '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid' + 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid' + 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid' + '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid' + '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid' + '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid' + '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid' + } + + OpcodePriority = { + DEPEX_OPCODE_AND : 1, + DEPEX_OPCODE_OR : 1, + DEPEX_OPCODE_NOT : 2, + } + + Opcode = { + "PEI" : { + DEPEX_OPCODE_PUSH : 0x02, + DEPEX_OPCODE_AND : 0x03, + DEPEX_OPCODE_OR : 0x04, + DEPEX_OPCODE_NOT : 0x05, + DEPEX_OPCODE_TRUE : 0x06, + DEPEX_OPCODE_FALSE : 0x07, + DEPEX_OPCODE_END : 0x08 + }, + + "DXE" : { + DEPEX_OPCODE_BEFORE: 0x00, + DEPEX_OPCODE_AFTER : 0x01, + DEPEX_OPCODE_PUSH : 0x02, + DEPEX_OPCODE_AND : 0x03, + DEPEX_OPCODE_OR : 0x04, + DEPEX_OPCODE_NOT : 0x05, + DEPEX_OPCODE_TRUE : 0x06, + DEPEX_OPCODE_FALSE : 0x07, + DEPEX_OPCODE_END : 0x08, + DEPEX_OPCODE_SOR : 0x09 + }, + + "MM" : { + DEPEX_OPCODE_BEFORE: 0x00, + DEPEX_OPCODE_AFTER : 0x01, + DEPEX_OPCODE_PUSH : 0x02, + DEPEX_OPCODE_AND : 0x03, + DEPEX_OPCODE_OR : 0x04, + DEPEX_OPCODE_NOT : 0x05, + DEPEX_OPCODE_TRUE : 0x06, + DEPEX_OPCODE_FALSE : 0x07, + DEPEX_OPCODE_END : 0x08, + DEPEX_OPCODE_SOR : 0x09 + } + } + + # all supported op codes and operands + SupportedOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER, DEPEX_OPCODE_PUSH, DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_END, DEPEX_OPCODE_SOR] + SupportedOperand = [DEPEX_OPCODE_TRUE, DEPEX_OPCODE_FALSE] + + OpcodeWithSingleOperand = [DEPEX_OPCODE_NOT, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] + OpcodeWithTwoOperand = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR] + + # op code that should not be the last one + NonEndingOpcode = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_SOR] + # op code must not present at the same time + ExclusiveOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] + # op code that should be the first one if it presents + AboveAllOpcode = [DEPEX_OPCODE_SOR, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] + + # + # open and close brace must be taken as individual tokens + # + TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)") + + ## Constructor + # + # @param Expression The list or string of dependency expression + # @param ModuleType The type of the module using the dependency expression + # + def __init__(self, Expression, ModuleType, Optimize=False): + self.ModuleType = ModuleType + self.Phase = gType2Phase[ModuleType] + if isinstance(Expression, type([])): + self.ExpressionString = " ".join(Expression) + self.TokenList = Expression + else: + self.ExpressionString = Expression + self.GetExpressionTokenList() + + self.PostfixNotation = [] + self.OpcodeList = [] + + self.GetPostfixNotation() + self.ValidateOpcode() + + EdkLogger.debug(EdkLogger.DEBUG_8, repr(self)) + if Optimize: + self.Optimize() + EdkLogger.debug(EdkLogger.DEBUG_8, "\n Optimized: " + repr(self)) + + def __str__(self): + return " ".join(self.TokenList) + + def __repr__(self): + WellForm = '' + for Token in self.PostfixNotation: + if Token in self.SupportedOpcode: + WellForm += "\n " + Token + else: + WellForm += ' ' + Token + return WellForm + + ## Split the expression string into token list + def GetExpressionTokenList(self): + self.TokenList = self.TokenPattern.findall(self.ExpressionString) + + ## Convert token list into postfix notation + def GetPostfixNotation(self): + Stack = [] + LastToken = '' + for Token in self.TokenList: + if Token == "(": + if LastToken not in self.SupportedOpcode + ['(', '', None]: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses", + ExtraData="Near %s" % LastToken) + Stack.append(Token) + elif Token == ")": + if '(' not in Stack: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses", + ExtraData=str(self)) + elif LastToken in self.SupportedOpcode + ['', None]: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses", + ExtraData="Near %s" % LastToken) + while len(Stack) > 0: + if Stack[-1] == '(': + Stack.pop() + break + self.PostfixNotation.append(Stack.pop()) + elif Token in self.OpcodePriority: + if Token == DEPEX_OPCODE_NOT: + if LastToken not in self.SupportedOpcode + ['(', '', None]: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT", + ExtraData="Near %s" % LastToken) + elif LastToken in self.SupportedOpcode + ['(', '', None]: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token, + ExtraData="Near %s" % LastToken) + + while len(Stack) > 0: + if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]: + break + self.PostfixNotation.append(Stack.pop()) + Stack.append(Token) + self.OpcodeList.append(Token) + else: + if Token not in self.SupportedOpcode: + # not OP, take it as GUID + if LastToken not in self.SupportedOpcode + ['(', '', None]: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token, + ExtraData="Near %s" % LastToken) + if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode: + if Token not in self.SupportedOperand: + self.PostfixNotation.append(DEPEX_OPCODE_PUSH) + # check if OP is valid in this phase + elif Token in self.Opcode[self.Phase]: + if Token == DEPEX_OPCODE_END: + break + self.OpcodeList.append(Token) + else: + EdkLogger.error("GenDepex", PARSER_ERROR, + "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase), + ExtraData=str(self)) + self.PostfixNotation.append(Token) + LastToken = Token + + # there should not be parentheses in Stack + if '(' in Stack or ')' in Stack: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses", + ExtraData=str(self)) + while len(Stack) > 0: + self.PostfixNotation.append(Stack.pop()) + if self.PostfixNotation[-1] != DEPEX_OPCODE_END: + self.PostfixNotation.append(DEPEX_OPCODE_END) + + ## Validate the dependency expression + def ValidateOpcode(self): + for Op in self.AboveAllOpcode: + if Op in self.PostfixNotation: + if Op != self.PostfixNotation[0]: + EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first opcode in the expression" % Op, + ExtraData=str(self)) + if len(self.PostfixNotation) < 3: + EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op, + ExtraData=str(self)) + for Op in self.ExclusiveOpcode: + if Op in self.OpcodeList: + if len(self.OpcodeList) > 1: + EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only opcode in the expression" % Op, + ExtraData=str(self)) + if len(self.PostfixNotation) < 3: + EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op, + ExtraData=str(self)) + if self.TokenList[-1] != DEPEX_OPCODE_END and self.TokenList[-1] in self.NonEndingOpcode: + EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1], + ExtraData=str(self)) + if self.TokenList[-1] == DEPEX_OPCODE_END and self.TokenList[-2] in self.NonEndingOpcode: + EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2], + ExtraData=str(self)) + if DEPEX_OPCODE_END in self.TokenList and DEPEX_OPCODE_END != self.TokenList[-1]: + EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END", + ExtraData=str(self)) + + ## Simply optimize the dependency expression by removing duplicated operands + def Optimize(self): + OpcodeSet = set(self.OpcodeList) + # if there are isn't one in the set, return + if len(OpcodeSet) != 1: + return + Op = OpcodeSet.pop() + #if Op isn't either OR or AND, return + if Op not in [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]: + return + NewOperand = [] + AllOperand = set() + for Token in self.PostfixNotation: + if Token in self.SupportedOpcode or Token in NewOperand: + continue + AllOperand.add(Token) + if Token == DEPEX_OPCODE_TRUE: + if Op == DEPEX_OPCODE_AND: + continue + else: + NewOperand.append(Token) + break + elif Token == DEPEX_OPCODE_FALSE: + if Op == DEPEX_OPCODE_OR: + continue + else: + NewOperand.append(Token) + break + NewOperand.append(Token) + + # don't generate depex if only TRUE operand left + if self.ModuleType == SUP_MODULE_PEIM and len(NewOperand) == 1 and NewOperand[0] == DEPEX_OPCODE_TRUE: + self.PostfixNotation = [] + return + + # don't generate depex if all operands are architecture protocols + if self.ModuleType in [SUP_MODULE_UEFI_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_DXE_RUNTIME_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE] and \ + Op == DEPEX_OPCODE_AND and \ + self.ArchProtocols == set(GuidStructureStringToGuidString(Guid) for Guid in AllOperand): + self.PostfixNotation = [] + return + + if len(NewOperand) == 0: + self.TokenList = list(AllOperand) + else: + self.TokenList = [] + while True: + self.TokenList.append(NewOperand.pop(0)) + if NewOperand == []: + break + self.TokenList.append(Op) + self.PostfixNotation = [] + self.GetPostfixNotation() + + + ## Convert a GUID value in C structure format into its binary form + # + # @param Guid The GUID value in C structure format + # + # @retval array The byte array representing the GUID value + # + def GetGuidValue(self, Guid): + GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "") + GuidValueList = GuidValueString.split(",") + if len(GuidValueList) != 11 and len(GuidValueList) == 16: + GuidValueString = GuidStringToGuidStructureString(GuidStructureByteArrayToGuidString(Guid)) + GuidValueString = GuidValueString.replace("{", "").replace("}", "").replace(" ", "") + GuidValueList = GuidValueString.split(",") + if len(GuidValueList) != 11: + EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid) + return pack("1I2H8B", *(int(value, 16) for value in GuidValueList)) + + ## Save the binary form of dependency expression in file + # + # @param File The path of file. If None is given, put the data on console + # + # @retval True If the file doesn't exist or file is changed + # @retval False If file exists and is not changed. + # + def Generate(self, File=None): + Buffer = BytesIO() + if len(self.PostfixNotation) == 0: + return False + + for Item in self.PostfixNotation: + if Item in self.Opcode[self.Phase]: + Buffer.write(pack("B", self.Opcode[self.Phase][Item])) + elif Item in self.SupportedOpcode: + EdkLogger.error("GenDepex", FORMAT_INVALID, + "Opcode [%s] is not expected in %s phase" % (Item, self.Phase), + ExtraData=self.ExpressionString) + else: + Buffer.write(self.GetGuidValue(Item)) + + FilePath = "" + FileChangeFlag = True + if File is None: + sys.stdout.write(Buffer.getvalue()) + FilePath = "STDOUT" + else: + FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True) + + Buffer.close() + return FileChangeFlag + +versionNumber = ("0.04" + " " + gBUILD_VERSION) +__version__ = "%prog Version " + versionNumber +__copyright__ = "Copyright (c) 2007-2018, Intel Corporation All rights reserved." +__usage__ = "%prog [options] [dependency_expression_file]" + +## Parse command line options +# +# @retval OptionParser +# +def GetOptions(): + from optparse import OptionParser + + Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__) + + Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE", + help="Specify the name of depex file to be generated") + Parser.add_option("-t", "--module-type", dest="ModuleType", default=None, + help="The type of module for which the dependency expression serves") + Parser.add_option("-e", "--dependency-expression", dest="Expression", default="", + help="The string of dependency expression. If this option presents, the input file will be ignored.") + Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true", + help="Do some simple optimization on the expression.") + Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true", + help="build with verbose information") + Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") + Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true", + help="build with little information") + + return Parser.parse_args() + + +## Entrance method +# +# @retval 0 Tool was successful +# @retval 1 Tool failed +# +def Main(): + EdkLogger.Initialize() + Option, Input = GetOptions() + + # Set log level + if Option.quiet: + EdkLogger.SetLevel(EdkLogger.QUIET) + elif Option.verbose: + EdkLogger.SetLevel(EdkLogger.VERBOSE) + elif Option.debug is not None: + EdkLogger.SetLevel(Option.debug + 1) + else: + EdkLogger.SetLevel(EdkLogger.INFO) + + try: + if Option.ModuleType is None or Option.ModuleType not in gType2Phase: + EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported") + + DxsFile = '' + if len(Input) > 0 and Option.Expression == "": + DxsFile = Input[0] + DxsString = open(DxsFile, 'r').read().replace("\n", " ").replace("\r", " ") + DxsString = gStartClosePattern.sub("\\1", DxsString) + elif Option.Expression != "": + if Option.Expression[0] == '"': + DxsString = Option.Expression[1:-1] + else: + DxsString = Option.Expression + else: + EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given") + + Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize) + if Option.OutputFile is not None: + FileChangeFlag = Dpx.Generate(Option.OutputFile) + if not FileChangeFlag and DxsFile: + # + # Touch the output file if its time stamp is older than the original + # DXS file to avoid re-invoke this tool for the dependency check in build rule. + # + if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]: + os.utime(Option.OutputFile, None) + else: + Dpx.Generate() + except BaseException as X: + EdkLogger.quiet("") + if Option is not None and Option.debug is not None: + EdkLogger.quiet(traceback.format_exc()) + else: + EdkLogger.quiet(str(X)) + return 1 + + return 0 + +if __name__ == '__main__': + sys.exit(Main()) + |