summaryrefslogtreecommitdiff
path: root/boot/image-fit.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/image-fit.c')
-rw-r--r--boot/image-fit.c538
1 files changed, 444 insertions, 94 deletions
diff --git a/boot/image-fit.c b/boot/image-fit.c
index b0fcaf6e17f..044a40e1910 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -21,8 +21,11 @@
extern void *aligned_alloc(size_t alignment, size_t size);
#else
#include <linux/compiler.h>
+#include <linux/log2.h>
#include <linux/sizes.h>
+#include <env.h>
#include <errno.h>
+#include <hexdump.h>
#include <log.h>
#include <mapmem.h>
#include <asm/io.h>
@@ -41,6 +44,7 @@ DECLARE_GLOBAL_DATA_PTR;
#include <bootm.h>
#include <image.h>
#include <bootstage.h>
+#include <fdt_region.h>
#include <upl.h>
#include <u-boot/crc.h>
@@ -156,18 +160,10 @@ static void fit_get_debug(const void *fit, int noffset,
int fit_get_subimage_count(const void *fit, int images_noffset)
{
int noffset;
- int ndepth;
int count = 0;
- /* Process its subnodes, print out component images details */
- for (ndepth = 0, count = 0,
- noffset = fdt_next_node(fit, images_noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
- if (ndepth == 1) {
- count++;
- }
- }
+ fdt_for_each_subnode(noffset, fit, images_noffset)
+ count++;
return count;
}
@@ -243,6 +239,39 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p,
}
}
+static __maybe_unused void fit_image_print_dm_verity(const void *fit,
+ int noffset,
+ const char *p)
+{
+#if defined(USE_HOSTCC) || CONFIG_IS_ENABLED(FIT_VERITY)
+ const char *algo;
+ const uint8_t *bin;
+ int len, i;
+
+ algo = fdt_getprop(fit, noffset, FIT_VERITY_ALGO_PROP, NULL);
+ if (algo)
+ printf("%s Verity algo: %s\n", p, algo);
+
+ bin = fdt_getprop(fit, noffset, FIT_VERITY_DIGEST_PROP,
+ &len);
+ if (bin && len > 0) {
+ printf("%s Verity hash: ", p);
+ for (i = 0; i < len; i++)
+ printf("%02x", bin[i]);
+ printf("\n");
+ }
+
+ bin = fdt_getprop(fit, noffset, FIT_VERITY_SALT_PROP,
+ &len);
+ if (bin && len > 0) {
+ printf("%s Verity salt: ", p);
+ for (i = 0; i < len; i++)
+ printf("%02x", bin[i]);
+ printf("\n");
+ }
+#endif
+}
+
/**
* fit_image_print_verification_data() - prints out the hash/signature details
* @fit: pointer to the FIT format image header
@@ -271,6 +300,11 @@ static void fit_image_print_verification_data(const void *fit, int noffset,
strlen(FIT_SIG_NODENAME))) {
fit_image_print_data(fit, noffset, p, "Sign");
}
+#if defined(USE_HOSTCC) || CONFIG_IS_ENABLED(FIT_VERITY)
+ else if (!strcmp(name, FIT_VERITY_NODENAME)) {
+ fit_image_print_dm_verity(fit, noffset, p);
+ }
+#endif
}
/**
@@ -291,7 +325,7 @@ static void fit_conf_print(const void *fit, int noffset, const char *p)
const char *uname;
int ret;
int fdt_index, loadables_index;
- int ndepth;
+ int sub_noffset;
/* Mandatory properties */
ret = fit_get_desc(fit, noffset, &desc);
@@ -357,14 +391,8 @@ static void fit_conf_print(const void *fit, int noffset, const char *p)
}
/* Process all hash subnodes of the component configuration node */
- for (ndepth = 0, noffset = fdt_next_node(fit, noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
- if (ndepth == 1) {
- /* Direct child node of the component configuration node */
- fit_image_print_verification_data(fit, noffset, p);
- }
- }
+ fdt_for_each_subnode(sub_noffset, fit, noffset)
+ fit_image_print_verification_data(fit, sub_noffset, p);
}
/**
@@ -386,8 +414,7 @@ void fit_print_contents(const void *fit)
int images_noffset;
int confs_noffset;
int noffset;
- int ndepth;
- int count = 0;
+ int count;
int ret;
const char *p;
time_t timestamp;
@@ -424,20 +451,12 @@ void fit_print_contents(const void *fit)
}
/* Process its subnodes, print out component images details */
- for (ndepth = 0, count = 0,
- noffset = fdt_next_node(fit, images_noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
- if (ndepth == 1) {
- /*
- * Direct child node of the images parent node,
- * i.e. component image node.
- */
- printf("%s Image %u (%s)\n", p, count++,
- fit_get_name(fit, noffset, NULL));
+ count = 0;
+ fdt_for_each_subnode(noffset, fit, images_noffset) {
+ printf("%s Image %u (%s)\n", p, count++,
+ fit_get_name(fit, noffset, NULL));
- fit_image_print(fit, noffset, p);
- }
+ fit_image_print(fit, noffset, p);
}
/* Find configurations parent node offset */
@@ -449,25 +468,17 @@ void fit_print_contents(const void *fit)
}
/* get default configuration unit name from default property */
- uname = (char *)fdt_getprop(fit, noffset, FIT_DEFAULT_PROP, NULL);
+ uname = (char *)fdt_getprop(fit, confs_noffset, FIT_DEFAULT_PROP, NULL);
if (uname)
printf("%s Default Configuration: '%s'\n", p, uname);
/* Process its subnodes, print out configurations details */
- for (ndepth = 0, count = 0,
- noffset = fdt_next_node(fit, confs_noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
- if (ndepth == 1) {
- /*
- * Direct child node of the configurations parent node,
- * i.e. configuration node.
- */
- printf("%s Configuration %u (%s)\n", p, count++,
- fit_get_name(fit, noffset, NULL));
+ count = 0;
+ fdt_for_each_subnode(noffset, fit, confs_noffset) {
+ printf("%s Configuration %u (%s)\n", p, count++,
+ fit_get_name(fit, noffset, NULL));
- fit_conf_print(fit, noffset, p);
- }
+ fit_conf_print(fit, noffset, p);
}
}
@@ -494,7 +505,6 @@ void fit_image_print(const void *fit, int image_noffset, const char *p)
ulong load, entry;
const void *data;
int noffset;
- int ndepth;
int ret;
if (!CONFIG_IS_ENABLED(FIT_PRINT))
@@ -584,14 +594,8 @@ void fit_image_print(const void *fit, int image_noffset, const char *p)
}
/* Process all hash subnodes of the component image node */
- for (ndepth = 0, noffset = fdt_next_node(fit, image_noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
- if (ndepth == 1) {
- /* Direct child node of the component image node */
- fit_image_print_verification_data(fit, noffset, p);
- }
- }
+ fdt_for_each_subnode(noffset, fit, image_noffset)
+ fit_image_print_verification_data(fit, noffset, p);
}
/**
@@ -1069,23 +1073,72 @@ int fit_image_get_data(const void *fit, int noffset, const void **data,
int offset;
int len;
int ret;
+ size_t fdt_total_size_aligned;
+ uintptr_t max_offset;
if (!fit_image_get_data_position(fit, noffset, &offset)) {
+ if (offset < 0) {
+ printf("Invalid external data position: %d\n", offset);
+ return -EINVAL;
+ }
+
external_data = true;
} else if (!fit_image_get_data_offset(fit, noffset, &offset)) {
- external_data = true;
/*
* For FIT with external data, figure out where
* the external images start. This is the base
* for the data-offset properties in each image.
*/
- offset += ((fdt_totalsize(fit) + 3) & ~3);
+ fdt_total_size_aligned = ((fdt_totalsize(fit) + 3) & ~3);
+ /* The resulting offset cannot exceed INT_MAX */
+ if (offset < 0 || fdt_total_size_aligned > INT_MAX - offset) {
+ printf("Invalid external data offset: %d\n", offset);
+ return -EINVAL;
+ }
+ offset += fdt_total_size_aligned;
+
+ external_data = true;
}
if (external_data) {
debug("External Data\n");
+
+ max_offset = UINTPTR_MAX - (uintptr_t)fit;
+ /* Check that external data offset is within the addressable range */
+ if (offset > max_offset) {
+ printf("Invalid external data offset: %d\n", offset);
+ return -EINVAL;
+ }
+
ret = fit_image_get_data_size(fit, noffset, &len);
if (!ret) {
+ if (len < 0) {
+ printf("Invalid external data size: %d\n", len);
+ return -EINVAL;
+ }
+ /*
+ * For non-signed FIT images, we can only check that
+ * (offset + len) doesn't exceed the addressable range.
+ * For signed FITs, we can additionally check that
+ * (offset + len) doesn't exceed the allowed FIT image
+ * maximum size.
+ */
+ if (len > max_offset - offset
+ /*
+ * #if (not a runtime if) is required: FIT_SIGNATURE_MAX_SIZE
+ * depends on FIT_SIGNATURE, so CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE)
+ * is undefined when signing is disabled and referencing it
+ * here would fail to compile.
+ */
+#if CONFIG_IS_ENABLED(FIT_SIGNATURE)
+ || offset > CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE) ||
+ len > CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE) - offset
+#endif
+ ) {
+ printf("FIT external data is out of bounds (offset=%d, size=%d)\n",
+ offset, len);
+ return -EINVAL;
+ }
*data = fit + offset;
*size = len;
}
@@ -1439,7 +1492,7 @@ int fit_image_verify(const void *fit, int image_noffset)
size_t size;
char *err_msg = "";
- if (IS_ENABLED(CONFIG_FIT_SIGNATURE) && strchr(name, '@')) {
+ if (CONFIG_IS_ENABLED(FIT_SIGNATURE) && strchr(name, '@')) {
/*
* We don't support this since libfdt considers names with the
* name root but different @ suffix to be equal
@@ -1477,7 +1530,6 @@ int fit_all_image_verify(const void *fit)
{
int images_noffset;
int noffset;
- int ndepth;
int count;
/* Find images parent node offset */
@@ -1491,23 +1543,15 @@ int fit_all_image_verify(const void *fit)
/* Process all image subnodes, check hashes for each */
printf("## Checking hash(es) for FIT Image at %08lx ...\n",
(ulong)fit);
- for (ndepth = 0, count = 0,
- noffset = fdt_next_node(fit, images_noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
- if (ndepth == 1) {
- /*
- * Direct child node of the images parent node,
- * i.e. component image node.
- */
- printf(" Hash(es) for Image %u (%s): ", count,
- fit_get_name(fit, noffset, NULL));
- count++;
+ count = 0;
+ fdt_for_each_subnode(noffset, fit, images_noffset) {
+ printf(" Hash(es) for Image %u (%s): ", count,
+ fit_get_name(fit, noffset, NULL));
+ count++;
- if (!fit_image_verify(fit, noffset))
- return 0;
- printf("\n");
- }
+ if (!fit_image_verify(fit, noffset))
+ return 0;
+ printf("\n");
}
return 1;
}
@@ -1643,20 +1687,24 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp)
*
* @fit: FIT to check
* @parent: Parent node to check
- * Return: 0 if OK, -EADDRNOTAVAIL is a node has a name containing '@'
+ * @depth: Current recursion depth
+ * Return: 0 if OK, or error value
*/
-static int fdt_check_no_at(const void *fit, int parent)
+static int fdt_check_no_at(const void *fit, int parent, int depth)
{
const char *name;
int node;
int ret;
+ if (depth >= FDT_MAX_DEPTH)
+ return -FDT_ERR_BADSTRUCTURE;
+
name = fdt_get_name(fit, parent, NULL);
if (!name || strchr(name, '@'))
return -EADDRNOTAVAIL;
fdt_for_each_subnode(node, fit, parent) {
- ret = fdt_check_no_at(fit, node);
+ ret = fdt_check_no_at(fit, node, depth + 1);
if (ret)
return ret;
}
@@ -1664,6 +1712,16 @@ static int fdt_check_no_at(const void *fit, int parent)
return 0;
}
+static int fit_check_images_node(const void *fit)
+{
+ if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) {
+ log_debug("Wrong FIT format: no images parent node\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
int fit_check_format(const void *fit, ulong size)
{
int ret;
@@ -1676,6 +1734,13 @@ int fit_check_format(const void *fit, ulong size)
return -ENOEXEC;
}
+ /*
+ * For the control DTB to act as a FIT image, we only require
+ * an /images node.
+ */
+ if (CONFIG_IS_ENABLED(CONTROL_DTB_AS_FIT) && fit == gd_fdt_blob())
+ return fit_check_images_node(fit);
+
if (CONFIG_IS_ENABLED(FIT_FULL_CHECK)) {
/*
* If we are not given the size, make do with calculating it.
@@ -1696,7 +1761,7 @@ int fit_check_format(const void *fit, ulong size)
* attached. Protect against this by disallowing unit addresses.
*/
if (!ret && CONFIG_IS_ENABLED(FIT_SIGNATURE)) {
- ret = fdt_check_no_at(fit, 0);
+ ret = fdt_check_no_at(fit, 0, 0);
if (ret) {
log_debug("FIT check error %d\n", ret);
@@ -1724,17 +1789,11 @@ int fit_check_format(const void *fit, ulong size)
}
/* mandatory subimages parent '/images' node */
- if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) {
- log_debug("Wrong FIT format: no images parent node\n");
- return -ENOENT;
- }
-
- return 0;
+ return fit_check_images_node(fit);
}
int fit_conf_find_compat(const void *fit, const void *fdt)
{
- int ndepth = 0;
int noffset, confs_noffset, images_noffset;
const void *fdt_compat;
int fdt_compat_len;
@@ -1757,9 +1816,7 @@ int fit_conf_find_compat(const void *fit, const void *fdt)
/*
* Loop over the configurations in the FIT image.
*/
- for (noffset = fdt_next_node(fit, confs_noffset, &ndepth);
- (noffset >= 0) && (ndepth > 0);
- noffset = fdt_next_node(fit, noffset, &ndepth)) {
+ fdt_for_each_subnode(noffset, fit, confs_noffset) {
const void *fdt;
const char *kfdt_name;
int kfdt_noffset, compat_noffset;
@@ -1768,9 +1825,6 @@ int fit_conf_find_compat(const void *fit, const void *fdt)
size_t sz;
int i;
- if (ndepth > 1)
- continue;
-
/* If there's a compat property in the config node, use that. */
if (fdt_getprop(fit, noffset, FIT_COMPAT_PROP, NULL)) {
fdt = fit; /* search in FIT image */
@@ -2642,3 +2696,299 @@ out:
return fdt_noffset;
}
#endif
+
+#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(FIT_VERITY)
+
+static const char *const verity_opt_props[] = {
+ FIT_VERITY_OPT_RESTART,
+ FIT_VERITY_OPT_PANIC,
+ FIT_VERITY_OPT_RERR,
+ FIT_VERITY_OPT_PERR,
+ FIT_VERITY_OPT_ONCE,
+};
+
+/**
+ * fit_verity_build_target() - build one dm-verity target specification
+ * @fit: pointer to the FIT blob
+ * @img_noffset: image node offset containing the dm-verity subnode
+ * @loadable_idx: index of this loadable (for /dev/fitN)
+ * @uname: unit name of the image
+ * @separator: true if a ";" prefix is needed (not the first target)
+ * @buf: output buffer, or NULL to measure only
+ * @bufsize: size of @buf (ignored when @buf is NULL)
+ *
+ * Parses all dm-verity properties from the image's ``dm-verity`` child
+ * node and writes (or measures) a dm target specification string of the
+ * form used by the ``dm-mod.create`` kernel parameter.
+ *
+ * Return: number of characters that would be written (excluding '\0'),
+ * or -ve errno on error (e.g. missing mandatory property)
+ */
+static int fit_verity_build_target(const void *fit, int img_noffset,
+ int loadable_idx, const char *uname,
+ bool separator, char *buf, int bufsize)
+{
+ const char *algorithm;
+ const u8 *digest_raw, *salt_raw;
+ const fdt32_t *val;
+ char *digest_hex = NULL, *salt_hex = NULL, *opt_buf = NULL;
+ int verity_node;
+ unsigned int data_block_size, hash_block_size;
+ int num_data_blocks, hash_start_block;
+ u64 data_sectors;
+ int digest_len, salt_len;
+ int opt_count, opt_off, opt_buf_size;
+ int len;
+ int i;
+
+ verity_node = fdt_subnode_offset(fit, img_noffset, FIT_VERITY_NODENAME);
+ if (verity_node < 0)
+ return -ENOENT;
+
+ /* Mandatory u32 properties */
+ val = fdt_getprop(fit, verity_node, FIT_VERITY_DBS_PROP, NULL);
+ if (!val)
+ return -EINVAL;
+ data_block_size = fdt32_to_cpu(*val);
+
+ val = fdt_getprop(fit, verity_node, FIT_VERITY_HBS_PROP, NULL);
+ if (!val)
+ return -EINVAL;
+ hash_block_size = fdt32_to_cpu(*val);
+
+ val = fdt_getprop(fit, verity_node, FIT_VERITY_NBLK_PROP, NULL);
+ if (!val)
+ return -EINVAL;
+ num_data_blocks = fdt32_to_cpu(*val);
+
+ val = fdt_getprop(fit, verity_node, FIT_VERITY_HBLK_PROP, NULL);
+ if (!val)
+ return -EINVAL;
+ hash_start_block = fdt32_to_cpu(*val);
+
+ if (data_block_size < 512U || !is_power_of_2(data_block_size) ||
+ hash_block_size < 512U || !is_power_of_2(hash_block_size) ||
+ !num_data_blocks)
+ return -EINVAL;
+
+ /* Mandatory string */
+ algorithm = fdt_getprop(fit, verity_node, FIT_VERITY_ALGO_PROP, NULL);
+ if (!algorithm)
+ return -EINVAL;
+
+ /* Mandatory byte arrays */
+ digest_raw = fdt_getprop(fit, verity_node, FIT_VERITY_DIGEST_PROP,
+ &digest_len);
+ if (!digest_raw || digest_len <= 0)
+ return -EINVAL;
+
+ salt_raw = fdt_getprop(fit, verity_node, FIT_VERITY_SALT_PROP,
+ &salt_len);
+ if (!salt_raw || salt_len <= 0)
+ return -EINVAL;
+
+ /* Hex-encode digest and salt into dynamically sized buffers */
+ digest_hex = malloc(digest_len * 2 + 1);
+ salt_hex = malloc(salt_len * 2 + 1);
+ if (!digest_hex || !salt_hex) {
+ len = -ENOMEM;
+ goto out;
+ }
+ *bin2hex(digest_hex, digest_raw, digest_len) = '\0';
+ *bin2hex(salt_hex, salt_raw, salt_len) = '\0';
+
+ data_sectors = (u64)num_data_blocks * ((u64)data_block_size / 512);
+
+ /* Compute space needed for optional boolean properties */
+ opt_buf_size = 1; /* NUL terminator */
+ for (i = 0; i < ARRAY_SIZE(verity_opt_props); i++)
+ opt_buf_size += strlen(verity_opt_props[i]) + 1;
+ opt_buf = malloc(opt_buf_size);
+ if (!opt_buf) {
+ len = -ENOMEM;
+ goto out;
+ }
+
+ /* Collect optional boolean properties */
+ opt_count = 0;
+ opt_off = 0;
+ opt_buf[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(verity_opt_props); i++) {
+ if (fdt_getprop(fit, verity_node,
+ verity_opt_props[i], NULL)) {
+ const char *s = verity_opt_props[i];
+ int slen = strlen(s);
+
+ if (opt_off)
+ opt_buf[opt_off++] = ' ';
+ /* Copy with hyphen-to-underscore conversion */
+ while (slen-- > 0) {
+ opt_buf[opt_off++] =
+ (*s == '-') ? '_' : *s;
+ s++;
+ }
+ opt_buf[opt_off] = '\0';
+ opt_count++;
+ }
+ }
+
+ /* Emit (or measure) the target spec */
+ len = snprintf(buf, buf ? bufsize : 0,
+ "%s%s,,, ro,0 %llu verity 1 /dev/fit%d /dev/fit%d %u %u %d %d %s %s %s",
+ separator ? ";" : "", uname,
+ (unsigned long long)data_sectors, loadable_idx, loadable_idx,
+ data_block_size, hash_block_size,
+ num_data_blocks, hash_start_block,
+ algorithm, digest_hex, salt_hex);
+ if (opt_count) {
+ int extra = snprintf(buf ? buf + len : NULL,
+ buf ? bufsize - len : 0,
+ " %d %s", opt_count, opt_buf);
+ len += extra;
+ }
+
+out:
+ free(digest_hex);
+ free(salt_hex);
+ free(opt_buf);
+ return len;
+}
+
+int fit_verity_build_cmdline(const void *fit, int conf_noffset,
+ struct bootm_headers *images)
+{
+ int images_noffset;
+ int dm_create_len = 0, dm_waitfor_len = 0;
+ char *dm_create = NULL, *dm_waitfor = NULL;
+ const char *uname;
+ int loadable_idx;
+ int found = 0;
+ int ret = 0;
+
+ images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
+ if (images_noffset < 0)
+ return 0;
+
+ for (loadable_idx = 0;
+ (uname = fdt_stringlist_get(fit, conf_noffset,
+ FIT_LOADABLE_PROP,
+ loadable_idx, NULL));
+ loadable_idx++) {
+ int img_noffset, need;
+ u8 img_type;
+ char *tmp;
+
+ img_noffset = fdt_subnode_offset(fit, images_noffset, uname);
+ if (img_noffset < 0)
+ continue;
+
+ if (fit_image_get_type(fit, img_noffset, &img_type) ||
+ img_type != IH_TYPE_FILESYSTEM)
+ continue;
+
+ /* Measure first, then allocate and write */
+ need = fit_verity_build_target(fit, img_noffset,
+ loadable_idx, uname,
+ found > 0, NULL, 0);
+ if (need == -ENOENT)
+ continue; /* no dm-verity subnode -- fine */
+ if (need < 0) {
+ log_err("FIT: broken dm-verity metadata in '%s'\n",
+ uname);
+ ret = need;
+ goto err;
+ }
+
+ tmp = realloc(dm_create, dm_create_len + need + 1);
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ dm_create = tmp;
+ fit_verity_build_target(fit, img_noffset, loadable_idx,
+ uname, found > 0,
+ dm_create + dm_create_len,
+ need + 1);
+ dm_create_len += need;
+
+ /* Grow dm_waitfor buffer */
+ need = snprintf(NULL, 0, "%s/dev/fit%d",
+ dm_waitfor_len ? "," : "",
+ loadable_idx);
+ tmp = realloc(dm_waitfor, dm_waitfor_len + need + 1);
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ dm_waitfor = tmp;
+ sprintf(dm_waitfor + dm_waitfor_len, "%s/dev/fit%d",
+ dm_waitfor_len ? "," : "",
+ loadable_idx);
+ dm_waitfor_len += need;
+
+ found++;
+ }
+
+ if (found) {
+ /* Transfer ownership to the bootm_headers */
+ images->dm_mod_create = dm_create;
+ images->dm_mod_waitfor = dm_waitfor;
+ } else {
+ free(dm_create);
+ free(dm_waitfor);
+ }
+
+ return 0;
+
+err:
+ free(dm_create);
+ free(dm_waitfor);
+ return ret;
+}
+
+/**
+ * fmt used by both the measurement and the actual write of bootargs.
+ * Shared to guarantee they stay in sync.
+ */
+#define VERITY_BOOTARGS_FMT "%s%sdm-mod.create=\"%s\" dm-mod.waitfor=\"%s\""
+
+int fit_verity_apply_bootargs(const struct bootm_headers *images)
+{
+ const char *existing;
+ char *newargs;
+ int len;
+
+ if (!images->dm_mod_create)
+ return 0;
+
+ existing = env_get("bootargs");
+ if (!existing)
+ existing = "";
+
+ /* Measure */
+ len = snprintf(NULL, 0, VERITY_BOOTARGS_FMT,
+ existing, existing[0] ? " " : "",
+ images->dm_mod_create, images->dm_mod_waitfor);
+
+ newargs = malloc(len + 1);
+ if (!newargs)
+ return -ENOMEM;
+
+ snprintf(newargs, len + 1, VERITY_BOOTARGS_FMT,
+ existing, existing[0] ? " " : "",
+ images->dm_mod_create, images->dm_mod_waitfor);
+
+ env_set("bootargs", newargs);
+ free(newargs);
+
+ return 0;
+}
+
+void fit_verity_free(struct bootm_headers *images)
+{
+ free(images->dm_mod_create);
+ free(images->dm_mod_waitfor);
+ images->dm_mod_create = NULL;
+ images->dm_mod_waitfor = NULL;
+}
+#endif /* FIT_VERITY */