summaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2026-07-06 18:26:12 -0600
committerTom Rini <trini@konsulko.com>2026-07-06 18:26:12 -0600
commitee5d46b45ec0c63f8f9dd1e816e0dac3452ccc3d (patch)
tree800cd9e204ca027144070101884c0d5d3c00130f /boot
parentece349ade2973e220f524ce59e59711cc919263f (diff)
parenta18265f1ccb7a272721ed4286ed3b5a6182ff424 (diff)
downloadu-boot-master.tar.gz
u-boot-master.zip
Merge branch 'next'HEADmasterWIP/06Jul2026
Diffstat (limited to 'boot')
-rw-r--r--boot/Kconfig43
-rw-r--r--boot/bootdev-uclass.c17
-rw-r--r--boot/bootm.c48
-rw-r--r--boot/cedit.c2
-rw-r--r--boot/fdt_region.c15
-rw-r--r--boot/fdt_support.c27
-rw-r--r--boot/image-board.c7
-rw-r--r--boot/image-fdt.c81
-rw-r--r--boot/image-fit-sig.c19
-rw-r--r--boot/image-fit.c538
10 files changed, 649 insertions, 148 deletions
diff --git a/boot/Kconfig b/boot/Kconfig
index ae6f09a6ede..8e468c56176 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -103,6 +103,19 @@ config FIT_FULL_CHECK
of bugs or omissions in the code. This includes a bad structure,
multiple root nodes and the like.
+config CONTROL_DTB_AS_FIT
+ bool "Allow U-Boot's control DTB to act as FIT image"
+ help
+ Enable this to exempt U-Boot's control DTB from the sanity
+ checks done to ensure FIT images are valid. This can for
+ example be used to embed whole scripts in the control DTB,
+ that can then be invoked using 'source ${fdtcontroladdr}'.
+ In a secure boot setup, this is safe, as the control DTB is
+ necessarily covered by any mechanism verifying U-Boot and
+ can therefore be trusted. This only affects the case where
+ the image being checked is gd->fdt_blob. See
+ doc/develop/devicetree/control.rst for details.
+
config FIT_SIGNATURE
bool "Enable signature verification of FIT uImages"
depends on DM
@@ -142,6 +155,26 @@ config FIT_CIPHER
Enable the feature of data ciphering/unciphering in the tool mkimage
and in the u-boot support of the FIT image.
+config FIT_VERITY
+ bool "dm-verity boot parameter generation from FIT metadata"
+ depends on FIT && OF_LIBFDT
+ help
+ When a FIT configuration contains loadable sub-images of type
+ IH_TYPE_FILESYSTEM with a dm-verity subnode, this option enables
+ building the dm-mod.create= and dm-mod.waitfor= kernel
+ command-line parameters from the verity metadata
+ (data-block-size, hash-block-size, num-data-blocks,
+ hash-start-block, algorithm, digest, salt) stored in the FIT.
+
+ The generated parameters reference /dev/fitN block devices that
+ Linux's uImage.FIT block driver assigns to loadable sub-images.
+
+ During FIT parsing (BOOTM_STATE_FINDOTHER), verity cmdline
+ fragments are stored in struct bootm_headers and automatically
+ appended to the bootargs environment variable during
+ BOOTM_STATE_OS_PREP. This works from both the bootm command
+ and BOOTSTD bootmeths.
+
config FIT_VERBOSE
bool "Show verbose messages when FIT images fail"
help
@@ -653,9 +686,9 @@ config BOOTMETH_QFW
depends on QFW
default y
help
- Use QEMU parameters -kernel, -initrd, -append to determine the kernel,
- initial RAM disk, and kernel command line parameters to boot an
- operating system. U-Boot's control device-tree is passed to the kernel.
+ Use QEMU parameters -kernel, -initrd, -append to determine the kernel,
+ initial RAM disk, and kernel command line parameters to boot an
+ operating system. U-Boot's control device-tree is passed to the kernel.
config BOOTMETH_VBE
bool "Bootdev support for Verified Boot for Embedded"
@@ -1051,7 +1084,7 @@ config MEASURED_BOOT
to use some attestation tools on your system.
if MEASURED_BOOT
- config MEASURE_DEVICETREE
+config MEASURE_DEVICETREE
bool "Measure the devicetree image"
default y if MEASURED_BOOT
help
@@ -1060,7 +1093,7 @@ if MEASURED_BOOT
Therefore, it should not be measured into the TPM. In that case,
disable the measurement here.
- config MEASURE_IGNORE_LOG
+config MEASURE_IGNORE_LOG
bool "Ignore the existing event log"
help
On platforms that use an event log memory region that persists
diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c
index 3f8dc2c3c4e..55e1a6c4e02 100644
--- a/boot/bootdev-uclass.c
+++ b/boot/bootdev-uclass.c
@@ -566,13 +566,18 @@ int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
{
const struct bootdev_ops *ops = bootdev_get_ops(dev);
- log_debug("->get_bootflow %s,%x=%p\n", dev->name, iter->part,
- ops->get_bootflow);
bootflow_init(bflow, dev, iter->method);
- if (!ops->get_bootflow)
- return default_get_bootflow(dev, iter, bflow);
- return ops->get_bootflow(dev, iter, bflow);
+ if (ops && ops->get_bootflow) {
+ log_debug("->get_bootflow %s,%x=%p\n", dev->name, iter->part,
+ ops->get_bootflow);
+
+ return ops->get_bootflow(dev, iter, bflow);
+ }
+
+ log_debug("->get_bootflow %s,%x is unset\n", dev->name, iter->part);
+
+ return default_get_bootflow(dev, iter, bflow);
}
int bootdev_next_label(struct bootflow_iter *iter, struct udevice **devp,
@@ -664,8 +669,6 @@ int bootdev_next_prio(struct bootflow_iter *iter, struct udevice **devp)
BOOTFLOWIF_SHOW);
log_debug("- bootdev_hunt_prio() ret %d\n",
ret);
- if (ret)
- return log_msg_ret("hun", ret);
}
} else {
ret = device_probe(dev);
diff --git a/boot/bootm.c b/boot/bootm.c
index 4836d6b2d41..803d6406be4 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -243,6 +243,13 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images,
static int bootm_start(void)
{
+ /*
+ * Free dm-verity allocations from a prior boot attempt before
+ * zeroing the structure. The pointers are guaranteed to be valid
+ * or NULL: .bss is zero-initialised, and memset() below zeroes
+ * them again after every boot.
+ */
+ fit_verity_free(&images);
memset((void *)&images, 0, sizeof(images));
images.verify = env_get_yesno("verify");
@@ -323,6 +330,10 @@ static int bootm_find_os(const char *cmd_name, const char *addr_fit)
images.os.type = image_get_type(os_hdr);
images.os.comp = image_get_comp(os_hdr);
images.os.os = image_get_os(os_hdr);
+ if (images.os.os >= IH_OS_COUNT) {
+ printf("Unsupported OS type %d\n", images.os.os);
+ return 1;
+ }
images.os.end = image_get_image_end(os_hdr);
images.os.load = image_get_load(os_hdr);
@@ -364,11 +375,17 @@ static int bootm_find_os(const char *cmd_name, const char *addr_fit)
images.os.end = fit_get_end(images.fit_hdr_os);
if (fit_image_get_load(images.fit_hdr_os, images.fit_noffset_os,
- &images.os.load)) {
+ &images.os.load) &&
+ images.os.type != IH_TYPE_KERNEL_NOLOAD) {
puts("Can't get image load address!\n");
bootstage_error(BOOTSTAGE_ID_FIT_LOADADDR);
return 1;
}
+ if (images.os.load && images.os.type == IH_TYPE_KERNEL_NOLOAD) {
+ puts("WARNING: load address set for kernel_noload image, ignoring\n");
+ images.os.load = 0;
+ }
+
break;
#endif
#ifdef CONFIG_ANDROID_BOOT_IMAGE
@@ -416,7 +433,7 @@ static int bootm_find_os(const char *cmd_name, const char *addr_fit)
ret = fit_image_get_entry(images.fit_hdr_os,
images.fit_noffset_os, &images.ep);
- if (ret) {
+ if (ret && images.os.type != IH_TYPE_KERNEL_NOLOAD) {
puts("Can't get entry point property!\n");
return 1;
}
@@ -610,7 +627,8 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress)
ulong blob_end = os.end;
ulong image_start = os.image_start;
ulong image_len = os.image_len;
- ulong flush_start = ALIGN_DOWN(load, ARCH_DMA_MINALIGN);
+ ulong decomp_len = CONFIG_SYS_BOOTM_LEN;
+ ulong flush_start;
bool no_overlap;
void *load_buf, *image_buf;
int err;
@@ -618,16 +636,16 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress)
/*
* For a "noload" compressed kernel we need to allocate a buffer large
* enough to decompress in to and use that as the load address now.
- * Assume that the kernel compression is at most a factor of 4 since
- * zstd almost achieves that.
+ * Allow up to 8x compression: this comfortably covers what zstd and xz
+ * achieve on real kernels, with headroom for well-compressed payloads.
* Use an alignment of 2MB since this might help arm64
*/
if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) {
- ulong req_size = ALIGN(image_len * 4, SZ_1M);
phys_addr_t addr;
+ decomp_len = ALIGN(image_len * 8, SZ_1M);
err = lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, &addr,
- req_size, LMB_NONE);
+ decomp_len, LMB_NONE);
if (err)
return 1;
@@ -635,23 +653,27 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress)
images->os.load = (ulong)addr;
images->ep = (ulong)addr;
debug("Allocated %lx bytes at %lx for kernel (size %lx) decompression\n",
- req_size, load, image_len);
+ decomp_len, load, image_len);
}
load_buf = map_sysmem(load, 0);
image_buf = map_sysmem(os.image_start, image_len);
err = image_decomp(os.comp, load, os.image_start, os.type,
load_buf, image_buf, image_len,
- CONFIG_SYS_BOOTM_LEN, &load_end);
+ decomp_len, &load_end);
if (err) {
err = handle_decomp_error(os.comp, load_end - load,
- CONFIG_SYS_BOOTM_LEN, err);
+ 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);
bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE);
return err;
}
/* We need the decompressed image size in the next steps */
images->os.image_len = load_end - load;
+ flush_start = ALIGN_DOWN(load, ARCH_DMA_MINALIGN);
flush_cache(flush_start, ALIGN(load_end, ARCH_DMA_MINALIGN) - flush_start);
debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, load_end);
@@ -1071,6 +1093,12 @@ int bootm_run_states(struct bootm_info *bmi, int states)
/* For Linux OS do all substitutions at console processing */
if (images->os.os == IH_OS_LINUX)
flags = BOOTM_CL_ALL;
+ ret = fit_verity_apply_bootargs(images);
+ if (ret) {
+ printf("dm-verity bootargs failed (err=%d)\n", ret);
+ ret = CMD_RET_FAILURE;
+ goto err;
+ }
ret = bootm_process_cmdline_env(flags);
if (ret) {
printf("Cmdline setup failed (err=%d)\n", ret);
diff --git a/boot/cedit.c b/boot/cedit.c
index 56dc7c6af15..b1b79d1752a 100644
--- a/boot/cedit.c
+++ b/boot/cedit.c
@@ -500,6 +500,8 @@ static int h_read_settings(struct scene_obj *obj, void *vpriv)
tline = (struct scene_obj_textline *)obj;
val = ofnode_read_prop(node, obj->name, &len);
+ if (!val)
+ return log_msg_ret("tline", -ENOENT);
if (len >= tline->max_chars)
return log_msg_ret("str", -ENOSPC);
strcpy(abuf_data(&tline->buf), val);
diff --git a/boot/fdt_region.c b/boot/fdt_region.c
index 295ea08ac91..dd6e87925be 100644
--- a/boot/fdt_region.c
+++ b/boot/fdt_region.c
@@ -69,6 +69,8 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
include = want >= 2;
stop_at = offset;
prop = fdt_get_property_by_offset(fdt, offset, NULL);
+ if (!prop)
+ return -FDT_ERR_BADSTRUCTURE;
str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
if (!str)
return -FDT_ERR_BADSTRUCTURE;
@@ -86,6 +88,8 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
if (depth == FDT_MAX_DEPTH)
return -FDT_ERR_BADSTRUCTURE;
name = fdt_get_name(fdt, offset, &len);
+ if (!name)
+ return len;
/* The root node must have an empty name */
if (!depth && *name)
@@ -271,7 +275,11 @@ int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count,
int target, next;
prop = fdt_get_property_by_offset(fdt, offset, NULL);
+ if (!prop)
+ return -FDT_ERR_BADSTRUCTURE;
name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ if (!name)
+ return -FDT_ERR_BADSTRUCTURE;
target = fdt_path_offset(fdt, name);
if (!region_list_contains_offset(info, fdt, target))
continue;
@@ -520,7 +528,11 @@ int fdt_next_region(const void *fdt,
case FDT_PROP:
stop_at = offset;
prop = fdt_get_property_by_offset(fdt, offset, NULL);
+ if (!prop)
+ return -FDT_ERR_BADSTRUCTURE;
str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
val = h_include(priv, fdt, last_node, FDT_IS_PROP, str,
strlen(str) + 1);
if (val == -1) {
@@ -553,6 +565,9 @@ int fdt_next_region(const void *fdt,
if (p.depth == FDT_MAX_DEPTH)
return -FDT_ERR_BADSTRUCTURE;
name = fdt_get_name(fdt, offset, &len);
+ if (!name)
+ return len;
+
if (p.end - path + 2 + len >= path_len)
return -FDT_ERR_NOSPACE;
diff --git a/boot/fdt_support.c b/boot/fdt_support.c
index 1c215e548db..2941df55996 100644
--- a/boot/fdt_support.c
+++ b/boot/fdt_support.c
@@ -160,6 +160,12 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff)
goto noalias;
}
+ if (len > (int)sizeof(tmp)) {
+ debug("%s: %s alias path too long (%d bytes)\n",
+ __func__, sername, len);
+ return -FDT_ERR_NOSPACE;
+ }
+
/* fdt_setprop may break "path" so we copy it to tmp buffer */
memcpy(tmp, path, len);
@@ -545,13 +551,13 @@ int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
if (banks > MEMORY_BANKS_MAX) {
printf("%s: num banks %d exceeds hardcoded limit %d."
" Recompile with higher MEMORY_BANKS_MAX?\n",
- __FUNCTION__, banks, MEMORY_BANKS_MAX);
+ __func__, banks, MEMORY_BANKS_MAX);
return -1;
}
err = fdt_check_header(blob);
if (err < 0) {
- printf("%s: %s\n", __FUNCTION__, fdt_strerror(err));
+ printf("%s: %s\n", __func__, fdt_strerror(err));
return err;
}
@@ -1497,7 +1503,7 @@ static u64 __of_translate_address(const void *blob, int node_offset,
/* Cound address cells & copy address locally */
bus->count_cells(blob, parent, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
- printf("%s: Bad cell count for %s\n", __FUNCTION__,
+ printf("%s: Bad cell count for %s\n", __func__,
fdt_get_name(blob, node_offset, NULL));
goto bail;
}
@@ -1524,8 +1530,8 @@ static u64 __of_translate_address(const void *blob, int node_offset,
pbus = of_match_bus(blob, parent);
pbus->count_cells(blob, parent, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- printf("%s: Bad cell count for %s\n", __FUNCTION__,
- fdt_get_name(blob, node_offset, NULL));
+ printf("%s: Bad cell count for %s\n", __func__,
+ fdt_get_name(blob, node_offset, NULL));
break;
}
@@ -1612,7 +1618,7 @@ int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu,
bus_node = of_match_bus(blob, node);
bus_node->count_cells(blob, node, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
- printf("%s: Bad cell count for %s\n", __FUNCTION__,
+ printf("%s: Bad cell count for %s\n", __func__,
fdt_get_name(blob, node, NULL));
return -EINVAL;
goto out;
@@ -1621,12 +1627,19 @@ int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu,
bus_node = of_match_bus(blob, parent);
bus_node->count_cells(blob, parent, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- printf("%s: Bad cell count for %s\n", __FUNCTION__,
+ printf("%s: Bad cell count for %s\n", __func__,
fdt_get_name(blob, parent, NULL));
return -EINVAL;
goto out;
}
+ if (len < (int)((na + pna + ns) * sizeof(*ranges))) {
+ debug("%s: dma-ranges too short for %s\n", __func__,
+ fdt_get_name(blob, node, NULL));
+ ret = -EINVAL;
+ goto out;
+ }
+
*bus = fdt_read_number(ranges, na);
*cpu = fdt_translate_dma_address(blob, node, ranges + na);
*size = fdt_read_number(ranges + na + pna, ns);
diff --git a/boot/image-board.c b/boot/image-board.c
index 005d60caf5c..67938fdd200 100644
--- a/boot/image-board.c
+++ b/boot/image-board.c
@@ -118,7 +118,7 @@ phys_addr_t env_get_bootm_low(void)
#if defined(CFG_SYS_SDRAM_BASE)
return CFG_SYS_SDRAM_BASE;
#elif defined(CONFIG_ARM) || defined(CONFIG_MICROBLAZE) || defined(CONFIG_RISCV)
- return gd->bd->bi_dram[0].start;
+ return gd->dram[0].start;
#else
return 0;
#endif
@@ -810,6 +810,11 @@ int boot_get_loadable(struct bootm_headers *images)
fit_loadable_process(img_type, img_data, img_len);
}
+
+ fit_img_result = fit_verity_build_cmdline(buf, conf_noffset,
+ images);
+ if (fit_img_result < 0)
+ return fit_img_result;
break;
default:
printf("The given image format is not supported (corrupt?)\n");
diff --git a/boot/image-fdt.c b/boot/image-fdt.c
index a3a4fb8b558..9e0e0f93edd 100644
--- a/boot/image-fdt.c
+++ b/boot/image-fdt.c
@@ -69,35 +69,50 @@ static const struct legacy_img_hdr *image_get_fdt(ulong fdt_addr)
}
#endif
-static void boot_fdt_reserve_region(u64 addr, u64 size, u32 flags)
+/**
+ * boot_fdt_handle_region - Reserve or free a given FDT region in LMB
+ * @addr: Reservation base address
+ * @size: Reservation size
+ * @flags: Reservation flags
+ * @free: Indicate if region is being freed or allocated
+ *
+ * Add or free a given reservation from LMB. This reports to the user if any
+ * errors occurred during either operation.
+ */
+static void boot_fdt_handle_region(u64 addr, u64 size, u32 flags, bool free)
{
long ret;
phys_addr_t rsv_addr;
rsv_addr = (phys_addr_t)addr;
- ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &rsv_addr, size, flags);
+ if (free)
+ ret = lmb_free(rsv_addr, size, flags);
+ else
+ ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &rsv_addr, size,
+ flags);
+
if (!ret) {
- debug(" reserving fdt memory region: addr=%llx size=%llx flags=%x\n",
- (unsigned long long)addr,
+ debug(" %s fdt memory region: addr=%llx size=%llx flags=%x\n",
+ free ? "freed" : "reserved", (unsigned long long)addr,
(unsigned long long)size, flags);
- } else if (ret != -EEXIST && ret != -EINVAL) {
- puts("ERROR: reserving fdt memory region failed ");
- printf("(addr=%llx size=%llx flags=%x)\n",
- (unsigned long long)addr,
- (unsigned long long)size, flags);
+ } else {
+ printf("ERROR: %s fdt memory region failed (addr=%llx size=%llx flags=%x): %ld\n",
+ free ? "freeing" : "reserving", (unsigned long long)addr,
+ (unsigned long long)size, flags, ret);
}
}
/**
- * boot_fdt_add_mem_rsv_regions - Mark the memreserve and reserved-memory
- * sections as unusable
+ * boot_fdt_handle_mem_rsv_regions - Handle FDT memreserve and reserved-memory
+ * sections
* @fdt_blob: pointer to fdt blob base address
+ * @free: indicate if regions are being freed
*
- * Adds the and reserved-memorymemreserve regions in the dtb to the lmb block.
- * Adding the memreserve regions prevents u-boot from using them to store the
- * initrd or the fdt blob.
+ * Adds or removes reserved-memory and memreserve regions in the dtb to the lmb
+ * block. Adding the memreserve regions prevents u-boot from using them to store
+ * the initrd or the fdt blob.
*/
-void boot_fdt_add_mem_rsv_regions(void *fdt_blob)
+static void boot_fdt_handle_mem_rsv_regions(const void *fdt_blob, bool free)
{
uint64_t addr, size;
int i, total, ret;
@@ -105,15 +120,12 @@ void boot_fdt_add_mem_rsv_regions(void *fdt_blob)
struct fdt_resource res;
u32 flags;
- if (fdt_check_header(fdt_blob) != 0)
- return;
-
/* process memreserve sections */
total = fdt_num_mem_rsv(fdt_blob);
for (i = 0; i < total; i++) {
if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
continue;
- boot_fdt_reserve_region(addr, size, LMB_NOOVERWRITE);
+ boot_fdt_handle_region(addr, size, LMB_NOOVERWRITE, free);
}
/* process reserved-memory */
@@ -131,7 +143,7 @@ void boot_fdt_add_mem_rsv_regions(void *fdt_blob)
flags = LMB_NOMAP;
addr = res.start;
size = res.end - res.start + 1;
- boot_fdt_reserve_region(addr, size, flags);
+ boot_fdt_handle_region(addr, size, flags, free);
}
subnode = fdt_next_subnode(fdt_blob, subnode);
@@ -140,6 +152,31 @@ void boot_fdt_add_mem_rsv_regions(void *fdt_blob)
}
/**
+ * boot_fdt_add_mem_rsv_regions - Add FDT memreserve and reserved-memory
+ * sections
+ * @fdt_blob: pointer to fdt blob base address
+ *
+ * Adds reserved-memory and memreserve regions in the dtb to the lmb block.
+ * Adding the memreserve regions prevents u-boot from using them to store the
+ * initrd or the fdt blob.
+ *
+ * This function will attempt to clean the currently active reservations if a
+ * new device tree blob is given. This must be called before gd->fdt_blob is
+ * switched.
+ */
+void boot_fdt_add_mem_rsv_regions(const void *fdt_blob)
+{
+ if (fdt_check_header(fdt_blob) != 0)
+ return;
+
+ /* Remove old regions */
+ if (gd->fdt_blob != fdt_blob)
+ boot_fdt_handle_mem_rsv_regions(gd->fdt_blob, true);
+
+ boot_fdt_handle_mem_rsv_regions(fdt_blob, false);
+}
+
+/**
* boot_relocate_fdt - relocate flat device tree
* @of_flat_tree: pointer to a char* variable, will hold fdt start address
* @of_size: pointer to a ulong variable, will hold fdt length
@@ -223,8 +260,8 @@ int boot_relocate_fdt(char **of_flat_tree, ulong *of_size)
of_start = NULL;
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
- start = gd->bd->bi_dram[bank].start;
- size = gd->bd->bi_dram[bank].size;
+ start = gd->dram[bank].start;
+ size = gd->dram[bank].size;
/* DRAM bank addresses are too low, skip it. */
if (start + size < low)
diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c
index 433df20281f..9b5ab754561 100644
--- a/boot/image-fit-sig.c
+++ b/boot/image-fit-sig.c
@@ -452,6 +452,8 @@ static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset,
int max_regions;
char path[200];
int count;
+ int len;
+ uint32_t size;
debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, key_blob,
fit_get_name(fit, noffset, NULL),
@@ -506,14 +508,27 @@ static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset,
}
/* Add the strings */
- strings = fdt_getprop(fit, noffset, "hashed-strings", NULL);
+ strings = fdt_getprop(fit, noffset, "hashed-strings", &len);
if (strings) {
+ if (len < (int)(2 * sizeof(fdt32_t))) {
+ *err_msgp = "Invalid hashed-strings property";
+ return -1;
+ }
+ size = fdt32_to_cpu(strings[1]);
+ /*
+ * The offset should be already validated by fdt_check_header();
+ * validate the size here.
+ */
+ if (size > fdt_size_dt_strings(fit)) {
+ *err_msgp = "Strings region is out of bounds";
+ return -1;
+ }
/*
* The strings region offset must be a static 0x0.
* This is set in tool/image-host.c
*/
fdt_regions[count].offset = fdt_off_dt_strings(fit);
- fdt_regions[count].size = fdt32_to_cpu(strings[1]);
+ fdt_regions[count].size = size;
count++;
}
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 */