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