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