blob: 1f34ee61cdef3a186961afe606d6036d932fb245 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001#!/usr/bin/env python
2
3from __future__ import print_function
4import binascii
5import json
6import os
7import struct
8import sys
9import uuid
10import xml.dom.minidom
11
12NumberOfPartitionEntries = 128
13SizeOfPartitionEntry = 128
14
15def write(path, data):
16 with open(path, "wb+") as f:
17 f.write(data)
18
19def crc32(data):
20 return binascii.crc32(data) & 0xffffffff
21
22def padding(data, size):
23 return data + b'\0' * (size - len(data))
24
25def 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
67def 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"),
90entry.get("is_download", False) and "true" or "false", entry.get("type", "NONE"), hex(entry["linear_start_addr"]),
91hex(entry["physical_start_addr"]), hex(entry["partition_size"]), entry.get("region", "EMMC_USER"),
92entry.get("storage", "HW_STORAGE_EMMC"), entry.get("boundary_check", True) and "true" or "false",
93entry.get("is_reserved", False) and "true" or "false", entry.get("operation_type", "PROTECTED"),
94hex(entry.get("reserve", 0))))
95
96def 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 resize_check: %s
112 skip_pmt_operate: %s
113############################################################################################################
114#
115# Layout Setting
116#
117############################################################################################################
118""" % (d["MTK_PLATFORM_CFG"]["config_version"], d["MTK_PLATFORM_CFG"]["platform"], d["MTK_PLATFORM_CFG"]["project"],
119 d["MTK_PLATFORM_CFG"]["storage"], d["MTK_PLATFORM_CFG"]["boot_channel"], d["MTK_PLATFORM_CFG"]["block_size"],
120 d["MTK_PLATFORM_CFG"].get("resize_check", True) and "true" or "false",
121 d["MTK_PLATFORM_CFG"].get("skip_pmt_operate", True) and "true" or "false" ))
122 d["PRELOADER"]["partition_index"] = "SYS0"
123 d["MBR"]["partition_index"] = "SYS1"
124 write_scatter_partition(f, d["PRELOADER"])
125 write_scatter_partition(f, d["MBR"])
126 i = 2
127 for node in partition.childNodes:
128 if node.nodeName != "entry":
129 continue
130 start = eval(node.getAttribute("start"))
131 end = eval(node.getAttribute("end"))
132 name = node.getAttribute("name")
133 if name not in d:
134 continue
135 entry = d[name]
136 entry["partition_name"] = name
137 entry["partition_index"] = "SYS%d" % i
138 i += 1
139 entry["linear_start_addr"] = start * 512
140 entry["physical_start_addr"] = start * 512
141 if (end != start and name != "userdata"):
142 entry["partition_size"] = (end + 1 - start) * 512
143 else:
144 entry["partition_size"] = 0
145 write_scatter_partition(f, entry)
146 if (d["MTK_PLATFORM_CFG"].get("skip_pmt_operate", True) and "true" or "false") == "false":
147 d["sgpt"]["partition_index"] = "SYS%d" % i
148 d["sgpt"]["linear_start_addr"] = 0xffff0080
149 d["sgpt"]["physical_start_addr"] = 0xffff0080
150 write_scatter_partition(f, d["sgpt"])
151
152
153def sanity_check(path, partition):
154 err = 0
155 lba = eval(partition.getAttribute("lba"))
156 lbs = partition.getAttribute("lbs")
157 lbs = lbs and eval(lbs) or 512
158 FirstUsableLBA = 2 + (NumberOfPartitionEntries * SizeOfPartitionEntry / lbs)
159 usable = (FirstUsableLBA, lba - FirstUsableLBA)
160 used = {}
161 for node in partition.childNodes:
162 if node.nodeName != "entry":
163 continue
164 name = node.getAttribute("name")
165 start = eval(node.getAttribute("start"))
166 end = eval(node.getAttribute("end"))
167 if start > end:
168 print("%s: error: partition '%s': start lba (%d) > end lba (%d)" %
169 (path, name, start, end), file = sys.stderr)
170 err += 1
171 if start < usable[0] or end > usable[1]:
172 print("%s: error: partition '%s': (%d...%d) out of usable range (%d...%d)" %
173 (path, name, start, end, usable[0], usable[1]), file = sys.stderr)
174 err += 1
175 for i in used:
176 if (used[i][0] <= start and start <= used[i][1] or
177 used[i][0] <= end and end <= used[i][1]):
178 print("%s: error: partition '%s': (%d...%d) overlapped with partition '%s' (%d...%d)" %
179 (path, name, start, end, i, used[i][0], used[i][1]), file = sys.stderr)
180 err += 1
181 used[name] = (start, end)
182 return err
183
184def main(argv):
185 if len(argv) == 5: # normal argument list len(argv) = 5 #
186 print ("len:%d .py=%s .xml=%s .json=%s MBR=%s .txt=%s" %
187 (len(argv), argv[0], argv[1], argv[2], argv[3], argv[4]))
188 if len(argv) <= 3: # len(argv) = 4 for mt2701 spi nor boot (scatter.txt isn't needed) #
189 print("Usage: len:%d,%s partition_*.xml scatter_*.json [MBR] [scatter.txt] " % (len(argv), argv[0]))
190 exit(1)
191 root = xml.dom.minidom.parse(argv[1])
192 for partition in root.childNodes:
193 if partition.nodeName == "partition":
194 break
195 else:
196 raise Exception("partition not found")
197 if sanity_check(argv[1], partition):
198 return 1
199 write(argv[3], gen_gpt(partition))
200 if len(argv) == 5:
201 with open(os.path.join(os.path.dirname(__file__), argv[2]), "r") as f:
202 d = json.load(f)
203 with open(argv[4], "w") as f:
204 write_scatter(f, partition, d)
205 return 0
206
207if __name__ == "__main__":
208 sys.exit(main(sys.argv))