From 9fea2cea68cdc889d667b8cbd2e387320a6ea445 Mon Sep 17 00:00:00 2001 From: 3A1 <2943788131@qq.com> Date: Sun, 10 Aug 2025 16:58:35 +0800 Subject: [PATCH] telescan2rw1c-or-rw1 --- tlscan2pcileech_rw1c.py | 71 +++++++++ writemask.py | 310 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 tlscan2pcileech_rw1c.py create mode 100644 writemask.py diff --git a/tlscan2pcileech_rw1c.py b/tlscan2pcileech_rw1c.py new file mode 100644 index 0000000..3f67761 --- /dev/null +++ b/tlscan2pcileech_rw1c.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +tlscan2pcileech_coe.py +将 TLScan 的 .tlscan 文件转换成 PCILeech 所需的 COE 格式 +用法: + python tlscan2pcileech_coe.py input.tlscan +结果: + 同目录下生成 input_rw1.coe 和 input_rw1c.coe +""" +import os +import sys +import datetime +import xml.etree.ElementTree as ET + +# ---------- 参数 ---------- +if len(sys.argv) < 2: + print("用法: python tlscan2pcileech_coe.py ") + sys.exit(1) + +src = sys.argv[1] +base, _ = os.path.splitext(src) +dst_rw1 = base + "_rw1.coe" +dst_rw1c = base + "_rw1c.coe" + +# ---------- 读取 TLScan ---------- +try: + bs_hex = ET.parse(src).find('.//bytes').text +except Exception as e: + sys.exit(f"解析 TLScan 文件失败: {e}") + +bs_hex = ''.join(bs_hex.split()) +if len(bs_hex) != 8192: # 4096 字节 => 8192 字符 + sys.exit(f"期望 8192 个十六进制字符,实际 {len(bs_hex)}") + +# ---------- 生成掩码 ---------- +# 4096 字节 => 1024 个 dword +words_rw1 = [0] * 1024 +words_rw1c = [0] * 1024 + +for i in range(4096): + val = int(bs_hex[i*2:i*2+2], 16) + # 简易规则:0x01 视为 RW1C/RW1CS + if val != 0x00: + words_rw1[i//4] |= 1 << ((i % 4) * 8) + if val == 0x01: + words_rw1c[i//4] |= 1 << ((i % 4) * 8) + +# ---------- 写 COE ---------- +def write_coe(path, words): + with open(path, 'w') as f: + f.write(f'; Converted to COE from "{src}" on {datetime.datetime.now()}\n') + f.write('memory_initialization_radix=16;\nmemory_initialization_vector=\n') + + for blk in range(16): + offset = blk * 256 + f.write(f'; {offset:04x}\n') + for line in range(16): + idx = blk * 64 + line * 4 + line_words = [words[idx + j] for j in range(4)] + f.write(','.join(f'{w:08x}' for w in line_words) + ',\n') + + # 去掉最后一个逗号 + f.seek(f.tell() - 2, os.SEEK_SET) # 回退 2 字节(,\n) + f.truncate() + f.write('\n;\n') + +write_coe(dst_rw1, words_rw1) +write_coe(dst_rw1c, words_rw1c) + +print(f'已生成:\n {dst_rw1}\n {dst_rw1c}') \ No newline at end of file diff --git a/writemask.py b/writemask.py new file mode 100644 index 0000000..d0705ac --- /dev/null +++ b/writemask.py @@ -0,0 +1,310 @@ +import re + +write_protected_bits_PCIE = ( + "00000f00", # 1 + "00000010", # 2 + "ff7f0f00", # 3 + "00000000", # 4 + "cb0d00c0", # 5 + "00000000", # 6 + "0000ffff", # 7 + "00000000", # 8 + "00000000", # 9 + "00000000", # 10 + "ff7f0000", # 11 + "00000000", # 12 + "bfff2000", # 13 +) + +write_protected_bits_PM = ( + "00000000", # 1 + "00000000", # 2 +) + +write_protected_bits_MSI = ( + "0000f104", # 1 + "03000000", # 2 + "00000000", # 3 + "00000000", # 4 + "00000000", # 5 + "00000000", # 6 + "00000000", # 7 + "ffff0000", # 8 +) + +write_protected_bits_MSIX = ( + "000000c0", # 1 + "00000000", # 2 + "00000000", # 3 +) + +write_protected_bits_VPD = ("00000000",) + +write_protected_bits_VSC = ( + "000000ff", # 1 + "ffffffff", # 2 + "00000000", # 3 +) + +write_protected_bits_PTH = ("00000000",) + +write_protected_bits_VSEC = ( + "00000000", # 1 + "00000000", # 2 + "ffffffff", # 3 + "ffffffff", # 4 +) + +write_protected_bits_AER = ( + "00000000", # 1 + "31f0ff07", # 2 + "31f0ff07", # 3 + "31f0ff07", # 4 + "c1f10000", # 5 + "c1f10000", # 6 + "40050000", # 7 + "00000000", # 8 + "00000000", # 9 + "00000000", # 10 + "00000000", # 11 +) + +write_protected_bits_DSN = ( + "00000000", # 1 + "00000000", # 2 + "00000000", # 3 +) + +write_protected_bits_LTR = ( + "00000000", # 1 + "00000000", # 2 +) + +write_protected_bits_L1PM = ( + "00000000", # 1 + "00000000", # 2 + "3f00ffe3", # 3 + "fb000000", # 4 +) + +write_protected_bits_PTM = ( + "00000000", # 1 + "00000000", # 2 + "00000000", # 3 + "03ff0000", # 4 +) + +CAPABILITY_NAMES = { + 0x01: "power management", + 0x02: "AGP", + 0x03: "VPD", + 0x04: "slot identification", + 0x05: "MSI", + 0x06: "compact PCI hot swap", + 0x07: "PCI-X", + 0x08: "hyper transport", + 0x09: "vendor specific", + 0x0A: "debug port", + 0x0B: "compact PCI central resource control", + 0x0C: "PCI hot plug", + 0x0D: "PCI bridge subsystem vendor ID", + 0x0E: "AGP 8x", + 0x0F: "secure device", + 0x10: "PCI express", + 0x11: "MSI-X", + 0x12: "SATA data/index configuration", + 0x13: "advanced features", + 0x14: "enhanced allocation", + 0x15: "flattening portal bridge", +} + +EXTENDED_CAPABILITY_NAMES = { + 0x0001: "advanced error reporting", + 0x0002: "virtual channel", + 0x0003: "device serial number", + 0x0004: "power budgeting", + 0x0005: "root complex link declaration", + 0x0006: "root complex internal link control", + 0x0007: "root complex event collector endpoint association", + 0x0008: "multi-function virtual channel", + 0x0009: "virtual channel", + 0x000A: "root complex register block", + 0x000B: "vendor specific", + 0x000C: "configuration access correlation", + 0x000D: "access control services", + 0x000E: "alternative routing-ID interpretation", + 0x000F: "address translation services", + 0x0010: "single root IO virtualization", + 0x0011: "multi-root IO virtualization", + 0x0012: "multicast", + 0x0013: "page request interface", + 0x0014: "AMD reserved", + 0x0015: "resizable BAR", + 0x0016: "dynamic power allocation", + 0x0017: "TPH requester", + 0x0018: "latency tolerance reporting", + 0x0019: "secondary PCI express", + 0x001A: "protocol multiplexing", + 0x001B: "process address space ID", + 0x001C: "LN requester", + 0x001D: "downstream port containment", + 0x001E: "L1 PM substates", + 0x001F: "precision time measurement", + 0x0020: "M-PCIe", + 0x0021: "FRS queueing", + 0x0022: "Readyness time reporting", + 0x0023: "designated vendor specific", + 0x0024: "VF resizable BAR", + 0x0025: "data link feature", + 0x0026: "physical layer 16.0 GT/s", + 0x0027: "receiver lane margining", + 0x0028: "hierarchy ID", + 0x0029: "native PCIe enclosure management", + 0x002A: "physical layer 32.0 GT/s", + 0x002B: "alternate protocol", + 0x002C: "system firmware intermediary", +} + +fixed_section = [ + "00000000", + "470500f9", + "00000000", + "ffff0040", + "f0ffffff", + "ffffffff", + "f0ffffff", + "ffffffff", + "f0ffffff", + "f0ffffff", + "00000000", + "00000000", + "01f8ffff", + "00000000", + "00000000", + "ff000000", +] + +writemask_dict = { + "0x10": write_protected_bits_PCIE, + "0x03": write_protected_bits_VPD, + "0x01": write_protected_bits_PM, + "0x05": write_protected_bits_MSI, + "0x11": write_protected_bits_MSIX, + "0x09": write_protected_bits_VSC, + "0x00A": write_protected_bits_VSEC, + "0x0001": write_protected_bits_AER, + "0x0003": write_protected_bits_DSN, + "0x0018": write_protected_bits_LTR, + "0x001E": write_protected_bits_L1PM, + "0x000B": write_protected_bits_PTM, + "0x0017": write_protected_bits_PTH, +} + + +def read_cfg_space(file_path): + # 初始化一个空字典来存储dword映射 + dword_map = {} + # 打开指定路径的文件并读取内容 + with open(file_path, "r") as file: + content = file.read() + # 使用正则表达式查找所有8位的十六进制数 + dwords = re.findall(r"[0-9a-fA-F]{8}", content) + # 遍历找到的dword并存储到字典中,最多存储1024个 + for index, dword in enumerate(dwords): + if index < 1024: + dword_map[index] = int(dword, 16) + # 返回dword映射字典 + return dword_map + + +def locate_caps(dword_map): + # 初始化一个空字典来存储能力 + capabilities = {} + + # 获取能力列表的起始位置 + start = dword_map[0x34 // 4] >> 24 + cap_location = start + + # 遍历能力列表,直到cap_location为0 + while cap_location != 0: + cap_dword = dword_map[cap_location // 4] + cap_id = (cap_dword >> 24) & 0xFF + next_cap = (cap_dword >> 16) & 0xFF + + # 打印找到的能力ID和偏移量 + print( + f"Found Cap ID: 0x{cap_id:02X}, Start offset: {hex(cap_location)}, Next cap offset: {hex(next_cap)}" + ) + capabilities[f"0x{cap_id:02X}"] = cap_location + cap_location = next_cap + + # 扩展能力的起始位置 + ext_cap_location = 0x100 + while ext_cap_location != 0: + ext_cap_dword = dword_map[ext_cap_location // 4] + + # 将大端字节序转换为小端字节序 + ext_cap_dword_le = int.from_bytes( + ext_cap_dword.to_bytes(4, byteorder="big"), byteorder="little" + ) + ext_cap_id = ext_cap_dword_le & 0xFFFF + next_ext_cap = (ext_cap_dword_le >> 20) & 0xFFF + + # 打印找到的扩展能力ID和偏移量 + print( + f"Found Ext Cap ID: 0x{ext_cap_id:04X}, Start offset: {hex(ext_cap_location)}, Next cap offset: {hex(next_ext_cap)}" + ) + capabilities[f"0x{ext_cap_id:04X}"] = ext_cap_location + ext_cap_location = next_ext_cap + + # 返回能力字典 + return capabilities + + +def create_wrmask(dwords): + # 创建一个与dwords长度相同的掩码列表,初始值为"ffffffff" + return ["ffffffff" for _ in dwords] + + +def update_writemask(wr_mask, input, start_index): + # 计算结束索引,确保不超过掩码列表的长度 + end_index = min(start_index + len(input), len(wr_mask)) + # 更新掩码列表中的部分值 + wr_mask[start_index:end_index] = input[: end_index - start_index] + return wr_mask + + +def main(file_in, file_out): + # 读取配置空间文件并转换为字典 + cfg_space = read_cfg_space(file_in) + # 定位能力并返回能力字典 + caps = locate_caps(cfg_space) + + # 创建初始的写掩码 + wr_mask = create_wrmask(cfg_space) + # 更新写掩码的固定部分 + wr_mask = update_writemask(wr_mask, fixed_section, 0) + + # 遍历能力字典并更新写掩码 + for cap_id, cap_start in caps.items(): + if cap_id not in writemask_dict: + print(f"Skipping cap {cap_id} as no writemask defined") + continue + section = writemask_dict.get(cap_id) + cap_start_index = cap_start // 4 + wr_mask = update_writemask(wr_mask, section, cap_start_index) + + # 写入更新后的写掩码到输出文件 + new_line_index = 0 + with open(file_out, "w") as f: + f.write(f"; {format(new_line_index, 'X').zfill(2) + '00'}\n") + for i in range(0, len(wr_mask), 4): + f.write(",".join(wr_mask[i : i + 4]) + ",\n") + if i >= 64 and (i) % 64 == 0: + f.write("\n") + new_line_index += 1 + f.write(f"; {format(new_line_index, 'X').zfill(2) + '00'}\n") + + +if __name__ == "__main__": + main("./pcileech_cfgspace.coe", "pcileech_cfgspace_writemask.coe")