--- /dev/null
+## @ PatchFv.py\r
+#\r
+# Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>\r
+# This program and the accompanying materials are licensed and made available under\r
+# the terms and conditions of the BSD License that accompanies this distribution.\r
+# The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+import os\r
+import re\r
+import sys\r
+\r
+def readDataFromFile (binfile, offset, len=1):\r
+ fd = open(binfile, "r+b")\r
+ fsize = os.path.getsize(binfile)\r
+ offval = offset & 0xFFFFFFFF\r
+ if (offval & 0x80000000):\r
+ offval = fsize - (0xFFFFFFFF - offval + 1)\r
+ fd.seek(offval)\r
+ bytearray = [ord(b) for b in fd.read(len)]\r
+ value = 0;\r
+ idx = len - 1;\r
+ while idx >= 0:\r
+ value = value << 8 | bytearray[idx]\r
+ idx = idx - 1\r
+ fd.close()\r
+ return value\r
+\r
+def patchDataInFile (binfile, offset, value, len=1):\r
+ fd = open(binfile, "r+b")\r
+ fsize = os.path.getsize(binfile)\r
+ offval = offset & 0xFFFFFFFF\r
+ if (offval & 0x80000000):\r
+ offval = fsize - (0xFFFFFFFF - offval + 1)\r
+ bytearray = []\r
+ idx = 0;\r
+ while idx < len:\r
+ bytearray.append(value & 0xFF)\r
+ value = value >> 8\r
+ idx = idx + 1\r
+ fd.seek(offval)\r
+ fd.write("".join(chr(b) for b in bytearray))\r
+ fd.close()\r
+ return len;\r
+\r
+\r
+class Symbols:\r
+ def __init__(self):\r
+ self.dictSymbolAddress = {}\r
+ self.dictGuidNameXref = {}\r
+ self.dictFfsOffset = {}\r
+ self.dictVariable = {}\r
+ self.dictModBase = {}\r
+ self.fdFile = None\r
+ self.string = ""\r
+ self.fdBase = 0xFFFFFFFF\r
+ self.fdSize = 0\r
+ self.index = 0\r
+ self.parenthesisOpenSet = '([{<'\r
+ self.parenthesisCloseSet = ')]}>'\r
+\r
+ def getFdFile (self):\r
+ return self.fdFile\r
+\r
+ def getFdSize (self):\r
+ return self.fdSize\r
+\r
+ def createDicts (self, fvDir, fvNames):\r
+ if not os.path.isdir(fvDir):\r
+ raise Exception ("'%s' is not a valid directory!" % FvDir)\r
+\r
+ xrefFile = os.path.join(fvDir, "Guid.xref")\r
+ if not os.path.exists(xrefFile):\r
+ raise Exception("Cannot open GUID Xref file '%s'!" % xrefFile)\r
+\r
+ self.dictGuidNameXref = {}\r
+ self.parseGuidXrefFile(xrefFile)\r
+\r
+ fvList = fvNames.split(":")\r
+ fdBase = fvList.pop()\r
+ if len(fvList) == 0:\r
+ fvList.append(fdBase)\r
+\r
+ fdFile = os.path.join(fvDir, fdBase.strip() + ".fd")\r
+ if not os.path.exists(fdFile):\r
+ raise Exception("Cannot open FD file '%s'!" % fdFile)\r
+\r
+ self.fdFile = fdFile\r
+ self.fdSize = os.path.getsize(fdFile)\r
+\r
+ infFile = os.path.join(fvDir, fvList[0].strip()) + ".inf"\r
+ if not os.path.exists(infFile):\r
+ raise Exception("Cannot open INF file '%s'!" % infFile)\r
+\r
+ self.parseInfFile(infFile)\r
+\r
+ self.dictVariable = {}\r
+ self.dictVariable["FDSIZE"] = self.fdSize\r
+ self.dictVariable["FDBASE"] = self.fdBase\r
+\r
+ self.dictSymbolAddress = {}\r
+ self.dictFfsOffset = {}\r
+ for file in fvList:\r
+\r
+ fvFile = os.path.join(fvDir, file.strip()) + ".Fv"\r
+ mapFile = fvFile + ".map"\r
+ if not os.path.exists(mapFile):\r
+ raise Exception("Cannot open MAP file '%s'!" % mapFile)\r
+\r
+ self.parseFvMapFile(mapFile)\r
+\r
+ fvTxtFile = fvFile + ".txt"\r
+ if not os.path.exists(fvTxtFile):\r
+ raise Exception("Cannot open FV TXT file '%s'!" % fvTxtFile)\r
+\r
+ self.parseFvTxtFile(fvTxtFile)\r
+\r
+ ffsDir = os.path.join(fvDir, "Ffs")\r
+ if (os.path.isdir(ffsDir)):\r
+ for item in os.listdir(ffsDir):\r
+ if len(item) <= 0x24:\r
+ continue\r
+ mapFile =os.path.join(ffsDir, item, "%s.map" % item[0:0x24])\r
+ if not os.path.exists(mapFile):\r
+ continue\r
+ self.parseModMapFile(item[0x24:], mapFile)\r
+\r
+ return 0\r
+\r
+ def getFvOffsetInFd(self, fvFile):\r
+ fvHandle = open(fvFile, "r+b")\r
+ fdHandle = open(self.fdFile, "r+b")\r
+ offset = fdHandle.read().find(fvHandle.read(0x70))\r
+ fvHandle.close()\r
+ fdHandle.close()\r
+ if offset == -1:\r
+ raise Exception("Could not locate FV file %s in FD!" % fvFile)\r
+ return offset\r
+\r
+ def parseInfFile(self, infFile):\r
+ fvOffset = self.getFvOffsetInFd(infFile[0:-4] + ".Fv")\r
+ fdIn = open(infFile, "r")\r
+ rptLine = fdIn.readline()\r
+ self.fdBase = 0xFFFFFFFF\r
+ while (rptLine != "" ):\r
+ #EFI_BASE_ADDRESS = 0xFFFDF400\r
+ match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine)\r
+ if match is not None:\r
+ self.fdBase = int(match.group(1), 16) - fvOffset\r
+ rptLine = fdIn.readline()\r
+ fdIn.close()\r
+ if self.fdBase == 0xFFFFFFFF:\r
+ raise Exception("Could not find EFI_BASE_ADDRESS in INF file!" % fvFile)\r
+ return 0\r
+\r
+ def parseFvTxtFile(self, fvTxtFile):\r
+ fvOffset = self.getFvOffsetInFd(fvTxtFile[0:-4])\r
+ fdIn = open(fvTxtFile, "r")\r
+ rptLine = fdIn.readline()\r
+ while (rptLine != "" ):\r
+ match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", rptLine)\r
+ if match is not None:\r
+ self.dictFfsOffset[match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset)\r
+ rptLine = fdIn.readline()\r
+ fdIn.close()\r
+ return 0\r
+\r
+ def parseFvMapFile(self, mapFile):\r
+ fdIn = open(mapFile, "r")\r
+ rptLine = fdIn.readline()\r
+ modName = ""\r
+ while (rptLine != "" ):\r
+ if rptLine[0] != ' ':\r
+ #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958)\r
+ #(GUID=86D70125-BAA3-4296-A62F-602BEBBB9081 .textbaseaddress=0x00fffb4398 .databaseaddress=0x00fffb4178)\r
+ match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+)\)", rptLine)\r
+ if match is not None:\r
+ modName = match.group(1)\r
+ if len(modName) == 36:\r
+ modName = self.dictGuidNameXref[modName.upper()]\r
+ self.dictModBase['%s:BASE' % modName] = int (match.group(2), 16)\r
+ self.dictModBase['%s:ENTRY' % modName] = int (match.group(3), 16)\r
+ match = re.match("\(GUID=([A-Z0-9\-]+)\s+\.textbaseaddress=(0x[0-9a-fA-F]+)\s+\.databaseaddress=(0x[0-9a-fA-F]+)\)", rptLine)\r
+ if match is not None:\r
+ modName = match.group(1)\r
+ if len(modName) == 36:\r
+ modName = self.dictGuidNameXref[modName.upper()]\r
+ self.dictModBase['%s:TEXT' % modName] = int (match.group(2), 16)\r
+ self.dictModBase['%s:DATA' % modName] = int (match.group(3), 16)\r
+ else:\r
+ # 0x00fff8016c __ModuleEntryPoint\r
+ match = re.match("^\s+(0x[a-z0-9]+)\s+([_a-zA-Z0-9]+)", rptLine)\r
+ if match is not None:\r
+ self.dictSymbolAddress["%s:%s"%(modName, match.group(2))] = match.group(1)\r
+ rptLine = fdIn.readline()\r
+ fdIn.close()\r
+ return 0\r
+\r
+ def parseModMapFile(self, moduleName, mapFile):\r
+ modSymbols = {}\r
+ fdIn = open(mapFile, "r")\r
+ reportLine = fdIn.readline()\r
+ if reportLine.strip().find("Archive member included because of file (symbol)") != -1:\r
+ #GCC\r
+ # 0x0000000000001d55 IoRead8\r
+ patchMapFileMatchString = "\s+(0x[0-9a-fA-F]{16})\s+([^\s][^0x][_a-zA-Z0-9\-]+)\s"\r
+ matchKeyGroupIndex = 2\r
+ matchSymbolGroupIndex = 1\r
+ moduleEntryPoint = "_ModuleEntryPoint"\r
+ else:\r
+ #MSFT\r
+ #0003:00000190 _gComBase 00007a50 SerialPo\r
+ patchMapFileMatchString = "^\s[0-9a-fA-F]{4}:[0-9a-fA-F]{8}\s+(\w+)\s+([0-9a-fA-F]{8}\s+)"\r
+ matchKeyGroupIndex = 1\r
+ matchSymbolGroupIndex = 2\r
+ moduleEntryPoint = "__ModuleEntryPoint"\r
+ while (reportLine != "" ):\r
+ match = re.match(patchMapFileMatchString, reportLine)\r
+ if match is not None:\r
+ modSymbols[match.group(matchKeyGroupIndex)] = match.group(matchSymbolGroupIndex)\r
+ reportLine = fdIn.readline()\r
+ fdIn.close()\r
+\r
+ if not moduleEntryPoint in modSymbols:\r
+ return 1\r
+\r
+ modEntry = '%s:%s' % (moduleName,moduleEntryPoint)\r
+ if not modEntry in self.dictSymbolAddress:\r
+ modKey = '%s:ENTRY' % moduleName\r
+ if modKey in self.dictModBase:\r
+ baseOffset = self.dictModBase['%s:ENTRY' % moduleName] - int(modSymbols[moduleEntryPoint], 16)\r
+ else:\r
+ return 2\r
+ else:\r
+ baseOffset = int(self.dictSymbolAddress[modEntry], 16) - int(modSymbols[moduleEntryPoint], 16)\r
+ for symbol in modSymbols:\r
+ fullSym = "%s:%s" % (moduleName, symbol)\r
+ if not fullSym in self.dictSymbolAddress:\r
+ self.dictSymbolAddress[fullSym] = "0x00%08x" % (baseOffset+ int(modSymbols[symbol], 16))\r
+ return 0\r
+\r
+ def parseGuidXrefFile(self, xrefFile):\r
+ fdIn = open(xrefFile, "r")\r
+ rptLine = fdIn.readline()\r
+ while (rptLine != "" ):\r
+ match = re.match("([0-9a-fA-F\-]+)\s([_a-zA-Z0-9]+)", rptLine)\r
+ if match is not None:\r
+ self.dictGuidNameXref[match.group(1).upper()] = match.group(2)\r
+ rptLine = fdIn.readline()\r
+ fdIn.close()\r
+ return 0\r
+\r
+ def getCurr(self):\r
+ try:\r
+ return self.string[self.index]\r
+ except Exception:\r
+ return ''\r
+\r
+ def isLast(self):\r
+ return self.index == len(self.string)\r
+\r
+ def moveNext(self):\r
+ self.index += 1\r
+\r
+ def skipSpace(self):\r
+ while not self.isLast():\r
+ if self.getCurr() in ' \t':\r
+ self.moveNext()\r
+ else:\r
+ return\r
+\r
+ def parseValue(self):\r
+ self.skipSpace()\r
+ var = ''\r
+ while not self.isLast():\r
+ char = self.getCurr()\r
+ if char.lower() in '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:-':\r
+ var += char\r
+ self.moveNext()\r
+ else:\r
+ break\r
+\r
+ if ':' in var:\r
+ partList = var.split(':')\r
+ if len(partList) != 2:\r
+ raise Exception("Unrecognized expression %s" % var)\r
+ modName = partList[0]\r
+ modOff = partList[1]\r
+ if ('-' not in modName) and (modOff[0] in '0123456789'):\r
+ # MOD: OFFSET\r
+ var = self.getModGuid(modName) + ":" + modOff\r
+ if '-' in var: # GUID:OFFSET\r
+ value = self.getGuidOff(var)\r
+ else:\r
+ value = self.getSymbols(var)\r
+ self.synUsed = True\r
+ else:\r
+ if var[0] in '0123456789':\r
+ value = self.getNumber(var)\r
+ else:\r
+ value = self.getVariable(var)\r
+ return int(value)\r
+\r
+ def parseSingleOp(self):\r
+ self.skipSpace()\r
+ char = self.getCurr()\r
+ if char == '~':\r
+ self.moveNext()\r
+ return ~self.parseBrace()\r
+ else:\r
+ return self.parseValue()\r
+\r
+ def parseBrace(self):\r
+ self.skipSpace()\r
+ char = self.getCurr()\r
+ parenthesisType = self.parenthesisOpenSet.find(char)\r
+ if parenthesisType >= 0:\r
+ self.moveNext()\r
+ value = self.parseExpr()\r
+ self.skipSpace()\r
+ if self.getCurr() != self.parenthesisCloseSet[parenthesisType]:\r
+ raise Exception("No closing brace")\r
+ self.moveNext()\r
+ if parenthesisType == 1: # [ : Get content\r
+ value = self.getContent(value)\r
+ elif parenthesisType == 2: # { : To address\r
+ value = self.toAddress(value)\r
+ elif parenthesisType == 3: # < : To offset\r
+ value = self.toOffset(value)\r
+ return value\r
+ else:\r
+ return self.parseSingleOp()\r
+\r
+ def parseMul(self):\r
+ values = [self.parseBrace()]\r
+ while True:\r
+ self.skipSpace()\r
+ char = self.getCurr()\r
+ if char == '*':\r
+ self.moveNext()\r
+ values.append(self.parseBrace())\r
+ else:\r
+ break\r
+ value = 1;\r
+ for each in values:\r
+ value *= each\r
+ return value\r
+\r
+ def parseAndOr(self):\r
+ values = [self.parseMul()]\r
+ op = None\r
+ value = 0xFFFFFFFF;\r
+ while True:\r
+ self.skipSpace()\r
+ char = self.getCurr()\r
+ if char == '&':\r
+ self.moveNext()\r
+ values.append(self.parseMul())\r
+ op = char\r
+ elif char == '|':\r
+ div_index = self.index\r
+ self.moveNext()\r
+ values.append(self.parseMul())\r
+ value = 0\r
+ op = char\r
+ else:\r
+ break\r
+\r
+ for each in values:\r
+ if op == '|':\r
+ value |= each\r
+ else:\r
+ value &= each\r
+\r
+ return value\r
+\r
+ def parseAddMinus(self):\r
+ values = [self.parseAndOr()]\r
+ while True:\r
+ self.skipSpace()\r
+ char = self.getCurr()\r
+ if char == '+':\r
+ self.moveNext()\r
+ values.append(self.parseAndOr())\r
+ elif char == '-':\r
+ self.moveNext()\r
+ values.append(-1 * self.parseAndOr())\r
+ else:\r
+ break\r
+ return sum(values)\r
+\r
+ def parseExpr(self):\r
+ return self.parseAddMinus()\r
+\r
+ def getResult(self):\r
+ value = self.parseExpr()\r
+ self.skipSpace()\r
+ if not self.isLast():\r
+ raise Exception("Unexpected character found '%s'" % self.getCurr())\r
+ return value\r
+\r
+ def getModGuid(self, var):\r
+ guid = (guid for guid,name in self.dictGuidNameXref.items() if name==var)\r
+ try:\r
+ value = guid.next()\r
+ except Exception:\r
+ raise Exception("Unknown module name %s !" % var)\r
+ return value\r
+\r
+ def getVariable(self, var):\r
+ value = self.dictVariable.get(var, None)\r
+ if value == None:\r
+ raise Exception("Unrecognized variable '%s'" % var)\r
+ return value\r
+\r
+ def getNumber(self, var):\r
+ var = var.strip()\r
+ if var.startswith('0x'): # HEX\r
+ value = int(var, 16)\r
+ else:\r
+ value = int(var, 10)\r
+ return value\r
+\r
+ def getContent(self, value):\r
+ if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):\r
+ value = value - self.fdBase\r
+ if value >= self.fdSize:\r
+ raise Exception("Invalid file offset 0x%08x !" % value)\r
+ return readDataFromFile (self.fdFile, value, 4)\r
+\r
+ def toAddress(self, value):\r
+ if value < self.fdSize:\r
+ value = value + self.fdBase\r
+ return value\r
+\r
+ def toOffset(self, value):\r
+ if value > self.fdBase:\r
+ value = value - self.fdBase\r
+ return value\r
+\r
+ def getGuidOff(self, value):\r
+ # GUID:Offset\r
+ symbolName = value.split(':')\r
+ if len(symbolName) == 2 and self.dictFfsOffset.has_key(symbolName[0]):\r
+ value = (int(self.dictFfsOffset[symbolName[0]], 16) + int(symbolName[1], 16)) & 0xFFFFFFFF\r
+ else:\r
+ raise Exception("Unknown GUID %s !" % value)\r
+ return value\r
+\r
+ def getSymbols(self, value):\r
+ if self.dictSymbolAddress.has_key(value):\r
+ # Module:Function\r
+ ret = int (self.dictSymbolAddress[value], 16)\r
+ else:\r
+ raise Exception("Unknown symbol %s !" % value)\r
+ return ret\r
+\r
+ def evaluate(self, expression, isOffset):\r
+ self.index = 0\r
+ self.synUsed = False\r
+ self.string = expression\r
+ value = self.getResult()\r
+ if isOffset:\r
+ if self.synUsed:\r
+ # Consider it as an address first\r
+ if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):\r
+ value = value - self.fdBase\r
+ if value & 0x80000000:\r
+ # Consider it as a negative offset next\r
+ offset = (~value & 0xFFFFFFFF) + 1\r
+ if offset < self.fdSize:\r
+ value = self.fdSize - offset\r
+ if value >= self.fdSize:\r
+ raise Exception("Invalid offset expression !")\r
+ return value & 0xFFFFFFFF\r
+\r
+def usage():\r
+ print "Usage: \n\tPatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch \"Offset, Value\""\r
+\r
+def main():\r
+ #\r
+ # Parse the options and args\r
+ #\r
+ symTables = Symbols()\r
+\r
+ if len(sys.argv) < 4:\r
+ Usage()\r
+ return 1\r
+\r
+ if symTables.createDicts(sys.argv[1], sys.argv[2]) != 0:\r
+ print "ERROR: Failed to create symbol dictionary!!"\r
+ return 2\r
+\r
+ fdFile = symTables.getFdFile()\r
+ fdSize = symTables.getFdSize()\r
+\r
+ try:\r
+ comment = ""\r
+ for fvFile in sys.argv[3:]:\r
+ items = fvFile.split(",")\r
+ if len (items) < 2:\r
+ raise Exception("Expect more arguments for '%s'!" % fvFile)\r
+\r
+ comment = ""\r
+ command = ""\r
+ params = []\r
+ for item in items:\r
+ item = item.strip()\r
+ if item.startswith("@"):\r
+ comment = item[1:]\r
+ elif item.startswith("$"):\r
+ command = item[1:]\r
+ else:\r
+ if len(params) == 0:\r
+ isOffset = True\r
+ else :\r
+ isOffset = False\r
+ params.append (symTables.evaluate(item, isOffset))\r
+\r
+ if command == "":\r
+ # Patch a DWORD\r
+ if len (params) == 2:\r
+ offset = params[0]\r
+ value = params[1]\r
+ oldvalue = readDataFromFile(fdFile, offset, 4)\r
+ ret = patchDataInFile (fdFile, offset, value, 4) - 4\r
+ else:\r
+ raise Exception ("Patch command needs 2 parameters !")\r
+\r
+ if ret:\r
+ raise Exception ("Patch failed for offset 0x%08X" % offset)\r
+ else:\r
+ print "Patched offset 0x%08X:[%08X] with value 0x%08X # %s" % (offset, oldvalue, value, comment)\r
+\r
+ elif command == "COPY":\r
+ # Copy binary block from source to destination\r
+ if len (params) == 3:\r
+ src = symTables.toOffset(params[0])\r
+ dest = symTables.toOffset(params[1])\r
+ clen = symTables.toOffset(params[2])\r
+ if (dest + clen <= fdSize) and (src + clen <= fdSize):\r
+ oldvalue = readDataFromFile(fdFile, src, clen)\r
+ ret = patchDataInFile (fdFile, dest, oldvalue, clen) - clen\r
+ else:\r
+ raise Exception ("Copy command OFFSET or LENGTH parameter is invalid !")\r
+ else:\r
+ raise Exception ("Copy command needs 3 parameters !")\r
+\r
+ if ret:\r
+ raise Exception ("Copy failed from offset 0x%08X to offset 0x%08X!" % (src, dest))\r
+ else :\r
+ print "Copied %d bytes from offset 0x%08X ~ offset 0x%08X # %s" % (clen, src, dest, comment)\r
+ else:\r
+ raise Exception ("Unknown command %s!" % command)\r
+ return 0\r
+\r
+ except Exception as (ex):\r
+ print "ERROR: %s" % ex\r
+ return 1\r
+\r
+if __name__ == '__main__':\r
+ sys.exit(main())\r