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