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