]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/basemodel/efibinary.py
BaseTools: Add PackageDocumentTools into Scripts folder
[mirror_edk2.git] / BaseTools / Scripts / PackageDocumentTools / plugins / EdkPlugins / basemodel / efibinary.py
CommitLineData
7ccc9c95
YZ
1## @file\r
2#\r
3# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
4#\r
5# This program and the accompanying materials are licensed and made available\r
6# under the terms and conditions of the BSD License which accompanies this\r
7# distribution. The full text of the license may be found at\r
8# http://opensource.org/licenses/bsd-license.php\r
9#\r
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12#\r
13\r
14import array\r
15import uuid\r
16import re\r
17import os\r
18import logging\r
19import core.pe as pe\r
20\r
21def GetLogger():\r
22 return logging.getLogger('EFI Binary File')\r
23\r
24class EFIBinaryError(Exception):\r
25 def __init__(self, message):\r
26 Exception.__init__(self)\r
27 self._message = message\r
28\r
29 def GetMessage(self):\r
30 return self._message\r
31\r
32class EfiFd(object):\r
33 EFI_FV_HEADER_SIZE = 0x48\r
34\r
35 def __init__(self):\r
36 self._fvs = []\r
37\r
38 def Load(self, fd, size):\r
39 index = fd.tell()\r
40 while (index + self.EFI_FV_HEADER_SIZE < size):\r
41 fv = EfiFv(self)\r
42 fv.Load(fd)\r
43 self._fvs.append(fv)\r
44 index += fv.GetHeader().GetFvLength()\r
45 index = align(index, 8)\r
46 fd.seek(index)\r
47\r
48 def GetFvs(self):\r
49 return self._fvs\r
50\r
51class EfiFv(object):\r
52 FILE_SYSTEM_GUID = uuid.UUID('{8c8ce578-8a3d-4f1c-9935-896185c32dd3}')\r
53\r
54 def __init__(self, parent=None):\r
55 self._size = 0\r
56 self._filename = None\r
57 self._fvheader = None\r
58 self._blockentries = []\r
59 self._ffs = []\r
60\r
61 # following field is for FV in FD\r
62 self._parent = parent\r
63 self._offset = 0\r
64 self._raw = array.array('B')\r
65\r
66 def Load(self, fd):\r
67 self._offset = fd.tell()\r
68 self._filename = fd.name\r
69\r
70 # get file header\r
71 self._fvheader = EfiFirmwareVolumeHeader.Read(fd)\r
72 #self._fvheader.Dump()\r
73\r
74 self._size = self._fvheader.GetFvLength()\r
75\r
76 if self._fvheader.GetFileSystemGuid() != self.FILE_SYSTEM_GUID:\r
77 fd.seek(self._offset)\r
78 self._raw.fromfile(fd, self.GetHeader().GetFvLength())\r
79 return\r
80\r
81 # read block map\r
82 blockentry = BlockMapEntry.Read(fd)\r
83 self._blockentries.append(blockentry)\r
84 while (blockentry.GetNumberBlocks() != 0 and blockentry.GetLength() != 0):\r
85 self._blockentries.append(blockentry)\r
86 blockentry = BlockMapEntry.Read(fd)\r
87\r
88\r
89 if self._fvheader.GetSize() + (len(self._blockentries)) * 8 != \\r
90 self._fvheader.GetHeaderLength():\r
91 raise EFIBinaryError("Volume Header length not consistent with block map!")\r
92\r
93 index = align(fd.tell(), 8)\r
94 count = 0\r
95 while ((index + EfiFfs.FFS_HEADER_SIZE) < self._size):\r
96 ffs = EfiFfs.Read(fd, self)\r
97 if not isValidGuid(ffs.GetNameGuid()):\r
98 break\r
99 self._ffs.append(ffs)\r
100 count += 1\r
101 index = align(fd.tell(), 8)\r
102\r
103 fd.seek(self._offset)\r
104 self._raw.fromfile(fd, self.GetHeader().GetFvLength())\r
105\r
106 def GetFfs(self):\r
107 return self._ffs\r
108\r
109 def GetHeader(self):\r
110 return self._fvheader\r
111\r
112 def GetBlockEntries(self):\r
113 return self._blockentries\r
114\r
115 def GetHeaderRawData(self):\r
116 ret = []\r
117 ret += self._fvheader.GetRawData()\r
118 for block in self._blockentries:\r
119 ret += block.GetRawData()\r
120 return ret\r
121\r
122 def GetOffset(self):\r
123 return 0\r
124\r
125 def GetRawData(self):\r
126 return self._raw.tolist()\r
127\r
128class BinaryItem(object):\r
129 def __init__(self, parent=None):\r
130 self._size = 0\r
131 self._arr = array.array('B')\r
132 self._parent = parent\r
133\r
134 @classmethod\r
135 def Read(cls, fd, parent=None):\r
136 item = cls(parent)\r
137 item.fromfile(fd)\r
138 return item\r
139\r
140 def Load(self, fd):\r
141 self.fromfile(fd)\r
142\r
143 def GetSize(self):\r
144 """should be implemented by inherited class"""\r
145\r
146 def fromfile(self, fd):\r
147 self._arr.fromfile(fd, self.GetSize())\r
148\r
149 def GetParent(self):\r
150 return self._parent\r
151\r
152class EfiFirmwareVolumeHeader(BinaryItem):\r
153 def GetSize(self):\r
154 return 56\r
155\r
156 def GetSigunature(self):\r
157 list = self._arr.tolist()\r
158 sig = ''\r
159 for x in list[40:44]:\r
160 sig += chr(x)\r
161 return sig\r
162\r
163 def GetAttribute(self):\r
164 return list2int(self._arr.tolist()[44:48])\r
165\r
166 def GetErasePolarity(self):\r
167 list = self.GetAttrStrings()\r
168 if 'EFI_FVB2_ERASE_POLARITY' in list:\r
169 return True\r
170 return False\r
171\r
172 def GetAttrStrings(self):\r
173 list = []\r
174 value = self.GetAttribute()\r
175 if (value & 0x01) != 0:\r
176 list.append('EFI_FVB2_READ_DISABLED_CAP')\r
177 if (value & 0x02) != 0:\r
178 list.append('EFI_FVB2_READ_ENABLED_CAP')\r
179 if (value & 0x04) != 0:\r
180 list.append('EFI_FVB2_READ_STATUS')\r
181 if (value & 0x08) != 0:\r
182 list.append('EFI_FVB2_WRITE_DISABLED_CAP')\r
183 if (value & 0x10) != 0:\r
184 list.append('EFI_FVB2_WRITE_ENABLED_CAP')\r
185 if (value & 0x20) != 0:\r
186 list.append('EFI_FVB2_WRITE_STATUS')\r
187 if (value & 0x40) != 0:\r
188 list.append('EFI_FVB2_LOCK_CAP')\r
189 if (value & 0x80) != 0:\r
190 list.append('EFI_FVB2_LOCK_STATUS')\r
191 if (value & 0x200) != 0:\r
192 list.append('EFI_FVB2_STICKY_WRITE')\r
193 if (value & 0x400) != 0:\r
194 list.append('EFI_FVB2_MEMORY_MAPPED')\r
195 if (value & 0x800) != 0:\r
196 list.append('EFI_FVB2_ERASE_POLARITY')\r
197 if (value & 0x1000) != 0:\r
198 list.append('EFI_FVB2_READ_LOCK_CAP')\r
199 if (value & 0x00002000) != 0:\r
200 list.append('EFI_FVB2_READ_LOCK_STATUS')\r
201 if (value & 0x00004000) != 0:\r
202 list.append('EFI_FVB2_WRITE_LOCK_CAP')\r
203 if (value & 0x00008000) != 0:\r
204 list.append('EFI_FVB2_WRITE_LOCK_STATUS')\r
205\r
206 if (value == 0):\r
207 list.append('EFI_FVB2_ALIGNMENT_1')\r
208 if (value & 0x001F0000) == 0x00010000:\r
209 list.append('EFI_FVB2_ALIGNMENT_2')\r
210 if (value & 0x001F0000) == 0x00020000:\r
211 list.append('EFI_FVB2_ALIGNMENT_4')\r
212 if (value & 0x001F0000) == 0x00030000:\r
213 list.append('EFI_FVB2_ALIGNMENT_8')\r
214 if (value & 0x001F0000) == 0x00040000:\r
215 list.append('EFI_FVB2_ALIGNMENT_16')\r
216 if (value & 0x001F0000) == 0x00050000:\r
217 list.append('EFI_FVB2_ALIGNMENT_32')\r
218 if (value & 0x001F0000) == 0x00060000:\r
219 list.append('EFI_FVB2_ALIGNMENT_64')\r
220 if (value & 0x001F0000) == 0x00070000:\r
221 list.append('EFI_FVB2_ALIGNMENT_128')\r
222 if (value & 0x001F0000) == 0x00080000:\r
223 list.append('EFI_FVB2_ALIGNMENT_256')\r
224 if (value & 0x001F0000) == 0x00090000:\r
225 list.append('EFI_FVB2_ALIGNMENT_512')\r
226 if (value & 0x001F0000) == 0x000A0000:\r
227 list.append('EFI_FVB2_ALIGNMENT_1K')\r
228 if (value & 0x001F0000) == 0x000B0000:\r
229 list.append('EFI_FVB2_ALIGNMENT_2K')\r
230 if (value & 0x001F0000) == 0x000C0000:\r
231 list.append('EFI_FVB2_ALIGNMENT_4K')\r
232 if (value & 0x001F0000) == 0x000D0000:\r
233 list.append('EFI_FVB2_ALIGNMENT_8K')\r
234 if (value & 0x001F0000) == 0x000E0000:\r
235 list.append('EFI_FVB2_ALIGNMENT_16K')\r
236 if (value & 0x001F0000) == 0x000F0000:\r
237 list.append('EFI_FVB2_ALIGNMENT_32K')\r
238 if (value & 0x001F0000) == 0x00100000:\r
239 list.append('EFI_FVB2_ALIGNMENT_64K')\r
240 if (value & 0x001F0000) == 0x00110000:\r
241 list.append('EFI_FVB2_ALIGNMENT_128K')\r
242 if (value & 0x001F0000) == 0x00120000:\r
243 list.append('EFI_FVB2_ALIGNMENT_256K')\r
244 if (value & 0x001F0000) == 0x00130000:\r
245 list.append('EFI_FVB2_ALIGNMENT_512K')\r
246\r
247 return list\r
248\r
249 def GetHeaderLength(self):\r
250 return list2int(self._arr.tolist()[48:50])\r
251\r
252 def Dump(self):\r
253 print 'Signature: %s' % self.GetSigunature()\r
254 print 'Attribute: 0x%X' % self.GetAttribute()\r
255 print 'Header Length: 0x%X' % self.GetHeaderLength()\r
256 print 'File system Guid: ', self.GetFileSystemGuid()\r
257 print 'Revision: 0x%X' % self.GetRevision()\r
258 print 'FvLength: 0x%X' % self.GetFvLength()\r
259\r
260 def GetFileSystemGuid(self):\r
261 list = self._arr.tolist()\r
262 return list2guid(list[16:32])\r
263\r
264 def GetRevision(self):\r
265 list = self._arr.tolist()\r
266 return int(list[55])\r
267\r
268 def GetFvLength(self):\r
269 list = self._arr.tolist()\r
270 return list2int(list[32:40])\r
271\r
272 def GetRawData(self):\r
273 return self._arr.tolist()\r
274\r
275class BlockMapEntry(BinaryItem):\r
276 def GetSize(self):\r
277 return 8\r
278\r
279 def GetNumberBlocks(self):\r
280 list = self._arr.tolist()\r
281 return list2int(list[0:4])\r
282\r
283 def GetLength(self):\r
284 list = self._arr.tolist()\r
285 return list2int(list[4:8])\r
286\r
287 def GetRawData(self):\r
288 return self._arr.tolist()\r
289\r
290 def __str__(self):\r
291 return '[BlockEntry] Number = 0x%X, length=0x%X' % (self.GetNumberBlocks(), self.GetLength())\r
292\r
293class EfiFfs(object):\r
294 FFS_HEADER_SIZE = 24\r
295\r
296 def __init__(self, parent=None):\r
297 self._header = None\r
298\r
299 # following field is for FFS in FV file.\r
300 self._parent = parent\r
301 self._offset = 0\r
302 self._sections = []\r
303\r
304 def Load(self, fd):\r
305 self._offset = align(fd.tell(), 8)\r
306\r
307 self._header = EfiFfsHeader.Read(fd, self)\r
308\r
309 if not isValidGuid(self.GetNameGuid()):\r
310 return\r
311\r
312 index = self._offset\r
313 fileend = self._offset + self.GetSize()\r
314 while (index + EfiSection.EFI_SECTION_HEADER_SIZE < fileend):\r
315 section = EfiSection(self)\r
316 section.Load(fd)\r
317 if section.GetSize() == 0 and section.GetHeader().GetType() == 0:\r
318 break\r
319 self._sections.append(section)\r
320 index = fd.tell()\r
321\r
322 # rebase file pointer to next ffs file\r
323 index = self._offset + self._header.GetFfsSize()\r
324 index = align(index, 8)\r
325 fd.seek(index)\r
326\r
327 def GetOffset(self):\r
328 return self._offset\r
329\r
330 def GetSize(self):\r
331 return self._header.GetFfsSize()\r
332\r
333 @classmethod\r
334 def Read(cls, fd, parent=None):\r
335 item = cls(parent)\r
336 item.Load(fd)\r
337 return item\r
338\r
339 def GetNameGuid(self):\r
340 return self._header.GetNameGuid()\r
341\r
342 def DumpContent(self):\r
343 list = self._content.tolist()\r
344 line = []\r
345 count = 0\r
346 for item in list:\r
347 if count < 32:\r
348 line.append('0x%X' % int(item))\r
349 count += 1\r
350 else:\r
351 print ' '.join(line)\r
352 count = 0\r
353 line = []\r
354 line.append('0x%X' % int(item))\r
355 count += 1\r
356\r
357 def GetHeader(self):\r
358 return self._header\r
359\r
360 def GetParent(self):\r
361 return self._parent\r
362\r
363 def GetSections(self):\r
364 return self._sections\r
365\r
366class EfiFfsHeader(BinaryItem):\r
367 ffs_state_map = {0x01:'EFI_FILE_HEADER_CONSTRUCTION',\r
368 0x02:'EFI_FILE_HEADER_VALID',\r
369 0x04:'EFI_FILE_DATA_VALID',\r
370 0x08:'EFI_FILE_MARKED_FOR_UPDATE',\r
371 0x10:'EFI_FILE_DELETED',\r
372 0x20:'EFI_FILE_HEADER_INVALID'}\r
373\r
374 def GetSize(self):\r
375 return 24\r
376\r
377 def GetNameGuid(self):\r
378 list = self._arr.tolist()\r
379 return list2guid(list[0:16])\r
380\r
381 def GetType(self):\r
382 list = self._arr.tolist()\r
383 return int(list[18])\r
384\r
385\r
386 def GetTypeString(self):\r
387 value = self.GetType()\r
388 if value == 0x01:\r
389 return 'EFI_FV_FILETYPE_RAW'\r
390 if value == 0x02:\r
391 return 'EFI_FV_FILETYPE_FREEFORM'\r
392 if value == 0x03:\r
393 return 'EFI_FV_FILETYPE_SECURITY_CORE'\r
394 if value == 0x04:\r
395 return 'EFI_FV_FILETYPE_PEI_CORE'\r
396 if value == 0x05:\r
397 return 'EFI_FV_FILETYPE_DXE_CORE'\r
398 if value == 0x06:\r
399 return 'EFI_FV_FILETYPE_PEIM'\r
400 if value == 0x07:\r
401 return 'EFI_FV_FILETYPE_DRIVER'\r
402 if value == 0x08:\r
403 return 'EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER'\r
404 if value == 0x09:\r
405 return 'EFI_FV_FILETYPE_APPLICATION'\r
406 if value == 0x0B:\r
407 return 'EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE'\r
408 if value == 0xc0:\r
409 return 'EFI_FV_FILETYPE_OEM_MIN'\r
410 if value == 0xdf:\r
411 return 'EFI_FV_FILETYPE_OEM_MAX'\r
412 if value == 0xe0:\r
413 return 'EFI_FV_FILETYPE_DEBUG_MIN'\r
414 if value == 0xef:\r
415 return 'EFI_FV_FILETYPE_DEBUG_MAX'\r
416 if value == 0xf0:\r
417 return 'EFI_FV_FILETYPE_FFS_PAD'\r
418 if value == 0xff:\r
419 return 'EFI_FV_FILETYPE_FFS_MAX'\r
420 return 'Unknown FFS Type'\r
421\r
422 def GetAttributes(self):\r
423 list = self._arr.tolist()\r
424 return int(list[19])\r
425\r
426 def GetFfsSize(self):\r
427 list = self._arr.tolist()\r
428 return list2int(list[20:23])\r
429\r
430 def GetState(self):\r
431 list = self._arr.tolist()\r
432 state = int(list[23])\r
433 polarity = self.GetParent().GetParent().GetHeader().GetErasePolarity()\r
434 if polarity:\r
435 state = (~state) & 0xFF\r
436 HighestBit = 0x80\r
437 while (HighestBit != 0) and (HighestBit & state) == 0:\r
438 HighestBit = HighestBit >> 1\r
439 return HighestBit\r
440\r
441 def GetStateString(self):\r
442 state = self.GetState()\r
443 if state in self.ffs_state_map.keys():\r
444 return self.ffs_state_map[state]\r
445 return 'Unknown Ffs State'\r
446\r
447 def Dump(self):\r
448 print "FFS name: ", self.GetNameGuid()\r
449 print "FFS type: ", self.GetType()\r
450 print "FFS attr: 0x%X" % self.GetAttributes()\r
451 print "FFS size: 0x%X" % self.GetFfsSize()\r
452 print "FFS state: 0x%X" % self.GetState()\r
453\r
454 def GetRawData(self):\r
455 return self._arr.tolist()\r
456\r
457\r
458class EfiSection(object):\r
459 EFI_SECTION_HEADER_SIZE = 4\r
460\r
461 def __init__(self, parent=None):\r
462 self._size = 0\r
463 self._parent = parent\r
464 self._offset = 0\r
465 self._contents = array.array('B')\r
466\r
467 def Load(self, fd):\r
468 self._offset = align(fd.tell(), 4)\r
469\r
470 self._header = EfiSectionHeader.Read(fd, self)\r
471\r
472 if self._header.GetTypeString() == "EFI_SECTION_PE32":\r
473 pefile = pe.PEFile(self)\r
474 pefile.Load(fd, self.GetContentSize())\r
475\r
476 fd.seek(self._offset)\r
477 self._contents.fromfile(fd, self.GetContentSize())\r
478\r
479 # rebase file pointer to next section\r
480 index = self._offset + self.GetSize()\r
481 index = align(index, 4)\r
482 fd.seek(index)\r
483\r
484 def GetContentSize(self):\r
485 return self.GetSize() - self.EFI_SECTION_HEADER_SIZE\r
486\r
487 def GetContent(self):\r
488 return self._contents.tolist()\r
489\r
490 def GetSize(self):\r
491 return self._header.GetSectionSize()\r
492\r
493 def GetHeader(self):\r
494 return self._header\r
495\r
496 def GetSectionOffset(self):\r
497 return self._offset + self.EFI_SECTION_HEADER_SIZE\r
498\r
499class EfiSectionHeader(BinaryItem):\r
500 section_type_map = {0x01: 'EFI_SECTION_COMPRESSION',\r
501 0x02: 'EFI_SECTION_GUID_DEFINED',\r
502 0x10: 'EFI_SECTION_PE32',\r
503 0x11: 'EFI_SECTION_PIC',\r
504 0x12: 'EFI_SECTION_TE',\r
505 0x13: 'EFI_SECTION_DXE_DEPEX',\r
506 0x14: 'EFI_SECTION_VERSION',\r
507 0x15: 'EFI_SECTION_USER_INTERFACE',\r
508 0x16: 'EFI_SECTION_COMPATIBILITY16',\r
509 0x17: 'EFI_SECTION_FIRMWARE_VOLUME_IMAGE',\r
510 0x18: 'EFI_SECTION_FREEFORM_SUBTYPE_GUID',\r
511 0x19: 'EFI_SECTION_RAW',\r
512 0x1B: 'EFI_SECTION_PEI_DEPEX'}\r
513 def GetSize(self):\r
514 return 4\r
515\r
516 def GetSectionSize(self):\r
517 list = self._arr.tolist()\r
518 return list2int(list[0:3])\r
519\r
520 def GetType(self):\r
521 list = self._arr.tolist()\r
522 return int(list[3])\r
523\r
524 def GetTypeString(self):\r
525 type = self.GetType()\r
526 if type not in self.section_type_map.keys():\r
527 return 'Unknown Section Type'\r
528 return self.section_type_map[type]\r
529\r
530 def Dump(self):\r
531 print 'size = 0x%X' % self.GetSectionSize()\r
532 print 'type = 0x%X' % self.GetType()\r
533\r
534\r
535\r
536rMapEntry = re.compile('^(\w+)[ \(\w\)]* \(BaseAddress=([0-9a-fA-F]+), EntryPoint=([0-9a-fA-F]+), GUID=([0-9a-fA-F\-]+)')\r
537class EfiFvMapFile(object):\r
538 def __init__(self):\r
539 self._mapentries = {}\r
540\r
541 def Load(self, path):\r
542 if not os.path.exists(path):\r
543 return False\r
544\r
545 try:\r
546 file = open(path, 'r')\r
547 lines = file.readlines()\r
548 file.close()\r
549 except:\r
550 return False\r
551\r
552 for line in lines:\r
553 if line[0] != ' ':\r
554 # new entry\r
555 ret = rMapEntry.match(line)\r
556 if ret != None:\r
557 name = ret.groups()[0]\r
558 baseaddr = int(ret.groups()[1], 16)\r
559 entry = int(ret.groups()[2], 16)\r
560 guidstr = '{' + ret.groups()[3] + '}'\r
561 guid = uuid.UUID(guidstr)\r
562 self._mapentries[guid] = EfiFvMapFileEntry(name, baseaddr, entry, guid)\r
563 return True\r
564\r
565 def GetEntry(self, guid):\r
566 if guid in self._mapentries.keys():\r
567 return self._mapentries[guid]\r
568 return None\r
569\r
570class EfiFvMapFileEntry(object):\r
571 def __init__(self, name, baseaddr, entry, guid):\r
572 self._name = name\r
573 self._baseaddr = baseaddr\r
574 self._entry = entry\r
575 self._guid = guid\r
576\r
577 def GetName(self):\r
578 return self._name\r
579\r
580 def GetBaseAddress(self):\r
581 return self._baseaddr\r
582\r
583 def GetEntryPoint(self):\r
584 return self._entry\r
585\r
586def list2guid(list):\r
587 val1 = list2int(list[0:4])\r
588 val2 = list2int(list[4:6])\r
589 val3 = list2int(list[6:8])\r
590 val4 = 0\r
591 for item in list[8:16]:\r
592 val4 = (val4 << 8) | int(item)\r
593\r
594 val = val1 << 12 * 8 | val2 << 10 * 8 | val3 << 8 * 8 | val4\r
595 guid = uuid.UUID(int=val)\r
596 return guid\r
597\r
598def list2int(list):\r
599 val = 0\r
600 for index in range(len(list) - 1, -1, -1):\r
601 val = (val << 8) | int(list[index])\r
602 return val\r
603\r
604def align(value, alignment):\r
605 return (value + ((alignment - value) & (alignment - 1)))\r
606\r
607gInvalidGuid = uuid.UUID(int=0xffffffffffffffffffffffffffffffff)\r
608def isValidGuid(guid):\r
609 if guid == gInvalidGuid:\r
610 return False\r
611 return True\r