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