1## @file 2# 3# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 4# 5# SPDX-License-Identifier: BSD-2-Clause-Patent 6# 7 8from __future__ import print_function 9import array 10import uuid 11import re 12import os 13import logging 14import core.pe as pe 15 16def GetLogger(): 17 return logging.getLogger('EFI Binary File') 18 19class EFIBinaryError(Exception): 20 def __init__(self, message): 21 Exception.__init__(self) 22 self._message = message 23 24 def GetMessage(self): 25 return self._message 26 27class EfiFd(object): 28 EFI_FV_HEADER_SIZE = 0x48 29 30 def __init__(self): 31 self._fvs = [] 32 33 def Load(self, fd, size): 34 index = fd.tell() 35 while (index + self.EFI_FV_HEADER_SIZE < size): 36 fv = EfiFv(self) 37 fv.Load(fd) 38 self._fvs.append(fv) 39 index += fv.GetHeader().GetFvLength() 40 index = align(index, 8) 41 fd.seek(index) 42 43 def GetFvs(self): 44 return self._fvs 45 46class EfiFv(object): 47 FILE_SYSTEM_GUID = uuid.UUID('{8c8ce578-8a3d-4f1c-9935-896185c32dd3}') 48 49 def __init__(self, parent=None): 50 self._size = 0 51 self._filename = None 52 self._fvheader = None 53 self._blockentries = [] 54 self._ffs = [] 55 56 # following field is for FV in FD 57 self._parent = parent 58 self._offset = 0 59 self._raw = array.array('B') 60 61 def Load(self, fd): 62 self._offset = fd.tell() 63 self._filename = fd.name 64 65 # get file header 66 self._fvheader = EfiFirmwareVolumeHeader.Read(fd) 67 #self._fvheader.Dump() 68 69 self._size = self._fvheader.GetFvLength() 70 71 if self._fvheader.GetFileSystemGuid() != self.FILE_SYSTEM_GUID: 72 fd.seek(self._offset) 73 self._raw.fromfile(fd, self.GetHeader().GetFvLength()) 74 return 75 76 # read block map 77 blockentry = BlockMapEntry.Read(fd) 78 self._blockentries.append(blockentry) 79 while (blockentry.GetNumberBlocks() != 0 and blockentry.GetLength() != 0): 80 self._blockentries.append(blockentry) 81 blockentry = BlockMapEntry.Read(fd) 82 83 84 if self._fvheader.GetSize() + (len(self._blockentries)) * 8 != \ 85 self._fvheader.GetHeaderLength(): 86 raise EFIBinaryError("Volume Header length not consistent with block map!") 87 88 index = align(fd.tell(), 8) 89 count = 0 90 while ((index + EfiFfs.FFS_HEADER_SIZE) < self._size): 91 ffs = EfiFfs.Read(fd, self) 92 if not isValidGuid(ffs.GetNameGuid()): 93 break 94 self._ffs.append(ffs) 95 count += 1 96 index = align(fd.tell(), 8) 97 98 fd.seek(self._offset) 99 self._raw.fromfile(fd, self.GetHeader().GetFvLength()) 100 101 def GetFfs(self): 102 return self._ffs 103 104 def GetHeader(self): 105 return self._fvheader 106 107 def GetBlockEntries(self): 108 return self._blockentries 109 110 def GetHeaderRawData(self): 111 ret = [] 112 ret += self._fvheader.GetRawData() 113 for block in self._blockentries: 114 ret += block.GetRawData() 115 return ret 116 117 def GetOffset(self): 118 return 0 119 120 def GetRawData(self): 121 return self._raw.tolist() 122 123class BinaryItem(object): 124 def __init__(self, parent=None): 125 self._size = 0 126 self._arr = array.array('B') 127 self._parent = parent 128 129 @classmethod 130 def Read(cls, fd, parent=None): 131 item = cls(parent) 132 item.fromfile(fd) 133 return item 134 135 def Load(self, fd): 136 self.fromfile(fd) 137 138 def GetSize(self): 139 """should be implemented by inherited class""" 140 141 def fromfile(self, fd): 142 self._arr.fromfile(fd, self.GetSize()) 143 144 def GetParent(self): 145 return self._parent 146 147class EfiFirmwareVolumeHeader(BinaryItem): 148 def GetSize(self): 149 return 56 150 151 def GetSigunature(self): 152 list = self._arr.tolist() 153 sig = '' 154 for x in list[40:44]: 155 sig += chr(x) 156 return sig 157 158 def GetAttribute(self): 159 return list2int(self._arr.tolist()[44:48]) 160 161 def GetErasePolarity(self): 162 list = self.GetAttrStrings() 163 if 'EFI_FVB2_ERASE_POLARITY' in list: 164 return True 165 return False 166 167 def GetAttrStrings(self): 168 list = [] 169 value = self.GetAttribute() 170 if (value & 0x01) != 0: 171 list.append('EFI_FVB2_READ_DISABLED_CAP') 172 if (value & 0x02) != 0: 173 list.append('EFI_FVB2_READ_ENABLED_CAP') 174 if (value & 0x04) != 0: 175 list.append('EFI_FVB2_READ_STATUS') 176 if (value & 0x08) != 0: 177 list.append('EFI_FVB2_WRITE_DISABLED_CAP') 178 if (value & 0x10) != 0: 179 list.append('EFI_FVB2_WRITE_ENABLED_CAP') 180 if (value & 0x20) != 0: 181 list.append('EFI_FVB2_WRITE_STATUS') 182 if (value & 0x40) != 0: 183 list.append('EFI_FVB2_LOCK_CAP') 184 if (value & 0x80) != 0: 185 list.append('EFI_FVB2_LOCK_STATUS') 186 if (value & 0x200) != 0: 187 list.append('EFI_FVB2_STICKY_WRITE') 188 if (value & 0x400) != 0: 189 list.append('EFI_FVB2_MEMORY_MAPPED') 190 if (value & 0x800) != 0: 191 list.append('EFI_FVB2_ERASE_POLARITY') 192 if (value & 0x1000) != 0: 193 list.append('EFI_FVB2_READ_LOCK_CAP') 194 if (value & 0x00002000) != 0: 195 list.append('EFI_FVB2_READ_LOCK_STATUS') 196 if (value & 0x00004000) != 0: 197 list.append('EFI_FVB2_WRITE_LOCK_CAP') 198 if (value & 0x00008000) != 0: 199 list.append('EFI_FVB2_WRITE_LOCK_STATUS') 200 201 if (value == 0): 202 list.append('EFI_FVB2_ALIGNMENT_1') 203 if (value & 0x001F0000) == 0x00010000: 204 list.append('EFI_FVB2_ALIGNMENT_2') 205 if (value & 0x001F0000) == 0x00020000: 206 list.append('EFI_FVB2_ALIGNMENT_4') 207 if (value & 0x001F0000) == 0x00030000: 208 list.append('EFI_FVB2_ALIGNMENT_8') 209 if (value & 0x001F0000) == 0x00040000: 210 list.append('EFI_FVB2_ALIGNMENT_16') 211 if (value & 0x001F0000) == 0x00050000: 212 list.append('EFI_FVB2_ALIGNMENT_32') 213 if (value & 0x001F0000) == 0x00060000: 214 list.append('EFI_FVB2_ALIGNMENT_64') 215 if (value & 0x001F0000) == 0x00070000: 216 list.append('EFI_FVB2_ALIGNMENT_128') 217 if (value & 0x001F0000) == 0x00080000: 218 list.append('EFI_FVB2_ALIGNMENT_256') 219 if (value & 0x001F0000) == 0x00090000: 220 list.append('EFI_FVB2_ALIGNMENT_512') 221 if (value & 0x001F0000) == 0x000A0000: 222 list.append('EFI_FVB2_ALIGNMENT_1K') 223 if (value & 0x001F0000) == 0x000B0000: 224 list.append('EFI_FVB2_ALIGNMENT_2K') 225 if (value & 0x001F0000) == 0x000C0000: 226 list.append('EFI_FVB2_ALIGNMENT_4K') 227 if (value & 0x001F0000) == 0x000D0000: 228 list.append('EFI_FVB2_ALIGNMENT_8K') 229 if (value & 0x001F0000) == 0x000E0000: 230 list.append('EFI_FVB2_ALIGNMENT_16K') 231 if (value & 0x001F0000) == 0x000F0000: 232 list.append('EFI_FVB2_ALIGNMENT_32K') 233 if (value & 0x001F0000) == 0x00100000: 234 list.append('EFI_FVB2_ALIGNMENT_64K') 235 if (value & 0x001F0000) == 0x00110000: 236 list.append('EFI_FVB2_ALIGNMENT_128K') 237 if (value & 0x001F0000) == 0x00120000: 238 list.append('EFI_FVB2_ALIGNMENT_256K') 239 if (value & 0x001F0000) == 0x00130000: 240 list.append('EFI_FVB2_ALIGNMENT_512K') 241 242 return list 243 244 def GetHeaderLength(self): 245 return list2int(self._arr.tolist()[48:50]) 246 247 def Dump(self): 248 print('Signature: %s' % self.GetSigunature()) 249 print('Attribute: 0x%X' % self.GetAttribute()) 250 print('Header Length: 0x%X' % self.GetHeaderLength()) 251 print('File system Guid: ', self.GetFileSystemGuid()) 252 print('Revision: 0x%X' % self.GetRevision()) 253 print('FvLength: 0x%X' % self.GetFvLength()) 254 255 def GetFileSystemGuid(self): 256 list = self._arr.tolist() 257 return list2guid(list[16:32]) 258 259 def GetRevision(self): 260 list = self._arr.tolist() 261 return int(list[55]) 262 263 def GetFvLength(self): 264 list = self._arr.tolist() 265 return list2int(list[32:40]) 266 267 def GetRawData(self): 268 return self._arr.tolist() 269 270class BlockMapEntry(BinaryItem): 271 def GetSize(self): 272 return 8 273 274 def GetNumberBlocks(self): 275 list = self._arr.tolist() 276 return list2int(list[0:4]) 277 278 def GetLength(self): 279 list = self._arr.tolist() 280 return list2int(list[4:8]) 281 282 def GetRawData(self): 283 return self._arr.tolist() 284 285 def __str__(self): 286 return '[BlockEntry] Number = 0x%X, length=0x%X' % (self.GetNumberBlocks(), self.GetLength()) 287 288class EfiFfs(object): 289 FFS_HEADER_SIZE = 24 290 291 def __init__(self, parent=None): 292 self._header = None 293 294 # following field is for FFS in FV file. 295 self._parent = parent 296 self._offset = 0 297 self._sections = [] 298 299 def Load(self, fd): 300 self._offset = align(fd.tell(), 8) 301 302 self._header = EfiFfsHeader.Read(fd, self) 303 304 if not isValidGuid(self.GetNameGuid()): 305 return 306 307 index = self._offset 308 fileend = self._offset + self.GetSize() 309 while (index + EfiSection.EFI_SECTION_HEADER_SIZE < fileend): 310 section = EfiSection(self) 311 section.Load(fd) 312 if section.GetSize() == 0 and section.GetHeader().GetType() == 0: 313 break 314 self._sections.append(section) 315 index = fd.tell() 316 317 # rebase file pointer to next ffs file 318 index = self._offset + self._header.GetFfsSize() 319 index = align(index, 8) 320 fd.seek(index) 321 322 def GetOffset(self): 323 return self._offset 324 325 def GetSize(self): 326 return self._header.GetFfsSize() 327 328 @classmethod 329 def Read(cls, fd, parent=None): 330 item = cls(parent) 331 item.Load(fd) 332 return item 333 334 def GetNameGuid(self): 335 return self._header.GetNameGuid() 336 337 def DumpContent(self): 338 list = self._content.tolist() 339 line = [] 340 count = 0 341 for item in list: 342 if count < 32: 343 line.append('0x%X' % int(item)) 344 count += 1 345 else: 346 print(' '.join(line)) 347 count = 0 348 line = [] 349 line.append('0x%X' % int(item)) 350 count += 1 351 352 def GetHeader(self): 353 return self._header 354 355 def GetParent(self): 356 return self._parent 357 358 def GetSections(self): 359 return self._sections 360 361class EfiFfsHeader(BinaryItem): 362 ffs_state_map = {0x01:'EFI_FILE_HEADER_CONSTRUCTION', 363 0x02:'EFI_FILE_HEADER_VALID', 364 0x04:'EFI_FILE_DATA_VALID', 365 0x08:'EFI_FILE_MARKED_FOR_UPDATE', 366 0x10:'EFI_FILE_DELETED', 367 0x20:'EFI_FILE_HEADER_INVALID'} 368 369 def GetSize(self): 370 return 24 371 372 def GetNameGuid(self): 373 list = self._arr.tolist() 374 return list2guid(list[0:16]) 375 376 def GetType(self): 377 list = self._arr.tolist() 378 return int(list[18]) 379 380 381 def GetTypeString(self): 382 value = self.GetType() 383 if value == 0x01: 384 return 'EFI_FV_FILETYPE_RAW' 385 if value == 0x02: 386 return 'EFI_FV_FILETYPE_FREEFORM' 387 if value == 0x03: 388 return 'EFI_FV_FILETYPE_SECURITY_CORE' 389 if value == 0x04: 390 return 'EFI_FV_FILETYPE_PEI_CORE' 391 if value == 0x05: 392 return 'EFI_FV_FILETYPE_DXE_CORE' 393 if value == 0x06: 394 return 'EFI_FV_FILETYPE_PEIM' 395 if value == 0x07: 396 return 'EFI_FV_FILETYPE_DRIVER' 397 if value == 0x08: 398 return 'EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER' 399 if value == 0x09: 400 return 'EFI_FV_FILETYPE_APPLICATION' 401 if value == 0x0B: 402 return 'EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE' 403 if value == 0xc0: 404 return 'EFI_FV_FILETYPE_OEM_MIN' 405 if value == 0xdf: 406 return 'EFI_FV_FILETYPE_OEM_MAX' 407 if value == 0xe0: 408 return 'EFI_FV_FILETYPE_DEBUG_MIN' 409 if value == 0xef: 410 return 'EFI_FV_FILETYPE_DEBUG_MAX' 411 if value == 0xf0: 412 return 'EFI_FV_FILETYPE_FFS_PAD' 413 if value == 0xff: 414 return 'EFI_FV_FILETYPE_FFS_MAX' 415 return 'Unknown FFS Type' 416 417 def GetAttributes(self): 418 list = self._arr.tolist() 419 return int(list[19]) 420 421 def GetFfsSize(self): 422 list = self._arr.tolist() 423 return list2int(list[20:23]) 424 425 def GetState(self): 426 list = self._arr.tolist() 427 state = int(list[23]) 428 polarity = self.GetParent().GetParent().GetHeader().GetErasePolarity() 429 if polarity: 430 state = (~state) & 0xFF 431 HighestBit = 0x80 432 while (HighestBit != 0) and (HighestBit & state) == 0: 433 HighestBit = HighestBit >> 1 434 return HighestBit 435 436 def GetStateString(self): 437 state = self.GetState() 438 if state in self.ffs_state_map.keys(): 439 return self.ffs_state_map[state] 440 return 'Unknown Ffs State' 441 442 def Dump(self): 443 print("FFS name: ", self.GetNameGuid()) 444 print("FFS type: ", self.GetType()) 445 print("FFS attr: 0x%X" % self.GetAttributes()) 446 print("FFS size: 0x%X" % self.GetFfsSize()) 447 print("FFS state: 0x%X" % self.GetState()) 448 449 def GetRawData(self): 450 return self._arr.tolist() 451 452 453class EfiSection(object): 454 EFI_SECTION_HEADER_SIZE = 4 455 456 def __init__(self, parent=None): 457 self._size = 0 458 self._parent = parent 459 self._offset = 0 460 self._contents = array.array('B') 461 462 def Load(self, fd): 463 self._offset = align(fd.tell(), 4) 464 465 self._header = EfiSectionHeader.Read(fd, self) 466 467 if self._header.GetTypeString() == "EFI_SECTION_PE32": 468 pefile = pe.PEFile(self) 469 pefile.Load(fd, self.GetContentSize()) 470 471 fd.seek(self._offset) 472 self._contents.fromfile(fd, self.GetContentSize()) 473 474 # rebase file pointer to next section 475 index = self._offset + self.GetSize() 476 index = align(index, 4) 477 fd.seek(index) 478 479 def GetContentSize(self): 480 return self.GetSize() - self.EFI_SECTION_HEADER_SIZE 481 482 def GetContent(self): 483 return self._contents.tolist() 484 485 def GetSize(self): 486 return self._header.GetSectionSize() 487 488 def GetHeader(self): 489 return self._header 490 491 def GetSectionOffset(self): 492 return self._offset + self.EFI_SECTION_HEADER_SIZE 493 494class EfiSectionHeader(BinaryItem): 495 section_type_map = {0x01: 'EFI_SECTION_COMPRESSION', 496 0x02: 'EFI_SECTION_GUID_DEFINED', 497 0x10: 'EFI_SECTION_PE32', 498 0x11: 'EFI_SECTION_PIC', 499 0x12: 'EFI_SECTION_TE', 500 0x13: 'EFI_SECTION_DXE_DEPEX', 501 0x14: 'EFI_SECTION_VERSION', 502 0x15: 'EFI_SECTION_USER_INTERFACE', 503 0x16: 'EFI_SECTION_COMPATIBILITY16', 504 0x17: 'EFI_SECTION_FIRMWARE_VOLUME_IMAGE', 505 0x18: 'EFI_SECTION_FREEFORM_SUBTYPE_GUID', 506 0x19: 'EFI_SECTION_RAW', 507 0x1B: 'EFI_SECTION_PEI_DEPEX'} 508 def GetSize(self): 509 return 4 510 511 def GetSectionSize(self): 512 list = self._arr.tolist() 513 return list2int(list[0:3]) 514 515 def GetType(self): 516 list = self._arr.tolist() 517 return int(list[3]) 518 519 def GetTypeString(self): 520 type = self.GetType() 521 if type not in self.section_type_map.keys(): 522 return 'Unknown Section Type' 523 return self.section_type_map[type] 524 525 def Dump(self): 526 print('size = 0x%X' % self.GetSectionSize()) 527 print('type = 0x%X' % self.GetType()) 528 529 530 531rMapEntry = re.compile('^(\w+)[ \(\w\)]* \(BaseAddress=([0-9a-fA-F]+), EntryPoint=([0-9a-fA-F]+), GUID=([0-9a-fA-F\-]+)') 532class EfiFvMapFile(object): 533 def __init__(self): 534 self._mapentries = {} 535 536 def Load(self, path): 537 if not os.path.exists(path): 538 return False 539 540 try: 541 file = open(path, 'r') 542 lines = file.readlines() 543 file.close() 544 except: 545 return False 546 547 for line in lines: 548 if line[0] != ' ': 549 # new entry 550 ret = rMapEntry.match(line) 551 if ret is not None: 552 name = ret.groups()[0] 553 baseaddr = int(ret.groups()[1], 16) 554 entry = int(ret.groups()[2], 16) 555 guidstr = '{' + ret.groups()[3] + '}' 556 guid = uuid.UUID(guidstr) 557 self._mapentries[guid] = EfiFvMapFileEntry(name, baseaddr, entry, guid) 558 return True 559 560 def GetEntry(self, guid): 561 if guid in self._mapentries.keys(): 562 return self._mapentries[guid] 563 return None 564 565class EfiFvMapFileEntry(object): 566 def __init__(self, name, baseaddr, entry, guid): 567 self._name = name 568 self._baseaddr = baseaddr 569 self._entry = entry 570 self._guid = guid 571 572 def GetName(self): 573 return self._name 574 575 def GetBaseAddress(self): 576 return self._baseaddr 577 578 def GetEntryPoint(self): 579 return self._entry 580 581def list2guid(list): 582 val1 = list2int(list[0:4]) 583 val2 = list2int(list[4:6]) 584 val3 = list2int(list[6:8]) 585 val4 = 0 586 for item in list[8:16]: 587 val4 = (val4 << 8) | int(item) 588 589 val = val1 << 12 * 8 | val2 << 10 * 8 | val3 << 8 * 8 | val4 590 guid = uuid.UUID(int=val) 591 return guid 592 593def list2int(list): 594 val = 0 595 for index in range(len(list) - 1, -1, -1): 596 val = (val << 8) | int(list[index]) 597 return val 598 599def align(value, alignment): 600 return (value + ((alignment - value) & (alignment - 1))) 601 602gInvalidGuid = uuid.UUID(int=0xffffffffffffffffffffffffffffffff) 603def isValidGuid(guid): 604 if guid == gInvalidGuid: 605 return False 606 return True 607