summaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/Kconfig2
-rw-r--r--boot/bootm.c38
-rw-r--r--boot/bootmeth_android.c45
-rw-r--r--boot/bootretry.c28
-rw-r--r--boot/image-android.c30
-rw-r--r--boot/image-fdt.c8
-rw-r--r--boot/image-fit-sig.c108
-rw-r--r--boot/image-fit.c20
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,