134 lines
6.1 KiB
Python
134 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
||
"""mkimage.py — сборка emmc.img с РЕАЛЬНОЙ GPT RM-1127 (33 раздела).
|
||
Layout: Boot1 4M + Boot2 4M + RPMB 4M + User 15154944 секторов.
|
||
Пока разделы пустые (нули) — контент из FFU payload будет следующим шагом.
|
||
Использование: ./tools/mkimage.py images/emmc.img
|
||
"""
|
||
import struct
|
||
import sys
|
||
import uuid
|
||
import zlib
|
||
|
||
SECTOR = 512
|
||
BOOT_MB = 4
|
||
RPMB_MB = 4
|
||
USER_SECTORS = 15154944 # MinSectorCount из FFU
|
||
# QEMU 6.2 sd-card требует power-of-2 размер: добиваем файл до 8GiB.
|
||
# GPT (primary+backup) остается на РЕАЛЬНЫХ смещениях User-области,
|
||
# хвост после User — нули (Windows увидит только primary GPT, WIP-допущение).
|
||
FILE_SIZE = 8 * 1024 * 1024 * 1024
|
||
|
||
BASIC = "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"
|
||
# (name, lba0, lba1, type_guid) — из tools/ffu_parse.py по orig FFU
|
||
PARTS = [
|
||
("DPP", 4096, 20479, BASIC),
|
||
("MODEM_FSG", 20480, 23551, "638ff8e2-22c9-e33b-8f5d-0e81686a68cb"),
|
||
("MODEM_FS1", 24576, 27647, "ebbeadaf-22c9-e33b-8f5d-0e81686a68cb"),
|
||
("MODEM_FS2", 28672, 31743, "0a288b1f-22c9-e33b-8f5d-0e81686a68cb"),
|
||
("MODEM_FSC", 32768, 32799, "57b90a16-22c9-e33b-8f5d-0e81686a68cb"),
|
||
("DDR", 36864, 36927, "20a0c19c-286a-42fa-9ce7-f64c3226a794"),
|
||
("LogFS", 40960, 73727, "bc0330eb-3410-4951-a617-03898dbe3372"),
|
||
("SSD", 73728, 73759, "2c86e742-745e-4fdd-bfd8-b6a7ac638772"),
|
||
("UEFI_BS_NV", 77824, 78335, "f0b4f48b-aeba-4ecf-9142-5dc30cdc3e77"),
|
||
("UEFI_RT_NV", 81920, 82431, "6bb94537-7d1c-44d0-9dfe-6d77c011dbfc"),
|
||
("BOOTMODE", 86016, 86047, "0e91ab96-8339-4ae4-a310-ed94357fd7d0"),
|
||
("SBL1", 90112, 91135, "dea0ba2c-cbdd-4805-b4f9-f428251c3e98"),
|
||
("DBI", 94208, 94271, "d4e0d938-b7fa-48c1-9d21-bc5ed5c4b203"),
|
||
("UEFI", 98304, 102399, "400ffdcd-22e0-47e7-9a23-f16ed9382388"),
|
||
("RPM", 102400, 103423, "098df793-d712-413d-9d4e-89d711772228"),
|
||
("QSEE", 106496, 108543, "a053aa7f-40b8-4b1c-ba08-2f68ac71a4f4"),
|
||
("QHEE", 110592, 111615, "e1a6a689-0c8d-4cc6-b4e8-55a4320fbd8a"),
|
||
("WINSECAPP", 114688, 115711, "69b4201f-a5ad-45eb-9f49-45b38ccdaef5"),
|
||
("TZAPPS", 118784, 151551, "14d11c40-2a3d-4f97-882d-103a1ec09333"),
|
||
("BACKUP_SBL1", 151552, 152575, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_DBI", 155648, 155711, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_UEFI", 159744, 163839, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_RPM", 163840, 164863, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_QSEE", 167936, 169983, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_QHEE", 172032, 173055, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_WINSECAPP", 176128, 177151, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("BACKUP_TZAPPS", 180224, 212991, "a3381699-350c-465e-bd5d-fa3ab901a39a"),
|
||
("PLAT", 212992, 229375, "543c031a-4cb6-4897-bffe-4b485768a8ad"),
|
||
("EFIESP", 229376, 360447, BASIC),
|
||
("MMOS", 360448, 372735, BASIC),
|
||
("SEC", 372736, 372767, "303e6ac3-af15-4c54-9e9b-d9a8fbecf401"),
|
||
("MainOS", 376832, 4089855, BASIC),
|
||
("Data", 4096000, 15150847, BASIC),
|
||
]
|
||
|
||
|
||
def gpt_header(nent, lba_backup, disk_guid, crc_entries, cur=1):
|
||
h = struct.pack("<8sIIIIQQQQ16sQIII",
|
||
b"EFI PART", 0x10000, 92, 0, 0, cur, lba_backup,
|
||
34, USER_SECTORS - 34,
|
||
disk_guid.bytes_le, 2, nent, 128, crc_entries)
|
||
crc = zlib.crc32(h) & 0xFFFFFFFF
|
||
return struct.pack("<8sIIIIQQQQ16sQIII",
|
||
b"EFI PART", 0x10000, 92, crc, 0, cur, lba_backup,
|
||
34, USER_SECTORS - 34,
|
||
disk_guid.bytes_le, 2, nent, 128, crc_entries)
|
||
|
||
|
||
def main():
|
||
out = sys.argv[-1] if not sys.argv[-1].startswith("-") else "images/emmc.img"
|
||
user_src = None
|
||
if "--user" in sys.argv:
|
||
user_src = sys.argv[sys.argv.index("--user") + 1]
|
||
pre = (BOOT_MB * 2 + RPMB_MB) * 1024 * 1024
|
||
total = FILE_SIZE
|
||
assert pre + USER_SECTORS * SECTOR <= total
|
||
print(f"[mkimage] {out}: boot {BOOT_MB}M x2 + rpmb {RPMB_MB}M + "
|
||
f"user {USER_SECTORS} sectors" +
|
||
(f" + content {user_src}" if user_src else " (zero)"))
|
||
with open(out, "wb") as f:
|
||
f.truncate(total) # sparse
|
||
uoff = pre
|
||
with open(out, "wb") as f:
|
||
f.truncate(total) # sparse
|
||
uoff = pre
|
||
# protective MBR
|
||
mbr = bytearray(512)
|
||
mbr[446:462] = struct.pack("<B3sB3sII", 0, b"\x00\x02\x00", 0xEE,
|
||
b"\xFF\xFF\xFF",
|
||
1, USER_SECTORS - 1)
|
||
mbr[510:512] = b"\x55\xAA"
|
||
f.seek(uoff)
|
||
f.write(mbr)
|
||
# entries
|
||
ent = bytearray(128 * 128)
|
||
for i, (name, a, b, t) in enumerate(PARTS):
|
||
e = struct.pack("<16s16sQQQ", uuid.UUID(t).bytes_le,
|
||
uuid.uuid4().bytes_le, a, b, 0)
|
||
e += name.encode("utf-16-le")[:72].ljust(72, b"\x00")
|
||
ent[i * 128:(i + 1) * 128] = e
|
||
crc_e = zlib.crc32(ent) & 0xFFFFFFFF
|
||
disk = uuid.uuid4()
|
||
f.seek(uoff + 512)
|
||
f.write(gpt_header(128, USER_SECTORS - 1, disk, crc_e))
|
||
f.seek(uoff + 1024)
|
||
f.write(ent)
|
||
# backup GPT at end of User
|
||
f.seek(uoff + (USER_SECTORS - 33) * SECTOR)
|
||
f.write(ent)
|
||
f.seek(uoff + (USER_SECTORS - 1) * SECTOR)
|
||
f.write(gpt_header(128, 1, disk, crc_e,
|
||
cur=USER_SECTORS - 1))
|
||
if user_src:
|
||
print(f"[mkimage] copying User content (keeps GPT in place)...")
|
||
with open(user_src, "rb") as u:
|
||
# copy in 128M chunks, skipping GPT areas (LBA 0..33, backup)
|
||
u.seek(34 * SECTOR)
|
||
f.seek(uoff + 34 * SECTOR)
|
||
left = (USER_SECTORS - 33) * SECTOR - 34 * SECTOR
|
||
while left > 0:
|
||
d = u.read(min(left, 134217728))
|
||
if not d:
|
||
break
|
||
f.write(d)
|
||
left -= len(d)
|
||
print(f"[mkimage] OK: {len(PARTS)} partitions, primary+backup GPT, CRC ok")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|