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