From 87dd071b3262e8f837af71bf217b8cc705a90a29 Mon Sep 17 00:00:00 2001 From: Minecrell Date: Tue, 4 Feb 2020 11:30:28 +0100 Subject: [PATCH] add "fastboot-bootpart" flasher to flash split images with fastboot (!1871) asus-me176c has a Fastboot interface that can be used for flashing, but in postmarketOS we do not use Android boot images for it. This is because is it not very practical - the boot partition is quite small and there is a (custom) EFI bootloader that can boot directly from any other FAT32 partition. At the moment the installation process is manual: 1. pmbootstrap install --split to have separated boot (FAT32) and rootfs images 2. pmbootstrap export 3. Flash boot and rootfs images manually using Fastboot The "fastboot-bootpart" flasher implements that process in a more convenient way. When a device uses the "fastboot-bootpart" flasher: - We generate --split images on "pmbootstrap install" by default. (This can be disabled using --no-split instead.) - pmbootstrap flasher flash_kernel flashes the raw boot partition (not an Android boot image) using Fastboot, just like the rootfs. There are some limitations that could be improved in the future: - "fastboot-bootpart" is not offered in the device wizard. I think it is special enough that no-one will be starting with it, and the difference to normal "fastboot" might be confusing. - Support "pmbootstrap flasher boot". asus-me176c does not support "fastboot boot" properly, but theoretically we could still generate Android boot images to use when booting an image directly. - At the moment the boot partition image is not regenerated when using "pmbootstrap flasher flash_kernel" (unlike when using Android boot images). "pmbootstrap install" needs to be run manually first. --- pmb/config/__init__.py | 21 +++++++++++++++- pmb/flasher/frontend.py | 15 +++++++---- pmb/flasher/variables.py | 4 ++- pmb/helpers/frontend.py | 6 +++++ pmb/install/_install.py | 54 ++++++++++++++++++++++++---------------- pmb/parse/arguments.py | 6 ++++- 6 files changed, 77 insertions(+), 29 deletions(-) diff --git a/pmb/config/__init__.py b/pmb/config/__init__.py index b2e60dab..0f36a83d 100644 --- a/pmb/config/__init__.py +++ b/pmb/config/__init__.py @@ -344,7 +344,9 @@ Flasher abstraction. Allowed variables: $BOOT: Path to the /boot partition $FLAVOR: Kernel flavor -$IMAGE: Path to the rootfs image +$IMAGE: Path to the combined boot/rootfs image +$IMAGE_SPLIT_BOOT: Path to the (split) boot image +$IMAGE_SPLIT_ROOT: Path to the (split) rootfs image $PARTITION_KERNEL: Partition to flash the kernel/boot.img to $PARTITION_SYSTEM: Partition to flash the rootfs to @@ -365,6 +367,23 @@ flashers = { "boot", "$BOOT/boot.img-$FLAVOR"]], }, }, + # Some devices provide Fastboot but using Android boot images is not practical + # for them (e.g. because they support booting from FAT32 partitions directly + # and/or the Android boot partition is too small). This can be implemented + # using --split (separate image files for boot and rootfs). + # This flasher allows flashing the split image files using Fastboot. + "fastboot-bootpart": { + "split": True, + "depends": ["android-tools"], + "actions": { + "list_devices": [["fastboot", "devices", "-l"]], + "flash_rootfs": [["fastboot", "flash", "$PARTITION_SYSTEM", + "$IMAGE_SPLIT_ROOT"]], + "flash_kernel": [["fastboot", "flash", "$PARTITION_KERNEL", + "$IMAGE_SPLIT_BOOT"]], + # TODO: Add support for boot + }, + }, # Some Samsung devices need the initramfs to be baked into the kernel (e.g. # i9070, i9100). We want the initramfs to be generated after the kernel was # built, so we put the real initramfs on another partition (e.g. RECOVERY) diff --git a/pmb/flasher/frontend.py b/pmb/flasher/frontend.py index 0d20c722..705803e5 100644 --- a/pmb/flasher/frontend.py +++ b/pmb/flasher/frontend.py @@ -61,16 +61,21 @@ def list_flavors(args): def rootfs(args): + method = args.flash_method or args.deviceinfo["flash_method"] + # Generate rootfs, install flasher - img_path = "/home/pmos/rootfs/" + args.device + ".img" - if not os.path.exists(args.work + "/chroot_native" + img_path): + suffix = ".img" + if pmb.config.flashers.get(method, {}).get("split", False): + suffix = "-root.img" + + img_path = args.work + "/chroot_native/home/pmos/rootfs/" + args.device + suffix + if not os.path.exists(img_path): raise RuntimeError("The rootfs has not been generated yet, please run" " 'pmbootstrap install' first.") # Do not flash if using fastboot & image is too large - method = args.flash_method or args.deviceinfo["flash_method"] - if method == "fastboot" and args.deviceinfo["flash_fastboot_max_size"]: - img_size = os.path.getsize(args.work + "/chroot_native" + img_path) / 1024**2 + if method.startswith("fastboot") and args.deviceinfo["flash_fastboot_max_size"]: + img_size = os.path.getsize(img_path) / 1024**2 max_size = int(args.deviceinfo["flash_fastboot_max_size"]) if img_size > max_size: raise RuntimeError("The rootfs is too large for fastboot to" diff --git a/pmb/flasher/variables.py b/pmb/flasher/variables.py index 845d1def..601055f7 100644 --- a/pmb/flasher/variables.py +++ b/pmb/flasher/variables.py @@ -23,7 +23,7 @@ def variables(args, flavor, method): if "cmdline" in args and args.cmdline: _cmdline = args.cmdline - if method == "fastboot": + if method.startswith("fastboot"): _partition_kernel = args.deviceinfo["flash_fastboot_partition_kernel"] or "boot" _partition_system = args.deviceinfo["flash_fastboot_partition_system"] or "system" else: @@ -38,6 +38,8 @@ def variables(args, flavor, method): vars = { "$BOOT": "/mnt/rootfs_" + args.device + "/boot", "$FLAVOR": flavor if flavor is not None else "", + "$IMAGE_SPLIT_BOOT": "/home/pmos/rootfs/" + args.device + "-boot.img", + "$IMAGE_SPLIT_ROOT": "/home/pmos/rootfs/" + args.device + "-root.img", "$IMAGE": "/home/pmos/rootfs/" + args.device + ".img", "$KERNEL_CMDLINE": _cmdline, "$PARTITION_KERNEL": _partition_kernel, diff --git a/pmb/helpers/frontend.py b/pmb/helpers/frontend.py index eabb5760..d08ebf91 100644 --- a/pmb/helpers/frontend.py +++ b/pmb/helpers/frontend.py @@ -195,6 +195,12 @@ def install(args): if args.rsync and not args.sdcard: raise ValueError("Installation using rsync only works on sdcard.") + if not args.sdcard and args.split is None: + # Default to split if the flash method requires it + flasher = pmb.config.flashers.get(args.deviceinfo["flash_method"], {}) + if flasher.get("split", False): + args.split = True + pmb.install.install(args) diff --git a/pmb/install/_install.py b/pmb/install/_install.py index 38f64a46..c14ca100 100644 --- a/pmb/install/_install.py +++ b/pmb/install/_install.py @@ -436,35 +436,47 @@ def install_system_image(args): " target device:") # System flash information - if not args.sdcard and not args.split: + method = args.deviceinfo["flash_method"] + flasher = pmb.config.flashers.get(method, {}) + flasher_actions = flasher.get("actions", {}) + requires_split = flasher.get("split", False) + + if "flash_rootfs" in flasher_actions and not args.sdcard and \ + bool(args.split) == requires_split: logging.info("* pmbootstrap flasher flash_rootfs") logging.info(" Flashes the generated rootfs image to your device:") - logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" + - args.device + ".img") - logging.info(" (NOTE: This file has a partition table, which contains" - " /boot and / subpartitions. That way we don't need to" - " change the partition layout on your device.)") + if args.split: + logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" + + args.device + "-rootfs.img") + else: + logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" + + args.device + ".img") + logging.info(" (NOTE: This file has a partition table, which contains" + " /boot and / subpartitions. That way we don't need to" + " change the partition layout on your device.)") - logging.info("* pmbootstrap flasher flash_kernel") - logging.info(" Flashes the kernel + initramfs to your device:") - logging.info(" " + args.work + "/chroot_rootfs_" + args.device + - "/boot") - method = args.deviceinfo["flash_method"] - if (method in pmb.config.flashers and "boot" in - pmb.config.flashers[method]["actions"]): + # Most flash methods operate independently of the boot partition. + # (e.g. an Android boot image is generated). In that case, "flash_kernel" + # works even when partitions are split or installing for sdcard. + # This is not possible if the flash method requires split partitions. + if "flash_kernel" in flasher_actions and (not requires_split or args.split): + logging.info("* pmbootstrap flasher flash_kernel") + logging.info(" Flashes the kernel + initramfs to your device:") + if requires_split: + logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" + + args.device + "-boot.img") + else: + logging.info(" " + args.work + "/chroot_rootfs_" + args.device + "/boot") + + if "boot" in flasher_actions: logging.info(" (NOTE: " + method + " also supports booting" " the kernel/initramfs directly without flashing." " Use 'pmbootstrap flasher boot' to do that.)") # Export information - if args.split: - logging.info("* Boot and root image files have been generated, run" - " 'pmbootstrap export' to create symlinks and flash" - " outside of pmbootstrap.") - else: - logging.info("* If the above steps do not work, you can also create" - " symlinks to the generated files with 'pmbootstrap export'" - " and flash outside of pmbootstrap.") + logging.info("* If the above steps do not work, you can also create" + " symlinks to the generated files with 'pmbootstrap export'" + " and flash outside of pmbootstrap.") def install_recovery_zip(args): diff --git a/pmb/parse/arguments.py b/pmb/parse/arguments.py index 6e71e3f0..ceb7c3a3 100644 --- a/pmb/parse/arguments.py +++ b/pmb/parse/arguments.py @@ -472,7 +472,11 @@ def arguments(): group.add_argument("--sdcard", help="path to the sdcard device," " eg. /dev/mmcblk0") group.add_argument("--split", help="install the boot and root partition" - " in separated image files", action="store_true") + " in separated image files (default: only if flash method" + " requires it)", action="store_true", default=None) + group.add_argument("--no-split", help="create combined boot + root image" + " even if flash method requires it", + dest="split", action="store_false") group.add_argument("--android-recovery-zip", help="generate TWRP flashable zip", action="store_true", dest="android_recovery_zip")