diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/Kconfig | 2 | ||||
| -rw-r--r-- | boot/bootm.c | 38 | ||||
| -rw-r--r-- | boot/bootmeth_android.c | 45 | ||||
| -rw-r--r-- | boot/bootretry.c | 28 | ||||
| -rw-r--r-- | boot/image-android.c | 30 | ||||
| -rw-r--r-- | boot/image-fdt.c | 8 | ||||
| -rw-r--r-- | boot/image-fit-sig.c | 108 | ||||
| -rw-r--r-- | boot/image-fit.c | 20 |
8 files changed, 195 insertions, 84 deletions
diff --git a/boot/Kconfig b/boot/Kconfig index 8e468c56176..c67dc0ba493 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -191,6 +191,8 @@ config FIT_BEST_MATCH U-Boot itself. A match is considered "best" if it matches the most specific compatibility entry of U-Boot's fdt's root node. The order of entries in the configuration's fdt is ignored. + If several configurations match equally well, the one named by + the configurations node 'default' property is preferred. config FIT_IMAGE_POST_PROCESS bool "Enable post-processing of FIT artifacts after loading by U-Boot" diff --git a/boot/bootm.c b/boot/bootm.c index 803d6406be4..3bce8586834 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -576,6 +576,18 @@ static int bootm_find_other(ulong img_addr, const char *conf_ramdisk, #if !defined(USE_HOSTCC) || defined(CONFIG_FIT_SIGNATURE) /** + * enum bootm_decomp_limit - What bounded the decompression buffer. + * @BOOTM_DECOMP_LIMIT_GLOBAL: Global CONFIG_SYS_BOOTM_LEN limit. + * @BOOTM_DECOMP_LIMIT_PER_IMAGE: Per-image buffer sized from the + * compressed image (e.g. the + * kernel_noload decompression buffer). + */ +enum bootm_decomp_limit { + BOOTM_DECOMP_LIMIT_GLOBAL, + BOOTM_DECOMP_LIMIT_PER_IMAGE, +}; + +/** * handle_decomp_error() - display a decompression error * * This function tries to produce a useful message. In the case where the @@ -585,11 +597,14 @@ static int bootm_find_other(ulong img_addr, const char *conf_ramdisk, * @comp_type: Compression type being used (IH_COMP_...) * @uncomp_size: Number of bytes uncompressed * @buf_size: Number of bytes the decompresion buffer was + * @limit: Which allocation actually bounded the buffer, so the + * hint points at the knob the reader can act on * @ret: errno error code received from compression library * Return: Appropriate BOOTM_ERR_ error code */ static int handle_decomp_error(int comp_type, size_t uncomp_size, - size_t buf_size, int ret) + size_t buf_size, + enum bootm_decomp_limit limit, int ret) { const char *name = genimg_get_comp_name(comp_type); @@ -598,10 +613,15 @@ static int handle_decomp_error(int comp_type, size_t uncomp_size, return BOOTM_ERR_UNIMPLEMENTED; if ((comp_type == IH_COMP_GZIP && ret == Z_BUF_ERROR) || - uncomp_size >= buf_size) - printf("Image too large: increase CONFIG_SYS_BOOTM_LEN\n"); - else + uncomp_size >= buf_size) { + if (limit == BOOTM_DECOMP_LIMIT_PER_IMAGE) + printf("Image too large for the per-image decompression buffer (%#zx bytes)\n", + buf_size); + else + printf("Image too large: increase CONFIG_SYS_BOOTM_LEN\n"); + } else { printf("%s: uncompress error %d\n", name, ret); + } /* * The decompression routines are now safe, so will not write beyond @@ -628,6 +648,7 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) ulong image_start = os.image_start; ulong image_len = os.image_len; ulong decomp_len = CONFIG_SYS_BOOTM_LEN; + enum bootm_decomp_limit decomp_limit = BOOTM_DECOMP_LIMIT_GLOBAL; ulong flush_start; bool no_overlap; void *load_buf, *image_buf; @@ -644,6 +665,7 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) phys_addr_t addr; decomp_len = ALIGN(image_len * 8, SZ_1M); + decomp_limit = BOOTM_DECOMP_LIMIT_PER_IMAGE; err = lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, &addr, decomp_len, LMB_NONE); if (err) @@ -663,10 +685,7 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) decomp_len, &load_end); if (err) { err = handle_decomp_error(os.comp, load_end - load, - decomp_len, err); - if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) - printf("Note: noload decompression buffer is %#lx bytes (not CONFIG_SYS_BOOTM_LEN)\n", - decomp_len); + decomp_len, decomp_limit, err); bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); return err; } @@ -1288,7 +1307,8 @@ static int bootm_host_load_image(const void *fit, int req_image_type, free(load_buf); if (ret) { - ret = handle_decomp_error(image_comp, load_end - 0, buf_size, ret); + ret = handle_decomp_error(image_comp, load_end - 0, buf_size, + BOOTM_DECOMP_LIMIT_GLOBAL, ret); if (ret != BOOTM_ERR_UNIMPLEMENTED) return ret; } diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c index 1d70e8d5c05..0db08d4f861 100644 --- a/boot/bootmeth_android.c +++ b/boot/bootmeth_android.c @@ -384,6 +384,14 @@ static int read_slotted_partition(struct blk_desc *desc, const char *const name, if (ret < 0) return log_msg_ret("part", ret); + /* + * The image size comes from the (untrusted) boot image header, so bound + * the read by the partition size: a valid image cannot be larger than + * the partition holding it. + */ + if (num_blks > partition.size) + return log_msg_ret("image larger than partition", -EFBIG); + n = blk_dread(desc, partition.start, num_blks, map_sysmem(addr, 0)); if (n < num_blks) return log_msg_ret("part read", -EIO); @@ -428,7 +436,7 @@ static int run_avb_verification(struct bootflow *bflow) const char * const requested_partitions[] = {"boot", "vendor_boot", NULL}; struct AvbOps *avb_ops; AvbSlotVerifyResult result; - AvbSlotVerifyData *out_data; + AvbSlotVerifyData *out_data = NULL; enum avb_boot_state boot_state; char *extra_args; char slot_suffix[3] = ""; @@ -443,8 +451,10 @@ static int run_avb_verification(struct bootflow *bflow) 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); + if (ret != AVB_IO_RESULT_OK) { + ret = log_msg_ret("avb lock", -EIO); + goto out; + } result = avb_slot_verify(avb_ops, requested_partitions, @@ -458,9 +468,8 @@ static int run_avb_verification(struct bootflow *bflow) if (result != AVB_SLOT_VERIFY_RESULT_OK) { printf("Verification failed, reason: %s\n", str_avb_slot_error(result)); - if (out_data) - avb_slot_verify_data_free(out_data); - return log_msg_ret("avb verify", -EIO); + ret = log_msg_ret("avb verify", -EIO); + goto out; } boot_state = AVB_GREEN; } else { @@ -469,9 +478,8 @@ static int run_avb_verification(struct bootflow *bflow) result != AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) { printf("Unlocked verification failed, reason: %s\n", str_avb_slot_error(result)); - if (out_data) - avb_slot_verify_data_free(out_data); - return log_msg_ret("avb verify unlocked", -EIO); + ret = log_msg_ret("avb verify unlocked", -EIO); + goto out; } boot_state = AVB_ORANGE; } @@ -480,23 +488,28 @@ static int run_avb_verification(struct bootflow *bflow) 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; + if (ret < 0) { + ret = log_msg_ret("avb cmdline", ret); + goto out; + } } if (result == AVB_SLOT_VERIFY_RESULT_OK) { ret = avb_append_commandline(bflow, out_data->cmdline); - if (ret < 0) - goto free_out_data; + if (ret < 0) { + ret = log_msg_ret("avb cmdline", ret); + goto out; + } } - return 0; + ret = 0; - free_out_data: + out: if (out_data) avb_slot_verify_data_free(out_data); + avb_ops_free(avb_ops); - return log_msg_ret("avb cmdline", ret); + return ret; } #else static int run_avb_verification(struct bootflow *bflow) diff --git a/boot/bootretry.c b/boot/bootretry.c index a60767eaa2e..52a253edd3e 100644 --- a/boot/bootretry.c +++ b/boot/bootretry.c @@ -8,6 +8,7 @@ #include <bootretry.h> #include <cli.h> #include <env.h> +#include <env_callback.h> #include <errno.h> #include <time.h> #include <vsprintf.h> @@ -19,10 +20,9 @@ static int retry_time = -1; /* -1 so can call readline before main_loop */ /*************************************************************************** * initialize command line timeout */ -void bootretry_init_cmd_timeout(void) -{ - char *s = env_get("bootretry"); +static void bootretry_parse(const char *s) +{ if (s != NULL) retry_time = (int)simple_strtol(s, NULL, 10); else @@ -32,13 +32,31 @@ void bootretry_init_cmd_timeout(void) retry_time = CONFIG_BOOT_RETRY_MIN; } +void bootretry_init_cmd_timeout(void) +{ + bootretry_parse(env_get("bootretry")); +} + +/* Parse changes to bootretry */ +static int on_bootretry(const char *name, const char *value, enum env_op op, + int flags) +{ + switch (op) { + case env_op_create: + case env_op_overwrite: + case env_op_delete: + bootretry_parse(value); + break; + } + return 0; +} +U_BOOT_ENV_CALLBACK(bootretry, on_bootretry); + /*************************************************************************** * reset command line timeout to retry_time seconds */ void bootretry_reset_cmd_timeout(void) { - /* Parse changes to bootretry */ - bootretry_init_cmd_timeout(); endtime = endtick(retry_time); } diff --git a/boot/image-android.c b/boot/image-android.c index fb26290d40c..7740cae8cb6 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -131,7 +131,8 @@ static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3 } static void android_vendor_boot_image_v3_v4_parse_hdr(const struct andr_vnd_boot_img_hdr - *hdr, struct andr_image_data *data) + *hdr, struct andr_image_data *data, + bool write_trailer) { ulong end; @@ -167,12 +168,23 @@ static void android_vendor_boot_image_v3_v4_parse_hdr(const struct andr_vnd_boot end += ALIGN(hdr->vendor_ramdisk_table_size, hdr->page_size); data->bootconfig_addr = end; if (hdr->bootconfig_size) { - void *bootconfig_ptr = map_sysmem(data->bootconfig_addr, - data->bootconfig_size + - BOOTCONFIG_TRAILER_SIZE); - data->bootconfig_size += add_trailer((ulong)bootconfig_ptr, - data->bootconfig_size); - unmap_sysmem(bootconfig_ptr); + if (write_trailer) { + void *bootconfig_ptr = map_sysmem(data->bootconfig_addr, + data->bootconfig_size + + BOOTCONFIG_TRAILER_SIZE); + data->bootconfig_size += add_trailer((ulong)bootconfig_ptr, + data->bootconfig_size); + unmap_sysmem(bootconfig_ptr); + } else { + /* + * Only the header has been loaded here (this is a + * size-only query), so the bootconfig region is not + * present in the buffer. Account for the trailer that + * will be appended at load time without writing it, to + * avoid corrupting memory past the header buffer. + */ + data->bootconfig_size += BOOTCONFIG_TRAILER_SIZE; + } data->ramdisk_size += data->bootconfig_size; } end += ALIGN(data->bootconfig_size, hdr->page_size); @@ -265,7 +277,7 @@ bool android_image_get_vendor_bootimg_size(const void *hdr, u32 *vendor_boot_img return false; } - android_vendor_boot_image_v3_v4_parse_hdr(hdr, &data); + android_vendor_boot_image_v3_v4_parse_hdr(hdr, &data, false); *vendor_boot_img_size = data.vendor_boot_img_total_size; @@ -304,7 +316,7 @@ bool android_image_get_data(const void *boot_hdr, const void *vendor_boot_hdr, return false; } android_boot_image_v3_v4_parse_hdr((const struct andr_boot_img_hdr_v3 *)bhdr, data); - android_vendor_boot_image_v3_v4_parse_hdr(vhdr, data); + android_vendor_boot_image_v3_v4_parse_hdr(vhdr, data, true); unmap_sysmem(vhdr); } else { android_boot_image_v0_v1_v2_parse_hdr(bhdr, data); diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 9e0e0f93edd..956a3d97c42 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -91,10 +91,10 @@ static void boot_fdt_handle_region(u64 addr, u64 size, u32 flags, bool free) ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &rsv_addr, size, flags); - if (!ret) { - debug(" %s fdt memory region: addr=%llx size=%llx flags=%x\n", - free ? "freed" : "reserved", (unsigned long long)addr, - (unsigned long long)size, flags); + if (!ret || ret == -EFAULT) { + debug(" %s fdt memory region%s: addr=%llx size=%llx flags=%x ret=%ld\n", + free ? "free" : "reserve", ret ? " failed" : "", + (unsigned long long)addr, (unsigned long long)size, flags, ret); } else { printf("ERROR: %s fdt memory region failed (addr=%llx size=%llx flags=%x): %ld\n", free ? "freeing" : "reserving", (unsigned long long)addr, diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index fe7ca6e4ab5..a0c50bba4cf 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -231,13 +231,10 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, } /** - * fit_config_add_hash() - Add hash nodes for one image to the node list - * - * Adds the image path, all its hash-* subnode paths, and its cipher - * subnode path (if present) to the packed buffer. + * fit_config_add_node() - Append one node's path to the hashed-node list * * @fit: FIT blob - * @image_noffset: Image node offset (e.g. /images/kernel-1) + * @noffset: Offset of the node whose path should be added * @node_inc: Array of path pointers to fill * @count: Pointer to current count (updated on return) * @max_nodes: Maximum entries in @node_inc @@ -246,23 +243,51 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, * @buf_len: Total size of @buf * Return: 0 on success, -ve on error */ -static int fit_config_add_hash(const void *fit, int image_noffset, - char **node_inc, int *count, int max_nodes, - char *buf, int *buf_used, int buf_len) +static int fit_config_add_node(const void *fit, int noffset, char **node_inc, + int *count, int max_nodes, char *buf, + int *buf_used, int buf_len) { - int noffset, hash_count, ret, len; + int ret, len; if (*count >= max_nodes) return -ENOSPC; - - ret = fdt_get_path(fit, image_noffset, buf + *buf_used, - buf_len - *buf_used); + ret = fdt_get_path(fit, noffset, buf + *buf_used, buf_len - *buf_used); if (ret < 0) return -ENOENT; len = strlen(buf + *buf_used) + 1; node_inc[(*count)++] = buf + *buf_used; *buf_used += len; + return 0; +} + +/** + * fit_config_add_hash() - Add hash nodes for one image to the node list + * + * Adds the image path, all its hash-* subnode paths, and its cipher and + * dm-verity subnode paths (each if present) to the packed buffer. + * + * @fit: FIT blob + * @image_noffset: Image node offset (e.g. /images/kernel-1) + * @node_inc: Array of path pointers to fill + * @count: Pointer to current count (updated on return) + * @max_nodes: Maximum entries in @node_inc + * @buf: Buffer for packed path strings + * @buf_used: Pointer to bytes used in @buf (updated on return) + * @buf_len: Total size of @buf + * Return: 0 on success, -ve on error + */ +static int fit_config_add_hash(const void *fit, int image_noffset, + char **node_inc, int *count, int max_nodes, + char *buf, int *buf_used, int buf_len) +{ + int noffset, hash_count, ret; + + ret = fit_config_add_node(fit, image_noffset, node_inc, count, + max_nodes, buf, buf_used, buf_len); + if (ret) + return ret; + /* Add all this image's hash subnodes */ hash_count = 0; for (noffset = fdt_first_subnode(fit, image_noffset); @@ -273,15 +298,10 @@ static int fit_config_add_hash(const void *fit, int image_noffset, if (strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) continue; - if (*count >= max_nodes) - return -ENOSPC; - ret = fdt_get_path(fit, noffset, buf + *buf_used, - buf_len - *buf_used); - if (ret < 0) - return -ENOENT; - len = strlen(buf + *buf_used) + 1; - node_inc[(*count)++] = buf + *buf_used; - *buf_used += len; + ret = fit_config_add_node(fit, noffset, node_inc, count, + max_nodes, buf, buf_used, buf_len); + if (ret) + return ret; hash_count++; } @@ -296,26 +316,36 @@ static int fit_config_add_hash(const void *fit, int image_noffset, if (noffset != -FDT_ERR_NOTFOUND) { if (noffset < 0) return -EIO; - if (*count >= max_nodes) - return -ENOSPC; - ret = fdt_get_path(fit, noffset, buf + *buf_used, - buf_len - *buf_used); - if (ret < 0) - return -ENOENT; - len = strlen(buf + *buf_used) + 1; - node_inc[(*count)++] = buf + *buf_used; - *buf_used += len; + ret = fit_config_add_node(fit, noffset, node_inc, count, + max_nodes, buf, buf_used, buf_len); + if (ret) + return ret; + } + + /* + * Add this image's dm-verity node if present. Its roothash is the + * only integrity anchor for a dm-verity filesystem image, so it must + * be covered by the configuration signature. + */ + noffset = fdt_subnode_offset(fit, image_noffset, FIT_VERITY_NODENAME); + if (noffset != -FDT_ERR_NOTFOUND) { + if (noffset < 0) + return -EIO; + ret = fit_config_add_node(fit, noffset, node_inc, count, + max_nodes, buf, buf_used, buf_len); + if (ret) + return ret; } return 0; } /** - * fit_config_get_hash_list() - Build the list of nodes to hash + * fit_config_get_signed_nodes() - Build the list of nodes to hash * * Works through every image referenced by the configuration and collects the - * node paths: root + config + all referenced images with their hash and - * cipher subnodes. + * node paths: root + config + all referenced images with their hash, + * cipher and dm-verity subnodes. * * Properties known not to be image references (description, compatible, * default, load-only) are skipped, so any new image type is covered by default. @@ -328,9 +358,9 @@ static int fit_config_add_hash(const void *fit, int image_noffset, * @buf_len: Size of @buf * Return: number of entries in @node_inc, or -ve on error */ -static int fit_config_get_hash_list(const void *fit, int conf_noffset, - char **node_inc, int max_nodes, - char *buf, int buf_len) +int fit_config_get_signed_nodes(const void *fit, int conf_noffset, + char **node_inc, int max_nodes, + char *buf, int buf_len) { const char *conf_name; int image_count; @@ -470,9 +500,9 @@ static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset, } /* Build the node list from the config, ignoring hashed-nodes */ - count = fit_config_get_hash_list(fit, conf_noffset, - node_inc, IMAGE_MAX_HASHED_NODES, - hash_buf, sizeof(hash_buf)); + count = fit_config_get_signed_nodes(fit, conf_noffset, + node_inc, IMAGE_MAX_HASHED_NODES, + hash_buf, sizeof(hash_buf)); if (count < 0) { *err_msgp = "Failed to build hash node list"; return -1; diff --git a/boot/image-fit.c b/boot/image-fit.c index 044a40e1910..ef90c5abd18 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -1799,6 +1799,8 @@ int fit_conf_find_compat(const void *fit, const void *fdt) int fdt_compat_len; int best_match_offset = 0; int best_match_pos = 0; + const char *default_name; + int default_noffset = -1; confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); @@ -1813,6 +1815,15 @@ int fit_conf_find_compat(const void *fit, const void *fdt) return -ENXIO; } + /* the default configuration breaks ties between equal matches */ + default_name = fdt_getprop(fit, confs_noffset, FIT_DEFAULT_PROP, NULL); + if (default_name) { + default_noffset = fdt_subnode_offset(fit, confs_noffset, + default_name); + if (default_noffset < 0) + default_noffset = -1; + } + /* * Loop over the configurations in the FIT image. */ @@ -1863,10 +1874,15 @@ int fit_conf_find_compat(const void *fit, const void *fdt) cur_fdt_compat = fdt_compat; /* * Look for a match for each U-Boot compatibility string in - * turn in the compat string property. + * turn in the compat string property. A configuration only + * replaces the current best match on a strictly better + * position, or on an equal position if it is the default + * configuration. */ for (i = 0; len > 0 && - (!best_match_offset || best_match_pos > i); i++) { + (!best_match_offset || best_match_pos > i || + (best_match_pos == i && noffset == default_noffset)); + i++) { int cur_len = strlen(cur_fdt_compat) + 1; if (!fdt_node_check_compatible(fdt, compat_noffset, |
