parse.bootimg: Separate kernel and ramdisk MediaTek headers

Currently, pmbootstrap checks if either the kernel or the ramdisk in a boot.img
contains the MediaTek header, and if one does, it assumes both do. It hardcodes
the label KERNEL for the kernel and ROOTFS for the ramdisk.

My Amazon Echo Dot (gen 2) has a boot.img where only the kernel has the header,
but not the ramdisk. These changes (as well as those in my new boot-deploy MR)
account for that situation (and any possible label an image has) by splitting
bootimg_mtk_mkimage into two variables for the kernel and the ramdisk labels.

Reviewed-by: Oliver Smith <ollieparanoid@postmarketos.org>
Link: https://lists.sr.ht/~postmarketos/pmbootstrap-devel/patches/46351
This commit is contained in:
Ben Westover 2023-11-02 17:03:06 +00:00 committed by Oliver Smith
parent 41daa850d7
commit c10a3ce73b
No known key found for this signature in database
GPG Key ID: 5AE7F5513E0885CB
8 changed files with 63 additions and 67 deletions

View File

@ -121,24 +121,33 @@ def generate_deviceinfo_fastboot_content(bootimg=None):
if bootimg is None:
bootimg = {"cmdline": "",
"qcdt": "false",
"mtk_mkimage": "false",
"dtb_second": "false",
"base": "",
"kernel_offset": "",
"ramdisk_offset": "",
"second_offset": "",
"tags_offset": "",
"pagesize": "2048"}
"pagesize": "2048",
"mtk_label_kernel": "",
"mtk_label_ramdisk": ""}
content = f"""\
deviceinfo_kernel_cmdline="{bootimg["cmdline"]}"
deviceinfo_generate_bootimg="true"
deviceinfo_bootimg_qcdt="{bootimg["qcdt"]}"
deviceinfo_bootimg_mtk_mkimage="{bootimg["mtk_mkimage"]}"
deviceinfo_bootimg_dtb_second="{bootimg["dtb_second"]}"
deviceinfo_flash_pagesize="{bootimg["pagesize"]}"
"""
if "mtk_label_kernel" in bootimg.keys():
content += f"""\
deviceinfo_mtk_label_kernel="{bootimg["mtk_label_kernel"]}"
"""
if "mtk_label_ramdisk" in bootimg.keys():
content += f"""\
deviceinfo_mtk_label_ramdisk="{bootimg["mtk_label_ramdisk"]}"
"""
if "header_version" in bootimg.keys():
content += f"""\
deviceinfo_header_version="{bootimg["header_version"]}"

View File

@ -823,7 +823,9 @@ deviceinfo_attributes = [
"generate_bootimg",
"header_version",
"bootimg_qcdt",
"bootimg_mtk_mkimage",
"bootimg_mtk_mkimage", # deprecated
"bootimg_mtk_label_kernel",
"bootimg_mtk_label_ramdisk",
"bootimg_dtb_second",
"bootimg_custom_args",
"flash_offset_base",

View File

@ -14,12 +14,11 @@ def is_dtb(path):
def get_mtk_label(path):
""" Read the label of a mediatek header of kernel or ramdisk inside an
extracted boot.img.
:param path: to either the kernel or ramdisk file extracted from
boot.img
:returns: * None: file does not exist or does not have Mediatek header
* Label string (e.g. "ROOTFS", "RECOVERY", "KERNEL") """
""" Read the label from the MediaTek header of the kernel or ramdisk inside
an extracted boot.img.
:param path: to either the kernel or ramdisk extracted from boot.img
:returns: * None: file does not exist or does not have MediaTek header
* Label string (e.g. "ROOTFS", "KERNEL") """
if not os.path.exists(path):
return None
@ -29,50 +28,20 @@ def get_mtk_label(path):
return None
f.seek(8)
label = f.read(32).decode("utf-8").rstrip('\0')
return label
def check_mtk_bootimg(bootimg_path):
""" Check if a boot.img contains a kernel and ramdisk with Mediatek
headers, and verify that these headers have labels we expect in
boot-deploy.
:param bootimg_path: path to boot.img, with extracted files in the same
directory
:returns: * True: has Mediatek headers
* False: has no Mediatek headers """
label_kernel = get_mtk_label(f"{bootimg_path}-kernel")
label_ramdisk = get_mtk_label(f"{bootimg_path}-ramdisk")
# Doesn't have Mediatek headers
if label_kernel is None and label_ramdisk is None:
return False
# Verify that the kernel and ramdisk have the labels we expect and have
# hardcoded in boot-deploy.git's add_mtk_header() function. We don't know
# if there are devices out there with different labels, but if there are,
# our code in boot-deploy needs to be adjusted to use the proper labels
# (store the label in deviceinfo and use it).
err_start = "This boot.img has Mediatek headers."
err_end = ("Please create an issue and attach your boot.img:"
" https://postmarketos.org/issues")
if label_kernel != "KERNEL":
raise RuntimeError(f"{err_start} Expected the kernel inside the"
" boot.img to have a 'KERNEL' label instead of"
f" '{label_kernel}'. {err_end}")
if label_ramdisk == "RECOVERY":
logging.warning(
f"WARNING: {err_start} But since you apparently passed a recovery"
" image instead of a regular boot.img, we can't tell if it has the"
" expected label 'ROOTFS' inside the ramdisk (found 'RECOVERY')."
" So there is a slight chance that it may not boot, in that case"
" run bootimg_analyze again with a regular boot.img. It will fail"
" if the label is different from 'ROOTFS'.")
elif label_ramdisk != "ROOTFS":
raise RuntimeError(f"{err_start} Expected the ramdisk inside the"
" boot.img to have a 'ROOTFS' label instead of"
f" '{label_ramdisk}'. {err_end}")
return True
if label == "RECOVERY":
logging.warning(
"WARNING: This boot.img has MediaTek headers. Since you passed a"
" recovery image instead of a regular boot.img, we can't tell what"
" the ramdisk signature label is supposed to be, so we assume that"
" it's the most common value, ROOTFS. There is a chance that this"
" is wrong and it may not boot; in that case, run bootimg_analyze"
" again with a regular boot.img. If this *is* a regular boot.img,"
" replace the value of deviceinfo_bootimg_mtk_label_ramdisk with"
" 'RECOVERY'.")
return "ROOTFS"
else:
return label
def bootimg(args, path):
@ -152,10 +121,14 @@ def bootimg(args, path):
output["dtb_offset"] = ("0x%08x"
% int(f.read().replace('\n', ''), 16))
if get_mtk_label(f"{bootimg_path}-kernel") is not None:
output["mtk_label_kernel"] = get_mtk_label(f"{bootimg_path}-kernel")
if get_mtk_label(f"{bootimg_path}-ramdisk") is not None:
output["mtk_label_ramdisk"] = get_mtk_label(f"{bootimg_path}-ramdisk")
output["qcdt"] = ("true" if os.path.isfile(f"{bootimg_path}-dt") and
os.path.getsize(f"{bootimg_path}-dt") > 0 else "false")
output["mtk_mkimage"] = ("true" if check_mtk_bootimg(bootimg_path)
else "false")
output["dtb_second"] = ("true" if is_dtb(f"{bootimg_path}-second")
else "false")

View File

@ -52,7 +52,6 @@ def test_bootimg_normal(args):
"pagesize": "2048",
"cmdline": "bootopt=64S3,32S1,32S1",
"qcdt": "false",
"mtk_mkimage": "false",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output
@ -67,13 +66,12 @@ def test_bootimg_qcdt(args):
"pagesize": "2048",
"cmdline": "bootopt=64S3,32S1,32S1",
"qcdt": "true",
"mtk_mkimage": "false",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output
def test_bootimg_mtk_mkimage(args):
path = pmb_test.const.testdata + "/bootimg/mtk_mkimage-boot.img"
def test_bootimg_mtk(args):
path = pmb_test.const.testdata + "/bootimg/mtk-boot.img"
output = {"header_version": "0",
"base": "0x10000000",
"kernel_offset": "0x00008000",
@ -81,15 +79,16 @@ def test_bootimg_mtk_mkimage(args):
"second_offset": "0x00f00000",
"tags_offset": "0x00000100",
"pagesize": "2048",
"mtk_label_kernel": "KERNEL",
"mtk_label_ramdisk": "ROOTFS",
"cmdline": "",
"qcdt": "false",
"mtk_mkimage": "true",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output
def test_bootimg_mtk_mkimage_recovery(args):
path = pmb_test.const.testdata + "/bootimg/mtk_mkimage-boot-recovery.img"
def test_bootimg_mtk_recovery(args):
path = pmb_test.const.testdata + "/bootimg/mtk-boot-recovery.img"
output = {"header_version": "0",
"base": "0x80000000",
"kernel_offset": "0x00008000",
@ -97,9 +96,26 @@ def test_bootimg_mtk_mkimage_recovery(args):
"second_offset": "0x00f00000",
"tags_offset": "0x00000100",
"pagesize": "2048",
"mtk_label_kernel": "KERNEL",
"mtk_label_ramdisk": "ROOTFS",
"cmdline": "",
"qcdt": "false",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output
def test_bootimg_mtk_kernelonly(args):
path = pmb_test.const.testdata + "/bootimg/mtk-boot-kernel-only.img"
output = {"header_version": "0",
"base": "0x10000000",
"kernel_offset": "0x00008000",
"ramdisk_offset": "0x01000000",
"second_offset": "0xf0000000",
"tags_offset": "0x00000100",
"pagesize": "2048",
"mtk_label_kernel": "KERNEL",
"cmdline": "",
"qcdt": "false",
"mtk_mkimage": "true",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output
@ -115,7 +131,6 @@ def test_bootimg_dtb_second(args):
"pagesize": "2048",
"cmdline": "bootopt=64S3,32S1,32S1",
"qcdt": "false",
"mtk_mkimage": "false",
"dtb_second": "true"}
assert pmb.parse.bootimg(args, path) == output
@ -132,7 +147,6 @@ def test_bootimg_v2(args):
"dtb_offset": "0x0bc08000",
"cmdline": "bootopt=64S3,32N2,64N2 systempart=/dev/mapper/system",
"qcdt": "false",
"mtk_mkimage": "false",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output
@ -143,6 +157,5 @@ def test_bootimg_v3(args):
"pagesize": "4096",
"cmdline": "twrpfastboot=1",
"qcdt": "false",
"mtk_mkimage": "false",
"dtb_second": "false"}
assert pmb.parse.bootimg(args, path) == output

View File

@ -100,7 +100,6 @@ def test_questions_bootimg(args, monkeypatch):
"pagesize": "2048",
"cmdline": "bootopt=64S3,32S1,32S1",
"qcdt": "false",
"mtk_mkimage": "false",
"dtb_second": "false"}
assert func(args) == output

Binary file not shown.