blob: 3ce67f0fbddd63d721f78ac30a06e0fceeadbb59 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001#!/usr/bin/env python3
2
3from __future__ import print_function
4import binascii
5import json
6import os
7import struct
8import sys
9import uuid
10import xml.dom.minidom
11
12NumberOfPartitionEntries = 64
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 entries2k = b''
31 lba = partition.getAttribute("lba")
32 lba = lba and eval(lba) or 130560
33 lbs = partition.getAttribute("lbs")
34 lbs = lbs and eval(lbs) or 4096
35 for node in partition.childNodes:
36 if node.nodeName != "entry":
37 continue
38 type = uuid.UUID(node.getAttribute("type"))
39 uniq = node.getAttribute("uuid")
40 uniq = uniq and uuid.UUID(uniq) or uuid.uuid4()
41 start = eval(node.getAttribute("start"))
42 end = eval(node.getAttribute("end"))
43 attr = node.getAttribute("attributes")
44 attr = attr and eval(attr) or 0
45 name = node.getAttribute("name")
46 if (name == b"userdata") and (end == 0):
47 end = lba - 1
48 if attr == 4:
49 start2k = (start / 256) * 256 + 8
50 entries2k += struct.pack("<16s16sQQQ72s",
51 type.bytes_le,
52 uniq.bytes_le,
53 int(start2k), end, 4,
54 name.encode("utf-16le"))
55 if ((start % 256) == 0):
56 start = (start / 256) * 256 + 4
57 entries += struct.pack("<16s16sQQQ72s",
58 type.bytes_le,
59 uniq.bytes_le,
60 int(start), end, attr,
61 name.encode("utf-16le"))
62 entries = padding(entries, NumberOfPartitionEntries * SizeOfPartitionEntry)
63 entries2k = padding(entries2k, 2048)
64 FirstUsableLBA = 2 + (NumberOfPartitionEntries * SizeOfPartitionEntry / lbs)
65 uniq = partition.getAttribute("uuid")
66 uniq = uniq and uuid.UUID(uniq) or uuid.uuid4()
67 if lbs == 4096:
68 pmbr = padding(pmbr, 2048)
69 else:
70 pmbr = padding(pmbr, lbs)
71 header2k = struct.pack("<8sIIIIQQQQ16sQIII",
72 b"EFI PART", 0x00010000, 92, 0, 0,
73 1, # MyLBA
74 1, #lba - 1, # AlternateLBA
75 int(FirstUsableLBA),
76 int(lba - FirstUsableLBA), # LastUsableLBA
77 uniq.bytes_le,
78 3, 4, int(SizeOfPartitionEntry), crc32(entries2k))
79 header2k = padding(header2k[:16] +
80 struct.pack('I', crc32(header2k)) +
81 header2k[20:], 2048)
82 header = struct.pack("<8sIIIIQQQQ16sQIII",
83 b"EFI PART", 0x00010000, 92, 0, 0,
84 1, # MyLBA
85 1, #lba - 1, # AlternateLBA
86 int(FirstUsableLBA),
87 int(lba - FirstUsableLBA), # LastUsableLBA
88 uniq.bytes_le,
89 2, int(NumberOfPartitionEntries), int(SizeOfPartitionEntry), crc32(entries))
90 if lbs == 4096:
91 header = padding(header[:16] +
92 struct.pack('I', crc32(header)) +
93 header[20:], 2048)
94 else:
95 header = padding(header[:16] +
96 struct.pack('I', crc32(header)) +
97 header[20:], lbs)
98 if lbs == 4096:
99 return pmbr + header2k + header + entries2k + entries
100 else:
101 return pmbr + header + entries
102
103def write_scatter_partition(f, entry):
104 if entry.get("file_name", "NONE") != "NONE":
105 entry.setdefault("is_download", True)
106 entry.setdefault("operation_type", "UPDATE")
107 entry.setdefault("type", "NORMAL_ROM")
108 f.write(
109"""- partition_index: %s
110 partition_name: %s
111 file_name: %s
112 is_download: %s
113 type: %s
114 linear_start_addr: %s
115 physical_start_addr: %s
116 partition_size: %s
117 region: %s
118 storage: %s
119 boundary_check: %s
120 is_reserved: %s
121 operation_type: %s
122 d_type: LOW_PAGE
123 slc_percentage: 0
124 is_upgradable: true
125 reserve: %s
126
127""" % (entry["partition_index"], entry["partition_name"], entry.get("file_name", "NONE"),
128entry.get("is_download", False) and "true" or "false", entry.get("type", "NONE"), hex(entry["linear_start_addr"]),
129hex(entry["physical_start_addr"]), hex(entry["partition_size"]), entry.get("region", "NONE"),
130entry.get("storage", "HW_STORAGE_NAND"), entry.get("boundary_check", True) and "true" or "false",
131entry.get("is_reserved", False) and "true" or "false", entry.get("operation_type", "PROTECTED"),
132hex(entry.get("reserve", 0))))
133
134def write_scatter(f, partition, d):
135 f.write(
136"""############################################################################################################
137#
138# General Setting
139#
140############################################################################################################
141- general: MTK_PLATFORM_CFG
142 info:
143 - config_version: %s
144 platform: %s
145 project: %s
146 storage: %s
147 boot_channel: %s
148 block_size: %s
149 skip_pmt_operate: %s
150############################################################################################################
151#
152# Layout Setting
153#
154############################################################################################################
155""" % (d["MTK_PLATFORM_CFG"]["config_version"], d["MTK_PLATFORM_CFG"]["platform"], d["MTK_PLATFORM_CFG"]["project"],
156 d["MTK_PLATFORM_CFG"]["storage"], d["MTK_PLATFORM_CFG"]["boot_channel"], d["MTK_PLATFORM_CFG"]["block_size"],
157 d["MTK_PLATFORM_CFG"].get("skip_pmt_operate", True) and "true" or "false"))
158# d["PRELOADER"]["partition_index"] = "SYS0"
159 d["MBR"]["partition_index"] = "SYS0"
160# write_scatter_partition(f, d["PRELOADER"])
161 write_scatter_partition(f, d["MBR"])
162 lbs = partition.getAttribute("lbs")
163 lbs = lbs and eval(lbs) or 4096
164 i = 1
165 for node in partition.childNodes:
166 if node.nodeName != "entry":
167 continue
168 start = eval(node.getAttribute("start"))
169 end = eval(node.getAttribute("end"))
170 name = node.getAttribute("name")
171 if name not in d:
172 continue
173 entry = d[name]
174 entry["partition_name"] = name
175 entry["partition_index"] = "SYS%d" % i
176 i += 1
177 entry["linear_start_addr"] = start * lbs
178 entry["physical_start_addr"] = start * lbs
179 if end != start:
180 entry["partition_size"] = (end + 1 - start) * lbs
181 else:
182 entry["partition_size"] = 0
183 write_scatter_partition(f, entry)
184 if (d["MTK_PLATFORM_CFG"].get("skip_pmt_operate", True) and "true" or "false") == "false":
185 d["sgpt"]["partition_index"] = "SYS%d" % i
186 write_scatter_partition(f, d["sgpt"])
187
188
189def sanity_check(path, partition):
190 err = 0
191 lba = partition.getAttribute("lba")
192 lba = lba and eval(lba) or 130560
193 lbs = partition.getAttribute("lbs")
194 lbs = lbs and eval(lbs) or 4096
195 FirstUsableLBA = 2 + (NumberOfPartitionEntries * SizeOfPartitionEntry / lbs)
196# usable = (FirstUsableLBA, lba - FirstUsableLBA)
197 usable = (0, lba)
198 used = {}
199 for node in partition.childNodes:
200 if node.nodeName != "entry":
201 continue
202 name = node.getAttribute("name")
203 start = eval(node.getAttribute("start"))
204 end = eval(node.getAttribute("end"))
205 if (name == "userdata") and (end == 0):
206 end = lba - 1
207 if start > end:
208 print("%s: error: partition '%s': start lba (%d) > end lba (%d)" %
209 (path, name, start, end), file = sys.stderr)
210 err += 1
211 if start < usable[0] or end > usable[1]:
212 print("%s: error: partition '%s': (%d...%d) out of usable range (%d...%d)" %
213 (path, name, start, end, usable[0], usable[1]), file = sys.stderr)
214 err += 1
215 for i in used:
216 if (used[i][0] <= start and start <= used[i][1] or
217 used[i][0] <= end and end <= used[i][1]):
218 print("%s: error: partition '%s': (%d...%d) overlapped with partition '%s' (%d...%d)" %
219 (path, name, start, end, i, used[i][0], used[i][1]), file = sys.stderr)
220 err += 1
221 used[name] = (start, end)
222 return err
223
224def main(argv):
225 if len(argv) == 5: # normal argument list len(argv) = 5 #
226 print ("len:%d .py=%s .xml=%s .json=%s MBR=%s .txt=%s" %
227 (len(argv), argv[0], argv[1], argv[2], argv[3], argv[4]))
228 if len(argv) <= 3: # len(argv) = 4 for mt2701 spi nor boot (scatter.txt isn't needed) #
229 print("Usage: len:%d,%s partition_*.xml scatter_*.json [MBR] [scatter.txt] " % (len(argv), argv[0]))
230 exit(1)
231 root = xml.dom.minidom.parse(argv[1])
232 for partition in root.childNodes:
233 if partition.nodeName == "partition":
234 break
235 else:
236 raise Exception("partition not found")
237 if sanity_check(argv[1], partition):
238 return 1
239 write(argv[3], gen_gpt(partition))
240 if len(argv) == 5:
241 with open(os.path.join(os.path.dirname(__file__), argv[2]), "r") as f:
242 d = json.load(f)
243 with open(argv[4], "w") as f:
244 write_scatter(f, partition, d)
245 return 0
246
247if __name__ == "__main__":
248 sys.exit(main(sys.argv))