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