[PATCH 13/14] bootm: add initial FIT support

Sascha Hauer s.hauer at pengutronix.de
Thu Jan 21 23:32:31 PST 2016


From: Jan Luebbe <jlu at pengutronix.de>

This implementation is inspired by U-Boot's FIT support. Instead of
using libfdt (which does not exist in barebox), configuration signatures
are verified by using a simplified DT parser based on barebox's own
code.

Currently, only signed configurations with hashed images are supported,
as the other variants are less useful for verified boot. Compatible FIT
images can be created using U-Boot's mkimage tool.

Signed-off-by: Jan Luebbe <jlu at pengutronix.de>
Signed-off-by: Marc Kleine-Budde <mkl at pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 arch/arm/lib/bootm.c |   8 +
 commands/Kconfig     |  25 +++
 common/Kconfig       |   9 +
 common/Makefile      |   1 +
 common/bootm.c       |  53 ++++-
 common/image-fit.c   | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/boot.h       |   4 +
 include/image-fit.h  |  45 ++++
 8 files changed, 726 insertions(+), 3 deletions(-)
 create mode 100644 common/image-fit.c
 create mode 100644 include/image-fit.h

diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index 4dfe148..1913d5f 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -531,6 +531,12 @@ BAREBOX_MAGICVAR(aimage_noverwrite_bootargs, "Disable overwrite of the bootargs
 BAREBOX_MAGICVAR(aimage_noverwrite_tags, "Disable overwrite of the tags addr with the one present in aimage");
 #endif
 
+static struct image_handler arm_fit_handler = {
+        .name = "FIT image",
+        .bootm = do_bootm_linux,
+        .filetype = filetype_oftree,
+};
+
 static struct binfmt_hook binfmt_aimage_hook = {
 	.type = filetype_aimage,
 	.exec = "bootm",
@@ -556,6 +562,8 @@ static int armlinux_register_image_handler(void)
 		register_image_handler(&aimage_handler);
 		binfmt_register(&binfmt_aimage_hook);
 	}
+	if (IS_BUILTIN(CONFIG_CMD_BOOTM_FITIMAGE))
+	        register_image_handler(&arm_fit_handler);
 	binfmt_register(&binfmt_arm_zimage_hook);
 	binfmt_register(&binfmt_barebox_hook);
 
diff --git a/commands/Kconfig b/commands/Kconfig
index 1743670..b4fdc86 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -418,6 +418,31 @@ config CMD_BOOTM_AIMAGE
 	help
 	  Support using Android Images.
 
+config CMD_BOOTM_FITIMAGE
+	bool
+	prompt "FIT image support"
+	select FITIMAGE
+	depends on CMD_BOOTM && ARM
+	help
+	  Support using Flattened Image Tree (FIT) Images. FIT is an image
+	  format introduced by U-Boot. A FIT image contains one or multiple
+	  kernels, device trees and initrds. The FIT image itself is a flattened
+	  device tree binary. Have a look at the u-boot source tree
+	  in the "doc/uImage.FIT" folder for more information:
+	  http://git.denx.de/?p=u-boot.git;a=tree;f=doc/uImage.FIT
+
+config CMD_BOOTM_FITIMAGE_SIGNATURE
+	bool
+	prompt "support verifying signed FIT images"
+	depends on CMD_BOOTM_FITIMAGE
+	select FITIMAGE_SIGNATURE
+	help
+	  Support verifying signed FIT images. This requires FIT images
+	  as described in:
+	  http://git.denx.de/?p=u-boot.git;a=blob;f=doc/uImage.FIT/signature.txt
+	  Additionally the barebox device tree needs a /signature node with the
+	  public key with which the image has been signed.
+
 config CMD_BOOTU
 	tristate
 	default y
diff --git a/common/Kconfig b/common/Kconfig
index 8e79509..345de50 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -66,6 +66,15 @@ config UIMAGE
 	select CRC32
 	bool
 
+config FITIMAGE
+	bool
+	select OFTREE
+	select DIGEST
+
+config FITIMAGE_SIGNATURE
+	select CRYPTO_RSA
+	bool
+
 config LOGBUF
 	bool
 
diff --git a/common/Makefile b/common/Makefile
index 56e6bec..ffaf8e7 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_SHELL_HUSH)	+= hush.o
 obj-$(CONFIG_SHELL_SIMPLE)	+= parser.o
 obj-$(CONFIG_STATE)		+= state.o
 obj-$(CONFIG_UIMAGE)		+= image.o uimage.o
+obj-$(CONFIG_FITIMAGE)		+= image-fit.o
 obj-$(CONFIG_MENUTREE)		+= menutree.o
 obj-$(CONFIG_EFI_GUID)		+= efi-guid.o
 obj-$(CONFIG_EFI_DEVICEPATH)	+= efi-devicepath.o
diff --git a/common/bootm.c b/common/bootm.c
index 78a6bb5..d8acff8 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -17,6 +17,7 @@
 #include <malloc.h>
 #include <memory.h>
 #include <libfile.h>
+#include <image-fit.h>
 #include <globalvar.h>
 #include <init.h>
 #include <linux/stat.h>
@@ -92,6 +93,17 @@ int bootm_load_os(struct image_data *data, unsigned long load_address)
 	if (load_address == UIMAGE_INVALID_ADDRESS)
 		return -EINVAL;
 
+	if (data->os_fit) {
+		data->os_res = request_sdram_region("kernel",
+				load_address,
+				data->os_fit->kernel_size);
+		if (!data->os_res)
+			return -ENOMEM;
+		memcpy((void *)load_address, data->os_fit->kernel,
+		       data->os_fit->kernel_size);
+		return 0;
+	}
+
 	if (data->os) {
 		int num;
 
@@ -121,6 +133,9 @@ bool bootm_has_initrd(struct image_data *data)
 	if (!IS_ENABLED(CONFIG_CMD_BOOTM_INITRD))
 		return false;
 
+	if (data->os_fit && data->os_fit->initrd)
+		return true;
+
 	if (data->initrd_file)
 		return true;
 
@@ -178,6 +193,18 @@ int bootm_load_initrd(struct image_data *data, unsigned long load_address)
 	if (data->initrd_res)
 		return 0;
 
+	if (data->os_fit && data->os_fit->initrd) {
+		data->initrd_res = request_sdram_region("initrd",
+				load_address,
+				data->os_fit->initrd_size);
+		if (!data->initrd_res)
+			return -ENOMEM;
+		memcpy((void *)load_address, data->os_fit->initrd,
+		       data->os_fit->initrd_size);
+		printf("Loaded initrd from FIT image\n");
+		goto done1;
+	}
+
 	type = file_name_detect_type(data->initrd_file);
 
 	if ((int)type < 0) {
@@ -216,7 +243,7 @@ done:
 	if (type == filetype_uimage && data->initrd->header.ih_type == IH_TYPE_MULTI)
 		printf(", multifile image %s", data->initrd_part);
 	printf("\n");
-
+done1:
 	printf("initrd is at " PRINTF_CONVERSION_RESOURCE "-" PRINTF_CONVERSION_RESOURCE "\n",
 		data->initrd_res->start,
 		data->initrd_res->end);
@@ -289,7 +316,9 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
 	if (!IS_ENABLED(CONFIG_OFTREE))
 		return 0;
 
-	if (data->oftree_file) {
+	if (data->os_fit && data->os_fit->oftree) {
+		data->of_root_node = of_unflatten_dtb(data->os_fit->oftree);
+	} else if (data->oftree_file) {
 		size_t size;
 
 		type = file_name_detect_type(data->oftree_file);
@@ -376,6 +405,8 @@ int bootm_get_os_size(struct image_data *data)
 	if (data->os)
 		return uimage_get_size(data->os,
 				       simple_strtoul(data->os_part, NULL, 0));
+	if (data->os_fit)
+		return data->os_fit->kernel_size;
 
 	if (data->os_file) {
 		struct stat s;
@@ -495,10 +526,24 @@ int bootm_boot(struct bootm_data *bootm_data)
 		goto err_out;
 	}
 
+	if (IS_ENABLED(CONFIG_FITIMAGE) && os_type == filetype_oftree) {
+		struct fit_handle *fit;
+
+		fit = fit_open(data->os_file, data->os_part, data->verbose, data->verify);
+		if (IS_ERR(fit)) {
+			printf("Loading FIT image %s failed with: %s\n", data->os_file,
+			       strerrorp(fit));
+			ret = PTR_ERR(fit);
+			goto err_out;
+		}
+
+		data->os_fit = fit;
+	}
+
 	if (os_type == filetype_uimage) {
 		ret = bootm_open_os_uimage(data);
 		if (ret) {
-			printf("Loading OS image failed with %s\n",
+			printf("Loading OS image failed with: %s\n",
 					strerror(-ret));
 			goto err_out;
 		}
@@ -544,6 +589,8 @@ err_out:
 		uimage_close(data->initrd);
 	if (data->os)
 		uimage_close(data->os);
+	if (IS_ENABLED(CONFIG_FITIMAGE) && data->os_fit)
+		fit_close(data->os_fit);
 	if (data->of_root_node && data->of_root_node != of_get_root_node())
 		of_delete_node(data->of_root_node);
 
diff --git a/common/image-fit.c b/common/image-fit.c
new file mode 100644
index 0000000..4f5c1f1
--- /dev/null
+++ b/common/image-fit.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) Jan Lübbe, 2014
+ *
+ * This code is inspired by the U-Boot FIT image code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "FIT: " fmt
+#include <common.h>
+#include <init.h>
+#include <boot.h>
+#include <libfile.h>
+#include <fdt.h>
+#include <digest.h>
+#include <of.h>
+#include <fs.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <stringlist.h>
+#include <rsa.h>
+#include <image-fit.h>
+
+#define FDT_MAX_DEPTH 32
+#define FDT_MAX_PATH_LEN 200
+
+#define CHECK_LEVEL_NONE 0
+#define CHECK_LEVEL_HASH 1
+#define CHECK_LEVEL_SIG 2
+#define CHECK_LEVEL_MAX 3
+
+static uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int size)
+{
+	dt += size;
+	dt = ALIGN(dt, 4);
+
+	if (dt > f->off_dt_struct + f->size_dt_struct)
+		return 0;
+
+	return dt;
+}
+
+static char *dt_string(struct fdt_header *f, char *strstart, uint32_t ofs)
+{
+	if (ofs > f->size_dt_strings)
+		return NULL;
+	else
+		return strstart + ofs;
+}
+
+static int of_read_string_list(struct device_node *np, const char *name, struct string_list *sl)
+{
+	struct property *prop;
+	const char *s;
+
+	of_property_for_each_string(np, name, prop, s) {
+		string_list_add(sl, s);
+	}
+
+	return prop ? 0 : -EINVAL;
+}
+
+static int fit_digest(void *fit, struct digest *digest,
+		      struct string_list *inc_nodes, struct string_list *exc_props,
+		      uint32_t hashed_strings_start, uint32_t hashed_strings_size)
+{
+	struct fdt_header *fdt = fit;
+	uint32_t dt_struct;
+	void *dt_strings;
+	struct fdt_header f = {};
+	int stack[FDT_MAX_DEPTH];
+	char path[FDT_MAX_PATH_LEN];
+	char *end;
+	uint32_t tag;
+	int start = -1;
+	int depth = -1;
+	int want = 0;
+
+	f.totalsize = fdt32_to_cpu(fdt->totalsize);
+	f.off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct);
+	f.size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct);
+	f.off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings);
+	f.size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings);
+
+	if (hashed_strings_start > f.size_dt_strings ||
+	    hashed_strings_size > f.size_dt_strings ||
+	    hashed_strings_start + hashed_strings_size > f.size_dt_strings) {
+		pr_err("%s: hashed-strings too large\n", __func__);
+		return -EINVAL;
+	}
+
+	dt_struct = f.off_dt_struct;
+	dt_strings = (void *)fdt + f.off_dt_strings;
+
+	end = path;
+	*end = '\0';
+
+	do {
+		const struct fdt_property *fdt_prop;
+		const struct fdt_node_header *fnh;
+		const char *name;
+		int include = 0;
+		int stop_at = 0;
+		int offset = dt_struct;
+		int maxlen, len;
+
+		tag = be32_to_cpu(*(uint32_t *)(fit + dt_struct));
+
+		switch (tag) {
+		case FDT_BEGIN_NODE:
+			fnh = fit + dt_struct;
+			name = fnh->name;
+			maxlen = (unsigned long)fdt + f.off_dt_struct +
+				f.size_dt_struct - (unsigned long)name;
+
+			len = strnlen(name, maxlen + 1);
+			if (len > maxlen)
+				return -ESPIPE;
+
+			dt_struct = dt_struct_advance(&f, dt_struct,
+						      sizeof(struct fdt_node_header) + len + 1);
+
+			depth++;
+			if (depth == FDT_MAX_DEPTH)
+				return -ESPIPE;
+			if (end - path + 2 + len >= FDT_MAX_PATH_LEN)
+				return -ESPIPE;
+			if (end != path + 1)
+				*end++ = '/';
+			strcpy(end, name);
+			end += len;
+			stack[depth] = want;
+			if (want == 1)
+				stop_at = offset;
+			if (string_list_contains(inc_nodes, path))
+				want = 2;
+			else if (want)
+				want--;
+			else
+				stop_at = offset;
+			include = want;
+
+			break;
+
+		case FDT_END_NODE:
+			dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE);
+
+			include = want;
+			want = stack[depth--];
+			while (end > path && *--end != '/')
+				;
+			*end = '\0';
+
+			break;
+
+		case FDT_PROP:
+			fdt_prop = fit + dt_struct;
+			len = fdt32_to_cpu(fdt_prop->len);
+
+			name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff));
+			if (!name)
+				return -ESPIPE;
+
+			dt_struct = dt_struct_advance(&f, dt_struct,
+						      sizeof(struct fdt_property) + len);
+
+			include = want >= 2;
+			stop_at = offset;
+			if (string_list_contains(exc_props, name))
+				include = 0;
+
+			break;
+
+		case FDT_NOP:
+			dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE);
+
+			include = want >= 2;
+			stop_at = offset;
+
+			break;
+
+		case FDT_END:
+			dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE);
+
+			include = 1;
+
+			break;
+
+		default:
+			pr_err("%s: Unknown tag 0x%08X\n", __func__, tag);
+			return -EINVAL;
+		}
+
+		if (!dt_struct)
+			return -ESPIPE;
+
+		pr_debug("%s: include %d, want %d, offset 0x%x, len 0x%x\n",
+			 path, include, want, offset, dt_struct-offset);
+
+		if (include && start == -1)
+			start = offset;
+
+		if (!include && start != -1) {
+			pr_debug("region: 0x%p+0x%x\n", fit + start, offset - start);
+			digest_update(digest, fit + start, offset - start);
+			start = -1;
+		}
+	} while (tag != FDT_END);
+
+	pr_debug("region: 0x%p+0x%x\n", fit + start, dt_struct - start);
+	digest_update(digest, fit + start, dt_struct - start);
+
+	pr_debug("strings: 0x%p+0x%x\n", dt_strings+hashed_strings_start, hashed_strings_size);
+	digest_update(digest, dt_strings + hashed_strings_start, hashed_strings_size);
+
+	return 0;
+}
+
+/*
+ * The consistency of the FTD structure was already checked by of_unflatten_dtb()
+ */
+static int fit_verify_signature(struct device_node *sig_node, void *fit)
+{
+	uint32_t hashed_strings_start, hashed_strings_size;
+	struct string_list inc_nodes, exc_props;
+	struct rsa_public_key key = {};
+	struct digest *digest;
+	int sig_len;
+	const char *algo_name, *key_name, *sig_value;
+	char *key_path;
+	struct device_node *key_node;
+	enum hash_algo algo;
+	void *hash;
+	int ret;
+
+	if (of_property_read_string(sig_node, "algo", &algo_name)) {
+		pr_err("algo not found\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	if (strcmp(algo_name, "sha1,rsa2048") == 0) {
+		algo = HASH_ALGO_SHA1;
+	} else if (strcmp(algo_name, "sha256,rsa4096") == 0) {
+		algo = HASH_ALGO_SHA256;
+	} else	{
+		pr_err("unknown algo %s\n", algo_name);
+		ret = -EINVAL;
+		goto out;
+	}
+	digest = digest_alloc_by_algo(algo);
+	if (!digest) {
+		pr_err("unsupported algo %s\n", algo_name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	sig_value = of_get_property(sig_node, "value", &sig_len);
+	if (!sig_value) {
+		pr_err("signature value not found in %s\n", sig_node->full_name);
+		ret = -EINVAL;
+		goto out_free_digest;
+	}
+
+	if (of_property_read_string(sig_node, "key-name-hint", &key_name)) {
+		pr_err("key name not found in %s\n", sig_node->full_name);
+		ret = -EINVAL;
+		goto out_free_digest;
+	}
+	key_path = xasprintf("/signature/key-%s", key_name);
+	key_node = of_find_node_by_path(key_path);
+	free(key_path);
+	if (!key_node) {
+		pr_info("failed to find key node\n");
+		ret = -ENOENT;
+		goto out_free_digest;
+	}
+
+	ret = rsa_of_read_key(key_node, &key);
+	if (ret) {
+		pr_info("failed to read key in %s\n", key_node->full_name);
+		ret = -ENOENT;
+		goto out_free_digest;
+	}
+
+	if (of_property_read_u32_index(sig_node, "hashed-strings", 0, &hashed_strings_start)) {
+		pr_err("hashed-strings start not found in %s\n", sig_node->full_name);
+		ret = -EINVAL;
+		goto out_free_digest;
+	}
+	if (of_property_read_u32_index(sig_node, "hashed-strings", 1, &hashed_strings_size)) {
+		pr_err("hashed-strings size not found in %s\n", sig_node->full_name);
+		ret = -EINVAL;
+		goto out_free_digest;
+	}
+
+	string_list_init(&inc_nodes);
+	string_list_init(&exc_props);
+
+	if (of_read_string_list(sig_node, "hashed-nodes", &inc_nodes)) {
+		pr_err("hashed-nodes property not found in %s\n", sig_node->full_name);
+		ret = -EINVAL;
+		goto out_sl;
+	}
+
+	string_list_add(&exc_props, "data");
+
+	digest_init(digest);
+	ret = fit_digest(fit, digest, &inc_nodes, &exc_props, hashed_strings_start, hashed_strings_size);
+	hash = xzalloc(digest_length(digest));
+	digest_final(digest, hash);
+
+	ret = rsa_verify(&key, sig_value, sig_len, hash, algo);
+	if (ret) {
+		pr_info("image signature BAD\n");
+		ret = -EBADMSG;
+	} else {
+		pr_info("image signature OK\n");
+		ret = 0;
+	}
+
+	free(hash);
+ out_sl:
+	string_list_free(&inc_nodes);
+	string_list_free(&exc_props);
+ out_free_digest:
+	digest_free(digest);
+ out:
+	return ret;
+}
+
+static int fit_verify_hash(struct device_node *hash, const void *data, int data_len)
+{
+	struct digest *d;
+	const char *algo;
+	const char *value_read;
+	char *value_calc;
+	int hash_len, ret;
+
+	value_read = of_get_property(hash, "value", &hash_len);
+	if (!value_read) {
+		pr_err("%s: \"value\" property not found\n", hash->full_name);
+		return -EINVAL;
+	}
+
+	if (of_property_read_string(hash, "algo", &algo)) {
+		pr_err("%s: \"algo\" property not found\n", hash->full_name);
+		return -EINVAL;
+	}
+
+	d = digest_alloc(algo);
+	if (!d) {
+		pr_err("%s: unsupported algo %s\n", hash->full_name, algo);
+		return -EINVAL;
+	}
+
+	if (hash_len != digest_length(d)) {
+		pr_err("%s: invalid hash length %d\n", hash->full_name, hash_len);
+		ret = -EINVAL;
+		goto err_digest_free;
+	}
+
+	value_calc = xmalloc(hash_len);
+
+	digest_init(d);
+	digest_update(d, data, data_len);
+	digest_final(d, value_calc);
+
+	if (memcmp(value_read, value_calc, hash_len)) {
+		pr_info("%s: hash BAD\n", hash->full_name);
+		ret =  -EBADMSG;
+	} else {
+		pr_info("%s: hash OK\n", hash->full_name);
+		ret = 0;
+	}
+
+	free(value_calc);
+
+err_digest_free:
+	digest_free(d);
+
+	return ret;
+}
+
+static int fit_open_image(struct fit_handle *handle, const char *unit, const void **outdata,
+	unsigned long *outsize)
+{
+	struct device_node *image = NULL, *hash;
+	const char *type = NULL, *desc= "(no description)";
+	const void *data;
+	int data_len;
+	int ret = 0;
+
+	image = of_get_child_by_name(handle->root, "images");
+	if (!image)
+		return -ENOENT;
+
+	image = of_get_child_by_name(image, unit);
+	if (!image)
+		return -ENOENT;
+
+	of_property_read_string(image, "description", &desc);
+	pr_info("image '%s': '%s'\n", unit, desc);
+
+	of_property_read_string(image, "type", &type);
+	if (!type) {
+		pr_err("No \"type\" property found in %s\n", image->full_name);
+		return -EINVAL;
+	}
+
+	data = of_get_property(image, "data", &data_len);
+	if (!data) {
+		pr_err("data not found\n");
+		return -EINVAL;
+	}
+
+	if (handle->verify > BOOTM_VERIFY_NONE) {
+		ret = -EINVAL;
+		for_each_child_of_node(image, hash) {
+			if (handle->verbose)
+				of_print_nodes(hash, 0);
+			ret = fit_verify_hash(hash, data, data_len);
+			if (ret < 0)
+				return ret;
+		}
+	}
+	if (ret < 0)
+		return ret;
+
+	*outdata = data;
+	*outsize = data_len;
+
+	return 0;
+}
+
+static int fit_open_configuration(struct fit_handle *handle, const char *name)
+{
+	struct device_node *conf_node = NULL, *sig_node;
+	const char *unit, *desc = "(no description)";
+	int ret;
+
+	conf_node = of_get_child_by_name(handle->root, "configurations");
+	if (!conf_node)
+		return -ENOENT;
+
+	if (name) {
+		unit = name;
+	} else if (of_property_read_string(conf_node, "default", &unit)) {
+		unit = "conf at 1";
+	}
+
+	conf_node = of_get_child_by_name(conf_node, unit);
+	if (!conf_node) {
+		pr_err("configuration '%s' not found\n", unit);
+		return -ENOENT;
+	}
+
+	of_property_read_string(conf_node, "description", &desc);
+	pr_info("configuration '%s': %s\n", unit, desc);
+
+	if (IS_ENABLED(CONFIG_FITIMAGE_SIGNATURE) &&
+	    handle->verify == BOOTM_VERIFY_SIGNATURE) {
+		ret = -EINVAL;
+		for_each_child_of_node(conf_node, sig_node) {
+			if (handle->verbose)
+				of_print_nodes(sig_node, 0);
+			ret = fit_verify_signature(sig_node, handle->fit);
+			if (ret < 0)
+				return ret;
+		}
+
+		if (ret < 0)
+			return ret;
+	}
+
+	if (of_property_read_string(conf_node, "kernel", &unit) == 0) {
+		ret = fit_open_image(handle, unit, &handle->kernel, &handle->kernel_size);
+		if (ret)
+			return ret;
+	} else {
+		return -ENOENT;
+	}
+
+	if (of_property_read_string(conf_node, "fdt", &unit) == 0) {
+		ret = fit_open_image(handle, unit, &handle->oftree, &handle->oftree_size);
+		if (ret)
+			return ret;
+	}
+
+	if (of_property_read_string(conf_node, "ramdisk", &unit) == 0) {
+		ret = fit_open_image(handle, unit, &handle->initrd, &handle->initrd_size);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+struct fit_handle *fit_open(const char *filename, const char *config, bool verbose,
+			    enum bootm_verify verify)
+{
+	struct fit_handle *handle = NULL;
+	const char *desc = "(no description)";
+	int ret;
+
+	handle = xzalloc(sizeof(struct fit_handle));
+
+	handle->verbose = verbose;
+
+	ret = read_file_2(filename, &handle->size, &handle->fit, FILESIZE_MAX);
+	if (ret) {
+		pr_err("unable to read %s: %s\n", filename, strerror(-ret));
+		goto err;
+	}
+
+	handle->root = of_unflatten_dtb(handle->fit);
+	if (IS_ERR(handle->root)) {
+		ret = PTR_ERR(handle->root);
+		goto err;
+	}
+
+	handle->verify = verify;
+
+	of_property_read_string(handle->root, "description", &desc);
+	pr_info("'%s': %s\n", filename, desc);
+
+	ret = fit_open_configuration(handle, config);
+	if (ret)
+		goto err;
+
+	return handle;
+ err:
+	if (handle->root)
+		of_delete_node(handle->root);
+	free(handle->fit);
+	free(handle);
+
+	return ERR_PTR(ret);
+}
+
+void fit_close(struct fit_handle *handle)
+{
+	if (handle->root)
+		of_delete_node(handle->root);
+	if (handle->fit)
+		free(handle->fit);
+	free(handle);
+}
+
+#ifdef CONFIG_SANDBOX
+static int do_bootm_sandbox_fit(struct image_data *data)
+{
+	struct fit_handle *handle;
+	handle = fit_open(data->os_file, data->os_part, data->verbose);
+	if (handle)
+		fit_close(handle);
+	return 0;
+}
+
+static struct image_handler sandbox_fit_handler = {
+	.name = "FIT image",
+	.bootm = do_bootm_sandbox_fit,
+	.filetype = filetype_oftree,
+};
+
+static int sandbox_fit_register(void)
+{
+	return register_image_handler(&sandbox_fit_handler);
+}
+late_initcall(sandbox_fit_register);
+#endif
diff --git a/include/boot.h b/include/boot.h
index 363a02a..0198cc8 100644
--- a/include/boot.h
+++ b/include/boot.h
@@ -34,6 +34,10 @@ struct image_data {
 
 	/* if os is an uImage this will be provided */
 	struct uimage_handle *os;
+
+	/* if os is a FIT image this will be provided */
+	struct fit_handle *os_fit;
+
 	char *os_part;
 
 	/* otherwise only the filename will be provided */
diff --git a/include/image-fit.h b/include/image-fit.h
new file mode 100644
index 0000000..c9d6911
--- /dev/null
+++ b/include/image-fit.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) Jan Lübbe, 2014
+ */
+
+#ifndef __IMAGE_FIT_H__
+#define __IMAGE_FIT_H__
+
+#include <linux/types.h>
+#include <boot.h>
+
+struct fit_handle {
+	void *fit;
+	size_t size;
+
+	bool verbose;
+	enum bootm_verify verify;
+
+	struct device_node *root;
+
+	const void *kernel;
+	unsigned long kernel_size;
+	const void *oftree;
+	unsigned long oftree_size;
+	const void *initrd;
+	unsigned long initrd_size;
+};
+
+struct fit_handle *fit_open(const char *filename, const char *config, bool verbose,
+			    enum bootm_verify verify);
+void fit_close(struct fit_handle *handle);
+
+#endif	/* __IMAGE_FIT_H__ */
-- 
2.7.0.rc3




More information about the barebox mailing list