| ## @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_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(r"(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\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_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()) | |