diff --git a/pmb/aportgen/device.py b/pmb/aportgen/device.py
index 5555a1ff..8f901ed8 100644
--- a/pmb/aportgen/device.py
+++ b/pmb/aportgen/device.py
@@ -17,9 +17,11 @@ You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see .
"""
import logging
+import os
import pmb.helpers.run
import pmb.aportgen.core
import pmb.parse.apkindex
+import pmb.parse.bootimg
def ask_for_architecture(args):
@@ -79,8 +81,47 @@ def ask_for_flash_method(args):
" pmb/config/__init__.py.")
+def ask_for_bootimg(args):
+ logging.info("You can analyze a known working boot.img file to automatically fill"
+ " out the flasher information for your deviceinfo file. Either specify"
+ " the path to an image or press return to skip this step (you can do"
+ " it later with 'pmbootstrap bootimg_analyze').")
+
+ while True:
+ path = os.path.expanduser(pmb.helpers.cli.ask(args, "Path", None, "", False))
+ if not len(path):
+ return None
+ try:
+ return pmb.parse.bootimg(args, path)
+ except Exception as e:
+ logging.fatal("ERROR: " + str(e) + ". Please try again.")
+
+
+def generate_deviceinfo_fastboot_content(args, bootimg=None):
+ if bootimg is None:
+ bootimg = {"cmdline": "",
+ "qcdt": "false",
+ "base": "",
+ "kernel_offset": "",
+ "ramdisk_offset": "",
+ "second_offset": "",
+ "tags_offset": "",
+ "pagesize": "2048"}
+ return """\
+ deviceinfo_kernel_cmdline=\"""" + bootimg["cmdline"] + """\"
+ deviceinfo_generate_bootimg="true"
+ deviceinfo_bootimg_qcdt=\"""" + bootimg["qcdt"] + """\"
+ deviceinfo_flash_offset_base=\"""" + bootimg["base"] + """\"
+ deviceinfo_flash_offset_kernel=\"""" + bootimg["kernel_offset"] + """\"
+ deviceinfo_flash_offset_ramdisk=\"""" + bootimg["ramdisk_offset"] + """\"
+ deviceinfo_flash_offset_second=\"""" + bootimg["second_offset"] + """\"
+ deviceinfo_flash_offset_tags=\"""" + bootimg["tags_offset"] + """\"
+ deviceinfo_flash_pagesize=\"""" + bootimg["pagesize"] + """\"
+ """
+
+
def generate_deviceinfo(args, pkgname, name, manufacturer, arch, has_keyboard,
- has_external_storage, flash_method):
+ has_external_storage, flash_method, bootimg=None):
content = """\
# Reference:
# Please use double quotes only. You can source this file in shell scripts.
@@ -106,18 +147,6 @@ def generate_deviceinfo(args, pkgname, name, manufacturer, arch, has_keyboard,
deviceinfo_flash_methods=\"""" + flash_method + """\"
"""
- content_fastboot = """\
- deviceinfo_kernel_cmdline=""
- deviceinfo_generate_bootimg="true"
- deviceinfo_bootimg_qcdt="false"
- deviceinfo_flash_offset_base=""
- deviceinfo_flash_offset_kernel=""
- deviceinfo_flash_offset_ramdisk=""
- deviceinfo_flash_offset_second=""
- deviceinfo_flash_offset_tags=""
- deviceinfo_flash_pagesize="2048"
- """
-
content_heimdall_bootimg = """\
deviceinfo_flash_heimdall_partition_kernel=""
deviceinfo_flash_heimdall_partition_system=""
@@ -134,9 +163,9 @@ def generate_deviceinfo(args, pkgname, name, manufacturer, arch, has_keyboard,
"""
if flash_method == "fastboot":
- content += content_fastboot
+ content += generate_deviceinfo_fastboot_content(args, bootimg)
elif flash_method == "heimdall-bootimg":
- content += content_fastboot
+ content += generate_deviceinfo_fastboot_content(args, bootimg)
content += content_heimdall_bootimg
elif flash_method == "heimdall-isorec":
content += content_heimdall_isorec
@@ -190,7 +219,10 @@ def generate(args, pkgname):
has_keyboard = ask_for_keyboard(args)
has_external_storage = ask_for_external_storage(args)
flash_method = ask_for_flash_method(args)
+ bootimg = None
+ if flash_method in ["fastboot", "heimdall-bootimg"]:
+ bootimg = ask_for_bootimg(args)
generate_deviceinfo(args, pkgname, name, manufacturer, arch, has_keyboard,
- has_external_storage, flash_method)
+ has_external_storage, flash_method, bootimg)
generate_apkbuild(args, pkgname, name, manufacturer, arch, flash_method)
diff --git a/pmb/helpers/frontend.py b/pmb/helpers/frontend.py
index 6f447815..1a6bc391 100644
--- a/pmb/helpers/frontend.py
+++ b/pmb/helpers/frontend.py
@@ -223,3 +223,11 @@ def zap(args):
pmb.chroot.zap(args, packages=args.packages, http=args.http,
mismatch_bins=args.mismatch_bins, old_bins=args.old_bins,
distfiles=args.distfiles)
+
+
+def bootimg_analyze(args):
+ bootimg = pmb.parse.bootimg(args, args.path)
+ tmp_output = "Put these variables in the deviceinfo file of your device:\n"
+ for line in pmb.aportgen.device.generate_deviceinfo_fastboot_content(args, bootimg).split("\n"):
+ tmp_output += "\n" + line.lstrip()
+ logging.info(tmp_output)
diff --git a/pmb/parse/__init__.py b/pmb/parse/__init__.py
index e2e82cec..23d1ebfa 100644
--- a/pmb/parse/__init__.py
+++ b/pmb/parse/__init__.py
@@ -21,4 +21,5 @@ from pmb.parse.apkbuild import apkbuild
from pmb.parse.binfmt_info import binfmt_info
from pmb.parse.deviceinfo import deviceinfo
from pmb.parse.kconfig import check
+from pmb.parse.bootimg import bootimg
import pmb.parse.arch
diff --git a/pmb/parse/arguments.py b/pmb/parse/arguments.py
index 32508dcc..d8e7d209 100644
--- a/pmb/parse/arguments.py
+++ b/pmb/parse/arguments.py
@@ -316,6 +316,11 @@ def arguments():
config.add_argument("name", nargs="?", help="variable name")
config.add_argument("value", nargs="?", help="set variable to value")
+ # Action: bootimg_analyze
+ bootimg_analyze = sub.add_parser("bootimg_analyze", help="Extract all the"
+ " information from an existing boot.img")
+ bootimg_analyze.add_argument("path", help="path to the boot.img")
+
# Use defaults from the user's config file
args = parser.parse_args()
cfg = pmb.config.load(args)
@@ -346,7 +351,7 @@ def arguments():
"find_aport": {}})
# Add and verify the deviceinfo (only after initialization)
- if args.action not in ("init", "config"):
+ if args.action not in ("init", "config", "bootimg_analyze"):
setattr(args, "deviceinfo", pmb.parse.deviceinfo(args))
arch = args.deviceinfo["arch"]
if (arch != args.arch_native and
diff --git a/pmb/parse/bootimg.py b/pmb/parse/bootimg.py
new file mode 100644
index 00000000..2299da21
--- /dev/null
+++ b/pmb/parse/bootimg.py
@@ -0,0 +1,73 @@
+"""
+Copyright 2017 Oliver Smith
+
+This file is part of pmbootstrap.
+
+pmbootstrap is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+pmbootstrap is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with pmbootstrap. If not, see .
+"""
+import os
+import logging
+import pmb
+
+
+def bootimg(args, path):
+ if not os.path.exists(path):
+ raise RuntimeError("Could not find file '" + path + "'")
+
+ logging.info("NOTE: You will be prompted for your sudo password, so we can set"
+ " up a chroot to extract and analyze your boot.img file")
+ pmb.chroot.apk.install(args, ["file", "unpackbootimg"])
+
+ temp_path = pmb.chroot.other.tempfolder(args, "/tmp/bootimg_parser")
+ bootimg_path = args.work + "/chroot_native" + temp_path + "/boot.img"
+
+ # Copy the boot.img into the chroot temporary folder
+ pmb.helpers.run.root(args, ["cp", path, bootimg_path])
+
+ file_output = pmb.chroot.user(args, ["file", "-b", "boot.img"], working_dir=temp_path,
+ return_stdout=True).rstrip()
+ if "android bootimg" not in file_output.lower():
+ if "linux kernel" in file_output.lower():
+ raise RuntimeError("File is a Kernel image, you might need the 'heimdall-isorec'"
+ " flash method. See also: "
+ "")
+ else:
+ raise RuntimeError("File is not an Android bootimg. (" + file_output + ")")
+
+ # Extract all the files
+ pmb.chroot.user(args, ["unpackbootimg", "-i", "boot.img"], working_dir=temp_path)
+
+ output = {}
+ # Get base, offsets, pagesize, cmdline and qcdt info
+ with open(bootimg_path + "-base", 'r') as f:
+ output["base"] = ("0x%08x" % int(f.read().replace('\n', ''), 16))
+ with open(bootimg_path + "-kernel_offset", 'r') as f:
+ output["kernel_offset"] = ("0x%08x" % int(f.read().replace('\n', ''), 16))
+ with open(bootimg_path + "-ramdisk_offset", 'r') as f:
+ output["ramdisk_offset"] = ("0x%08x" % int(f.read().replace('\n', ''), 16))
+ with open(bootimg_path + "-second_offset", 'r') as f:
+ output["second_offset"] = ("0x%08x" % int(f.read().replace('\n', ''), 16))
+ with open(bootimg_path + "-tags_offset", 'r') as f:
+ output["tags_offset"] = ("0x%08x" % int(f.read().replace('\n', ''), 16))
+ with open(bootimg_path + "-pagesize", 'r') as f:
+ output["pagesize"] = f.read().replace('\n', '')
+ with open(bootimg_path + "-cmdline", 'r') as f:
+ output["cmdline"] = f.read().replace('\n', '')
+ output["qcdt"] = ("true" if os.path.isfile(bootimg_path + "-dt") and
+ os.path.getsize(bootimg_path + "-dt") > 0 else "false")
+
+ # Cleanup
+ pmb.chroot.root(args, ["rm", "-r", temp_path])
+
+ return output
diff --git a/test/test_aportgen_device_wizard.py b/test/test_aportgen_device_wizard.py
index 0c3e3742..774ff310 100644
--- a/test/test_aportgen_device_wizard.py
+++ b/test/test_aportgen_device_wizard.py
@@ -139,6 +139,7 @@ def test_aportgen_device_wizard(args, monkeypatch):
# fastboot (mkbootimg)
answers["overwrite"] = "y"
answers["Flash method"] = "fastboot"
+ answers["Path"] = ""
deviceinfo, apkbuild, apkbuild_linux = generate(args, monkeypatch, answers)
assert apkbuild["depends"] == ["linux-testsuite-testdevice", "mkbootimg"]
assert deviceinfo["flash_methods"] == answers["Flash method"]
diff --git a/test/test_bootimg.py b/test/test_bootimg.py
new file mode 100644
index 00000000..b24c1969
--- /dev/null
+++ b/test/test_bootimg.py
@@ -0,0 +1,85 @@
+"""
+Copyright 2017 Oliver Smith
+
+This file is part of pmbootstrap.
+
+pmbootstrap is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+pmbootstrap is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with pmbootstrap. If not, see .
+"""
+import os
+import sys
+import pytest
+
+# Import from parent directory
+pmb_src = os.path.realpath(os.path.join(os.path.dirname(__file__) + "/.."))
+sys.path.append(pmb_src)
+import pmb.chroot.apk_static
+import pmb.parse.apkindex
+import pmb.helpers.logging
+import pmb.parse.bootimg
+
+
+@pytest.fixture
+def args(request):
+ import pmb.parse
+ sys.argv = ["pmbootstrap.py", "chroot"]
+ args = pmb.parse.arguments()
+ args.log = args.work + "/log_testsuite.txt"
+ pmb.helpers.logging.init(args)
+ request.addfinalizer(args.logfd.close)
+ return args
+
+
+def test_bootimg_invalid_path(args):
+ with pytest.raises(RuntimeError) as e:
+ pmb.parse.bootimg(args, "/invalid-path")
+ assert "Could not find file" in str(e.value)
+
+
+def test_bootimg_kernel(args):
+ path = pmb_src + "/test/testdata/bootimg/kernel-boot.img"
+ with pytest.raises(RuntimeError) as e:
+ pmb.parse.bootimg(args, path)
+ assert "heimdall-isorec" in str(e.value)
+
+
+def test_bootimg_invalid_file(args):
+ with pytest.raises(RuntimeError) as e:
+ pmb.parse.bootimg(args, __file__)
+ assert "File is not an Android bootimg" in str(e.value)
+
+
+def test_bootimg_normal(args):
+ path = pmb_src + "/test/testdata/bootimg/normal-boot.img"
+ output = {"base": "0x80000000",
+ "kernel_offset": "0x00008000",
+ "ramdisk_offset": "0x04000000",
+ "second_offset": "0x00f00000",
+ "tags_offset": "0x0e000000",
+ "pagesize": "2048",
+ "cmdline": "bootopt=64S3,32S1,32S1",
+ "qcdt": "false"}
+ assert pmb.parse.bootimg(args, path) == output
+
+
+def test_bootimg_qcdt(args):
+ path = pmb_src + "/test/testdata/bootimg/qcdt-boot.img"
+ output = {"base": "0x80000000",
+ "kernel_offset": "0x00008000",
+ "ramdisk_offset": "0x04000000",
+ "second_offset": "0x00f00000",
+ "tags_offset": "0x0e000000",
+ "pagesize": "2048",
+ "cmdline": "bootopt=64S3,32S1,32S1",
+ "qcdt": "true"}
+ assert pmb.parse.bootimg(args, path) == output
diff --git a/test/test_questions.py b/test/test_questions.py
index 102830d8..f8eab2c0 100644
--- a/test/test_questions.py
+++ b/test/test_questions.py
@@ -22,8 +22,8 @@ import pytest
import sys
# Import from parent directory
-sys.path.append(os.path.realpath(
- os.path.join(os.path.dirname(__file__) + "/..")))
+pmb_src = os.path.realpath(os.path.join(os.path.dirname(__file__) + "/.."))
+sys.path.append(pmb_src)
import pmb.aportgen.device
import pmb.config
import pmb.config.init
@@ -92,6 +92,23 @@ def test_questions(args, monkeypatch, tmpdir):
answers = ["invalid_arch", "aarch64"]
assert pmb.aportgen.device.ask_for_architecture(args) == "aarch64"
+ # Bootimg
+ func = pmb.aportgen.device.ask_for_bootimg
+ answers = ["invalid_path", ""]
+ assert func(args) is None
+
+ bootimg_path = pmb_src + "/test/testdata/bootimg/normal-boot.img"
+ answers = [bootimg_path]
+ output = {"base": "0x80000000",
+ "kernel_offset": "0x00008000",
+ "ramdisk_offset": "0x04000000",
+ "second_offset": "0x00f00000",
+ "tags_offset": "0x0e000000",
+ "pagesize": "2048",
+ "cmdline": "bootopt=64S3,32S1,32S1",
+ "qcdt": "false"}
+ assert func(args) == output
+
# Device
func = pmb.config.init.ask_for_device
answers = ["lg-mako"]
diff --git a/test/testdata/bootimg/kernel-boot.img b/test/testdata/bootimg/kernel-boot.img
new file mode 100644
index 00000000..47682366
Binary files /dev/null and b/test/testdata/bootimg/kernel-boot.img differ
diff --git a/test/testdata/bootimg/normal-boot.img b/test/testdata/bootimg/normal-boot.img
new file mode 100644
index 00000000..2a2f8633
Binary files /dev/null and b/test/testdata/bootimg/normal-boot.img differ
diff --git a/test/testdata/bootimg/qcdt-boot.img b/test/testdata/bootimg/qcdt-boot.img
new file mode 100644
index 00000000..a6f917ff
Binary files /dev/null and b/test/testdata/bootimg/qcdt-boot.img differ