From da3c9bb3c5c3b1f2e6e67a3695ce39b17bf68d5b Mon Sep 17 00:00:00 2001 From: Leah Rowe Date: Mon, 4 Sep 2023 02:36:41 +0100 Subject: merge config/ and resources/ Signed-off-by: Leah Rowe --- config/blobs/me7_update_parser.py | 616 ++++++++++++++++++++++++++++++++++++++ config/blobs/sources | 112 +++++++ 2 files changed, 728 insertions(+) create mode 100755 config/blobs/me7_update_parser.py create mode 100644 config/blobs/sources (limited to 'config/blobs') diff --git a/config/blobs/me7_update_parser.py b/config/blobs/me7_update_parser.py new file mode 100755 index 00000000..e3e91413 --- /dev/null +++ b/config/blobs/me7_update_parser.py @@ -0,0 +1,616 @@ +#!/usr/bin/env python3 + +"""ME7 Update binary parser.""" + +# Copyright (C) 2020 Tom Hiller +# Copyright (C) 2016-2018 Nicola Corna +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +# Based on the amazing me_cleaner, https://github.com/corna/me_cleaner, parses +# the required signed partition from an ME update file to generate a valid +# flashable ME binary. +# +# This was written for Heads ROM, https://github.com/osresearch/heads +# to allow continuous integration reproducible builds for Lenovo xx20 models +# (X220, T420, T520, etc). +# +# A full model list can be found: +# https://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/83rf46ww.txt + + +from struct import pack, unpack +from typing import List +import argparse +import sys +import hashlib +import binascii +import os.path + +############################################################################# + +FTPR_END = 0x76000 +MINIFIED_FTPR_OFFSET = 0x400 # offset start of Factory Partition (FTPR) +ORIG_FTPR_OFFSET = 0xCC000 +PARTITION_HEADER_OFFSET = 0x30 # size of partition header + +DEFAULT_OUTPUT_FILE_NAME = "flashregion_2_intel_me.bin" + +############################################################################# + + +class EntryFlags: + """EntryFlag bitmap values.""" + + ExclBlockUse = 8192 + WOPDisable = 4096 + Logical = 2048 + Execute = 1024 + Write = 512 + Read = 256 + DirectAccess = 128 + Type = 64 + + +def generateHeader() -> bytes: + """Generate Header.""" + ROM_BYPASS_INSTR_0 = binascii.unhexlify("2020800F") + ROM_BYPASS_INSTR_1 = binascii.unhexlify("40000010") + ROM_BYPASS_INSTR_2 = pack(" bytes: + """Partition table entry.""" + ENTRY_NAME = binascii.unhexlify("46545052") + ENTRY_OWNER = binascii.unhexlify("FFFFFFFF") # "None" + ENTRY_OFFSET = binascii.unhexlify("00040000") + ENTRY_LENGTH = binascii.unhexlify("00600700") + ENTRY_START_TOKENS = pack(" bytes: + """Copy data of a given size from FTPR starting from offset.""" + offset_end = offset + size + return self.ftpr[offset:offset_end] + + def unpack_next_int(self, offset: int) -> int: + """Sugar syntax for unpacking a little-endian UINT at offset.""" + return self.unpack_val(self.slice(offset, 4)) + + def unpack_val(self, data: bytes) -> int: + """Sugar syntax for unpacking a little-endian unsigned integer.""" + return unpack(" str: + """Decode bytes into ASCII.""" + return data.rstrip(b"\x00").decode("ascii") + + def clear_ftpr_data(self, start: int, end: int) -> None: + """Replace values in range with 0xFF.""" + empty_data = bytes() + + for i in range(0, end - start): + empty_data += b"\xff" + self.write_ftpr_data(start, empty_data) + + def write_ftpr_data(self, start: int, data: bytes) -> None: + """Replace data in FTPR starting at a given offset.""" + end = len(data) + start + + new_partition = self.ftpr[:start] + new_partition += data + + if end != FTPR_END: + new_partition += self.ftpr[end:] + + self.ftpr = new_partition + + ###################################################################### + # FTPR cleanig/checking functions + ###################################################################### + def get_chunks_offsets(self, llut: bytes): + """Calculate Chunk offsets from LLUT.""" + chunk_count = self.unpack_val(llut[0x04:0x08]) + huffman_stream_end = sum(unpack(" int: + """Relocate partition.""" + new_offset = MINIFIED_FTPR_OFFSET + name = self.bytes_to_ascii(self.slice(PARTITION_HEADER_OFFSET, 4)) + + old_offset, partition_size = unpack( + "> 4) & 7 == 0x01: + llut_start = self.unpack_val(mod_header[0x38:0x3C]) + llut_start += old_offset + break + + if self.mod_headers and llut_start != 0: + # Bytes 0x9:0xb of the LLUT (bytes 0x1:0x3 of the AddrBase) are + # added to the SpiBase (bytes 0xc:0x10 of the LLUT) to compute the + # final start of the LLUT. Since AddrBase is not modifiable, we can + # act only on SpiBase and here we compute the minimum allowed + # new_offset. + llut_start_corr = unpack(" int: + """Remove modules.""" + unremovable_huff_chunks = [] + chunks_offsets = [] + base = 0 + chunk_size = 0 + end_addr = 0 + + for mod_header in self.mod_headers: + name = self.bytes_to_ascii(mod_header[0x04:0x14]) + offset = self.unpack_val(mod_header[0x38:0x3C]) + size = self.unpack_val(mod_header[0x40:0x44]) + flags = self.unpack_val(mod_header[0x50:0x54]) + comp_type = (flags >> 4) & 7 + comp_type_name = self.COMPRESSION_TYPE_NAME[comp_type] + + print(" {:<16} ({:<7}, ".format(name, comp_type_name), end="") + + # If compresion type uncompressed or LZMA + if comp_type == 0x00 or comp_type == 0x02: + offset_end = offset + size + range_msg = "0x{:06x} - 0x{:06x} ): " + print(range_msg.format(offset, offset_end), end="") + + if name in self.UNREMOVABLE_MODULES: + end_addr = max(end_addr, offset + size) + print("NOT removed, essential") + else: + offset_end = min(offset + size, FTPR_END) + self.clear_ftpr_data(offset, offset_end) + print("removed") + + # Else if compression type huffman + elif comp_type == 0x01: + if not chunks_offsets: + # Check if Local Look Up Table (LLUT) is present + if self.slice(offset, 4) == b"LLUT": + llut = self.slice(offset, 0x40) + + chunk_count = self.unpack_val(llut[0x4:0x8]) + base = self.unpack_val(llut[0x8:0xC]) + 0x10000000 + chunk_size = self.unpack_val(llut[0x30:0x34]) + + llut = self.slice(offset, (chunk_count * 4) + 0x40) + + # calculate offsets of chunks from LLUT + chunks_offsets = self.get_chunks_offsets(llut) + else: + no_llut_msg = "Huffman modules found," + no_llut_msg += "but LLUT is not present." + sys.exit(no_llut_msg) + + module_base = self.unpack_val(mod_header[0x34:0x38]) + module_size = self.unpack_val(mod_header[0x3C:0x40]) + first_chunk_num = (module_base - base) // chunk_size + last_chunk_num = first_chunk_num + module_size // chunk_size + huff_size = 0 + + chunk_length = last_chunk_num + 1 + for chunk in chunks_offsets[first_chunk_num:chunk_length]: + huff_size += chunk[1] - chunk[0] + + size_in_kiB = "~" + str(int(round(huff_size / 1024))) + " KiB" + print( + "fragmented data, {:<9}): ".format(size_in_kiB), + end="", + ) + + # Check if module is in the unremovable list + if name in self.UNREMOVABLE_MODULES: + print("NOT removed, essential") + + # add to list of unremovable chunks + for x in chunks_offsets[first_chunk_num:chunk_length]: + if x[0] != 0: + unremovable_huff_chunks.append(x) + else: + print("removed") + + # Else unknown compression type + else: + unkwn_comp_msg = " 0x{:06x} - 0x{:06x}): " + unkwn_comp_msg += "unknown compression, skipping" + print(unkwn_comp_msg.format(offset, offset + size), end="") + + if chunks_offsets: + removable_huff_chunks = [] + + for chunk in chunks_offsets: + # if chunk is not in a unremovable chunk, it must be removable + if all( + not ( + unremovable_chk[0] <= chunk[0] < unremovable_chk[1] + or unremovable_chk[0] < chunk[1] <= unremovable_chk[1] + ) + for unremovable_chk in unremovable_huff_chunks + ): + removable_huff_chunks.append(chunk) + + for removable_chunk in removable_huff_chunks: + if removable_chunk[1] > removable_chunk[0]: + chunk_start = removable_chunk[0] - ORIG_FTPR_OFFSET + chunk_end = removable_chunk[1] - ORIG_FTPR_OFFSET + self.clear_ftpr_data(chunk_start, chunk_end) + + end_addr = max( + end_addr, max(unremovable_huff_chunks, key=lambda x: x[1])[1] + ) + end_addr -= ORIG_FTPR_OFFSET + + return end_addr + + def find_mod_header_size(self) -> None: + """Find module header size.""" + self.mod_header_size = 0 + data = self.slice(0x290, 0x84) + + # check header size + if data[0x0:0x4] == b"$MME": + if data[0x60:0x64] == b"$MME" or self.num_modules == 1: + self.mod_header_size = 0x60 + elif data[0x80:0x84] == b"$MME": + self.mod_header_size = 0x80 + + def find_mod_headers(self) -> None: + """Find module headers.""" + data = self.slice(0x290, self.mod_header_size * self.num_modules) + + for i in range(0, self.num_modules): + header_start = i * self.mod_header_size + header_end = (i + 1) * self.mod_header_size + self.mod_headers.append(data[header_start:header_end]) + + def resize_partition(self, end_addr: int) -> None: + """Resize partition.""" + spared_blocks = 4 + if end_addr > 0: + end_addr = (end_addr // 0x1000 + 1) * 0x1000 + end_addr += spared_blocks * 0x1000 + + # partition header not added yet + # remove trailing data the same size as the header. + end_addr -= MINIFIED_FTPR_OFFSET + + me_size_msg = "The ME minimum size should be {0} " + me_size_msg += "bytes ({0:#x} bytes)" + print(me_size_msg.format(end_addr)) + print("Truncating file at {:#x}...".format(end_addr)) + self.ftpr = self.ftpr[:end_addr] + + def check_and_clean_ftpr(self) -> None: + """Check and clean FTPR (factory partition).""" + self.num_modules = self.unpack_next_int(0x20) + self.find_mod_header_size() + + if self.mod_header_size != 0: + self.find_mod_headers() + + # ensure all of the headers begin with b'$MME' + if all(hdr.startswith(b"$MME") for hdr in self.mod_headers): + end_addr = self.remove_modules() + new_offset = self.relocate_partition() + end_addr += new_offset + + self.resize_partition(end_addr) + + # flip bit + # XXX: I have no idea why this works and passes RSA signiture + self.write_ftpr_data(0x39, b"\x00") + else: + sys.exit( + "Found less modules than expected in the FTPR " + "partition; skipping modules removal and exiting." + ) + else: + sys.exit( + "Can't find the module header size; skipping modules" + "removal and exiting." + ) + + +########################################################################## + + +def check_partition_signature(f, offset) -> bool: + """check_partition_signature copied/shamelessly stolen from me_cleaner.""" + f.seek(offset) + header = f.read(0x80) + modulus = int(binascii.hexlify(f.read(0x100)[::-1]), 16) + public_exponent = unpack(" None: + """Generate ME blob.""" + print("Starting ME 7.x Update parser.") + + orig_f = open(input_file, "rb") + cleaned_ftpr = clean_ftpr(orig_f.read(FTPR_END)) + orig_f.close() + + fo = open(output_file, "wb") + fo.write(generateHeader()) + fo.write(generateFtpPartition()) + fo.write(cleaned_ftpr.ftpr) + fo.close() + + +def verify_output(output_file: str) -> None: + """Verify Generated ME file.""" + file_verifiy = open(output_file, "rb") + + if check_partition_signature(file_verifiy, MINIFIED_FTPR_OFFSET): + print(output_file + " is VALID") + file_verifiy.close() + else: + print(output_file + " is INVALID!!") + file_verifiy.close() + sys.exit("The FTPR partition signature is not valid.") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Tool to remove as much code " + "as possible from Intel ME/TXE 7.x firmware " + "update and create paratition for a flashable ME parition." + ) + + +parser.add_argument("file", help="ME/TXE image or full dump") +parser.add_argument( + "-O", + "--output", + metavar="output_file", + help="save " + "save file name other than the default '" + DEFAULT_OUTPUT_FILE_NAME + "'", +) + +args = parser.parse_args() + +output_file_name = DEFAULT_OUTPUT_FILE_NAME if not args.output else args.output + +# Check if output file exists, ask to overwrite or exit +if os.path.isfile(output_file_name): + input_msg = output_file_name + input_msg += " exists. Do you want to overwrite? [y/N]: " + if not str(input(input_msg)).lower().startswith("y"): + sys.exit("Not overwriting file. Exiting.") + +generate_me_blob(args.file, output_file_name) +verify_output(output_file_name) diff --git a/config/blobs/sources b/config/blobs/sources new file mode 100644 index 00000000..8f82dfc6 --- /dev/null +++ b/config/blobs/sources @@ -0,0 +1,112 @@ +# This file holds the download sources for various intel blobs +# board shortnames are listed and enclosed by '{}' followed by an opening +# and closing '{}' for all blobs available for the board. +# The board shortname must be the name of the board minus the trailing rom size. +# If you want to make additions, try to add a backup url for download links and +# list hashes as sha1 sums. + +{x230 x230t x230i x230edp t430 t530 w530}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe +} + +{x220 x220t t420 t520 t420s}{ + DL_hash fa0f96c8f36646492fb8c57ad3296bf5f647d9c5 + DL_url https://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/83rf46ww.exe + DL_url_bkup https://web.archive.org/web/20220202201637/https://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/83rf46ww.exe +} + +{t440pmrc w541mrc t440plibremrc w541}{ + DL_hash b2f2a1baa1f0c8139e46b0d3e206386ff197bed5 + DL_url https://download.lenovo.com/pccbbs/mobiles/glrg22ww.exe + DL_url_bkup https://web.archive.org/web/20211120031520/https://download.lenovo.com/pccbbs/mobiles/glrg22ww.exe +} + +{hp8200sff}{ + DL_hash c59e693effc1862c38cc4caa15be0a6a92557e0b + DL_url https://ftp.ext.hp.com/pub/softpaq/sp96001-96500/sp96026.exe + DL_url_bkup https://web.archive.org/web/20220708171920/https://ftp.ext.hp.com/pub/softpaq/sp96001-96500/sp96026.exe +} + +{hp8300usdt}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe +} + +{hp2560p}{ + DL_hash fa0f96c8f36646492fb8c57ad3296bf5f647d9c5 + DL_url https://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/83rf46ww.exe + DL_url_bkup https://web.archive.org/web/20220202201637/https://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/83rf46ww.exe + + EC_hash c1b1fb0a525cf90459bf024f407e302314bd981b + EC_url https://ftp.hp.com/pub/softpaq/sp85501-86000/sp85526.exe + EC_url_bkup https://web.archive.org/web/20230416125725/https://ftp.hp.com/pub/softpaq/sp85501-86000/sp85526.exe +} + +{hp2570p}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + + EC_hash a896ef72799e8abd4d0601ec415a2113b2a7f240 + EC_url https://ftp.hp.com/pub/softpaq/sp96001-96500/sp96085.exe + EC_url_bkup https://web.archive.org/web/20230610174558/https://ftp.hp.com/pub/softpaq/sp96001-96500/sp96085.exe +} + +{hp9470m}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + + EC_hash 1a03e985552060a9dfe7c40b5ea97ecfb2794583 + EC_url https://ftp.hp.com/pub/softpaq/sp96001-96500/sp96090.exe + EC_url_bkup http://web.archive.org/web/20220504072602/https://ftp.ext.hp.com/pub/softpaq/sp96001-96500/sp96090.exe +} + +{hp2170p}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + + EC_hash 201f7f40c02df42188f4ee3073f8df7f21ab6fa1 + EC_url https://ftp.hp.com/pub/softpaq/sp96001-96500/sp96088.exe + EC_url_bkup https://ftp.hp.com/pub/softpaq/sp96001-96500/sp96088.exe +} + +{t1650}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + + SCH5545EC_DL_url https://dl.dell.com/FOLDER05065992M/1/T1650A28.exe + SCH5545EC_DL_url_bkup https://web.archive.org/web/20230811151654/https://dl.dell.com/FOLDER05065992M/1/T1650A28.exe + SCH5545EC_DL_hash 9651bab78b8a0063997f568f7698590c7deb7925 +} + +{hp8470pintel}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + + EC_hash 20e49c92f610e0bba4b67faac7ae2bc78f421cb7 + EC_url https://ftp.hp.com/pub/softpaq/sp77501-78000/sp77818.exe + EC_url_bkup https://ftp.hp.com/pub/softpaq/sp77501-78000/sp77818.exe +} + +# nvidia vga option rom for dgpu models of Dell Latitude E6400 +# for downloading the nvidia rom to pciroms/pci10de,06eb.rom +{e6400}{ + E6400_VGA_DL_hash a24ed919e80287b281e407d525af31f307746250 + E6400_VGA_DL_url https://dl.dell.com/FOLDER01530530M/1/E6400A34.exe + E6400_VGA_DL_url_bkup https://web.archive.org/web/20230506014903/https://dl.dell.com/FOLDER01530530M/1/E6400A34.exe + E6400_VGA_offset 274451 + E6400_VGA_romname mod_21.bin +} + +{e6430}{ + DL_hash 039c89c6d44ae11ae2510cbd5fed756e97ed9a31 + DL_url https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe + DL_url_bkup https://web.archive.org/web/20210706183911/https://download.lenovo.com/pccbbs/mobiles/g1rg24ww.exe +} -- cgit v1.2.1