[U-Boot] [PATCH 2/2] cmd: fsfitxtract: Extract a part of a FIT multi-image stored on a filesystem

Alexey Ignatov lexszero at gmail.com
Sat May 20 06:50:20 UTC 2017


Sometimes we interested only in one single small subimage from big multipart
FIT. This command tries to avoid reading out the whole FIT because of memory
or time considerations.
Since we don't have mmap() in U-Boot, this is done by reading the file in
small chunks and trying to parse FIT until requested image is found or
total read size limit exceeded.

Signed-off-by: Alexey Ignatov <lexszero at gmail.com>
---
 cmd/Kconfig |   7 ++
 cmd/ximg.c  | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 233 insertions(+)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 2e4d2334a6..02b8357224 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -265,6 +265,13 @@ config CMD_XIMG
 	help
 	  Extract a part of a multi-image.
 
+config CMD_FSFITXIMG
+	bool "fsfitxtract"
+	default y
+	depends on FIT
+	help
+	  Extract a part of a FIT multi-image stored on a filesystem
+
 config CMD_POWEROFF
 	bool
 
diff --git a/cmd/ximg.c b/cmd/ximg.c
index 73a571b52b..17d961d5b6 100644
--- a/cmd/ximg.c
+++ b/cmd/ximg.c
@@ -22,6 +22,11 @@
 #endif
 #include <asm/byteorder.h>
 #include <asm/io.h>
+#ifdef CONFIG_CMD_FSFITXIMG
+#include <fs.h>
+#include <div64.h>
+#include <libfdt.h>
+#endif
 
 #ifndef CONFIG_SYS_XIMG_LEN
 /* use 8MByte as default max gunzip size */
@@ -283,3 +288,224 @@ U_BOOT_CMD(
 	imxtract, 4, 1, do_imgextract,
 	"extract a part of a multi-image", imgextract_help_text
 );
+
+#if defined(CONFIG_FIT) && defined(CONFIG_CMD_FSFITXIMG)
+
+/* TODO: maybe make this configurable */
+#define CHUNK_SIZE		(1024*1024)
+#define MAX_LOAD_SIZE	(16*1024*1024)
+
+struct fs_file {
+	const char *dev;
+	const char *part;
+	const char *name;
+};
+
+static int fs_load_chunk(struct fs_file *file, ulong addr, loff_t offset, loff_t len)
+{
+	int ret = 0;
+	loff_t len_read;
+
+	debug("Reading %lld bytes from offset 0x%llx to 0x%lx\n", len, offset, addr);
+
+	if (fs_set_blk_dev(file->dev, file->part, FS_TYPE_ANY))
+		return -1;
+
+	ret = fs_read(file->name, addr, offset, len, &len_read);
+
+	if (ret < 0) {
+		printf("Read failed: %d", ret);
+		return ret;
+	}
+
+	return len_read;
+}
+
+static int fdt_err_retry(int err)
+{
+	return
+		(err == -FDT_ERR_TRUNCATED) ||
+		(err == -FDT_ERR_BADSTRUCTURE);
+}
+
+static int
+do_fsfitextract(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
+{
+	ulong		dest;
+	int		ret;
+	int		verify;
+	int		part = 0;
+	struct fs_file file;
+	const char	*uname;
+	void	*fit_hdr;
+	const void	*fit_data;
+	size_t	fit_len;
+	int		noffset = 0,
+			src_off, fit_off = 0;
+	uint8_t		comp;
+	unsigned long time;
+
+	if (argc != 6)
+		return CMD_RET_USAGE;
+
+	verify = getenv_yesno("verify");
+
+	file.dev = argv[1];
+	file.part = argv[2];
+	file.name = argv[3];
+	uname = argv[4];
+	dest = simple_strtoul(argv[5], NULL, 16);
+	
+	time = get_timer(0);
+
+	/* Load FIT from the filesystem incrementally to avoid reading data we
+	 * won't need. If requested subimage contained in the beginning of file,
+	 * this can make a huge speedup.
+	 *
+	 * First, read just the header
+	 */
+	printf("## Loading FIT header to 0x%08lx ...\n", dest);
+	ret = fs_load_chunk(&file, dest, 0, sizeof(struct fdt_header));
+	if (ret < 0)
+		return -1;
+
+	fit_hdr = map_sysmem(dest, sizeof(struct fdt_header));
+	ret = fdt_check_header(fit_hdr);
+	if (ret) {
+		printf("Bad FIT header: %d\n", ret);
+		return 1;
+	}
+	
+	fit_off += sizeof(struct fdt_header);
+
+	/* Read "strings" block to resolve all the node names, put it after the
+	 * header and adjust offset appropriately
+	 */
+	printf("## Loading FIT strings to 0x%08lx ...\n", dest + fit_off);
+	ret = fs_load_chunk(&file, dest + fit_off,
+			fdt_off_dt_strings(fit_hdr),
+			fdt_size_dt_strings(fit_hdr));
+	if (ret < 0)
+		return 1;
+
+	fdt_set_off_dt_strings(fit_hdr, fit_off);
+	fit_off += fdt_size_dt_strings(fit_hdr);
+
+	/* Align to 4 bytes */
+	fit_off &= ~0x3;
+	fit_off += 4;
+
+	/* Save offset of "struct" block in file and set the real in-memory offset */
+	src_off = fdt_off_dt_struct(fit_hdr);
+	fdt_set_off_dt_struct(fit_hdr, fit_off);
+
+	/* Now, start to load struct in CHUNK_SIZE chunks, and parse it at the same
+	 * time. When libfdt notices that the data is truncated or broken, read next
+	 * chunk from the filesystem.
+	 */
+	printf("## Loading FIT struct to 0x%08lx ...\n", dest + fit_off);
+	do {
+		ret = fs_load_chunk(&file, dest + fit_off,
+				src_off, CHUNK_SIZE);
+		if (ret < 0)
+			return 1;
+		fit_off += ret;
+		src_off += ret;
+
+		ret = fit_image_get_node(fit_hdr, uname);
+		if (ret > 0) {
+			noffset = ret;
+			break;
+		}
+
+		debug("fit_image_get_node returned %d\n", ret);
+		if (!fdt_err_retry(ret)) {
+			printf("Error while processing FIT: %d\n", ret);
+			return 1;
+		}
+	} while (src_off < MAX_LOAD_SIZE);
+
+	if (src_off > MAX_LOAD_SIZE) {
+		printf("Requested subimage not found in first 0x%x bytes of FIT\n",
+				MAX_LOAD_SIZE);
+		return 1;
+	}
+
+	debug("found image node at %08x\n", noffset);
+
+	do {
+		/* get subimage fit_data address and length */
+		ret = fit_image_get_data(fit_hdr, noffset,
+				&fit_data, &fit_len);
+		if (!ret)
+			break;
+
+		if (!fdt_err_retry(fit_len)) {
+			puts("Could not find subimage fit_data\n");
+			return 1;
+		}
+
+		ret = fs_load_chunk(&file, dest + fit_off,
+				src_off, CHUNK_SIZE);
+		if (ret < 0)
+			return 1;
+		fit_off += ret;
+		src_off += ret;
+	} while (src_off < MAX_LOAD_SIZE);
+
+	debug("fit_data at %p, fit_len 0x%lx\n", fit_data, fit_len);
+
+	/* Now we know the length of data, so read it out */
+	while (fit_data + fit_len > fit_hdr + fit_off) {
+		ret = fs_load_chunk(&file, dest + fit_off,
+				src_off, CHUNK_SIZE);
+		if (ret < 0)
+			return 1;
+		fit_off += ret;
+		src_off += ret;
+	}
+
+	time = get_timer(time);
+
+	printf("0x%x bytes read in %lu ms", fit_off, time);
+	if (time > 0) {
+		puts(" (");
+		print_size(lldiv(fit_off, time) * 1000, "/s");
+		puts(")");
+	}
+	puts("\n");
+
+	fit_image_print(fit_hdr, noffset, "   ");
+
+	if (fit_image_get_comp(fit_hdr, noffset, &comp)) {
+		puts("Could not find FIT subimage compression type\n");
+		return 1;
+	}
+
+	/* verify integrity */
+	if (verify) {
+		if (!fit_image_verify(fit_hdr, noffset)) {
+			puts("Bad fit_data Hash\n");
+			return 1;
+		}
+	}
+
+	if (!decompress_data(dest, (ulong)fit_data, (ulong)fit_len, comp, part))
+		return 1;
+
+	flush_cache(dest, fit_len);
+
+	setenv_hex("filesize", fit_len);
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	fsfitxtract, 7, 1, do_fsfitextract,
+	"extract a part of a FIT stored on a filesystem",
+	"<interface> [<dev[:part]> <filename> <uname> <addr>\n"
+	"    - Extract subimage <uname> from FIT file <filename> from\n"
+	"       partition <part> on device type <interface> instance <dev>\n"
+	"       to address <addr> in memory"
+);
+#endif
-- 
2.12.2



More information about the U-Boot mailing list