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