SickGear/lib/hachoir/parser/misc/pifv.py
2023-02-09 13:41:15 +00:00

243 lines
8.4 KiB
Python

"""
EFI Platform Initialization Firmware Volume parser.
Author: Alexandre Boeglin
Creation date: 08 jul 2007
"""
from hachoir.parser import Parser
from hachoir.field import (FieldSet,
UInt8, UInt16, UInt24, UInt32, UInt64, Enum,
CString, String, PaddingBytes, RawBytes, NullBytes)
from hachoir.core.endian import LITTLE_ENDIAN
from hachoir.core.tools import paddingSize, humanFilesize
from hachoir.parser.common.win32 import GUID
EFI_SECTION_COMPRESSION = 0x1
EFI_SECTION_GUID_DEFINED = 0x2
EFI_SECTION_PE32 = 0x10
EFI_SECTION_PIC = 0x11
EFI_SECTION_TE = 0x12
EFI_SECTION_DXE_DEPEX = 0x13
EFI_SECTION_VERSION = 0x14
EFI_SECTION_USER_INTERFACE = 0x15
EFI_SECTION_COMPATIBILITY16 = 0x16
EFI_SECTION_FIRMWARE_VOLUME_IMAGE = 0x17
EFI_SECTION_FREEFORM_SUBTYPE_GUID = 0x18
EFI_SECTION_RAW = 0x19
EFI_SECTION_PEI_DEPEX = 0x1b
EFI_SECTION_TYPE = {
EFI_SECTION_COMPRESSION: "Encapsulation section where other sections"
+ " are compressed",
EFI_SECTION_GUID_DEFINED: "Encapsulation section where other sections"
+ " have format defined by a GUID",
EFI_SECTION_PE32: "PE32+ Executable image",
EFI_SECTION_PIC: "Position-Independent Code",
EFI_SECTION_TE: "Terse Executable image",
EFI_SECTION_DXE_DEPEX: "DXE Dependency Expression",
EFI_SECTION_VERSION: "Version, Text and Numeric",
EFI_SECTION_USER_INTERFACE: "User-Friendly name of the driver",
EFI_SECTION_COMPATIBILITY16: "DOS-style 16-bit EXE",
EFI_SECTION_FIRMWARE_VOLUME_IMAGE: "PI Firmware Volume image",
EFI_SECTION_FREEFORM_SUBTYPE_GUID: "Raw data with GUID in header to"
+ " define format",
EFI_SECTION_RAW: "Raw data",
EFI_SECTION_PEI_DEPEX: "PEI Dependency Expression",
}
EFI_FV_FILETYPE_RAW = 0x1
EFI_FV_FILETYPE_FREEFORM = 0x2
EFI_FV_FILETYPE_SECURITY_CORE = 0x3
EFI_FV_FILETYPE_PEI_CORE = 0x4
EFI_FV_FILETYPE_DXE_CORE = 0x5
EFI_FV_FILETYPE_PEIM = 0x6
EFI_FV_FILETYPE_DRIVER = 0x7
EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER = 0x8
EFI_FV_FILETYPE_APPLICATION = 0x9
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0xb
EFI_FV_FILETYPE_FFS_PAD = 0xf0
EFI_FV_FILETYPE = {
EFI_FV_FILETYPE_RAW: "Binary data",
EFI_FV_FILETYPE_FREEFORM: "Sectioned data",
EFI_FV_FILETYPE_SECURITY_CORE: "Platform core code used during the SEC"
+ " phase",
EFI_FV_FILETYPE_PEI_CORE: "PEI Foundation",
EFI_FV_FILETYPE_DXE_CORE: "DXE Foundation",
EFI_FV_FILETYPE_PEIM: "PEI module (PEIM)",
EFI_FV_FILETYPE_DRIVER: "DXE driver",
EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: "Combined PEIM/DXE driver",
EFI_FV_FILETYPE_APPLICATION: "Application",
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: "Firmware volume image",
EFI_FV_FILETYPE_FFS_PAD: "Pad File For FFS",
}
for x in range(0xc0, 0xe0):
EFI_FV_FILETYPE[x] = "OEM File"
for x in range(0xe0, 0xf0):
EFI_FV_FILETYPE[x] = "Debug/Test File"
for x in range(0xf1, 0x100):
EFI_FV_FILETYPE[x] = "Firmware File System Specific File"
class BlockMap(FieldSet):
static_size = 8 * 8
def createFields(self):
yield UInt32(self, "num_blocks")
yield UInt32(self, "len")
def createDescription(self):
return "%d blocks of %s" % (
self["num_blocks"].value, humanFilesize(self["len"].value))
class FileSection(FieldSet):
COMPRESSION_TYPE = {
0: 'Not Compressed',
1: 'Standard Compression',
}
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
self._size = self["size"].value * 8
section_type = self["type"].value
if section_type in (EFI_SECTION_DXE_DEPEX, EFI_SECTION_PEI_DEPEX):
# These sections can sometimes be longer than what their size
# claims! It's so nice to have so detailled specs and not follow
# them ...
if self.stream.readBytes(self.absolute_address +
self._size, 1) == b'\0':
self._size = self._size + 16
def createFields(self):
# Header
yield UInt24(self, "size")
yield Enum(UInt8(self, "type"), EFI_SECTION_TYPE)
section_type = self["type"].value
if section_type == EFI_SECTION_COMPRESSION:
yield UInt32(self, "uncomp_len")
yield Enum(UInt8(self, "comp_type"), self.COMPRESSION_TYPE)
elif section_type == EFI_SECTION_FREEFORM_SUBTYPE_GUID:
yield GUID(self, "sub_type_guid")
elif section_type == EFI_SECTION_GUID_DEFINED:
yield GUID(self, "section_definition_guid")
yield UInt16(self, "data_offset")
yield UInt16(self, "attributes")
elif section_type == EFI_SECTION_USER_INTERFACE:
yield CString(self, "file_name", charset="UTF-16-LE")
elif section_type == EFI_SECTION_VERSION:
yield UInt16(self, "build_number")
yield CString(self, "version", charset="UTF-16-LE")
# Content
content_size = (self.size - self.current_size) // 8
if content_size == 0:
return
if section_type == EFI_SECTION_COMPRESSION:
compression_type = self["comp_type"].value
if compression_type == 1:
while not self.eof:
yield RawBytes(self, "compressed_content", content_size)
else:
while not self.eof:
yield FileSection(self, "section[]")
elif section_type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
yield FirmwareVolume(self, "firmware_volume")
else:
yield RawBytes(self, "content", content_size,
EFI_SECTION_TYPE.get(self["type"].value,
"Unknown Section Type"))
def createDescription(self):
return EFI_SECTION_TYPE.get(self["type"].value,
"Unknown Section Type")
class File(FieldSet):
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
self._size = self["size"].value * 8
def createFields(self):
# Header
yield GUID(self, "name")
yield UInt16(self, "integrity_check")
yield Enum(UInt8(self, "type"), EFI_FV_FILETYPE)
yield UInt8(self, "attributes")
yield UInt24(self, "size")
yield UInt8(self, "state")
# Content
while not self.eof:
yield FileSection(self, "section[]")
def createDescription(self):
return "%s: %s containing %d section(s)" % (
self["name"].value,
self["type"].display,
len(self.array("section")))
class FirmwareVolume(FieldSet):
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
if not self._size:
self._size = self["volume_len"].value * 8
def createFields(self):
# Header
yield NullBytes(self, "zero_vector", 16)
yield GUID(self, "fs_guid")
yield UInt64(self, "volume_len")
yield String(self, "signature", 4)
yield UInt32(self, "attributes")
yield UInt16(self, "header_len")
yield UInt16(self, "checksum")
yield UInt16(self, "ext_header_offset")
yield UInt8(self, "reserved")
yield UInt8(self, "revision")
while True:
bm = BlockMap(self, "block_map[]")
yield bm
if bm['num_blocks'].value == 0 and bm['len'].value == 0:
break
# TODO must handle extended header
# Content
while not self.eof:
padding = paddingSize(self.current_size // 8, 8)
if padding:
yield PaddingBytes(self, "padding[]", padding)
yield File(self, "file[]")
def createDescription(self):
return "Firmware Volume containing %d file(s)" % len(self.array("file"))
class PIFVFile(Parser):
endian = LITTLE_ENDIAN
MAGIC = b'_FVH'
PARSER_TAGS = {
"id": "pifv",
"category": "program",
"file_ext": ("bin", ""),
"min_size": 64 * 8, # smallest possible header
"magic_regex": ((b"\0{16}.{24}" + MAGIC, 0), ),
"description": "EFI Platform Initialization Firmware Volume",
}
def validate(self):
if self.stream.readBytes(40 * 8, 4) != self.MAGIC:
return "Invalid magic number"
if self.stream.readBytes(0, 16) != b"\0" * 16:
return "Invalid zero vector"
return True
def createFields(self):
while not self.eof:
yield FirmwareVolume(self, "firmware_volume[]")