PatchFv.py 29 KB

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