1## @file 2# This file is used to generate DEPEX file for module's dependency expression 3# 4# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> 5# SPDX-License-Identifier: BSD-2-Clause-Patent 6 7## Import Modules 8# 9import sys 10import Common.LongFilePathOs as os 11import re 12import traceback 13from Common.LongFilePathSupport import OpenLongFilePath as open 14from io import BytesIO 15from struct import pack 16from Common.BuildToolError import * 17from Common.Misc import SaveFileOnChange 18from Common.Misc import GuidStructureStringToGuidString 19from Common.Misc import GuidStructureByteArrayToGuidString 20from Common.Misc import GuidStringToGuidStructureString 21from Common import EdkLogger as EdkLogger 22from Common.BuildVersion import gBUILD_VERSION 23from Common.DataType import * 24 25## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END" 26gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S) 27 28## Mapping between module type and EFI phase 29gType2Phase = { 30 SUP_MODULE_BASE : None, 31 SUP_MODULE_SEC : "PEI", 32 SUP_MODULE_PEI_CORE : "PEI", 33 SUP_MODULE_PEIM : "PEI", 34 SUP_MODULE_DXE_CORE : "DXE", 35 SUP_MODULE_DXE_DRIVER : "DXE", 36 SUP_MODULE_DXE_SMM_DRIVER : "DXE", 37 SUP_MODULE_DXE_RUNTIME_DRIVER: "DXE", 38 SUP_MODULE_DXE_SAL_DRIVER : "DXE", 39 SUP_MODULE_UEFI_DRIVER : "DXE", 40 SUP_MODULE_UEFI_APPLICATION : "DXE", 41 SUP_MODULE_SMM_CORE : "DXE", 42 SUP_MODULE_MM_STANDALONE : "MM", 43 SUP_MODULE_MM_CORE_STANDALONE : "MM", 44} 45 46## Convert dependency expression string into EFI internal representation 47# 48# DependencyExpression class is used to parse dependency expression string and 49# convert it into its binary form. 50# 51class DependencyExpression: 52 53 ArchProtocols = { 54 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid' 55 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid' 56 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid' 57 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid' 58 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid' 59 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid' 60 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid' 61 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid' 62 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid' 63 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid' 64 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid' 65 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid' 66 } 67 68 OpcodePriority = { 69 DEPEX_OPCODE_AND : 1, 70 DEPEX_OPCODE_OR : 1, 71 DEPEX_OPCODE_NOT : 2, 72 } 73 74 Opcode = { 75 "PEI" : { 76 DEPEX_OPCODE_PUSH : 0x02, 77 DEPEX_OPCODE_AND : 0x03, 78 DEPEX_OPCODE_OR : 0x04, 79 DEPEX_OPCODE_NOT : 0x05, 80 DEPEX_OPCODE_TRUE : 0x06, 81 DEPEX_OPCODE_FALSE : 0x07, 82 DEPEX_OPCODE_END : 0x08 83 }, 84 85 "DXE" : { 86 DEPEX_OPCODE_BEFORE: 0x00, 87 DEPEX_OPCODE_AFTER : 0x01, 88 DEPEX_OPCODE_PUSH : 0x02, 89 DEPEX_OPCODE_AND : 0x03, 90 DEPEX_OPCODE_OR : 0x04, 91 DEPEX_OPCODE_NOT : 0x05, 92 DEPEX_OPCODE_TRUE : 0x06, 93 DEPEX_OPCODE_FALSE : 0x07, 94 DEPEX_OPCODE_END : 0x08, 95 DEPEX_OPCODE_SOR : 0x09 96 }, 97 98 "MM" : { 99 DEPEX_OPCODE_BEFORE: 0x00, 100 DEPEX_OPCODE_AFTER : 0x01, 101 DEPEX_OPCODE_PUSH : 0x02, 102 DEPEX_OPCODE_AND : 0x03, 103 DEPEX_OPCODE_OR : 0x04, 104 DEPEX_OPCODE_NOT : 0x05, 105 DEPEX_OPCODE_TRUE : 0x06, 106 DEPEX_OPCODE_FALSE : 0x07, 107 DEPEX_OPCODE_END : 0x08, 108 DEPEX_OPCODE_SOR : 0x09 109 } 110 } 111 112 # all supported op codes and operands 113 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] 114 SupportedOperand = [DEPEX_OPCODE_TRUE, DEPEX_OPCODE_FALSE] 115 116 OpcodeWithSingleOperand = [DEPEX_OPCODE_NOT, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] 117 OpcodeWithTwoOperand = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR] 118 119 # op code that should not be the last one 120 NonEndingOpcode = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_SOR] 121 # op code must not present at the same time 122 ExclusiveOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] 123 # op code that should be the first one if it presents 124 AboveAllOpcode = [DEPEX_OPCODE_SOR, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] 125 126 # 127 # open and close brace must be taken as individual tokens 128 # 129 TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)") 130 131 ## Constructor 132 # 133 # @param Expression The list or string of dependency expression 134 # @param ModuleType The type of the module using the dependency expression 135 # 136 def __init__(self, Expression, ModuleType, Optimize=False): 137 self.ModuleType = ModuleType 138 self.Phase = gType2Phase[ModuleType] 139 if isinstance(Expression, type([])): 140 self.ExpressionString = " ".join(Expression) 141 self.TokenList = Expression 142 else: 143 self.ExpressionString = Expression 144 self.GetExpressionTokenList() 145 146 self.PostfixNotation = [] 147 self.OpcodeList = [] 148 149 self.GetPostfixNotation() 150 self.ValidateOpcode() 151 152 EdkLogger.debug(EdkLogger.DEBUG_8, repr(self)) 153 if Optimize: 154 self.Optimize() 155 EdkLogger.debug(EdkLogger.DEBUG_8, "\n Optimized: " + repr(self)) 156 157 def __str__(self): 158 return " ".join(self.TokenList) 159 160 def __repr__(self): 161 WellForm = '' 162 for Token in self.PostfixNotation: 163 if Token in self.SupportedOpcode: 164 WellForm += "\n " + Token 165 else: 166 WellForm += ' ' + Token 167 return WellForm 168 169 ## Split the expression string into token list 170 def GetExpressionTokenList(self): 171 self.TokenList = self.TokenPattern.findall(self.ExpressionString) 172 173 ## Convert token list into postfix notation 174 def GetPostfixNotation(self): 175 Stack = [] 176 LastToken = '' 177 for Token in self.TokenList: 178 if Token == "(": 179 if LastToken not in self.SupportedOpcode + ['(', '', None]: 180 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses", 181 ExtraData="Near %s" % LastToken) 182 Stack.append(Token) 183 elif Token == ")": 184 if '(' not in Stack: 185 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses", 186 ExtraData=str(self)) 187 elif LastToken in self.SupportedOpcode + ['', None]: 188 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses", 189 ExtraData="Near %s" % LastToken) 190 while len(Stack) > 0: 191 if Stack[-1] == '(': 192 Stack.pop() 193 break 194 self.PostfixNotation.append(Stack.pop()) 195 elif Token in self.OpcodePriority: 196 if Token == DEPEX_OPCODE_NOT: 197 if LastToken not in self.SupportedOpcode + ['(', '', None]: 198 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT", 199 ExtraData="Near %s" % LastToken) 200 elif LastToken in self.SupportedOpcode + ['(', '', None]: 201 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token, 202 ExtraData="Near %s" % LastToken) 203 204 while len(Stack) > 0: 205 if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]: 206 break 207 self.PostfixNotation.append(Stack.pop()) 208 Stack.append(Token) 209 self.OpcodeList.append(Token) 210 else: 211 if Token not in self.SupportedOpcode: 212 # not OP, take it as GUID 213 if LastToken not in self.SupportedOpcode + ['(', '', None]: 214 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token, 215 ExtraData="Near %s" % LastToken) 216 if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode: 217 if Token not in self.SupportedOperand: 218 self.PostfixNotation.append(DEPEX_OPCODE_PUSH) 219 # check if OP is valid in this phase 220 elif Token in self.Opcode[self.Phase]: 221 if Token == DEPEX_OPCODE_END: 222 break 223 self.OpcodeList.append(Token) 224 else: 225 EdkLogger.error("GenDepex", PARSER_ERROR, 226 "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase), 227 ExtraData=str(self)) 228 self.PostfixNotation.append(Token) 229 LastToken = Token 230 231 # there should not be parentheses in Stack 232 if '(' in Stack or ')' in Stack: 233 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses", 234 ExtraData=str(self)) 235 while len(Stack) > 0: 236 self.PostfixNotation.append(Stack.pop()) 237 if self.PostfixNotation[-1] != DEPEX_OPCODE_END: 238 self.PostfixNotation.append(DEPEX_OPCODE_END) 239 240 ## Validate the dependency expression 241 def ValidateOpcode(self): 242 for Op in self.AboveAllOpcode: 243 if Op in self.PostfixNotation: 244 if Op != self.PostfixNotation[0]: 245 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first opcode in the expression" % Op, 246 ExtraData=str(self)) 247 if len(self.PostfixNotation) < 3: 248 EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op, 249 ExtraData=str(self)) 250 for Op in self.ExclusiveOpcode: 251 if Op in self.OpcodeList: 252 if len(self.OpcodeList) > 1: 253 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only opcode in the expression" % Op, 254 ExtraData=str(self)) 255 if len(self.PostfixNotation) < 3: 256 EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op, 257 ExtraData=str(self)) 258 if self.TokenList[-1] != DEPEX_OPCODE_END and self.TokenList[-1] in self.NonEndingOpcode: 259 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1], 260 ExtraData=str(self)) 261 if self.TokenList[-1] == DEPEX_OPCODE_END and self.TokenList[-2] in self.NonEndingOpcode: 262 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2], 263 ExtraData=str(self)) 264 if DEPEX_OPCODE_END in self.TokenList and DEPEX_OPCODE_END != self.TokenList[-1]: 265 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END", 266 ExtraData=str(self)) 267 268 ## Simply optimize the dependency expression by removing duplicated operands 269 def Optimize(self): 270 OpcodeSet = set(self.OpcodeList) 271 # if there are isn't one in the set, return 272 if len(OpcodeSet) != 1: 273 return 274 Op = OpcodeSet.pop() 275 #if Op isn't either OR or AND, return 276 if Op not in [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]: 277 return 278 NewOperand = [] 279 AllOperand = set() 280 for Token in self.PostfixNotation: 281 if Token in self.SupportedOpcode or Token in NewOperand: 282 continue 283 AllOperand.add(Token) 284 if Token == DEPEX_OPCODE_TRUE: 285 if Op == DEPEX_OPCODE_AND: 286 continue 287 else: 288 NewOperand.append(Token) 289 break 290 elif Token == DEPEX_OPCODE_FALSE: 291 if Op == DEPEX_OPCODE_OR: 292 continue 293 else: 294 NewOperand.append(Token) 295 break 296 NewOperand.append(Token) 297 298 # don't generate depex if only TRUE operand left 299 if self.ModuleType == SUP_MODULE_PEIM and len(NewOperand) == 1 and NewOperand[0] == DEPEX_OPCODE_TRUE: 300 self.PostfixNotation = [] 301 return 302 303 # don't generate depex if all operands are architecture protocols 304 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 \ 305 Op == DEPEX_OPCODE_AND and \ 306 self.ArchProtocols == set(GuidStructureStringToGuidString(Guid) for Guid in AllOperand): 307 self.PostfixNotation = [] 308 return 309 310 if len(NewOperand) == 0: 311 self.TokenList = list(AllOperand) 312 else: 313 self.TokenList = [] 314 while True: 315 self.TokenList.append(NewOperand.pop(0)) 316 if NewOperand == []: 317 break 318 self.TokenList.append(Op) 319 self.PostfixNotation = [] 320 self.GetPostfixNotation() 321 322 323 ## Convert a GUID value in C structure format into its binary form 324 # 325 # @param Guid The GUID value in C structure format 326 # 327 # @retval array The byte array representing the GUID value 328 # 329 def GetGuidValue(self, Guid): 330 GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "") 331 GuidValueList = GuidValueString.split(",") 332 if len(GuidValueList) != 11 and len(GuidValueList) == 16: 333 GuidValueString = GuidStringToGuidStructureString(GuidStructureByteArrayToGuidString(Guid)) 334 GuidValueString = GuidValueString.replace("{", "").replace("}", "").replace(" ", "") 335 GuidValueList = GuidValueString.split(",") 336 if len(GuidValueList) != 11: 337 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid) 338 return pack("1I2H8B", *(int(value, 16) for value in GuidValueList)) 339 340 ## Save the binary form of dependency expression in file 341 # 342 # @param File The path of file. If None is given, put the data on console 343 # 344 # @retval True If the file doesn't exist or file is changed 345 # @retval False If file exists and is not changed. 346 # 347 def Generate(self, File=None): 348 Buffer = BytesIO() 349 if len(self.PostfixNotation) == 0: 350 return False 351 352 for Item in self.PostfixNotation: 353 if Item in self.Opcode[self.Phase]: 354 Buffer.write(pack("B", self.Opcode[self.Phase][Item])) 355 elif Item in self.SupportedOpcode: 356 EdkLogger.error("GenDepex", FORMAT_INVALID, 357 "Opcode [%s] is not expected in %s phase" % (Item, self.Phase), 358 ExtraData=self.ExpressionString) 359 else: 360 Buffer.write(self.GetGuidValue(Item)) 361 362 FilePath = "" 363 FileChangeFlag = True 364 if File is None: 365 sys.stdout.write(Buffer.getvalue()) 366 FilePath = "STDOUT" 367 else: 368 FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True) 369 370 Buffer.close() 371 return FileChangeFlag 372 373versionNumber = ("0.04" + " " + gBUILD_VERSION) 374__version__ = "%prog Version " + versionNumber 375__copyright__ = "Copyright (c) 2007-2018, Intel Corporation All rights reserved." 376__usage__ = "%prog [options] [dependency_expression_file]" 377 378## Parse command line options 379# 380# @retval OptionParser 381# 382def GetOptions(): 383 from optparse import OptionParser 384 385 Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__) 386 387 Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE", 388 help="Specify the name of depex file to be generated") 389 Parser.add_option("-t", "--module-type", dest="ModuleType", default=None, 390 help="The type of module for which the dependency expression serves") 391 Parser.add_option("-e", "--dependency-expression", dest="Expression", default="", 392 help="The string of dependency expression. If this option presents, the input file will be ignored.") 393 Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true", 394 help="Do some simple optimization on the expression.") 395 Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true", 396 help="build with verbose information") 397 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") 398 Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true", 399 help="build with little information") 400 401 return Parser.parse_args() 402 403 404## Entrance method 405# 406# @retval 0 Tool was successful 407# @retval 1 Tool failed 408# 409def Main(): 410 EdkLogger.Initialize() 411 Option, Input = GetOptions() 412 413 # Set log level 414 if Option.quiet: 415 EdkLogger.SetLevel(EdkLogger.QUIET) 416 elif Option.verbose: 417 EdkLogger.SetLevel(EdkLogger.VERBOSE) 418 elif Option.debug is not None: 419 EdkLogger.SetLevel(Option.debug + 1) 420 else: 421 EdkLogger.SetLevel(EdkLogger.INFO) 422 423 try: 424 if Option.ModuleType is None or Option.ModuleType not in gType2Phase: 425 EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported") 426 427 DxsFile = '' 428 if len(Input) > 0 and Option.Expression == "": 429 DxsFile = Input[0] 430 DxsString = open(DxsFile, 'r').read().replace("\n", " ").replace("\r", " ") 431 DxsString = gStartClosePattern.sub("\\1", DxsString) 432 elif Option.Expression != "": 433 if Option.Expression[0] == '"': 434 DxsString = Option.Expression[1:-1] 435 else: 436 DxsString = Option.Expression 437 else: 438 EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given") 439 440 Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize) 441 if Option.OutputFile is not None: 442 FileChangeFlag = Dpx.Generate(Option.OutputFile) 443 if not FileChangeFlag and DxsFile: 444 # 445 # Touch the output file if its time stamp is older than the original 446 # DXS file to avoid re-invoke this tool for the dependency check in build rule. 447 # 448 if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]: 449 os.utime(Option.OutputFile, None) 450 else: 451 Dpx.Generate() 452 except BaseException as X: 453 EdkLogger.quiet("") 454 if Option is not None and Option.debug is not None: 455 EdkLogger.quiet(traceback.format_exc()) 456 else: 457 EdkLogger.quiet(str(X)) 458 return 1 459 460 return 0 461 462if __name__ == '__main__': 463 sys.exit(Main()) 464 465