// SPDX-License-Identifier: GPL-2.0+ /* * Bootmeth for Android * * Copyright (C) 2024 BayLibre, SAS * Written by Mattijs Korpershoek */ #define LOG_CATEGORY UCLASS_BOOTSTD #include #include #if CONFIG_IS_ENABLED(AVB_VERIFY) #include #endif #include #include #include #include #include #include #include #include #include #include #include "bootmeth_android.h" #define BCB_FIELD_COMMAND_SZ 32 #define BCB_PART_NAME "misc" #define BOOT_PART_NAME "boot" #define VENDOR_BOOT_PART_NAME "vendor_boot" /** * struct android_priv - Private data * * This is read from the disk and recorded for use when the full Android * kernel must be loaded and booted * * @boot_mode: Requested boot mode (normal, recovery, bootloader) * @slot: Nul-terminated partition slot suffix read from BCB ("a\0" or "b\0") * @header_version: Android boot image header version */ struct android_priv { enum android_boot_mode boot_mode; char slot[2]; u32 header_version; }; static int android_check(struct udevice *dev, struct bootflow_iter *iter) { /* This only works on mmc devices */ if (bootflow_iter_check_mmc(iter)) return log_msg_ret("mmc", -ENOTSUPP); /* * This only works on whole devices, as multiple * partitions are needed to boot Android */ if (iter->part != 0) return log_msg_ret("mmc part", -ENOTSUPP); return 0; } static int scan_boot_part(struct udevice *blk, struct android_priv *priv) { struct blk_desc *desc = dev_get_uclass_plat(blk); struct disk_partition partition; char partname[PART_NAME_LEN]; ulong num_blks, bufsz; char *buf; int ret; sprintf(partname, BOOT_PART_NAME "_%s", priv->slot); ret = part_get_info_by_name(desc, partname, &partition); if (ret < 0) return log_msg_ret("part info", ret); num_blks = DIV_ROUND_UP(sizeof(struct andr_boot_img_hdr_v0), desc->blksz); bufsz = num_blks * desc->blksz; buf = malloc(bufsz); if (!buf) return log_msg_ret("buf", -ENOMEM); ret = blk_read(blk, partition.start, num_blks, buf); if (ret != num_blks) { free(buf); return log_msg_ret("part read", -EIO); } if (!is_android_boot_image_header(buf)) { free(buf); return log_msg_ret("header", -ENOENT); } priv->header_version = ((struct andr_boot_img_hdr_v0 *)buf)->header_version; free(buf); return 0; } static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv) { struct blk_desc *desc = dev_get_uclass_plat(blk); struct disk_partition partition; char partname[PART_NAME_LEN]; ulong num_blks, bufsz; char *buf; int ret; sprintf(partname, VENDOR_BOOT_PART_NAME "_%s", priv->slot); ret = part_get_info_by_name(desc, partname, &partition); if (ret < 0) return log_msg_ret("part info", ret); num_blks = DIV_ROUND_UP(sizeof(struct andr_vnd_boot_img_hdr), desc->blksz); bufsz = num_blks * desc->blksz; buf = malloc(bufsz); if (!buf) return log_msg_ret("buf", -ENOMEM); ret = blk_read(blk, partition.start, num_blks, buf); if (ret != num_blks) { free(buf); return log_msg_ret("part read", -EIO); } if (!is_android_vendor_boot_image_header(buf)) { free(buf); return log_msg_ret("header", -ENOENT); } free(buf); return 0; } static int android_read_slot_from_bcb(struct bootflow *bflow, bool decrement) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); struct android_priv *priv = bflow->bootmeth_priv; struct disk_partition misc; char slot_suffix[3]; int ret; ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc); if (ret < 0) return log_msg_ret("part", ret); ret = ab_select_slot(desc, &misc, decrement); if (ret < 0) return log_msg_ret("slot", ret); priv->slot[0] = BOOT_SLOT_NAME(ret); priv->slot[1] = '\0'; sprintf(slot_suffix, "_%s", priv->slot); ret = bootflow_cmdline_set_arg(bflow, "androidboot.slot_suffix", slot_suffix, false); if (ret < 0) return log_msg_ret("cmdl", ret); return 0; } static int configure_serialno(struct bootflow *bflow) { char *serialno = env_get("serial#"); if (!serialno) return log_msg_ret("serial", -ENOENT); return bootflow_cmdline_set_arg(bflow, "androidboot.serialno", serialno, false); } static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); struct disk_partition misc; struct android_priv *priv; char command[BCB_FIELD_COMMAND_SZ]; int ret; bflow->state = BOOTFLOWST_MEDIA; /* * bcb_find_partition_and_load() will print errors to stdout * if BCB_PART_NAME is not found. To avoid that, check if the * partition exists first. */ ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc); if (ret < 0) return log_msg_ret("part", ret); ret = bcb_find_partition_and_load("mmc", desc->devnum, BCB_PART_NAME); if (ret < 0) return log_msg_ret("bcb load", ret); ret = bcb_get(BCB_FIELD_COMMAND, command, sizeof(command)); if (ret < 0) return log_msg_ret("bcb read", ret); priv = malloc(sizeof(struct android_priv)); if (!priv) return log_msg_ret("buf", -ENOMEM); if (!strcmp("bootonce-bootloader", command)) { priv->boot_mode = ANDROID_BOOT_MODE_BOOTLOADER; bflow->os_name = strdup("Android (bootloader)"); } else if (!strcmp("boot-fastboot", command)) { priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY; bflow->os_name = strdup("Android (fastbootd)"); } else if (!strcmp("boot-recovery", command)) { priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY; bflow->os_name = strdup("Android (recovery)"); } else { priv->boot_mode = ANDROID_BOOT_MODE_NORMAL; bflow->os_name = strdup("Android"); } if (!bflow->os_name) return log_msg_ret("os", -ENOMEM); if (priv->boot_mode == ANDROID_BOOT_MODE_BOOTLOADER) { /* Clear BCB */ memset(command, 0, sizeof(command)); ret = bcb_set(BCB_FIELD_COMMAND, command); if (ret < 0) { free(priv); return log_msg_ret("bcb set", ret); } ret = bcb_store(); if (ret < 0) { free(priv); return log_msg_ret("bcb store", ret); } bflow->bootmeth_priv = priv; bflow->state = BOOTFLOWST_READY; return 0; } bflow->bootmeth_priv = priv; /* For recovery and normal boot, we need to scan the partitions */ ret = android_read_slot_from_bcb(bflow, false); if (ret < 0) { log_err("read slot: %d", ret); goto free_priv; } ret = scan_boot_part(bflow->blk, priv); if (ret < 0) { log_debug("scan boot failed: err=%d\n", ret); goto free_priv; } if (priv->header_version != 4) { log_debug("only boot.img v4 is supported %u\n", priv->header_version); ret = -EINVAL; goto free_priv; } ret = scan_vendor_boot_part(bflow->blk, priv); if (ret < 0) { log_debug("scan vendor_boot failed: err=%d\n", ret); goto free_priv; } /* Ignoring return code: setting serial number is not mandatory for booting */ configure_serialno(bflow); if (priv->boot_mode == ANDROID_BOOT_MODE_NORMAL) { ret = bootflow_cmdline_set_arg(bflow, "androidboot.force_normal_boot", "1", false); if (ret < 0) { log_debug("normal_boot %d", ret); goto free_priv; } } bflow->state = BOOTFLOWST_READY; return 0; free_priv: free(priv); bflow->bootmeth_priv = NULL; return ret; } static int android_read_file(struct udevice *dev, struct bootflow *bflow, const char *file_path, ulong addr, ulong *sizep) { /* * Reading individual files is not supported since we only * operate on whole mmc devices (because we require multiple partitions) */ return log_msg_ret("Unsupported", -ENOSYS); } /** * read_slotted_partition() - Read a partition by appending a slot suffix * * Most modern Android devices use Seamless Updates, where each partition * is duplicated. For example, the boot partition has boot_a and boot_b. * For more information, see: * https://source.android.com/docs/core/ota/ab * https://source.android.com/docs/core/ota/ab/ab_implement * * @blk: Block device to read * @name: Partition name to read * @slot: Nul-terminated slot suffixed to partition name ("a\0" or "b\0") * @addr: Address where the partition content is loaded into * Return: 0 if OK, negative errno on failure. */ static int read_slotted_partition(struct blk_desc *desc, const char *const name, const char slot[2], ulong addr) { struct disk_partition partition; char partname[PART_NAME_LEN]; int ret; u32 n; /* Ensure name fits in partname it should be: _\0 */ if (strlen(name) > (PART_NAME_LEN - 2 - 1)) return log_msg_ret("name too long", -EINVAL); sprintf(partname, "%s_%s", name, slot); ret = part_get_info_by_name(desc, partname, &partition); if (ret < 0) return log_msg_ret("part", ret); n = blk_dread(desc, partition.start, partition.size, map_sysmem(addr, 0)); if (n < partition.size) return log_msg_ret("part read", -EIO); return 0; } #if CONFIG_IS_ENABLED(AVB_VERIFY) static int avb_append_commandline_arg(struct bootflow *bflow, char *arg) { char *key = strsep(&arg, "="); char *value = arg; int ret; ret = bootflow_cmdline_set_arg(bflow, key, value, false); if (ret < 0) return log_msg_ret("avb cmdline", ret); return 0; } static int avb_append_commandline(struct bootflow *bflow, char *cmdline) { char *arg = strsep(&cmdline, " "); int ret; while (arg) { ret = avb_append_commandline_arg(bflow, arg); if (ret < 0) return ret; arg = strsep(&cmdline, " "); } return 0; } static int run_avb_verification(struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); struct android_priv *priv = bflow->bootmeth_priv; const char * const requested_partitions[] = {"boot", "vendor_boot"}; struct AvbOps *avb_ops; AvbSlotVerifyResult result; AvbSlotVerifyData *out_data; enum avb_boot_state boot_state; char *extra_args; char slot_suffix[3]; bool unlocked = false; int ret; avb_ops = avb_ops_alloc(desc->devnum); if (!avb_ops) return log_msg_ret("avb ops", -ENOMEM); sprintf(slot_suffix, "_%s", priv->slot); ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked); if (ret != AVB_IO_RESULT_OK) return log_msg_ret("avb lock", -EIO); result = avb_slot_verify(avb_ops, requested_partitions, slot_suffix, unlocked, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &out_data); if (result != AVB_SLOT_VERIFY_RESULT_OK) { printf("Verification failed, reason: %s\n", str_avb_slot_error(result)); avb_slot_verify_data_free(out_data); return log_msg_ret("avb verify", -EIO); } if (unlocked) boot_state = AVB_ORANGE; else boot_state = AVB_GREEN; extra_args = avb_set_state(avb_ops, boot_state); if (extra_args) { /* extra_args will be modified after this. This is fine */ ret = avb_append_commandline_arg(bflow, extra_args); if (ret < 0) goto free_out_data; } ret = avb_append_commandline(bflow, out_data->cmdline); if (ret < 0) goto free_out_data; return 0; free_out_data: if (out_data) avb_slot_verify_data_free(out_data); return log_msg_ret("avb cmdline", ret); } #else static int run_avb_verification(struct bootflow *bflow) { int ret; /* When AVB is unsupported, pass ORANGE state */ ret = bootflow_cmdline_set_arg(bflow, "androidboot.verifiedbootstate", "orange", false); if (ret < 0) return log_msg_ret("avb cmdline", ret); return 0; } #endif /* AVB_VERIFY */ static int boot_android_normal(struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); struct android_priv *priv = bflow->bootmeth_priv; int ret; ulong loadaddr = env_get_hex("loadaddr", 0); ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0); ret = run_avb_verification(bflow); if (ret < 0) return log_msg_ret("avb", ret); /* Read slot once more to decrement counter from BCB */ ret = android_read_slot_from_bcb(bflow, true); if (ret < 0) return log_msg_ret("read slot", ret); ret = read_slotted_partition(desc, "boot", priv->slot, loadaddr); if (ret < 0) return log_msg_ret("read boot", ret); ret = read_slotted_partition(desc, "vendor_boot", priv->slot, vloadaddr); if (ret < 0) return log_msg_ret("read vendor_boot", ret); set_abootimg_addr(loadaddr); set_avendor_bootimg_addr(vloadaddr); ret = bootm_boot_start(loadaddr, bflow->cmdline); return log_msg_ret("boot", ret); } static int boot_android_recovery(struct bootflow *bflow) { int ret; ret = boot_android_normal(bflow); return log_msg_ret("boot", ret); } static int boot_android_bootloader(struct bootflow *bflow) { int ret; ret = run_command("fastboot usb 0", 0); do_reset(NULL, 0, 0, NULL); return log_msg_ret("boot", ret); } static int android_boot(struct udevice *dev, struct bootflow *bflow) { struct android_priv *priv = bflow->bootmeth_priv; int ret; switch (priv->boot_mode) { case ANDROID_BOOT_MODE_NORMAL: ret = boot_android_normal(bflow); break; case ANDROID_BOOT_MODE_RECOVERY: ret = boot_android_recovery(bflow); break; case ANDROID_BOOT_MODE_BOOTLOADER: ret = boot_android_bootloader(bflow); break; default: printf("ANDROID: Unknown boot mode %d. Running fastboot...\n", priv->boot_mode); boot_android_bootloader(bflow); /* Tell we failed to boot since boot mode is unknown */ ret = -EFAULT; } return ret; } static int android_bootmeth_bind(struct udevice *dev) { struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); plat->desc = "Android boot"; plat->flags = BOOTMETHF_ANY_PART; return 0; } static struct bootmeth_ops android_bootmeth_ops = { .check = android_check, .read_bootflow = android_read_bootflow, .read_file = android_read_file, .boot = android_boot, }; static const struct udevice_id android_bootmeth_ids[] = { { .compatible = "u-boot,android" }, { } }; U_BOOT_DRIVER(bootmeth_android) = { .name = "bootmeth_android", .id = UCLASS_BOOTMETH, .of_match = android_bootmeth_ids, .ops = &android_bootmeth_ops, .bind = android_bootmeth_bind, };