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