]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFsp2Pkg/Tools/PatchFv.py
IntelFsp2Pkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / IntelFsp2Pkg / Tools / PatchFv.py
1 ## @ PatchFv.py
2 #
3 # Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
4 # SPDX-License-Identifier: BSD-2-Clause-Patent
5 #
6 ##
7
8 import os
9 import re
10 import sys
11
12 #
13 # Read data from file
14 #
15 # param [in] binfile Binary file
16 # param [in] offset Offset
17 # param [in] len Length
18 #
19 # retval value Value
20 #
21 def readDataFromFile (binfile, offset, len=1):
22 fd = open(binfile, "r+b")
23 fsize = os.path.getsize(binfile)
24 offval = offset & 0xFFFFFFFF
25 if (offval & 0x80000000):
26 offval = fsize - (0xFFFFFFFF - offval + 1)
27 fd.seek(offval)
28 bytearray = [ord(b) for b in fd.read(len)]
29 value = 0
30 idx = len - 1
31 while idx >= 0:
32 value = value << 8 | bytearray[idx]
33 idx = idx - 1
34 fd.close()
35 return value
36
37 #
38 # Check FSP header is valid or not
39 #
40 # param [in] binfile Binary file
41 #
42 # retval boolean True: valid; False: invalid
43 #
44 def IsFspHeaderValid (binfile):
45 fd = open (binfile, "rb")
46 bindat = fd.read(0x200) # only read first 0x200 bytes
47 fd.close()
48 HeaderList = ['FSPH' , 'FSPP' , 'FSPE'] # Check 'FSPH', 'FSPP', and 'FSPE' in the FSP header
49 OffsetList = []
50 for each in HeaderList:
51 if each in bindat:
52 idx = bindat.index(each)
53 else:
54 idx = 0
55 OffsetList.append(idx)
56 if not OffsetList[0] or not OffsetList[1]: # If 'FSPH' or 'FSPP' is missing, it will return false
57 return False
58 Revision = ord(bindat[OffsetList[0] + 0x0B])
59 #
60 # if revision is bigger than 1, it means it is FSP v1.1 or greater revision, which must contain 'FSPE'.
61 #
62 if Revision > 1 and not OffsetList[2]:
63 return False # If FSP v1.1 or greater without 'FSPE', then return false
64 return True
65
66 #
67 # Patch data in file
68 #
69 # param [in] binfile Binary file
70 # param [in] offset Offset
71 # param [in] value Patch value
72 # param [in] len Length
73 #
74 # retval len Length
75 #
76 def patchDataInFile (binfile, offset, value, len=1):
77 fd = open(binfile, "r+b")
78 fsize = os.path.getsize(binfile)
79 offval = offset & 0xFFFFFFFF
80 if (offval & 0x80000000):
81 offval = fsize - (0xFFFFFFFF - offval + 1)
82 bytearray = []
83 idx = 0
84 while idx < len:
85 bytearray.append(value & 0xFF)
86 value = value >> 8
87 idx = idx + 1
88 fd.seek(offval)
89 fd.write("".join(chr(b) for b in bytearray))
90 fd.close()
91 return len
92
93
94 class Symbols:
95 def __init__(self):
96 self.dictSymbolAddress = {}
97 self.dictGuidNameXref = {}
98 self.dictFfsOffset = {}
99 self.dictVariable = {}
100 self.dictModBase = {}
101 self.fdFile = None
102 self.string = ""
103 self.fdBase = 0xFFFFFFFF
104 self.fdSize = 0
105 self.index = 0
106 self.fvList = []
107 self.parenthesisOpenSet = '([{<'
108 self.parenthesisCloseSet = ')]}>'
109
110 #
111 # Get FD file
112 #
113 # retval self.fdFile Retrieve FD file
114 #
115 def getFdFile (self):
116 return self.fdFile
117
118 #
119 # Get FD size
120 #
121 # retval self.fdSize Retrieve the size of FD file
122 #
123 def getFdSize (self):
124 return self.fdSize
125
126 def parseFvInfFile (self, infFile):
127 fvInfo = {}
128 fvFile = infFile[0:-4] + ".Fv"
129 fvInfo['Name'] = os.path.splitext(os.path.basename(infFile))[0]
130 fvInfo['Offset'] = self.getFvOffsetInFd(fvFile)
131 fvInfo['Size'] = readDataFromFile (fvFile, 0x20, 4)
132 fdIn = open(infFile, "r")
133 rptLines = fdIn.readlines()
134 fdIn.close()
135 fvInfo['Base'] = 0
136 for rptLine in rptLines:
137 match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine)
138 if match:
139 fvInfo['Base'] = int(match.group(1), 16)
140 break
141 self.fvList.append(dict(fvInfo))
142 return 0
143
144 #
145 # Create dictionaries
146 #
147 # param [in] fvDir FV's directory
148 # param [in] fvNames All FV's names
149 #
150 # retval 0 Created dictionaries successfully
151 #
152 def createDicts (self, fvDir, fvNames):
153 #
154 # If the fvDir is not a dirctory, then raise an exception
155 #
156 if not os.path.isdir(fvDir):
157 raise Exception ("'%s' is not a valid directory!" % FvDir)
158
159 #
160 # If the Guid.xref is not existing in fvDir, then raise an exception
161 #
162 xrefFile = os.path.join(fvDir, "Guid.xref")
163 if not os.path.exists(xrefFile):
164 raise Exception("Cannot open GUID Xref file '%s'!" % xrefFile)
165
166 #
167 # Add GUID reference to dictionary
168 #
169 self.dictGuidNameXref = {}
170 self.parseGuidXrefFile(xrefFile)
171
172 #
173 # Split up each FV from fvNames and get the fdBase
174 #
175 fvList = fvNames.split(":")
176 fdBase = fvList.pop()
177 if len(fvList) == 0:
178 fvList.append(fdBase)
179
180 #
181 # If the FD file is not existing, then raise an exception
182 #
183 fdFile = os.path.join(fvDir, fdBase.strip() + ".fd")
184 if not os.path.exists(fdFile):
185 raise Exception("Cannot open FD file '%s'!" % fdFile)
186
187 #
188 # Get the size of the FD file
189 #
190 self.fdFile = fdFile
191 self.fdSize = os.path.getsize(fdFile)
192
193 #
194 # If the INF file, which is the first element of fvList, is not existing, then raise an exception
195 #
196 infFile = os.path.join(fvDir, fvList[0].strip()) + ".inf"
197 if not os.path.exists(infFile):
198 raise Exception("Cannot open INF file '%s'!" % infFile)
199
200 #
201 # Parse INF file in order to get fdBase and then assign those values to dictVariable
202 #
203 self.parseInfFile(infFile)
204 self.dictVariable = {}
205 self.dictVariable["FDSIZE"] = self.fdSize
206 self.dictVariable["FDBASE"] = self.fdBase
207
208 #
209 # Collect information from FV MAP file and FV TXT file then
210 # put them into dictionaries
211 #
212 self.fvList = []
213 self.dictSymbolAddress = {}
214 self.dictFfsOffset = {}
215 for file in fvList:
216
217 #
218 # If the .Fv.map file is not existing, then raise an exception.
219 # Otherwise, parse FV MAP file
220 #
221 fvFile = os.path.join(fvDir, file.strip()) + ".Fv"
222 mapFile = fvFile + ".map"
223 if not os.path.exists(mapFile):
224 raise Exception("Cannot open MAP file '%s'!" % mapFile)
225
226 infFile = fvFile[0:-3] + ".inf"
227 self.parseFvInfFile(infFile)
228 self.parseFvMapFile(mapFile)
229
230 #
231 # If the .Fv.txt file is not existing, then raise an exception.
232 # Otherwise, parse FV TXT file
233 #
234 fvTxtFile = fvFile + ".txt"
235 if not os.path.exists(fvTxtFile):
236 raise Exception("Cannot open FV TXT file '%s'!" % fvTxtFile)
237
238 self.parseFvTxtFile(fvTxtFile)
239
240 for fv in self.fvList:
241 self.dictVariable['_BASE_%s_' % fv['Name']] = fv['Base']
242 #
243 # Search all MAP files in FFS directory if it exists then parse MOD MAP file
244 #
245 ffsDir = os.path.join(fvDir, "Ffs")
246 if (os.path.isdir(ffsDir)):
247 for item in os.listdir(ffsDir):
248 if len(item) <= 0x24:
249 continue
250 mapFile =os.path.join(ffsDir, item, "%s.map" % item[0:0x24])
251 if not os.path.exists(mapFile):
252 continue
253 self.parseModMapFile(item[0x24:], mapFile)
254
255 return 0
256
257 #
258 # Get FV offset in FD file
259 #
260 # param [in] fvFile FV file
261 #
262 # retval offset Got FV offset successfully
263 #
264 def getFvOffsetInFd(self, fvFile):
265 #
266 # Check if the first 0x70 bytes of fvFile can be found in fdFile
267 #
268 fvHandle = open(fvFile, "r+b")
269 fdHandle = open(self.fdFile, "r+b")
270 offset = fdHandle.read().find(fvHandle.read(0x70))
271 fvHandle.close()
272 fdHandle.close()
273 if offset == -1:
274 raise Exception("Could not locate FV file %s in FD!" % fvFile)
275 return offset
276
277 #
278 # Parse INF file
279 #
280 # param [in] infFile INF file
281 #
282 # retval 0 Parsed INF file successfully
283 #
284 def parseInfFile(self, infFile):
285 #
286 # Get FV offset and search EFI_BASE_ADDRESS in the FD file
287 # then assign the value of EFI_BASE_ADDRESS to fdBase
288 #
289 fvOffset = self.getFvOffsetInFd(infFile[0:-4] + ".Fv")
290 fdIn = open(infFile, "r")
291 rptLine = fdIn.readline()
292 self.fdBase = 0xFFFFFFFF
293 while (rptLine != "" ):
294 #EFI_BASE_ADDRESS = 0xFFFDF400
295 match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine)
296 if match is not None:
297 self.fdBase = int(match.group(1), 16) - fvOffset
298 rptLine = fdIn.readline()
299 fdIn.close()
300 if self.fdBase == 0xFFFFFFFF:
301 raise Exception("Could not find EFI_BASE_ADDRESS in INF file!" % fvFile)
302 return 0
303
304 #
305 # Parse FV TXT file
306 #
307 # param [in] fvTxtFile .Fv.txt file
308 #
309 # retval 0 Parsed FV TXT file successfully
310 #
311 def parseFvTxtFile(self, fvTxtFile):
312 fvName = os.path.basename(fvTxtFile)[0:-7].upper()
313 #
314 # Get information from .Fv.txt in order to create a dictionary
315 # For example,
316 # self.dictFfsOffset[912740BE-2284-4734-B971-84B027353F0C] = 0x000D4078
317 #
318 fvOffset = self.getFvOffsetInFd(fvTxtFile[0:-4])
319 fdIn = open(fvTxtFile, "r")
320 rptLine = fdIn.readline()
321 while (rptLine != "" ):
322 match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", rptLine)
323 if match is not None:
324 if match.group(2) in self.dictFfsOffset:
325 self.dictFfsOffset[fvName + ':' + match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset)
326 else:
327 self.dictFfsOffset[match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset)
328 rptLine = fdIn.readline()
329 fdIn.close()
330 return 0
331
332 #
333 # Parse FV MAP file
334 #
335 # param [in] mapFile .Fv.map file
336 #
337 # retval 0 Parsed FV MAP file successfully
338 #
339 def parseFvMapFile(self, mapFile):
340 #
341 # Get information from .Fv.map in order to create dictionaries
342 # For example,
343 # self.dictModBase[FspSecCore:BASE] = 4294592776 (0xfffa4908)
344 # self.dictModBase[FspSecCore:ENTRY] = 4294606552 (0xfffa7ed8)
345 # self.dictModBase[FspSecCore:TEXT] = 4294593080 (0xfffa4a38)
346 # self.dictModBase[FspSecCore:DATA] = 4294612280 (0xfffa9538)
347 # self.dictSymbolAddress[FspSecCore:_SecStartup] = 0x00fffa4a38
348 #
349 fdIn = open(mapFile, "r")
350 rptLine = fdIn.readline()
351 modName = ""
352 foundModHdr = False
353 while (rptLine != "" ):
354 if rptLine[0] != ' ':
355 #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958)
356 #(GUID=86D70125-BAA3-4296-A62F-602BEBBB9081 .textbaseaddress=0x00fffb4398 .databaseaddress=0x00fffb4178)
357 match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+)\)", rptLine)
358 if match is not None:
359 foundModHdr = True
360 modName = match.group(1)
361 if len(modName) == 36:
362 modName = self.dictGuidNameXref[modName.upper()]
363 self.dictModBase['%s:BASE' % modName] = int (match.group(2), 16)
364 self.dictModBase['%s:ENTRY' % modName] = int (match.group(3), 16)
365 match = re.match("\(GUID=([A-Z0-9\-]+)\s+\.textbaseaddress=(0x[0-9a-fA-F]+)\s+\.databaseaddress=(0x[0-9a-fA-F]+)\)", rptLine)
366 if match is not None:
367 if foundModHdr:
368 foundModHdr = False
369 else:
370 modName = match.group(1)
371 if len(modName) == 36:
372 modName = self.dictGuidNameXref[modName.upper()]
373 self.dictModBase['%s:TEXT' % modName] = int (match.group(2), 16)
374 self.dictModBase['%s:DATA' % modName] = int (match.group(3), 16)
375 else:
376 # 0x00fff8016c __ModuleEntryPoint
377 foundModHdr = False
378 match = re.match("^\s+(0x[a-z0-9]+)\s+([_a-zA-Z0-9]+)", rptLine)
379 if match is not None:
380 self.dictSymbolAddress["%s:%s"%(modName, match.group(2))] = match.group(1)
381 rptLine = fdIn.readline()
382 fdIn.close()
383 return 0
384
385 #
386 # Parse MOD MAP file
387 #
388 # param [in] moduleName Module name
389 # param [in] mapFile .Fv.map file
390 #
391 # retval 0 Parsed MOD MAP file successfully
392 # retval 1 There is no moduleEntryPoint in modSymbols
393 #
394 def parseModMapFile(self, moduleName, mapFile):
395 #
396 # Get information from mapFile by moduleName in order to create a dictionary
397 # For example,
398 # self.dictSymbolAddress[FspSecCore:___guard_fids_count] = 0x00fffa4778
399 #
400 modSymbols = {}
401 fdIn = open(mapFile, "r")
402 reportLines = fdIn.readlines()
403 fdIn.close()
404
405 moduleEntryPoint = "__ModuleEntryPoint"
406 reportLine = reportLines[0]
407 if reportLine.strip().find("Archive member included") != -1:
408 #GCC
409 # 0x0000000000001d55 IoRead8
410 patchMapFileMatchString = "\s+(0x[0-9a-fA-F]{16})\s+([^\s][^0x][_a-zA-Z0-9\-]+)\s"
411 matchKeyGroupIndex = 2
412 matchSymbolGroupIndex = 1
413 prefix = '_'
414 else:
415 #MSFT
416 #0003:00000190 _gComBase 00007a50 SerialPo
417 patchMapFileMatchString = "^\s[0-9a-fA-F]{4}:[0-9a-fA-F]{8}\s+(\w+)\s+([0-9a-fA-F]{8}\s+)"
418 matchKeyGroupIndex = 1
419 matchSymbolGroupIndex = 2
420 prefix = ''
421
422 for reportLine in reportLines:
423 match = re.match(patchMapFileMatchString, reportLine)
424 if match is not None:
425 modSymbols[prefix + match.group(matchKeyGroupIndex)] = match.group(matchSymbolGroupIndex)
426
427 # Handle extra module patchable PCD variable in Linux map since it might have different format
428 # .data._gPcd_BinaryPatch_PcdVpdBaseAddress
429 # 0x0000000000003714 0x4 /tmp/ccmytayk.ltrans1.ltrans.o
430 handleNext = False
431 if matchSymbolGroupIndex == 1:
432 for reportLine in reportLines:
433 if handleNext:
434 handleNext = False
435 pcdName = match.group(1)
436 match = re.match("\s+(0x[0-9a-fA-F]{16})\s+", reportLine)
437 if match is not None:
438 modSymbols[prefix + pcdName] = match.group(1)
439 else:
440 match = re.match("^\s\.data\.(_gPcd_BinaryPatch[_a-zA-Z0-9\-]+)", reportLine)
441 if match is not None:
442 handleNext = True
443 continue
444
445 if not moduleEntryPoint in modSymbols:
446 return 1
447
448 modEntry = '%s:%s' % (moduleName,moduleEntryPoint)
449 if not modEntry in self.dictSymbolAddress:
450 modKey = '%s:ENTRY' % moduleName
451 if modKey in self.dictModBase:
452 baseOffset = self.dictModBase['%s:ENTRY' % moduleName] - int(modSymbols[moduleEntryPoint], 16)
453 else:
454 return 2
455 else:
456 baseOffset = int(self.dictSymbolAddress[modEntry], 16) - int(modSymbols[moduleEntryPoint], 16)
457 for symbol in modSymbols:
458 fullSym = "%s:%s" % (moduleName, symbol)
459 if not fullSym in self.dictSymbolAddress:
460 self.dictSymbolAddress[fullSym] = "0x00%08x" % (baseOffset+ int(modSymbols[symbol], 16))
461 return 0
462
463 #
464 # Parse Guid.xref file
465 #
466 # param [in] xrefFile the full directory of Guid.xref file
467 #
468 # retval 0 Parsed Guid.xref file successfully
469 #
470 def parseGuidXrefFile(self, xrefFile):
471 #
472 # Get information from Guid.xref in order to create a GuidNameXref dictionary
473 # The dictGuidNameXref, for example, will be like
474 # dictGuidNameXref [1BA0062E-C779-4582-8566-336AE8F78F09] = FspSecCore
475 #
476 fdIn = open(xrefFile, "r")
477 rptLine = fdIn.readline()
478 while (rptLine != "" ):
479 match = re.match("([0-9a-fA-F\-]+)\s([_a-zA-Z0-9]+)", rptLine)
480 if match is not None:
481 self.dictGuidNameXref[match.group(1).upper()] = match.group(2)
482 rptLine = fdIn.readline()
483 fdIn.close()
484 return 0
485
486 #
487 # Get current character
488 #
489 # retval elf.string[self.index]
490 # retval '' Exception
491 #
492 def getCurr(self):
493 try:
494 return self.string[self.index]
495 except Exception:
496 return ''
497
498 #
499 # Check to see if it is last index
500 #
501 # retval self.index
502 #
503 def isLast(self):
504 return self.index == len(self.string)
505
506 #
507 # Move to next index
508 #
509 def moveNext(self):
510 self.index += 1
511
512 #
513 # Skip space
514 #
515 def skipSpace(self):
516 while not self.isLast():
517 if self.getCurr() in ' \t':
518 self.moveNext()
519 else:
520 return
521
522 #
523 # Parse value
524 #
525 # retval value
526 #
527 def parseValue(self):
528 self.skipSpace()
529 var = ''
530 while not self.isLast():
531 char = self.getCurr()
532 if char.lower() in '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:-':
533 var += char
534 self.moveNext()
535 else:
536 break
537
538 if ':' in var:
539 partList = var.split(':')
540 lenList = len(partList)
541 if lenList != 2 and lenList != 3:
542 raise Exception("Unrecognized expression %s" % var)
543 modName = partList[lenList-2]
544 modOff = partList[lenList-1]
545 if ('-' not in modName) and (modOff[0] in '0123456789'):
546 # MOD: OFFSET
547 var = self.getModGuid(modName) + ":" + modOff
548 if '-' in var: # GUID:OFFSET
549 value = self.getGuidOff(var)
550 else:
551 value = self.getSymbols(var)
552 self.synUsed = True
553 else:
554 if var[0] in '0123456789':
555 value = self.getNumber(var)
556 else:
557 value = self.getVariable(var)
558 return int(value)
559
560 #
561 # Parse single operation
562 #
563 # retval ~self.parseBrace() or self.parseValue()
564 #
565 def parseSingleOp(self):
566 self.skipSpace()
567 char = self.getCurr()
568 if char == '~':
569 self.moveNext()
570 return ~self.parseBrace()
571 else:
572 return self.parseValue()
573
574 #
575 # Parse symbol of Brace([, {, <)
576 #
577 # retval value or self.parseSingleOp()
578 #
579 def parseBrace(self):
580 self.skipSpace()
581 char = self.getCurr()
582 parenthesisType = self.parenthesisOpenSet.find(char)
583 if parenthesisType >= 0:
584 self.moveNext()
585 value = self.parseExpr()
586 self.skipSpace()
587 if self.getCurr() != self.parenthesisCloseSet[parenthesisType]:
588 raise Exception("No closing brace")
589 self.moveNext()
590 if parenthesisType == 1: # [ : Get content
591 value = self.getContent(value)
592 elif parenthesisType == 2: # { : To address
593 value = self.toAddress(value)
594 elif parenthesisType == 3: # < : To offset
595 value = self.toOffset(value)
596 return value
597 else:
598 return self.parseSingleOp()
599
600 #
601 # Parse symbol of Multiplier(*)
602 #
603 # retval value or self.parseSingleOp()
604 #
605 def parseMul(self):
606 values = [self.parseBrace()]
607 while True:
608 self.skipSpace()
609 char = self.getCurr()
610 if char == '*':
611 self.moveNext()
612 values.append(self.parseBrace())
613 else:
614 break
615 value = 1
616 for each in values:
617 value *= each
618 return value
619
620 #
621 # Parse symbol of And(&) and Or(|)
622 #
623 # retval value
624 #
625 def parseAndOr(self):
626 value = self.parseMul()
627 op = None
628 while True:
629 self.skipSpace()
630 char = self.getCurr()
631 if char == '&':
632 self.moveNext()
633 value &= self.parseMul()
634 elif char == '|':
635 div_index = self.index
636 self.moveNext()
637 value |= self.parseMul()
638 else:
639 break
640
641 return value
642
643 #
644 # Parse symbol of Add(+) and Minus(-)
645 #
646 # retval sum(values)
647 #
648 def parseAddMinus(self):
649 values = [self.parseAndOr()]
650 while True:
651 self.skipSpace()
652 char = self.getCurr()
653 if char == '+':
654 self.moveNext()
655 values.append(self.parseAndOr())
656 elif char == '-':
657 self.moveNext()
658 values.append(-1 * self.parseAndOr())
659 else:
660 break
661 return sum(values)
662
663 #
664 # Parse expression
665 #
666 # retval self.parseAddMinus()
667 #
668 def parseExpr(self):
669 return self.parseAddMinus()
670
671 #
672 # Get result
673 #
674 # retval value
675 #
676 def getResult(self):
677 value = self.parseExpr()
678 self.skipSpace()
679 if not self.isLast():
680 raise Exception("Unexpected character found '%s'" % self.getCurr())
681 return value
682
683 #
684 # Get module GUID
685 #
686 # retval value
687 #
688 def getModGuid(self, var):
689 guid = (guid for guid,name in self.dictGuidNameXref.items() if name==var)
690 try:
691 value = guid.next()
692 except Exception:
693 raise Exception("Unknown module name %s !" % var)
694 return value
695
696 #
697 # Get variable
698 #
699 # retval value
700 #
701 def getVariable(self, var):
702 value = self.dictVariable.get(var, None)
703 if value == None:
704 raise Exception("Unrecognized variable '%s'" % var)
705 return value
706
707 #
708 # Get number
709 #
710 # retval value
711 #
712 def getNumber(self, var):
713 var = var.strip()
714 if var.startswith('0x'): # HEX
715 value = int(var, 16)
716 else:
717 value = int(var, 10)
718 return value
719
720 #
721 # Get content
722 #
723 # param [in] value
724 #
725 # retval value
726 #
727 def getContent(self, value):
728 return readDataFromFile (self.fdFile, self.toOffset(value), 4)
729
730 #
731 # Change value to address
732 #
733 # param [in] value
734 #
735 # retval value
736 #
737 def toAddress(self, value):
738 if value < self.fdSize:
739 value = value + self.fdBase
740 return value
741
742 #
743 # Change value to offset
744 #
745 # param [in] value
746 #
747 # retval value
748 #
749 def toOffset(self, value):
750 offset = None
751 for fvInfo in self.fvList:
752 if (value >= fvInfo['Base']) and (value < fvInfo['Base'] + fvInfo['Size']):
753 offset = value - fvInfo['Base'] + fvInfo['Offset']
754 if not offset:
755 if (value >= self.fdBase) and (value < self.fdBase + self.fdSize):
756 offset = value - self.fdBase
757 else:
758 offset = value
759 if offset >= self.fdSize:
760 raise Exception("Invalid file offset 0x%08x !" % value)
761 return offset
762
763 #
764 # Get GUID offset
765 #
766 # param [in] value
767 #
768 # retval value
769 #
770 def getGuidOff(self, value):
771 # GUID:Offset
772 symbolName = value.split(':')
773 if len(symbolName) == 3:
774 fvName = symbolName[0].upper()
775 keyName = '%s:%s' % (fvName, symbolName[1])
776 offStr = symbolName[2]
777 elif len(symbolName) == 2:
778 keyName = symbolName[0]
779 offStr = symbolName[1]
780 if keyName in self.dictFfsOffset:
781 value = (int(self.dictFfsOffset[keyName], 16) + int(offStr, 16)) & 0xFFFFFFFF
782 else:
783 raise Exception("Unknown GUID %s !" % value)
784 return value
785
786 #
787 # Get symbols
788 #
789 # param [in] value
790 #
791 # retval ret
792 #
793 def getSymbols(self, value):
794 if self.dictSymbolAddress.has_key(value):
795 # Module:Function
796 ret = int (self.dictSymbolAddress[value], 16)
797 else:
798 raise Exception("Unknown symbol %s !" % value)
799 return ret
800
801 #
802 # Evaluate symbols
803 #
804 # param [in] expression
805 # param [in] isOffset
806 #
807 # retval value & 0xFFFFFFFF
808 #
809 def evaluate(self, expression, isOffset):
810 self.index = 0
811 self.synUsed = False
812 self.string = expression
813 value = self.getResult()
814 if isOffset:
815 if self.synUsed:
816 # Consider it as an address first
817 value = self.toOffset(value)
818 if value & 0x80000000:
819 # Consider it as a negative offset next
820 offset = (~value & 0xFFFFFFFF) + 1
821 if offset < self.fdSize:
822 value = self.fdSize - offset
823 if value >= self.fdSize:
824 raise Exception("Invalid offset expression !")
825 return value & 0xFFFFFFFF
826
827 #
828 # Print out the usage
829 #
830 def usage():
831 print "Usage: \n\tPatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch \"Offset, Value\""
832
833 def main():
834 #
835 # Parse the options and args
836 #
837 symTables = Symbols()
838
839 #
840 # If the arguments are less than 4, then return an error.
841 #
842 if len(sys.argv) < 4:
843 Usage()
844 return 1
845
846 #
847 # If it fails to create dictionaries, then return an error.
848 #
849 if symTables.createDicts(sys.argv[1], sys.argv[2]) != 0:
850 print "ERROR: Failed to create symbol dictionary!!"
851 return 2
852
853 #
854 # Get FD file and size
855 #
856 fdFile = symTables.getFdFile()
857 fdSize = symTables.getFdSize()
858
859 try:
860 #
861 # Check to see if FSP header is valid
862 #
863 ret = IsFspHeaderValid(fdFile)
864 if ret == False:
865 raise Exception ("The FSP header is not valid. Stop patching FD.")
866 comment = ""
867 for fvFile in sys.argv[3:]:
868 #
869 # Check to see if it has enough arguments
870 #
871 items = fvFile.split(",")
872 if len (items) < 2:
873 raise Exception("Expect more arguments for '%s'!" % fvFile)
874
875 comment = ""
876 command = ""
877 params = []
878 for item in items:
879 item = item.strip()
880 if item.startswith("@"):
881 comment = item[1:]
882 elif item.startswith("$"):
883 command = item[1:]
884 else:
885 if len(params) == 0:
886 isOffset = True
887 else :
888 isOffset = False
889 #
890 # Parse symbols then append it to params
891 #
892 params.append (symTables.evaluate(item, isOffset))
893
894 #
895 # Patch a new value into FD file if it is not a command
896 #
897 if command == "":
898 # Patch a DWORD
899 if len (params) == 2:
900 offset = params[0]
901 value = params[1]
902 oldvalue = readDataFromFile(fdFile, offset, 4)
903 ret = patchDataInFile (fdFile, offset, value, 4) - 4
904 else:
905 raise Exception ("Patch command needs 2 parameters !")
906
907 if ret:
908 raise Exception ("Patch failed for offset 0x%08X" % offset)
909 else:
910 print "Patched offset 0x%08X:[%08X] with value 0x%08X # %s" % (offset, oldvalue, value, comment)
911
912 elif command == "COPY":
913 #
914 # Copy binary block from source to destination
915 #
916 if len (params) == 3:
917 src = symTables.toOffset(params[0])
918 dest = symTables.toOffset(params[1])
919 clen = symTables.toOffset(params[2])
920 if (dest + clen <= fdSize) and (src + clen <= fdSize):
921 oldvalue = readDataFromFile(fdFile, src, clen)
922 ret = patchDataInFile (fdFile, dest, oldvalue, clen) - clen
923 else:
924 raise Exception ("Copy command OFFSET or LENGTH parameter is invalid !")
925 else:
926 raise Exception ("Copy command needs 3 parameters !")
927
928 if ret:
929 raise Exception ("Copy failed from offset 0x%08X to offset 0x%08X!" % (src, dest))
930 else :
931 print "Copied %d bytes from offset 0x%08X ~ offset 0x%08X # %s" % (clen, src, dest, comment)
932 else:
933 raise Exception ("Unknown command %s!" % command)
934 return 0
935
936 except Exception as (ex):
937 print "ERROR: %s" % ex
938 return 1
939
940 if __name__ == '__main__':
941 sys.exit(main())