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