Home | History | Annotate | Download | only in Tools
      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