]> git.proxmox.com Git - mirror_edk2.git/blobdiff - IntelFspPkg/Tools/PatchFv.py
Add IntelFspPkg to support create FSP bin based on EDKII.
[mirror_edk2.git] / IntelFspPkg / Tools / PatchFv.py
diff --git a/IntelFspPkg/Tools/PatchFv.py b/IntelFspPkg/Tools/PatchFv.py
new file mode 100644 (file)
index 0000000..cc22cc2
--- /dev/null
@@ -0,0 +1,567 @@
+## @ 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