Bug fix in PatchFv.py for GCC build in IntelFspPkg.
[mirror_edk2.git] / IntelFspPkg / Tools / PatchFv.py
CommitLineData
c8ec22a2
JY
1## @ PatchFv.py\r
2#\r
9da59186 3# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>\r
c8ec22a2
JY
4# This program and the accompanying materials are licensed and made available under\r
5# the terms and conditions of the BSD License that accompanies this distribution.\r
6# The full text of the license may be found at\r
7# http://opensource.org/licenses/bsd-license.php.\r
8#\r
9# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11#\r
12##\r
13\r
14import os\r
15import re\r
16import sys\r
17\r
18def readDataFromFile (binfile, offset, len=1):\r
19 fd = open(binfile, "r+b")\r
20 fsize = os.path.getsize(binfile)\r
21 offval = offset & 0xFFFFFFFF\r
22 if (offval & 0x80000000):\r
23 offval = fsize - (0xFFFFFFFF - offval + 1)\r
24 fd.seek(offval)\r
25 bytearray = [ord(b) for b in fd.read(len)]\r
9da59186
JY
26 value = 0\r
27 idx = len - 1\r
c8ec22a2
JY
28 while idx >= 0:\r
29 value = value << 8 | bytearray[idx]\r
30 idx = idx - 1\r
31 fd.close()\r
32 return value\r
33\r
9da59186
JY
34def IsFspHeaderValid (binfile):\r
35 fd = open (binfile, "rb")\r
36 bindat = fd.read(0x200)\r
37 fd.close()\r
38 HeaderList = ['FSPH' , 'FSPP' , 'FSPE']\r
39 OffsetList = []\r
40 for each in HeaderList:\r
41 if each in bindat:\r
42 idx = bindat.index(each)\r
43 else:\r
44 idx = 0\r
45 OffsetList.append(idx)\r
46 if not OffsetList[0] or not OffsetList[1]:\r
47 return False\r
48 Revision = ord(bindat[OffsetList[0] + 0x0B])\r
49 if Revision > 1 and not OffsetList[2]:\r
50 return False\r
51 return True\r
52\r
c8ec22a2
JY
53def patchDataInFile (binfile, offset, value, len=1):\r
54 fd = open(binfile, "r+b")\r
55 fsize = os.path.getsize(binfile)\r
56 offval = offset & 0xFFFFFFFF\r
57 if (offval & 0x80000000):\r
58 offval = fsize - (0xFFFFFFFF - offval + 1)\r
59 bytearray = []\r
9da59186 60 idx = 0\r
c8ec22a2
JY
61 while idx < len:\r
62 bytearray.append(value & 0xFF)\r
63 value = value >> 8\r
64 idx = idx + 1\r
65 fd.seek(offval)\r
66 fd.write("".join(chr(b) for b in bytearray))\r
67 fd.close()\r
9da59186 68 return len\r
c8ec22a2
JY
69\r
70\r
71class Symbols:\r
72 def __init__(self):\r
73 self.dictSymbolAddress = {}\r
74 self.dictGuidNameXref = {}\r
75 self.dictFfsOffset = {}\r
76 self.dictVariable = {}\r
77 self.dictModBase = {}\r
78 self.fdFile = None\r
79 self.string = ""\r
80 self.fdBase = 0xFFFFFFFF\r
81 self.fdSize = 0\r
82 self.index = 0\r
83 self.parenthesisOpenSet = '([{<'\r
84 self.parenthesisCloseSet = ')]}>'\r
85\r
86 def getFdFile (self):\r
87 return self.fdFile\r
88\r
89 def getFdSize (self):\r
90 return self.fdSize\r
91\r
92 def createDicts (self, fvDir, fvNames):\r
93 if not os.path.isdir(fvDir):\r
94 raise Exception ("'%s' is not a valid directory!" % FvDir)\r
95\r
96 xrefFile = os.path.join(fvDir, "Guid.xref")\r
97 if not os.path.exists(xrefFile):\r
98 raise Exception("Cannot open GUID Xref file '%s'!" % xrefFile)\r
99\r
100 self.dictGuidNameXref = {}\r
101 self.parseGuidXrefFile(xrefFile)\r
102\r
103 fvList = fvNames.split(":")\r
104 fdBase = fvList.pop()\r
105 if len(fvList) == 0:\r
106 fvList.append(fdBase)\r
107\r
108 fdFile = os.path.join(fvDir, fdBase.strip() + ".fd")\r
109 if not os.path.exists(fdFile):\r
110 raise Exception("Cannot open FD file '%s'!" % fdFile)\r
111\r
112 self.fdFile = fdFile\r
113 self.fdSize = os.path.getsize(fdFile)\r
114\r
115 infFile = os.path.join(fvDir, fvList[0].strip()) + ".inf"\r
116 if not os.path.exists(infFile):\r
117 raise Exception("Cannot open INF file '%s'!" % infFile)\r
118\r
119 self.parseInfFile(infFile)\r
120\r
121 self.dictVariable = {}\r
122 self.dictVariable["FDSIZE"] = self.fdSize\r
123 self.dictVariable["FDBASE"] = self.fdBase\r
124\r
125 self.dictSymbolAddress = {}\r
126 self.dictFfsOffset = {}\r
127 for file in fvList:\r
128\r
129 fvFile = os.path.join(fvDir, file.strip()) + ".Fv"\r
130 mapFile = fvFile + ".map"\r
131 if not os.path.exists(mapFile):\r
132 raise Exception("Cannot open MAP file '%s'!" % mapFile)\r
133\r
134 self.parseFvMapFile(mapFile)\r
135\r
136 fvTxtFile = fvFile + ".txt"\r
137 if not os.path.exists(fvTxtFile):\r
138 raise Exception("Cannot open FV TXT file '%s'!" % fvTxtFile)\r
139\r
140 self.parseFvTxtFile(fvTxtFile)\r
141\r
142 ffsDir = os.path.join(fvDir, "Ffs")\r
143 if (os.path.isdir(ffsDir)):\r
144 for item in os.listdir(ffsDir):\r
145 if len(item) <= 0x24:\r
146 continue\r
147 mapFile =os.path.join(ffsDir, item, "%s.map" % item[0:0x24])\r
148 if not os.path.exists(mapFile):\r
149 continue\r
150 self.parseModMapFile(item[0x24:], mapFile)\r
151\r
152 return 0\r
153\r
154 def getFvOffsetInFd(self, fvFile):\r
155 fvHandle = open(fvFile, "r+b")\r
156 fdHandle = open(self.fdFile, "r+b")\r
157 offset = fdHandle.read().find(fvHandle.read(0x70))\r
158 fvHandle.close()\r
159 fdHandle.close()\r
160 if offset == -1:\r
161 raise Exception("Could not locate FV file %s in FD!" % fvFile)\r
162 return offset\r
163\r
164 def parseInfFile(self, infFile):\r
165 fvOffset = self.getFvOffsetInFd(infFile[0:-4] + ".Fv")\r
166 fdIn = open(infFile, "r")\r
167 rptLine = fdIn.readline()\r
168 self.fdBase = 0xFFFFFFFF\r
169 while (rptLine != "" ):\r
170 #EFI_BASE_ADDRESS = 0xFFFDF400\r
171 match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine)\r
172 if match is not None:\r
173 self.fdBase = int(match.group(1), 16) - fvOffset\r
174 rptLine = fdIn.readline()\r
175 fdIn.close()\r
176 if self.fdBase == 0xFFFFFFFF:\r
177 raise Exception("Could not find EFI_BASE_ADDRESS in INF file!" % fvFile)\r
178 return 0\r
179\r
180 def parseFvTxtFile(self, fvTxtFile):\r
181 fvOffset = self.getFvOffsetInFd(fvTxtFile[0:-4])\r
182 fdIn = open(fvTxtFile, "r")\r
183 rptLine = fdIn.readline()\r
184 while (rptLine != "" ):\r
185 match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", rptLine)\r
186 if match is not None:\r
187 self.dictFfsOffset[match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset)\r
188 rptLine = fdIn.readline()\r
189 fdIn.close()\r
190 return 0\r
191\r
192 def parseFvMapFile(self, mapFile):\r
193 fdIn = open(mapFile, "r")\r
194 rptLine = fdIn.readline()\r
195 modName = ""\r
196 while (rptLine != "" ):\r
197 if rptLine[0] != ' ':\r
198 #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958)\r
199 #(GUID=86D70125-BAA3-4296-A62F-602BEBBB9081 .textbaseaddress=0x00fffb4398 .databaseaddress=0x00fffb4178)\r
200 match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+)\)", rptLine)\r
201 if match is not None:\r
202 modName = match.group(1)\r
203 if len(modName) == 36:\r
204 modName = self.dictGuidNameXref[modName.upper()]\r
205 self.dictModBase['%s:BASE' % modName] = int (match.group(2), 16)\r
206 self.dictModBase['%s:ENTRY' % modName] = int (match.group(3), 16)\r
207 match = re.match("\(GUID=([A-Z0-9\-]+)\s+\.textbaseaddress=(0x[0-9a-fA-F]+)\s+\.databaseaddress=(0x[0-9a-fA-F]+)\)", rptLine)\r
208 if match is not None:\r
209 modName = match.group(1)\r
210 if len(modName) == 36:\r
211 modName = self.dictGuidNameXref[modName.upper()]\r
212 self.dictModBase['%s:TEXT' % modName] = int (match.group(2), 16)\r
213 self.dictModBase['%s:DATA' % modName] = int (match.group(3), 16)\r
214 else:\r
215 # 0x00fff8016c __ModuleEntryPoint\r
216 match = re.match("^\s+(0x[a-z0-9]+)\s+([_a-zA-Z0-9]+)", rptLine)\r
217 if match is not None:\r
218 self.dictSymbolAddress["%s:%s"%(modName, match.group(2))] = match.group(1)\r
219 rptLine = fdIn.readline()\r
220 fdIn.close()\r
221 return 0\r
222\r
223 def parseModMapFile(self, moduleName, mapFile):\r
224 modSymbols = {}\r
225 fdIn = open(mapFile, "r")\r
226 reportLine = fdIn.readline()\r
71a6022f 227 if reportLine.strip().find("Archive member included") != -1:\r
c8ec22a2
JY
228 #GCC\r
229 # 0x0000000000001d55 IoRead8\r
230 patchMapFileMatchString = "\s+(0x[0-9a-fA-F]{16})\s+([^\s][^0x][_a-zA-Z0-9\-]+)\s"\r
231 matchKeyGroupIndex = 2\r
232 matchSymbolGroupIndex = 1\r
233 moduleEntryPoint = "_ModuleEntryPoint"\r
234 else:\r
235 #MSFT\r
236 #0003:00000190 _gComBase 00007a50 SerialPo\r
237 patchMapFileMatchString = "^\s[0-9a-fA-F]{4}:[0-9a-fA-F]{8}\s+(\w+)\s+([0-9a-fA-F]{8}\s+)"\r
238 matchKeyGroupIndex = 1\r
239 matchSymbolGroupIndex = 2\r
240 moduleEntryPoint = "__ModuleEntryPoint"\r
241 while (reportLine != "" ):\r
242 match = re.match(patchMapFileMatchString, reportLine)\r
243 if match is not None:\r
244 modSymbols[match.group(matchKeyGroupIndex)] = match.group(matchSymbolGroupIndex)\r
245 reportLine = fdIn.readline()\r
246 fdIn.close()\r
247\r
248 if not moduleEntryPoint in modSymbols:\r
249 return 1\r
250\r
251 modEntry = '%s:%s' % (moduleName,moduleEntryPoint)\r
252 if not modEntry in self.dictSymbolAddress:\r
253 modKey = '%s:ENTRY' % moduleName\r
254 if modKey in self.dictModBase:\r
255 baseOffset = self.dictModBase['%s:ENTRY' % moduleName] - int(modSymbols[moduleEntryPoint], 16)\r
256 else:\r
257 return 2\r
258 else:\r
259 baseOffset = int(self.dictSymbolAddress[modEntry], 16) - int(modSymbols[moduleEntryPoint], 16)\r
260 for symbol in modSymbols:\r
261 fullSym = "%s:%s" % (moduleName, symbol)\r
262 if not fullSym in self.dictSymbolAddress:\r
263 self.dictSymbolAddress[fullSym] = "0x00%08x" % (baseOffset+ int(modSymbols[symbol], 16))\r
264 return 0\r
265\r
266 def parseGuidXrefFile(self, xrefFile):\r
267 fdIn = open(xrefFile, "r")\r
268 rptLine = fdIn.readline()\r
269 while (rptLine != "" ):\r
270 match = re.match("([0-9a-fA-F\-]+)\s([_a-zA-Z0-9]+)", rptLine)\r
271 if match is not None:\r
272 self.dictGuidNameXref[match.group(1).upper()] = match.group(2)\r
273 rptLine = fdIn.readline()\r
274 fdIn.close()\r
275 return 0\r
276\r
277 def getCurr(self):\r
278 try:\r
279 return self.string[self.index]\r
280 except Exception:\r
281 return ''\r
282\r
283 def isLast(self):\r
284 return self.index == len(self.string)\r
285\r
286 def moveNext(self):\r
287 self.index += 1\r
288\r
289 def skipSpace(self):\r
290 while not self.isLast():\r
291 if self.getCurr() in ' \t':\r
292 self.moveNext()\r
293 else:\r
294 return\r
295\r
296 def parseValue(self):\r
297 self.skipSpace()\r
298 var = ''\r
299 while not self.isLast():\r
300 char = self.getCurr()\r
301 if char.lower() in '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:-':\r
302 var += char\r
303 self.moveNext()\r
304 else:\r
305 break\r
306\r
307 if ':' in var:\r
308 partList = var.split(':')\r
309 if len(partList) != 2:\r
310 raise Exception("Unrecognized expression %s" % var)\r
311 modName = partList[0]\r
312 modOff = partList[1]\r
313 if ('-' not in modName) and (modOff[0] in '0123456789'):\r
314 # MOD: OFFSET\r
315 var = self.getModGuid(modName) + ":" + modOff\r
316 if '-' in var: # GUID:OFFSET\r
317 value = self.getGuidOff(var)\r
318 else:\r
319 value = self.getSymbols(var)\r
320 self.synUsed = True\r
321 else:\r
322 if var[0] in '0123456789':\r
323 value = self.getNumber(var)\r
324 else:\r
325 value = self.getVariable(var)\r
326 return int(value)\r
327\r
328 def parseSingleOp(self):\r
329 self.skipSpace()\r
330 char = self.getCurr()\r
331 if char == '~':\r
332 self.moveNext()\r
333 return ~self.parseBrace()\r
334 else:\r
335 return self.parseValue()\r
336\r
337 def parseBrace(self):\r
338 self.skipSpace()\r
339 char = self.getCurr()\r
340 parenthesisType = self.parenthesisOpenSet.find(char)\r
341 if parenthesisType >= 0:\r
342 self.moveNext()\r
343 value = self.parseExpr()\r
344 self.skipSpace()\r
345 if self.getCurr() != self.parenthesisCloseSet[parenthesisType]:\r
346 raise Exception("No closing brace")\r
347 self.moveNext()\r
348 if parenthesisType == 1: # [ : Get content\r
349 value = self.getContent(value)\r
350 elif parenthesisType == 2: # { : To address\r
351 value = self.toAddress(value)\r
352 elif parenthesisType == 3: # < : To offset\r
353 value = self.toOffset(value)\r
354 return value\r
355 else:\r
356 return self.parseSingleOp()\r
357\r
358 def parseMul(self):\r
359 values = [self.parseBrace()]\r
360 while True:\r
361 self.skipSpace()\r
362 char = self.getCurr()\r
363 if char == '*':\r
364 self.moveNext()\r
365 values.append(self.parseBrace())\r
366 else:\r
367 break\r
9da59186 368 value = 1\r
c8ec22a2
JY
369 for each in values:\r
370 value *= each\r
371 return value\r
372\r
373 def parseAndOr(self):\r
374 values = [self.parseMul()]\r
375 op = None\r
9da59186 376 value = 0xFFFFFFFF\r
c8ec22a2
JY
377 while True:\r
378 self.skipSpace()\r
379 char = self.getCurr()\r
380 if char == '&':\r
381 self.moveNext()\r
382 values.append(self.parseMul())\r
383 op = char\r
384 elif char == '|':\r
385 div_index = self.index\r
386 self.moveNext()\r
387 values.append(self.parseMul())\r
388 value = 0\r
389 op = char\r
390 else:\r
391 break\r
392\r
393 for each in values:\r
394 if op == '|':\r
395 value |= each\r
396 else:\r
397 value &= each\r
398\r
399 return value\r
400\r
401 def parseAddMinus(self):\r
402 values = [self.parseAndOr()]\r
403 while True:\r
404 self.skipSpace()\r
405 char = self.getCurr()\r
406 if char == '+':\r
407 self.moveNext()\r
408 values.append(self.parseAndOr())\r
409 elif char == '-':\r
410 self.moveNext()\r
411 values.append(-1 * self.parseAndOr())\r
412 else:\r
413 break\r
414 return sum(values)\r
415\r
416 def parseExpr(self):\r
417 return self.parseAddMinus()\r
418\r
419 def getResult(self):\r
420 value = self.parseExpr()\r
421 self.skipSpace()\r
422 if not self.isLast():\r
423 raise Exception("Unexpected character found '%s'" % self.getCurr())\r
424 return value\r
425\r
426 def getModGuid(self, var):\r
427 guid = (guid for guid,name in self.dictGuidNameXref.items() if name==var)\r
428 try:\r
429 value = guid.next()\r
430 except Exception:\r
431 raise Exception("Unknown module name %s !" % var)\r
432 return value\r
433\r
434 def getVariable(self, var):\r
435 value = self.dictVariable.get(var, None)\r
436 if value == None:\r
437 raise Exception("Unrecognized variable '%s'" % var)\r
438 return value\r
439\r
440 def getNumber(self, var):\r
441 var = var.strip()\r
442 if var.startswith('0x'): # HEX\r
443 value = int(var, 16)\r
444 else:\r
445 value = int(var, 10)\r
446 return value\r
447\r
448 def getContent(self, value):\r
449 if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):\r
450 value = value - self.fdBase\r
451 if value >= self.fdSize:\r
452 raise Exception("Invalid file offset 0x%08x !" % value)\r
453 return readDataFromFile (self.fdFile, value, 4)\r
454\r
455 def toAddress(self, value):\r
456 if value < self.fdSize:\r
457 value = value + self.fdBase\r
458 return value\r
459\r
460 def toOffset(self, value):\r
461 if value > self.fdBase:\r
462 value = value - self.fdBase\r
463 return value\r
464\r
465 def getGuidOff(self, value):\r
466 # GUID:Offset\r
467 symbolName = value.split(':')\r
468 if len(symbolName) == 2 and self.dictFfsOffset.has_key(symbolName[0]):\r
469 value = (int(self.dictFfsOffset[symbolName[0]], 16) + int(symbolName[1], 16)) & 0xFFFFFFFF\r
470 else:\r
471 raise Exception("Unknown GUID %s !" % value)\r
472 return value\r
473\r
474 def getSymbols(self, value):\r
475 if self.dictSymbolAddress.has_key(value):\r
476 # Module:Function\r
477 ret = int (self.dictSymbolAddress[value], 16)\r
478 else:\r
479 raise Exception("Unknown symbol %s !" % value)\r
480 return ret\r
481\r
482 def evaluate(self, expression, isOffset):\r
483 self.index = 0\r
484 self.synUsed = False\r
485 self.string = expression\r
486 value = self.getResult()\r
487 if isOffset:\r
488 if self.synUsed:\r
489 # Consider it as an address first\r
490 if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):\r
491 value = value - self.fdBase\r
492 if value & 0x80000000:\r
493 # Consider it as a negative offset next\r
494 offset = (~value & 0xFFFFFFFF) + 1\r
495 if offset < self.fdSize:\r
496 value = self.fdSize - offset\r
497 if value >= self.fdSize:\r
498 raise Exception("Invalid offset expression !")\r
499 return value & 0xFFFFFFFF\r
500\r
501def usage():\r
502 print "Usage: \n\tPatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch \"Offset, Value\""\r
503\r
504def main():\r
505 #\r
506 # Parse the options and args\r
507 #\r
508 symTables = Symbols()\r
509\r
510 if len(sys.argv) < 4:\r
511 Usage()\r
512 return 1\r
513\r
514 if symTables.createDicts(sys.argv[1], sys.argv[2]) != 0:\r
515 print "ERROR: Failed to create symbol dictionary!!"\r
516 return 2\r
517\r
518 fdFile = symTables.getFdFile()\r
519 fdSize = symTables.getFdSize()\r
520\r
521 try:\r
9da59186
JY
522 ret = IsFspHeaderValid(fdFile)\r
523 if ret == False:\r
524 raise Exception ("The FSP header is not valid. Stop patching FD.")\r
c8ec22a2
JY
525 comment = ""\r
526 for fvFile in sys.argv[3:]:\r
527 items = fvFile.split(",")\r
528 if len (items) < 2:\r
529 raise Exception("Expect more arguments for '%s'!" % fvFile)\r
530\r
531 comment = ""\r
532 command = ""\r
533 params = []\r
534 for item in items:\r
535 item = item.strip()\r
536 if item.startswith("@"):\r
537 comment = item[1:]\r
538 elif item.startswith("$"):\r
539 command = item[1:]\r
540 else:\r
541 if len(params) == 0:\r
542 isOffset = True\r
543 else :\r
544 isOffset = False\r
545 params.append (symTables.evaluate(item, isOffset))\r
546\r
547 if command == "":\r
548 # Patch a DWORD\r
549 if len (params) == 2:\r
550 offset = params[0]\r
551 value = params[1]\r
552 oldvalue = readDataFromFile(fdFile, offset, 4)\r
553 ret = patchDataInFile (fdFile, offset, value, 4) - 4\r
554 else:\r
555 raise Exception ("Patch command needs 2 parameters !")\r
556\r
557 if ret:\r
558 raise Exception ("Patch failed for offset 0x%08X" % offset)\r
559 else:\r
560 print "Patched offset 0x%08X:[%08X] with value 0x%08X # %s" % (offset, oldvalue, value, comment)\r
561\r
562 elif command == "COPY":\r
563 # Copy binary block from source to destination\r
564 if len (params) == 3:\r
565 src = symTables.toOffset(params[0])\r
566 dest = symTables.toOffset(params[1])\r
567 clen = symTables.toOffset(params[2])\r
568 if (dest + clen <= fdSize) and (src + clen <= fdSize):\r
569 oldvalue = readDataFromFile(fdFile, src, clen)\r
570 ret = patchDataInFile (fdFile, dest, oldvalue, clen) - clen\r
571 else:\r
572 raise Exception ("Copy command OFFSET or LENGTH parameter is invalid !")\r
573 else:\r
574 raise Exception ("Copy command needs 3 parameters !")\r
575\r
576 if ret:\r
577 raise Exception ("Copy failed from offset 0x%08X to offset 0x%08X!" % (src, dest))\r
578 else :\r
579 print "Copied %d bytes from offset 0x%08X ~ offset 0x%08X # %s" % (clen, src, dest, comment)\r
580 else:\r
581 raise Exception ("Unknown command %s!" % command)\r
582 return 0\r
583\r
584 except Exception as (ex):\r
585 print "ERROR: %s" % ex\r
586 return 1\r
587\r
588if __name__ == '__main__':\r
589 sys.exit(main())\r