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