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