1 ## @ PatchFv.py 2 # 3 # Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.<BR> 4 # This program and the accompanying materials are licensed and made available under 5 # the terms and conditions of the BSD License that accompanies this distribution. 6 # The full text of the license may be found at 7 # http://opensource.org/licenses/bsd-license.php. 8 # 9 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 # 12 ## 13 14 import os 15 import re 16 import sys 17 18 # 19 # Read data from file 20 # 21 # param [in] binfile Binary file 22 # param [in] offset Offset 23 # param [in] len Length 24 # 25 # retval value Value 26 # 27 def readDataFromFile (binfile, offset, len=1): 28 fd = open(binfile, "r+b") 29 fsize = os.path.getsize(binfile) 30 offval = offset & 0xFFFFFFFF 31 if (offval & 0x80000000): 32 offval = fsize - (0xFFFFFFFF - offval + 1) 33 fd.seek(offval) 34 bytearray = [ord(b) for b in fd.read(len)] 35 value = 0 36 idx = len - 1 37 while idx >= 0: 38 value = value << 8 | bytearray[idx] 39 idx = idx - 1 40 fd.close() 41 return value 42 43 # 44 # Check FSP header is valid or not 45 # 46 # param [in] binfile Binary file 47 # 48 # retval boolean True: valid; False: invalid 49 # 50 def IsFspHeaderValid (binfile): 51 fd = open (binfile, "rb") 52 bindat = fd.read(0x200) # only read first 0x200 bytes 53 fd.close() 54 HeaderList = ['FSPH' , 'FSPP' , 'FSPE'] # Check 'FSPH', 'FSPP', and 'FSPE' in the FSP header 55 OffsetList = [] 56 for each in HeaderList: 57 if each in bindat: 58 idx = bindat.index(each) 59 else: 60 idx = 0 61 OffsetList.append(idx) 62 if not OffsetList[0] or not OffsetList[1]: # If 'FSPH' or 'FSPP' is missing, it will return false 63 return False 64 Revision = ord(bindat[OffsetList[0] + 0x0B]) 65 # 66 # if revision is bigger than 1, it means it is FSP v1.1 or greater revision, which must contain 'FSPE'. 67 # 68 if Revision > 1 and not OffsetList[2]: 69 return False # If FSP v1.1 or greater without 'FSPE', then return false 70 return True 71 72 # 73 # Patch data in file 74 # 75 # param [in] binfile Binary file 76 # param [in] offset Offset 77 # param [in] value Patch value 78 # param [in] len Length 79 # 80 # retval len Length 81 # 82 def patchDataInFile (binfile, offset, value, len=1): 83 fd = open(binfile, "r+b") 84 fsize = os.path.getsize(binfile) 85 offval = offset & 0xFFFFFFFF 86 if (offval & 0x80000000): 87 offval = fsize - (0xFFFFFFFF - offval + 1) 88 bytearray = [] 89 idx = 0 90 while idx < len: 91 bytearray.append(value & 0xFF) 92 value = value >> 8 93 idx = idx + 1 94 fd.seek(offval) 95 fd.write("".join(chr(b) for b in bytearray)) 96 fd.close() 97 return len 98 99 100 class Symbols: 101 def __init__(self): 102 self.dictSymbolAddress = {} 103 self.dictGuidNameXref = {} 104 self.dictFfsOffset = {} 105 self.dictVariable = {} 106 self.dictModBase = {} 107 self.fdFile = None 108 self.string = "" 109 self.fdBase = 0xFFFFFFFF 110 self.fdSize = 0 111 self.index = 0 112 self.fvList = [] 113 self.parenthesisOpenSet = '([{<' 114 self.parenthesisCloseSet = ')]}>' 115 116 # 117 # Get FD file 118 # 119 # retval self.fdFile Retrieve FD file 120 # 121 def getFdFile (self): 122 return self.fdFile 123 124 # 125 # Get FD size 126 # 127 # retval self.fdSize Retrieve the size of FD file 128 # 129 def getFdSize (self): 130 return self.fdSize 131 132 def parseFvInfFile (self, infFile): 133 fvInfo = {} 134 fvFile = infFile[0:-4] + ".Fv" 135 fvInfo['Name'] = os.path.splitext(os.path.basename(infFile))[0] 136 fvInfo['Offset'] = self.getFvOffsetInFd(fvFile) 137 fvInfo['Size'] = readDataFromFile (fvFile, 0x20, 4) 138 fdIn = open(infFile, "r") 139 rptLines = fdIn.readlines() 140 fdIn.close() 141 fvInfo['Base'] = 0 142 for rptLine in rptLines: 143 match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine) 144 if match: 145 fvInfo['Base'] = int(match.group(1), 16) 146 break 147 self.fvList.append(dict(fvInfo)) 148 return 0 149 150 # 151 # Create dictionaries 152 # 153 # param [in] fvDir FV's directory 154 # param [in] fvNames All FV's names 155 # 156 # retval 0 Created dictionaries successfully 157 # 158 def createDicts (self, fvDir, fvNames): 159 # 160 # If the fvDir is not a dirctory, then raise an exception 161 # 162 if not os.path.isdir(fvDir): 163 raise Exception ("'%s' is not a valid directory!" % FvDir) 164 165 # 166 # If the Guid.xref is not existing in fvDir, then raise an exception 167 # 168 xrefFile = os.path.join(fvDir, "Guid.xref") 169 if not os.path.exists(xrefFile): 170 raise Exception("Cannot open GUID Xref file '%s'!" % xrefFile) 171 172 # 173 # Add GUID reference to dictionary 174 # 175 self.dictGuidNameXref = {} 176 self.parseGuidXrefFile(xrefFile) 177 178 # 179 # Split up each FV from fvNames and get the fdBase 180 # 181 fvList = fvNames.split(":") 182 fdBase = fvList.pop() 183 if len(fvList) == 0: 184 fvList.append(fdBase) 185 186 # 187 # If the FD file is not existing, then raise an exception 188 # 189 fdFile = os.path.join(fvDir, fdBase.strip() + ".fd") 190 if not os.path.exists(fdFile): 191 raise Exception("Cannot open FD file '%s'!" % fdFile) 192 193 # 194 # Get the size of the FD file 195 # 196 self.fdFile = fdFile 197 self.fdSize = os.path.getsize(fdFile) 198 199 # 200 # If the INF file, which is the first element of fvList, is not existing, then raise an exception 201 # 202 infFile = os.path.join(fvDir, fvList[0].strip()) + ".inf" 203 if not os.path.exists(infFile): 204 raise Exception("Cannot open INF file '%s'!" % infFile) 205 206 # 207 # Parse INF file in order to get fdBase and then assign those values to dictVariable 208 # 209 self.parseInfFile(infFile) 210 self.dictVariable = {} 211 self.dictVariable["FDSIZE"] = self.fdSize 212 self.dictVariable["FDBASE"] = self.fdBase 213 214 # 215 # Collect information from FV MAP file and FV TXT file then 216 # put them into dictionaries 217 # 218 self.fvList = [] 219 self.dictSymbolAddress = {} 220 self.dictFfsOffset = {} 221 for file in fvList: 222 223 # 224 # If the .Fv.map file is not existing, then raise an exception. 225 # Otherwise, parse FV MAP file 226 # 227 fvFile = os.path.join(fvDir, file.strip()) + ".Fv" 228 mapFile = fvFile + ".map" 229 if not os.path.exists(mapFile): 230 raise Exception("Cannot open MAP file '%s'!" % mapFile) 231 232 infFile = fvFile[0:-3] + ".inf" 233 self.parseFvInfFile(infFile) 234 self.parseFvMapFile(mapFile) 235 236 # 237 # If the .Fv.txt file is not existing, then raise an exception. 238 # Otherwise, parse FV TXT file 239 # 240 fvTxtFile = fvFile + ".txt" 241 if not os.path.exists(fvTxtFile): 242 raise Exception("Cannot open FV TXT file '%s'!" % fvTxtFile) 243 244 self.parseFvTxtFile(fvTxtFile) 245 246 for fv in self.fvList: 247 self.dictVariable['_BASE_%s_' % fv['Name']] = fv['Base'] 248 # 249 # Search all MAP files in FFS directory if it exists then parse MOD MAP file 250 # 251 ffsDir = os.path.join(fvDir, "Ffs") 252 if (os.path.isdir(ffsDir)): 253 for item in os.listdir(ffsDir): 254 if len(item) <= 0x24: 255 continue 256 mapFile =os.path.join(ffsDir, item, "%s.map" % item[0:0x24]) 257 if not os.path.exists(mapFile): 258 continue 259 self.parseModMapFile(item[0x24:], mapFile) 260 261 return 0 262 263 # 264 # Get FV offset in FD file 265 # 266 # param [in] fvFile FV file 267 # 268 # retval offset Got FV offset successfully 269 # 270 def getFvOffsetInFd(self, fvFile): 271 # 272 # Check if the first 0x70 bytes of fvFile can be found in fdFile 273 # 274 fvHandle = open(fvFile, "r+b") 275 fdHandle = open(self.fdFile, "r+b") 276 offset = fdHandle.read().find(fvHandle.read(0x70)) 277 fvHandle.close() 278 fdHandle.close() 279 if offset == -1: 280 raise Exception("Could not locate FV file %s in FD!" % fvFile) 281 return offset 282 283 # 284 # Parse INF file 285 # 286 # param [in] infFile INF file 287 # 288 # retval 0 Parsed INF file successfully 289 # 290 def parseInfFile(self, infFile): 291 # 292 # Get FV offset and search EFI_BASE_ADDRESS in the FD file 293 # then assign the value of EFI_BASE_ADDRESS to fdBase 294 # 295 fvOffset = self.getFvOffsetInFd(infFile[0:-4] + ".Fv") 296 fdIn = open(infFile, "r") 297 rptLine = fdIn.readline() 298 self.fdBase = 0xFFFFFFFF 299 while (rptLine != "" ): 300 #EFI_BASE_ADDRESS = 0xFFFDF400 301 match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine) 302 if match is not None: 303 self.fdBase = int(match.group(1), 16) - fvOffset 304 rptLine = fdIn.readline() 305 fdIn.close() 306 if self.fdBase == 0xFFFFFFFF: 307 raise Exception("Could not find EFI_BASE_ADDRESS in INF file!" % fvFile) 308 return 0 309 310 # 311 # Parse FV TXT file 312 # 313 # param [in] fvTxtFile .Fv.txt file 314 # 315 # retval 0 Parsed FV TXT file successfully 316 # 317 def parseFvTxtFile(self, fvTxtFile): 318 fvName = os.path.basename(fvTxtFile)[0:-7].upper() 319 # 320 # Get information from .Fv.txt in order to create a dictionary 321 # For example, 322 # self.dictFfsOffset[912740BE-2284-4734-B971-84B027353F0C] = 0x000D4078 323 # 324 fvOffset = self.getFvOffsetInFd(fvTxtFile[0:-4]) 325 fdIn = open(fvTxtFile, "r") 326 rptLine = fdIn.readline() 327 while (rptLine != "" ): 328 match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", rptLine) 329 if match is not None: 330 if match.group(2) in self.dictFfsOffset: 331 self.dictFfsOffset[fvName + ':' + match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset) 332 else: 333 self.dictFfsOffset[match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset) 334 rptLine = fdIn.readline() 335 fdIn.close() 336 return 0 337 338 # 339 # Parse FV MAP file 340 # 341 # param [in] mapFile .Fv.map file 342 # 343 # retval 0 Parsed FV MAP file successfully 344 # 345 def parseFvMapFile(self, mapFile): 346 # 347 # Get information from .Fv.map in order to create dictionaries 348 # For example, 349 # self.dictModBase[FspSecCore:BASE] = 4294592776 (0xfffa4908) 350 # self.dictModBase[FspSecCore:ENTRY] = 4294606552 (0xfffa7ed8) 351 # self.dictModBase[FspSecCore:TEXT] = 4294593080 (0xfffa4a38) 352 # self.dictModBase[FspSecCore:DATA] = 4294612280 (0xfffa9538) 353 # self.dictSymbolAddress[FspSecCore:_SecStartup] = 0x00fffa4a38 354 # 355 fdIn = open(mapFile, "r") 356 rptLine = fdIn.readline() 357 modName = "" 358 foundModHdr = False 359 while (rptLine != "" ): 360 if rptLine[0] != ' ': 361 #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958) 362 #(GUID=86D70125-BAA3-4296-A62F-602BEBBB9081 .textbaseaddress=0x00fffb4398 .databaseaddress=0x00fffb4178) 363 match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+)\)", rptLine) 364 if match is not None: 365 foundModHdr = True 366 modName = match.group(1) 367 if len(modName) == 36: 368 modName = self.dictGuidNameXref[modName.upper()] 369 self.dictModBase['%s:BASE' % modName] = int (match.group(2), 16) 370 self.dictModBase['%s:ENTRY' % modName] = int (match.group(3), 16) 371 match = re.match("\(GUID=([A-Z0-9\-]+)\s+\.textbaseaddress=(0x[0-9a-fA-F]+)\s+\.databaseaddress=(0x[0-9a-fA-F]+)\)", rptLine) 372 if match is not None: 373 if foundModHdr: 374 foundModHdr = False 375 else: 376 modName = match.group(1) 377 if len(modName) == 36: 378 modName = self.dictGuidNameXref[modName.upper()] 379 self.dictModBase['%s:TEXT' % modName] = int (match.group(2), 16) 380 self.dictModBase['%s:DATA' % modName] = int (match.group(3), 16) 381 else: 382 # 0x00fff8016c __ModuleEntryPoint 383 foundModHdr = False 384 match = re.match("^\s+(0x[a-z0-9]+)\s+([_a-zA-Z0-9]+)", rptLine) 385 if match is not None: 386 self.dictSymbolAddress["%s:%s"%(modName, match.group(2))] = match.group(1) 387 rptLine = fdIn.readline() 388 fdIn.close() 389 return 0 390 391 # 392 # Parse MOD MAP file 393 # 394 # param [in] moduleName Module name 395 # param [in] mapFile .Fv.map file 396 # 397 # retval 0 Parsed MOD MAP file successfully 398 # retval 1 There is no moduleEntryPoint in modSymbols 399 # 400 def parseModMapFile(self, moduleName, mapFile): 401 # 402 # Get information from mapFile by moduleName in order to create a dictionary 403 # For example, 404 # self.dictSymbolAddress[FspSecCore:___guard_fids_count] = 0x00fffa4778 405 # 406 modSymbols = {} 407 fdIn = open(mapFile, "r") 408 reportLines = fdIn.readlines() 409 fdIn.close() 410 411 moduleEntryPoint = "__ModuleEntryPoint" 412 reportLine = reportLines[0] 413 if reportLine.strip().find("Archive member included") != -1: 414 #GCC 415 # 0x0000000000001d55 IoRead8 416 patchMapFileMatchString = "\s+(0x[0-9a-fA-F]{16})\s+([^\s][^0x][_a-zA-Z0-9\-]+)\s" 417 matchKeyGroupIndex = 2 418 matchSymbolGroupIndex = 1 419 prefix = '_' 420 else: 421 #MSFT 422 #0003:00000190 _gComBase 00007a50 SerialPo 423 patchMapFileMatchString = "^\s[0-9a-fA-F]{4}:[0-9a-fA-F]{8}\s+(\w+)\s+([0-9a-fA-F]{8}\s+)" 424 matchKeyGroupIndex = 1 425 matchSymbolGroupIndex = 2 426 prefix = '' 427 428 for reportLine in reportLines: 429 match = re.match(patchMapFileMatchString, reportLine) 430 if match is not None: 431 modSymbols[prefix + match.group(matchKeyGroupIndex)] = match.group(matchSymbolGroupIndex) 432 433 # Handle extra module patchable PCD variable in Linux map since it might have different format 434 # .data._gPcd_BinaryPatch_PcdVpdBaseAddress 435 # 0x0000000000003714 0x4 /tmp/ccmytayk.ltrans1.ltrans.o 436 handleNext = False 437 if matchSymbolGroupIndex == 1: 438 for reportLine in reportLines: 439 if handleNext: 440 handleNext = False 441 pcdName = match.group(1) 442 match = re.match("\s+(0x[0-9a-fA-F]{16})\s+", reportLine) 443 if match is not None: 444 modSymbols[prefix + pcdName] = match.group(1) 445 else: 446 match = re.match("^\s\.data\.(_gPcd_BinaryPatch[_a-zA-Z0-9\-]+)", reportLine) 447 if match is not None: 448 handleNext = True 449 continue 450 451 if not moduleEntryPoint in modSymbols: 452 return 1 453 454 modEntry = '%s:%s' % (moduleName,moduleEntryPoint) 455 if not modEntry in self.dictSymbolAddress: 456 modKey = '%s:ENTRY' % moduleName 457 if modKey in self.dictModBase: 458 baseOffset = self.dictModBase['%s:ENTRY' % moduleName] - int(modSymbols[moduleEntryPoint], 16) 459 else: 460 return 2 461 else: 462 baseOffset = int(self.dictSymbolAddress[modEntry], 16) - int(modSymbols[moduleEntryPoint], 16) 463 for symbol in modSymbols: 464 fullSym = "%s:%s" % (moduleName, symbol) 465 if not fullSym in self.dictSymbolAddress: 466 self.dictSymbolAddress[fullSym] = "0x00%08x" % (baseOffset+ int(modSymbols[symbol], 16)) 467 return 0 468 469 # 470 # Parse Guid.xref file 471 # 472 # param [in] xrefFile the full directory of Guid.xref file 473 # 474 # retval 0 Parsed Guid.xref file successfully 475 # 476 def parseGuidXrefFile(self, xrefFile): 477 # 478 # Get information from Guid.xref in order to create a GuidNameXref dictionary 479 # The dictGuidNameXref, for example, will be like 480 # dictGuidNameXref [1BA0062E-C779-4582-8566-336AE8F78F09] = FspSecCore 481 # 482 fdIn = open(xrefFile, "r") 483 rptLine = fdIn.readline() 484 while (rptLine != "" ): 485 match = re.match("([0-9a-fA-F\-]+)\s([_a-zA-Z0-9]+)", rptLine) 486 if match is not None: 487 self.dictGuidNameXref[match.group(1).upper()] = match.group(2) 488 rptLine = fdIn.readline() 489 fdIn.close() 490 return 0 491 492 # 493 # Get current character 494 # 495 # retval elf.string[self.index] 496 # retval '' Exception 497 # 498 def getCurr(self): 499 try: 500 return self.string[self.index] 501 except Exception: 502 return '' 503 504 # 505 # Check to see if it is last index 506 # 507 # retval self.index 508 # 509 def isLast(self): 510 return self.index == len(self.string) 511 512 # 513 # Move to next index 514 # 515 def moveNext(self): 516 self.index += 1 517 518 # 519 # Skip space 520 # 521 def skipSpace(self): 522 while not self.isLast(): 523 if self.getCurr() in ' \t': 524 self.moveNext() 525 else: 526 return 527 528 # 529 # Parse value 530 # 531 # retval value 532 # 533 def parseValue(self): 534 self.skipSpace() 535 var = '' 536 while not self.isLast(): 537 char = self.getCurr() 538 if char.lower() in '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:-': 539 var += char 540 self.moveNext() 541 else: 542 break 543 544 if ':' in var: 545 partList = var.split(':') 546 lenList = len(partList) 547 if lenList != 2 and lenList != 3: 548 raise Exception("Unrecognized expression %s" % var) 549 modName = partList[lenList-2] 550 modOff = partList[lenList-1] 551 if ('-' not in modName) and (modOff[0] in '0123456789'): 552 # MOD: OFFSET 553 var = self.getModGuid(modName) + ":" + modOff 554 if '-' in var: # GUID:OFFSET 555 value = self.getGuidOff(var) 556 else: 557 value = self.getSymbols(var) 558 self.synUsed = True 559 else: 560 if var[0] in '0123456789': 561 value = self.getNumber(var) 562 else: 563 value = self.getVariable(var) 564 return int(value) 565 566 # 567 # Parse single operation 568 # 569 # retval ~self.parseBrace() or self.parseValue() 570 # 571 def parseSingleOp(self): 572 self.skipSpace() 573 char = self.getCurr() 574 if char == '~': 575 self.moveNext() 576 return ~self.parseBrace() 577 else: 578 return self.parseValue() 579 580 # 581 # Parse symbol of Brace([, {, <) 582 # 583 # retval value or self.parseSingleOp() 584 # 585 def parseBrace(self): 586 self.skipSpace() 587 char = self.getCurr() 588 parenthesisType = self.parenthesisOpenSet.find(char) 589 if parenthesisType >= 0: 590 self.moveNext() 591 value = self.parseExpr() 592 self.skipSpace() 593 if self.getCurr() != self.parenthesisCloseSet[parenthesisType]: 594 raise Exception("No closing brace") 595 self.moveNext() 596 if parenthesisType == 1: # [ : Get content 597 value = self.getContent(value) 598 elif parenthesisType == 2: # { : To address 599 value = self.toAddress(value) 600 elif parenthesisType == 3: # < : To offset 601 value = self.toOffset(value) 602 return value 603 else: 604 return self.parseSingleOp() 605 606 # 607 # Parse symbol of Multiplier(*) 608 # 609 # retval value or self.parseSingleOp() 610 # 611 def parseMul(self): 612 values = [self.parseBrace()] 613 while True: 614 self.skipSpace() 615 char = self.getCurr() 616 if char == '*': 617 self.moveNext() 618 values.append(self.parseBrace()) 619 else: 620 break 621 value = 1 622 for each in values: 623 value *= each 624 return value 625 626 # 627 # Parse symbol of And(&) and Or(|) 628 # 629 # retval value 630 # 631 def parseAndOr(self): 632 value = self.parseMul() 633 op = None 634 while True: 635 self.skipSpace() 636 char = self.getCurr() 637 if char == '&': 638 self.moveNext() 639 value &= self.parseMul() 640 elif char == '|': 641 div_index = self.index 642 self.moveNext() 643 value |= self.parseMul() 644 else: 645 break 646 647 return value 648 649 # 650 # Parse symbol of Add(+) and Minus(-) 651 # 652 # retval sum(values) 653 # 654 def parseAddMinus(self): 655 values = [self.parseAndOr()] 656 while True: 657 self.skipSpace() 658 char = self.getCurr() 659 if char == '+': 660 self.moveNext() 661 values.append(self.parseAndOr()) 662 elif char == '-': 663 self.moveNext() 664 values.append(-1 * self.parseAndOr()) 665 else: 666 break 667 return sum(values) 668 669 # 670 # Parse expression 671 # 672 # retval self.parseAddMinus() 673 # 674 def parseExpr(self): 675 return self.parseAddMinus() 676 677 # 678 # Get result 679 # 680 # retval value 681 # 682 def getResult(self): 683 value = self.parseExpr() 684 self.skipSpace() 685 if not self.isLast(): 686 raise Exception("Unexpected character found '%s'" % self.getCurr()) 687 return value 688 689 # 690 # Get module GUID 691 # 692 # retval value 693 # 694 def getModGuid(self, var): 695 guid = (guid for guid,name in self.dictGuidNameXref.items() if name==var) 696 try: 697 value = guid.next() 698 except Exception: 699 raise Exception("Unknown module name %s !" % var) 700 return value 701 702 # 703 # Get variable 704 # 705 # retval value 706 # 707 def getVariable(self, var): 708 value = self.dictVariable.get(var, None) 709 if value == None: 710 raise Exception("Unrecognized variable '%s'" % var) 711 return value 712 713 # 714 # Get number 715 # 716 # retval value 717 # 718 def getNumber(self, var): 719 var = var.strip() 720 if var.startswith('0x'): # HEX 721 value = int(var, 16) 722 else: 723 value = int(var, 10) 724 return value 725 726 # 727 # Get content 728 # 729 # param [in] value 730 # 731 # retval value 732 # 733 def getContent(self, value): 734 return readDataFromFile (self.fdFile, self.toOffset(value), 4) 735 736 # 737 # Change value to address 738 # 739 # param [in] value 740 # 741 # retval value 742 # 743 def toAddress(self, value): 744 if value < self.fdSize: 745 value = value + self.fdBase 746 return value 747 748 # 749 # Change value to offset 750 # 751 # param [in] value 752 # 753 # retval value 754 # 755 def toOffset(self, value): 756 offset = None 757 for fvInfo in self.fvList: 758 if (value >= fvInfo['Base']) and (value < fvInfo['Base'] + fvInfo['Size']): 759 offset = value - fvInfo['Base'] + fvInfo['Offset'] 760 if not offset: 761 if (value >= self.fdBase) and (value < self.fdBase + self.fdSize): 762 offset = value - self.fdBase 763 else: 764 offset = value 765 if offset >= self.fdSize: 766 raise Exception("Invalid file offset 0x%08x !" % value) 767 return offset 768 769 # 770 # Get GUID offset 771 # 772 # param [in] value 773 # 774 # retval value 775 # 776 def getGuidOff(self, value): 777 # GUID:Offset 778 symbolName = value.split(':') 779 if len(symbolName) == 3: 780 fvName = symbolName[0].upper() 781 keyName = '%s:%s' % (fvName, symbolName[1]) 782 offStr = symbolName[2] 783 elif len(symbolName) == 2: 784 keyName = symbolName[0] 785 offStr = symbolName[1] 786 if keyName in self.dictFfsOffset: 787 value = (int(self.dictFfsOffset[keyName], 16) + int(offStr, 16)) & 0xFFFFFFFF 788 else: 789 raise Exception("Unknown GUID %s !" % value) 790 return value 791 792 # 793 # Get symbols 794 # 795 # param [in] value 796 # 797 # retval ret 798 # 799 def getSymbols(self, value): 800 if self.dictSymbolAddress.has_key(value): 801 # Module:Function 802 ret = int (self.dictSymbolAddress[value], 16) 803 else: 804 raise Exception("Unknown symbol %s !" % value) 805 return ret 806 807 # 808 # Evaluate symbols 809 # 810 # param [in] expression 811 # param [in] isOffset 812 # 813 # retval value & 0xFFFFFFFF 814 # 815 def evaluate(self, expression, isOffset): 816 self.index = 0 817 self.synUsed = False 818 self.string = expression 819 value = self.getResult() 820 if isOffset: 821 if self.synUsed: 822 # Consider it as an address first 823 value = self.toOffset(value) 824 if value & 0x80000000: 825 # Consider it as a negative offset next 826 offset = (~value & 0xFFFFFFFF) + 1 827 if offset < self.fdSize: 828 value = self.fdSize - offset 829 if value >= self.fdSize: 830 raise Exception("Invalid offset expression !") 831 return value & 0xFFFFFFFF 832 833 # 834 # Print out the usage 835 # 836 def usage(): 837 print "Usage: \n\tPatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch \"Offset, Value\"" 838 839 def main(): 840 # 841 # Parse the options and args 842 # 843 symTables = Symbols() 844 845 # 846 # If the arguments are less than 4, then return an error. 847 # 848 if len(sys.argv) < 4: 849 Usage() 850 return 1 851 852 # 853 # If it fails to create dictionaries, then return an error. 854 # 855 if symTables.createDicts(sys.argv[1], sys.argv[2]) != 0: 856 print "ERROR: Failed to create symbol dictionary!!" 857 return 2 858 859 # 860 # Get FD file and size 861 # 862 fdFile = symTables.getFdFile() 863 fdSize = symTables.getFdSize() 864 865 try: 866 # 867 # Check to see if FSP header is valid 868 # 869 ret = IsFspHeaderValid(fdFile) 870 if ret == False: 871 raise Exception ("The FSP header is not valid. Stop patching FD.") 872 comment = "" 873 for fvFile in sys.argv[3:]: 874 # 875 # Check to see if it has enough arguments 876 # 877 items = fvFile.split(",") 878 if len (items) < 2: 879 raise Exception("Expect more arguments for '%s'!" % fvFile) 880 881 comment = "" 882 command = "" 883 params = [] 884 for item in items: 885 item = item.strip() 886 if item.startswith("@"): 887 comment = item[1:] 888 elif item.startswith("$"): 889 command = item[1:] 890 else: 891 if len(params) == 0: 892 isOffset = True 893 else : 894 isOffset = False 895 # 896 # Parse symbols then append it to params 897 # 898 params.append (symTables.evaluate(item, isOffset)) 899 900 # 901 # Patch a new value into FD file if it is not a command 902 # 903 if command == "": 904 # Patch a DWORD 905 if len (params) == 2: 906 offset = params[0] 907 value = params[1] 908 oldvalue = readDataFromFile(fdFile, offset, 4) 909 ret = patchDataInFile (fdFile, offset, value, 4) - 4 910 else: 911 raise Exception ("Patch command needs 2 parameters !") 912 913 if ret: 914 raise Exception ("Patch failed for offset 0x%08X" % offset) 915 else: 916 print "Patched offset 0x%08X:[%08X] with value 0x%08X # %s" % (offset, oldvalue, value, comment) 917 918 elif command == "COPY": 919 # 920 # Copy binary block from source to destination 921 # 922 if len (params) == 3: 923 src = symTables.toOffset(params[0]) 924 dest = symTables.toOffset(params[1]) 925 clen = symTables.toOffset(params[2]) 926 if (dest + clen <= fdSize) and (src + clen <= fdSize): 927 oldvalue = readDataFromFile(fdFile, src, clen) 928 ret = patchDataInFile (fdFile, dest, oldvalue, clen) - clen 929 else: 930 raise Exception ("Copy command OFFSET or LENGTH parameter is invalid !") 931 else: 932 raise Exception ("Copy command needs 3 parameters !") 933 934 if ret: 935 raise Exception ("Copy failed from offset 0x%08X to offset 0x%08X!" % (src, dest)) 936 else : 937 print "Copied %d bytes from offset 0x%08X ~ offset 0x%08X # %s" % (clen, src, dest, comment) 938 else: 939 raise Exception ("Unknown command %s!" % command) 940 return 0 941 942 except Exception as (ex): 943 print "ERROR: %s" % ex 944 return 1 945 946 if __name__ == '__main__': 947 sys.exit(main()) 948