/* * Copyright 2018 NXP */ #include #include #include #include #include #include #include "utils.h" #include "fsl_caam.h" #include "fsl_avbkey.h" #if defined(CONFIG_DUAL_BOOTLOADER) || !defined(CONFIG_SPL_BUILD) static const char* slot_suffixes[2] = {"_a", "_b"}; /* This is a copy of slot_set_unbootable() form * external/avb/libavb_ab/avb_ab_flow.c. */ void fsl_slot_set_unbootable(AvbABSlotData* slot) { slot->priority = 0; slot->tries_remaining = 0; slot->successful_boot = 0; } /* Ensure all unbootable and/or illegal states are marked as the * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0, * and successful_boot=0. This is a copy of slot_normalize from * external/avb/libavb_ab/avb_ab_flow.c. */ void fsl_slot_normalize(AvbABSlotData* slot) { if (slot->priority > 0) { #if defined(CONFIG_DUAL_BOOTLOADER) && !defined(CONFIG_SPL_BUILD) if ((slot->tries_remaining == 0) && (!slot->successful_boot) && (slot->bootloader_verified != 1)) { /* We've exhausted all tries -> unbootable. */ fsl_slot_set_unbootable(slot); } #else if ((slot->tries_remaining == 0) && (!slot->successful_boot)) { /* We've exhausted all tries -> unbootable. */ fsl_slot_set_unbootable(slot); } #endif if ((slot->tries_remaining > 0) && (slot->successful_boot)) { /* Illegal state - avb_ab_mark_slot_successful() will clear * tries_remaining when setting successful_boot. */ fsl_slot_set_unbootable(slot); } } else { fsl_slot_set_unbootable(slot); } } /* This is a copy of slot_is_bootable() from * externel/avb/libavb_ab/avb_ab_flow.c. */ bool fsl_slot_is_bootable(AvbABSlotData* slot) { return (slot->priority > 0) && (slot->successful_boot || (slot->tries_remaining > 0)); } #endif /* CONFIG_DUAL_BOOTLOADER || !CONFIG_SPL_BUILD */ #if defined(CONFIG_DUAL_BOOTLOADER) && defined(CONFIG_SPL_BUILD) #define FSL_AB_METADATA_MISC_PARTITION_OFFSET 2048 #define PARTITION_NAME_LEN 13 #define PARTITION_MISC "misc" #define PARTITION_BOOTLOADER "bootloader" extern int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value); extern int mmc_load_image_parse_container(struct spl_image_info *spl_image, struct mmc *mmc, unsigned long sector); /* Pre-declaration of h_spl_load_read(), see detail implementation in * common/spl/spl_mmc.c. */ ulong h_spl_load_read(struct spl_load_info *load, ulong sector, ulong count, void *buf); void fsl_avb_ab_data_update_crc_and_byteswap(const AvbABData* src, AvbABData* dest) { memcpy(dest, src, sizeof(AvbABData)); dest->crc32 = cpu_to_be32( avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))); } void fsl_avb_ab_data_init(AvbABData* data) { memset(data, '\0', sizeof(AvbABData)); memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN); data->version_major = AVB_AB_MAJOR_VERSION; data->version_minor = AVB_AB_MINOR_VERSION; data->slots[0].priority = AVB_AB_MAX_PRIORITY; data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; data->slots[0].successful_boot = 0; data->slots[0].bootloader_verified = 0; data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1; data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; data->slots[1].successful_boot = 0; data->slots[1].bootloader_verified = 0; } bool fsl_avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest) { /* Ensure magic is correct. */ if (memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) { printf("Magic is incorrect.\n"); return false; } memcpy(dest, src, sizeof(AvbABData)); dest->crc32 = be32_to_cpu(dest->crc32); /* Ensure we don't attempt to access any fields if the major version * is not supported. */ if (dest->version_major > AVB_AB_MAJOR_VERSION) { printf("No support for given major version.\n"); return false; } /* Fail if CRC32 doesn't match. */ if (dest->crc32 != avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))) { printf("CRC32 does not match.\n"); return false; } return true; } /* Writes A/B metadata to disk only if it has changed. */ int fsl_save_metadata_if_changed_dual_uboot(struct blk_desc *dev_desc, AvbABData* ab_data, AvbABData* ab_data_orig) { AvbABData serialized; size_t num_bytes; disk_partition_t info; /* Save metadata if changed. */ if (memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) { /* Get misc partition info */ if (part_get_info_by_name(dev_desc, PARTITION_MISC, &info) == -1) { printf("Can't get partition info of partition: misc\n"); return -1; } /* Writing A/B metadata to disk. */ fsl_avb_ab_data_update_crc_and_byteswap(ab_data, &serialized); if (write_to_partition_in_bytes(dev_desc, &info, FSL_AB_METADATA_MISC_PARTITION_OFFSET, sizeof(AvbABData), (void *)&serialized, &num_bytes) || (num_bytes != sizeof(AvbABData))) { printf("Error--write metadata fail!\n"); return -1; } } return 0; } /* Load metadate from misc partition. */ int fsl_load_metadata_dual_uboot(struct blk_desc *dev_desc, AvbABData* ab_data, AvbABData* ab_data_orig) { disk_partition_t info; AvbABData serialized; size_t num_bytes; if (part_get_info_by_name(dev_desc, PARTITION_MISC, &info) == -1) { printf("Can't get partition info of partition: misc\n"); return -1; } else { read_from_partition_in_bytes(dev_desc, &info, FSL_AB_METADATA_MISC_PARTITION_OFFSET, sizeof(AvbABData), (void *)ab_data, &num_bytes ); if (num_bytes != sizeof(AvbABData)) { printf("Error--read metadata fail!\n"); return -1; } else { if (!fsl_avb_ab_data_verify_and_byteswap(ab_data, &serialized)) { printf("Error validating A/B metadata from disk.\n"); printf("Resetting and writing new A/B metadata to disk.\n"); fsl_avb_ab_data_init(ab_data); fsl_avb_ab_data_update_crc_and_byteswap(ab_data, &serialized); num_bytes = 0; if (write_to_partition_in_bytes( dev_desc, &info, FSL_AB_METADATA_MISC_PARTITION_OFFSET, sizeof(AvbABData), (void *)&serialized, &num_bytes) || (num_bytes != sizeof(AvbABData))) { printf("Error--write metadata fail!\n"); return -1; } else return 0; } else { memcpy(ab_data_orig, ab_data, sizeof(AvbABData)); /* Ensure data is normalized, e.g. illegal states will be marked as * unbootable and all unbootable states are represented with * (priority=0, tries_remaining=0, successful_boot=0). */ fsl_slot_normalize(&ab_data->slots[0]); fsl_slot_normalize(&ab_data->slots[1]); return 0; } } } } #ifndef CONFIG_XEN static int spl_verify_rbidx(struct mmc *mmc, AvbABSlotData *slot, struct spl_image_info *spl_image) { kblb_hdr_t hdr; kblb_tag_t *rbk; uint64_t extract_idx; #ifdef CONFIG_AVB_ATX struct bl_rbindex_package *bl_rbindex; #endif /* Make sure rollback index has been initialized before verify */ if (rpmb_init()) { printf("RPMB init failed!\n"); return -1; } /* Read bootloader rollback index header first. */ if (rpmb_read(mmc, (uint8_t *)&hdr, sizeof(hdr), BOOTLOADER_RBIDX_OFFSET) != 0) { printf("Read RPMB error!\n"); return -1; } /* Read bootloader rollback index. */ rbk = &(hdr.bootloader_rbk_tags); if (rpmb_read(mmc, (uint8_t *)&extract_idx, rbk->len, rbk->offset) != 0) { printf("Read rollback index error!\n"); return -1; } /* Verify bootloader rollback index. */ if (spl_image->rbindex >= extract_idx) { /* Rollback index verify pass, update it only when current slot * has been marked as successful. */ if ((slot->successful_boot != 0) && (spl_image->rbindex != extract_idx) && rpmb_write(mmc, (uint8_t *)(&(spl_image->rbindex)), rbk->len, rbk->offset)) { printf("Update bootloader rollback index failed!\n"); return -1; } #ifdef CONFIG_AVB_ATX /* Pass bootloader rbindex to u-boot here. */ bl_rbindex = (struct bl_rbindex_package *)BL_RBINDEX_LOAD_ADDR; memcpy(bl_rbindex->magic, BL_RBINDEX_MAGIC, BL_RBINDEX_MAGIC_LEN); if (slot->successful_boot != 0) bl_rbindex->rbindex = spl_image->rbindex; else bl_rbindex->rbindex = extract_idx; #endif return 0; } else { printf("Rollback index verify rejected!\n"); return -1; } } #endif /* CONFIG_XEN */ #ifdef CONFIG_PARSE_CONTAINER int mmc_load_image_parse_container_dual_uboot( struct spl_image_info *spl_image, struct mmc *mmc) { disk_partition_t info; int ret = 0, n = 0; char partition_name[PARTITION_NAME_LEN]; struct blk_desc *dev_desc; AvbABData ab_data, ab_data_orig; size_t slot_index_to_boot, target_slot; #ifndef CONFIG_XEN struct keyslot_package kp; #endif /* Check if gpt is valid */ dev_desc = mmc_get_blk_desc(mmc); if (dev_desc) { if (part_get_info(dev_desc, 1, &info)) { printf("GPT is invalid, please flash correct GPT!\n"); return -1; } } else { printf("Get block desc fail!\n"); return -1; } #ifndef CONFIG_XEN /* Read RPMB keyslot package, xen won't check this. */ read_keyslot_package(&kp); if (strcmp(kp.magic, KEYPACK_MAGIC)) { if (rpmbkey_is_set()) { printf("\nFATAL - RPMB key was destroyed!\n"); hang(); } else printf("keyslot package magic error, do nothing here!\n"); } else { /* Set power-on write protection to boot1 partition. */ if (mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, BOOT1_PWR_WP)) { printf("Unable to set power-on write protection to boot1!\n"); return -1; } } #endif /* Load AB metadata from misc partition */ if (fsl_load_metadata_dual_uboot(dev_desc, &ab_data, &ab_data_orig)) { return -1; } slot_index_to_boot = 2; // Means not 0 or 1 target_slot = (ab_data.slots[1].priority > ab_data.slots[0].priority) ? 1 : 0; for (n = 0; n < 2; n++) { if (!fsl_slot_is_bootable(&ab_data.slots[target_slot])) { target_slot = (target_slot == 1 ? 0 : 1); continue; } /* Choose slot to load. */ snprintf(partition_name, PARTITION_NAME_LEN, PARTITION_BOOTLOADER"%s", slot_suffixes[target_slot]); /* Read part info from gpt */ if (part_get_info_by_name(dev_desc, partition_name, &info) == -1) { printf("Can't get partition info of partition bootloader%s\n", slot_suffixes[target_slot]); ret = -1; goto end; } else { ret = mmc_load_image_parse_container(spl_image, mmc, info.start); /* Don't need to check rollback index for xen. */ #ifndef CONFIG_XEN /* Image loaded successfully, go to verify rollback index */ if (!ret && rpmbkey_is_set()) ret = spl_verify_rbidx(mmc, &ab_data.slots[target_slot], spl_image); /* Copy rpmb keyslot to secure memory. */ if (!ret) fill_secure_keyslot_package(&kp); #endif } /* Set current slot to unbootable if load/verify fail. */ if (ret != 0) { printf("Load or verify bootloader%s fail, setting unbootable..\n", slot_suffixes[target_slot]); fsl_slot_set_unbootable(&ab_data.slots[target_slot]); /* Switch to another slot. */ target_slot = (target_slot == 1 ? 0 : 1); } else { slot_index_to_boot = target_slot; n = 2; } } if (slot_index_to_boot == 2) { /* No bootable slots! */ printf("No bootable slots found.\n"); ret = -1; goto end; } else if (!ab_data.slots[slot_index_to_boot].successful_boot && (ab_data.slots[slot_index_to_boot].tries_remaining > 0)) { /* Set the bootloader_verified flag if current slot only has one chance. */ if (ab_data.slots[slot_index_to_boot].tries_remaining == 1) ab_data.slots[slot_index_to_boot].bootloader_verified = 1; ab_data.slots[slot_index_to_boot].tries_remaining -= 1; } printf("Booting from bootloader%s...\n", slot_suffixes[slot_index_to_boot]); end: /* Save metadata if changed. */ if (fsl_save_metadata_if_changed_dual_uboot(dev_desc, &ab_data, &ab_data_orig)) { ret = -1; } if (ret) return -1; else return 0; } #else /* CONFIG_PARSE_CONTAINER */ int mmc_load_image_raw_sector_dual_uboot( struct spl_image_info *spl_image, struct mmc *mmc) { unsigned long count; disk_partition_t info; int ret = 0, n = 0; char partition_name[PARTITION_NAME_LEN]; struct blk_desc *dev_desc; struct image_header *header; AvbABData ab_data, ab_data_orig; size_t slot_index_to_boot, target_slot; struct keyslot_package kp; /* Check if gpt is valid */ dev_desc = mmc_get_blk_desc(mmc); if (dev_desc) { if (part_get_info(dev_desc, 1, &info)) { printf("GPT is invalid, please flash correct GPT!\n"); return -1; } } else { printf("Get block desc fail!\n"); return -1; } /* Init RPMB keyslot package if not initialized before. */ read_keyslot_package(&kp); if (strcmp(kp.magic, KEYPACK_MAGIC)) { printf("keyslot package magic error. Will generate new one\n"); if (gen_rpmb_key(&kp)) { printf("Generate keyslot package fail!\n"); return -1; } } /* Set power-on write protection to boot1 partition. */ if (mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, BOOT1_PWR_WP)) { printf("Unable to set power-on write protection to boot1!\n"); return -1; } /* Load AB metadata from misc partition */ if (fsl_load_metadata_dual_uboot(dev_desc, &ab_data, &ab_data_orig)) { return -1; } slot_index_to_boot = 2; // Means not 0 or 1 target_slot = (ab_data.slots[1].priority > ab_data.slots[0].priority) ? 1 : 0; for (n = 0; n < 2; n++) { if (!fsl_slot_is_bootable(&ab_data.slots[target_slot])) { target_slot = (target_slot == 1 ? 0 : 1); continue; } /* Choose slot to load. */ snprintf(partition_name, PARTITION_NAME_LEN, PARTITION_BOOTLOADER"%s", slot_suffixes[target_slot]); /* Read part info from gpt */ if (part_get_info_by_name(dev_desc, partition_name, &info) == -1) { printf("Can't get partition info of partition bootloader%s\n", slot_suffixes[target_slot]); ret = -1; goto end; } else { header = (struct image_header *)(CONFIG_SYS_TEXT_BASE - sizeof(struct image_header)); /* read image header to find the image size & load address */ count = blk_dread(dev_desc, info.start, 1, header); if (count == 0) { ret = -1; goto end; } /* Load fit and check HAB */ if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) { struct spl_load_info load; debug("Found FIT\n"); load.dev = mmc; load.priv = NULL; load.filename = NULL; load.bl_len = mmc->read_bl_len; load.read = h_spl_load_read; ret = spl_load_simple_fit(spl_image, &load, info.start, header); } else { ret = -1; } /* Fit image loaded successfully, go to verify rollback index */ if (!ret) ret = spl_verify_rbidx(mmc, &ab_data.slots[target_slot], spl_image); /* Copy rpmb keyslot to secure memory. */ if (!ret) fill_secure_keyslot_package(&kp); } /* Set current slot to unbootable if load/verify fail. */ if (ret != 0) { printf("Load or verify bootloader%s fail, setting unbootable..\n", slot_suffixes[target_slot]); fsl_slot_set_unbootable(&ab_data.slots[target_slot]); /* Switch to another slot. */ target_slot = (target_slot == 1 ? 0 : 1); } else { slot_index_to_boot = target_slot; n = 2; } } if (slot_index_to_boot == 2) { /* No bootable slots! */ printf("No bootable slots found.\n"); ret = -1; goto end; } else if (!ab_data.slots[slot_index_to_boot].successful_boot && (ab_data.slots[slot_index_to_boot].tries_remaining > 0)) { /* Set the bootloader_verified flag as if current slot only has one chance. */ if (ab_data.slots[slot_index_to_boot].tries_remaining == 1) ab_data.slots[slot_index_to_boot].bootloader_verified = 1; ab_data.slots[slot_index_to_boot].tries_remaining -= 1; } printf("Booting from bootloader%s...\n", slot_suffixes[slot_index_to_boot]); end: /* Save metadata if changed. */ if (fsl_save_metadata_if_changed_dual_uboot(dev_desc, &ab_data, &ab_data_orig)) { ret = -1; } if (ret) return -1; else return 0; } /* * spl_fit_get_rbindex(): Get rollback index of the bootloader. * @fit: Pointer to the FDT blob. * @images: Offset of the /images subnode. * * Return: the rollback index value of bootloader or a negative * error number. */ int spl_fit_get_rbindex(const void *fit, int images) { const char *str; uint64_t index; int conf_node; int len; conf_node = fit_find_config_node(fit); if (conf_node < 0) { return conf_node; } str = fdt_getprop(fit, conf_node, "rbindex", &len); if (!str) { debug("cannot find property 'rbindex'\n"); return -EINVAL; } index = simple_strtoul(str, NULL, 10); return index; } #endif /* CONFIG_PARSE_CONTAINER */ /* For normal build */ #elif !defined(CONFIG_SPL_BUILD) /* Writes A/B metadata to disk only if it has been changed. */ static AvbIOResult fsl_save_metadata_if_changed(AvbABOps* ab_ops, AvbABData* ab_data, AvbABData* ab_data_orig) { if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) { avb_debug("Writing A/B metadata to disk.\n"); return ab_ops->write_ab_metadata(ab_ops, ab_data); } return AVB_IO_RESULT_OK; } /* Helper function to load metadata - returns AVB_IO_RESULT_OK on * success, error code otherwise. This is a copy of load_metadata() * from /lib/avb/libavb_ab/avb_ab_flow.c. */ static AvbIOResult fsl_load_metadata(AvbABOps* ab_ops, AvbABData* ab_data, AvbABData* ab_data_orig) { AvbIOResult io_ret; io_ret = ab_ops->read_ab_metadata(ab_ops, ab_data); if (io_ret != AVB_IO_RESULT_OK) { avb_error("I/O error while loading A/B metadata.\n"); return io_ret; } *ab_data_orig = *ab_data; /* Ensure data is normalized, e.g. illegal states will be marked as * unbootable and all unbootable states are represented with * (priority=0, tries_remaining=0, successful_boot=0). */ fsl_slot_normalize(&ab_data->slots[0]); fsl_slot_normalize(&ab_data->slots[1]); return AVB_IO_RESULT_OK; } #ifdef CONFIG_DUAL_BOOTLOADER AvbABFlowResult avb_flow_dual_uboot(AvbABOps* ab_ops, const char* const* requested_partitions, AvbSlotVerifyFlags flags, AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data) { AvbOps* ops = ab_ops->ops; AvbSlotVerifyData* slot_data = NULL; AvbSlotVerifyData* data = NULL; AvbABFlowResult ret; AvbABData ab_data, ab_data_orig; AvbIOResult io_ret; bool saw_and_allowed_verification_error = false; AvbSlotVerifyResult verify_result; bool set_slot_unbootable = false; int target_slot, n; uint64_t rollback_index_value = 0; uint64_t current_rollback_index_value = 0; io_ret = fsl_load_metadata(ab_ops, &ab_data, &ab_data_orig); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; } /* Choose the target slot, it should be the same with the one in SPL. */ target_slot = get_curr_slot(&ab_data); if (target_slot == -1) { ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS; printf("No bootable slot found!\n"); goto out; } /* Clear the bootloader_verified flag. */ ab_data.slots[target_slot].bootloader_verified = 0; printf("Verifying slot %s ...\n", slot_suffixes[target_slot]); verify_result = avb_slot_verify(ops, requested_partitions, slot_suffixes[target_slot], flags, hashtree_error_mode, &slot_data); switch (verify_result) { case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; case AVB_SLOT_VERIFY_RESULT_ERROR_IO: ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; case AVB_SLOT_VERIFY_RESULT_OK: ret = AVB_AB_FLOW_RESULT_OK; break; case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR * these mean game over. */ set_slot_unbootable = true; break; case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) { /* Do nothing since we allow this. */ avb_debugv("Allowing slot ", slot_suffixes[target_slot], " which verified " "with result ", avb_slot_verify_result_to_string(verify_result), " because " "AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR " "is set.\n", NULL); saw_and_allowed_verification_error = true; } else { set_slot_unbootable = true; } break; case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT; goto out; /* Do not add a 'default:' case here because * of -Wswitch. */ } if (set_slot_unbootable) { avb_errorv("Error verifying slot ", slot_suffixes[target_slot], " with result ", avb_slot_verify_result_to_string(verify_result), " - setting unbootable.\n", NULL); fsl_slot_set_unbootable(&ab_data.slots[target_slot]); /* Only the slot chosen by SPL will be verified here so we * return AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS if the * slot should be set unbootable. */ ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS; goto out; } /* Update stored rollback index only when the slot has been marked * as successful. Do this for every rollback index location. */ if (ab_data.slots[target_slot].successful_boot != 0) { for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) { rollback_index_value = slot_data->rollback_indexes[n]; if (rollback_index_value != 0) { io_ret = ops->read_rollback_index( ops, n, ¤t_rollback_index_value); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { avb_error("Error getting rollback index for slot.\n"); ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; } if (current_rollback_index_value != rollback_index_value) { io_ret = ops->write_rollback_index( ops, n, rollback_index_value); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { avb_error("Error setting stored rollback index.\n"); ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; } } } } } /* Finally, select this slot. */ avb_assert(slot_data != NULL); data = slot_data; slot_data = NULL; if (saw_and_allowed_verification_error) { avb_assert( flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR; } else { ret = AVB_AB_FLOW_RESULT_OK; } out: io_ret = fsl_save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig); if (io_ret != AVB_IO_RESULT_OK) { if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; } else { ret = AVB_AB_FLOW_RESULT_ERROR_IO; } if (data != NULL) { avb_slot_verify_data_free(data); data = NULL; } } if (slot_data != NULL) avb_slot_verify_data_free(slot_data); if (out_data != NULL) { *out_data = data; } else { if (data != NULL) { avb_slot_verify_data_free(data); } } return ret; } #else /* CONFIG_DUAL_BOOTLOADER */ /* For legacy i.mx6/7, we won't enable A/B due to the limitation of * storage capacity, but we still want to verify boot/recovery with * AVB. */ AvbABFlowResult avb_single_flow(AvbABOps* ab_ops, const char* const* requested_partitions, AvbSlotVerifyFlags flags, AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data) { AvbOps* ops = ab_ops->ops; AvbSlotVerifyData* slot_data = NULL; AvbSlotVerifyData* data = NULL; AvbABFlowResult ret; bool saw_and_allowed_verification_error = false; /* Validate boot/recovery. */ AvbSlotVerifyResult verify_result; verify_result = avb_slot_verify(ops, requested_partitions, "", flags, hashtree_error_mode, &slot_data); switch (verify_result) { case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; case AVB_SLOT_VERIFY_RESULT_ERROR_IO: ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; case AVB_SLOT_VERIFY_RESULT_OK: break; case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR * these mean game over. */ ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS; goto out; /* explicit fallthrough. */ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) { /* Do nothing since we allow this. */ avb_debugv("Allowing slot ", slot_suffixes[n], " which verified " "with result ", avb_slot_verify_result_to_string(verify_result), " because " "AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR " "is set.\n", NULL); saw_and_allowed_verification_error = true; } else { ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS; goto out; } break; case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT; goto out; /* Do not add a 'default:' case here because of -Wswitch. */ } avb_assert(slot_data != NULL); data = slot_data; slot_data = NULL; if (saw_and_allowed_verification_error) { avb_assert(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR; } else { ret = AVB_AB_FLOW_RESULT_OK; } out: if (slot_data != NULL) { avb_slot_verify_data_free(slot_data); } if (out_data != NULL) { *out_data = data; } else { if (data != NULL) { avb_slot_verify_data_free(data); } } return ret; } AvbABFlowResult avb_ab_flow_fast(AvbABOps* ab_ops, const char* const* requested_partitions, AvbSlotVerifyFlags flags, AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data) { AvbOps* ops = ab_ops->ops; AvbSlotVerifyData* slot_data[2] = {NULL, NULL}; AvbSlotVerifyData* data = NULL; AvbABFlowResult ret; AvbABData ab_data, ab_data_orig; size_t slot_index_to_boot, n; AvbIOResult io_ret; bool saw_and_allowed_verification_error = false; size_t target_slot; AvbSlotVerifyResult verify_result; bool set_slot_unbootable = false; uint64_t rollback_index_value = 0; uint64_t current_rollback_index_value = 0; io_ret = fsl_load_metadata(ab_ops, &ab_data, &ab_data_orig); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; } slot_index_to_boot = 2; // Means not 0 or 1 target_slot = (ab_data.slots[1].priority > ab_data.slots[0].priority) ? 1 : 0; for (n = 0; n < 2; n++) { if (!fsl_slot_is_bootable(&ab_data.slots[target_slot])) { target_slot = (target_slot == 1 ? 0 : 1); continue; } verify_result = avb_slot_verify(ops, requested_partitions, slot_suffixes[target_slot], flags, hashtree_error_mode, &slot_data[target_slot]); switch (verify_result) { case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; case AVB_SLOT_VERIFY_RESULT_ERROR_IO: ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; case AVB_SLOT_VERIFY_RESULT_OK: slot_index_to_boot = target_slot; n = 2; break; case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR * these mean game over. */ set_slot_unbootable = true; break; /* explicit fallthrough. */ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) { /* Do nothing since we allow this. */ avb_debugv("Allowing slot ", slot_suffixes[target_slot], " which verified " "with result ", avb_slot_verify_result_to_string(verify_result), " because " "AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR " "is set.\n", NULL); saw_and_allowed_verification_error = true; slot_index_to_boot = target_slot; n = 2; } else { set_slot_unbootable = true; } break; case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT; goto out; /* Do not add a 'default:' case here because * of -Wswitch. */ } if (set_slot_unbootable) { avb_errorv("Error verifying slot ", slot_suffixes[target_slot], " with result ", avb_slot_verify_result_to_string(verify_result), " - setting unbootable.\n", NULL); fsl_slot_set_unbootable(&ab_data.slots[target_slot]); set_slot_unbootable = false; } /* switch to another slot */ target_slot = (target_slot == 1 ? 0 : 1); } if (slot_index_to_boot == 2) { /* No bootable slots! */ avb_error("No bootable slots found.\n"); ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS; goto out; } /* Update stored rollback index only when the slot has been marked * as successful. Do this for every rollback index location. */ if (ab_data.slots[slot_index_to_boot].successful_boot != 0) { for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) { rollback_index_value = slot_data[slot_index_to_boot]->rollback_indexes[n]; if (rollback_index_value != 0) { io_ret = ops->read_rollback_index( ops, n, ¤t_rollback_index_value); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { avb_error("Error getting rollback index for slot.\n"); ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; } if (current_rollback_index_value != rollback_index_value) { io_ret = ops->write_rollback_index( ops, n, rollback_index_value); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { avb_error("Error setting stored rollback index.\n"); ret = AVB_AB_FLOW_RESULT_ERROR_IO; goto out; } } } } } /* Finally, select this slot. */ avb_assert(slot_data[slot_index_to_boot] != NULL); data = slot_data[slot_index_to_boot]; slot_data[slot_index_to_boot] = NULL; if (saw_and_allowed_verification_error) { avb_assert( flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR; } else { ret = AVB_AB_FLOW_RESULT_OK; } /* ... and decrement tries remaining, if applicable. */ if (!ab_data.slots[slot_index_to_boot].successful_boot && (ab_data.slots[slot_index_to_boot].tries_remaining > 0)) { ab_data.slots[slot_index_to_boot].tries_remaining -= 1; } out: io_ret = fsl_save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig); if (io_ret != AVB_IO_RESULT_OK) { if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_AB_FLOW_RESULT_ERROR_OOM; } else { ret = AVB_AB_FLOW_RESULT_ERROR_IO; } if (data != NULL) { avb_slot_verify_data_free(data); data = NULL; } } for (n = 0; n < 2; n++) { if (slot_data[n] != NULL) { avb_slot_verify_data_free(slot_data[n]); } } if (out_data != NULL) { *out_data = data; } else { if (data != NULL) { avb_slot_verify_data_free(data); } } return ret; } #endif /* CONFIG_DUAL_BOOTLOADER */ #endif /* CONFIG_DUAL_BOOTLOADER && CONFIG_SPL_BUILD */