Add comment for python function, too. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: "Yao, Jiewen" <jiewen.yao@intel.com> Reviewed-by: "Mudusuru, Giri P" <giri.p.mudusuru@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18406 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			912 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			912 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| ## @ PatchFv.py
 | |
| #
 | |
| # Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
 | |
| # This program and the accompanying materials are licensed and made available under
 | |
| # the terms and conditions of the BSD License that accompanies this distribution.
 | |
| # The full text of the license may be found at
 | |
| # http://opensource.org/licenses/bsd-license.php.
 | |
| #
 | |
| # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| #
 | |
| ##
 | |
| 
 | |
| import os
 | |
| import re
 | |
| import sys
 | |
| 
 | |
| #
 | |
| #  Read data from file
 | |
| #
 | |
| #  param [in]  binfile     Binary file
 | |
| #  param [in]  offset      Offset
 | |
| #  param [in]  len         Length
 | |
| #
 | |
| #  retval      value       Value
 | |
| #
 | |
| def readDataFromFile (binfile, offset, len=1):
 | |
|     fd     = open(binfile, "r+b")
 | |
|     fsize  = os.path.getsize(binfile)
 | |
|     offval = offset & 0xFFFFFFFF
 | |
|     if (offval & 0x80000000):
 | |
|         offval = fsize - (0xFFFFFFFF - offval + 1)
 | |
|     fd.seek(offval)
 | |
|     bytearray = [ord(b) for b in fd.read(len)]
 | |
|     value = 0
 | |
|     idx   = len - 1
 | |
|     while  idx >= 0:
 | |
|         value = value << 8 | bytearray[idx]
 | |
|         idx = idx - 1
 | |
|     fd.close()
 | |
|     return value
 | |
| 
 | |
| #
 | |
| #  Check FSP header is valid or not
 | |
| #
 | |
| #  param [in]  binfile     Binary file
 | |
| #
 | |
| #  retval      boolean     True: valid; False: invalid
 | |
| #
 | |
| def IsFspHeaderValid (binfile):
 | |
|     fd     = open (binfile, "rb")
 | |
|     bindat = fd.read(0x200) # only read first 0x200 bytes
 | |
|     fd.close()
 | |
|     HeaderList = ['FSPH' , 'FSPP' , 'FSPE']       # Check 'FSPH', 'FSPP', and 'FSPE' in the FSP header
 | |
|     OffsetList = []
 | |
|     for each in HeaderList:
 | |
|         if each in bindat:
 | |
|             idx = bindat.index(each)
 | |
|         else:
 | |
|             idx = 0
 | |
|         OffsetList.append(idx)
 | |
|     if not OffsetList[0] or not OffsetList[1]:    # If 'FSPH' or 'FSPP' is missing, it will return false
 | |
|         return False
 | |
|     Revision = ord(bindat[OffsetList[0] + 0x0B])
 | |
|     #
 | |
|     # if revision is bigger than 1, it means it is FSP v1.1 or greater revision, which must contain 'FSPE'.
 | |
|     #
 | |
|     if Revision > 1 and not OffsetList[2]:
 | |
|         return False                              # If FSP v1.1 or greater without 'FSPE', then return false
 | |
|     return True
 | |
| 
 | |
| #
 | |
| #  Patch data in file
 | |
| #
 | |
| #  param [in]  binfile     Binary file
 | |
| #  param [in]  offset      Offset
 | |
| #  param [in]  value       Patch value
 | |
| #  param [in]  len         Length
 | |
| #
 | |
| #  retval      len         Length
 | |
| #
 | |
| def patchDataInFile (binfile, offset, value, len=1):
 | |
|     fd     = open(binfile, "r+b")
 | |
|     fsize  = os.path.getsize(binfile)
 | |
|     offval = offset & 0xFFFFFFFF
 | |
|     if (offval & 0x80000000):
 | |
|         offval = fsize - (0xFFFFFFFF - offval + 1)
 | |
|     bytearray = []
 | |
|     idx = 0
 | |
|     while  idx < len:
 | |
|         bytearray.append(value & 0xFF)
 | |
|         value          = value >> 8
 | |
|         idx            = idx + 1
 | |
|     fd.seek(offval)
 | |
|     fd.write("".join(chr(b) for b in bytearray))
 | |
|     fd.close()
 | |
|     return len
 | |
| 
 | |
| 
 | |
| class Symbols:
 | |
|     def __init__(self):
 | |
|         self.dictSymbolAddress = {}
 | |
|         self.dictGuidNameXref  = {}
 | |
|         self.dictFfsOffset     = {}
 | |
|         self.dictVariable      = {}
 | |
|         self.dictModBase       = {}
 | |
|         self.fdFile            = None
 | |
|         self.string            = ""
 | |
|         self.fdBase            = 0xFFFFFFFF
 | |
|         self.fdSize            = 0
 | |
|         self.index             = 0
 | |
|         self.parenthesisOpenSet   =  '([{<'
 | |
|         self.parenthesisCloseSet  =  ')]}>'
 | |
| 
 | |
|     #
 | |
|     #  Get FD file
 | |
|     #
 | |
|     #  retval      self.fdFile Retrieve FD file
 | |
|     #
 | |
|     def getFdFile (self):
 | |
|         return self.fdFile
 | |
| 
 | |
|     #
 | |
|     #  Get FD size
 | |
|     #
 | |
|     #  retval      self.fdSize Retrieve the size of FD file
 | |
|     #
 | |
|     def getFdSize (self):
 | |
|         return self.fdSize
 | |
| 
 | |
|     #
 | |
|     #  Create dictionaries
 | |
|     #
 | |
|     #  param [in]  fvDir       FV's directory
 | |
|     #  param [in]  fvNames     All FV's names
 | |
|     #
 | |
|     #  retval      0           Created dictionaries successfully
 | |
|     #
 | |
|     def createDicts (self, fvDir, fvNames):
 | |
|         #
 | |
|         # If the fvDir is not a dirctory, then raise an exception
 | |
|         #
 | |
|         if not os.path.isdir(fvDir):
 | |
|             raise Exception ("'%s' is not a valid directory!" % FvDir)
 | |
| 
 | |
|         #
 | |
|         # If the Guid.xref is not existing in fvDir, then raise an exception
 | |
|         #
 | |
|         xrefFile = os.path.join(fvDir, "Guid.xref")
 | |
|         if not os.path.exists(xrefFile):
 | |
|             raise Exception("Cannot open GUID Xref file '%s'!" % xrefFile)
 | |
| 
 | |
|         #
 | |
|         # Add GUID reference to dictionary
 | |
|         #
 | |
|         self.dictGuidNameXref  = {}
 | |
|         self.parseGuidXrefFile(xrefFile)
 | |
| 
 | |
|         #
 | |
|         # Split up each FV from fvNames and get the fdBase
 | |
|         #
 | |
|         fvList = fvNames.split(":")
 | |
|         fdBase = fvList.pop()
 | |
|         if len(fvList) == 0:
 | |
|             fvList.append(fdBase)
 | |
| 
 | |
|         #
 | |
|         # If the FD file is not existing, then raise an exception
 | |
|         #
 | |
|         fdFile =  os.path.join(fvDir, fdBase.strip() + ".fd")
 | |
|         if not os.path.exists(fdFile):
 | |
|             raise Exception("Cannot open FD file '%s'!" % fdFile)
 | |
| 
 | |
|         #
 | |
|         # Get the size of the FD file
 | |
|         #
 | |
|         self.fdFile = fdFile
 | |
|         self.fdSize = os.path.getsize(fdFile)
 | |
| 
 | |
|         #
 | |
|         # If the INF file, which is the first element of fvList, is not existing, then raise an exception
 | |
|         #
 | |
|         infFile = os.path.join(fvDir, fvList[0].strip()) + ".inf"
 | |
|         if not os.path.exists(infFile):
 | |
|             raise Exception("Cannot open INF file '%s'!" % infFile)
 | |
| 
 | |
|         #
 | |
|         # Parse INF file in order to get fdBase and then assign those values to dictVariable
 | |
|         #
 | |
|         self.parseInfFile(infFile)
 | |
|         self.dictVariable = {}
 | |
|         self.dictVariable["FDSIZE"] =  self.fdSize
 | |
|         self.dictVariable["FDBASE"] =  self.fdBase
 | |
| 
 | |
|         #
 | |
|         # Collect information from FV MAP file and FV TXT file then
 | |
|         # put them into dictionaries
 | |
|         #
 | |
|         self.dictSymbolAddress = {}
 | |
|         self.dictFfsOffset     = {}
 | |
|         for file in fvList:
 | |
| 
 | |
|             #
 | |
|             # If the .Fv.map file is not existing, then raise an exception.
 | |
|             # Otherwise, parse FV MAP file
 | |
|             #
 | |
|             fvFile  = os.path.join(fvDir, file.strip()) + ".Fv"
 | |
|             mapFile = fvFile + ".map"
 | |
|             if not os.path.exists(mapFile):
 | |
|                 raise Exception("Cannot open MAP file '%s'!" % mapFile)
 | |
| 
 | |
|             self.parseFvMapFile(mapFile)
 | |
| 
 | |
|             #
 | |
|             # If the .Fv.txt file is not existing, then raise an exception.
 | |
|             # Otherwise, parse FV TXT file
 | |
|             #
 | |
|             fvTxtFile  = fvFile + ".txt"
 | |
|             if not os.path.exists(fvTxtFile):
 | |
|                 raise Exception("Cannot open FV TXT file '%s'!" % fvTxtFile)
 | |
| 
 | |
|             self.parseFvTxtFile(fvTxtFile)
 | |
| 
 | |
|         #
 | |
|         # Search all MAP files in FFS directory if it exists then parse MOD MAP file
 | |
|         #
 | |
|         ffsDir = os.path.join(fvDir, "Ffs")
 | |
|         if (os.path.isdir(ffsDir)):
 | |
|             for item in os.listdir(ffsDir):
 | |
|                 if len(item) <= 0x24:
 | |
|                     continue
 | |
|                 mapFile =os.path.join(ffsDir, item, "%s.map" % item[0:0x24])
 | |
|                 if not os.path.exists(mapFile):
 | |
|                     continue
 | |
|                 self.parseModMapFile(item[0x24:], mapFile)
 | |
| 
 | |
|         return 0
 | |
| 
 | |
|     #
 | |
|     #  Get FV offset in FD file
 | |
|     #
 | |
|     #  param [in]  fvFile      FV file
 | |
|     #
 | |
|     #  retval      offset      Got FV offset successfully
 | |
|     #
 | |
|     def getFvOffsetInFd(self, fvFile):
 | |
|         #
 | |
|         # Check if the first 0x70 bytes of fvFile can be found in fdFile
 | |
|         #
 | |
|         fvHandle = open(fvFile, "r+b")
 | |
|         fdHandle = open(self.fdFile, "r+b")
 | |
|         offset = fdHandle.read().find(fvHandle.read(0x70))
 | |
|         fvHandle.close()
 | |
|         fdHandle.close()
 | |
|         if offset == -1:
 | |
|             raise Exception("Could not locate FV file %s in FD!" % fvFile)
 | |
|         return offset
 | |
| 
 | |
|     #
 | |
|     #  Parse INF file
 | |
|     #
 | |
|     #  param [in]  infFile     INF file
 | |
|     #
 | |
|     #  retval      0           Parsed INF file successfully
 | |
|     #
 | |
|     def parseInfFile(self, infFile):
 | |
|         #
 | |
|         # Get FV offset and search EFI_BASE_ADDRESS in the FD file 
 | |
|         # then assign the value of EFI_BASE_ADDRESS to fdBase
 | |
|         #
 | |
|         fvOffset    = self.getFvOffsetInFd(infFile[0:-4] + ".Fv")
 | |
|         fdIn        = open(infFile, "r")
 | |
|         rptLine     = fdIn.readline()
 | |
|         self.fdBase = 0xFFFFFFFF
 | |
|         while (rptLine != "" ):
 | |
|             #EFI_BASE_ADDRESS = 0xFFFDF400
 | |
|             match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine)
 | |
|             if match is not None:
 | |
|                 self.fdBase = int(match.group(1), 16) - fvOffset
 | |
|             rptLine  = fdIn.readline()
 | |
|         fdIn.close()
 | |
|         if self.fdBase == 0xFFFFFFFF:
 | |
|             raise Exception("Could not find EFI_BASE_ADDRESS in INF file!" % fvFile)
 | |
|         return 0
 | |
| 
 | |
|     #
 | |
|     #  Parse FV TXT file
 | |
|     #
 | |
|     #  param [in]  fvTxtFile   .Fv.txt file
 | |
|     #
 | |
|     #  retval      0           Parsed FV TXT file successfully
 | |
|     #
 | |
|     def parseFvTxtFile(self, fvTxtFile):
 | |
|         #
 | |
|         # Get information from .Fv.txt in order to create a dictionary
 | |
|         # For example,
 | |
|         # self.dictFfsOffset[912740BE-2284-4734-B971-84B027353F0C] = 0x000D4078
 | |
|         #
 | |
|         fvOffset = self.getFvOffsetInFd(fvTxtFile[0:-4])
 | |
|         fdIn     = open(fvTxtFile, "r")
 | |
|         rptLine  = fdIn.readline()
 | |
|         while (rptLine != "" ):
 | |
|             match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", rptLine)
 | |
|             if match is not None:
 | |
|                 self.dictFfsOffset[match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset)
 | |
|             rptLine  = fdIn.readline()
 | |
|         fdIn.close()
 | |
|         return 0
 | |
| 
 | |
|     #
 | |
|     #  Parse FV MAP file
 | |
|     #
 | |
|     #  param [in]  mapFile     .Fv.map file
 | |
|     #
 | |
|     #  retval      0           Parsed FV MAP file successfully
 | |
|     #
 | |
|     def parseFvMapFile(self, mapFile):
 | |
|         #
 | |
|         # Get information from .Fv.map in order to create dictionaries
 | |
|         # For example,
 | |
|         # self.dictModBase[FspSecCore:BASE]  = 4294592776 (0xfffa4908)
 | |
|         # self.dictModBase[FspSecCore:ENTRY] = 4294606552 (0xfffa7ed8)
 | |
|         # self.dictModBase[FspSecCore:TEXT]  = 4294593080 (0xfffa4a38)
 | |
|         # self.dictModBase[FspSecCore:DATA]  = 4294612280 (0xfffa9538)
 | |
|         # self.dictSymbolAddress[FspSecCore:_SecStartup] = 0x00fffa4a38
 | |
|         #
 | |
|         fdIn     = open(mapFile, "r")
 | |
|         rptLine  = fdIn.readline()
 | |
|         modName  = ""
 | |
|         while (rptLine != "" ):
 | |
|             if rptLine[0] != ' ':
 | |
|                 #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958)
 | |
|                 #(GUID=86D70125-BAA3-4296-A62F-602BEBBB9081 .textbaseaddress=0x00fffb4398 .databaseaddress=0x00fffb4178)
 | |
|                 match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+)\)", rptLine)
 | |
|                 if match is not None:
 | |
|                     modName = match.group(1)
 | |
|                     if len(modName) == 36:
 | |
|                        modName = self.dictGuidNameXref[modName.upper()]
 | |
|                     self.dictModBase['%s:BASE'  % modName] = int (match.group(2), 16)
 | |
|                     self.dictModBase['%s:ENTRY' % modName] = int (match.group(3), 16)
 | |
|                 match = re.match("\(GUID=([A-Z0-9\-]+)\s+\.textbaseaddress=(0x[0-9a-fA-F]+)\s+\.databaseaddress=(0x[0-9a-fA-F]+)\)", rptLine)
 | |
|                 if match is not None:
 | |
|                     modName = match.group(1)
 | |
|                     if len(modName) == 36:
 | |
|                        modName = self.dictGuidNameXref[modName.upper()]
 | |
|                        self.dictModBase['%s:TEXT' % modName] = int (match.group(2), 16)
 | |
|                        self.dictModBase['%s:DATA' % modName] = int (match.group(3), 16)
 | |
|             else:
 | |
|                 #   0x00fff8016c    __ModuleEntryPoint
 | |
|                 match = re.match("^\s+(0x[a-z0-9]+)\s+([_a-zA-Z0-9]+)", rptLine)
 | |
|                 if match is not None:
 | |
|                     self.dictSymbolAddress["%s:%s"%(modName, match.group(2))] = match.group(1)
 | |
|             rptLine  = fdIn.readline()
 | |
|         fdIn.close()
 | |
|         return 0
 | |
| 
 | |
|     #
 | |
|     #  Parse MOD MAP file
 | |
|     #
 | |
|     #  param [in]  moduleName  Module name
 | |
|     #  param [in]  mapFile     .Fv.map file
 | |
|     #
 | |
|     #  retval      0           Parsed MOD MAP file successfully
 | |
|     #  retval      1           There is no moduleEntryPoint in modSymbols
 | |
|     #
 | |
|     def parseModMapFile(self, moduleName, mapFile):
 | |
|         #
 | |
|         # Get information from mapFile by moduleName in order to create a dictionary
 | |
|         # For example,
 | |
|         # self.dictSymbolAddress[FspSecCore:___guard_fids_count] = 0x00fffa4778
 | |
|         #
 | |
|         modSymbols  = {}
 | |
|         fdIn        = open(mapFile, "r")
 | |
|         reportLines = fdIn.readlines()
 | |
|         fdIn.close()
 | |
| 
 | |
|         moduleEntryPoint = "__ModuleEntryPoint"
 | |
|         reportLine = reportLines[0]
 | |
|         if reportLine.strip().find("Archive member included") != -1:
 | |
|             #GCC
 | |
|             #                0x0000000000001d55                IoRead8
 | |
|             patchMapFileMatchString = "\s+(0x[0-9a-fA-F]{16})\s+([^\s][^0x][_a-zA-Z0-9\-]+)\s"
 | |
|             matchKeyGroupIndex = 2
 | |
|             matchSymbolGroupIndex  = 1
 | |
|             prefix = '_'
 | |
|         else:
 | |
|             #MSFT
 | |
|             #0003:00000190       _gComBase                  00007a50     SerialPo
 | |
|             patchMapFileMatchString =  "^\s[0-9a-fA-F]{4}:[0-9a-fA-F]{8}\s+(\w+)\s+([0-9a-fA-F]{8}\s+)"
 | |
|             matchKeyGroupIndex = 1
 | |
|             matchSymbolGroupIndex  = 2
 | |
|             prefix = ''
 | |
| 
 | |
|         for reportLine in reportLines:
 | |
|             match = re.match(patchMapFileMatchString, reportLine)
 | |
|             if match is not None:
 | |
|                 modSymbols[prefix + match.group(matchKeyGroupIndex)] = match.group(matchSymbolGroupIndex)
 | |
| 
 | |
|         # Handle extra module patchable PCD variable in Linux map since it might have different format
 | |
|         # .data._gPcd_BinaryPatch_PcdVpdBaseAddress
 | |
|         #        0x0000000000003714        0x4 /tmp/ccmytayk.ltrans1.ltrans.o
 | |
|         handleNext = False
 | |
|         if matchSymbolGroupIndex == 1:
 | |
|             for reportLine in reportLines:
 | |
|                 if handleNext:
 | |
|                     handleNext = False
 | |
|                     pcdName = match.group(1)
 | |
|                     match   = re.match("\s+(0x[0-9a-fA-F]{16})\s+", reportLine)
 | |
|                     if match is not None:
 | |
|                         modSymbols[prefix + pcdName] = match.group(1)
 | |
|                 else:
 | |
|                     match = re.match("^\s\.data\.(_gPcd_BinaryPatch[_a-zA-Z0-9\-]+)", reportLine)
 | |
|                     if match is not None:
 | |
|                         handleNext = True
 | |
|                         continue
 | |
| 
 | |
|         if not moduleEntryPoint in modSymbols:
 | |
|             return 1
 | |
| 
 | |
|         modEntry = '%s:%s' % (moduleName,moduleEntryPoint)
 | |
|         if not modEntry in self.dictSymbolAddress:
 | |
|             modKey = '%s:ENTRY' % moduleName
 | |
|             if modKey in self.dictModBase:
 | |
|                 baseOffset = self.dictModBase['%s:ENTRY' % moduleName] - int(modSymbols[moduleEntryPoint], 16)
 | |
|             else:
 | |
|                return 2
 | |
|         else:
 | |
|             baseOffset = int(self.dictSymbolAddress[modEntry], 16) - int(modSymbols[moduleEntryPoint], 16)
 | |
|         for symbol in modSymbols:
 | |
|             fullSym = "%s:%s" % (moduleName, symbol)
 | |
|             if not fullSym in self.dictSymbolAddress:
 | |
|                 self.dictSymbolAddress[fullSym] = "0x00%08x" % (baseOffset+ int(modSymbols[symbol], 16))
 | |
|         return 0
 | |
| 
 | |
|     #
 | |
|     #  Parse Guid.xref file
 | |
|     #
 | |
|     #  param [in]  xrefFile    the full directory of Guid.xref file
 | |
|     #
 | |
|     #  retval      0           Parsed Guid.xref file successfully
 | |
|     #
 | |
|     def parseGuidXrefFile(self, xrefFile):
 | |
|         #
 | |
|         # Get information from Guid.xref in order to create a GuidNameXref dictionary
 | |
|         # The dictGuidNameXref, for example, will be like
 | |
|         # dictGuidNameXref [1BA0062E-C779-4582-8566-336AE8F78F09] = FspSecCore
 | |
|         #
 | |
|         fdIn     = open(xrefFile, "r")
 | |
|         rptLine  = fdIn.readline()
 | |
|         while (rptLine != "" ):
 | |
|             match = re.match("([0-9a-fA-F\-]+)\s([_a-zA-Z0-9]+)", rptLine)
 | |
|             if match is not None:
 | |
|                 self.dictGuidNameXref[match.group(1).upper()] = match.group(2)
 | |
|             rptLine  = fdIn.readline()
 | |
|         fdIn.close()
 | |
|         return 0
 | |
| 
 | |
|     #
 | |
|     #  Get current character
 | |
|     #
 | |
|     #  retval      elf.string[self.index]
 | |
|     #  retval      ''                       Exception
 | |
|     #
 | |
|     def getCurr(self):
 | |
|         try:
 | |
|             return self.string[self.index]
 | |
|         except Exception:
 | |
|             return ''
 | |
| 
 | |
|     #
 | |
|     #  Check to see if it is last index
 | |
|     #
 | |
|     #  retval      self.index
 | |
|     #
 | |
|     def isLast(self):
 | |
|         return self.index == len(self.string)
 | |
| 
 | |
|     #
 | |
|     #  Move to next index
 | |
|     #
 | |
|     def moveNext(self):
 | |
|         self.index += 1
 | |
| 
 | |
|     #
 | |
|     #  Skip space
 | |
|     #
 | |
|     def skipSpace(self):
 | |
|         while not self.isLast():
 | |
|             if self.getCurr() in ' \t':
 | |
|                 self.moveNext()
 | |
|             else:
 | |
|                 return
 | |
| 
 | |
|     #
 | |
|     #  Parse value
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def parseValue(self):
 | |
|         self.skipSpace()
 | |
|         var = ''
 | |
|         while not self.isLast():
 | |
|             char = self.getCurr()
 | |
|             if char.lower() in '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:-':
 | |
|                 var += char
 | |
|                 self.moveNext()
 | |
|             else:
 | |
|                 break
 | |
| 
 | |
|         if ':' in var:
 | |
|             partList = var.split(':')
 | |
|             if len(partList) != 2:
 | |
|                 raise Exception("Unrecognized expression %s" % var)
 | |
|             modName = partList[0]
 | |
|             modOff  = partList[1]
 | |
|             if ('-' not in  modName) and (modOff[0] in '0123456789'):
 | |
|                 # MOD: OFFSET
 | |
|                 var = self.getModGuid(modName) + ":" + modOff
 | |
|             if '-' in var:  # GUID:OFFSET
 | |
|                 value = self.getGuidOff(var)
 | |
|             else:
 | |
|                 value = self.getSymbols(var)
 | |
|                 self.synUsed   = True
 | |
|         else:
 | |
|             if var[0] in '0123456789':
 | |
|                 value = self.getNumber(var)
 | |
|             else:
 | |
|                 value = self.getVariable(var)
 | |
|         return int(value)
 | |
| 
 | |
|     #
 | |
|     #  Parse single operation
 | |
|     #
 | |
|     #  retval      ~self.parseBrace() or self.parseValue()
 | |
|     #
 | |
|     def parseSingleOp(self):
 | |
|         self.skipSpace()
 | |
|         char = self.getCurr()
 | |
|         if char == '~':
 | |
|             self.moveNext()
 | |
|             return ~self.parseBrace()
 | |
|         else:
 | |
|             return self.parseValue()
 | |
| 
 | |
|     #
 | |
|     #  Parse symbol of Brace([, {, <)
 | |
|     #
 | |
|     #  retval      value or self.parseSingleOp()
 | |
|     #
 | |
|     def parseBrace(self):
 | |
|         self.skipSpace()
 | |
|         char = self.getCurr()
 | |
|         parenthesisType = self.parenthesisOpenSet.find(char)
 | |
|         if parenthesisType >= 0:
 | |
|             self.moveNext()
 | |
|             value = self.parseExpr()
 | |
|             self.skipSpace()
 | |
|             if self.getCurr() != self.parenthesisCloseSet[parenthesisType]:
 | |
|                 raise Exception("No closing brace")
 | |
|             self.moveNext()
 | |
|             if parenthesisType   == 1:  # [ : Get content
 | |
|                 value = self.getContent(value)
 | |
|             elif parenthesisType == 2:  # { : To  address
 | |
|                 value = self.toAddress(value)
 | |
|             elif parenthesisType == 3:  # < : To  offset
 | |
|                 value = self.toOffset(value)
 | |
|             return value
 | |
|         else:
 | |
|             return self.parseSingleOp()
 | |
| 
 | |
|     #
 | |
|     #  Parse symbol of Multiplier(*)
 | |
|     #
 | |
|     #  retval      value or self.parseSingleOp()
 | |
|     #
 | |
|     def parseMul(self):
 | |
|         values = [self.parseBrace()]
 | |
|         while True:
 | |
|             self.skipSpace()
 | |
|             char = self.getCurr()
 | |
|             if char == '*':
 | |
|                 self.moveNext()
 | |
|                 values.append(self.parseBrace())
 | |
|             else:
 | |
|                 break
 | |
|         value  = 1
 | |
|         for each in values:
 | |
|             value *= each
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Parse symbol of And(&) and Or(|)
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def parseAndOr(self):
 | |
|         values = [self.parseMul()]
 | |
|         op     = None
 | |
|         value  = 0xFFFFFFFF
 | |
|         while True:
 | |
|             self.skipSpace()
 | |
|             char = self.getCurr()
 | |
|             if char == '&':
 | |
|                 self.moveNext()
 | |
|                 values.append(self.parseMul())
 | |
|                 op = char
 | |
|             elif char == '|':
 | |
|                 div_index = self.index
 | |
|                 self.moveNext()
 | |
|                 values.append(self.parseMul())
 | |
|                 value = 0
 | |
|                 op = char
 | |
|             else:
 | |
|                 break
 | |
| 
 | |
|         for each in values:
 | |
|             if op == '|':
 | |
|                 value |= each
 | |
|             else:
 | |
|                 value &= each
 | |
| 
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Parse symbol of Add(+) and Minus(-)
 | |
|     #
 | |
|     #  retval      sum(values)
 | |
|     #
 | |
|     def parseAddMinus(self):
 | |
|         values = [self.parseAndOr()]
 | |
|         while True:
 | |
|             self.skipSpace()
 | |
|             char = self.getCurr()
 | |
|             if char == '+':
 | |
|                 self.moveNext()
 | |
|                 values.append(self.parseAndOr())
 | |
|             elif char == '-':
 | |
|                 self.moveNext()
 | |
|                 values.append(-1 * self.parseAndOr())
 | |
|             else:
 | |
|                 break
 | |
|         return sum(values)
 | |
| 
 | |
|     #
 | |
|     #  Parse expression
 | |
|     #
 | |
|     #  retval      self.parseAddMinus()
 | |
|     #
 | |
|     def parseExpr(self):
 | |
|         return self.parseAddMinus()
 | |
| 
 | |
|     #
 | |
|     #  Get result
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def getResult(self):
 | |
|         value = self.parseExpr()
 | |
|         self.skipSpace()
 | |
|         if not self.isLast():
 | |
|             raise Exception("Unexpected character found '%s'" % self.getCurr())
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Get module GUID
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def getModGuid(self, var):
 | |
|         guid = (guid for guid,name in self.dictGuidNameXref.items() if name==var)
 | |
|         try:
 | |
|             value = guid.next()
 | |
|         except Exception:
 | |
|             raise Exception("Unknown module name %s !" % var)
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Get variable
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def getVariable(self, var):
 | |
|         value = self.dictVariable.get(var, None)
 | |
|         if value == None:
 | |
|             raise Exception("Unrecognized variable '%s'" % var)
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Get number
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def getNumber(self, var):
 | |
|         var = var.strip()
 | |
|         if var.startswith('0x'):  # HEX
 | |
|             value = int(var, 16)
 | |
|         else:
 | |
|             value = int(var, 10)
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Get content
 | |
|     #
 | |
|     #  param [in]  value
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def getContent(self, value):
 | |
|         if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):
 | |
|             value = value - self.fdBase
 | |
|         if value >= self.fdSize:
 | |
|             raise Exception("Invalid file offset 0x%08x !" % value)
 | |
|         return readDataFromFile (self.fdFile, value, 4)
 | |
| 
 | |
|     #
 | |
|     #  Change value to address
 | |
|     #
 | |
|     #  param [in]  value
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def toAddress(self, value):
 | |
|         if value < self.fdSize:
 | |
|             value = value + self.fdBase
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Change value to offset
 | |
|     #
 | |
|     #  param [in]  value
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def toOffset(self, value):
 | |
|         if value > self.fdBase:
 | |
|             value = value - self.fdBase
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Get GUID offset
 | |
|     #
 | |
|     #  param [in]  value
 | |
|     #
 | |
|     #  retval      value
 | |
|     #
 | |
|     def getGuidOff(self, value):
 | |
|         # GUID:Offset
 | |
|         symbolName = value.split(':')
 | |
|         if len(symbolName) == 2 and self.dictFfsOffset.has_key(symbolName[0]):
 | |
|             value = (int(self.dictFfsOffset[symbolName[0]], 16) + int(symbolName[1], 16)) & 0xFFFFFFFF
 | |
|         else:
 | |
|             raise Exception("Unknown GUID %s !" % value)
 | |
|         return value
 | |
| 
 | |
|     #
 | |
|     #  Get symbols
 | |
|     #
 | |
|     #  param [in]  value
 | |
|     #
 | |
|     #  retval      ret
 | |
|     #
 | |
|     def getSymbols(self, value):
 | |
|         if self.dictSymbolAddress.has_key(value):
 | |
|             # Module:Function
 | |
|             ret = int (self.dictSymbolAddress[value], 16)
 | |
|         else:
 | |
|             raise Exception("Unknown symbol %s !" % value)
 | |
|         return ret
 | |
| 
 | |
|     #
 | |
|     #  Evaluate symbols
 | |
|     #
 | |
|     #  param [in]  expression
 | |
|     #  param [in]  isOffset
 | |
|     #
 | |
|     #  retval      value & 0xFFFFFFFF
 | |
|     #
 | |
|     def evaluate(self, expression, isOffset):
 | |
|         self.index     = 0
 | |
|         self.synUsed   = False
 | |
|         self.string    = expression
 | |
|         value = self.getResult()
 | |
|         if isOffset:
 | |
|             if self.synUsed:
 | |
|                 # Consider it as an address first
 | |
|                 if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):
 | |
|                     value = value - self.fdBase
 | |
|             if value & 0x80000000:
 | |
|                 # Consider it as a negative offset next
 | |
|                 offset = (~value & 0xFFFFFFFF) + 1
 | |
|                 if offset < self.fdSize:
 | |
|                     value = self.fdSize - offset
 | |
|             if value >= self.fdSize:
 | |
|                 raise Exception("Invalid offset expression !")
 | |
|         return value & 0xFFFFFFFF
 | |
| 
 | |
| #
 | |
| #  Print out the usage
 | |
| #
 | |
| def usage():
 | |
|     print "Usage: \n\tPatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch \"Offset, Value\""
 | |
| 
 | |
| def main():
 | |
|     #
 | |
|     # Parse the options and args
 | |
|     #
 | |
|     symTables = Symbols()
 | |
| 
 | |
|     #
 | |
|     # If the arguments are less than 4, then return an error.
 | |
|     #
 | |
|     if len(sys.argv) < 4:
 | |
|         Usage()
 | |
|         return 1
 | |
| 
 | |
|     #
 | |
|     # If it fails to create dictionaries, then return an error.
 | |
|     #
 | |
|     if symTables.createDicts(sys.argv[1], sys.argv[2]) != 0:
 | |
|         print "ERROR: Failed to create symbol dictionary!!"
 | |
|         return 2
 | |
| 
 | |
|     #
 | |
|     # Get FD file and size
 | |
|     #
 | |
|     fdFile = symTables.getFdFile()
 | |
|     fdSize = symTables.getFdSize()
 | |
| 
 | |
|     try:
 | |
|         #
 | |
|         # Check to see if FSP header is valid
 | |
|         #
 | |
|         ret = IsFspHeaderValid(fdFile)
 | |
|         if ret == False:
 | |
|           raise Exception ("The FSP header is not valid. Stop patching FD.")
 | |
|         comment = ""
 | |
|         for fvFile in  sys.argv[3:]:
 | |
|             #
 | |
|             # Check to see if it has enough arguments
 | |
|             #
 | |
|             items = fvFile.split(",")
 | |
|             if len (items) < 2:
 | |
|                 raise Exception("Expect more arguments for '%s'!" % fvFile)
 | |
| 
 | |
|             comment = ""
 | |
|             command = ""
 | |
|             params  = []
 | |
|             for item in items:
 | |
|                 item = item.strip()
 | |
|                 if item.startswith("@"):
 | |
|                     comment = item[1:]
 | |
|                 elif item.startswith("$"):
 | |
|                     command = item[1:]
 | |
|                 else:
 | |
|                     if len(params) == 0:
 | |
|                         isOffset = True
 | |
|                     else :
 | |
|                         isOffset = False
 | |
|                     #
 | |
|                     # Parse symbols then append it to params
 | |
|                     #
 | |
|                     params.append (symTables.evaluate(item, isOffset))
 | |
| 
 | |
|             #
 | |
|             # Patch a new value into FD file if it is not a command
 | |
|             #
 | |
|             if command == "":
 | |
|                 # Patch a DWORD
 | |
|                 if len (params) == 2:
 | |
|                     offset   = params[0]
 | |
|                     value    = params[1]
 | |
|                     oldvalue = readDataFromFile(fdFile, offset, 4)
 | |
|                     ret = patchDataInFile (fdFile, offset, value, 4) - 4
 | |
|                 else:
 | |
|                     raise Exception ("Patch command needs 2 parameters !")
 | |
| 
 | |
|                 if ret:
 | |
|                     raise Exception ("Patch failed for offset 0x%08X" % offset)
 | |
|                 else:
 | |
|                     print  "Patched offset 0x%08X:[%08X] with value 0x%08X  # %s" % (offset, oldvalue, value, comment)
 | |
| 
 | |
|             elif command == "COPY":
 | |
|                 #
 | |
|                 # Copy binary block from source to destination
 | |
|                 #
 | |
|                 if len (params) == 3:
 | |
|                     src  = symTables.toOffset(params[0])
 | |
|                     dest = symTables.toOffset(params[1])
 | |
|                     clen = symTables.toOffset(params[2])
 | |
|                     if (dest + clen <= fdSize) and (src + clen <= fdSize):
 | |
|                         oldvalue = readDataFromFile(fdFile, src, clen)
 | |
|                         ret = patchDataInFile (fdFile, dest, oldvalue, clen) - clen
 | |
|                     else:
 | |
|                         raise Exception ("Copy command OFFSET or LENGTH parameter is invalid !")
 | |
|                 else:
 | |
|                     raise Exception ("Copy command needs 3 parameters !")
 | |
| 
 | |
|                 if ret:
 | |
|                     raise Exception ("Copy failed from offset 0x%08X to offset 0x%08X!" % (src, dest))
 | |
|                 else :
 | |
|                     print  "Copied %d bytes from offset 0x%08X ~ offset 0x%08X  # %s" % (clen, src, dest, comment)
 | |
|             else:
 | |
|                 raise Exception ("Unknown command %s!" % command)
 | |
|         return 0
 | |
| 
 | |
|     except Exception as (ex):
 | |
|         print "ERROR: %s" % ex
 | |
|         return 1
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     sys.exit(main())
 |