diff options
Diffstat (limited to 'resources/blobs')
| -rwxr-xr-x | resources/blobs/me7_update_parser.py | 616 | ||||
| -rw-r--r-- | resources/blobs/sources | 76 | 
2 files changed, 0 insertions, 692 deletions
| diff --git a/resources/blobs/me7_update_parser.py b/resources/blobs/me7_update_parser.py deleted file mode 100755 index e3e91413..00000000 --- a/resources/blobs/me7_update_parser.py +++ /dev/null @@ -1,616 +0,0 @@ -#!/usr/bin/env python3 - -"""ME7 Update binary parser.""" - -# Copyright (C) 2020 Tom Hiller <thrilleratplay@gmail.com> -# Copyright (C) 2016-2018 Nicola Corna <nicola@corna.info> -# -# 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("<I", 0) -    ROM_BYPASS_INSTR_3 = pack("<I", 0) - -    # $FPT Partition table header -    HEADER_TAG = "$FPT".encode() -    HEADER_NUM_PARTITIONS = pack("<I", 1) -    HEADER_VERSION = b"\x20"  # version 2.0 -    HEADER_ENTRY_TYPE = b"\x10" -    HEADER_LENGTH = b"\x30" -    HEADER_CHECKSUM = pack("<B", 0) -    HEADER_FLASH_CYCLE_LIFE = pack("<H", 7) -    HEADER_FLASH_CYCLE_LIMIT = pack("<H", 100) -    HEADER_UMA_SIZE = pack("<H", 32) -    HEADER_FLAGS = binascii.unhexlify("000000FCFFFF") -    HEADER_FITMAJOR = pack("<H", 0) -    HEADER_FITMINOR = pack("<H", 0) -    HEADER_FITHOTFIX = pack("<H", 0) -    HEADER_FITBUILD = pack("<H", 0) - -    FTPR_header_layout = bytearray( -        ROM_BYPASS_INSTR_0 -        + ROM_BYPASS_INSTR_1 -        + ROM_BYPASS_INSTR_2 -        + ROM_BYPASS_INSTR_3 -        + HEADER_TAG -        + HEADER_NUM_PARTITIONS -        + HEADER_VERSION -        + HEADER_ENTRY_TYPE -        + HEADER_LENGTH -        + HEADER_CHECKSUM -        + HEADER_FLASH_CYCLE_LIFE -        + HEADER_FLASH_CYCLE_LIMIT -        + HEADER_UMA_SIZE -        + HEADER_FLAGS -        + HEADER_FITMAJOR -        + HEADER_FITMINOR -        + HEADER_FITHOTFIX -        + HEADER_FITBUILD -    ) - -    # Update checksum -    FTPR_header_layout[27] = (0x100 - sum(FTPR_header_layout) & 0xFF) & 0xFF - -    return FTPR_header_layout - - -def generateFtpPartition() -> 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("<I", 1) -    ENTRY_MAX_TOKENS = pack("<I", 1) -    ENTRY_SCRATCH_SECTORS = pack("<I", 0) -    ENTRY_FLAGS = pack( -        "<I", -        ( -            EntryFlags.ExclBlockUse -            + EntryFlags.Execute -            + EntryFlags.Write -            + EntryFlags.Read -            + EntryFlags.DirectAccess -        ), -    ) - -    partition = ( -        ENTRY_NAME -        + ENTRY_OWNER -        + ENTRY_OFFSET -        + ENTRY_LENGTH -        + ENTRY_START_TOKENS -        + ENTRY_MAX_TOKENS -        + ENTRY_SCRATCH_SECTORS -        + ENTRY_FLAGS -    ) - -    # offset of the partition - length of partition entry -length of header -    pad_len = MINIFIED_FTPR_OFFSET - (len(partition) + PARTITION_HEADER_OFFSET) -    padding = b"" - -    for i in range(0, pad_len): -        padding += b"\xFF" - -    return partition + padding - - -############################################################################ - - -class OutOfRegionException(Exception): -    """Out of Region Exception.""" - -    pass - - -class clean_ftpr: -    """Clean Factory Parition (FTPR).""" - -    UNREMOVABLE_MODULES = ("ROMP", "BUP") -    COMPRESSION_TYPE_NAME = ("uncomp.", "Huffman", "LZMA") - -    def __init__(self, ftpr: bytes): -        """Init.""" -        self.orig_ftpr = ftpr -        self.ftpr = ftpr -        self.mod_headers: List[bytes] = [] -        self.check_and_clean_ftpr() - -    ##################################################################### -    # tilities -    ##################################################################### -    def slice(self, offset: int, size: int) -> 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("<I", data)[0] - -    def bytes_to_ascii(self, data: bytes) -> 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("<II", llut[0x10:0x18])) -        nonzero_offsets = [huffman_stream_end] -        offsets = [] - -        for i in range(0, chunk_count): -            llut_start = 0x40 + (i * 4) -            llut_end = 0x44 + (i * 4) - -            chunk = llut[llut_start:llut_end] -            offset = 0 - -            if chunk[3] != 0x80: -                offset = self.unpack_val(chunk[0:3] + b"\x00") - -            offsets.append([offset, 0]) - -            if offset != 0: -                nonzero_offsets.append(offset) - -        nonzero_offsets.sort() - -        for i in offsets: -            if i[0] != 0: -                i[1] = nonzero_offsets[nonzero_offsets.index(i[0]) + 1] - -        return offsets - -    def relocate_partition(self) -> int: -        """Relocate partition.""" -        new_offset = MINIFIED_FTPR_OFFSET -        name = self.bytes_to_ascii(self.slice(PARTITION_HEADER_OFFSET, 4)) - -        old_offset, partition_size = unpack( -            "<II", self.slice(PARTITION_HEADER_OFFSET + 0x8, 0x8) -        ) - -        llut_start = 0 -        for mod_header in self.mod_headers: -            if (self.unpack_val(mod_header[0x50:0x54]) >> 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("<H", self.slice(llut_start + 0x9, 2))[0] -            new_offset = max( -                new_offset, llut_start_corr - llut_start - 0x40 + old_offset -            ) -            new_offset = ((new_offset + 0x1F) // 0x20) * 0x20 -        offset_diff = new_offset - old_offset - -        print( -            "Relocating {} from {:#x} - {:#x} to {:#x} - {:#x}...".format( -                name, -                old_offset, -                old_offset + partition_size, -                new_offset, -                new_offset + partition_size, -            ) -        ) - -        print(" Adjusting FPT entry...") -        self.write_ftpr_data( -            PARTITION_HEADER_OFFSET + 0x08, -            pack("<I", new_offset), -        ) - -        if self.mod_headers: -            if llut_start != 0: -                if self.slice(llut_start, 4) == b"LLUT": -                    print(" Adjusting LUT start offset...") -                    llut_offset = pack( -                        "<I", llut_start + offset_diff + 0x40 - llut_start_corr -                    ) -                    self.write_ftpr_data(llut_start + 0x0C, llut_offset) - -                    print(" Adjusting Huffman start offset...") -                    old_huff_offset = self.unpack_next_int(llut_start + 0x14) -                    ftpr_offset_diff = MINIFIED_FTPR_OFFSET - ORIG_FTPR_OFFSET -                    self.write_ftpr_data( -                        llut_start + 0x14, -                        pack("<I", old_huff_offset + ftpr_offset_diff), -                    ) - -                    print(" Adjusting chunks offsets...") -                    chunk_count = self.unpack_next_int(llut_start + 0x4) -                    offset = llut_start + 0x40 -                    offset_end = chunk_count * 4 -                    chunks = bytearray(self.slice(offset, offset_end)) - -                    for i in range(0, offset_end, 4): -                        i_plus_3 = i + 3 - -                        if chunks[i_plus_3] != 0x80: -                            chunks[i:i_plus_3] = pack( -                                "<I", -                                self.unpack_val(chunks[i:i_plus_3] + b"\x00") -                                + (MINIFIED_FTPR_OFFSET - ORIG_FTPR_OFFSET), -                            )[0:3] -                    self.write_ftpr_data(offset, bytes(chunks)) -                else: -                    sys.exit("Huffman modules present but no LLUT found!") -            else: -                print(" No Huffman modules found") - -        print(" Moving data...") -        partition_size = min(partition_size, FTPR_END - old_offset) - -        if ( -            old_offset + partition_size <= FTPR_END -            and new_offset + partition_size <= FTPR_END -        ): -            for i in range(0, partition_size, 4096): -                block_length = min(partition_size - i, 4096) -                block = self.slice(old_offset + i, block_length) -                self.clear_ftpr_data(old_offset + i, len(block)) - -                self.write_ftpr_data(new_offset + i, block) -        else: -            raise OutOfRegionException() - -        return new_offset - -    def remove_modules(self) -> 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("<I", f.read(4))[0] -    signature = int(binascii.hexlify(f.read(0x100)[::-1]), 16) - -    header_len = unpack("<I", header[0x4:0x8])[0] * 4 -    manifest_len = unpack("<I", header[0x18:0x1C])[0] * 4 -    f.seek(offset + header_len) - -    sha256 = hashlib.sha256() -    sha256.update(header) -    tmp = f.read(manifest_len - header_len) -    sha256.update(tmp) - -    decrypted_sig = pow(signature, public_exponent, modulus) -    return "{:#x}".format(decrypted_sig).endswith(sha256.hexdigest())  # FIXME - - -########################################################################## - - -def generate_me_blob(input_file: str, output_file: str) -> 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/resources/blobs/sources b/resources/blobs/sources deleted file mode 100644 index 1818c66d..00000000 --- a/resources/blobs/sources +++ /dev/null @@ -1,76 +0,0 @@ -# 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 -} - -# nvidia vga option rom for dgpu models of Dell Latitude E6400 -# for downloading the nvidia rom to pciroms/pci0x10de,0x06eb.rom -{e6400nvidia}{ -	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 -} | 
