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