From 98f30340cf7443d4ef63a855ee96935946223da0 Mon Sep 17 00:00:00 2001 From: Nicola Corna Date: Tue, 8 Aug 2017 21:24:49 +0200 Subject: util/me_cleaner: Pull the latest changes from upstream Relevant changes (commit 2e8761e): * Add an option to truncate the ME image file * Add full support for Skylake (ME 11) and following, including modules removal, truncation informations and partition relocation * Add two options to generate a shrinked ME image file and the corresponding descriptor with a modified flash layout * Update README.md * Bug fixes Also add a link to the usage guide in the Kconfig help. Change-Id: I690c5d558139f64f38babf3c0988b53834ba8b37 Signed-off-by: Nicola Corna Reviewed-on: https://review.coreboot.org/20915 Reviewed-by: Arthur Heymans Tested-by: build bot (Jenkins) Reviewed-by: Philipp Deppenwiese --- util/me_cleaner/me_cleaner.py | 474 ++++++++++++++++++++++++++++++------------ 1 file changed, 340 insertions(+), 134 deletions(-) (limited to 'util/me_cleaner/me_cleaner.py') diff --git a/util/me_cleaner/me_cleaner.py b/util/me_cleaner/me_cleaner.py index 8ca5498134..e8a89cc92c 100755 --- a/util/me_cleaner/me_cleaner.py +++ b/util/me_cleaner/me_cleaner.py @@ -25,14 +25,15 @@ from struct import pack, unpack min_ftpr_offset = 0x400 spared_blocks = 4 -unremovable_modules = ("BUP", "ROMP") +unremovable_modules = ("ROMP", "BUP") +unremovable_modules_me11 = ("rbe", "kernel", "syslib", "bup") class OutOfRegionException(Exception): pass -class regionFile: +class RegionFile: def __init__(self, f, region_start, region_end): self.f = f self.region_start = region_start @@ -81,6 +82,13 @@ class regionFile: else: raise OutOfRegionException() + def save(self, filename, size): + self.f.seek(self.region_start) + copyf = open(filename, "w+b") + for i in range(0, size, 4096): + copyf.write(self.f.read(4096 if size - i >= 4096 else size - i)) + return copyf + def get_chunks_offsets(llut, me_start): chunk_count = unpack("> 25 + + modules.append((name, offset, comp_type)) + + modules.sort(key=lambda x: x[1]) + + for i in range(0, module_count): + name = modules[i][0] + offset = partition_offset + modules[i][1] + end = partition_offset + modules[i + 1][1] + removed = False + + if name.endswith(".man") or name.endswith(".met"): + compression = "uncompressed" + else: + compression = comp_str[modules[i][2]] + + sys.stdout.write(" {:<12} ({:<12}, 0x{:06x} - 0x{:06x}): " + .format(name, compression, offset, end)) + + if name.endswith(".man"): + print("NOT removed, partition manif.") + elif name.endswith(".met"): + print("NOT removed, module metadata") + elif any(name.startswith(m) for m in unremovable_modules_me11): + print("NOT removed, essential") + else: + removed = True + f.fill_range(offset, min(end, me_end), b"\xff") + print("removed") + + if not removed: + end_data = max(end_data, end) + + if relocate: + new_offset = relocate_partition(f, me_start, me_end, me_start + 0x30, + min_offset + me_start, []) + end_data += new_offset - partition_offset + partition_offset = new_offset + + return end_data, partition_offset + + +def check_mn2_tag(f, offset): + f.seek(offset + 0x1c) + tag = f.read(4) + if tag != b"$MN2": + sys.exit("Wrong FTPR manifest tag ({}), this image may be corrupted" + .format(tag)) + + +def flreg_to_start_end(flreg): + return (flreg & 0x7fff) << 12, (flreg >> 4 & 0x7fff000 | 0xfff) + 1 + + +def start_end_to_flreg(start, end): + return (start & 0x7fff000) >> 12 | ((end - 1) & 0x7fff000) << 4 + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Tool to remove as much code " - "as possible from Intel ME/TXE firmwares") + "as possible from Intel ME/TXE firmware " + "images") parser.add_argument("file", help="ME/TXE image or full dump") parser.add_argument("-O", "--output", help="save the modified image in a " "separate file, instead of modifying the original " "file") parser.add_argument("-r", "--relocate", help="relocate the FTPR partition " - "to the top of the ME region", action="store_true") + "to the top of the ME region to save even more space", + action="store_true") parser.add_argument("-k", "--keep-modules", help="don't remove the FTPR " "modules, even when possible", action="store_true") parser.add_argument("-d", "--descriptor", help="remove the ME/TXE " "Read/Write permissions to the other regions on the " "flash from the Intel Flash Descriptor (requires a " "full dump)", action="store_true") + parser.add_argument("-t", "--truncate", help="truncate the empty part of " + "the firmware (requires a separated ME/TXE image or " + "--extract-me)", action="store_true") parser.add_argument("-c", "--check", help="verify the integrity of the " "fundamental parts of the firmware and exit", action="store_true") + parser.add_argument("-D", "--extract-descriptor", help="extract the " + "flash descriptor from a full dump; when used with " + "--truncate save a descriptor with adjusted regions " + "start and end") + parser.add_argument("-M", "--extract-me", help="extract the ME firmware " + "from a full dump; when used with --truncate save a " + "truncated ME/TXE image") + args = parser.parse_args() + if args.check: + if args.relocate: + sys.exit("-c and -r can't be used together") + elif args.truncate: + sys.exit("-c and -t can't be used together") + f = open(args.file, "rb" if args.check or args.output else "r+b") f.seek(0x10) magic = f.read(4) if magic == b"$FPT": print("ME/TXE image detected") - me_start = 0 - f.seek(0, 2) - me_end = f.tell() if args.descriptor: sys.exit("-d requires a full dump") + if args.extract_descriptor: + sys.exit("-D requires a full dump") + + if args.extract_me: + sys.exit("-M requires a full dump") + + me_start = 0 + f.seek(0, 2) + me_end = f.tell() + elif magic == b"\x5a\xa5\xf0\x0f": print("Full image detected") + + if args.truncate and not args.extract_me: + sys.exit("-t requires a separated ME/TXE image (or --extract-me)") + f.seek(0x14) flmap0, flmap1 = unpack("> 24 & 0x7 frba = flmap0 >> 12 & 0xff0 fmba = (flmap1 & 0xff) << 4 - if nr >= 2: - f.seek(frba) - flreg0, flreg1, flreg2 = unpack("> 4 & 0x1fff000 | 0xfff + 1 - me_start = (flreg2 & 0x1fff) << 12 - me_end = flreg2 >> 4 & 0x1fff000 | 0xfff + 1 - - if me_start >= me_end: - sys.exit("The ME/TXE region in this image has been disabled") - - f.seek(me_start + 0x10) - if f.read(4) != b"$FPT": - sys.exit("The ME/TXE region is corrupted or missing") - - print("The ME/TXE region goes from {:#x} to {:#x}" - .format(me_start, me_end)) - else: - sys.exit("This image does not contains a ME/TXE firmware NR = {})" - .format(nr)) + + f.seek(frba) + flreg = unpack("= me_end: + sys.exit("The ME/TXE region in this image has been disabled") + + f.seek(me_start + 0x10) + if f.read(4) != b"$FPT": + sys.exit("The ME/TXE region is corrupted or missing") + + print("The ME/TXE region goes from {:#x} to {:#x}" + .format(me_start, me_end)) else: sys.exit("Unknown image") @@ -378,8 +552,28 @@ if __name__ == "__main__": if f.read(4) == b"$CPD": me11 = True num_entries = unpack("= 0: + check_mn2_tag(f, ftpr_offset + ftpr_mn2_offset) + print("Found FTPR manifest at {:#x}" + .format(ftpr_offset + ftpr_mn2_offset)) + else: + sys.exit("Can't find the manifest of the FTPR partition") + else: + check_mn2_tag(f, ftpr_offset) me11 = False ftpr_mn2_offset = 0 @@ -394,7 +588,10 @@ if __name__ == "__main__": shutil.copy(args.file, args.output) f = open(args.output, "r+b") - mef = regionFile(f, me_start, me_end) + mef = RegionFile(f, me_start, me_end) + + if args.descriptor or args.extract_descriptor: + fdf = RegionFile(f, fd_start, fd_end) print("Removing extra partitions...") mef.fill_range(me_start + 0x30, ftpr_offset, b"\xff") @@ -410,18 +607,15 @@ if __name__ == "__main__": flags &= ~(0x00000001) mef.write_to(me_start + 0x24, pack(" 0: - print("The ME region can be reduced up to:\n" - " {:08x}:{:08x} me" - .format(me_start, end_addr - 1)) - else: - print("Found less modules than expected in the FTPR " - "partition; skipping modules removal") - else: - print("Can't find the module header size; skipping " - "modules removal") + print("Reading FTPR modules list...") + if me11: + end_addr, ftpr_offset = \ + check_and_remove_modules_me11(mef, me_start, me_end, + ftpr_offset, ftpr_lenght, + min_ftpr_offset, args.relocate, + args.keep_modules) + else: + end_addr, ftpr_offset = \ + check_and_remove_modules(mef, me_start, me_end, ftpr_offset, + min_ftpr_offset, args.relocate, + args.keep_modules) + + if end_addr > 0: + end_addr = (end_addr // 0x1000 + 1) * 0x1000 + end_addr += spared_blocks * 0x1000 + + print("The ME minimum size should be {0} bytes " + "({0:#x} bytes)".format(end_addr - me_start)) + + if me_start > 0: + print("The ME region can be reduced up to:\n" + " {:08x}:{:08x} me".format(me_start, end_addr - 1)) + elif args.truncate: + print("Truncating file at {:#x}...".format(end_addr)) + f.truncate(end_addr) + + if args.descriptor: + print("Removing ME/TXE R/W access to the other flash regions...") + fdf.write_to(fmba + 0x4, pack(" {:08x}:{:08x} me" + .format(me_start, me_end - 1, me_start, end_addr - 1)) + print(" {:08x}:{:08x} bios --> {:08x}:{:08x} bios" + .format(bios_start, bios_end - 1, end_addr, bios_end - 1)) + + flreg1 = start_end_to_flreg(end_addr, bios_end) + flreg2 = start_end_to_flreg(me_start, end_addr) + + fdf_copy.seek(frba + 0x4) + fdf_copy.write(pack("