1## @ FspTool.py
2#
3# Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.<BR>
4# SPDX-License-Identifier: BSD-2-Clause-Patent
5#
6##
7
8import os
9import sys
10import uuid
11import copy
12import struct
13import argparse
14from   ctypes import *
15from functools import reduce
16
17"""
18This utility supports some operations for Intel FSP 1.x/2.x image.
19It supports:
20    - Display FSP 1.x/2.x information header
21    - Split FSP 2.x image into individual FSP-T/M/S/O component
22    - Rebase FSP 1.x/2.x components to a different base address
23    - Generate FSP 1.x/2.x mapping C header file
24"""
25
26CopyRightHeaderFile = """/*
27 *
28 * Automatically generated file; DO NOT EDIT.
29 * FSP mapping file
30 *
31 */
32"""
33
34class c_uint24(Structure):
35    """Little-Endian 24-bit Unsigned Integer"""
36    _pack_   = 1
37    _fields_ = [('Data', (c_uint8 * 3))]
38
39    def __init__(self, val=0):
40        self.set_value(val)
41
42    def __str__(self, indent=0):
43        return '0x%.6x' % self.value
44
45    def __int__(self):
46        return self.get_value()
47
48    def set_value(self, val):
49        self.Data[0:3] = Val2Bytes(val, 3)
50
51    def get_value(self):
52        return Bytes2Val(self.Data[0:3])
53
54    value = property(get_value, set_value)
55
56class EFI_FIRMWARE_VOLUME_HEADER(Structure):
57    _fields_ = [
58        ('ZeroVector',           ARRAY(c_uint8, 16)),
59        ('FileSystemGuid',       ARRAY(c_uint8, 16)),
60        ('FvLength',             c_uint64),
61        ('Signature',            ARRAY(c_char, 4)),
62        ('Attributes',           c_uint32),
63        ('HeaderLength',         c_uint16),
64        ('Checksum',             c_uint16),
65        ('ExtHeaderOffset',      c_uint16),
66        ('Reserved',             c_uint8),
67        ('Revision',             c_uint8)
68        ]
69
70class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):
71    _fields_ = [
72        ('FvName',               ARRAY(c_uint8, 16)),
73        ('ExtHeaderSize',        c_uint32)
74        ]
75
76class EFI_FFS_INTEGRITY_CHECK(Structure):
77    _fields_ = [
78        ('Header',               c_uint8),
79        ('File',                 c_uint8)
80        ]
81
82class EFI_FFS_FILE_HEADER(Structure):
83    _fields_ = [
84        ('Name',                 ARRAY(c_uint8, 16)),
85        ('IntegrityCheck',       EFI_FFS_INTEGRITY_CHECK),
86        ('Type',                 c_uint8),
87        ('Attributes',           c_uint8),
88        ('Size',                 c_uint24),
89        ('State',                c_uint8)
90        ]
91
92class EFI_COMMON_SECTION_HEADER(Structure):
93    _fields_ = [
94        ('Size',                 c_uint24),
95        ('Type',                 c_uint8)
96        ]
97
98class FSP_COMMON_HEADER(Structure):
99     _fields_ = [
100        ('Signature',            ARRAY(c_char, 4)),
101        ('HeaderLength',         c_uint32)
102        ]
103
104class FSP_INFORMATION_HEADER(Structure):
105     _fields_ = [
106        ('Signature',            ARRAY(c_char, 4)),
107        ('HeaderLength',         c_uint32),
108        ('Reserved1',            c_uint16),
109        ('SpecVersion',          c_uint8),
110        ('HeaderRevision',       c_uint8),
111        ('ImageRevision',        c_uint32),
112        ('ImageId',              ARRAY(c_char, 8)),
113        ('ImageSize',            c_uint32),
114        ('ImageBase',            c_uint32),
115        ('ImageAttribute',       c_uint16),
116        ('ComponentAttribute',   c_uint16),
117        ('CfgRegionOffset',      c_uint32),
118        ('CfgRegionSize',        c_uint32),
119        ('Reserved2',            c_uint32),
120        ('TempRamInitEntryOffset',     c_uint32),
121        ('Reserved3',                  c_uint32),
122        ('NotifyPhaseEntryOffset',     c_uint32),
123        ('FspMemoryInitEntryOffset',   c_uint32),
124        ('TempRamExitEntryOffset',     c_uint32),
125        ('FspSiliconInitEntryOffset',  c_uint32)
126    ]
127
128class FSP_PATCH_TABLE(Structure):
129    _fields_ = [
130        ('Signature',            ARRAY(c_char, 4)),
131        ('HeaderLength',         c_uint16),
132        ('HeaderRevision',       c_uint8),
133        ('Reserved',             c_uint8),
134        ('PatchEntryNum',        c_uint32)
135        ]
136
137class EFI_IMAGE_DATA_DIRECTORY(Structure):
138    _fields_ = [
139        ('VirtualAddress',       c_uint32),
140        ('Size',                 c_uint32)
141        ]
142
143class EFI_TE_IMAGE_HEADER(Structure):
144    _fields_ = [
145        ('Signature',            ARRAY(c_char, 2)),
146        ('Machine',              c_uint16),
147        ('NumberOfSections',     c_uint8),
148        ('Subsystem',            c_uint8),
149        ('StrippedSize',         c_uint16),
150        ('AddressOfEntryPoint',  c_uint32),
151        ('BaseOfCode',           c_uint32),
152        ('ImageBase',            c_uint64),
153        ('DataDirectoryBaseReloc',  EFI_IMAGE_DATA_DIRECTORY),
154        ('DataDirectoryDebug',      EFI_IMAGE_DATA_DIRECTORY)
155        ]
156
157class EFI_IMAGE_DOS_HEADER(Structure):
158    _fields_ = [
159        ('e_magic',              c_uint16),
160        ('e_cblp',               c_uint16),
161        ('e_cp',                 c_uint16),
162        ('e_crlc',               c_uint16),
163        ('e_cparhdr',            c_uint16),
164        ('e_minalloc',           c_uint16),
165        ('e_maxalloc',           c_uint16),
166        ('e_ss',                 c_uint16),
167        ('e_sp',                 c_uint16),
168        ('e_csum',               c_uint16),
169        ('e_ip',                 c_uint16),
170        ('e_cs',                 c_uint16),
171        ('e_lfarlc',             c_uint16),
172        ('e_ovno',               c_uint16),
173        ('e_res',                ARRAY(c_uint16, 4)),
174        ('e_oemid',              c_uint16),
175        ('e_oeminfo',            c_uint16),
176        ('e_res2',               ARRAY(c_uint16, 10)),
177        ('e_lfanew',             c_uint16)
178        ]
179
180class EFI_IMAGE_FILE_HEADER(Structure):
181    _fields_ = [
182        ('Machine',               c_uint16),
183        ('NumberOfSections',      c_uint16),
184        ('TimeDateStamp',         c_uint32),
185        ('PointerToSymbolTable',  c_uint32),
186        ('NumberOfSymbols',       c_uint32),
187        ('SizeOfOptionalHeader',  c_uint16),
188        ('Characteristics',       c_uint16)
189        ]
190
191class PE_RELOC_BLOCK_HEADER(Structure):
192    _fields_ = [
193        ('PageRVA',              c_uint32),
194        ('BlockSize',            c_uint32)
195        ]
196
197class EFI_IMAGE_OPTIONAL_HEADER32(Structure):
198    _fields_ = [
199        ('Magic',                         c_uint16),
200        ('MajorLinkerVersion',            c_uint8),
201        ('MinorLinkerVersion',            c_uint8),
202        ('SizeOfCode',                    c_uint32),
203        ('SizeOfInitializedData',         c_uint32),
204        ('SizeOfUninitializedData',       c_uint32),
205        ('AddressOfEntryPoint',           c_uint32),
206        ('BaseOfCode',                    c_uint32),
207        ('BaseOfData',                    c_uint32),
208        ('ImageBase',                     c_uint32),
209        ('SectionAlignment',              c_uint32),
210        ('FileAlignment',                 c_uint32),
211        ('MajorOperatingSystemVersion',   c_uint16),
212        ('MinorOperatingSystemVersion',   c_uint16),
213        ('MajorImageVersion',             c_uint16),
214        ('MinorImageVersion',             c_uint16),
215        ('MajorSubsystemVersion',         c_uint16),
216        ('MinorSubsystemVersion',         c_uint16),
217        ('Win32VersionValue',             c_uint32),
218        ('SizeOfImage',                   c_uint32),
219        ('SizeOfHeaders',                 c_uint32),
220        ('CheckSum'     ,                 c_uint32),
221        ('Subsystem',                     c_uint16),
222        ('DllCharacteristics',            c_uint16),
223        ('SizeOfStackReserve',            c_uint32),
224        ('SizeOfStackCommit' ,            c_uint32),
225        ('SizeOfHeapReserve',             c_uint32),
226        ('SizeOfHeapCommit' ,             c_uint32),
227        ('LoaderFlags'     ,              c_uint32),
228        ('NumberOfRvaAndSizes',           c_uint32),
229        ('DataDirectory',                 ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
230        ]
231
232class EFI_IMAGE_OPTIONAL_HEADER32_PLUS(Structure):
233    _fields_ = [
234        ('Magic',                         c_uint16),
235        ('MajorLinkerVersion',            c_uint8),
236        ('MinorLinkerVersion',            c_uint8),
237        ('SizeOfCode',                    c_uint32),
238        ('SizeOfInitializedData',         c_uint32),
239        ('SizeOfUninitializedData',       c_uint32),
240        ('AddressOfEntryPoint',           c_uint32),
241        ('BaseOfCode',                    c_uint32),
242        ('ImageBase',                     c_uint64),
243        ('SectionAlignment',              c_uint32),
244        ('FileAlignment',                 c_uint32),
245        ('MajorOperatingSystemVersion',   c_uint16),
246        ('MinorOperatingSystemVersion',   c_uint16),
247        ('MajorImageVersion',             c_uint16),
248        ('MinorImageVersion',             c_uint16),
249        ('MajorSubsystemVersion',         c_uint16),
250        ('MinorSubsystemVersion',         c_uint16),
251        ('Win32VersionValue',             c_uint32),
252        ('SizeOfImage',                   c_uint32),
253        ('SizeOfHeaders',                 c_uint32),
254        ('CheckSum'     ,                 c_uint32),
255        ('Subsystem',                     c_uint16),
256        ('DllCharacteristics',            c_uint16),
257        ('SizeOfStackReserve',            c_uint64),
258        ('SizeOfStackCommit' ,            c_uint64),
259        ('SizeOfHeapReserve',             c_uint64),
260        ('SizeOfHeapCommit' ,             c_uint64),
261        ('LoaderFlags'     ,              c_uint32),
262        ('NumberOfRvaAndSizes',           c_uint32),
263        ('DataDirectory',                 ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
264        ]
265
266class EFI_IMAGE_OPTIONAL_HEADER(Union):
267    _fields_ = [
268        ('PeOptHdr',             EFI_IMAGE_OPTIONAL_HEADER32),
269        ('PePlusOptHdr',         EFI_IMAGE_OPTIONAL_HEADER32_PLUS)
270        ]
271
272class EFI_IMAGE_NT_HEADERS32(Structure):
273    _fields_ = [
274        ('Signature',            c_uint32),
275        ('FileHeader',           EFI_IMAGE_FILE_HEADER),
276        ('OptionalHeader',       EFI_IMAGE_OPTIONAL_HEADER)
277        ]
278
279
280class EFI_IMAGE_DIRECTORY_ENTRY:
281    EXPORT                     = 0
282    IMPORT                     = 1
283    RESOURCE                   = 2
284    EXCEPTION                  = 3
285    SECURITY                   = 4
286    BASERELOC                  = 5
287    DEBUG                      = 6
288    COPYRIGHT                  = 7
289    GLOBALPTR                  = 8
290    TLS                        = 9
291    LOAD_CONFIG                = 10
292
293class EFI_FV_FILETYPE:
294    ALL                        = 0x00
295    RAW                        = 0x01
296    FREEFORM                   = 0x02
297    SECURITY_CORE              = 0x03
298    PEI_CORE                   = 0x04
299    DXE_CORE                   = 0x05
300    PEIM                       = 0x06
301    DRIVER                     = 0x07
302    COMBINED_PEIM_DRIVER       = 0x08
303    APPLICATION                = 0x09
304    SMM                        = 0x0a
305    FIRMWARE_VOLUME_IMAGE      = 0x0b
306    COMBINED_SMM_DXE           = 0x0c
307    SMM_CORE                   = 0x0d
308    OEM_MIN                    = 0xc0
309    OEM_MAX                    = 0xdf
310    DEBUG_MIN                  = 0xe0
311    DEBUG_MAX                  = 0xef
312    FFS_MIN                    = 0xf0
313    FFS_MAX                    = 0xff
314    FFS_PAD                    = 0xf0
315
316class EFI_SECTION_TYPE:
317    """Enumeration of all valid firmware file section types."""
318    ALL                        = 0x00
319    COMPRESSION                = 0x01
320    GUID_DEFINED               = 0x02
321    DISPOSABLE                 = 0x03
322    PE32                       = 0x10
323    PIC                        = 0x11
324    TE                         = 0x12
325    DXE_DEPEX                  = 0x13
326    VERSION                    = 0x14
327    USER_INTERFACE             = 0x15
328    COMPATIBILITY16            = 0x16
329    FIRMWARE_VOLUME_IMAGE      = 0x17
330    FREEFORM_SUBTYPE_GUID      = 0x18
331    RAW                        = 0x19
332    PEI_DEPEX                  = 0x1b
333    SMM_DEPEX                  = 0x1c
334
335def AlignPtr (offset, alignment = 8):
336    return (offset + alignment - 1) & ~(alignment - 1)
337
338def Bytes2Val (bytes):
339    return reduce(lambda x,y: (x<<8)|y,  bytes[::-1] )
340
341def Val2Bytes (value, blen):
342    return [(value>>(i*8) & 0xff) for i in range(blen)]
343
344def IsIntegerType (val):
345    if sys.version_info[0] < 3:
346        if type(val) in (int, long):
347            return True
348    else:
349        if type(val) is int:
350            return True
351    return False
352
353def IsStrType (val):
354    if sys.version_info[0] < 3:
355        if type(val) is str:
356            return True
357    else:
358        if type(val) is bytes:
359            return True
360    return False
361
362def HandleNameStr (val):
363    if sys.version_info[0] < 3:
364        rep = "0x%X ('%s')" % (Bytes2Val (bytearray (val)), val)
365    else:
366        rep = "0x%X ('%s')" % (Bytes2Val (bytearray (val)), str (val, 'utf-8'))
367    return rep
368
369def OutputStruct (obj, indent = 0, plen = 0):
370    if indent:
371        body = ''
372    else:
373        body = ('  ' * indent + '<%s>:\n') % obj.__class__.__name__
374
375    if plen == 0:
376        plen = sizeof(obj)
377
378    max_key_len = 26
379    pstr = ('  ' * (indent + 1) + '{0:<%d} = {1}\n') % max_key_len
380
381    for field in obj._fields_:
382        key = field[0]
383        val = getattr(obj, key)
384        rep = ''
385        if not isinstance(val, c_uint24) and isinstance(val, Structure):
386            body += pstr.format(key, val.__class__.__name__)
387            body += OutputStruct (val, indent + 1)
388            plen -= sizeof(val)
389        else:
390            if IsStrType (val):
391                rep = HandleNameStr (val)
392            elif IsIntegerType (val):
393                rep = '0x%X' % val
394            elif isinstance(val, c_uint24):
395                rep = '0x%X' % val.get_value()
396            elif 'c_ubyte_Array' in str(type(val)):
397                if sizeof(val) == 16:
398                    if sys.version_info[0] < 3:
399                        rep = str(bytearray(val))
400                    else:
401                        rep = bytes(val)
402                    rep = str(uuid.UUID(bytes_le = rep)).upper()
403                else:
404                    res = ['0x%02X'%i for i in bytearray(val)]
405                    rep = '[%s]' % (','.join(res))
406            else:
407                rep = str(val)
408            plen -= sizeof(field[1])
409            body += pstr.format(key, rep)
410        if plen <= 0:
411            break
412    return body
413
414class Section:
415    def __init__(self, offset, secdata):
416        self.SecHdr   = EFI_COMMON_SECTION_HEADER.from_buffer (secdata, 0)
417        self.SecData  = secdata[0:int(self.SecHdr.Size)]
418        self.Offset   = offset
419
420class FirmwareFile:
421    def __init__(self, offset, filedata):
422        self.FfsHdr   = EFI_FFS_FILE_HEADER.from_buffer (filedata, 0)
423        self.FfsData  = filedata[0:int(self.FfsHdr.Size)]
424        self.Offset   = offset
425        self.SecList  = []
426
427    def ParseFfs(self):
428        ffssize = len(self.FfsData)
429        offset  = sizeof(self.FfsHdr)
430        if self.FfsHdr.Name != '\xff' * 16:
431            while offset < (ffssize - sizeof (EFI_COMMON_SECTION_HEADER)):
432                sechdr = EFI_COMMON_SECTION_HEADER.from_buffer (self.FfsData, offset)
433                sec = Section (offset, self.FfsData[offset:offset + int(sechdr.Size)])
434                self.SecList.append(sec)
435                offset += int(sechdr.Size)
436                offset  = AlignPtr(offset, 4)
437
438class FirmwareVolume:
439    def __init__(self, offset, fvdata):
440        self.FvHdr    = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (fvdata, 0)
441        self.FvData   = fvdata[0 : self.FvHdr.FvLength]
442        self.Offset   = offset
443        if self.FvHdr.ExtHeaderOffset > 0:
444            self.FvExtHdr = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer (self.FvData, self.FvHdr.ExtHeaderOffset)
445        else:
446            self.FvExtHdr = None
447        self.FfsList  = []
448
449    def ParseFv(self):
450        fvsize = len(self.FvData)
451        if self.FvExtHdr:
452            offset = self.FvHdr.ExtHeaderOffset + self.FvExtHdr.ExtHeaderSize
453        else:
454            offset = self.FvHdr.HeaderLength
455        offset = AlignPtr(offset)
456        while offset < (fvsize - sizeof (EFI_FFS_FILE_HEADER)):
457            ffshdr = EFI_FFS_FILE_HEADER.from_buffer (self.FvData, offset)
458            if (ffshdr.Name == '\xff' * 16) and (int(ffshdr.Size) == 0xFFFFFF):
459                offset = fvsize
460            else:
461                ffs = FirmwareFile (offset, self.FvData[offset:offset + int(ffshdr.Size)])
462                ffs.ParseFfs()
463                self.FfsList.append(ffs)
464                offset += int(ffshdr.Size)
465                offset = AlignPtr(offset)
466
467class FspImage:
468    def __init__(self, offset, fih, fihoff, patch):
469        self.Fih       = fih
470        self.FihOffset = fihoff
471        self.Offset    = offset
472        self.FvIdxList = []
473        self.Type      = "XTMSXXXXOXXXXXXX"[(fih.ComponentAttribute >> 12) & 0x0F]
474        self.PatchList = patch
475        self.PatchList.append(fihoff + 0x1C)
476
477    def AppendFv(self, FvIdx):
478        self.FvIdxList.append(FvIdx)
479
480    def Patch(self, delta, fdbin):
481        count   = 0
482        applied = 0
483        for idx, patch in enumerate(self.PatchList):
484            ptype = (patch>>24) & 0x0F
485            if ptype not in [0x00, 0x0F]:
486                raise Exception('ERROR: Invalid patch type %d !' % ptype)
487            if patch & 0x80000000:
488                patch = self.Fih.ImageSize - (0x1000000 - (patch & 0xFFFFFF))
489            else:
490                patch = patch & 0xFFFFFF
491            if (patch < self.Fih.ImageSize) and (patch + sizeof(c_uint32) <= self.Fih.ImageSize):
492                offset = patch + self.Offset
493                value  = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)])
494                value += delta
495                fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(value, sizeof(c_uint32))
496                applied += 1
497            count += 1
498        # Don't count the FSP base address patch entry appended at the end
499        if count != 0:
500            count   -= 1
501            applied -= 1
502        return (count, applied)
503
504class FirmwareDevice:
505    def __init__(self, offset, fdfile):
506        self.FvList  = []
507        self.FspList = []
508        self.FdFile = fdfile
509        self.Offset = 0
510        hfsp = open (self.FdFile, 'rb')
511        self.FdData = bytearray(hfsp.read())
512        hfsp.close()
513
514    def ParseFd(self):
515        offset = 0
516        fdsize = len(self.FdData)
517        self.FvList  = []
518        while offset < (fdsize - sizeof (EFI_FIRMWARE_VOLUME_HEADER)):
519            fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FdData, offset)
520            if b'_FVH' != fvh.Signature:
521                raise Exception("ERROR: Invalid FV header !")
522            fv = FirmwareVolume (offset, self.FdData[offset:offset + fvh.FvLength])
523            fv.ParseFv ()
524            self.FvList.append(fv)
525            offset += fv.FvHdr.FvLength
526
527    def CheckFsp (self):
528        if len(self.FspList) == 0:
529            return
530
531        fih = None
532        for fsp in self.FspList:
533            if not fih:
534                fih = fsp.Fih
535            else:
536                newfih = fsp.Fih
537                if (newfih.ImageId != fih.ImageId) or (newfih.ImageRevision != fih.ImageRevision):
538                    raise Exception("ERROR: Inconsistent FSP ImageId or ImageRevision detected !")
539
540    def ParseFsp(self):
541        flen = 0
542        for idx, fv in enumerate(self.FvList):
543            # Check if this FV contains FSP header
544            if flen == 0:
545                if len(fv.FfsList) == 0:
546                    continue
547                ffs = fv.FfsList[0]
548                if len(ffs.SecList) == 0:
549                    continue
550                sec = ffs.SecList[0]
551                if sec.SecHdr.Type != EFI_SECTION_TYPE.RAW:
552                    continue
553                fihoffset = ffs.Offset + sec.Offset + sizeof(sec.SecHdr)
554                fspoffset = fv.Offset
555                offset    = fspoffset + fihoffset
556                fih = FSP_INFORMATION_HEADER.from_buffer (self.FdData, offset)
557                if b'FSPH' != fih.Signature:
558                    continue
559
560                offset += fih.HeaderLength
561                offset = AlignPtr(offset, 4)
562                plist  = []
563                while True:
564                    fch = FSP_COMMON_HEADER.from_buffer (self.FdData, offset)
565                    if b'FSPP' != fch.Signature:
566                        offset += fch.HeaderLength
567                        offset = AlignPtr(offset, 4)
568                    else:
569                        fspp = FSP_PATCH_TABLE.from_buffer (self.FdData, offset)
570                        offset += sizeof(fspp)
571                        pdata  = (c_uint32 * fspp.PatchEntryNum).from_buffer(self.FdData, offset)
572                        plist  = list(pdata)
573                        break
574
575                fsp  = FspImage (fspoffset, fih, fihoffset, plist)
576                fsp.AppendFv (idx)
577                self.FspList.append(fsp)
578                flen = fsp.Fih.ImageSize - fv.FvHdr.FvLength
579            else:
580                fsp.AppendFv (idx)
581                flen -= fv.FvHdr.FvLength
582                if flen < 0:
583                    raise Exception("ERROR: Incorrect FV size in image !")
584        self.CheckFsp ()
585
586class PeTeImage:
587    def __init__(self, offset, data):
588        self.Offset    = offset
589        tehdr          = EFI_TE_IMAGE_HEADER.from_buffer (data, 0)
590        if   tehdr.Signature == b'VZ': # TE image
591            self.TeHdr   = tehdr
592        elif tehdr.Signature == b'MZ': # PE image
593            self.TeHdr   = None
594            self.DosHdr  = EFI_IMAGE_DOS_HEADER.from_buffer (data, 0)
595            self.PeHdr   = EFI_IMAGE_NT_HEADERS32.from_buffer (data, self.DosHdr.e_lfanew)
596            if self.PeHdr.Signature != 0x4550:
597                raise Exception("ERROR: Invalid PE32 header !")
598            if self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b: # PE32 image
599                if self.PeHdr.FileHeader.SizeOfOptionalHeader < EFI_IMAGE_OPTIONAL_HEADER32.DataDirectory.offset:
600                    raise Exception("ERROR: Unsupported PE32 image !")
601                if self.PeHdr.OptionalHeader.PeOptHdr.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC:
602                    raise Exception("ERROR: No relocation information available !")
603            elif self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x20b: # PE32+ image
604                if self.PeHdr.FileHeader.SizeOfOptionalHeader < EFI_IMAGE_OPTIONAL_HEADER32_PLUS.DataDirectory.offset:
605                    raise Exception("ERROR: Unsupported PE32+ image !")
606                if self.PeHdr.OptionalHeader.PePlusOptHdr.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC:
607                    raise Exception("ERROR: No relocation information available !")
608            else:
609                raise Exception("ERROR: Invalid PE32 optional header !")
610        self.Offset    = offset
611        self.Data      = data
612        self.RelocList = []
613
614    def IsTeImage(self):
615        return  self.TeHdr is not None
616
617    def ParseReloc(self):
618        if self.IsTeImage():
619            rsize   = self.TeHdr.DataDirectoryBaseReloc.Size
620            roffset = sizeof(self.TeHdr) - self.TeHdr.StrippedSize + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress
621        else:
622            # Assuming PE32 image type (self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b)
623            rsize   = self.PeHdr.OptionalHeader.PeOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].Size
624            roffset = self.PeHdr.OptionalHeader.PeOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].VirtualAddress
625            if self.PeHdr.OptionalHeader.PePlusOptHdr.Magic == 0x20b: # PE32+ image
626                rsize   = self.PeHdr.OptionalHeader.PePlusOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].Size
627                roffset = self.PeHdr.OptionalHeader.PePlusOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].VirtualAddress
628
629        alignment = 4
630        offset = roffset
631        while offset < roffset + rsize:
632            offset = AlignPtr(offset, 4)
633            blkhdr = PE_RELOC_BLOCK_HEADER.from_buffer(self.Data, offset)
634            offset += sizeof(blkhdr)
635            # Read relocation type,offset pairs
636            rlen  = blkhdr.BlockSize - sizeof(PE_RELOC_BLOCK_HEADER)
637            rnum  = int (rlen/sizeof(c_uint16))
638            rdata = (c_uint16 * rnum).from_buffer(self.Data, offset)
639            for each in rdata:
640                roff  = each & 0xfff
641                rtype = each >> 12
642                if rtype == 0: # IMAGE_REL_BASED_ABSOLUTE:
643                    continue
644                if ((rtype != 3) and (rtype != 10)): # IMAGE_REL_BASED_HIGHLOW and IMAGE_REL_BASED_DIR64
645                    raise Exception("ERROR: Unsupported relocation type %d!" % rtype)
646                # Calculate the offset of the relocation
647                aoff  = blkhdr.PageRVA + roff
648                if self.IsTeImage():
649                    aoff += sizeof(self.TeHdr) - self.TeHdr.StrippedSize
650                self.RelocList.append((rtype, aoff))
651            offset += sizeof(rdata)
652
653    def Rebase(self, delta, fdbin):
654        count = 0
655        if delta == 0:
656            return count
657
658        for (rtype, roff) in self.RelocList:
659            if rtype == 3: # IMAGE_REL_BASED_HIGHLOW
660                offset = roff + self.Offset
661                value  = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)])
662                value += delta
663                fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(value, sizeof(c_uint32))
664                count += 1
665            elif rtype == 10: # IMAGE_REL_BASED_DIR64
666                offset = roff + self.Offset
667                value  = Bytes2Val(fdbin[offset:offset+sizeof(c_uint64)])
668                value += delta
669                fdbin[offset:offset+sizeof(c_uint64)] = Val2Bytes(value, sizeof(c_uint64))
670                count += 1
671            else:
672                raise Exception('ERROR: Unknown relocation type %d !' % rtype)
673
674        if self.IsTeImage():
675            offset  = self.Offset + EFI_TE_IMAGE_HEADER.ImageBase.offset
676            size    = EFI_TE_IMAGE_HEADER.ImageBase.size
677        else:
678            offset  = self.Offset + self.DosHdr.e_lfanew
679            offset += EFI_IMAGE_NT_HEADERS32.OptionalHeader.offset
680            offset += EFI_IMAGE_OPTIONAL_HEADER32.ImageBase.offset
681            size    = EFI_IMAGE_OPTIONAL_HEADER32.ImageBase.size
682
683        value  = Bytes2Val(fdbin[offset:offset+size]) + delta
684        fdbin[offset:offset+size] = Val2Bytes(value, size)
685
686        return count
687
688def ShowFspInfo (fspfile):
689    fd = FirmwareDevice(0, fspfile)
690    fd.ParseFd  ()
691    fd.ParseFsp ()
692
693    print ("\nFound the following %d Firmware Volumes in FSP binary:" % (len(fd.FvList)))
694    for idx, fv in enumerate(fd.FvList):
695        name = fv.FvExtHdr.FvName
696        if not name:
697            name = '\xff' * 16
698        else:
699            if sys.version_info[0] < 3:
700                name = str(bytearray(name))
701            else:
702                name = bytes(name)
703        guid = uuid.UUID(bytes_le = name)
704        print ("FV%d:" % idx)
705        print ("  GUID   : %s" % str(guid).upper())
706        print ("  Offset : 0x%08X" %  fv.Offset)
707        print ("  Length : 0x%08X" % fv.FvHdr.FvLength)
708    print ("\n")
709
710    for fsp in fd.FspList:
711        fvlist = map(lambda x : 'FV%d' % x, fsp.FvIdxList)
712        print ("FSP_%s contains %s" % (fsp.Type, ','.join(fvlist)))
713        print ("%s" % (OutputStruct(fsp.Fih, 0, fsp.Fih.HeaderLength)))
714
715def GenFspHdr (fspfile, outdir, hfile):
716    fd = FirmwareDevice(0, fspfile)
717    fd.ParseFd  ()
718    fd.ParseFsp ()
719
720    if not hfile:
721        hfile = os.path.splitext(os.path.basename(fspfile))[0] + '.h'
722    fspname, ext = os.path.splitext(os.path.basename(hfile))
723    filename = os.path.join(outdir, fspname + ext)
724    hfsp   = open(filename, 'w')
725    hfsp.write ('%s\n\n' % CopyRightHeaderFile)
726
727    firstfv = True
728    for fsp in fd.FspList:
729        fih = fsp.Fih
730        if firstfv:
731            if sys.version_info[0] < 3:
732                hfsp.write("#define  FSP_IMAGE_ID    0x%016X    /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), fih.ImageId))
733            else:
734                hfsp.write("#define  FSP_IMAGE_ID    0x%016X    /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), str (fih.ImageId, 'utf-8')))
735            hfsp.write("#define  FSP_IMAGE_REV   0x%08X \n\n" % fih.ImageRevision)
736            firstfv = False
737        fv = fd.FvList[fsp.FvIdxList[0]]
738        hfsp.write ('#define  FSP%s_BASE       0x%08X\n'   % (fsp.Type, fih.ImageBase))
739        hfsp.write ('#define  FSP%s_OFFSET     0x%08X\n'   % (fsp.Type, fv.Offset))
740        hfsp.write ('#define  FSP%s_LENGTH     0x%08X\n\n' % (fsp.Type, fih.ImageSize))
741
742    hfsp.close()
743
744def SplitFspBin (fspfile, outdir, nametemplate):
745    fd = FirmwareDevice(0, fspfile)
746    fd.ParseFd  ()
747    fd.ParseFsp ()
748
749    for fsp in fd.FspList:
750        if fsp.Fih.HeaderRevision < 3:
751            raise Exception("ERROR: FSP 1.x is not supported by the split command !")
752        ftype = fsp.Type
753        if not nametemplate:
754            nametemplate = fspfile
755        fspname, ext = os.path.splitext(os.path.basename(nametemplate))
756        filename = os.path.join(outdir, fspname + '_' + fsp.Type + ext)
757        hfsp = open(filename, 'wb')
758        print ("Create FSP component file '%s'" % filename)
759        for fvidx in fsp.FvIdxList:
760            fv = fd.FvList[fvidx]
761            hfsp.write(fv.FvData)
762        hfsp.close()
763
764def RebaseFspBin (FspBinary, FspComponent, FspBase, OutputDir, OutputFile):
765    fd = FirmwareDevice(0, FspBinary)
766    fd.ParseFd  ()
767    fd.ParseFsp ()
768
769    numcomp  = len(FspComponent)
770    baselist = FspBase
771    if numcomp != len(baselist):
772        print ("ERROR: Required number of base does not match number of FSP component !")
773        return
774
775    newfspbin = fd.FdData[:]
776
777    for idx, fspcomp in enumerate(FspComponent):
778
779        found = False
780        for fsp in fd.FspList:
781            # Is this FSP 1.x single binary?
782            if fsp.Fih.HeaderRevision < 3:
783                found = True
784                ftype = 'X'
785                break
786            ftype = fsp.Type.lower()
787            if ftype == fspcomp:
788                found = True
789                break
790
791        if not found:
792            print ("ERROR: Could not find FSP_%c component to rebase !" % fspcomp.upper())
793            return
794
795        fspbase = baselist[idx]
796        if fspbase.startswith('0x'):
797            newbase = int(fspbase, 16)
798        else:
799            newbase = int(fspbase)
800        oldbase = fsp.Fih.ImageBase
801        delta = newbase - oldbase
802        print ("Rebase FSP-%c from 0x%08X to 0x%08X:" % (ftype.upper(),oldbase,newbase))
803
804        imglist = []
805        for fvidx in fsp.FvIdxList:
806            fv = fd.FvList[fvidx]
807            for ffs in fv.FfsList:
808                for sec in ffs.SecList:
809                    if sec.SecHdr.Type in [EFI_SECTION_TYPE.TE, EFI_SECTION_TYPE.PE32]:   # TE or PE32
810                        offset = fd.Offset + fv.Offset + ffs.Offset + sec.Offset + sizeof(sec.SecHdr)
811                        imglist.append ((offset, len(sec.SecData) - sizeof(sec.SecHdr)))
812
813        fcount  = 0
814        pcount  = 0
815        for (offset, length) in imglist:
816            img = PeTeImage(offset, fd.FdData[offset:offset + length])
817            img.ParseReloc()
818            pcount += img.Rebase(delta, newfspbin)
819            fcount += 1
820
821        print ("  Patched %d entries in %d TE/PE32 images." % (pcount, fcount))
822
823        (count, applied) = fsp.Patch(delta, newfspbin)
824        print ("  Patched %d entries using FSP patch table." % applied)
825        if count != applied:
826            print ("  %d invalid entries are ignored !" % (count - applied))
827
828    if OutputFile == '':
829        filename = os.path.basename(FspBinary)
830        base, ext  = os.path.splitext(filename)
831        OutputFile = base + "_%08X" % newbase + ext
832
833    fspname, ext = os.path.splitext(os.path.basename(OutputFile))
834    filename = os.path.join(OutputDir, fspname + ext)
835    fd = open(filename, "wb")
836    fd.write(newfspbin)
837    fd.close()
838
839def main ():
840    parser     = argparse.ArgumentParser()
841    subparsers = parser.add_subparsers(title='commands', dest="which")
842
843    parser_rebase  = subparsers.add_parser('rebase',  help='rebase a FSP into a new base address')
844    parser_rebase.set_defaults(which='rebase')
845    parser_rebase.add_argument('-f',  '--fspbin' , dest='FspBinary',  type=str, help='FSP binary file path', required = True)
846    parser_rebase.add_argument('-c',  '--fspcomp', choices=['t','m','s','o'],  nargs='+', dest='FspComponent', type=str, help='FSP component to rebase', default = "['t']", required = True)
847    parser_rebase.add_argument('-b',  '--newbase', dest='FspBase', nargs='+', type=str, help='Rebased FSP binary file name', default = '', required = True)
848    parser_rebase.add_argument('-o',  '--outdir' , dest='OutputDir',  type=str, help='Output directory path', default = '.')
849    parser_rebase.add_argument('-n',  '--outfile', dest='OutputFile', type=str, help='Rebased FSP binary file name', default = '')
850
851    parser_split  = subparsers.add_parser('split',  help='split a FSP into multiple components')
852    parser_split.set_defaults(which='split')
853    parser_split.add_argument('-f',  '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
854    parser_split.add_argument('-o',  '--outdir' , dest='OutputDir', type=str, help='Output directory path',   default = '.')
855    parser_split.add_argument('-n',  '--nametpl', dest='NameTemplate', type=str, help='Output name template', default = '')
856
857    parser_genhdr = subparsers.add_parser('genhdr',  help='generate a header file for FSP binary')
858    parser_genhdr.set_defaults(which='genhdr')
859    parser_genhdr.add_argument('-f',  '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
860    parser_genhdr.add_argument('-o',  '--outdir' , dest='OutputDir', type=str, help='Output directory path',   default = '.')
861    parser_genhdr.add_argument('-n',  '--hfile',   dest='HFileName', type=str, help='Output header file name', default = '')
862
863    parser_info = subparsers.add_parser('info',  help='display FSP information')
864    parser_info.set_defaults(which='info')
865    parser_info.add_argument('-f',  '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
866
867    args = parser.parse_args()
868    if args.which in ['rebase', 'split', 'genhdr', 'info']:
869        if not os.path.exists(args.FspBinary):
870            raise Exception ("ERROR: Could not locate FSP binary file '%s' !" % args.FspBinary)
871        if hasattr(args, 'OutputDir') and not os.path.exists(args.OutputDir):
872            raise Exception ("ERROR: Invalid output directory '%s' !" % args.OutputDir)
873
874    if args.which == 'rebase':
875        RebaseFspBin (args.FspBinary, args.FspComponent, args.FspBase, args.OutputDir, args.OutputFile)
876    elif args.which == 'split':
877        SplitFspBin (args.FspBinary, args.OutputDir, args.NameTemplate)
878    elif args.which == 'genhdr':
879        GenFspHdr (args.FspBinary, args.OutputDir, args.HFileName)
880    elif args.which == 'info':
881        ShowFspInfo (args.FspBinary)
882    else:
883        parser.print_help()
884
885    return 0
886
887if __name__ == '__main__':
888    sys.exit(main())
889