rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | from __future__ import print_function |
| 4 | import binascii |
| 5 | import json |
| 6 | import os |
| 7 | import struct |
| 8 | import sys |
| 9 | import uuid |
| 10 | import xml.dom.minidom |
| 11 | |
| 12 | NumberOfPartitionEntries = 128 |
| 13 | SizeOfPartitionEntry = 128 |
| 14 | |
| 15 | def write(path, data): |
| 16 | with open(path, "wb+") as f: |
| 17 | f.write(data) |
| 18 | |
| 19 | def crc32(data): |
| 20 | return binascii.crc32(data) & 0xffffffff |
| 21 | |
| 22 | def padding(data, size): |
| 23 | return data + b'\0' * (size - len(data)) |
| 24 | |
| 25 | def gen_gpt(partition): |
| 26 | pmbr = (b'\0' * 446 + |
| 27 | b"\x00\x00\x02\x00\xee\xff\xff\xff\x01\x00\x00\x00\xff\xff\xff\xff" + |
| 28 | b'\0' * 48 + b"\x55\xaa") |
| 29 | entries = b'' |
| 30 | for node in partition.childNodes: |
| 31 | if node.nodeName != "entry": |
| 32 | continue |
| 33 | type = uuid.UUID(node.getAttribute("type")) |
| 34 | uniq = node.getAttribute("uuid") |
| 35 | uniq = uniq and uuid.UUID(uniq) or uuid.uuid4() |
| 36 | start = eval(node.getAttribute("start")) |
| 37 | end = eval(node.getAttribute("end")) |
| 38 | attr = node.getAttribute("attributes") |
| 39 | attr = attr and eval(attr) or 0 |
| 40 | name = node.getAttribute("name") |
| 41 | entries += struct.pack("<16s16sQQQ72s", |
| 42 | type.bytes_le, |
| 43 | uniq.bytes_le, |
| 44 | start, end, attr, |
| 45 | name.encode("utf-16le")) |
| 46 | entries = padding(entries, NumberOfPartitionEntries * SizeOfPartitionEntry) |
| 47 | lba = eval(partition.getAttribute("lba")) |
| 48 | lbs = partition.getAttribute("lbs") |
| 49 | lbs = lbs and eval(lbs) or 512 |
| 50 | FirstUsableLBA = 2 + (NumberOfPartitionEntries * SizeOfPartitionEntry / lbs) |
| 51 | uniq = partition.getAttribute("uuid") |
| 52 | uniq = uniq and uuid.UUID(uniq) or uuid.uuid4() |
| 53 | pmbr = padding(pmbr, lbs) |
| 54 | header = struct.pack("<8sIIIIQQQQ16sQIII", |
| 55 | b"EFI PART", 0x00010000, 92, 0, 0, |
| 56 | 1, # MyLBA |
| 57 | 1, #lba - 1, # AlternateLBA |
| 58 | int(FirstUsableLBA), |
| 59 | int(lba - FirstUsableLBA), # LastUsableLBA |
| 60 | uniq.bytes_le, |
| 61 | 2, NumberOfPartitionEntries, SizeOfPartitionEntry, crc32(entries)) |
| 62 | header = padding(header[:16] + |
| 63 | struct.pack('I', crc32(header)) + |
| 64 | header[20:], lbs) |
| 65 | return pmbr + header + entries |
| 66 | |
| 67 | def write_scatter_partition(f, entry): |
| 68 | if entry.get("file_name", "NONE") != "NONE": |
| 69 | entry.setdefault("is_download", True) |
| 70 | entry.setdefault("operation_type", "UPDATE") |
| 71 | entry.setdefault("type", "NORMAL_ROM") |
| 72 | f.write( |
| 73 | """- partition_index: %s |
| 74 | partition_name: %s |
| 75 | file_name: %s |
| 76 | is_download: %s |
| 77 | type: %s |
| 78 | linear_start_addr: %s |
| 79 | physical_start_addr: %s |
| 80 | partition_size: %s |
| 81 | region: %s |
| 82 | storage: %s |
| 83 | boundary_check: %s |
| 84 | is_reserved: %s |
| 85 | operation_type: %s |
| 86 | d_type: FALSE |
| 87 | reserve: %s |
| 88 | |
| 89 | """ % (entry["partition_index"], entry["partition_name"], entry.get("file_name", "NONE"), |
| 90 | entry.get("is_download", False) and "true" or "false", entry.get("type", "NONE"), hex(entry["linear_start_addr"]), |
| 91 | hex(entry["physical_start_addr"]), hex(entry["partition_size"]), entry.get("region", "EMMC_USER"), |
| 92 | entry.get("storage", "HW_STORAGE_EMMC"), entry.get("boundary_check", True) and "true" or "false", |
| 93 | entry.get("is_reserved", False) and "true" or "false", entry.get("operation_type", "PROTECTED"), |
| 94 | hex(entry.get("reserve", 0)))) |
| 95 | |
| 96 | def write_scatter(f, partition, d): |
| 97 | f.write( |
| 98 | """############################################################################################################ |
| 99 | # |
| 100 | # General Setting |
| 101 | # |
| 102 | ############################################################################################################ |
| 103 | - general: MTK_PLATFORM_CFG |
| 104 | info: |
| 105 | - config_version: %s |
| 106 | platform: %s |
| 107 | project: %s |
| 108 | storage: %s |
| 109 | boot_channel: %s |
| 110 | block_size: %s |
| 111 | skip_pmt_operate: %s |
| 112 | ############################################################################################################ |
| 113 | # |
| 114 | # Layout Setting |
| 115 | # |
| 116 | ############################################################################################################ |
| 117 | """ % (d["MTK_PLATFORM_CFG"]["config_version"], d["MTK_PLATFORM_CFG"]["platform"], d["MTK_PLATFORM_CFG"]["project"], |
| 118 | d["MTK_PLATFORM_CFG"]["storage"], d["MTK_PLATFORM_CFG"]["boot_channel"], d["MTK_PLATFORM_CFG"]["block_size"], |
| 119 | d["MTK_PLATFORM_CFG"].get("skip_pmt_operate", True) and "true" or "false")) |
| 120 | d["PRELOADER"]["partition_index"] = "SYS0" |
| 121 | d["MBR"]["partition_index"] = "SYS1" |
| 122 | write_scatter_partition(f, d["PRELOADER"]) |
| 123 | write_scatter_partition(f, d["MBR"]) |
| 124 | i = 2 |
| 125 | for node in partition.childNodes: |
| 126 | if node.nodeName != "entry": |
| 127 | continue |
| 128 | start = eval(node.getAttribute("start")) |
| 129 | end = eval(node.getAttribute("end")) |
| 130 | name = node.getAttribute("name") |
| 131 | if name not in d: |
| 132 | continue |
| 133 | entry = d[name] |
| 134 | entry["partition_name"] = name |
| 135 | entry["partition_index"] = "SYS%d" % i |
| 136 | i += 1 |
| 137 | entry["linear_start_addr"] = start * 512 |
| 138 | entry["physical_start_addr"] = start * 512 |
| 139 | if (end != start and name != "userdata"): |
| 140 | entry["partition_size"] = (end + 1 - start) * 512 |
| 141 | else: |
| 142 | entry["partition_size"] = 0 |
| 143 | write_scatter_partition(f, entry) |
| 144 | if (d["MTK_PLATFORM_CFG"].get("skip_pmt_operate", True) and "true" or "false") == "false": |
| 145 | d["sgpt"]["partition_index"] = "SYS%d" % i |
| 146 | d["sgpt"]["linear_start_addr"] = 0xffff0080 |
| 147 | d["sgpt"]["physical_start_addr"] = 0xffff0080 |
| 148 | write_scatter_partition(f, d["sgpt"]) |
| 149 | |
| 150 | |
| 151 | def sanity_check(path, partition): |
| 152 | err = 0 |
| 153 | lba = eval(partition.getAttribute("lba")) |
| 154 | lbs = partition.getAttribute("lbs") |
| 155 | lbs = lbs and eval(lbs) or 512 |
| 156 | FirstUsableLBA = 2 + (NumberOfPartitionEntries * SizeOfPartitionEntry / lbs) |
| 157 | usable = (FirstUsableLBA, lba - FirstUsableLBA) |
| 158 | used = {} |
| 159 | for node in partition.childNodes: |
| 160 | if node.nodeName != "entry": |
| 161 | continue |
| 162 | name = node.getAttribute("name") |
| 163 | start = eval(node.getAttribute("start")) |
| 164 | end = eval(node.getAttribute("end")) |
| 165 | if start > end: |
| 166 | print("%s: error: partition '%s': start lba (%d) > end lba (%d)" % |
| 167 | (path, name, start, end), file = sys.stderr) |
| 168 | err += 1 |
| 169 | if start < usable[0] or end > usable[1]: |
| 170 | print("%s: error: partition '%s': (%d...%d) out of usable range (%d...%d)" % |
| 171 | (path, name, start, end, usable[0], usable[1]), file = sys.stderr) |
| 172 | err += 1 |
| 173 | for i in used: |
| 174 | if (used[i][0] <= start and start <= used[i][1] or |
| 175 | used[i][0] <= end and end <= used[i][1]): |
| 176 | print("%s: error: partition '%s': (%d...%d) overlapped with partition '%s' (%d...%d)" % |
| 177 | (path, name, start, end, i, used[i][0], used[i][1]), file = sys.stderr) |
| 178 | err += 1 |
| 179 | used[name] = (start, end) |
| 180 | return err |
| 181 | |
| 182 | def main(argv): |
| 183 | if len(argv) == 5: # normal argument list len(argv) = 5 # |
| 184 | print ("len:%d .py=%s .xml=%s .json=%s MBR=%s .txt=%s" % |
| 185 | (len(argv), argv[0], argv[1], argv[2], argv[3], argv[4])) |
| 186 | if len(argv) <= 3: # len(argv) = 4 for mt2701 spi nor boot (scatter.txt isn't needed) # |
| 187 | print("Usage: len:%d,%s partition_*.xml scatter_*.json [MBR] [scatter.txt] " % (len(argv), argv[0])) |
| 188 | exit(1) |
| 189 | root = xml.dom.minidom.parse(argv[1]) |
| 190 | for partition in root.childNodes: |
| 191 | if partition.nodeName == "partition": |
| 192 | break |
| 193 | else: |
| 194 | raise Exception("partition not found") |
| 195 | if sanity_check(argv[1], partition): |
| 196 | return 1 |
| 197 | write(argv[3], gen_gpt(partition)) |
| 198 | if len(argv) == 5: |
| 199 | with open(os.path.join(os.path.dirname(__file__), argv[2]), "r") as f: |
| 200 | d = json.load(f) |
| 201 | with open(argv[4], "w") as f: |
| 202 | write_scatter(f, partition, d) |
| 203 | return 0 |
| 204 | |
| 205 | if __name__ == "__main__": |
| 206 | sys.exit(main(sys.argv)) |