## @ ifwi_utility.py # # copyright (c) 2019, intel corporation. all rights reserved.
# SPDX-license-identifier: BSD-2-clause-patent # ## import sys import os import argparse from ctypes import Structure, c_char, c_uint32, c_uint8, c_uint64, c_uint16, sizeof, ARRAY sys.dont_write_bytecode = True from CommonUtility import * class UCODE_HEADER (Structure): _pack_ = 1 _fields_ = [ ('header_version', c_uint32), ('update_revision', c_uint32), ('date', c_uint32), ('processor_signature', c_uint32), ('checksum', c_uint32), ('loader_revision', c_uint32), ('processor_flags', c_uint32), ('data_size', c_uint32), ('total_size', c_uint32), ('reserved', ARRAY(c_uint8, 12)), ] class FIT_ENTRY(Structure): FIT_OFFSET = -0x40 FIT_SIGNATURE = b'_FIT_ ' _pack_ = 1 _fields_ = [ ('address', c_uint64), ('size', c_uint32), # Bits[31:24] Reserved ('version', c_uint16), ('type', c_uint8), # Bit[7] = C_V ('checksum', c_uint8), ] def set_values(self, _address, _size, _version, _type, _checksum): self.address = _address self.size = _size self.version = _version self.type = _type self.checksum = _checksum class BPDT_ENTRY_TYPE(Structure): _pack_ = 1 _fields_ = [('data', c_uint16)] BPDT_PART_VAL = { "BpdtOemSmip" : 0, "BpdtCseRbe" : 1, "BpdtCseBup" : 2, "BpdtUcode" : 3, "BpdtIbb" : 4, "BpdtSbpdt" : 5, "BpdtObb" : 6, "BpdtCseMain" : 7, "BpdtIsh" : 8, "BpdtCseIdlm" : 9, "BpdtIfpOverride" : 10, "BpdtDebugTokens" : 11, "BpdtUfsPhyConfig" : 12, "BpdtUfsGppLunId" : 13, "BpdtPmc" : 14, "BpdtIunit" : 15, "BpdtNvmConfig" : 16, "BpdtUepType" : 17, "BpdtUfsRateType" : 18, "BpdtInvalidType" : 19, } BPDT_PART_NAME = {v: k for k, v in BPDT_PART_VAL.items()} def __init__(self, val=0): self.set_value(val) def __str__(self): if self.value < 0 or self.value >= self.BPDT_PART_VAL['BpdtInvalidType']: str = "BpdtInvalidType" else: str = self.BPDT_PART_NAME[self.value] return str def __int__(self): return self.get_value() def set_value(self, val): self.data = val def get_value(self): return self.data value = property(get_value, set_value) class BPDT_INFO(): def __init__(self, name, offset, bpdt_offset, primary): self.name = name self.primary = primary self.offset = offset self.bpdt_offset = bpdt_offset class BPDT_HEADER(Structure): _pack_ = 1 _fields_ = [ ('signature', c_uint32), ('desc_cnt', c_uint16), ('version', c_uint16), ('xor_sum', c_uint32), ('ifwi_ver', c_uint32), ('reserved', ARRAY(c_uint8, 8)) ] class BPDT_ENTRY(Structure): _pack_ = 1 _fields_ = [ ('type', BPDT_ENTRY_TYPE), ('flags', c_uint16), ('sub_part_offset', c_uint32), ('sub_part_size', c_uint32), ] class SUBPART_DIR_HEADER(Structure): _pack_ = 1 _fields_ = [ ('header_marker', ARRAY(c_char, 4)), ('num_of_entries', c_uint32), ('header_version', c_uint8), ('entry_version', c_uint8), ('header_length', c_uint8), ('checksum', c_uint8), ('sub_part_name', ARRAY(c_char, 4)), ] class SUBPART_DIR_ENTRY(Structure): _pack_ = 1 _fields_ = [ ('entry_name', ARRAY(c_char, 12)), ('entry_offset', c_uint32, 24), ('reserved1', c_uint32, 8), ('entry_size', c_uint32), ('reserved2', c_uint32), ] class BIOS_ENTRY(Structure): _pack_ = 1 _fields_ = [ ('name', ARRAY(c_char, 4)), ('offset', c_uint32), ('length', c_uint32), ('reserved', c_uint32), ] class SPI_DESCRIPTOR(Structure): DESC_SIGNATURE = 0x0FF0A55A FLASH_REGIONS = { "descriptor" : 0x00, "bios" : 0x04, "txe" : 0x08, "gbe" : 0x0c, "pdr" : 0x10, "dev_expansion" : 0x14, } _pack_ = 1 _fields_ = [ ('reserved', ARRAY(c_char, 16)), ('fl_val_sig', c_uint32), ('fl_map0', c_uint32), ('fl_map1', c_uint32), ('fl_map2', c_uint32), ('remaining', ARRAY(c_char, 0x1000 - 0x20)), ] class COMPONENT: COMP_TYPE = { "IFWI" : 0, "RGN" : 1, "BP" : 2, "BPDT" : 3, "PART" : 4, "FILE" : 5, } def __init__(self, name, com_type, offset, length): self.name = name self.type = com_type self.offset = offset self.length = length self.child = [] self.data = None self.parent = None def add_child(self, child, index = -1): child.parent = self if index == -1: self.child.append (child) else: self.child.insert (index, child) def set_data(self, file): if file: fd =open(file, 'rb') data = bytearray(fd.read()) fd.close() else: data = bytearray(b'\xff' * self.length) if self.length > len(data): self.data = data + b'\xff' * (self.length - len(data)) else: self.data = data[:self.length] def get_data(self): return self.data class FLASH_MAP_DESC(Structure): _pack_ = 1 _fields_ = [ ('sig', ARRAY(c_char, 4)), ('flags', c_uint32), ('offset', c_uint32), ('size', c_uint32), ] class FLASH_MAP(Structure): FLASH_MAP_SIGNATURE = b'FLMP' FLASH_MAP_COMPONENT_SIGNATURE = { "STAGE1A" : "SG1A", "STAGE1B" : "SG1B", "STAGE2" : "SG02", "ACM" : "ACM0", "DIAGNOSTICACM" : "DACM", "UCODE" : "UCOD", "MRCDATA" : "MRCD", "VARIABLE" : "VARS", "PAYLOAD" : "PYLD", "EPAYLOAD" : "EPLD", "SIIPFW" : "IPFW", "UEFIVARIABLE" : "UVAR", "SPI_IAS1" : "IAS1", "SPI_IAS2" : "IAS2", "FWUPDATE" : "FWUP", "CFGDATA" : "CNFG", "KEYHASH" : "KEYH", "BPM" : "_BPM", "OEMKEY" : "OEMK", "SBLRSVD" : "RSVD", "EMPTY" : "EMTY", "UNKNOWN" : "UNKN", } FLASH_MAP_ATTRIBUTES = { "PRIMARY_REGION" : 0x00000000, "BACKUP_REGION" : 0x00000001, } FLASH_MAP_DESC_FLAGS = { "TOP_SWAP" : 0x00000001, "REDUNDANT" : 0x00000002, "NON_REDUNDANT": 0x00000004, "NON_VOLATILE" : 0x00000008, "COMPRESSED" : 0x00000010, "BACKUP" : 0x00000040, } FLASH_MAP_REGION = { 0x00: "RGN", 0x01: "TS0", 0x41: "TS1", 0x02: "RD0", 0x42: "RD1", 0x04: "NRD", 0x08: "NVS", } _pack_ = 1 _fields_ = [ ('sig', ARRAY(c_char, 4)), ('version', c_uint16), ('length', c_uint16), ('attributes', c_uint8), ('reserved', ARRAY(c_char, 3)), ('romsize', c_uint32), ] def __init__(self): self.sig = FLASH_MAP.FLASH_MAP_SIGNATURE self.version = 1 self.romsize = 0 self.attributes = 0 self.length = sizeof(self) self.descriptors = [] def add(self, desc): self.descriptors.append(desc) def finalize (self): # Calculate size of the flash map self.romsize = sum ([x.size for x in self.descriptors]) self.length = sizeof(self) + len(self.descriptors) * sizeof(FLASH_MAP_DESC) class UCODE_PARSER: @staticmethod def dump (bin): ucode_list = UCODE_PARSER.parse (bin) for idx, bin in enumerate(ucode_list): print ('Microcode %d:' % (idx + 1)) ucode_hdr = UCODE_HEADER.from_buffer(bin) print (' Processor : %X' % (ucode_hdr.processor_signature)) print (' Revision : %X' % (ucode_hdr.update_revision)) month = (ucode_hdr.date & 0xFF000000) >> 24 day = (ucode_hdr.date & 0xFF0000) >> 16 year = ucode_hdr.date & 0xFFFF print (' Date : %02X/%02X/%04X' % (month, day, year)) print (' Length : %X' % (ucode_hdr.total_size)) @staticmethod def extract (bin, out_dir): ucode_list = UCODE_PARSER.parse (bin) for idx, bin in enumerate(ucode_list): ucode_hdr = UCODE_HEADER.from_buffer(bin) name = '%03d0_%08X_%08X.mcb' % (idx, ucode_hdr.processor_signature, ucode_hdr.update_revision) path = os.path.join (out_dir, name) gen_file_from_object (path, bin) print ("%d microcode binaries were extraced to directory '%s' !" % (idx + 1, out_dir)) @staticmethod def is_valid (ucode): valid = True ucode_hdr = UCODE_HEADER.from_buffer(ucode) if ucode_hdr.header_version != 1: print ('ERROR: Invalid header version !') valid = False if bytearray(ucode_hdr.reserved) != b'\x00' * 12: print ('ERROR: Invalid reserved bytes !') valid = False if ucode_hdr.total_size % 1024 != 0: print ('ERROR: Invalid total size !') valid = False data = ARRAY(c_uint32, ucode_hdr.total_size >> 2).from_buffer(ucode) if (sum(data) & 0xffffffff) != 0: print ('ERROR: Invalid checksum !') valid = False return valid @staticmethod def pack (ucode_files, out_file = None): bins = bytearray() if type(ucode_files) is type([]): ucode_list = ucode_files elif os.path.isdir(ucode_files): ucode_list = [os.path.join(ucode_files, f) for f in sorted(os.listdir(ucode_files)) if f.endswith('.mcb')] else: return bins for ucode in ucode_list: bin = bytearray (get_file_data (ucode)) if UCODE_PARSER.is_valid (bin): ucode_hdr = UCODE_HEADER.from_buffer(bin) bins.extend (bin[:ucode_hdr.total_size]) else: print ("Microcode file '%s' is ignored !" % ucode) if out_file: gen_file_from_object (out_file, bins) return bins @staticmethod def parse (bin): ucode = [] offset = 0 valid = True while valid and (offset < len(bin)): ucode_hdr = UCODE_HEADER.from_buffer(bin, offset) if ucode_hdr.header_version == 0xffffffff: break valid = UCODE_PARSER.is_valid (bin) if valid: ucode.append (bytearray(bin[offset:offset+ucode_hdr.total_size])) offset += ucode_hdr.total_size return ucode class IFWI_PARSER: def __init__(self): return @staticmethod def is_ifwi_image(bios_bins): spi_desc = SPI_DESCRIPTOR.from_buffer(bios_bins) return spi_desc.fl_val_sig == spi_desc.DESC_SIGNATURE @staticmethod def locate_components(root, path): result = [] nodes = path.split('/') if len(nodes) < 1 or root.name != nodes[0]: return [] if len(nodes) == 1: return [root] for comp in root.child: ret = IFWI_PARSER.locate_components(comp, '/'.join(nodes[1:])) if len(ret) > 0: result.extend(ret) return result @staticmethod def locate_component(root, path): result = IFWI_PARSER.locate_components(root, path) if len(result) > 0: return result[0] else: return None @staticmethod def find_components(root, name, comp_type = COMPONENT.COMP_TYPE['FILE']): result = [] if root.type == comp_type and root.name == name: return [root] for comp in root.child: ret = IFWI_PARSER.find_components(comp, name, comp_type) if len(ret) > 0: result.extend(ret) return result @staticmethod def get_component_path (comp): path = [] while comp: path.append (comp.name) comp = comp.parent return '/'.join(path[::-1]) @staticmethod def print_tree(root, level=0): if root is None: return print ("%-24s [O:0x%08X L:0x%08X]" % (' ' * level + root.name, root.offset, root.length)) for comp in root.child: level += 1 IFWI_PARSER.print_tree(comp, level) level -= 1 bp = IFWI_PARSER.locate_component (root, 'IFWI/BIOS/BP0') if bp: print ("\nBPDT Space Information:") for idx in range(2): bp = IFWI_PARSER.locate_component (root, 'IFWI/BIOS/BP%d' % idx) if len(bp.child) > 1: sbpdt = bp.child[1] bplen = bp.length - ((sbpdt.offset + sbpdt.length) - bp.offset) else: bplen = bp.length print (" BP%d Free Space: 0x%05X" % (idx, bplen)) @staticmethod def find_ifwi_region (spi_descriptor, rgn_name): frba = ((spi_descriptor.fl_map0 >> 16) & 0xFF) << 4 reg_off = spi_descriptor.FLASH_REGIONS[rgn_name] fl_reg = reg_off + frba rgn_off = c_uint32.from_buffer(spi_descriptor, fl_reg) rgn_base = (rgn_off.value & 0x7FFF) << 12 rgn_limit = ((rgn_off.value & 0x7FFF0000) >> 4) | 0xFFF if (reg_off > 0 and rgn_off.value == 0) or (rgn_off.value == 0xFFFFFFFF) or (rgn_limit <= rgn_base): return None, None else: return (rgn_base, rgn_limit) @staticmethod def get_boot_partition_from_path (comp_path): if '/RD0/' in comp_path or '/TS0/' in comp_path: bp = 0 elif '/RD1/' in comp_path or '/TS1/' in comp_path: bp = 1 else: bp = 0 return bp @staticmethod def update_ucode_fit_entry (ifwi_bin, ucode_path): ifwi = IFWI_PARSER.parse_ifwi_binary (ifwi_bin) if not ifwi: print ("Not a valid ifwi image!") return -2 # Get microcode ucode_comps = IFWI_PARSER.locate_components (ifwi, ucode_path) if len(ucode_comps) == 0: print ("Cannot find microcode component in ifwi image!" % path) return -3 # Get partition from path bp = IFWI_PARSER.get_boot_partition_from_path (ucode_path) # Get fit entry path = 'IFWI/BIOS/TS%d/SG1A' % bp ifwi_comps = IFWI_PARSER.locate_components (ifwi, path) if len(ifwi_comps) == 0: path = 'IFWI/BIOS/SG1A' % bp ifwi_comps = IFWI_PARSER.locate_components (ifwi, path) if len(ifwi_comps) == 0: print ("Cannot find 'SG1A' in ifwi image!" % path) return -4 img_base = 0x100000000 - len(ifwi_bin) fit_addr = c_uint32.from_buffer(ifwi_bin, ifwi_comps[0].offset + ifwi_comps[0].length + FIT_ENTRY.FIT_OFFSET) fit_offset = fit_addr.value - img_base fit_header = FIT_ENTRY.from_buffer(ifwi_bin, fit_offset) if fit_header.address != bytes_to_value (bytearray(FIT_ENTRY.FIT_SIGNATURE)): print ("Cannot find FIT table !" % path) return -4 # Update Ucode entry address ucode_idx = 0 ucode_off = ucode_comps[0].offset ucode_list = UCODE_PARSER.parse (ifwi_bin[ucode_off:]) for fit_type in [0x01, 0x7f]: for idx in range(fit_header.size): fit_entry = FIT_ENTRY.from_buffer(ifwi_bin, fit_offset + (idx + 1) * 16) if fit_entry.type == fit_type: if ucode_idx < len(ucode_list): fit_entry.set_values(img_base + ucode_off, 0, 0x100, 0x1, 0) ucode_off += len(ucode_list[ucode_idx]) ucode_idx += 1 else: # more fit entry is available, clear this entry fit_entry.type = 0x7f if ucode_idx != len(ucode_list): print ("Not all microcode can be listed in FIT table due to limited FIT entry number !") return -5 # Update FIT checksum fit_header.checksum = 0 fit_sum = sum(ifwi_bin[fit_offset:fit_offset+fit_header.size*16]) fit_header.checksum = (0 - fit_sum) & 0xff return 0 @staticmethod def replace_component (ifwi_bin, comp_bin, path): ifwi = IFWI_PARSER.parse_ifwi_binary (ifwi_bin) if not ifwi: print ("Not a valid ifwi image!") return -2 ifwi_comps = IFWI_PARSER.locate_components (ifwi, path) if len(ifwi_comps) == 0: print ("Cannot find path '%s' in ifwi image!" % path) return -4 for ifwi_comp in ifwi_comps: gap = len(comp_bin) - ifwi_comp.length if gap > 0: print ("Component image file is too big (0x%x vs 0x%x)!" % (ifwi_comp.length, len(comp_bin))) return -5 elif gap < 0: gap = -gap print ("Padding 0x%x bytes at the end to fill the region '%s'" % (gap, ifwi_comp.name)) comp_bin.extend (b'\xff' * gap) ifwi_bin[ifwi_comp.offset:ifwi_comp.offset + ifwi_comp.length] = \ comp_bin[0:ifwi_comp.length] return 0 @staticmethod def extract_component (ifwi_bin, comp_bin, path): bins_comp = bytearray () ifwi = IFWI_PARSER.parse_ifwi_binary (ifwi_bin) if not ifwi: print ("Not a valid ifwi image!") return -1 ifwi_comps = IFWI_PARSER.locate_components (ifwi, path) if len(ifwi_comps) == 0: print ("Cannot find path '%s' in ifwi image!" % path) return -2 if len(ifwi_comps) > 1: print ("Found multiple components for '%s'!" % path) return -3 ifwi_comp = ifwi_comps[0] comp_bin[:] = ifwi_bin[ifwi_comp.offset:ifwi_comp.offset + ifwi_comp.length] return 0 @staticmethod def bpdt_parser (bin_data, bpdt_offset, offset): sub_part_list = [] idx = bpdt_offset + offset bpdt_hdr = BPDT_HEADER.from_buffer(bytearray(bin_data[idx:idx + sizeof(BPDT_HEADER)])) idx += sizeof(bpdt_hdr) sbpdt = None for desc in range(bpdt_hdr.desc_cnt): bpdt_entry = BPDT_ENTRY.from_buffer(bytearray(bin_data[idx:idx + sizeof(BPDT_ENTRY)])) idx += sizeof(bpdt_entry) dir_list = [] if 'BpdtSbpdt' == str(bpdt_entry.type): sbpdt = bpdt_entry if bpdt_entry.sub_part_size > sizeof(SUBPART_DIR_HEADER): part_idx = bpdt_offset + bpdt_entry.sub_part_offset if part_idx > len(bin_data): break sub_part_dir_hdr = SUBPART_DIR_HEADER.from_buffer( bytearray(bin_data[part_idx:part_idx + sizeof( SUBPART_DIR_HEADER)]), 0) part_idx += sizeof(sub_part_dir_hdr) if b'$CPD' == sub_part_dir_hdr.header_marker: for dir in range(sub_part_dir_hdr.num_of_entries): part_dir = SUBPART_DIR_ENTRY.from_buffer( bytearray(bin_data[part_idx:part_idx + sizeof( SUBPART_DIR_ENTRY)]), 0) part_idx += sizeof(part_dir) dir_list.append(part_dir) sub_part_list.append((bpdt_entry, dir_list)) return sub_part_list, sbpdt @staticmethod def parse_bios_bpdt (img_data): offset = 0 bios_hdr = BIOS_ENTRY.from_buffer(img_data, offset) if bios_hdr.name != b"BIOS": return None bios_comp = COMPONENT(bios_hdr.name.decode(), COMPONENT.COMP_TYPE['RGN'], 0, len(img_data)) offset += sizeof(bios_hdr) entry_num = bios_hdr.offset for idx in range(entry_num): part_entry = BIOS_ENTRY.from_buffer(img_data, offset) part_comp = COMPONENT(part_entry.name.decode(), COMPONENT.COMP_TYPE['PART'], part_entry.offset, part_entry.length) bios_comp.add_child(part_comp) sub_part_dir_hdr = SUBPART_DIR_HEADER.from_buffer(img_data, part_entry.offset) if b'$CPD' == sub_part_dir_hdr.header_marker: for dir in range(sub_part_dir_hdr.num_of_entries): part_dir = SUBPART_DIR_ENTRY.from_buffer( img_data, part_entry.offset + sizeof(SUBPART_DIR_HEADER) + sizeof(SUBPART_DIR_ENTRY) * dir) dir_comp = COMPONENT(part_dir.entry_name.decode(), COMPONENT.COMP_TYPE['FILE'], part_entry.offset + part_dir.entry_offset, part_dir.entry_size) part_comp.add_child(dir_comp) offset += sizeof(part_entry) return bios_comp @staticmethod def parse_bios_region (img_data, base_off = 0): offset = bytes_to_value(img_data[-8:-4]) - (0x100000000 - len(img_data)) if offset <0 or offset >= len(img_data) - 0x10: return None fla_map_off = offset if bytes_to_value(img_data[fla_map_off:fla_map_off+4]) != 0x504d4c46: return None bios_comp = COMPONENT('BIOS', COMPONENT.COMP_TYPE['RGN'], base_off, len(img_data)) curr_part = -1 fla_map_str = FLASH_MAP.from_buffer (img_data, fla_map_off) entry_num = (fla_map_str.length - sizeof(FLASH_MAP)) // sizeof(FLASH_MAP_DESC) for idx in range (entry_num): idx = entry_num - 1 - idx desc = FLASH_MAP_DESC.from_buffer (img_data, fla_map_off + sizeof(FLASH_MAP) + idx * sizeof(FLASH_MAP_DESC)) file_comp = COMPONENT(desc.sig.decode(), COMPONENT.COMP_TYPE['FILE'], desc.offset + base_off, desc.size) if curr_part != desc.flags & 0x4F: curr_part = desc.flags & 0x4F part_comp = COMPONENT('%s' % (FLASH_MAP.FLASH_MAP_REGION[curr_part]), COMPONENT.COMP_TYPE['PART'], desc.offset + base_off, desc.size) bios_comp.add_child (part_comp) else: part_comp.length += desc.size part_comp.add_child(file_comp) return bios_comp @staticmethod def parse_ifwi_binary(img_data): if len(img_data) < 0x1000: return None ifwi_comp = COMPONENT('IFWI', COMPONENT.COMP_TYPE['IFWI'], 0, len(img_data)) bios_comp = IFWI_PARSER.parse_bios_bpdt (img_data) if bios_comp is not None: ifwi_comp.add_child (bios_comp) return ifwi_comp spi_descriptor = SPI_DESCRIPTOR.from_buffer(img_data) if spi_descriptor.fl_val_sig != spi_descriptor.DESC_SIGNATURE: # no SPI descriptor, try to check the flash map bios_comp = IFWI_PARSER.parse_bios_region (img_data, 0) if bios_comp is not None: ifwi_comp.add_child (bios_comp) return ifwi_comp # It is a full IFWI image bios_comp = None ifwi_comp = COMPONENT('IFWI', COMPONENT.COMP_TYPE['IFWI'], 0, len(img_data)) rgn_dict = sorted(SPI_DESCRIPTOR.FLASH_REGIONS, key=SPI_DESCRIPTOR.FLASH_REGIONS.get) for rgn in rgn_dict: rgn_start, rgn_limit = IFWI_PARSER.find_ifwi_region(spi_descriptor, rgn) if rgn_start is None: continue rgn_comp = COMPONENT(rgn.upper(), COMPONENT.COMP_TYPE['RGN'], rgn_start, rgn_limit - rgn_start + 1) if rgn == 'bios': bios_comp = rgn_comp else: ifwi_comp.add_child (rgn_comp) if bios_comp is None: return None bios_start = bios_comp.offset bios_limit = bios_comp.offset + bios_comp.length - 1 if not (img_data[bios_start] == 0xAA and img_data[bios_start + 1] == 0x55): # normal layout new_bios_comp = IFWI_PARSER.parse_bios_region (img_data[bios_start:bios_limit+1], bios_start) if new_bios_comp is not None: bios_comp = new_bios_comp ifwi_comp.add_child (bios_comp) ifwi_comp.child.sort (key=lambda x: x.offset) return ifwi_comp # Sort region by offset ifwi_comp.add_child (bios_comp) ifwi_comp.child.sort (key=lambda x: x.offset) # It is BPDT format bp_offset = [bios_start, (bios_start + bios_limit + 1) // 2] for idx, offset in enumerate(bp_offset): bp_comp = COMPONENT('BP%d' % idx, COMPONENT.COMP_TYPE['BP'], offset, (bios_limit - bios_start + 1) // 2) sub_part_offset = 0 while True: bpdt, sbpdt_entry = IFWI_PARSER.bpdt_parser(img_data, offset, sub_part_offset) bpdt_prefix = '' if sub_part_offset == 0 else 'S' bpdt_size = sbpdt_entry.sub_part_offset if sbpdt_entry else bpdt_comp.child[-1].length bpdt_comp = COMPONENT('%sBPDT' % bpdt_prefix, COMPONENT.COMP_TYPE['BPDT'], offset + sub_part_offset, bpdt_size) sorted_bpdt = sorted(bpdt, key=lambda x: x[0].sub_part_offset) for part, dir_list in sorted_bpdt: if not part.sub_part_size: continue part_comp = COMPONENT( str(part.type), COMPONENT.COMP_TYPE['PART'], offset + part.sub_part_offset, part.sub_part_size) sorted_dir = sorted(dir_list, key=lambda x: x.entry_offset) for dir in sorted_dir: file_comp = COMPONENT(dir.entry_name.decode(), COMPONENT.COMP_TYPE['FILE'], part_comp.offset + dir.entry_offset, dir.entry_size) part_comp.add_child(file_comp) bpdt_comp.add_child(part_comp) bp_comp.add_child(bpdt_comp) if sbpdt_entry: sub_part_offset = sbpdt_entry.sub_part_offset else: break bios_comp.add_child(bp_comp) return ifwi_comp if __name__ == '__main__': parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(title='commands') parser_view = subparsers.add_parser('view', help='print IFWI component layout') parser_view.set_defaults(which='view') parser_view.add_argument('-i', '--input-image', dest='ifwi_image', type=str, required=True, help='Specify input IFWI image file path') parser_replace = subparsers.add_parser('replace', help='replace component in IFWI') parser_replace.set_defaults(which='replace') parser_replace.add_argument('-f', '--component-image', dest='comp_image', type=str, default = '', help="Specify component image file") parser_replace.add_argument('-i', '--input-image', dest='ifwi_image', type=str, required=True, help='Specify input IFWI image file path') parser_replace.add_argument('-o', '--output-image', dest='output_image', type=str, default = '', help='Specify output IFWI image file path') parser_replace.add_argument('-p', '--path', dest='component_path', type=str, default = '', help='Specify replace path in IFWI image flashmap') parser_replace.add_argument('-u', '--input-ucode-dir', dest='input_ucode_dir', type=str, default = '', help="Specify a directory containing all microcode to pack if the '-p' path is a microcode component") parser_extract = subparsers.add_parser('extract', help='extract component from IFWI') parser_extract.set_defaults(which='extract') parser_extract.add_argument('-i', '--input-image', dest='ifwi_image', type=str, required=True, help='Specify input IFWI image file path') parser_extract.add_argument('-o', '--output-component', dest='output_image', type=str, default = '', help='Specify output component image file path') parser_extract.add_argument('-p', '--path', dest='component_path', type=str, default = '', help='Specify component path to be extracted from IFWI image') parser_extract.add_argument('-u', '--output-ucode-dir', dest='output_ucode_dir', type=str, default = '', help="Specify a directory to store the extraced microcode binaries if the '-p' path is a microcode component") args = parser.parse_args() ifwi = None ifwi_bin = bytearray (get_file_data (args.ifwi_image)) ret = -1 show = False if args.which == 'view': show = True elif args.which == 'extract': comp_bin = bytearray () if not args.component_path: show = True else: ret = IFWI_PARSER.extract_component (ifwi_bin, comp_bin, args.component_path) if ret == 0: out_image = args.output_image if out_image: gen_file_from_object (out_image, comp_bin) print ("Components @ %s was extracted successfully!" % args.component_path) parts = args.component_path.split('/') if len(parts) > 0 and parts[-1] == 'UCOD' and args.output_ucode_dir: out_dir = args.output_ucode_dir if not os.path.exists(out_dir): os.mkdir (out_dir) else: if not os.path.isdir (out_dir): parser.error('-u needs to be a directory !') ucode = UCODE_PARSER () ucode.dump (comp_bin) ucode.extract (comp_bin, out_dir) elif args.which == 'replace': if args.comp_image and args.input_ucode_dir: parser_replace.error("Option '-f' and '-u' are exclusive !") if not args.component_path: show = True else: if args.input_ucode_dir: parts = args.component_path.split('/') if len(parts) > 0 and parts[-1] == 'UCOD': comp_bin = UCODE_PARSER.pack (args.input_ucode_dir) else: parser_replace.error("Option '-p' needs to be a microcode component path !") else: if not args.comp_image: parser_replace.error('Component image file is required when path is specified!') comp_bin = bytearray (get_file_data (args.comp_image)) ret = IFWI_PARSER.replace_component (ifwi_bin, comp_bin, args.component_path) if ret == 0: if args.input_ucode_dir: ret = IFWI_PARSER.update_ucode_fit_entry (ifwi_bin, args.component_path) if ret == 0: out_image = args.output_image if args.output_image else args.ifwi_image gen_file_from_object (out_image, ifwi_bin) print ("Components @ %s was replaced successfully!" % args.component_path) if show: ifwi = IFWI_PARSER.parse_ifwi_binary (ifwi_bin) if ifwi: IFWI_PARSER.print_tree (ifwi) ret = 0 if ret != 0: raise Exception ('Execution failed for %s !' % sys.argv[0]) sys.exit(ret)